jbosscache-core-3.2.8.GA/0000755000175000017500000000000011675222145014731 5ustar moellermoellerjbosscache-core-3.2.8.GA/README-COMMITTERS.txt0000644000175000017500000000116711111047320020101 0ustar moellermoellerBefore committing anything, PLEASE make sure you have the following in your ~/.subversion/config. This will ensure files have the *correct* line endings. Note: we do not use native since cygwin users like LF endings. Also be sure that your IDE uses this config: [miscellany] enable-auto-props = yes [auto-props] *.java = svn:keywords=Id Revision;svn:eol-style=LF *.xml = svn:keywords=Id Revision;svn:eol-style=LF *.wsdl = svn:keywords=Id Revision;svn:eol-style=LF *.xsd = svn:keywords=Id Revision;svn:eol-style=LF *.txt = svn:keywords=Id Revision;svn:eol-style=LF *.sh = svn:keywords=Id Revision;svn:eol-style=LF;svn:executable jbosscache-core-3.2.8.GA/README-Maven.txt0000644000175000017500000002176211363357042017502 0ustar moellermoeller 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 groups run are the "unit" and "functional". * 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. Important! Please note that if you try to generate user documentation using OpenJDK (at least build 14.0-b15) you might see an exception like this: java.lang.NullPointerException at org.apache.fop.render.pdf.FopPDFImage.setup(FopPDFImage.java:144) at org.apache.fop.pdf.PDFDocument.addImage(PDFDocument.java:794) at org.apache.fop.render.pdf.PDFRenderer.putImage(PDFRenderer.java:1725) at org.apache.fop.render.pdf.PDFRenderer.renderImage(PDFRenderer.java:1652) ... This appears to be a known issue: https://lists.launchpad.net/openjdk/msg00820.html. To get around the issue, simply switch to Sun JDK 5 or 6 to generate documentation. * 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 username and password to make releases and shapshot release -------------------------------------------------------------------------------- You will also have to configure maven to use your username and password to access the 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: ... ... jboss-developer jboss.org username jboss.org password jboss-snapshots jboss.org username jboss.org password jboss-releases jboss.org username jboss.org password ... ... 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. The JBoss repository will hold this release in a staging directory which can be accessed by logging in to https://repository.jboss.org/nexus After verifying the POM and the artifacts in Nexus, you can then close the repository and push the relase live. 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-3.2.8.GA/assembly/0000755000175000017500000000000011675221271016547 5ustar moellermoellerjbosscache-core-3.2.8.GA/assembly/assembly-1.1.0-SNAPSHOT.xsd0000644000175000017500000017301711057166721023011 0ustar moellermoeller 1.0.0+ An assembly defines a collection of files usually distributed in an archive format such as zip, tar, or tar.gz that is generated from a project. For example, a project could produce a ZIP assembly which contains a project's JAR artifact in the root directory, the runtime dependencies in a lib/ directory, and a shell script to launch a stand-alone application. 1.0.0+ An assembly defines a collection of files usually distributed in an archive format such as zip, tar, or tar.gz that is generated from a project. For example, a project could produce a ZIP assembly which contains a project's JAR artifact in the root directory, the runtime dependencies in a lib/ directory, and a shell script to launch a stand-alone application. 1.0.0+ Sets the id of this assembly. This is a symbolic name for a particular assembly of files from this project. Also, aside from being used to distinctly name the assembled package by attaching its value to the generated archive, the id is used as your artifact's classifier when deploying. 1.0.0+ Specifies the formats of the assembly. Multiple formats can be supplied and the Assembly Plugin will generate an archive for each desired formats. When deploying your project, all file formats specified will also be deployed. A format is specified by supplying one of the following values in a &lt;format&gt; subelement: <ul> <li><b>"zip"</b> - Creates a ZIP file format</li> <li><b>"gz"</b> - Creates a GZIP format</li> <li><b>"tar"</b> - Creates a TAR format</li> <li><b>"tar.gz"</b> - Creates a gzip'd TAR format</li> <li><b>"tar.bz2</b> - Creates a bzip'd TAR format</li> </ul> 0.0.0+ Includes a base directory in the final archive. For example, if you are creating an assembly named "your-app", setting includeBaseDirectory to true will create an archive that includes this base directory. If this option is set to false the archive created will unzip its content to the current directory. Default value is true. 1.1.0 Sets the base directory of the resulting assembly archive. If this is not set and includeBaseDirectory == true, ${project.build.finalName} will be used instead. 0.0.0+ Includes a site directory in the final archive. The site directory location of a project is determined by the siteDirectory parameter of the Assembly Plugin. Default value is false. 1.0.0+ Specifies which module files to include in the assembly. A moduleSet is specified by providing one or more of &lt;moduleSet&gt; subelements. 1.0.0+ Specifies which groups of files to include in the assembly. A fileSet is specified by providing one or more of &lt;fileSet&gt; subelements. 1.0.0+ Specifies which single files to include in the assembly. A file is specified by providing one or more of &lt;file&gt; subelements. 1.0.0+ Specifies which dependencies to include in the assembly. A dependencySet is specified by providing one or more of &lt;dependencySet&gt; subelements. 1.0.0+ Specifies which repository files to include in the assembly. A repository is specified by providing one or more of &lt;repository&gt; subelements. 1.0.0+ Specifies the shared components xml file locations to include in the assembly. The locations specified must be relative to the basedir of the project. When multiple componentDescriptors are found, their contents are merged. Check out the <a href="component.html"> descriptor components</a> for more information. A componentDescriptor is specified by providing one or more of &lt;componentDescriptor&gt; subelements. 1.0.0+ Defines a Maven repository to be included in the assembly. The artifacts available to be included in a repository are your project's dependency artifacts. The repository created contains the needed metadata entries and also contains both sha1 and md5 checksums. This is useful for creating archives which will be deployed to internal repositories. <br/><b>NOTE:</b> Currently, only artifacts from the central repository are allowed. 1.0.0+ If set to true, this property will trigger the creation of repository metadata which will allow the repository to be used as a functional remote repository. Default value is false. 1.0.0+ Specifies that you want to align a group of artifacts to a specified version. A groupVersionAlignment is specified by providing one or more of &lt;groupVersionAlignment&gt; subelements. 1.1.0 Specifies the scope for artifacts included in this repository. Default scope value is "runtime". 1.1.0 When specified as true, any include/exclude patterns which aren't used to filter an actual artifact during assembly creation will cause the build to fail with an error. This is meant to highlight obsolete inclusions or exclusions, or else signal that the assembly descriptor is incorrectly configured. 1.1.0 Whether standard exclusion patterns, such as those matching CVS and Subversion metadata files, should be used when calculating the files affected by this set. For backward compatibility, the default value is true. 1.0.0+ Sets the output directory relative to the root of the root directory of the assembly. For example, "log" will put the specified files in the log directory. 1.0.0+ When &lt;include&gt; subelements are present, they define a set of files and directory to include. If none is present, then &lt;includes&gt; represents all valid values. 1.0.0+ When &lt;exclude&gt; subelements are present, they define a set of files and directory to exclude. If none is present, then &lt;excludes&gt; represents no exclusions. 1.0.0+ Similar to a UNIX permission, sets the file mode of the files included. Format: (User)(Group)(Other) where each component is a sum of Read = 4, Write = 2, and Execute = 1. For example, the default value of 0644 translates to User read-write, Group and Other read-only. <a href="http://www.onlamp.com/pub/a/bsd/2000/09/06/FreeBSD_Basics.html">(more on unix-style permissions)</a> 1.0.0+ Similar to a UNIX permission, sets the directory mode of the directories included. Format: (User)(Group)(Other) where each component is a sum of Read = 4, Write = 2, and Execute = 1. For example, the default value of 0755 translates to User read-write, Group and Other read-only. <a href="http://www.onlamp.com/pub/a/bsd/2000/09/06/FreeBSD_Basics.html">(more on unix-style permissions)</a> 1.0.0+ Allows a group of artifacts to be aligned to a specified version. 1.0.0+ The groupId of the artifacts for which you want to align the versions. 1.0.0+ The version you want to align this group to. 1.0.0+ When &lt;exclude&gt; subelements are present, they define the artifactIds of the artifacts to exclude. If none is present, then &lt;excludes&gt; represents no exclusions. An exclude is specified by providing one or more of &lt;exclude&gt; subelements. 1.0.0+ A dependencySet allows inclusion and exclusion of project dependencies in the assembly. 1.0.0+ Sets the mapping pattern for all dependencies included in this assembly. Default is ${artifactId}-${version}.${extension}. 1.0.0+ If set to true, this property will unpack all dependencies into the specified output directory. When set to false dependencies will be includes as archives (jars). Can only unpack jar, zip, tar.gz, and tar.bz archives. Default value is false. 1.1.0 Allows the specification of includes and excludes, along with filtering options, for items unpacked from a dependency artifact. 1.0.0+ Sets the dependency scope for this dependencySet. Default scope value is "runtime". 1.1.0 When specified as true, any include/exclude patterns which aren't used to filter an actual artifact during assembly creation will cause the build to fail with an error. This is meant to highlight obsolete inclusions or exclusions, or else signal that the assembly descriptor is incorrectly configured. 1.1.0 Whether standard exclusion patterns, such as those matching CVS and Subversion metadata files, should be used when calculating the files affected by this set. For backward compatibility, the default value is true. 1.0.0+ Sets the output directory relative to the root of the root directory of the assembly. For example, "log" will put the specified files in the log directory. 1.0.0+ When &lt;include&gt; subelements are present, they define a set of files and directory to include. If none is present, then &lt;includes&gt; represents all valid values. 1.0.0+ When &lt;exclude&gt; subelements are present, they define a set of files and directory to exclude. If none is present, then &lt;excludes&gt; represents no exclusions. 1.0.0+ Similar to a UNIX permission, sets the file mode of the files included. Format: (User)(Group)(Other) where each component is a sum of Read = 4, Write = 2, and Execute = 1. For example, the default value of 0644 translates to User read-write, Group and Other read-only. <a href="http://www.onlamp.com/pub/a/bsd/2000/09/06/FreeBSD_Basics.html">(more on unix-style permissions)</a> 1.0.0+ Similar to a UNIX permission, sets the directory mode of the directories included. Format: (User)(Group)(Other) where each component is a sum of Read = 4, Write = 2, and Execute = 1. For example, the default value of 0755 translates to User read-write, Group and Other read-only. <a href="http://www.onlamp.com/pub/a/bsd/2000/09/06/FreeBSD_Basics.html">(more on unix-style permissions)</a> 1.1.0 Specifies options for including/excluding/filtering items extracted from an archive. 1.1.0 Set of patterns for matching items to be included from an archive as it is unpacked. 1.1.0 Set of patterns for matching items to be excluded from an archive as it is unpacked. 1.1.0 Whether to filter symbols in the files as they are unpacked from the archive, using properties from the build configuration. 1.0.0+ A moduleSet represent one or more project &lt;module&gt; present inside a project's pom.xml. This allows you to include sources or binaries belonging to a project's &lt;modules&gt;. <br/><b>NOTE:</b> When using &lt;moduleSets&gt; from the command-line, it is required to pass first the package phase by doing: "mvn package assembly:assembly". This bug/issue is scheduled to be addressed by Maven 2.1. 1.1.0 If set to false, the plugin will exclude sub-modules from processing in this ModuleSet. Otherwise, it will process all sub-modules, each subject to include/exclude rules. Default value is true. 1.0.0+ This is a list of &lt;include/&gt; subelements, each containing a module reference of the type groupId:artifactId. Modules matching these elements will be included in this set. If none is present, then &lt;includes&gt; represents all valid values. 1.0.0+ This is a list of &lt;exclude/&gt; subelements, each containing a module reference of the type groupId:artifactId. Modules matching these elements will be excluded from this set. 1.0.0+ When this is present, the plugin will include the source files of the included modules from this set in the resulting assembly. 1.0.0+ When this is present, the plugin will include the binaries of the included modules from this set in the resulting assembly. 1.0.0+ Contains configuration options for including the source files of a project module in an assembly. 1.1.0 Specifies which groups of files from each included module to include in the assembly. A fileSet is specified by providing one or more of &lt;fileSet&gt; subelements. 1.1.0 Specifies whether the module's finalName should be prepended to the outputDirectory values of any fileSets applied to it. Default value is true. 1.1.0 Specifies whether sub-module directories below the current module should be excluded from fileSets applied to that module. This might be useful if you only mean to copy the sources for the exact module list matched by this ModuleSet, ignoring (or processing separately) the modules which exist in directories below the current one. Default value is true. 1.1.0 Sets the mapping pattern for all module base-directories included in this assembly. NOTE: This field is only used if includeModuleDirectory == true. Default is the module's ${artifactId}. 1.1.0 When specified as true, any include/exclude patterns which aren't used to filter an actual artifact during assembly creation will cause the build to fail with an error. This is meant to highlight obsolete inclusions or exclusions, or else signal that the assembly descriptor is incorrectly configured. 1.1.0 Whether standard exclusion patterns, such as those matching CVS and Subversion metadata files, should be used when calculating the files affected by this set. For backward compatibility, the default value is true. 1.0.0+ Sets the output directory relative to the root of the root directory of the assembly. For example, "log" will put the specified files in the log directory. 1.0.0+ When &lt;include&gt; subelements are present, they define a set of files and directory to include. If none is present, then &lt;includes&gt; represents all valid values. 1.0.0+ When &lt;exclude&gt; subelements are present, they define a set of files and directory to exclude. If none is present, then &lt;excludes&gt; represents no exclusions. 1.0.0+ Similar to a UNIX permission, sets the file mode of the files included. Format: (User)(Group)(Other) where each component is a sum of Read = 4, Write = 2, and Execute = 1. For example, the default value of 0644 translates to User read-write, Group and Other read-only. <a href="http://www.onlamp.com/pub/a/bsd/2000/09/06/FreeBSD_Basics.html">(more on unix-style permissions)</a> 1.0.0+ Similar to a UNIX permission, sets the directory mode of the directories included. Format: (User)(Group)(Other) where each component is a sum of Read = 4, Write = 2, and Execute = 1. For example, the default value of 0755 translates to User read-write, Group and Other read-only. <a href="http://www.onlamp.com/pub/a/bsd/2000/09/06/FreeBSD_Basics.html">(more on unix-style permissions)</a> 1.0.0+ A fileSet allows the inclusion of groups of files into the assembly. 1.0.0+ Sets the absolute or relative location from the module's directory. For example, "src/main/bin" would select this subdirectory of the project in which this dependency is defined. 1.0.0+ Sets the line-endings of the files in this fileSet. Valid values: <ul> <li><b>"keep"</b> - Preserve all line endings</li> <li><b>"unix"</b> - Use Unix-style line endings</li> <li><b>"lf"</b> - Use a single line-feed line endings</li> <li><b>"dos"</b> - Use DOS-style line endings</li> <li><b>"crlf"</b> - Use Carraige-return, line-feed line endings</li> </ul> 1.1.0 Whether to filter symbols in the files as they are copied, using properties from the build configuration. 1.1.0 When specified as true, any include/exclude patterns which aren't used to filter an actual artifact during assembly creation will cause the build to fail with an error. This is meant to highlight obsolete inclusions or exclusions, or else signal that the assembly descriptor is incorrectly configured. 1.1.0 Whether standard exclusion patterns, such as those matching CVS and Subversion metadata files, should be used when calculating the files affected by this set. For backward compatibility, the default value is true. 1.0.0+ Sets the output directory relative to the root of the root directory of the assembly. For example, "log" will put the specified files in the log directory. 1.0.0+ When &lt;include&gt; subelements are present, they define a set of files and directory to include. If none is present, then &lt;includes&gt; represents all valid values. 1.0.0+ When &lt;exclude&gt; subelements are present, they define a set of files and directory to exclude. If none is present, then &lt;excludes&gt; represents no exclusions. 1.0.0+ Similar to a UNIX permission, sets the file mode of the files included. Format: (User)(Group)(Other) where each component is a sum of Read = 4, Write = 2, and Execute = 1. For example, the default value of 0644 translates to User read-write, Group and Other read-only. <a href="http://www.onlamp.com/pub/a/bsd/2000/09/06/FreeBSD_Basics.html">(more on unix-style permissions)</a> 1.0.0+ Similar to a UNIX permission, sets the directory mode of the directories included. Format: (User)(Group)(Other) where each component is a sum of Read = 4, Write = 2, and Execute = 1. For example, the default value of 0755 translates to User read-write, Group and Other read-only. <a href="http://www.onlamp.com/pub/a/bsd/2000/09/06/FreeBSD_Basics.html">(more on unix-style permissions)</a> 1.0.0+ Contains configuration options for including the binary files of a project module in an assembly. 1.1.0 When specified, the attachmentClassifier will cause the assembler to look at artifacts attached to the module instead of the main project artifact. If it can find an attached artifact matching the specified classifier, it will use it; otherwise, it will throw an exception. 1.0.0+ If set to true, the plugin will include the direct and transitive dependencies of of the project modules included here. Otherwise, it will only include the module packages only. Default value is true. 1.1.0 Specifies which dependencies of the module to include in the assembly. A dependencySet is specified by providing one or more of &lt;dependencySet&gt; subelements. 1.0.0+ If set to true, this property will unpack all module packages into the specified output directory. When set to false module packages will be included as archives (jars). Default value is true. 1.1.0 Allows the specification of includes and excludes, along with filtering options, for items unpacked from a module artifact. 1.0.0+ Sets the mapping pattern for all dependencies included in this assembly. Default is ${artifactId}-${version}.${extension}. 1.1.0 When specified as true, any include/exclude patterns which aren't used to filter an actual artifact during assembly creation will cause the build to fail with an error. This is meant to highlight obsolete inclusions or exclusions, or else signal that the assembly descriptor is incorrectly configured. 1.1.0 Whether standard exclusion patterns, such as those matching CVS and Subversion metadata files, should be used when calculating the files affected by this set. For backward compatibility, the default value is true. 1.0.0+ Sets the output directory relative to the root of the root directory of the assembly. For example, "log" will put the specified files in the log directory. 1.0.0+ When &lt;include&gt; subelements are present, they define a set of files and directory to include. If none is present, then &lt;includes&gt; represents all valid values. 1.0.0+ When &lt;exclude&gt; subelements are present, they define a set of files and directory to exclude. If none is present, then &lt;excludes&gt; represents no exclusions. 1.0.0+ Similar to a UNIX permission, sets the file mode of the files included. Format: (User)(Group)(Other) where each component is a sum of Read = 4, Write = 2, and Execute = 1. For example, the default value of 0644 translates to User read-write, Group and Other read-only. <a href="http://www.onlamp.com/pub/a/bsd/2000/09/06/FreeBSD_Basics.html">(more on unix-style permissions)</a> 1.0.0+ Similar to a UNIX permission, sets the directory mode of the directories included. Format: (User)(Group)(Other) where each component is a sum of Read = 4, Write = 2, and Execute = 1. For example, the default value of 0755 translates to User read-write, Group and Other read-only. <a href="http://www.onlamp.com/pub/a/bsd/2000/09/06/FreeBSD_Basics.html">(more on unix-style permissions)</a> 1.0.0+ A file allows individual file inclusion with the option to change the destination filename not supported by fileSets. 1.0.0+ Sets the absolute or relative path from the module's directory of the file to be included in the assembly. 1.0.0+ Sets the output directory relative to the root of the root directory of the assembly. For example, "log" will put the specified files in the log directory. 1.0.0+ Sets the destination filename in the outputDirectory. Default is the same name as the source's file. 1.0.0+ Similar to a UNIX permission, sets the file mode of the files included. Format: (User)(Group)(Other) where each component is a sum of Read = 4, Write = 2, and Execute = 1. For example, the default value of 0644 translates to User read-write, Group and Other read-only. <a href="http://www.onlamp.com/pub/a/bsd/2000/09/06/FreeBSD_Basics.html">(more on unix-style permissions)</a> 1.0.0+ Sets the line-endings of the files in this file. Valid values are: <ul> <li><b>"keep"</b> - Preserve all line endings</li> <li><b>"unix"</b> - Use Unix-style line endings</li> <li><b>"lf"</b> - Use a single line-feed line endings</li> <li><b>"dos"</b> - Use DOS-style line endings</li> <li><b>"crlf"</b> - Use Carraige-return, line-feed line endings</li> </ul> 1.0.0+ Sets whether to determine if the file is filtered. jbosscache-core-3.2.8.GA/assembly/doc.xml0000644000175000017500000000203011057166721020033 0ustar moellermoeller doc zip true src/main/release *.txt **lib** license/* target/site/apidocs doc/apidocs target/docbook doc/ jbosscache-core-3.2.8.GA/assembly/src.xml0000644000175000017500000000162711057166721020070 0ustar moellermoeller src zip true target/** output/** test-output/** jbossdb/** *.jdb *.iml *.ipr *.iws *.log *.lck jbosscache-core-3.2.8.GA/assembly/all.xml0000644000175000017500000000602111074713752020043 0ustar moellermoeller all zip true target *.jar *test*.jar src/main/resources etc **/*.sh src/main/resources etc **/*.sh 0777 src/test/resources etc log4j.xml src/main/release **/*.txt target/site/apidocs doc/apidocs target/docbook doc/ src/main/tutorial tutorial/ target tutorial/lib/ **/jbosscache-core-tests.jar lib ${artifactId}.${extension} false runtime tutorial/lib ${artifactId}.${extension} false test jbosscache-core-3.2.8.GA/assembly/bin.xml0000644000175000017500000000422111057166721020042 0ustar moellermoeller bin zip true target *.jar *test*.jar *sources*.jar src/main/resources etc **/*.sh src/main/resources etc **/*.sh 0777 src/test/resources etc log4j.xml src/main/release **/*.txt lib ${artifactId}.${extension} false runtime jbosscache-core-3.2.8.GA/fixlineendings.sh0000755000175000017500000000071611111047320020263 0ustar moellermoeller#!/bin/bash find src -name \*.java -or -name \*.xml -or -name \*.txt -or -name \*.xsd -or -name \*.sh | xargs svn ps --force svn:eol-style LF find src -name \*.java -or -name \*.xml -or -name \*.txt -or -name \*.xsd -or -name \*.sh | xargs svn ps --force svn:keywords 'Id Revision' find src -name \*.sh | xargs svn ps svn:executable on svn ps --force svn:eol-style LF *.txt *.sh svn ps --force svn:keywords 'Id Revision' *.txt *.sh svn ps svn:executable on *.sh jbosscache-core-3.2.8.GA/src/0000755000175000017500000000000011675221627015524 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/0000755000175000017500000000000011675221613016476 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/0000755000175000017500000000000011675221272017420 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/0000755000175000017500000000000011675221273020210 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/0000755000175000017500000000000011675221273021330 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/0000755000175000017500000000000011675221612022370 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/factories/0000755000175000017500000000000011675221302024343 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/factories/LifeCycleTest.java0000644000175000017500000002246711121730272027715 0ustar moellermoellerpackage org.jboss.cache.factories; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.CacheStatus; import org.jboss.cache.Fqn; import org.jboss.cache.ReplicationException; import org.jboss.cache.SuspectException; 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.jboss.cache.util.TestingUtil; 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; import org.jboss.cache.UnitTestCacheFactory; /** * Tests restart (stop-destroy-create-start) of ComponentRegistry * * @author Bela Ban * @version $Id: LifeCycleTest.java 7332 2008-12-16 13:44:26Z mircea.markus $ */ @Test(groups = "functional", sequential = true, testName = "factories.LifeCycleTest") public class LifeCycleTest { private CacheSPI[] c; @AfterMethod public void tearDown() { TestingUtil.killCaches(c); c = null; } @SuppressWarnings("unchecked") private void createAndRegisterCache(Configuration.CacheMode mode, boolean start) throws Exception { CacheSPI 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 testLocalRestartWithTransactionsPessimistic() throws Exception { createAndRegisterCache(Configuration.CacheMode.LOCAL, false); c[0].getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); c[0].start(); 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 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) { } } 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) { Configuration c = new Configuration(); c.setCacheMode(cache_mode); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); CacheSPI retval = (CacheSPI) new UnitTestCacheFactory().createCache(c, false, getClass()); 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-3.2.8.GA/src/test/java/org/jboss/cache/factories/UnitTestConfigurationFactory.java0000644000175000017500000002404311132354355033053 0ustar moellermoeller/* * 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.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.config.parsing.XmlConfigHelper; import org.jboss.cache.config.parsing.XmlConfigurationParser; import org.jboss.cache.transaction.TransactionSetup; import org.jboss.cache.util.FileLookup; import org.jboss.cache.UnitTestCacheFactory; import org.jgroups.conf.XmlConfigurator; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import java.io.InputStream; import java.net.URL; import java.util.Properties; import org.jgroups.conf.ConfiguratorFactory; /** * Cache configuration factory used by unit tests. */ public class UnitTestConfigurationFactory { public static final String JGROUPS_CHANNEL; public static final String JGROUPS_STACK_TYPE = "jgroups.stack"; public static final String DEFAULT_CONFIGURATION_FILE = "unit-test-cache-service.xml"; static { JGROUPS_CHANNEL = System.getProperty(JGROUPS_STACK_TYPE, "tcp"); System.out.println("IN USE JGROUPS_CHANNEL = " + JGROUPS_CHANNEL); } 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 buildSingleCacheLoaderConfig(boolean passivation, String preload, String cacheloaderClass, String properties, boolean async, boolean fetchPersistentState, boolean shared, boolean purgeOnStartup, boolean ignoreModifications) throws Exception { CacheLoaderConfig clc = new CacheLoaderConfig(); IndividualCacheLoaderConfig iclc = new IndividualCacheLoaderConfig(); iclc.setClassName(cacheloaderClass); iclc.setAsync(async); iclc.setFetchPersistentState(fetchPersistentState); iclc.setPurgeOnStartup(purgeOnStartup); iclc.setIgnoreModifications(ignoreModifications); iclc.setProperties(properties); clc.addIndividualCacheLoaderConfig(iclc); clc.setPassivation(passivation); clc.setShared(shared); clc.setPreload(preload); return clc; } public static CacheLoaderConfig buildSingleCacheLoaderConfig(boolean passivation, String preload, String cacheloaderClass, Properties properties, boolean async, boolean fetchPersistentState, boolean shared, boolean purgeOnStartup, boolean ignoreModifications) throws Exception { CacheLoaderConfig clc = new CacheLoaderConfig(); IndividualCacheLoaderConfig iclc = new IndividualCacheLoaderConfig(); iclc.setClassName(cacheloaderClass); iclc.setAsync(async); iclc.setFetchPersistentState(fetchPersistentState); iclc.setPurgeOnStartup(purgeOnStartup); iclc.setIgnoreModifications(ignoreModifications); iclc.setProperties(properties); clc.addIndividualCacheLoaderConfig(iclc); clc.setPassivation(passivation); clc.setShared(shared); clc.setPreload(preload); return clc; } public static CacheLoaderConfig.IndividualCacheLoaderConfig buildIndividualCacheLoaderConfig(String preload, String cacheloaderClass, String properties, boolean async, boolean fetchPersistentState, boolean purgeOnStartup, boolean ignoreModifications) throws Exception { return buildSingleCacheLoaderConfig(false, preload, cacheloaderClass, properties, async, fetchPersistentState, false, purgeOnStartup, ignoreModifications).getFirstCacheLoaderConfig(); } /** * 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 properties config properties * @return a String */ public static String getClusterConfigFromProperties(String properties) { try { XmlConfigurator conf = XmlConfigurator.getInstance(ConfiguratorFactory.getConfigStream(properties)); 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"); return tmp; } catch (Exception e) { throw new RuntimeException("Problems with properties " + properties, 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(":")); } /** * This will make sure that cluster config is according {@link #JGROUPS_STACK_TYPE}, even for local caches. * This is to avoid the following scenario: if you build a Configuration through new Configuration() then clusterCOnfig * is set to default value, which might be UDP. * */ public static Configuration getEmptyConfiguration() { Configuration tmp = createConfiguration(CacheMode.REPL_SYNC); Configuration conf = new Configuration(); conf.setClusterConfig(UnitTestCacheFactory.mangleClusterConfiguration(tmp.getClusterConfig())); assert conf.getClusterConfig() != null; return conf; } private static class UnitTestXmlConfigurationParser extends XmlConfigurationParser { public Configuration parseFile(String filename, CacheMode mode) { String finalFileName = filename == null ? DEFAULT_CONFIGURATION_FILE : filename; return parseStream(new FileLookup().lookupFile(finalFileName), 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 = XmlConfigHelper.getDocumentRoot(stream); XmlConfigurationParser parser = new UnitTestXmlConfigurationParser(); Configuration conf = parser.parseElement(root); Element list = (Element) root.getElementsByTagNameNS("*","protocol_stacks").item(0); NodeList stacks = list.getElementsByTagNameNS("*", "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.getElementsByTagNameNS("*", "config").item(0); if (!mode.isSynchronous() && !stackName.contains("-")) { conf.setClusterConfig(jgroupsStack); conf.setCacheMode(CacheMode.REPL_ASYNC); break; } else if (mode.isSynchronous() && stackName.contains("-")) { conf.setClusterConfig(jgroupsStack); conf.setCacheMode(CacheMode.REPL_SYNC); break; } } } // either way, set mode in the config!! conf.setCacheMode(mode); return conf; } } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/factories/CustomInterceptorConstructionTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/factories/CustomInterceptorConstructionTest.j0000644000175000017500000001374311132625723033475 0ustar moellermoeller/* * 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.factories; import org.jboss.cache.CacheSPI; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.CustomInterceptorConfig; import org.jboss.cache.config.parsing.custominterceptors.AaaCustomInterceptor; import org.jboss.cache.interceptors.MVCCLockingInterceptor; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import java.util.Collections; import java.util.List; /** * Tests how custom interceptor construction is handled. * * @author Mircea.Markus@jboss.com * @since 3.0 */ @Test(groups = "functional", sequential = true, testName = "factories.CustomInterceptorConstructionTest") public class CustomInterceptorConstructionTest { CacheSPI cache; int defaultInterceptroCount; @AfterMethod public void tearDown() { if (cache != null) { TestingUtil.killCaches(cache); } cache = null; } public void testAddFirst() { AaaCustomInterceptor interceptor = new AaaCustomInterceptor(); CustomInterceptorConfig config = new CustomInterceptorConfig(interceptor); config.setFirst(true); buildCache(config); assert cache.getInterceptorChain().get(0).equals(interceptor); assert cache.getInterceptorChain().size() == defaultInterceptroCount + 1; } public void testAddLast() { AaaCustomInterceptor interceptor = new AaaCustomInterceptor(); CustomInterceptorConfig config = new CustomInterceptorConfig(interceptor); config.setLast(true); buildCache(config); List chain = cache.getInterceptorChain(); assert chain.get(chain.size() - 1).equals(interceptor); assert cache.getInterceptorChain().size() == defaultInterceptroCount + 1; } public void testAddAfter() { AaaCustomInterceptor interceptor = new AaaCustomInterceptor(); CustomInterceptorConfig config = new CustomInterceptorConfig(interceptor); config.setAfterClass(MVCCLockingInterceptor.class.getName()); buildCache(config); List chain = cache.getInterceptorChain(); int occurenceCount = 0; for (CommandInterceptor ci : chain) { if (ci instanceof MVCCLockingInterceptor) { assert ci.getNext().equals(interceptor); occurenceCount++; } } assert occurenceCount == 1; assert cache.getInterceptorChain().size() == defaultInterceptroCount + 1; } public void testAddBefore() { AaaCustomInterceptor interceptor = new AaaCustomInterceptor(); CustomInterceptorConfig config = new CustomInterceptorConfig(interceptor); config.setBeforeClass(MVCCLockingInterceptor.class.getName()); buildCache(config); List chain = cache.getInterceptorChain(); int occurenceCount = 0; for (CommandInterceptor ci : chain) { if (ci instanceof AaaCustomInterceptor) { assert ci.getNext() instanceof MVCCLockingInterceptor; occurenceCount++; } } assert occurenceCount == 1; assert cache.getInterceptorChain().size() == defaultInterceptroCount + 1; } @Test(enabled = true) public void testAddAtIndex() { AaaCustomInterceptor interceptor = new AaaCustomInterceptor(); CustomInterceptorConfig config = new CustomInterceptorConfig(interceptor); config.setIndex(1); buildCache(config); List chain = cache.getInterceptorChain(); assert chain.get(1).equals(interceptor); assert cache.getInterceptorChain().size() == defaultInterceptroCount + 1; } public void testAddAtInvalidIndex() { AaaCustomInterceptor interceptor = new AaaCustomInterceptor(); CustomInterceptorConfig config = new CustomInterceptorConfig(interceptor); config.setIndex(1000); try { buildCache(config); assert false : "exception expected here"; } catch (Exception e) { //expected } } private void buildCache(CustomInterceptorConfig interceptorConfig) { buildCache(Collections.singletonList(interceptorConfig)); } private void buildCache(List interceptorConfig) { Configuration config = new Configuration(); config.setCacheMode(Configuration.CacheMode.LOCAL); config.setNodeLockingScheme(Configuration.NodeLockingScheme.MVCC); UnitTestCacheFactory cacheFactory2 = new UnitTestCacheFactory(); CacheSPI tmpCacheSPI = (CacheSPI) cacheFactory2.createCache(config, getClass()); defaultInterceptroCount = tmpCacheSPI.getInterceptorChain().size(); tmpCacheSPI.stop(); UnitTestCacheFactory cacheFactory = new UnitTestCacheFactory(); config.setCustomInterceptors(interceptorConfig); cache = (CacheSPI) cacheFactory.createCache(config, true, getClass()); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/factories/UnitTestXmlConfigurationParser.java0000644000175000017500000001012711131613416033352 0ustar moellermoellerpackage org.jboss.cache.factories; import org.jboss.cache.config.parsing.XmlConfigurationParser; import org.jboss.cache.config.parsing.XmlConfigHelper; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.util.FileLookup; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.io.InputStream; import java.util.Map; import java.util.HashMap; /** * The purpose of this class is to make sure that the parsing of the test files is only performed once. * * @author Mircea.Markus@jboss.com */ class UnitTestXmlConfigurationParser { private static Log log = LogFactory.getLog(UnitTestConfigurationFactory.class); public static final String DEFAULT_CONFIGURATION_FILE = "unit-test-cache-service.xml"; public static final String JGROUPS_CHANNEL; public static final String JGROUPS_STACK_TYPE = "jgroups.stack"; static { JGROUPS_CHANNEL = System.getProperty(JGROUPS_STACK_TYPE); } private volatile static UnitTestXmlConfigurationParser instance; private volatile Configuration confCache; private volatile Map elementCache = new HashMap(); public static Configuration getConfiguration(Configuration.CacheMode cacheMode, boolean useTcp) { if (instance == null) { synchronized (UnitTestXmlConfigurationParser.class) { if (instance == null) { instance = new UnitTestXmlConfigurationParser(); instance.parseFile(); } } } if (instance == null) { log.error("instance is null after creating node!!!"); throw new IllegalStateException(); } //if there is an enforced jgroups stack, then only consider that one if (JGROUPS_CHANNEL != null) { useTcp = JGROUPS_CHANNEL.trim().equals("tcp"); } String resultKey = useTcp ? "tcp-" : "udp-"; resultKey += cacheMode.isSynchronous() ? "sync" : "async"; Configuration resultConf; try { resultConf = instance.confCache.clone(); } catch (CloneNotSupportedException e) { log.error("Could not clone:", e); throw new IllegalStateException(e); } Element stack = instance.elementCache.get(resultKey); if (stack == null) { log.error("stack is null!!!"); throw new NullPointerException(); } resultConf.setClusterConfig(stack); if (resultConf.getClusterConfig() == null) { log.error("Null cluster config"); throw new IllegalStateException(); } resultConf.setCacheMode(cacheMode); return resultConf; } private void parseFile() { parseStream(new FileLookup().lookupFile(DEFAULT_CONFIGURATION_FILE)); } private void parseStream(InputStream stream) { try { // loop through all elements in XML. if (stream == null) throw new ConfigurationException("Input stream for configuration xml is null!"); Element root = XmlConfigHelper.getDocumentRoot(stream); XmlConfigurationParser parser = new XmlConfigurationParser(); confCache = parser.parseElement(root); if (confCache == null) throw new NullPointerException("Null conf cache!!"); Element list = (Element) root.getElementsByTagNameNS("*", "protocol_stacks").item(0); NodeList stacks = list.getElementsByTagNameNS("*", "stack"); for (int i = 0; i < stacks.getLength(); i++) { Element stack = (Element) stacks.item(i); String stackName = stack.getAttribute("name"); Element jgroupsStack = (Element) stack.getElementsByTagNameNS("*", "config").item(0); elementCache.put(stackName, jgroupsStack); } } catch (Exception e) { log.error(e); throw new IllegalStateException(e); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/factories/InterceptorChainFactoryTest.java0000644000175000017500000006500111121730272032636 0ustar moellermoellerpackage org.jboss.cache.factories; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.BuddyReplicationConfig; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.config.Configuration; import static org.jboss.cache.config.Configuration.CacheMode.*; import static org.jboss.cache.config.Configuration.NodeLockingScheme.MVCC; import static org.jboss.cache.config.Configuration.NodeLockingScheme.OPTIMISTIC; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.interceptors.*; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.loader.testloaders.DummyInMemoryCacheLoader; import org.jboss.cache.transaction.DummyTransactionManagerLookup; 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.util.Iterator; import java.util.List; @Test(groups = "unit", sequential = true, testName = "factories.InterceptorChainFactoryTest") public class InterceptorChainFactoryTest extends InterceptorChainTestBase { CacheSPI cache = null; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { Configuration configuration = new Configuration(); configuration.setCacheMode(LOCAL); configuration.setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); configuration.setUseLazyDeserialization(false); cache = (CacheSPI) new UnitTestCacheFactory().createCache(configuration,false, getClass()); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache); cache = null; } public void testBareConfig() throws Exception { cache.getConfiguration().setExposeManagementStatistics(false); InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain(); List list = chain.asList(); Iterator interceptors = list.iterator(); 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 testBatchingConfig() throws Exception { cache.getConfiguration().setExposeManagementStatistics(false); cache.getConfiguration().setInvocationBatchingEnabled(true); InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain(); List list = chain.asList(); Iterator interceptors = list.iterator(); assertNotNull(list); assertEquals(6, list.size()); assertEquals(BatchingInterceptor.class, interceptors.next().getClass()); 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 testMvccConfig() throws Exception { cache.getConfiguration().setExposeManagementStatistics(false); cache.getConfiguration().setNodeLockingScheme(MVCC); InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain(); List list = chain.asList(); Iterator interceptors = list.iterator(); 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(MVCCLockingInterceptor.class, interceptors.next().getClass()); assertEquals(CallInterceptor.class, interceptors.next().getClass()); assertInterceptorLinkage(list); } public void testTxConfig() throws Exception { cache.getConfiguration().setExposeManagementStatistics(false); cache.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain(); List list = chain.asList(); Iterator interceptors = list.iterator(); 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 { CacheLoaderConfig clc = new CacheLoaderConfig(); IndividualCacheLoaderConfig iclc = new IndividualCacheLoaderConfig(); iclc.setClassName(DummyInMemoryCacheLoader.class.getName()); iclc.setFetchPersistentState(fetchPersistentState); clc.addIndividualCacheLoaderConfig(iclc); clc.setPassivation(pasv); return clc; } public void testSharedCacheLoaderConfig() throws Exception { cache.getConfiguration().setExposeManagementStatistics(false); cache.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); 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(); 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(LegacyCacheLoaderInterceptor.class, interceptors.next().getClass()); assertEquals(LegacyCacheStoreInterceptor.class, interceptors.next().getClass()); assertEquals(CallInterceptor.class, interceptors.next().getClass()); assertInterceptorLinkage(list); } public void testSharedCacheLoaderMvccConfig() throws Exception { cache.getConfiguration().setExposeManagementStatistics(false); cache.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cache.getConfiguration().setCacheLoaderConfig(getCacheLoaderConfig(false, false)); cache.getConfiguration().setCacheMode(REPL_ASYNC); cache.getConfiguration().setNodeLockingScheme(MVCC); cache.getConfiguration().setFetchInMemoryState(false); cache.create(); InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain(); List list = chain.asList(); Iterator interceptors = list.iterator(); 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(CacheLoaderInterceptor.class, interceptors.next().getClass()); assertEquals(MVCCLockingInterceptor.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(DummyTransactionManagerLookup.class.getName()); 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(); 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(LegacyCacheLoaderInterceptor.class, interceptors.next().getClass()); assertEquals(LegacyCacheStoreInterceptor.class, interceptors.next().getClass()); assertEquals(CallInterceptor.class, interceptors.next().getClass()); assertInterceptorLinkage(list); } public void testUnsharedCacheLoaderMvccConfig() throws Exception { cache.getConfiguration().setExposeManagementStatistics(false); cache.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cache.getConfiguration().setCacheLoaderConfig(getCacheLoaderConfig(false, true)); cache.getConfiguration().setCacheMode(REPL_ASYNC); cache.getConfiguration().setNodeLockingScheme(MVCC); cache.getConfiguration().setFetchInMemoryState(false); cache.create(); InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain(); List list = chain.asList(); Iterator interceptors = list.iterator(); 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(CacheLoaderInterceptor.class, interceptors.next().getClass()); assertEquals(MVCCLockingInterceptor.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(DummyTransactionManagerLookup.class.getName()); InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain(); List list = chain.asList(); Iterator interceptors = list.iterator(); 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().setNodeLockingScheme(OPTIMISTIC); 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().setNodeLockingScheme(OPTIMISTIC); 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().setNodeLockingScheme(OPTIMISTIC); 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(LegacyCacheLoaderInterceptor.class, interceptors.next().getClass()); assertEquals(LegacyCacheStoreInterceptor.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().setNodeLockingScheme(OPTIMISTIC); 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(LegacyActivationInterceptor.class, interceptors.next().getClass()); assertEquals(LegacyPassivationInterceptor.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 testPassivationMvccChain() throws Exception { cache.getConfiguration().setExposeManagementStatistics(false); cache.getConfiguration().setNodeLockingScheme(MVCC); cache.getConfiguration().setCacheLoaderConfig(getCacheLoaderConfig(true, false)); cache.create(); InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain(); List list = chain.asList(); Iterator interceptors = list.iterator(); assertEquals(7, list.size()); assertEquals(InvocationContextInterceptor.class, interceptors.next().getClass()); assertEquals(TxInterceptor.class, interceptors.next().getClass()); assertEquals(NotificationInterceptor.class, interceptors.next().getClass()); assertEquals(ActivationInterceptor.class, interceptors.next().getClass()); assertEquals(MVCCLockingInterceptor.class, interceptors.next().getClass()); assertEquals(PassivationInterceptor.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(); 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; } } ); InterceptorChainFactory factory = getInterceptorChainFactory(cache); InterceptorChain chain = factory.buildInterceptorChain(); List list = chain.asList(); Iterator interceptors = list.iterator(); 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 { BuddyReplicationConfig brc = new BuddyReplicationConfig(); brc.setEnabled(true); 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(); 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(LegacyDataGravitatorInterceptor.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 { BuddyReplicationConfig brc = new BuddyReplicationConfig(); brc.setEnabled(true); 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(); 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(LegacyDataGravitatorInterceptor.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-3.2.8.GA/src/test/java/org/jboss/cache/factories/ComponentRegistryUnitTest.java0000644000175000017500000000673611131613416032413 0ustar moellermoellerpackage org.jboss.cache.factories; import org.jboss.cache.Cache; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.Configuration; import org.testng.annotations.Test; /** * @author Manik Surtani (manik AT jboss DOT org) * @since 2.1.0 */ @Test(groups = "functional", testName = "factories.ComponentRegistryUnitTest") public class ComponentRegistryUnitTest { public void testConstruction() { Cache c = new UnitTestCacheFactory().createCache(UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.REPL_SYNC), getClass()); c.put("/a", "b", "c"); TestingUtil.killCaches(c); } // 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; // } // } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/factories/CustomInterceptorChainTest.java0000644000175000017500000001320611120421412032471 0ustar moellermoellerpackage org.jboss.cache.factories; import org.jboss.cache.CacheSPI; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.util.TestingUtil; 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"}, sequential = true, testName = "factories.CustomInterceptorChainTest") public class CustomInterceptorChainTest extends InterceptorChainTestBase { private CacheSPI cache; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { Configuration c = new Configuration(); cache = (CacheSPI) new UnitTestCacheFactory().createCache(c, getClass()); cache.create(); } @AfterMethod(alwaysRun = true) public void tearDown() { if (cache != null) { TestingUtil.killCaches(cache); 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(); 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-3.2.8.GA/src/test/java/org/jboss/cache/factories/InterceptorChainTestBase.java0000644000175000017500000000132211111047320032067 0ustar moellermoellerpackage 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-3.2.8.GA/src/test/java/org/jboss/cache/factories/LateConfigurationTest.java0000644000175000017500000001111311121730272031455 0ustar moellermoellerpackage org.jboss.cache.factories; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; 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.testloaders.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.jboss.cache.util.TestingUtil; 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 AT jboss DOT org) * @since 2.1.0 */ @Test(groups = "functional", sequential = true, testName = "factories.LateConfigurationTest") public class LateConfigurationTest { CacheSPI c; @BeforeMethod public void setUp() { c = (CacheSPI) new UnitTestCacheFactory().createCache(false, getClass()); } @AfterMethod public void tearDown() { TestingUtil.killCaches(c); c = null; } 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; } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/factories/ComponentRegistryFunctionalTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/factories/ComponentRegistryFunctionalTest.jav0000644000175000017500000001747611111047320033431 0ustar moellermoellerpackage org.jboss.cache.factories; import org.testng.annotations.Test; /** * @author Manik Surtani (manik AT jboss DOT org) * @since 2.1.0 */ @Test(groups = {"functional"}, testName = "factories.ComponentRegistryFunctionalTest") 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 its 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 its 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-3.2.8.GA/src/test/java/org/jboss/cache/factories/LifeCycleWithReplTest.java0000644000175000017500000001220311142423437031364 0ustar moellermoellerpackage org.jboss.cache.factories; import org.jboss.cache.*; import org.jboss.cache.config.Configuration; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.fail; import org.testng.annotations.Test; import java.util.LinkedList; import java.util.List; /** * @author Mircea.Markus@jboss.com */ @Test(groups = {"functional"}, testName = "factories.LifeCycleWithReplTest") public class LifeCycleWithReplTest extends AbstractMultipleCachesTest { private CacheSPI first; private CacheSPI second; protected void createCaches() throws Throwable { first = createCache(Configuration.CacheMode.REPL_SYNC); second = createCache(Configuration.CacheMode.REPL_SYNC); TestingUtil.blockUntilViewReceived(first, 2, 10000); registerCaches(first, second); } public void testRemoteInvalidStateInvocations() throws Exception { 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(second); cr1.state = CacheStatus.STOPPING; // Thanks to JBCACHE-1179, this should only log a warning and not throw an exception first.put(Fqn.ROOT, "k", "v"); } finally { ComponentRegistry cr1 = TestingUtil.extractComponentRegistry(second); cr1.state = CacheStatus.STARTED; } } public void testStopInstanceWhileOtherInstanceSends() throws Exception { final Fqn fqn = Fqn.fromString("/a"); final List running = new LinkedList(); final List exceptions = new LinkedList(); running.add(true); first.put(fqn, "k", "v"); assert "v".equals(first.get(fqn, "k")); assert "v".equals(second.get(fqn, "k")); // now kick start a thread on second 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)) second.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(); running.add(false); running.remove(true); updater.join(); for (Exception e : exceptions) throw e; } public void testRemoteInvalidStateInvocations2() throws Exception { try { // now DIRECTLY change the status of second. // 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 seconds's start() method doesn't set status to STARTED // after we attempt to change this. ComponentRegistry cr1 = TestingUtil.extractComponentRegistry(second); cr1.state = CacheStatus.STARTING; try { // This call should wait for up to StateRetrievalTimeout secs or until second has entered the STARTED state, and then barf. first.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 second to STARTED final int sleepTime = 500; new Thread() { public void run() { TestingUtil.sleepThread(sleepTime); ComponentRegistry cr1 = TestingUtil.extractComponentRegistry(second); cr1.state = CacheStatus.STARTED; } }.start(); first.put(Fqn.ROOT, "k", "v"); } finally { // reset second to running so the tearDown method can clean it up ComponentRegistry cr1 = TestingUtil.extractComponentRegistry(second); cr1.state = CacheStatus.STARTED; } } private CacheSPI createCache(Configuration.CacheMode cacheMode) { Configuration c = new Configuration(); c.setCacheMode(cacheMode); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); CacheSPI retval = (CacheSPI) new UnitTestCacheFactory().createCache(c, false, getClass()); retval.start(); return retval; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/ResourceCleanupTest.java0000644000175000017500000000457111144530753027201 0ustar moellermoellerpackage org.jboss.cache; import org.testng.annotations.BeforeSuite; import org.testng.annotations.AfterSuite; import org.jboss.cache.util.TestingUtil; import java.io.File; /** * Make sure that all files are being deleted after each test run. * * @author Mircea.Markus@jboss.com */ public class ResourceCleanupTest { @BeforeSuite public void removeTempDir() { TestingUtil.recursiveFileRemove(TestingUtil.TEST_FILES); System.out.println("Removing all the files from " + TestingUtil.TEST_FILES); File file = new File(TestingUtil.TEST_FILES); if (file.exists()) { System.err.println("!!!!!!!!!!!!! Directory '" + TestingUtil.TEST_FILES + "' should have been deleted!!!"); } else { System.out.println("Successfully removed folder: '" + TestingUtil.TEST_FILES + "'"); } } @BeforeSuite @AfterSuite public void printEnvInformation() { System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~ ENVIRONMENT INFO ~~~~~~~~~~~~~~~~~~~~~~~~~~"); String bindAddress = System.getProperty("bind.address"); System.out.println("bind.address = " + bindAddress); //todo for some funny reasons MVN ignores bind.address passed in. This is a hack.. if (bindAddress == null) { System.out.println("Setting bind.address to 127.0.0.1 as it is missing!!!"); System.setProperty("bind.address","127.0.0.1"); } System.out.println("java.runtime.version = " + System.getProperty("java.runtime.version")); System.out.println("java.runtime.name =" + System.getProperty("java.runtime.name")); System.out.println("java.vm.version = " + System.getProperty("java.vm.version")); System.out.println("java.vm.vendor = " + System.getProperty("java.vm.vendor")); System.out.println("os.name = " + System.getProperty("os.name")); System.out.println("os.version = " + System.getProperty("os.version")); System.out.println("sun.arch.data.model = " + System.getProperty("sun.arch.data.model")); System.out.println("sun.cpu.endian = " + System.getProperty("sun.cpu.endian")); System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~ ENVIRONMENT INFO ~~~~~~~~~~~~~~~~~~~~~~~~~~"); } public static void main(String[] args) { System.out.println("System.getProperties() = " + System.getProperties()); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/0000755000175000017500000000000011675221324024210 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/FIFOPolicyTest.java0000644000175000017500000002317111132110773027613 0ustar moellermoeller/* * 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.Configuration; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.EvictionController; 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 org.jboss.cache.UnitTestCacheFactory; /** * Unit tests for FIFOPolicy. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 7439 $ */ @Test(groups = {"functional"}, testName = "eviction.FIFOPolicyTest") public class FIFOPolicyTest extends EvictionTestsBase { CacheSPI cache; long wakeupIntervalMillis = 0; final String ROOT_STR = "/test"; volatile Throwable t1_ex, t2_ex; volatile boolean isTrue; int maxNodes = 50; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { initCaches(); wakeupIntervalMillis = cache.getConfiguration().getEvictionConfig().getWakeupInterval(); 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 = UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); EvictionConfig evConfig = new EvictionConfig(new EvictionRegionConfig(Fqn.ROOT, new FIFOAlgorithmConfig(maxNodes), 2000000), 0); evConfig.addEvictionRegionConfig(new EvictionRegionConfig(Fqn.fromString("/org/jboss/test/data"), new FIFOAlgorithmConfig(5))); config.setEvictionConfig(evConfig); config.setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); config.setIsolationLevel(IsolationLevel.SERIALIZABLE); cache = (CacheSPI) new UnitTestCacheFactory().createCache(config, true, getClass());// read in generic local xml } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache); cache = 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(); } } 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"; 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(); // wait a few secs for eviction to complete // TestingUtil.sleepThread(500); 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() throws InterruptedException { 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 { 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)); } // 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 } } 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); TestingUtil.sleepThread(2); } catch (Throwable e) { e.printStackTrace(); if (t1_ex == null) { isTrue = false; t1_ex = e; } } } } } public void testConcurrentPutAndEvict() throws Exception { cache.put(ROOT_STR + "/concurrentPutAndEvict", "value", 1); List putters = new ArrayList(); for (int i = 0; i < 5; i++) { MyPutter p = new MyPutter("FifoPolicyTestPutter" + i); putters.add(p); p.start(); } int counter = 0; while (true) { counter++; if (t1_ex != null) { fail("Exception generated in put() " + t1_ex); } TestingUtil.sleepThread(250); if (counter > 5) {// run for 5 seconds isTrue = false; break; } } for (MyPutter p : putters) p.join(); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/LRUAlgorithmTest.java0000755000175000017500000003215711357727733030253 0ustar moellermoellerpackage 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.RegionImpl; import org.jboss.cache.RegionManager; import org.jboss.cache.RegionManagerImpl; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.AfterMethod; 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", testName = "eviction.LRUAlgorithmTest") public class LRUAlgorithmTest extends EvictionTestsBase { RegionManager regionManager; LRUAlgorithm algorithm; LRUAlgorithmConfig config; Log log = LogFactory.getLog(LRUAlgorithm.class); @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { regionManager = new RegionManagerImpl(); config = new LRUAlgorithmConfig(); config.setTimeToLive(-1); algorithm = (LRUAlgorithm) createAndAssignToRegion("/a/b", regionManager, config); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { regionManager = null; config = null; algorithm = null; } /** * 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"); RegionImpl region = (RegionImpl) regionManager.getRegion("/a/b", true); config.setMaxNodes(1); region.registerEvictionEvent(fqn1, EvictionEvent.Type.ADD_NODE_EVENT); region.registerEvictionEvent(fqn2, EvictionEvent.Type.ADD_NODE_EVENT); algorithm.process(region.getEvictionEventQueue()); assertEquals("Queue size should be ", 1, algorithm.getEvictionQueue().getNumberOfNodes()); region.registerEvictionEvent(fqn2, EvictionEvent.Type.ADD_NODE_EVENT); region.registerEvictionEvent(fqn3, EvictionEvent.Type.ADD_NODE_EVENT); algorithm.process(region.getEvictionEventQueue()); assertEquals("Queue size should be ", 1, algorithm.getEvictionQueue().getNumberOfNodes()); } /** * maxNodes = -1 case */ public void testMaxNode1() throws EvictionException { Fqn fqn1 = Fqn.fromString("/a/b/c"); Fqn fqn2 = Fqn.fromString("/a/b/d"); RegionImpl region = (RegionImpl) regionManager.getRegion("/a/b", true); config.setMaxNodes(-1); region.registerEvictionEvent(fqn1, EvictionEvent.Type.ADD_NODE_EVENT); region.registerEvictionEvent(fqn2, EvictionEvent.Type.ADD_NODE_EVENT); algorithm.process(region.getEvictionEventQueue()); assertEquals("Queue size should be ", 2, algorithm.getEvictionQueue().getNumberOfNodes()); } /** * maxNodes = 0 case */ public void testMaxNode2() throws EvictionException { Fqn fqn1 = Fqn.fromString("/a/b/c"); Fqn fqn2 = Fqn.fromString("/a/b/d"); RegionImpl region = (RegionImpl) regionManager.getRegion("/a/b", true); config.setMaxNodes(0); region.registerEvictionEvent(fqn1, EvictionEvent.Type.ADD_NODE_EVENT); region.registerEvictionEvent(fqn2, EvictionEvent.Type.ADD_NODE_EVENT); algorithm.process(region.getEvictionEventQueue()); assertEquals("Queue size should be ", 0, algorithm.getEvictionQueue().getNumberOfNodes()); } /** * maxNodes = 1 */ public void testMaxNode3() throws EvictionException { Fqn fqn1 = Fqn.fromString("/a/b/c"); Fqn fqn2 = Fqn.fromString("/a/b/d"); Fqn fqn3 = Fqn.fromString("/a/b/e"); RegionImpl region = (RegionImpl) regionManager.getRegion("/a/b", true); config.setMaxNodes(1); region.registerEvictionEvent(fqn1, EvictionEvent.Type.ADD_NODE_EVENT); region.registerEvictionEvent(fqn2, EvictionEvent.Type.ADD_NODE_EVENT); algorithm.process(region.getEvictionEventQueue()); assertEquals("Queue size should be ", 1, algorithm.getEvictionQueue().getNumberOfNodes()); region.registerEvictionEvent(fqn2, EvictionEvent.Type.ADD_NODE_EVENT); region.registerEvictionEvent(fqn3, EvictionEvent.Type.ADD_NODE_EVENT); algorithm.process(region.getEvictionEventQueue()); 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"); RegionImpl region = (RegionImpl) regionManager.getRegion("/a/b", true); config.setMaxNodes(-1); config.setTimeToLive(-1); region.registerEvictionEvent(fqn1, EvictionEvent.Type.ADD_NODE_EVENT); region.registerEvictionEvent(fqn2, EvictionEvent.Type.ADD_NODE_EVENT); algorithm.process(region.getEvictionEventQueue()); 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"); RegionImpl region = (RegionImpl) regionManager.getRegion("/a/b", true); config.setMaxNodes(-1); config.setTimeToLive(1000); region.registerEvictionEvent(fqn1, EvictionEvent.Type.ADD_NODE_EVENT); region.registerEvictionEvent(fqn2, EvictionEvent.Type.ADD_NODE_EVENT); region.registerEvictionEvent(fqn3, EvictionEvent.Type.ADD_NODE_EVENT); algorithm.process(region.getEvictionEventQueue()); assertEquals("Queue size #1: ", 3, algorithm.getEvictionQueue().getNumberOfNodes()); TestingUtil.sleepThread(1100); algorithm.process(region.getEvictionEventQueue()); 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"); RegionImpl region = (RegionImpl) regionManager.getRegion("/a/b", true); config.setMaxNodes(-1); config.setTimeToLive(1000); region.registerEvictionEvent(fqn1, EvictionEvent.Type.ADD_NODE_EVENT); region.registerEvictionEvent(fqn2, EvictionEvent.Type.ADD_NODE_EVENT); region.registerEvictionEvent(fqn3, EvictionEvent.Type.ADD_NODE_EVENT); algorithm.process(region.getEvictionEventQueue()); assertEquals("Queue size #1: ", 3, algorithm.getEvictionQueue().getNumberOfNodes()); TestingUtil.sleepThread(1100); region.registerEvictionEvent(fqn2, EvictionEvent.Type.VISIT_NODE_EVENT); algorithm.process(region.getEvictionEventQueue()); 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"); RegionImpl region = (RegionImpl) regionManager.getRegion("/a/b", true); config.setMaxNodes(-1); config.setTimeToLive(-1); config.setMaxAge(1000); region.registerEvictionEvent(fqn1, EvictionEvent.Type.ADD_NODE_EVENT); region.registerEvictionEvent(fqn2, EvictionEvent.Type.ADD_NODE_EVENT); region.registerEvictionEvent(fqn3, EvictionEvent.Type.ADD_NODE_EVENT); algorithm.process(region.getEvictionEventQueue()); assertEquals("Queue size #1: ", 3, algorithm.getEvictionQueue().getNumberOfNodes()); TestingUtil.sleepThread(1100); algorithm.process(region.getEvictionEventQueue()); assertEquals("Queue size #2: ", 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"); RegionImpl region = (RegionImpl) regionManager.getRegion("/a/b", true); // Should have a maximum of 2 nodes. config.setMaxNodes(2); config.setTimeToLive(1000); config.setMaxAge(3000); region.registerEvictionEvent(fqn1, EvictionEvent.Type.ADD_NODE_EVENT); region.registerEvictionEvent(fqn2, EvictionEvent.Type.ADD_NODE_EVENT); region.registerEvictionEvent(fqn4, EvictionEvent.Type.ADD_NODE_EVENT); algorithm.process(region.getEvictionEventQueue()); 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.registerEvictionEvent(fqn3, EvictionEvent.Type.ADD_NODE_EVENT); algorithm.process(region.getEvictionEventQueue()); 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.registerEvictionEvent(fqn3, EvictionEvent.Type.VISIT_NODE_EVENT); algorithm.process(region.getEvictionEventQueue()); 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"); RegionImpl region = (RegionImpl) regionManager.getRegion("/a/b", true); config.setMaxNodes(2); config.setTimeToLive(1000); config.setMaxAge(3000); region.registerEvictionEvent(fqn1, EvictionEvent.Type.ADD_NODE_EVENT); region.registerEvictionEvent(fqn2, EvictionEvent.Type.ADD_NODE_EVENT); region.registerEvictionEvent(fqn2, EvictionEvent.Type.REMOVE_NODE_EVENT); algorithm.process(region.getEvictionEventQueue()); 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.registerEvictionEvent(fqn3, EvictionEvent.Type.ADD_NODE_EVENT); algorithm.process(region.getEvictionEventQueue()); numNodesInQueue = eq.getNumberOfNodes(); assert 1 == numNodesInQueue : "Queue size #2: expected 1 but was " + numNodesInQueue; TestingUtil.sleepThread(3100); region.registerEvictionEvent(fqn3, EvictionEvent.Type.VISIT_NODE_EVENT); algorithm.process(region.getEvictionEventQueue()); numNodesInQueue = eq.getNumberOfNodes(); assert 0 == numNodesInQueue : "Queue size #3: expected 0 but was " + numNodesInQueue; } public void testEvictionSortOrder() throws EvictionException { RegionImpl region = (RegionImpl) regionManager.getRegion("/a/b", true); config.setMaxAge(1000000); config.setMaxNodes(0); config.setTimeToLive(1000000); for (int i = 0; i < 100; i++) { Fqn fqn = Fqn.fromString("/a/b/" + Integer.toString(i)); region.registerEvictionEvent(fqn, EvictionEvent.Type.ADD_NODE_EVENT); } algorithm.process(region.getEvictionEventQueue()); for (int i = 0; i < 100; i++) { Fqn fqn = Fqn.fromString("/a/b/" + Integer.toString(i)); if (i % 2 == 0) { region.registerEvictionEvent(fqn, EvictionEvent.Type.VISIT_NODE_EVENT); } } algorithm.process(region.getEvictionEventQueue()); 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.registerEvictionEvent(fqn, EvictionEvent.Type.ADD_NODE_EVENT); } algorithm.process(region.getEvictionEventQueue()); long lastCreateTimestamp = 0; while ((ne = queue.getFirstMaxAgeNodeEntry()) != null) { assertTrue(ne.getCreationTimeStamp() >= lastCreateTimestamp); lastCreateTimestamp = ne.getCreationTimeStamp(); queue.removeNodeEntry(ne); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/MRUAlgorithmTest.java0000644000175000017500000001201111111047320030203 0ustar moellermoeller/* * 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.RegionImpl; import org.jboss.cache.RegionManager; import org.jboss.cache.RegionManagerImpl; import org.testng.annotations.AfterMethod; import static org.testng.AssertJUnit.*; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Unit tests for MRUAlgorithm. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 7168 $ */ @Test(groups = {"functional"}, sequential = true, testName = "eviction.MRUAlgorithmTest") public class MRUAlgorithmTest extends EvictionTestsBase { MRUAlgorithm algorithm; RegionManager regionManager; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { regionManager = new RegionManagerImpl(); MRUAlgorithmConfig config = new MRUAlgorithmConfig(); config.setMaxNodes(0); algorithm = (MRUAlgorithm) createAndAssignToRegion("/a/b", regionManager, config); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { regionManager = null; algorithm = null; } 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"); RegionImpl region = (RegionImpl) regionManager.getRegion("/a/b", true); MRUAlgorithmConfig config = (MRUAlgorithmConfig) region.getEvictionRegionConfig().getEvictionAlgorithmConfig(); config.setMaxNodes(1); region.registerEvictionEvent(fqn1, EvictionEvent.Type.ADD_NODE_EVENT); region.registerEvictionEvent(fqn2, EvictionEvent.Type.ADD_NODE_EVENT); region.registerEvictionEvent(fqn3, EvictionEvent.Type.ADD_NODE_EVENT); algorithm.process(region.getEvictionEventQueue()); 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.registerEvictionEvent(fqn, EvictionEvent.Type.ADD_NODE_EVENT); } algorithm.process(region.getEvictionEventQueue()); 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"); RegionImpl region = (RegionImpl) regionManager.getRegion("/a/b", true); MRUAlgorithmConfig config = (MRUAlgorithmConfig) region.getEvictionRegionConfig().getEvictionAlgorithmConfig(); config.setMaxNodes(8); region.registerEvictionEvent(fqn1, EvictionEvent.Type.ADD_NODE_EVENT); region.registerEvictionEvent(fqn2, EvictionEvent.Type.ADD_NODE_EVENT); region.registerEvictionEvent(fqn3, EvictionEvent.Type.ADD_NODE_EVENT); region.registerEvictionEvent(fqn4, EvictionEvent.Type.ADD_NODE_EVENT); region.registerEvictionEvent(fqn5, EvictionEvent.Type.ADD_NODE_EVENT); region.registerEvictionEvent(fqn6, EvictionEvent.Type.ADD_NODE_EVENT); region.registerEvictionEvent(fqn7, EvictionEvent.Type.ADD_NODE_EVENT); region.registerEvictionEvent(fqn8, EvictionEvent.Type.ADD_NODE_EVENT); algorithm.process(region.getEvictionEventQueue()); assertEquals(8, algorithm.getEvictionQueue().getNumberOfNodes()); region.registerEvictionEvent(fqn9, EvictionEvent.Type.ADD_NODE_EVENT); region.registerEvictionEvent(fqn10, EvictionEvent.Type.ADD_NODE_EVENT); // Thread.sleep(5000); assertEquals(8, algorithm.getEvictionQueue().getNumberOfNodes()); region.registerEvictionEvent(fqn2, EvictionEvent.Type.ADD_NODE_EVENT); region.registerEvictionEvent(fqn4, EvictionEvent.Type.ADD_NODE_EVENT); algorithm.process(region.getEvictionEventQueue()); 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-3.2.8.GA/src/test/java/org/jboss/cache/eviction/ReplicatedLRUPolicyTest.java0000755000175000017500000001310011136027056031526 0ustar moellermoellerpackage org.jboss.cache.eviction; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.UnitTestConfigurationFactory; 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 org.jboss.cache.util.internals.EvictionController; import org.jboss.cache.util.internals.EvictionWatcher; 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 org.jboss.cache.UnitTestCacheFactory; /** * @author Ben Wang, Feb 11, 2004 */ @Test(groups = {"functional"}, testName = "eviction.ReplicatedLRUPolicyTest") public class ReplicatedLRUPolicyTest extends EvictionTestsBase { CacheSPI cache1, cache2; EvictionListener listener = new EvictionListener(); @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache1 = (CacheSPI) new UnitTestCacheFactory().createCache(UnitTestConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC, true), false, getClass()); cache1.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cache1.getConfiguration().setUseRegionBasedMarshalling(true); cache1.getConfiguration().getEvictionConfig().setWakeupInterval(0); cache1.start(); cache1.getNotifier().addCacheListener(listener); listener.resetCounter(); cache2 = (CacheSPI) new UnitTestCacheFactory().createCache(cache1.getConfiguration().clone(), false, getClass()); cache2.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cache2.getConfiguration().setUseRegionBasedMarshalling(true); cache2.getConfiguration().getEvictionConfig().setWakeupInterval(0); cache2.start(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache1, cache2); cache1 = null; cache2 = null; } /** * Test local eviction policy that failed for eviction event. */ public void testBasic() throws Exception { String rootStr = "/org/jboss/test/data/"; LRUAlgorithmConfig cfg = (LRUAlgorithmConfig) cache1.getConfiguration().getEvictionConfig().getEvictionRegionConfig(rootStr).getEvictionAlgorithmConfig(); cfg.setMaxAge(0); cfg.setTimeToLive(0); cfg = (LRUAlgorithmConfig) cache2.getConfiguration().getEvictionConfig().getEvictionRegionConfig(rootStr).getEvictionAlgorithmConfig(); cfg.setMaxAge(-1, TimeUnit.SECONDS); cfg.setTimeToLive(-1, TimeUnit.SECONDS); cfg.setMaxNodes(200); String str = rootStr + "0"; Fqn fqn = Fqn.fromString(str); cache1.put(str, str, str); new EvictionController(cache1).startEviction(); Object node = cache1.peek(fqn, false); assertNull("Node should be evicted already ", node); assertEquals("Eviction counter ", 1, listener.getCounter()); String val = (String) cache2.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/"; LRUAlgorithmConfig cfg = (LRUAlgorithmConfig) cache2.getConfiguration().getEvictionConfig().getEvictionRegionConfig(rootStr).getEvictionAlgorithmConfig(); cfg.setMaxAge(60, TimeUnit.SECONDS); cfg.setTimeToLive(360, TimeUnit.SECONDS); cfg.setMaxNodes(200); for (int i = 0; i < 10; i++) { String str = rootStr + i; Fqn fqn = Fqn.fromString(str); cache1.put(fqn, str, str); } new EvictionController(cache1).startEviction(); String val = (String) cache1.get(rootStr + "3", rootStr + "3"); assertNull("DataNode should be evicted already ", val); val = (String) cache2.get(rootStr + "3", rootStr + "3"); assertNotNull("DataNode should not be evicted here ", val); } public void testEvictionReplication() throws Exception { String rootStr = "/org/jboss/test/data/"; LRUAlgorithmConfig cfg = (LRUAlgorithmConfig) cache2.getConfiguration().getEvictionConfig().getEvictionRegionConfig(rootStr).getEvictionAlgorithmConfig(); cfg.setMaxAge(60, TimeUnit.SECONDS); cfg.setTimeToLive(360, TimeUnit.SECONDS); for (int i = 0; i < 10; i++) { String str = rootStr + i; Fqn fqn = Fqn.fromString(str); cache1.put(fqn, str, str); } String str = rootStr + "7"; Fqn fqn = Fqn.fromString(str); cache1.get(fqn, str); new EvictionController(cache1).startEviction(); String val = (String) cache1.get(rootStr + "3", rootStr + "3"); assertNull("DataNode should be empty ", val); val = (String) cache2.get(rootStr + "7", rootStr + "7"); assertNotNull("DataNode should not be null", val); } @CacheListener public class EvictionListener { int counter = 0; public int getCounter() { return counter; } public void resetCounter() { counter = 0; } @NodeEvicted public void nodeEvicted(Event e) { if (e.isPre()) counter++; } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/EvictionQueueListTest.java0000644000175000017500000001516111120421353031325 0ustar moellermoeller/* * 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.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 7289 $ */ @Test(groups = {"functional"}, sequential = true, testName = "eviction.EvictionQueueListTest") public class EvictionQueueListTest { EvictionQueueList list; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { list = new EvictionQueueList(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { list = null; } 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()); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/BaseEvictionAlgorithmTest.java0000644000175000017500000001353011111047320032122 0ustar moellermoeller/* * 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 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.RegionImpl; import org.jboss.cache.RegionManager; import org.jboss.cache.RegionManagerImpl; import org.jboss.cache.RegionRegistry; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.config.EvictionAlgorithmConfig; import org.jboss.cache.config.EvictionRegionConfig; import static org.testng.AssertJUnit.fail; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; 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; /** * Tests BaseEvictionAlgorithm class. * * @author Galder Zamarreno */ @Test(groups = "functional", testName = "eviction.BaseEvictionAlgorithmTest") public class BaseEvictionAlgorithmTest { private static final Log log = LogFactory.getLog(BaseEvictionAlgorithmTest.class); private RegionManager regionManager; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { RegionManagerImpl rmi = new RegionManagerImpl(); rmi.injectDependencies(null, new Configuration(), null, null, null, new RegionRegistry()); regionManager = rmi; } 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.setEvictionRegionConfig(new EvictionRegionConfig(region.getFqn(), new MockEvictionAlgorithmConfig())); for (int i = 0; i < (recycleQueueCapacity + 1); i++) { Fqn fqn = Fqn.fromString("/a/b/c/" + Integer.toString(i + 1)); region.registerEvictionEvent(fqn, EvictionEvent.Type.ADD_NODE_EVENT); } ExecutorService executor = Executors.newSingleThreadExecutor(); Future future = executor.submit(new ProcessEvictionRegion((RegionImpl) 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 { private static MockEvictionAlgorithm singleton; private MockEvictionAlgorithm(int recycleQueueCapacity) { recycleQueue = new LinkedBlockingQueue(recycleQueueCapacity); singleton = this; } public static MockEvictionAlgorithm getInstance() { return singleton; } @Override protected EvictionQueue setupEvictionQueue() throws EvictionException { return new LRUQueue(); } @Override protected boolean shouldEvictNode(NodeEntry ne) { /* all node entries need evicting */ return true; } public Class getConfigurationClass() { return null; } } public static class MockEvictionAlgorithmConfig implements EvictionAlgorithmConfig { public void reset() { /* no op */ } public String getEvictionAlgorithmClassName() { return MockEvictionAlgorithm.class.getName(); } public void validate() throws ConfigurationException { /* no op */ } public EvictionAlgorithmConfig clone() throws CloneNotSupportedException { return (EvictionAlgorithmConfig) super.clone(); } } public class ProcessEvictionRegion implements Callable { private RegionImpl region; private EvictionAlgorithm algorithm; public ProcessEvictionRegion(RegionImpl region, EvictionAlgorithm algorithm) { this.region = region; this.algorithm = algorithm; } public Void call() throws Exception { try { algorithm.process(region.getEvictionEventQueue()); } catch (EvictionException e) { log.error("Eviction exception reported", e); fail("Eviction exception reported" + e); } return null; } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/MRUPolicyTest.java0000644000175000017500000001460111132110773027531 0ustar moellermoeller/* * 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.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.EvictionController; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Unit tests for MRUPolicy. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 7439 $ */ @Test(groups = {"functional"}, testName = "eviction.MRUPolicyTest") public class MRUPolicyTest { CacheSPI cache; long wakeupIntervalMillis = 0; final String ROOT_STR = "/test"; volatile Throwable t1_ex, t2_ex; volatile boolean isTrue; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { initCaches(); wakeupIntervalMillis = cache.getConfiguration().getEvictionConfig().getWakeupInterval(); 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 { TestingUtil.killCaches(cache); cache = null; } private void initCaches() { Configuration config = UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); EvictionConfig evConfig = config.getEvictionConfig(); evConfig.setWakeupInterval(200); // root ERC evConfig.setDefaultEvictionRegionConfig(new EvictionRegionConfig(Fqn.ROOT, new MRUAlgorithmConfig(100), 200000)); // new region ERC evConfig.addEvictionRegionConfig(new EvictionRegionConfig(Fqn.fromString("/org/jboss/test/data"), new MRUAlgorithmConfig(6))); config.setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); config.setIsolationLevel(IsolationLevel.SERIALIZABLE); cache = (CacheSPI) new UnitTestCacheFactory().createCache(config, getClass()); } 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"); EvictionController ec = new EvictionController(cache); ec.startEviction(); 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")); ec.startEviction(); 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"); EvictionController ec = new EvictionController(cache); ec.startEviction(); 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"); ec.startEviction(); 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) { isTrue = false; 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 < 5; i++) { new MyPutter("MRUPolicyTestPutter" + i).start(); } int counter = 0; while (true) { counter++; if (t1_ex != null) { fail("Exception generated in put() " + t1_ex); } TestingUtil.sleepThread(250); if (counter > 5) {// run for 5 seconds isTrue = false; break; } } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/ConcurrentEvictionTest.java0000644000175000017500000001016711131613416031535 0ustar moellermoeller/* * 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.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.UnitTestConfigurationFactory; import org.jboss.cache.transaction.DummyTransactionManagerLookup; 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 org.jboss.cache.UnitTestCacheFactory; /** * Tests cache behavior in the presence of concurrent passivation. * * @author Brian Stansberry * @version $Revision: 7422 $ */ @Test(groups = {"functional"}, testName = "eviction.ConcurrentEvictionTest") public class ConcurrentEvictionTest { private Cache cache; private long wakeupIntervalMillis = 0; private String tmpDir = TestingUtil.TEST_FILES; private String cacheLoaderDir = "JBossCacheFileCacheLoader"; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { initCaches(); wakeupIntervalMillis = cache.getConfiguration().getEvictionConfig().getWakeupInterval(); if (wakeupIntervalMillis < 0) { fail("testEviction(): eviction thread wake up interval is illegal " + wakeupIntervalMillis); } } void initCaches() throws Exception { UnitTestCacheFactory factory = new UnitTestCacheFactory(); Configuration conf = UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); conf.setEvictionConfig(buildEvictionConfig()); conf.setCacheLoaderConfig(buildCacheLoaderConfig()); conf.setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cache = factory.createCache(conf, true, getClass());// 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.testloaders.DummyInMemoryCacheLoader"); iclc.setAsync(false); iclc.setFetchPersistentState(true); iclc.setIgnoreModifications(false); cacheLoaderConfig.addIndividualCacheLoaderConfig(iclc); return cacheLoaderConfig; } private EvictionConfig buildEvictionConfig() { return new EvictionConfig( new EvictionRegionConfig( Fqn.ROOT, new LRUAlgorithmConfig(1000000, 5000) ), 200); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache); } 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); assertNotNull("found value under Fqn " + fqn + " and key " + i, cache.get(fqn, i)); } } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/OptimisticEvictionTest.java0000644000175000017500000000755711131613416031550 0ustar moellermoellerpackage org.jboss.cache.eviction; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.interceptors.EvictionInterceptor; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.EvictionController; 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 java.util.Iterator; import java.util.List; import java.util.concurrent.TimeUnit; import org.jboss.cache.UnitTestCacheFactory; /** * 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"}, testName = "eviction.OptimisticEvictionTest") public class OptimisticEvictionTest extends EvictionTestsBase { private CacheSPI cache; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { Configuration config = UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); config.setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); config.setEvictionConfig(buildEvictionConfig()); cache = (CacheSPI) new UnitTestCacheFactory().createCache(config, getClass()); } private EvictionConfig buildEvictionConfig() throws Exception { EvictionConfig result = new EvictionConfig(new EvictionRegionConfig(Fqn.ROOT, new LRUAlgorithmConfig(0, 0, 10)), 200); result.addEvictionRegionConfig(new EvictionRegionConfig(Fqn.fromString("/testingRegion"), new LRUAlgorithmConfig(0, 0, 10))); result.addEvictionRegionConfig(new EvictionRegionConfig(Fqn.fromString("/timeBased"), new LRUAlgorithmConfig(1000, 1000, 0))); return result; } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache); cache = null; } public void testEvictionOccurence() throws Exception { cache.put("/timeBased/test", "key", "value"); assertTrue(cache.exists("/timeBased/test")); TestingUtil.sleepThread(2000); new EvictionController(cache).startEviction(); assertTrue(!cache.exists("/timeBased/test")); } public void testInterceptorChain() throws Exception { List interceptors = cache.getInterceptorChain(); 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 assert waitForEviction(cache, 30, TimeUnit.SECONDS, parent); // wait for this twice since the first time will only clear the parent's contents since a child exists. assert waitForEviction(cache, 30, TimeUnit.SECONDS, parent); assertFalse("Parent completely removed", cache.getRoot().hasChild(parent)); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/ProgrammaticLRUPolicyTest.java0000644000175000017500000001627111131613416032104 0ustar moellermoeller/* * 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.CacheSPI; 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.UnitTestConfigurationFactory; import org.jboss.cache.lock.IsolationLevel; 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 org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.EvictionController; /** * Unit tests for programmatic configuration of LRU policy * * @author Ben Wang, Oct, 2006 * @version $Revision: 7422 $ */ @Test(groups = {"functional"}, sequential = true, testName = "eviction.ProgrammaticLRUPolicyTest") public class ProgrammaticLRUPolicyTest extends EvictionTestsBase { CacheSPI cache; EvictionController evController; long wakeupIntervalMillis = 0; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { initCache(); wakeupIntervalMillis = cache.getConfiguration().getEvictionConfig().getWakeupInterval(); evController = new EvictionController(cache); } private void initCache() { Configuration conf = UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); UnitTestCacheFactory instance = new UnitTestCacheFactory(); cache = (CacheSPI) instance.createCache(conf, false, getClass()); EvictionConfig erc = new EvictionConfig(new EvictionRegionConfig(Fqn.ROOT, new LRUAlgorithmConfig(0, 0, 10)), -1); conf.setEvictionConfig(erc); cache.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cache.getConfiguration().setIsolationLevel(IsolationLevel.SERIALIZABLE); cache.create(); cache.start(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache); cache = null; } private void addStringBasedRegion() throws Exception { LRUAlgorithmConfig lru = new LRUAlgorithmConfig(150, 0, 1000); EvictionRegionConfig regConfig = new EvictionRegionConfig(Fqn.fromString("/dummy"), lru); RegionManager regionManager = cache.getRegionManager(); EvictionConfig topConfig = cache.getConfiguration().getEvictionConfig(); topConfig.addEvictionRegionConfig(regConfig); regionManager.setEvictionConfig(topConfig); // Fqn is the region name regionManager.getRegion("/programmatic", true).setEvictionRegionConfig(regConfig); } 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); evController.startEviction(); val = (String) cache.get(rootStr + "3", rootStr + "3"); assertNull("DataNode should be empty ", val); } private void addObjectBasedRegion() throws Exception { LRUAlgorithmConfig lru = new LRUAlgorithmConfig(150, 1000); EvictionRegionConfig regConfig = new EvictionRegionConfig(Fqn.fromElements(1), lru); RegionManager regionManager = cache.getRegionManager(); EvictionConfig topConfig = cache.getConfiguration().getEvictionConfig(); topConfig.addEvictionRegionConfig(regConfig); regionManager.setEvictionConfig(topConfig); regionManager.getRegion(Fqn.fromElements(1), true).setEvictionRegionConfig(regConfig); } 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); } evController.startEviction(); 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(); Fqn rootfqn = Fqn.fromElements((Integer) 1); for (int i = 0; i < 10; i++) { Fqn fqn = Fqn.fromRelativeElements(rootfqn, i); try { cache.put(fqn, i, i); } 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 not be empty ", val); } catch (Exception e) { e.printStackTrace(); fail("Failed to get" + e); } Integer in = 3; Fqn fqn = Fqn.fromRelativeElements(rootfqn, in); Thread.sleep(1500);//max age is 1000, so this should expire evController.startEviction(); try { Object val = cache.get(fqn, in); assertNull("DataNode should be empty ", val); } catch (Exception e) { e.printStackTrace(); fail("Failed to get" + e); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/FIFOQueueTest.java0000644000175000017500000001254311121730272027441 0ustar moellermoeller/* * 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.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Unit tests for FIFOQueue. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 7332 $ */ @Test(groups = {"functional"}, sequential = true, testName = "eviction.FIFOQueueTest") public class FIFOQueueTest { private static final int CAPACITY = EvictionConfig.EVENT_QUEUE_SIZE_DEFAULT/4; private FIFOQueue queue; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { queue = new FIFOQueue(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { queue = null; } 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 { for (int i = 0; i < CAPACITY; i++) { queue.addNodeEntry(new NodeEntry("/test/" + Integer.toString(i))); } assertEquals(CAPACITY, queue.getNumberOfNodes()); 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))); } assertEquals(0, queue.getNumberOfNodes()); for (int i = 0; i < CAPACITY; i++) { queue.addNodeEntry(new NodeEntry("/test/" + Integer.toString(i))); } assertEquals(CAPACITY, queue.getNumberOfNodes()); NodeEntry ne; while ((ne = queue.getFirstNodeEntry()) != null) { queue.removeNodeEntry(ne); } 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-3.2.8.GA/src/test/java/org/jboss/cache/eviction/LongTransactionEvictionsTest.java0000644000175000017500000001226411435676533032723 0ustar moellermoellerpackage org.jboss.cache.eviction; 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.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.testng.AssertJUnit; import org.testng.annotations.Test; import static org.testng.AssertJUnit.*; @Test(groups = {"functional"}, testName = "eviction.LongTransactionEvictionsTest") public class LongTransactionEvictionsTest { public void testFIFOAlgoWithLongTx() throws Exception { // Create the cache CacheFactory factory = new DefaultCacheFactory(); final Cache cache = factory.createCache(false); // Configure the Eviction EvictionConfig evictionConfig = new EvictionConfig(); cache.getConfiguration().setEvictionConfig(evictionConfig); // Set the LRUAlgorithm as eviction algorithm int maxNodes = 1; long timeout = 4000; FIFOAlgorithmConfig fifo = new FIFOAlgorithmConfig(maxNodes); fifo.setMinTimeToLive(timeout); EvictionRegionConfig erConfig = new EvictionRegionConfig(Fqn.ROOT, fifo); evictionConfig.setDefaultEvictionRegionConfig(erConfig); // Set the wakeup interval long wakeupInterval = 1000; evictionConfig.setWakeupInterval(wakeupInterval); cache.getConfiguration().setInvocationBatchingEnabled(true); // Start the cache cache.start(); cache.startBatch(); cache.put(Fqn.fromElements("a"), "a", "value"); // Add a pause before committing long pause = wakeupInterval + 1000; Thread.sleep(pause); cache.endBatch(true); AssertJUnit.assertEquals(1, ((CacheSPI)cache).getNumberOfNodes()); cache.startBatch(); cache.put(Fqn.fromElements("b"), "b", "value"); // Add a pause before committing pause = wakeupInterval + 1000; assertEquals(1, ((CacheSPI)cache).getNumberOfNodes()); Thread.sleep(pause); cache.endBatch(true); assertEquals(2, ((CacheSPI)cache).getNumberOfNodes()); Thread.sleep(timeout + pause); // We expect to have 1 entry into the cache there assertEquals(1, ((CacheSPI)cache).getNumberOfNodes()); } public void testLRUAlgoWithLongTx() throws Exception { // Create the cache CacheFactory factory = new DefaultCacheFactory(); final Cache cache = factory.createCache(false); // Configure the Eviction EvictionConfig evictionConfig = new EvictionConfig(); cache.getConfiguration().setEvictionConfig(evictionConfig); // Set the LRUAlgorithm as eviction algorithm int maxNodes = 5; long timeout = 4000; LRUAlgorithmConfig lru = new LRUAlgorithmConfig(1000, timeout, maxNodes); EvictionRegionConfig erConfig = new EvictionRegionConfig(Fqn.ROOT, lru); evictionConfig.setDefaultEvictionRegionConfig(erConfig); // Set the wakeup interval long wakeupInterval = 1000; evictionConfig.setWakeupInterval(wakeupInterval); cache.getConfiguration().setInvocationBatchingEnabled(true); // Start the cache cache.start(); cache.startBatch(); Fqn fqn = Fqn.fromElements("key"); cache.put(fqn, "key", "value"); // Add a pause before committing long pause = wakeupInterval + 1000; Thread.sleep(pause); cache.endBatch(true); Thread.sleep(timeout + pause); // We expect to have an empty cache there assertEquals(0, ((CacheSPI)cache).getNumberOfNodes()); } public void testExpAlgoWithLongTx() throws Exception { // Create the cache CacheFactory factory = new DefaultCacheFactory(); final Cache cache = factory.createCache(false); // Configure the Eviction EvictionConfig evictionConfig = new EvictionConfig(); cache.getConfiguration().setEvictionConfig(evictionConfig); // Set the ExpirationAlgorithm as eviction algorithm int maxNodes = 5; ExpirationAlgorithmConfig ea = new ExpirationAlgorithmConfig(); ea.setMaxNodes(maxNodes); EvictionRegionConfig erConfig = new EvictionRegionConfig(Fqn.ROOT, ea); evictionConfig.setDefaultEvictionRegionConfig(erConfig); // Set the wakeup interval long wakeupInterval = 1000; evictionConfig.setWakeupInterval(wakeupInterval); cache.getConfiguration().setInvocationBatchingEnabled(true); // Start the cache cache.start(); cache.startBatch(); long timeout = 4000; Fqn fqn = Fqn.fromElements("key"); Long future = new Long(System.currentTimeMillis() + timeout); cache.put(fqn, ExpirationAlgorithmConfig.EXPIRATION_KEY, future); // Add a pause before committing long pause = wakeupInterval + 1000; Thread.sleep(pause); cache.endBatch(true); Thread.sleep(timeout + pause); // We expect to have an empty cache there assertEquals(0, ((CacheSPI)cache).getNumberOfNodes()); } }jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/ElementSizeQueueTest.java0000644000175000017500000001314011120421353031130 0ustar moellermoeller/* * 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.*; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.List; import java.util.Set; import org.testng.annotations.AfterMethod; /** * @author Daniel Huang * @version $Revision: 7289 $ */ @Test(groups = {"functional"}, sequential = true, testName = "eviction.ElementSizeQueueTest") public class ElementSizeQueueTest { private ElementSizeQueue queue; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { queue = new ElementSizeQueue(); } @AfterMethod(alwaysRun = true) public void tearDown() { queue = null; } public void testQueue() throws Exception { for (int i = 0; i < 500; i++) { queue.addNodeEntry(new NodeEntry("/a/b/c/" + Integer.toString(i))); } 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. int k = 0; for (NodeEntry ne : queue) { assertEquals("/a/b/c/" + Integer.toString(k), ne.getFqn().toString()); if (k % 2 == 0) { ne.setNumberOfElements(k); } k++; } queue.resortEvictionQueue(); k = 0; for (NodeEntry ne : queue) { // the first 250 elements should have (250 - 1 - n) * 2 elements. The rest should have 0 elements. int expectedElements = 0; if (k < 250) { expectedElements = (250 - 1 - k) * 2; } assertEquals("k is " + k, expectedElements, 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))); } int i = 0; for (NodeEntry ne : queue) { if (i % 2 == 0) { queue.removeNodeEntry(ne); } i++; } assertEquals(2500, queue.getNumberOfNodes()); Set removalQueue = queue.getRemovalQueue(); List evictionList = queue.getEvictionList(); assertEquals(2500, removalQueue.size()); for (NodeEntry ne : removalQueue) { 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-3.2.8.GA/src/test/java/org/jboss/cache/eviction/NullEvictionPolicyTest.java0000644000175000017500000000676011131613416031511 0ustar moellermoellerpackage org.jboss.cache.eviction; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.jboss.cache.util.internals.EvictionWatcher; 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 java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.util.TestingUtil; @Test(groups = {"functional"}, sequential = true, testName = "eviction.NullEvictionPolicyTest") public class NullEvictionPolicyTest extends EvictionTestsBase { CacheSPI cache; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache = null; } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache); cache = null; } /** * 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() throws InterruptedException { Configuration config = UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); EvictionConfig evConfig = new EvictionConfig(new EvictionRegionConfig(Fqn.ROOT, new NullEvictionAlgorithmConfig(), 200000), 200); evConfig.addEvictionRegionConfig(new EvictionRegionConfig(Fqn.fromString("/test"), new NullEvictionAlgorithmConfig())); evConfig.addEvictionRegionConfig(new EvictionRegionConfig(Fqn.fromString("/lru"), new LRUAlgorithmConfig(1000, 10000))); config.setEvictionConfig(evConfig); config.setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); config.setIsolationLevel(IsolationLevel.SERIALIZABLE); cache = (CacheSPI) new UnitTestCacheFactory().createCache(config, getClass()); String dfltRootStr = "/a/"; String testRootStr = "/test/"; String lruRootStr = "/lru/"; List toBeEvicted = new ArrayList(); for (int i = 0; i < 20; i++) toBeEvicted.add(Fqn.fromString(lruRootStr + i)); EvictionWatcher watcher = new EvictionWatcher(cache, toBeEvicted); 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"); } assert watcher.waitForEviction(30, TimeUnit.SECONDS); 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")); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/LFUQueueTest.java0000644000175000017500000001523011111047320027332 0ustar moellermoeller/* * 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.*; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.List; import java.util.Set; import org.testng.annotations.AfterMethod; /** * Unit tests for LFUQueue. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 7168 $ */ @Test(groups = {"functional"}, sequential = true, testName = "eviction.LFUQueueTest") public class LFUQueueTest { private LFUQueue queue; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { queue = new LFUQueue(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { queue = null; } 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()); int k = 0; for (NodeEntry entry : queue) { assertEquals("/a/b/c/" + Integer.toString(k), entry.getFqn().toString()); if (k % 2 == 0) { entry.setNumberOfNodeVisits(entry.getNumberOfNodeVisits() + 1); } k++; } queue.resortEvictionQueue(); assertEquals("/a/b/c/1", queue.getFirstNodeEntry().getFqn().toString()); // now check the sort order. k = 0; for (NodeEntry entry : queue) { if (k < 250) { assertEquals(0, entry.getNumberOfNodeVisits()); } else { assertEquals(1, entry.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; for (NodeEntry entry : queue) { if (k <= 125) { assertEquals(1, entry.getNumberOfNodeVisits()); } else { assertEquals(2, entry.getNumberOfNodeVisits()); } k++; } } public void testPrune() throws Exception { for (int i = 0; i < 500; i++) { queue.addNodeEntry(new NodeEntry("/a/b/c/" + Integer.toString(i))); } int i = 0; for (NodeEntry ne : queue) { if (i % 2 == 0) { queue.removeNodeEntry(ne); } i++; } assertEquals(250, queue.getNumberOfNodes()); Set removalQueue = queue.getRemovalQueue(); List evictionList = queue.getEvictionList(); assertEquals(250, removalQueue.size()); for (NodeEntry ne : removalQueue) { 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(500, evictionList.size()); queue.prune(); assertEquals(0, removalQueue.size()); assertEquals(250, 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-3.2.8.GA/src/test/java/org/jboss/cache/eviction/DisabledEvictionThreadTest.java0000644000175000017500000000355011131613416032250 0ustar moellermoellerpackage org.jboss.cache.eviction; import org.jboss.cache.Cache; import org.jboss.cache.RegionManager; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.Test; @Test(groups = "functional", testName = "eviction.DisabledEvictionThreadTest") public class DisabledEvictionThreadTest { public void testDisabledEvictionTimer() { Cache c = null; try { Configuration cfg = UnitTestConfigurationFactory.createConfiguration(CacheMode.LOCAL, true); cfg.getEvictionConfig().setWakeupInterval(0); c = new UnitTestCacheFactory().createCache(cfg, getClass()); ComponentRegistry cr = TestingUtil.extractComponentRegistry(c); RegionManager rm = cr.getComponent(RegionManager.class); EvictionTimerTask ett = rm.getEvictionTimerTask(); assert ett.scheduledExecutor == null; } finally { TestingUtil.killCaches(c); } } public void testControl() { Cache c = null; try { Configuration cfg = UnitTestConfigurationFactory.createConfiguration(CacheMode.LOCAL, true); cfg.getEvictionConfig().setWakeupInterval(10); c = new UnitTestCacheFactory().createCache(cfg, getClass()); ComponentRegistry cr = TestingUtil.extractComponentRegistry(c); RegionManager rm = cr.getComponent(RegionManager.class); EvictionTimerTask ett = rm.getEvictionTimerTask(); assert ett.scheduledExecutor != null; } finally { TestingUtil.killCaches(c); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/NullEvictionConfigTest.java0000644000175000017500000000250711111047320031443 0ustar moellermoeller/* * 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.parsing.XmlConfigHelper; import org.jboss.cache.config.parsing.element.EvictionElementParser; import org.testng.annotations.Test; import org.w3c.dom.Element; /** * Unit tests for NullEvictionPolicyConfig. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 7168 $ */ @Test(groups = "unit", sequential = false, testName = "eviction.NullEvictionConfigTest") 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" + ""; testConfigBlock(xml); xml = "\n"; testConfigBlock(xml); } private void testConfigBlock(String xml) throws Exception { Element element = XmlConfigHelper.stringToElementInCoreNS(xml); NullEvictionAlgorithmConfig config = new NullEvictionAlgorithmConfig(); EvictionElementParser.parseEvictionPolicyConfig(element, config); config.validate(); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/ExpirationPolicyTest.java0000755000175000017500000001023211364010163031206 0ustar moellermoeller/* * 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.Fqn; import org.jboss.cache.Region; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.EvictionController; 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; /** * Unit tests for {@link org.jboss.cache.eviction.ExpirationAlgorithm}. * * @author Elias Ross * @version $Revision: 8393 $ */ @Test(groups = {"functional"}, sequential = true, testName = "eviction.ExpirationPolicyTest") public class ExpirationPolicyTest extends EvictionTestsBase { private static final Log log = LogFactory.getLog(ExpirationPolicyTest.class); private CacheSPI cache; private EvictionController ec; 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(); ExpirationAlgorithmConfig eAC = new ExpirationAlgorithmConfig(); EvictionRegionConfig eRC = new EvictionRegionConfig(Fqn.ROOT, eAC); EvictionConfig econf = new EvictionConfig(eRC); econf.setWakeupInterval(0); conf.setEvictionConfig(econf); cache = (CacheSPI) new UnitTestCacheFactory().createCache(conf, false, getClass()); cache.start(); ec = new EvictionController(cache); future = System.currentTimeMillis() + 1500; past = System.currentTimeMillis() - 1500; } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache); cache = null; } public void testEviction() throws Exception { cache.put(fqn1, ExpirationAlgorithmConfig.EXPIRATION_KEY, future); cache.put(fqn2, ExpirationAlgorithmConfig.EXPIRATION_KEY, past); cache.put(fqn3, ExpirationAlgorithmConfig.EXPIRATION_KEY, future); cache.put(fqn4, "foo", "bar"); ec.startEviction(); assertNotNull(cache.getNode(fqn1)); assertNull(cache.getNode(fqn2)); assertNotNull(cache.getNode(fqn3)); assertNotNull(cache.getNode(fqn4)); Thread.sleep(2000); ec.startEviction(); log.info("should remove 1 and 3 now"); assertNull(cache.getNode(fqn1)); assertNull(cache.getNode(fqn3)); } public void testAddRemoveEviction() throws Exception { cache.put(fqn4, "foo", "bar"); cache.removeNode(fqn4); cache.put(fqn1, ExpirationAlgorithmConfig.EXPIRATION_KEY, future); cache.put(fqn2, ExpirationAlgorithmConfig.EXPIRATION_KEY, past); cache.put(fqn3, ExpirationAlgorithmConfig.EXPIRATION_KEY, future); ec.startEviction(); assertNotNull(cache.getNode(fqn1)); assertNull(cache.getNode(fqn2)); assertNotNull(cache.getNode(fqn3)); assertNull(cache.getNode(fqn4)); Thread.sleep(2000); ec.startEviction(); log.info("should remove 1 and 3 now"); assertNull(cache.getNode(fqn1)); assertNull(cache.getNode(fqn3)); } public void testUpdate() throws Exception { try { log.info("update 1 from future to past"); cache.put(fqn1, ExpirationAlgorithmConfig.EXPIRATION_KEY, future); new EvictionController(cache).startEviction(); assertNotNull(cache.getNode(fqn1)); cache.put(fqn1, ExpirationAlgorithmConfig.EXPIRATION_KEY, past); new EvictionController(cache).startEviction(); assertNull(cache.getNode(fqn1)); } finally { cache.removeNode(Fqn.ROOT); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/ElementSizePolicyTest.java0000644000175000017500000001364111131613416031316 0ustar moellermoeller/* * 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.*; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.EvictionController; 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; /** * @author Daniel Huang * @version $Revison: $ */ @Test(groups = {"functional"}, sequential = true, testName = "eviction.ElementSizePolicyTest") public class ElementSizePolicyTest extends EvictionTestsBase { CacheSPI cache; long 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().getWakeupInterval(); t1_ex = t2_ex = null; isTrue = true; } void initCaches() throws Exception { Configuration conf = UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); EvictionConfig evConfig = new EvictionConfig(new EvictionRegionConfig(Fqn.ROOT, new ElementSizeAlgorithmConfig(5000, 100), 200000), -1); evConfig.addEvictionRegionConfig(new EvictionRegionConfig(Fqn.fromString("/org/jboss/data"), new ElementSizeAlgorithmConfig(10, 20))); evConfig.addEvictionRegionConfig(new EvictionRegionConfig(Fqn.fromString("/org/jboss/test/data"), new ElementSizeAlgorithmConfig(-1, 5))); evConfig.addEvictionRegionConfig(new EvictionRegionConfig(Fqn.fromString("/test/"), new ElementSizeAlgorithmConfig(5000, 1))); conf.setEvictionConfig(evConfig); cache = (CacheSPI) new UnitTestCacheFactory().createCache(conf, false, getClass()); cache.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cache.getConfiguration().setIsolationLevel(IsolationLevel.SERIALIZABLE); cache.start(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache); cache = null; } 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(); } } EvictionController evController = new EvictionController(cache); evController.startEviction(); TestingUtil.sleepThread(200); // small grace period for (int i = 0; i < 10; i++) { String f = "/org/jboss/test/data/" + i; Node node = cache.getNode(f); 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 { List fqnsThatShouldBeEvicted = new ArrayList(); for (int i = 10; i < 20; i++) fqnsThatShouldBeEvicted.add(Fqn.fromString("/org/jboss/data/" + i)); EvictionController evictionController = new EvictionController(cache); String rootStr = "/org/jboss/data/"; for (int i = 0; i < 20; i++) { String str = rootStr + Integer.toString(i); Fqn fqn = Fqn.fromString(str);//"/org/jboss/data/i"; cache.put(fqn, i, str); for (int k = 0; k < i; k++) { cache.put(fqn, k, str); } } evictionController.startEviction(); for (int i = 0; i < 20; i++) { String str = rootStr + Integer.toString(i); Fqn fqn = Fqn.fromString(str); Node node = cache.getNode(fqn); 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/3", 100 + i, "value"); } Node node = cache.getNode("/org/jboss/data/3"); assertEquals(21, node.getData().size()); evictionController.startEviction(); TestingUtil.sleepThread(200); // small grace period assertNull(cache.getNode("/org/jboss/data/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); } catch (Throwable e) { e.printStackTrace(); if (t1_ex == null) { t1_ex = e; } } } } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/LFUPolicyTest.java0000644000175000017500000002333011132110773027513 0ustar moellermoeller/* * 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.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.EvictionController; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Unit tests for LFU Policy. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 7439 $ */ @Test(groups = {"functional"}, testName = "eviction.LFUPolicyTest") public class LFUPolicyTest extends EvictionTestsBase { CacheSPI cache; final String ROOT_STR = "/test"; volatile Throwable t1_ex, t2_ex; volatile boolean isTrue; int maxNodesDefault = 500, minNodesDefault = 10, maxNodesR1 = 200, minNodesR1 = 100, maxNodesR2 = -1, minNodesR2 = 5; private EvictionController evController; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { initCaches(); t1_ex = t2_ex = null; isTrue = true; } void initCaches() throws Exception { Configuration config = UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); EvictionConfig evConfig = new EvictionConfig(new EvictionRegionConfig(Fqn.ROOT, new LFUAlgorithmConfig(maxNodesDefault, minNodesDefault), 200000), 0); evConfig.addEvictionRegionConfig(new EvictionRegionConfig(Fqn.fromString("/org/jboss/data"), new LFUAlgorithmConfig(maxNodesR1, minNodesR1))); evConfig.addEvictionRegionConfig(new EvictionRegionConfig(Fqn.fromString("/org/jboss/test/data"), new LFUAlgorithmConfig(maxNodesR2, minNodesR2))); config.setEvictionConfig(evConfig); config.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); config.setIsolationLevel(IsolationLevel.SERIALIZABLE); cache = (CacheSPI) new UnitTestCacheFactory().createCache(config, true, getClass()); evController = new EvictionController(cache); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache); cache = null; } /* THIS TEST NEEDS REWRITING public void testEviction() throws Exception { int numNodes = (int) ((maxNodesR1 * 2.6) - 1); String rootStr = "/org/jboss/data/"; List fqns = new ArrayList(); for (int i = 0; i < numNodes; i += 2) fqns.add(Fqn.fromString(rootStr + i)); EvictionWatcher ew = new EvictionWatcher(cache, fqns); List toRevisit = new ArrayList(); for (int i = 0; i < numNodes; 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) toRevisit.add(fqn); revisit(toRevisit); } assert ew.waitForEviction(30, TimeUnit.SECONDS); for (int i = 0; i < numNodes; 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)); } } } private void revisit(List fqns) { for (Fqn fqn : fqns) cache.getNode(fqn); }*/ public void testNodeVisited() throws InterruptedException { 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(); } } evController.startEviction(); try { for (int i = 0; i < 5; i++) { String str = rootStr + Integer.toString(i); Fqn fqn = Fqn.fromString(str); assertNull("Fqn " + fqn + " should be null", 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)); } // 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); } } evController.startEviction(); // 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 < maxNodesR1; 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(); } } evController.startEviction(); for (int i = 0; i < (maxNodesR1 - minNodesR1); i++) { String str = rootStr + i; Fqn fqn = Fqn.fromString(str); assertNull(cache.get(fqn, str)); } for (int i = (maxNodesR1 - minNodesR1); i < maxNodesR1; i++) { String str = rootStr + i; Fqn fqn = Fqn.fromString(str); assertNotNull(cache.get(fqn, str)); } for (int i = (maxNodesR1 - minNodesR1); i < maxNodesR1; i++) { if (i % 2 == 0) { String str = rootStr + i; Fqn fqn = Fqn.fromString(str); cache.removeNode(fqn); } } evController.startEviction(); for (int i = (maxNodesR1 - minNodesR1); i < maxNodesR1; 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(); System.out.println("Got exception:" + e); if (t1_ex == null) { isTrue = false; t1_ex = e; } } } } } //todo mmarkus create a test profile to use greater values for thread count below 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 < 5; i++) { new MyPutter("LFUPolicyTestPutter" + i).start(); } int counter = 0; while (true) { counter++; if (t1_ex != null) { isTrue = false; fail("Exception generated in put() " + t1_ex); } TestingUtil.sleepThread(250); if (counter > 5) {// run for 5 seconds isTrue = false; break; } } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/LRUQueueTest.java0000644000175000017500000001577311111047320027362 0ustar moellermoeller/* * 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.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Unit tests for LRUQueue. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 7168 $ */ @Test(groups = {"functional"}, sequential = true, testName = "eviction.LRUQueueTest") public class LRUQueueTest { private LRUQueue queue; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { queue = new LRUQueue(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { queue = null; } 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-3.2.8.GA/src/test/java/org/jboss/cache/eviction/EvictionConfigurationTest.java0000644000175000017500000002645211120431615032223 0ustar moellermoeller/* * 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.*; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.Test; /** * Unit test to test Eviction configuration types. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 7314 $ */ @Test(groups = "unit", sequential = false, testName = "eviction.EvictionConfigurationTest") public class EvictionConfigurationTest { public void testPolicyPerRegion() throws Exception { CacheSPI cache = null; RegionManager regionManager = null; try { cache = setupCache("configs/policyPerRegion-eviction.xml"); regionManager = cache.getRegionManager(); assertEquals(5000, cache.getConfiguration().getEvictionConfig().getWakeupInterval()); Region region = regionManager.getRegion("/org/jboss/data", true); EvictionRegionConfig evictionRegionConfig = region.getEvictionRegionConfig(); assertEquals(Fqn.fromString("/org/jboss/data"), region.getFqn()); assertTrue(evictionRegionConfig.getEvictionAlgorithmConfig() instanceof LFUAlgorithmConfig); assertEquals(5000, ((LFUAlgorithmConfig) evictionRegionConfig.getEvictionAlgorithmConfig()).getMaxNodes()); assertEquals(1000, ((LFUAlgorithmConfig) evictionRegionConfig.getEvictionAlgorithmConfig()).getMinNodes()); region = regionManager.getRegion("/org/jboss/test/data", true); evictionRegionConfig = region.getEvictionRegionConfig(); assertEquals(Fqn.fromString("/org/jboss/test/data"), region.getFqn()); assertTrue(evictionRegionConfig.getEvictionAlgorithmConfig() instanceof FIFOAlgorithmConfig); assertEquals(5, ((FIFOAlgorithmConfig) evictionRegionConfig.getEvictionAlgorithmConfig()).getMaxNodes()); region = regionManager.getRegion("/test", true); evictionRegionConfig = region.getEvictionRegionConfig(); assertEquals(Fqn.fromString("/test"), region.getFqn()); assertTrue(evictionRegionConfig.getEvictionAlgorithmConfig() instanceof MRUAlgorithmConfig); assertEquals(10000, ((MRUAlgorithmConfig) evictionRegionConfig.getEvictionAlgorithmConfig()).getMaxNodes()); region = regionManager.getRegion("/maxAgeTest", true); evictionRegionConfig = region.getEvictionRegionConfig(); assertEquals(Fqn.fromString("/maxAgeTest"), region.getFqn()); assertTrue(evictionRegionConfig.getEvictionAlgorithmConfig() instanceof LRUAlgorithmConfig); assertEquals(10000, ((LRUAlgorithmConfig) evictionRegionConfig.getEvictionAlgorithmConfig()).getMaxNodes()); assertEquals(8000, ((LRUAlgorithmConfig) evictionRegionConfig.getEvictionAlgorithmConfig()).getTimeToLive()); assertEquals(10000, ((LRUAlgorithmConfig) evictionRegionConfig.getEvictionAlgorithmConfig()).getMaxAge()); // test the default region. use a region name that isn't defined explicitly in conf file. region = regionManager.getRegion("/a/b/c", false); evictionRegionConfig = region.getEvictionRegionConfig(); assertEquals(Fqn.ROOT, region.getFqn()); assertTrue(evictionRegionConfig.getEvictionAlgorithmConfig() instanceof LRUAlgorithmConfig); assertEquals(5000, ((LRUAlgorithmConfig) evictionRegionConfig.getEvictionAlgorithmConfig()).getMaxNodes()); assertEquals(1000000, ((LRUAlgorithmConfig) evictionRegionConfig.getEvictionAlgorithmConfig()).getTimeToLive()); assertEquals(-1, ((LRUAlgorithmConfig) evictionRegionConfig.getEvictionAlgorithmConfig()).getMaxAge()); } finally { TestingUtil.killCaches(cache); } } public void testMixedPolicies() throws Exception { CacheSPI cache = null; RegionManager regionManager = null; try { cache = setupCache("configs/mixedPolicy-eviction.xml"); regionManager = cache.getRegionManager(); assertEquals(5000, cache.getConfiguration().getEvictionConfig().getWakeupInterval()); Region region = regionManager.getRegion("/org/jboss/data", true); EvictionRegionConfig evictionRegionConfig = region.getEvictionRegionConfig(); assertEquals(Fqn.fromString("/org/jboss/data/"), region.getFqn()); assertTrue(evictionRegionConfig.getEvictionAlgorithmConfig() instanceof FIFOAlgorithmConfig); assertEquals(5000, ((FIFOAlgorithmConfig) evictionRegionConfig.getEvictionAlgorithmConfig()).getMaxNodes()); region = regionManager.getRegion("/test", true); evictionRegionConfig = region.getEvictionRegionConfig(); assertEquals(Fqn.fromString("/test/"), region.getFqn()); assertTrue(evictionRegionConfig.getEvictionAlgorithmConfig() instanceof MRUAlgorithmConfig); assertEquals(10000, ((MRUAlgorithmConfig) evictionRegionConfig.getEvictionAlgorithmConfig()).getMaxNodes()); // test the default region. use a region name that isn't defined explicitly in conf file. region = regionManager.getRegion("/a/b/c", false); evictionRegionConfig = region.getEvictionRegionConfig(); assertEquals(Fqn.ROOT, region.getFqn()); assertTrue(evictionRegionConfig.getEvictionAlgorithmConfig() instanceof LRUAlgorithmConfig); assertEquals(5000, ((LRUAlgorithmConfig) evictionRegionConfig.getEvictionAlgorithmConfig()).getMaxNodes()); assertEquals(1000000, ((LRUAlgorithmConfig) evictionRegionConfig.getEvictionAlgorithmConfig()).getTimeToLive()); assertEquals(-1, ((LRUAlgorithmConfig) evictionRegionConfig.getEvictionAlgorithmConfig()).getMaxAge()); region = regionManager.getRegion("/maxAgeTest", false); evictionRegionConfig = region.getEvictionRegionConfig(); assertEquals(Fqn.fromString("/maxAgeTest/"), region.getFqn()); assertTrue(evictionRegionConfig.getEvictionAlgorithmConfig() instanceof LRUAlgorithmConfig); assertEquals(10000, ((LRUAlgorithmConfig) evictionRegionConfig.getEvictionAlgorithmConfig()).getMaxNodes()); assertEquals(8000, ((LRUAlgorithmConfig) evictionRegionConfig.getEvictionAlgorithmConfig()).getTimeToLive()); assertEquals(10000, ((LRUAlgorithmConfig) evictionRegionConfig.getEvictionAlgorithmConfig()).getMaxAge()); } finally { TestingUtil.killCaches(cache); } } public void testLegacyPolicyConfiguration() throws Exception { CacheSPI cache = null; RegionManager regionManager = null; try { cache = setupCache("configs/local-lru-eviction.xml"); regionManager = cache.getRegionManager(); assertEquals(5000, cache.getConfiguration().getEvictionConfig().getWakeupInterval()); Region region = regionManager.getRegion("/org/jboss/data", false); EvictionRegionConfig evictionRegionConfig = region.getEvictionRegionConfig(); assertEquals(Fqn.fromString("/org/jboss/data/"), region.getFqn()); assertTrue(evictionRegionConfig.getEvictionAlgorithmConfig() instanceof LRUAlgorithmConfig); assertEquals(5000, ((LRUAlgorithmConfig) evictionRegionConfig.getEvictionAlgorithmConfig()).getMaxNodes()); assertEquals(1000000, ((LRUAlgorithmConfig) evictionRegionConfig.getEvictionAlgorithmConfig()).getTimeToLive()); region = regionManager.getRegion("/org/jboss/test/data", false); evictionRegionConfig = region.getEvictionRegionConfig(); assertEquals(Fqn.fromString("/org/jboss/test/data/"), region.getFqn()); assertTrue(evictionRegionConfig.getEvictionAlgorithmConfig() instanceof LRUAlgorithmConfig); assertEquals(5, ((LRUAlgorithmConfig) evictionRegionConfig.getEvictionAlgorithmConfig()).getMaxNodes()); assertEquals(4000, ((LRUAlgorithmConfig) evictionRegionConfig.getEvictionAlgorithmConfig()).getTimeToLive()); region = regionManager.getRegion("/test", true); evictionRegionConfig = region.getEvictionRegionConfig(); assertEquals(Fqn.fromString("/test/"), region.getFqn()); assertTrue(evictionRegionConfig.getEvictionAlgorithmConfig() instanceof LRUAlgorithmConfig); assertEquals(10000, ((LRUAlgorithmConfig) evictionRegionConfig.getEvictionAlgorithmConfig()).getMaxNodes()); assertEquals(4000, ((LRUAlgorithmConfig) evictionRegionConfig.getEvictionAlgorithmConfig()).getTimeToLive()); region = regionManager.getRegion("/maxAgeTest", true); evictionRegionConfig = region.getEvictionRegionConfig(); assertEquals(Fqn.fromString("/maxAgeTest/"), region.getFqn()); assertTrue(evictionRegionConfig.getEvictionAlgorithmConfig() instanceof LRUAlgorithmConfig); assertEquals(10000, ((LRUAlgorithmConfig) evictionRegionConfig.getEvictionAlgorithmConfig()).getMaxNodes()); assertEquals(8000, ((LRUAlgorithmConfig) evictionRegionConfig.getEvictionAlgorithmConfig()).getTimeToLive()); assertEquals(10000, ((LRUAlgorithmConfig) evictionRegionConfig.getEvictionAlgorithmConfig()).getMaxAge()); // test the default region. use a region name that isn't defined explicitly in conf file. region = regionManager.getRegion("/a/b/c", false); evictionRegionConfig = region.getEvictionRegionConfig(); assertEquals(Fqn.ROOT, region.getFqn()); assertTrue(evictionRegionConfig.getEvictionAlgorithmConfig() instanceof LRUAlgorithmConfig); assertEquals(5000, ((LRUAlgorithmConfig) evictionRegionConfig.getEvictionAlgorithmConfig()).getMaxNodes()); assertEquals(1000000, ((LRUAlgorithmConfig) evictionRegionConfig.getEvictionAlgorithmConfig()).getTimeToLive()); assertEquals(-1, ((LRUAlgorithmConfig) evictionRegionConfig.getEvictionAlgorithmConfig()).getMaxAge()); } finally { TestingUtil.killCaches(cache); } } public void testTwoCacheInstanceConfiguration() throws Exception { this.setupCache("configs/local-lru-eviction.xml"); this.setupCache("configs/local-lru-eviction.xml"); } public void testNoEviction() throws Exception { CacheSPI cache = null; RegionManager regionManager = null; try { cache = (CacheSPI) new UnitTestCacheFactory().createCache(getClass()); regionManager = cache.getRegionManager(); assertEquals(0, regionManager.getAllRegions(Region.Type.ANY).size()); } finally { TestingUtil.killCaches(cache); } } private CacheSPI setupCache(String configurationName) { UnitTestCacheFactory testCacheFactory = new UnitTestCacheFactory(); Configuration config = testCacheFactory.getConfigurationFromFile(configurationName); config.setCacheMode(Configuration.CacheMode.LOCAL); config.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); config.setIsolationLevel(IsolationLevel.SERIALIZABLE); return (CacheSPI) testCacheFactory.createCache(config, getClass()); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/MRUQueueTest.java0000644000175000017500000000717311111047320027356 0ustar moellermoeller/* * 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: 7168 $ */ @Test(groups = {"functional"}, sequential = true, testName = "eviction.MRUQueueTest") 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-3.2.8.GA/src/test/java/org/jboss/cache/eviction/FIFOConfigurationTest.java0000644000175000017500000000430211111047320031150 0ustar moellermoeller/* * 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.parsing.XmlConfigHelper; import org.jboss.cache.config.parsing.element.EvictionElementParser; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.Test; import org.w3c.dom.Element; /** * Unit test for FIFOConfiguration. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 7168 $ */ @Test(groups = "unit", sequential = false, testName = "eviction.FIFOConfigurationTest") public class FIFOConfigurationTest { public void testXMLParse() throws Exception { FIFOAlgorithmConfig config = new FIFOAlgorithmConfig(); String xml = "" + "" + ""; Element element = XmlConfigHelper.stringToElementInCoreNS(xml); EvictionElementParser.parseEvictionPolicyConfig(element, config); config.validate(); assertEquals(1000, config.getMaxNodes()); } public void testXMLParse2() throws Exception { FIFOAlgorithmConfig config = new FIFOAlgorithmConfig(); String xml = "" + ""; Element element = XmlConfigHelper.stringToElementInCoreNS(xml); EvictionElementParser.parseEvictionPolicyConfig(element, config); config.validate(); assert config.getMaxNodes() == -1; assert config.getMinTimeToLive() == -1; } public void testXMLParse3() throws Exception { FIFOAlgorithmConfig config = new FIFOAlgorithmConfig(); String xml = "" + "" + ""; Element element = XmlConfigHelper.stringToElementInCoreNS(xml); try { EvictionElementParser.parseEvictionPolicyConfig(element, config); config.validate(); } catch (ConfigurationException ce) { assertTrue("Configure Exception properly thrown", true); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/RecursiveRootEvictionTest.java0000644000175000017500000000666211155772024032242 0ustar moellermoeller/* * 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.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; 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.UnitTestConfigurationFactory; import org.jboss.cache.loader.testloaders.DummyInMemoryCacheLoader; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.io.IOException; @Test(groups = { "functional" }, testName = "eviction.RecursiveRootEvictionTest") public class RecursiveRootEvictionTest { private CacheSPI cache; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { UnitTestCacheFactory factory = new UnitTestCacheFactory(); Configuration conf = UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); conf.setEvictionConfig(new EvictionConfig(new EvictionRegionConfig(Fqn.ROOT, new LRUAlgorithmConfig(1000000, 5000)), 200)); conf.setCacheLoaderConfig(buildCacheLoaderConfig()); conf.setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cache = (CacheSPI) factory.createCache(conf, true, getClass()); } private CacheLoaderConfig buildCacheLoaderConfig() throws IOException { CacheLoaderConfig cacheLoaderConfig = new CacheLoaderConfig(); CacheLoaderConfig.IndividualCacheLoaderConfig iclc = new CacheLoaderConfig.IndividualCacheLoaderConfig(); iclc.setClassName(DummyInMemoryCacheLoader.class.getName()); cacheLoaderConfig.addIndividualCacheLoaderConfig(iclc); return cacheLoaderConfig; } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache); } public void testNonrecursiveRootEviction() { cache.put(Fqn.fromElements("a", "a"), "x", "x"); cache.put(Fqn.fromElements("a", "b"), "x", "x"); cache.put(Fqn.fromElements("a", "c"), "x", "x"); assert cache.getNumberOfNodes() == 4; cache.evict(Fqn.fromElements("a", "a"), false); assert cache.getNumberOfNodes() == 3; cache.evict(Fqn.fromElements("a", "b"), false); assert cache.getNumberOfNodes() == 2; cache.evict(Fqn.fromElements("a", "c"), false); assert cache.getNumberOfNodes() == 1; cache.evict(Fqn.fromElements("a"), false); assert cache.getNumberOfNodes() == 0; } public void testRecursiveNonRootEviction() { cache.put(Fqn.fromElements("a", "a"), "x", "x"); cache.put(Fqn.fromElements("a", "b"), "x", "x"); cache.put(Fqn.fromElements("a", "c"), "x", "x"); cache.evict(Fqn.fromElements("a"), true); assert cache.getNumberOfNodes() == 0; } public void testRecursiveRootEviction() { cache.put(Fqn.fromElements("a", "a"), "x", "x"); cache.put(Fqn.fromElements("a", "b"), "x", "x"); cache.put(Fqn.fromElements("a", "c"), "x", "x"); cache.evict(Fqn.ROOT, true); assert cache.getNumberOfNodes() == 0; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/ElementSizeConfigurationTest.java0000644000175000017500000000435211111047320032656 0ustar moellermoeller/* * 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.parsing.XmlConfigHelper; import org.jboss.cache.config.parsing.element.EvictionElementParser; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.Test; import org.w3c.dom.Element; /** * @author Daniel Huang * @version $Revision: 7168 $ */ @Test(groups = "unit", sequential = false, testName = "eviction.ElementSizeConfigurationTest") public class ElementSizeConfigurationTest { public void testXMLParse1() throws Exception { ElementSizeAlgorithmConfig config = new ElementSizeAlgorithmConfig(); String xml = "" + "" + "" + ""; Element element = XmlConfigHelper.stringToElementInCoreNS(xml); EvictionElementParser.parseEvictionPolicyConfig(element, config); config.validate(); assertEquals(100, config.getMaxElementsPerNode()); assertEquals(1000, config.getMaxNodes()); } public void testXMLParse2() throws Exception { ElementSizeAlgorithmConfig config = new ElementSizeAlgorithmConfig(); String xml = "" + "" + ""; Element element = XmlConfigHelper.stringToElementInCoreNS(xml); EvictionElementParser.parseEvictionPolicyConfig(element, config); config.validate(); assert config.getMaxElementsPerNode() == -1; } public void testXMLParse3() throws Exception { ElementSizeAlgorithmConfig config = new ElementSizeAlgorithmConfig(); String xml = "" + "" + ""; Element element = XmlConfigHelper.stringToElementInCoreNS(xml); EvictionElementParser.parseEvictionPolicyConfig(element, config); config.validate(); assertEquals(100, config.getMaxElementsPerNode()); assertEquals(-1, config.getMaxNodes()); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/RegionManagerTest.java0000755000175000017500000001311611245232602030430 0ustar moellermoellerpackage org.jboss.cache.eviction; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.RegionManager; import org.jboss.cache.RegionManagerImpl; import org.jboss.cache.RegionRegistry; import org.jboss.cache.config.EvictionRegionConfig; import static org.testng.AssertJUnit.*; import org.testng.annotations.Test; import java.util.List; /** * Region Manager unit tests. * * @author Ben Wang, Feb 11, 2004 * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 8208 $ */ @Test(groups = "functional", testName = "eviction.RegionManagerTest") 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"); EvictionRegionConfig config = new EvictionRegionConfig(null, new NullEvictionAlgorithmConfig()); public void testCreateRegion() { RegionManager regionManager = new RegionManagerImpl(); RegionRegistry rr = new RegionRegistry(); ((RegionManagerImpl) regionManager).injectDependencies(null, null, null, null, null, rr); regionManager.setUsingEvictions(true); regionManager.getRegion(DEFAULT_REGION, true).setEvictionRegionConfig(config); regionManager.getRegion(A_B_C, true).setEvictionRegionConfig(config); regionManager.getRegion(A_B, true).setEvictionRegionConfig(config); regionManager.getRegion(AOP, true).setEvictionRegionConfig(config); List regions = regionManager.getAllRegions(Region.Type.ANY); System.out.println("RegionRegistry: " + rr); assertEquals("Region size ", 4, regions.size()); } public void testCreateRegion2() { RegionManager regionManager = new RegionManagerImpl(); ((RegionManagerImpl) regionManager).injectDependencies(null, null, null, null, null, new RegionRegistry()); regionManager.setUsingEvictions(true); regionManager.getRegion(A_B_C, true).setEvictionRegionConfig(config); regionManager.getRegion(A_B, true).setEvictionRegionConfig(config); regionManager.getRegion(DEFAULT_REGION, true).setEvictionRegionConfig(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 RegionManagerImpl(); ((RegionManagerImpl) regionManager).injectDependencies(null, null, null, null, null, new RegionRegistry()); regionManager.setUsingEvictions(true); regionManager.getRegion(A_B_C, true).setEvictionRegionConfig(config); regionManager.getRegion(A_B, true).setEvictionRegionConfig(config); regionManager.getRegion(Fqn.fromString("/a"), Region.Type.EVICTION, false); } public void testGetRegion() { RegionManager regionManager = new RegionManagerImpl(); ((RegionManagerImpl) regionManager).injectDependencies(null, null, null, null, null, new RegionRegistry()); regionManager.setUsingEvictions(true); regionManager.getRegion(DEFAULT_REGION, true).setEvictionRegionConfig(config); regionManager.getRegion(A_BC, true).setEvictionRegionConfig(config); regionManager.getRegion(A_B, true).setEvictionRegionConfig(config); Region region = regionManager.getRegion(A_BC, true); assertNotSame("Region ", DEFAULT_REGION, 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 RegionManagerImpl(); ((RegionManagerImpl) rm).injectDependencies(null, null, null, null, null, new RegionRegistry()); rm.setUsingEvictions(true); rm.getRegion(DEFAULT_REGION, true).setEvictionRegionConfig(config); rm.getRegion(A_B_C_D_E, true).setEvictionRegionConfig(config); rm.getRegion(A_B_C_D, true).setEvictionRegionConfig(config); rm.getRegion(A_B_C, true).setEvictionRegionConfig(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-3.2.8.GA/src/test/java/org/jboss/cache/eviction/LFUConfigurationTest.java0000644000175000017500000000444511111047320031063 0ustar moellermoeller/* * 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.parsing.XmlConfigHelper; import org.jboss.cache.config.parsing.element.EvictionElementParser; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.Test; import org.w3c.dom.Element; /** * LFU Configuration test. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 7168 $ */ @Test(groups = "unit", sequential = false, testName = "eviction.LFUConfigurationTest") public class LFUConfigurationTest { public void testXMLParsing() throws Exception { LFUAlgorithmConfig config = new LFUAlgorithmConfig(); String xml = "" + "" + "" + ""; Element element = XmlConfigHelper.stringToElementInCoreNS(xml); EvictionElementParser.parseEvictionPolicyConfig(element, config); config.validate(); assertEquals(10, config.getMinNodes()); assertEquals(20, config.getMaxNodes()); } public void testXMLParsing2() throws Exception { LFUAlgorithmConfig config = new LFUAlgorithmConfig(); String xml = "" + "" + ""; Element element = XmlConfigHelper.stringToElementInCoreNS(xml); EvictionElementParser.parseEvictionPolicyConfig(element, config); config.validate(); assertEquals(10, config.getMinNodes()); assertEquals(-1, config.getMaxNodes()); } public void testXMLParsing3() throws Exception { LFUAlgorithmConfig config = new LFUAlgorithmConfig(); String xml = "" + "" + ""; Element element = XmlConfigHelper.stringToElementInCoreNS(xml); EvictionElementParser.parseEvictionPolicyConfig(element, config); config.validate(); assertEquals(-1, config.getMinNodes()); assertEquals(20, config.getMaxNodes()); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/LRUPolicyTest.java0000755000175000017500000002632011132110773027534 0ustar moellermoellerpackage org.jboss.cache.eviction; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.EvictionController; import org.jboss.cache.util.internals.EvictionWatcher; 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.concurrent.TimeUnit; import org.jboss.cache.UnitTestCacheFactory; /** * Unit tests for LRU Policy. * * @author Ben Wang, Feb 11, 2004 * @author Daniel Huang - dhuang@jboss.org * @version $Revision: 7439 $ */ @Test(groups = "functional", testName = "eviction.LRUPolicyTest") public class LRUPolicyTest extends EvictionTestsBase { CacheSPI cache; long wakeupIntervalMillis = 1000; long dataRegionTTLMillis = 1000; long testRegionTTLMillis = 1000; private int baseRegionMaxNodes = 10; private int baseRegionTTLMillis = 1000; final String ROOT_STR = "/test"; volatile Throwable t1_ex, t2_ex; volatile boolean isTrue; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { Configuration conf = UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); EvictionConfig evConfig = conf.getEvictionConfig(); evConfig.setWakeupInterval(wakeupIntervalMillis); List regionConfigs = new ArrayList(); regionConfigs.add(new EvictionRegionConfig(Fqn.fromString("/org/jboss/test/data"), new LRUAlgorithmConfig(dataRegionTTLMillis, -1, 5))); regionConfigs.add(new EvictionRegionConfig(Fqn.fromString("/test"), new LRUAlgorithmConfig(testRegionTTLMillis, 10000))); regionConfigs.add(new EvictionRegionConfig(Fqn.fromString("/base"), new LRUAlgorithmConfig(baseRegionTTLMillis, -1, baseRegionMaxNodes))); evConfig.setEvictionRegionConfigs(regionConfigs); conf.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); conf.setIsolationLevel(IsolationLevel.SERIALIZABLE); cache = (CacheSPI) new UnitTestCacheFactory().createCache(conf, getClass()); wakeupIntervalMillis = cache.getConfiguration().getEvictionConfig().getWakeupInterval(); 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 { TestingUtil.killCaches(cache); cache = null; } 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); } 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); } new EvictionController(cache).startEviction(); 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(); } } TestingUtil.sleepThread(wakeupIntervalMillis + 500); String val = (String) cache.get(rootStr + "3", rootStr + "3"); assertNull("Node should be empty ", val); } public void testNodeVisited() throws InterruptedException { String rootStr = "/org/jboss/test/data/"; final Fqn fqn7 = Fqn.fromString(rootStr + "7"); for (int i = 0; i < 10; i++) { String str = rootStr + i; Fqn fqn = Fqn.fromString(str); cache.put(fqn, str, str); // playing favourites here: cache.getNode(fqn7);// just to keep it fresh } Thread retrieverThread = new Thread() { @Override public void run() { try { while (true) { cache.getNode(fqn7);// just to keep it fresh sleep(50); } } catch (Exception ie) { } } }; retrieverThread.setDaemon(true); retrieverThread.start(); assert waitForEviction(cache, 30, TimeUnit.SECONDS, Fqn.fromString(rootStr + 3)); String val = (String) cache.get(rootStr + "3", rootStr + "3"); assertNull("Node should be empty ", val); Node n = cache.getNode(fqn7); assert n != null; val = (String) n.get(rootStr + "7"); assertNotNull("Node should not be empty ", val); retrieverThread.interrupt(); retrieverThread.join(); new EvictionController(cache).startEviction(true); assert waitForEviction(cache, 30, TimeUnit.SECONDS, Fqn.fromString(rootStr + 7)); val = (String) cache.get(rootStr + "7", rootStr + "7"); 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); } 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 new EvictionController(cache).startEviction(); cache.get(fqn1, str1);// just to keep it fresh cache.get(fqn2, str2);// just to keep it fresh new EvictionController(cache).startEviction(); 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); new EvictionController(cache).startEviction(); 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 long period = (wakeupIntervalMillis + testRegionTTLMillis) * 2; 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) { System.out.println("Got exception: " + e); if (t1_ex == null) { isTrue = false; 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("LRUPolicyTestPutter" + i).start(); } int counter = 0; while (true) { counter++; if (t1_ex != null) { isTrue = false; fail("Exception generated in put() " + t1_ex); } TestingUtil.sleepThread(1000); if (counter > 5) {// run for 5 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 { Node node = cache.getRoot().addChild(Fqn.fromString("/base/")); node.setResident(true); cache.getRoot().setResident(true); EvictionWatcher ew = new EvictionWatcher(cache, Fqn.fromString("/base/1")); for (int i = 1; i < baseRegionMaxNodes + 2; i++) { Fqn f = Fqn.fromString("/base/" + i); cache.put(f, "key", "base" + i); } new EvictionController(cache).startEviction(); assert ew.waitForEviction(30, TimeUnit.SECONDS); assertEquals(baseRegionMaxNodes, cache.getRoot().getChild(Fqn.fromString("/base")).getChildren().size()); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/RegionTest.java0000755000175000017500000001007511120421353027131 0ustar moellermoellerpackage org.jboss.cache.eviction; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.RegionImpl; import org.jboss.cache.RegionManager; import org.jboss.cache.RegionManagerImpl; import org.jboss.cache.RegionRegistry; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNull; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.concurrent.TimeUnit; import org.testng.annotations.AfterMethod; /** * @author Ben Wang, Feb 11, 2004 * @author Daniel Huang (dhuang@jboss.org) */ @Test(groups = {"functional"}, sequential = true, testName = "eviction.RegionTest") public class RegionTest { RegionManager regionManager; EvictionAlgorithm algorithm; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { algorithm = new LRUAlgorithm(); regionManager = new RegionManagerImpl(); ((RegionManagerImpl) regionManager).injectDependencies(null, null, null, null, null, new RegionRegistry()); Region r = regionManager.getRegion("/a/b", true);//.setEvictionPolicy(new DummyEvictionConfiguration()); r.setEvictionRegionConfig(new EvictionRegionConfig(r.getFqn(), new LRUAlgorithmConfig())); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { algorithm = null; regionManager = null; } public void testAddedQueue() throws InterruptedException { 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.registerEvictionEvent(fqn1, EvictionEvent.Type.ADD_NODE_EVENT); region.registerEvictionEvent(fqn2, EvictionEvent.Type.ADD_NODE_EVENT); region.registerEvictionEvent(fqn3, EvictionEvent.Type.ADD_NODE_EVENT); assertEquals("queue size ", 3, getQueueSize((RegionImpl) region)); EvictionEvent node = takeLastEvent((RegionImpl) region); Fqn fqn = node.getFqn(); assertEquals("DataNode retrieved should be FILO ", fqn, fqn1); assertEquals("AddedNode queue size ", 2, getQueueSize((RegionImpl) region)); fqn = takeLastEvent((RegionImpl) region).getFqn(); fqn = takeLastEvent((RegionImpl) region).getFqn(); node = takeLastEvent((RegionImpl) region); assertNull("DataNode should be null", node); } public void testEventQueue() throws InterruptedException { 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.registerEvictionEvent(fqn1, EvictionEvent.Type.REMOVE_NODE_EVENT); region.registerEvictionEvent(fqn2, EvictionEvent.Type.ADD_NODE_EVENT); region.registerEvictionEvent(fqn3, EvictionEvent.Type.VISIT_NODE_EVENT); assertEquals("RemovedNode queue size ", 3, getQueueSize((RegionImpl) region)); EvictionEvent.Type event = takeLastEvent((RegionImpl) region).getEventType(); assertEquals("DataNode retrieved should be: ", EvictionEvent.Type.REMOVE_NODE_EVENT, event); takeLastEvent((RegionImpl) region); takeLastEvent((RegionImpl) region); EvictionEvent node = takeLastEvent((RegionImpl) region); 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.registerEvictionEvent(fqn2, EvictionEvent.Type.ADD_NODE_EVENT); } } EvictionEvent takeLastEvent(RegionImpl r) throws InterruptedException { return r.getEvictionEventQueue().poll(0, TimeUnit.MILLISECONDS); } int getQueueSize(RegionImpl r) { return r.getEvictionEventQueue().size(); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/legacy/0000755000175000017500000000000011675221317025456 5ustar moellermoeller././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/legacy/BackwardCompatibilityTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/legacy/BackwardCompatibilityTest.jav0000644000175000017500000002230511135607632033271 0ustar moellermoellerpackage org.jboss.cache.eviction.legacy; import org.jboss.cache.Cache; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionPolicyConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.config.UnsupportedEvictionImplException; import org.jboss.cache.config.parsing.XmlConfigHelper; import org.jboss.cache.config.parsing.XmlConfigurationParser; import org.jboss.cache.config.parsing.XmlConfigurationParser2x; import org.jboss.cache.eviction.BaseEvictionPolicy; import org.jboss.cache.eviction.DefaultEvictionActionPolicy; import org.jboss.cache.eviction.EvictionAlgorithm; import org.jboss.cache.eviction.EvictionPolicyConfigBase; import org.jboss.cache.eviction.FIFOAlgorithm; import org.jboss.cache.eviction.FIFOAlgorithmConfig; import org.jboss.cache.eviction.FIFOConfiguration; import org.jboss.cache.eviction.FIFOPolicy; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.EvictionController; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.List; @Test(groups = "functional", testName = "eviction.legacy.BackwardCompatibilityTest") public class BackwardCompatibilityTest { private EvictionRegionConfig createEvictionRegionConfig(String regionName, int maxNodes, EvictionPolicyConfigBase cfg) { EvictionRegionConfig ercDefault = new EvictionRegionConfig(); ercDefault.setRegionName(regionName); if (maxNodes >= 0) cfg.setMaxNodes(maxNodes); ercDefault.setEvictionPolicyConfig(cfg); return ercDefault; } public void testUsingEvictionPolicy() { Configuration c = new Configuration(); c.setEvictionConfig(new EvictionConfig()); EvictionConfig evConfig = c.getEvictionConfig(); evConfig.setDefaultEventQueueSize(20000); evConfig.setDefaultEvictionPolicyClass(FIFOPolicy.class.getName()); List erConfigs = new ArrayList(); erConfigs.add(createEvictionRegionConfig("/_default_", 2, new FIFOConfiguration())); evConfig.setEvictionRegionConfigs(erConfigs); doTest(c); } public void testUsingEvictionPolicyXml() throws Exception { String xml = "\n" + " \n" + " 2\n" + " \n" + " org.jboss.cache.eviction.FIFOPolicy\n" + "\n" + " \n" + " \n" + " 2\n" + " \n" + " \n" + " "; doTest(xml, true); } public void testUsingCustomEvictionPolicy() { try { Configuration c = new Configuration(); c.setEvictionConfig(new EvictionConfig()); EvictionConfig evConfig = c.getEvictionConfig(); evConfig.setDefaultEventQueueSize(20000); evConfig.setDefaultEvictionPolicyClass(MyPolicy.class.getName()); List erConfigs = new ArrayList(); erConfigs.add(createEvictionRegionConfig("/_default_", 2, new MyPolicyConfig())); evConfig.setEvictionRegionConfigs(erConfigs); doTest(c); assert false : "Should throw exception"; } catch (UnsupportedEvictionImplException ce) { // expected } } public void testUsingCustomEvictionPolicyXml() throws Exception { String xml = "\n" + " \n" + " 2\n" + " \n" + " " + MyPolicy.class.getName() + "\n" + "\n" + " \n" + " \n" + " 2\n" + " \n" + " \n" + " "; try { doTest(xml, true); assert false : "Should throw exception"; } catch (UnsupportedEvictionImplException ce) { // expected } } public void testUsingCustomEvictionPolicyNonDefault() { try { Configuration c = new Configuration(); c.setEvictionConfig(new EvictionConfig()); EvictionConfig evConfig = c.getEvictionConfig(); evConfig.setDefaultEventQueueSize(20000); evConfig.setDefaultEvictionPolicyClass(FIFOPolicy.class.getName()); List erConfigs = new ArrayList(); erConfigs.add(createEvictionRegionConfig("/_default_", 2, new FIFOConfiguration())); erConfigs.add(createEvictionRegionConfig("/a/b/c", 2, new MyPolicyConfig())); evConfig.setEvictionRegionConfigs(erConfigs); doTest(c); assert false : "Should throw exception"; } catch (UnsupportedEvictionImplException ce) { // expected } } public void testUsingCustomEvictionPolicyNonDefaultXml() throws Exception { String xml = "\n" + " \n" + " 2\n" + " \n" + " " + FIFOPolicy.class.getName() + "\n" + "\n" + " \n" + " \n" + " 2\n" + " \n" + " " + " 2\n" + " \n" + " \n" + " "; try { doTest(xml, true); assert false : "Should throw exception"; } catch (UnsupportedEvictionImplException ce) { // expected } } public void testControl() { Configuration c = new Configuration(); c.setEvictionConfig(new EvictionConfig()); EvictionRegionConfig defaultRegion = c.getEvictionConfig().getDefaultEvictionRegionConfig(); defaultRegion.setEvictionAlgorithmConfig(new FIFOAlgorithmConfig(2)); doTest(c); } public void testControlXml() throws Exception { String xml = "" + "" + "" + "" + ""; doTest(xml, false); } private void doTest(String xml, boolean legacy) throws Exception { if (legacy) { EvictionConfig ec = XmlConfigurationParser2x.parseEvictionConfig(XmlConfigHelper.stringToElementInCoreNS(xml)); Configuration c = new Configuration(); c.setEvictionConfig(ec); doTest(c); } else { doTest(new XmlConfigurationParser().parseElement(XmlConfigHelper.stringToElementInCoreNS(xml))); } } private void doTest(Configuration c) { Cache cache = null; try { c.getEvictionConfig().setWakeupInterval(0); cache = new UnitTestCacheFactory().createCache(c, getClass()); EvictionRegionConfig erc = cache.getRegion(Fqn.ROOT, false).getEvictionRegionConfig(); assert erc.getEvictionAlgorithmConfig() instanceof FIFOAlgorithmConfig; assert erc.getEvictionActionPolicyClassName().equals(DefaultEvictionActionPolicy.class.getName()); cache.put("/a/b/1", "a", "b"); cache.put("/a/b/2", "a", "b"); cache.put("/a/b/3", "a", "b"); new EvictionController(cache).startEviction(); assert cache.getNode("/a/b/1") == null; assert cache.getNode("/a/b/2") != null; assert cache.getNode("/a/b/3") != null; } finally { TestingUtil.killCaches(cache); } } public static class MyPolicy extends BaseEvictionPolicy { public EvictionAlgorithm getEvictionAlgorithm() { return null; } public Class getEvictionConfigurationClass() { return null; } } public class MyPolicyConfig extends EvictionPolicyConfigBase { protected void setEvictionPolicyClassName() { this.setEvictionPolicyClass(MyPolicy.class.getName()); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/minttl/0000755000175000017500000000000011675221322025515 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/minttl/MinTTLTestBase.java0000644000175000017500000000751011135370315031123 0ustar moellermoellerpackage org.jboss.cache.eviction.minttl; import org.jboss.cache.Cache; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.eviction.EvictionAlgorithmConfigBase; import org.jboss.cache.eviction.EvictionTestsBase; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.EvictionController; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * This test exercises the minimum time to live for any element in the cache * * @author Manik Surtani * @since 2.1.0 */ @Test(groups = {"functional"}, sequential = true) public abstract class MinTTLTestBase extends EvictionTestsBase { // 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 volatile CountDownLatch cacheInitialisedLatch; protected abstract EvictionAlgorithmConfigBase getEvictionAlgorithmConfig(); @BeforeMethod public void setUp() { cacheInitialisedLatch = new CountDownLatch(1); // the LRU policy cfg EvictionAlgorithmConfigBase cfg = getEvictionAlgorithmConfig(); // the region configuration EvictionRegionConfig regionCfg = new EvictionRegionConfig(); regionCfg.setRegionFqn(region); regionCfg.setRegionName(region.toString()); regionCfg.setEvictionAlgorithmConfig(cfg); // cache-wide EvictionConfig ec = new EvictionConfig(); ec.setWakeupInterval(200); ec.addEvictionRegionConfig(regionCfg); cache = new UnitTestCacheFactory().createCache(false, getClass()); cache.getConfiguration().setEvictionConfig(ec); } @AfterMethod public void tearDown() { TestingUtil.killCaches(cache); cache = null; } @Test(invocationCount = 5, successPercentage = 80) public void testNoMinimumTTL() throws InterruptedException { 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"; assert waitForEviction(cache, 10, TimeUnit.SECONDS, fqn); assert cache.get(fqn, "k") == null : "Node should have been evicted"; } public void testWithMinimumTTL() throws InterruptedException { ((EvictionAlgorithmConfigBase) cache.getConfiguration().getEvictionConfig().getEvictionRegionConfigs().get(0).getEvictionAlgorithmConfig()).setMinTimeToLive(500); 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"; new EvictionController(cache).startEviction(); 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. new EvictionController(cache).startEviction(true); assert waitForEviction(cache, 5, TimeUnit.SECONDS, fqn); assert cache.get(fqn, "k") == null : "Node should have been evicted"; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/minttl/LRUMinTTLTest.java0000644000175000017500000000114411111047320030677 0ustar moellermoellerpackage org.jboss.cache.eviction.minttl; import org.jboss.cache.eviction.EvictionAlgorithmConfigBase; import org.jboss.cache.eviction.LRUAlgorithmConfig; import org.testng.annotations.Test; /** * @author Manik Surtani * @since 2.1.0 */ @Test(groups = {"functional"}, testName = "eviction.minttl.LRUMinTTLTest") public class LRUMinTTLTest extends MinTTLTestBase { @Override protected EvictionAlgorithmConfigBase getEvictionAlgorithmConfig() { LRUAlgorithmConfig cfg = new LRUAlgorithmConfig(); cfg.setTimeToLive(200); return cfg; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/minttl/FIFOMinTTLTest.java0000644000175000017500000000356611120421353030774 0ustar moellermoellerpackage org.jboss.cache.eviction.minttl; import org.jboss.cache.Fqn; import org.jboss.cache.eviction.EvictionAlgorithmConfigBase; import org.jboss.cache.eviction.FIFOAlgorithmConfig; 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"}, testName = "eviction.minttl.FIFOMinTTLTest") public class FIFOMinTTLTest extends MinTTLTestBase { private Fqn fqn2 = Fqn.fromRelativeElements(region, "b"); private Thread busyThread; private volatile boolean busyThreadRunning = true; @Override protected EvictionAlgorithmConfigBase getEvictionAlgorithmConfig() { startBusyThread(); FIFOAlgorithmConfig cfg = new FIFOAlgorithmConfig(); cfg.setMaxNodes(1); return cfg; } @AfterMethod public void stopBusyThread() { busyThreadRunning = false; try { busyThread.interrupt(); 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-3.2.8.GA/src/test/java/org/jboss/cache/eviction/minttl/MRUMinTTLTest.java0000644000175000017500000000317411111047320030705 0ustar moellermoellerpackage org.jboss.cache.eviction.minttl; import org.jboss.cache.CacheStatus; import org.jboss.cache.Fqn; import org.jboss.cache.eviction.EvictionAlgorithmConfigBase; import org.jboss.cache.eviction.MRUAlgorithmConfig; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.Test; /** * @author Manik Surtani * @since 2.1.0 */ @Test(groups = {"functional"}, testName = "eviction.minttl.MRUMinTTLTest") public class MRUMinTTLTest extends MinTTLTestBase { private Fqn fqn2 = Fqn.fromRelativeElements(region, "b"); @Override protected EvictionAlgorithmConfigBase getEvictionAlgorithmConfig() { MRUAlgorithmConfig cfg = new MRUAlgorithmConfig(); 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-3.2.8.GA/src/test/java/org/jboss/cache/eviction/minttl/LFUMinTTLTest.java0000644000175000017500000000104211111047320030660 0ustar moellermoellerpackage org.jboss.cache.eviction.minttl; import org.jboss.cache.eviction.EvictionAlgorithmConfigBase; import org.jboss.cache.eviction.LFUAlgorithmConfig; import org.testng.annotations.Test; /** * @author Manik Surtani * @since 2.1.0 */ @Test(groups = {"functional"}, testName = "eviction.minttl.LFUMinTTLTest") public class LFUMinTTLTest extends MinTTLTestBase { @Override protected EvictionAlgorithmConfigBase getEvictionAlgorithmConfig() { return new LFUAlgorithmConfig(); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/LRUConfigurationTest.java0000644000175000017500000000555311111047320031100 0ustar moellermoeller/* * 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.EvictionConfig; import org.jboss.cache.config.parsing.XmlConfigHelper; import org.jboss.cache.config.parsing.element.EvictionElementParser; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.Test; import org.w3c.dom.Element; /** * Unit tests for LRUConfiguration. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 7168 $ */ @Test(groups = "unit", sequential = false, testName = "eviction.LRUConfigurationTest") public class LRUConfigurationTest { public void testXMLParsing() throws Exception { LRUAlgorithmConfig config = new LRUAlgorithmConfig(); String xml = "\n" + "\n" + "\n" + ""; Element element = XmlConfigHelper.stringToElementInCoreNS(xml); EvictionElementParser.parseEvictionPolicyConfig(element, config); config.validate(); assertEquals(5000, config.getMaxNodes()); assertEquals(1000, config.getTimeToLive()); } public void testXMLParsing2() throws Exception { LRUAlgorithmConfig config = new LRUAlgorithmConfig(); String xml = "\n" + "\n" + "\n" + "\n" + ""; Element element = XmlConfigHelper.stringToElementInCoreNS(xml); EvictionElementParser.parseEvictionPolicyConfig(element, config); config.validate(); assertEquals(10000, config.getMaxNodes()); assertEquals(1000, config.getTimeToLive()); assertEquals(10, config.getMaxAge()); } public void testXMLParsing3() throws Exception { String xml = "\n" + "\n" + "\n" + ""; Element element = XmlConfigHelper.stringToElementInCoreNS(xml); boolean caught = false; try { EvictionConfig ec = new EvictionElementParser().parseEvictionElement(element); ec.getEvictionRegionConfigs().get(0).validate(); } catch (ConfigurationException ce) { caught = true; } assertTrue("Configure exception should have been caught", caught); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/FIFOAlgorithmTest.java0000644000175000017500000001015511111047320030272 0ustar moellermoeller/* * 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.RegionImpl; import org.jboss.cache.RegionManager; import org.jboss.cache.RegionManagerImpl; import org.jboss.cache.RegionRegistry; 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 FIFOAlgorithm. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 7168 $ */ @Test(groups = {"functional"}, sequential = true, testName = "eviction.FIFOAlgorithmTest") public class FIFOAlgorithmTest extends EvictionTestsBase { RegionManager regionManager; FIFOAlgorithm algo; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { regionManager = new RegionManagerImpl(); ((RegionManagerImpl) regionManager).injectDependencies(null, null, null, null, null, new RegionRegistry()); FIFOAlgorithmConfig config = new FIFOAlgorithmConfig(); config.setMaxNodes(0); algo = (FIFOAlgorithm) createAndAssignToRegion("/a/b", regionManager, config); } public void testMaxNodes1() throws Exception { RegionImpl region = (RegionImpl) regionManager.getRegion("/a/b", true); FIFOAlgorithmConfig config = (FIFOAlgorithmConfig) region.getEvictionRegionConfig().getEvictionAlgorithmConfig(); config.setMaxNodes(5); for (int i = 0; i < 8; i++) { Fqn fqn = Fqn.fromString("/a/b/" + Integer.toString(i)); region.registerEvictionEvent(fqn, EvictionEvent.Type.ADD_NODE_EVENT); } algo.process(region.getEvictionEventQueue()); FIFOQueue queue = (FIFOQueue) algo.evictionQueue; assertEquals(5, algo.getEvictionQueue().getNumberOfNodes()); // now verify the order. int index = 3; for (NodeEntry ne : queue) { 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.registerEvictionEvent(fqn, EvictionEvent.Type.VISIT_NODE_EVENT); } for (int i = 3; i < 5; i++) { Fqn fqn = Fqn.fromString("/a/b/" + Integer.toString(i)); region.registerEvictionEvent(fqn, EvictionEvent.Type.VISIT_NODE_EVENT); } algo.process(region.getEvictionEventQueue()); assertEquals(5, algo.getEvictionQueue().getNumberOfNodes()); index = 3; for (NodeEntry ne : queue) { String fqn = ne.getFqn().toString(); assertTrue(fqn.endsWith("/" + Integer.toString(index))); index++; } } public void testMaxNodes2() throws Exception { RegionImpl region = (RegionImpl) regionManager.getRegion("/a/b", true); FIFOAlgorithmConfig config = (FIFOAlgorithmConfig) region.getEvictionRegionConfig().getEvictionAlgorithmConfig(); config.setMaxNodes(500); for (int i = 0; i < 500; i++) { Fqn fqn = Fqn.fromString("/a/b/" + Integer.toString(i)); region.registerEvictionEvent(fqn, EvictionEvent.Type.ADD_NODE_EVENT); } algo.process(region.getEvictionEventQueue()); FIFOQueue queue = (FIFOQueue) algo.evictionQueue; assertEquals(500, algo.getEvictionQueue().getNumberOfNodes()); int index = 0; for (NodeEntry ne : queue) { assertTrue(ne.getFqn().toString().endsWith("/" + Integer.toString(index))); index++; } for (int i = 500; i < 600; i++) { Fqn fqn = Fqn.fromString("/a/b/" + Integer.toString(i)); region.registerEvictionEvent(fqn, EvictionEvent.Type.ADD_NODE_EVENT); } algo.process(region.getEvictionEventQueue()); index = 100; for (NodeEntry ne : queue) { assertTrue(ne.getFqn().toString().endsWith("/" + Integer.toString(index))); index++; } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/MRUConfigurationTest.java0000644000175000017500000000502111111047320031067 0ustar moellermoeller/* * 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.parsing.XmlConfigHelper; import org.jboss.cache.config.parsing.element.EvictionElementParser; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.AfterMethod; 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: 7168 $ */ @Test(groups = "unit", sequential = true, testName = "eviction.MRUConfigurationTest") public class MRUConfigurationTest { MRUAlgorithmConfig config = null; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { config = new MRUAlgorithmConfig(); } @AfterMethod(alwaysRun = true) public void tearDown() { config = null; } public void testXMLParsing() throws Exception { String xml = "\n" + "\n" + ""; Element element = XmlConfigHelper.stringToElementInCoreNS(xml); EvictionElementParser.parseEvictionPolicyConfig(element, config); config.validate(); assertEquals(5000, config.getMaxNodes()); } public void testXMLParsing2() throws Exception { String xml = "\n" + "\n" + ""; Element element = XmlConfigHelper.stringToElementInCoreNS(xml); EvictionElementParser.parseEvictionPolicyConfig(element, config); config.validate(); assertEquals(10000, config.getMaxNodes()); } public void testXMLParsing3() throws Exception { String xml = "\n" + ""; Element element = XmlConfigHelper.stringToElementInCoreNS(xml); EvictionElementParser.parseEvictionPolicyConfig(element, config); config.validate(); assert config.getMaxNodes() == -1; xml = "\n" + "\n" + ""; element = XmlConfigHelper.stringToElementInCoreNS(xml); EvictionElementParser.parseEvictionPolicyConfig(element, config); config.validate(); assertEquals(10000, config.getMaxNodes()); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/ElementSizeAlgorithmTest.java0000644000175000017500000000725111120421353032000 0ustar moellermoellerpackage org.jboss.cache.eviction; import org.jboss.cache.Fqn; import org.jboss.cache.RegionImpl; import org.jboss.cache.RegionManager; import org.jboss.cache.RegionManagerImpl; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * @author Daniel Huang * @version $Revision: 7289 $ */ @Test(groups = {"functional"}, sequential = true, testName = "eviction.ElementSizeAlgorithmTest") public class ElementSizeAlgorithmTest extends EvictionTestsBase { RegionManager regionManager; ElementSizeAlgorithm algo; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { regionManager = new RegionManagerImpl(); ElementSizeAlgorithmConfig config = new ElementSizeAlgorithmConfig(); config.setMaxElementsPerNode(0); algo = (ElementSizeAlgorithm) createAndAssignToRegion("/a/b", regionManager, config); } public void testMaxElements() throws Exception { RegionImpl region = (RegionImpl) regionManager.getRegion("/a/b", true); ElementSizeAlgorithmConfig config = (ElementSizeAlgorithmConfig) region.getEvictionRegionConfig().getEvictionAlgorithmConfig(); config.setMaxNodes(10); config.setMaxElementsPerNode(6); for (int i = 0; i < 10; i++) { Fqn fqn = Fqn.fromString("/a/b/" + Integer.toString(i)); region.registerEvictionEvent(fqn, EvictionEvent.Type.ADD_NODE_EVENT); if (i % 2 == 0) { for (int k = 0; k < i; k++) { region.registerEvictionEvent(fqn, EvictionEvent.Type.ADD_ELEMENT_EVENT); } } } algo.process(region.getEvictionEventQueue()); ElementSizeQueue queue = (ElementSizeQueue) algo.evictionQueue; assertEquals(9, algo.getEvictionQueue().getNumberOfNodes()); assertEquals(12, algo.getEvictionQueue().getNumberOfElements()); // now verify the order. int count = 6; for (NodeEntry ne : queue) { if (count > 0) { assertEquals(count, ne.getNumberOfElements()); } else { assertEquals(0, ne.getNumberOfElements()); } count -= 2; } for (int i = 0; i < 7; i++) { region.registerEvictionEvent(Fqn.fromString("/a/b/9"), EvictionEvent.Type.ADD_ELEMENT_EVENT); region.registerEvictionEvent(Fqn.fromString("/a/b/7"), EvictionEvent.Type.ADD_ELEMENT_EVENT); } algo.process(region.getEvictionEventQueue()); assertEquals(7, queue.getNumberOfNodes()); } public void testMaxNodesAndMaxElements() throws Exception { RegionImpl region = (RegionImpl) regionManager.getRegion("/a/b", true); ElementSizeAlgorithmConfig config = (ElementSizeAlgorithmConfig) region.getEvictionRegionConfig().getEvictionAlgorithmConfig(); config.setMaxNodes(10); config.setMaxElementsPerNode(100); for (int i = 0; i < 20; i++) { Fqn fqn = Fqn.fromString("/a/b/" + Integer.toString(i)); region.registerEvictionEvent(fqn, EvictionEvent.Type.ADD_NODE_EVENT); for (int k = 0; k < i; k++) { region.registerEvictionEvent(fqn, EvictionEvent.Type.ADD_ELEMENT_EVENT); } } algo.process(region.getEvictionEventQueue()); ElementSizeQueue queue = (ElementSizeQueue) algo.evictionQueue; assertEquals(10, algo.getEvictionQueue().getNumberOfNodes()); assertEquals(45, algo.getEvictionQueue().getNumberOfElements()); // now verify the order. int num = 9; for (NodeEntry ne : queue) { assertEquals(num, ne.getNumberOfElements()); num--; } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/eviction/LFUAlgorithmTest.java0000644000175000017500000003160711120421353030204 0ustar moellermoeller/* * 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.RegionImpl; import org.jboss.cache.RegionManager; import org.jboss.cache.RegionManagerImpl; import org.jboss.cache.config.EvictionRegionConfig; import org.testng.annotations.AfterMethod; import static org.jboss.cache.eviction.EvictionEvent.Type.*; import static org.testng.AssertJUnit.*; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Unit test for LFUAlgorithm. * * @author Daniel Huang - dhuang@jboss.org - 10/2005 * @version $Revision: 7289 $ */ @Test(groups = {"functional"}, sequential = true, testName = "eviction.LFUAlgorithmTest") public class LFUAlgorithmTest extends EvictionTestsBase { RegionManager regionManager; LFUAlgorithm algo; LFUAlgorithmConfig config; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { regionManager = new RegionManagerImpl(); config = new LFUAlgorithmConfig(); algo = (LFUAlgorithm) createAndAssignToRegion("/a/b", regionManager, config); } @AfterMethod(alwaysRun = true) public void tearDown() { regionManager = null; config = null; algo = null; } public void testMaxNode1() { Fqn fqn1 = Fqn.fromString("/a/b/c"); Fqn fqn2 = Fqn.fromString("/a/b/d"); RegionImpl region = (RegionImpl) regionManager.getRegion("/a/b", true); config.setMaxNodes(-1); config.setMinNodes(20); region.registerEvictionEvent(fqn1, ADD_NODE_EVENT); region.registerEvictionEvent(fqn2, ADD_NODE_EVENT); try { algo.process(region.getEvictionEventQueue()); } 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"); RegionImpl region = (RegionImpl) regionManager.getRegion("/a/b", true); config.setMaxNodes(1); config.setMinNodes(20); region.registerEvictionEvent(fqn1, ADD_NODE_EVENT); region.registerEvictionEvent(fqn2, ADD_NODE_EVENT); try { algo.process(region.getEvictionEventQueue()); } catch (EvictionException e) { fail("testMaxNode: process failed " + e); e.printStackTrace(); } assertEquals("Queue size should be ", 1, algo.getEvictionQueue().getNumberOfNodes()); region.registerEvictionEvent(fqn2, ADD_NODE_EVENT); region.registerEvictionEvent(fqn3, ADD_NODE_EVENT); try { algo.process(region.getEvictionEventQueue()); } catch (EvictionException e) { fail("testMaxNode: process failed " + e); e.printStackTrace(); } assertEquals("Queue size should be ", 1, algo.getEvictionQueue().getNumberOfNodes()); } 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"); RegionImpl region = (RegionImpl) regionManager.getRegion("/a/b", true); LFUAlgorithmConfig config = (LFUAlgorithmConfig) region.getEvictionRegionConfig().getEvictionAlgorithmConfig(); config.setMaxNodes(-1); config.setMinNodes(2); region.registerEvictionEvent(fqn1, ADD_NODE_EVENT); region.registerEvictionEvent(fqn2, ADD_NODE_EVENT); region.registerEvictionEvent(fqn3, ADD_NODE_EVENT); region.registerEvictionEvent(fqn4, ADD_NODE_EVENT); algo.process(region.getEvictionEventQueue()); 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"); RegionImpl region = (RegionImpl) regionManager.getRegion("/a/b", true); LFUAlgorithmConfig config = (LFUAlgorithmConfig) region.getEvictionRegionConfig().getEvictionAlgorithmConfig(); config.setMaxNodes(-1); config.setMinNodes(-1); region.registerEvictionEvent(fqn1, ADD_NODE_EVENT); region.registerEvictionEvent(fqn2, ADD_NODE_EVENT); algo.process(region.getEvictionEventQueue()); 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"); RegionImpl region = (RegionImpl) regionManager.getRegion("/a/b", true); LFUAlgorithmConfig config = (LFUAlgorithmConfig) region.getEvictionRegionConfig().getEvictionAlgorithmConfig(); config.setMaxNodes(-1); config.setMinNodes(100); region.registerEvictionEvent(fqn1, ADD_NODE_EVENT); region.registerEvictionEvent(fqn2, ADD_NODE_EVENT); region.registerEvictionEvent(fqn3, ADD_NODE_EVENT); region.registerEvictionEvent(fqn4, ADD_NODE_EVENT); region.registerEvictionEvent(fqn5, ADD_NODE_EVENT); region.registerEvictionEvent(fqn6, ADD_NODE_EVENT); region.registerEvictionEvent(fqn7, ADD_NODE_EVENT); region.registerEvictionEvent(fqn8, ADD_NODE_EVENT); region.registerEvictionEvent(fqn9, ADD_NODE_EVENT); region.registerEvictionEvent(fqn10, ADD_NODE_EVENT); algo.process(region.getEvictionEventQueue()); LFUQueue queue = (LFUQueue) algo.evictionQueue; assertEquals(10, algo.getEvictionQueue().getNumberOfNodes()); for (NodeEntry ne : queue) { assertEquals(1, ne.getNumberOfNodeVisits()); } // fqn1 visited 4 additional times. region.registerEvictionEvent(fqn1, VISIT_NODE_EVENT); region.registerEvictionEvent(fqn1, VISIT_NODE_EVENT); region.registerEvictionEvent(fqn1, VISIT_NODE_EVENT); region.registerEvictionEvent(fqn1, VISIT_NODE_EVENT); // fqn2 visited 3 additional times. region.registerEvictionEvent(fqn2, VISIT_NODE_EVENT); region.registerEvictionEvent(fqn2, VISIT_NODE_EVENT); region.registerEvictionEvent(fqn2, VISIT_NODE_EVENT); // fqn3 visited 1 additional time. region.registerEvictionEvent(fqn3, VISIT_NODE_EVENT); // fqn4 visited 2 additional times. region.registerEvictionEvent(fqn4, VISIT_NODE_EVENT); region.registerEvictionEvent(fqn4, VISIT_NODE_EVENT); // fqn9 visited 1 additional time. region.registerEvictionEvent(fqn9, VISIT_NODE_EVENT); // fqn10 visited 2 additional times. region.registerEvictionEvent(fqn10, VISIT_NODE_EVENT); region.registerEvictionEvent(fqn10, VISIT_NODE_EVENT); algo.process(region.getEvictionEventQueue()); int count = 0; for (NodeEntry ne : queue) { count++; 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.registerEvictionEvent(fqn11, ADD_NODE_EVENT); region.registerEvictionEvent(fqn12, ADD_NODE_EVENT); algo.process(region.getEvictionEventQueue()); count = 0; for (NodeEntry ne : queue) { count++; 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.registerEvictionEvent(fqn1, REMOVE_NODE_EVENT); region.registerEvictionEvent(fqn11, REMOVE_NODE_EVENT); region.registerEvictionEvent(fqn12, REMOVE_NODE_EVENT); region.registerEvictionEvent(fqn10, REMOVE_NODE_EVENT); algo.process(region.getEvictionEventQueue()); count = 0; for (NodeEntry ne : queue) { count++; 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.registerEvictionEvent(fqn11, ADD_NODE_EVENT); region.registerEvictionEvent(fqn11, VISIT_NODE_EVENT); region.registerEvictionEvent(fqn11, VISIT_NODE_EVENT); region.registerEvictionEvent(fqn11, VISIT_NODE_EVENT); region.registerEvictionEvent(fqn11, VISIT_NODE_EVENT); region.registerEvictionEvent(fqn11, VISIT_NODE_EVENT); region.registerEvictionEvent(fqn4, VISIT_NODE_EVENT); // purposefully revisit a node that has been removed. assert that it is readded. region.registerEvictionEvent(fqn1, VISIT_NODE_EVENT); region.registerEvictionEvent(fqn1, VISIT_NODE_EVENT); region.registerEvictionEvent(fqn3, REMOVE_NODE_EVENT); algo.process(region.getEvictionEventQueue()); count = 0; for (NodeEntry ne : queue) { count++; 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 { RegionImpl region = (RegionImpl) regionManager.getRegion("/a/b", true); EvictionRegionConfig config = region.getEvictionRegionConfig(); ((LFUAlgorithmConfig) config.getEvictionAlgorithmConfig()).setMaxNodes(-1); ((LFUAlgorithmConfig) config.getEvictionAlgorithmConfig()).setMinNodes(10000); for (int i = 0; i < 10000; i++) { Fqn fqn = Fqn.fromString("/a/b/" + Integer.toString(i)); region.registerEvictionEvent(fqn, ADD_NODE_EVENT); } algo.process(region.getEvictionEventQueue()); LFUQueue queue = (LFUQueue) algo.evictionQueue; long lastModifiedTimestamp = 0; for (NodeEntry ne : queue) { 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.registerEvictionEvent(fqn, VISIT_NODE_EVENT); } } algo.process(region.getEvictionEventQueue()); int count = 0; lastModifiedTimestamp = 0; for (NodeEntry ne : queue) { 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-3.2.8.GA/src/test/java/org/jboss/cache/eviction/EvictionTestsBase.java0000644000175000017500000000434311111047320030440 0ustar moellermoellerpackage org.jboss.cache.eviction; import org.easymock.EasyMock; 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.RegionManagerImpl; import org.jboss.cache.RegionRegistry; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionAlgorithmConfig; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.EvictionWatcher; import java.util.concurrent.TimeUnit; public abstract class EvictionTestsBase { public EvictionAlgorithm createAndAssignToRegion(String fqnString, RegionManager regionManager, EvictionAlgorithmConfig config) { Fqn fqn = Fqn.fromString(fqnString); Configuration c = new Configuration(); EvictionConfig evictionConfig = new EvictionConfig(); evictionConfig.setWakeupInterval(-1); c.setEvictionConfig(evictionConfig); EvictionRegionConfig erc = new EvictionRegionConfig(fqn, config); c.getEvictionConfig().addEvictionRegionConfig(erc); CacheSPI mockCache = EasyMock.createNiceMock(CacheSPI.class); EasyMock.replay(mockCache); ((RegionManagerImpl) regionManager).injectDependencies(mockCache, c, null, null, null, new RegionRegistry()); Region r = regionManager.getRegion(fqn, Region.Type.EVICTION, true); r.setEvictionRegionConfig(erc); ((RegionManagerImpl) regionManager).start(); return (EvictionAlgorithm) TestingUtil.extractField(r, "evictionAlgorithm"); } /** * Blocks until an eviction event is seen on the given cache for the given array of Fqns. Returns true if the eviction event * is received, false if it times out. * * @param cache cache to monitor * @param timeToWait timeout * @param unit timeout unit * @param fqnsToEvict fqns to watch for * @return true if evicted, false otherwise */ public boolean waitForEviction(Cache cache, long timeToWait, TimeUnit unit, Fqn... fqnsToEvict) throws InterruptedException { return new EvictionWatcher(cache, fqnsToEvict).waitForEviction(timeToWait, unit); } }jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/invalidation/0000755000175000017500000000000011675221326025053 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/invalidation/OptSyncInvalidationTest.java0000644000175000017500000001607411120571606032521 0ustar moellermoellerpackage org.jboss.cache.invalidation; import org.jboss.cache.*; import org.jboss.cache.optimistic.DefaultDataVersion; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.Configuration; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import static org.testng.AssertJUnit.assertFalse; import org.testng.annotations.Test; import javax.transaction.TransactionManager; import javax.transaction.Transaction; import javax.transaction.RollbackException; /** * @author Mircea.Markus@jboss.com */ // @Test( groups = "functional", testName = "invalidation.OptSyncInvalidationTest") public class OptSyncInvalidationTest extends AbstractMultipleCachesSyncInvalidationTest { protected void createCaches() throws Throwable { Configuration c = new Configuration(); c.setStateRetrievalTimeout(3000); c.setCacheMode(Configuration.CacheMode.INVALIDATION_SYNC); c.setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache1 = (CacheSPI) new UnitTestCacheFactory().createCache(c, true, getClass()); cache2 = (CacheSPI) new UnitTestCacheFactory().createCache(c.clone(), true, getClass()); TestingUtil.blockUntilViewReceived(cache1, 2, 10000); registerCaches(cache1, cache2); } public void testOptSyncUnableToEvict() throws Exception { Fqn fqn = Fqn.fromString("/a/b"); cache2.put(fqn, "key", "value"); assertEquals("value", cache2.get(fqn, "key")); Node n = cache1.getNode(fqn); CacheLoaderInvalidationTest.assertHasBeenInvalidated(n, "Should have been invalidated"); CacheLoaderInvalidationTest.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 testOptimistic() throws Exception { 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")); Node n2 = cache2.getNode(fqn); CacheLoaderInvalidationTest.assertHasBeenInvalidated(n2, "Should have been invalidated"); CacheLoaderInvalidationTest.assertHasBeenInvalidated(cache2.peek(fqn, true, true), "Should have been invalidated"); // now make sure cache2 is in sync with cache1: cache2.put(fqn, "key", "value"); Node n1 = cache1.getNode(fqn); CacheLoaderInvalidationTest.assertHasBeenInvalidated(n1, "Should have been invalidated"); CacheLoaderInvalidationTest.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"); assertEquals("value2", cache1.get(fqn, "key2")); n2 = cache2.getNode(fqn); CacheLoaderInvalidationTest.assertHasBeenInvalidated(n2, "Should have been invalidated"); CacheLoaderInvalidationTest.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); CacheLoaderInvalidationTest.assertHasBeenInvalidated(n1, "Should have been invalidated"); CacheLoaderInvalidationTest.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); CacheLoaderInvalidationTest.assertHasBeenInvalidated(n2, "Should have been invalidated"); CacheLoaderInvalidationTest.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); CacheLoaderInvalidationTest.assertHasBeenInvalidated(n2, "Should have been invalidated"); CacheLoaderInvalidationTest.assertHasBeenInvalidated(cache2.peek(fqn, false, false), "Should have been invalidated"); } public void dataInconsistency() throws Exception { Fqn node = Fqn.fromString("/a"); TransactionManager tm1 = cache1.getConfiguration().getRuntimeConfig().getTransactionManager(); TransactionManager tm2 = cache2.getConfiguration().getRuntimeConfig().getTransactionManager(); 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"); assert val == null : "Older data should not have committed"; val = cache2.get(node, "k"); 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"; } } ././@LongLink0000000000000000000000000000015500000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/invalidation/PessWithCacheLoaderInvalidationTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/invalidation/PessWithCacheLoaderInvalidationT0000644000175000017500000000676711121730272033314 0ustar moellermoellerpackage org.jboss.cache.invalidation; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.AbstractMultipleCachesTest; import org.jboss.cache.loader.testloaders.DummySharedInMemoryCacheLoader; import org.jboss.cache.config.Configuration; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNull; import org.testng.annotations.Test; import org.testng.annotations.BeforeMethod; import javax.transaction.TransactionManager; /** * @author Mircea.Markus@jboss.com */ @Test(groups = "functional", testName = "invalidation.PessWithCacheLoaderInvalidationTest") public class PessWithCacheLoaderInvalidationTest extends AbstractMultipleCachesTest { private CacheSPI cache1; private CacheSPI cache2; protected void createCaches() throws Throwable { Configuration c = new Configuration(); c.setStateRetrievalTimeout(3000); c.setCacheMode(Configuration.CacheMode.INVALIDATION_SYNC); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); c.setCacheLoaderConfig(CacheLoaderInvalidationTest.getCacheLoaderConfig(getClass())); cache1 = (CacheSPI) new UnitTestCacheFactory().createCache(c, true, getClass()); cache2 = (CacheSPI) new UnitTestCacheFactory().createCache(c.clone(), true, getClass()); registerCaches(cache1, cache2); } @BeforeMethod public void clearCacheLoaderBetweenTests() throws Exception { DummySharedInMemoryCacheLoader sharedCl = (DummySharedInMemoryCacheLoader) cache1.getCacheLoaderManager().getCacheLoader(); sharedCl.remove(Fqn.ROOT); } public void testPessimisticNonTransactionalWithCacheLoader() throws Exception { Fqn fqn = Fqn.fromString("/a/b"); cache1.put(fqn, "key", "value"); assertEquals("value", cache1.get(fqn, "key")); assertEquals("value", cache2.get(fqn, "key")); // now make sure cache2 is in sync with cache1: cache2.put(fqn, "key", "value"); assertEquals("value", cache2.get(fqn, "key")); assertEquals("value", cache1.get(fqn, "key")); // now test the invalidation: cache1.put(fqn, "key2", "value2"); assertEquals("value2", cache1.get(fqn, "key2")); assertEquals("value2", cache2.get(fqn, "key2")); assertEquals("value", cache1.get(fqn, "key")); assertEquals("value", cache2.get(fqn, "key")); } public void testPessimisticTransactionalWithCacheLoader() throws Exception { Fqn fqn = Fqn.fromString("/a/b"); TransactionManager mgr = cache1.getTransactionManager(); assertNull("Should be null", cache1.get(fqn, "key")); assertNull("Should be null", cache2.get(fqn, "key")); mgr.begin(); cache1.put(fqn, "key", "value"); assertEquals("value", cache1.get(fqn, "key")); mgr.commit(); assertEquals("value", cache2.get(fqn, "key")); assertEquals("value", cache1.get(fqn, "key")); mgr.begin(); cache1.put(fqn, "key2", "value2"); assertEquals("value2", cache1.get(fqn, "key2")); mgr.rollback(); assertEquals("value", cache2.get(fqn, "key")); assertEquals("value", cache1.get(fqn, "key")); assertNull("Should be null", cache1.get(fqn, "key2")); assertNull("Should be null", cache2.get(fqn, "key2")); } } ././@LongLink0000000000000000000000000000016400000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/invalidation/AbstractMultipleCachesSyncInvalidationTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/invalidation/AbstractMultipleCachesSyncInvali0000644000175000017500000001600711120571606033362 0ustar moellermoellerpackage org.jboss.cache.invalidation; import org.jboss.cache.AbstractMultipleCachesTest; import org.jboss.cache.CacheSPI; import org.jboss.cache.Node; import org.jboss.cache.Fqn; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.fail; import javax.transaction.TransactionManager; /** * @author Mircea.Markus@jboss.com */ public abstract class AbstractMultipleCachesSyncInvalidationTest extends AbstractMultipleCachesTest { protected CacheSPI cache1; protected CacheSPI cache2; public void nodeRemovalTest() throws Exception { 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); CacheLoaderInvalidationTest.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); CacheLoaderInvalidationTest.checkRemoteNodeIsRemoved(remoteNode); assertEquals(false, cache1.removeNode(fqn)); } public void nodeResurrectionTest() throws Exception { // 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")); } /** * 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() throws Exception { 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); CacheLoaderInvalidationTest.checkRemoteNodeIsRemoved(remoteNode); cache2.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); root2.addChild(fqn); assertEquals(true, root2.hasChild(fqn)); } public void deleteNonExistentTest() throws Exception { 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; fail(msg + " -- " + e); } CacheLoaderInvalidationTest.assertHasBeenInvalidated(cache1.getNode(fqn), "Should have been invalidated"); assertNull("Should be null", cache2.getNode(fqn)); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/invalidation/PessAsyncInterceptorTest.java0000644000175000017500000000522111120571606032677 0ustar moellermoellerpackage org.jboss.cache.invalidation; import org.jboss.cache.config.Configuration; import org.jboss.cache.*; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.Test; import javax.transaction.TransactionManager; import javax.transaction.Transaction; import javax.transaction.RollbackException; import java.util.List; import java.util.ArrayList; /** * @author Mircea.Markus@jboss.com */ @Test (groups = "functional", testName = "invalidation.PessAsyncInterceptorTest") public class PessAsyncInterceptorTest extends AbstractMultipleCachesTest { private CacheSPI cache1; private CacheSPI cache2; protected void createCaches() throws Throwable { Configuration c = new Configuration(); c.setStateRetrievalTimeout(3000); c.setCacheMode(Configuration.CacheMode.INVALIDATION_ASYNC); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache1 = (CacheSPI) new UnitTestCacheFactory().createCache(c, true, getClass()); cache2 = (CacheSPI) new UnitTestCacheFactory().createCache(c.clone(), true, getClass()); TestingUtil.blockUntilViewReceived(cache1, 2, 10000); registerCaches(cache1, cache2); } public void testPessTxAsyncUnableToEvict() throws Exception { 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); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/invalidation/PessSyncInvalidationTest.java0000644000175000017500000001704511120571606032670 0ustar moellermoellerpackage org.jboss.cache.invalidation; import org.jboss.cache.*; import org.jboss.cache.config.Configuration; import org.jboss.cache.optimistic.DefaultDataVersion; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.*; import org.testng.annotations.Test; import javax.transaction.RollbackException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; /** * @author Mircea.Markus@jboss.com */ @Test(groups = "functional", testName = "invalidation.PessSyncInvalidationTest") public class PessSyncInvalidationTest extends AbstractMultipleCachesSyncInvalidationTest { protected void createCaches() throws Throwable { Configuration c = new Configuration(); c.setStateRetrievalTimeout(3000); c.setCacheMode(Configuration.CacheMode.INVALIDATION_SYNC); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache1 = (CacheSPI) new UnitTestCacheFactory().createCache(c, true, getClass()); cache2 = (CacheSPI) new UnitTestCacheFactory().createCache(c.clone(), true, getClass()); TestingUtil.blockUntilViewReceived(cache1, 2, 10000); registerCaches(cache1, cache2); } public void testPessimisticNonTransactional() throws Exception { 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)); // 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); CacheLoaderInvalidationTest.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); CacheLoaderInvalidationTest.assertHasBeenInvalidated(n, "Should have been invalidated"); } public void testUnnecessaryEvictions() throws Exception { 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 testPessimisticTransactional() throws Exception { 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)); // 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); CacheLoaderInvalidationTest.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); CacheLoaderInvalidationTest.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); CacheLoaderInvalidationTest.assertHasBeenInvalidated(n, "Should have been invalidated"); } public void testPessTxSyncUnableToEvict() throws Exception { 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); } } /** * Test for JBCACHE-1298. * * @throws Exception */ public void testAddOfDeletedNonExistent() throws Exception { 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; 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 CacheLoaderInvalidationTest.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")); CacheLoaderInvalidationTest.assertHasBeenInvalidated(cache2.getNode(fqn), "Should have been invalidated"); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/invalidation/TombstoneEvictionTest.java0000644000175000017500000001226711120571606032233 0ustar moellermoellerpackage org.jboss.cache.invalidation; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.*; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.eviction.FIFOAlgorithmConfig; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.EvictionController; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.List; /** * Make sure tombstones are evicted * * @author Manik Surtani * @since 2.1.0 */ @Test(groups = {"functional", "optimistic"}, testName = "invalidation.TombstoneEvictionTest") public class TombstoneEvictionTest extends AbstractMultipleCachesTest { private CacheSPI c1, c2; private Fqn fqn = Fqn.fromString("/data/test"); private Fqn dummy = Fqn.fromString("/data/dummy"); private EvictionController ec1; private EvictionController ec2; protected void createCaches() throws Throwable { Configuration c = new Configuration(); // the FIFO policy cfg FIFOAlgorithmConfig cfg = new FIFOAlgorithmConfig(); cfg.setMaxNodes(1); cfg.setMinTimeToLive(0); // the region configuration EvictionRegionConfig regionCfg = new EvictionRegionConfig(); regionCfg.setRegionFqn(dummy.getParent()); regionCfg.setRegionName(dummy.getParent().toString()); regionCfg.setEvictionAlgorithmConfig(cfg); // set regions in a list List evictionRegionConfigs = new ArrayList(); evictionRegionConfigs.add(regionCfg); EvictionConfig ec = new EvictionConfig(); ec.setWakeupInterval(-1); ec.setEvictionRegionConfigs(evictionRegionConfigs); c.setCacheMode(Configuration.CacheMode.INVALIDATION_SYNC); c.setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); c.setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); c.setEvictionConfig(ec); c1 = (CacheSPI) new UnitTestCacheFactory().createCache(c, false, getClass()); c2 = (CacheSPI) new UnitTestCacheFactory().createCache(c.clone(), false, getClass()); c1.start(); c2.start(); ec1 = new EvictionController(c1); ec2 = new EvictionController(c2); TestingUtil.blockUntilViewsReceived(60000, c1, c2); registerCaches(c1, c2); } @BeforeMethod public void clearQueues() { clearRegions(c1.getRegionManager().getAllRegions(Region.Type.ANY)); clearRegions(c2.getRegionManager().getAllRegions(Region.Type.ANY)); } private void clearRegions(List regionList) { for (Region region : regionList) { region.resetEvictionQueues(); } } 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"; ec1.startEviction(); 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"; ec1.startEviction(); ec2.startEviction(); 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"; ec1.startEviction(); ec2.startEviction(); 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-3.2.8.GA/src/test/java/org/jboss/cache/invalidation/CacheLoaderInvalidationTest.java0000644000175000017500000001520411131613416033244 0ustar moellermoeller/* * 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.Fqn; import org.jboss.cache.Node; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; 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 AT jboss DOT org) */ @Test(groups = {"functional", "jgroups"}, testName = "invalidation.CacheLoaderInvalidationTest") public class CacheLoaderInvalidationTest { private static Log log = LogFactory.getLog(CacheLoaderInvalidationTest.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 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")); } protected CacheSPI createUnstartedCache(boolean optimistic) throws Exception { Configuration c = new Configuration(); //c.setClusterName("MyCluster"); c.setStateRetrievalTimeout(3000); c.setCacheMode(Configuration.CacheMode.INVALIDATION_SYNC); if (optimistic) c.setNodeLockingScheme("OPTIMISTIC"); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); CacheSPI cache = (CacheSPI) new UnitTestCacheFactory().createCache(c, false, getClass()); 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(getClass())); caches.get(1).getConfiguration().setCacheLoaderConfig(getCacheLoaderConfig(getClass())); caches.get(0).start(); caches.get(1).start(); toClean.addAll(caches); return caches; } 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); CacheLoaderInvalidationTest.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); CacheLoaderInvalidationTest.assertHasBeenInvalidated(n, "Should have been invalidated"); } static CacheLoaderConfig getCacheLoaderConfig(Class requestor) throws Exception { return UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", "org.jboss.cache.loader.testloaders.DummySharedInMemoryCacheLoader", "bin=" + requestor , false, false, false, false, false); } static void assertHasBeenInvalidated(Node n, String message) { // depending on how n was retrieved! if (n == null) { assert true : message; } else { assert !n.isValid() : message; } } static void checkRemoteNodeIsRemoved(Node remoteNode) { assertHasBeenInvalidated(remoteNode, "Should have been removed"); // Recursively check any children if (remoteNode != null) { for (Node child : remoteNode.getChildren()) { checkRemoteNodeIsRemoved(child); } } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/cluster/0000755000175000017500000000000011675221327024054 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/cluster/ReplicationQueueTxTest.java0000644000175000017500000000462611131613416031350 0ustar moellermoellerpackage org.jboss.cache.cluster; import org.jboss.cache.Cache; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.replicationlisteners.ReplicationListener; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.TransactionManager; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.commands.tx.PrepareCommand; @Test(groups = {"functional", "transaction"}, testName = "cluster.ReplicationQueueTxTest") public class ReplicationQueueTxTest { Cache cache, cache2; TransactionManager txManager; @BeforeMethod public void setUp() throws CloneNotSupportedException { Configuration c = UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.REPL_ASYNC); cache = new UnitTestCacheFactory().createCache(c, false, getClass()); cache.getConfiguration().setUseReplQueue(true); cache.getConfiguration().setReplQueueInterval(100); cache.getConfiguration().setReplQueueMaxElements(10); cache.start(); cache2 = new UnitTestCacheFactory().createCache(cache.getConfiguration().clone(), getClass()); TestingUtil.blockUntilViewsReceived(60000, cache, cache2); txManager = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); } @AfterMethod public void tearDown() { TestingUtil.killCaches(cache, cache2); cache = null; cache2 = null; } public void testTransactionalReplication() throws Exception { ReplicationListener cache1Listener = ReplicationListener.getReplicationListener(cache); ReplicationListener cache2Listener = ReplicationListener.getReplicationListener(cache2); cache2Listener.expect(PutKeyValueCommand.class); // outside of tx scope cache.put("/a", "k", "v"); cache2Listener.waitForReplicationToOccur(5000); assert cache2.get("/a", "k").equals("v"); // now, a transactional call cache1Listener.expect(PrepareCommand.class); txManager.begin(); cache2.put("/a", "k", "v2"); txManager.commit(); cache1Listener.waitForReplicationToOccur(5000); assert cache.get("/a", "k").equals("v2"); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/cluster/ReplicationQueueTest.java0000644000175000017500000001473111135416456031043 0ustar moellermoellerpackage org.jboss.cache.cluster; import org.easymock.EasyMock; import static org.easymock.EasyMock.*; import org.jboss.cache.AbstractMultipleCachesTest; import org.jboss.cache.Cache; import org.jboss.cache.RPCManager; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.ReplicationQueueNotifier; import org.jboss.cache.util.internals.replicationlisteners.ReplicationListener; import org.jgroups.Address; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNotNull; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import java.util.Collections; import java.util.Vector; import java.util.concurrent.CountDownLatch; @Test(groups = "functional", testName = "cluster.ReplicationQueueTest") public class ReplicationQueueTest extends AbstractMultipleCachesTest { private static final int COUNT = 10; Cache cache, cache2; ReplicationQueue replQ; ComponentRegistry registry; RPCManager originalRpcManager; private ReplicationListener replicationListener; protected void createCaches() throws Throwable { Configuration c = new Configuration(); c.setCacheMode(Configuration.CacheMode.REPL_ASYNC); c.setUseReplQueue(true); c.setReplQueueMaxElements(COUNT); c.setReplQueueInterval(-1); cache = new UnitTestCacheFactory().createCache(c, false, getClass()); cache.start(); registry = TestingUtil.extractComponentRegistry(cache); replQ = registry.getComponent(ReplicationQueue.class); originalRpcManager = cache.getConfiguration().getRuntimeConfig().getRPCManager(); cache2 = new UnitTestCacheFactory().createCache(cache.getConfiguration().clone(), getClass()); registerCaches(cache, cache2); TestingUtil.blockUntilViewsReceived(60000, cache, cache2); replicationListener = ReplicationListener.getReplicationListener(cache2); } @AfterMethod public void tearDown() { // reset the original RPCManager injectRpcManager(originalRpcManager); registry.rewire(); } private void injectRpcManager(RPCManager manager) { registry.registerComponent(manager, RPCManager.class); } public void testQueueHoldAndFlush() throws Exception { assert replQ != null; assert replQ.elements.size() == 0 : "expected 0, recieved " + replQ.elements.size(); // 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"); assertEquals(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 testFailure() throws InterruptedException { for (int i = 0; i < COUNT; i++) { cache.put("/a/b/c" + i, "key", "value"); assertNotNull(cache.get("/a/b/c" + i, "key")); replicationListener.expect(PutKeyValueCommand.class); } replicationListener.waitForReplicationToOccur(); for (int i = 0; i < COUNT; i++) assertNotNull("on get i = " + i, cache2.get("/a/b/c" + i, "key")); } @Test(dependsOnMethods = "testFailure") //if this method will run first then PutKeyValues might still be on wire and influence the other method 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 = 5; final int numLoopsPerThread = 200; 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); assert replQ.elements.size() == 0; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/demo/0000755000175000017500000000000011675221332023313 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/demo/CacheModelDelegate.java0000644000175000017500000000176111111047320027566 0ustar moellermoeller/* * 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-3.2.8.GA/src/test/java/org/jboss/cache/demo/JBossCacheView.java0000644000175000017500000001303211111047320026740 0ustar moellermoeller/* * 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: 7168 $ */ 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-3.2.8.GA/src/test/java/org/jboss/cache/demo/JBossCacheModelDelegate.java0000644000175000017500000000126011111047320030521 0ustar moellermoeller/* * 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-3.2.8.GA/src/test/java/org/jboss/cache/demo/JBossCacheGUI.java0000644000175000017500000010653411111047320026464 0ustar moellermoeller/* * 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.Arrays; 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 AT jboss DOT 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 = Arrays.asList(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; } } 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 (Object childName : fqn.peekElements()) { n = curr.findChild((String) childName); if (n == null) { n = new JBossCacheGUI.DisplayNode((String) childName); 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 (Object childName : fqn.peekElements()) { n = curr.findChild((String) childName); 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-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/0000755000175000017500000000000011675221352024555 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/ParentVersionTest.java0000644000175000017500000001542011134665336031065 0ustar moellermoellerpackage 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 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.TransactionManager; /** * @author Manik Surtani * @since 2.0.0 */ @Test(groups = {"functional", "optimistic"}, sequential = true, testName = "optimistic.ParentVersionTest") 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() { TestingUtil.killCaches(cache); cache = null; } 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-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/OptimisticWithPassivationTest.java0000644000175000017500000000522311131613416033454 0ustar moellermoeller/* * 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 static org.jboss.cache.factories.UnitTestConfigurationFactory.buildSingleCacheLoaderConfig; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.transaction.DummyTransactionManager; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNull; import org.testng.annotations.Test; /** * Tests optimistic locking with pasivation * * @author Manik Surtani (manik AT jboss DOT org) */ @Test(groups = {"functional", "optimistic"}, sequential = true, testName = "optimistic.OptimisticWithPassivationTest") public class OptimisticWithPassivationTest extends AbstractOptimisticTestCase { protected CacheLoaderConfig getCacheLoaderConfig() throws Exception { return buildSingleCacheLoaderConfig(true, null, "org.jboss.cache.loader.testloaders.DummyInMemoryCacheLoader", "", false, false, false, false, false); } private CacheSPI createLocalCache() throws Exception { CacheSPI cache = createCacheUnstarted(true); 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(); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/CacheTest.java0000644000175000017500000003404411240012635027256 0ustar moellermoeller/* * Created on 17-Feb-2005 * */ package org.jboss.cache.optimistic; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.easymock.EasyMock; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.NodeSPI; import org.jboss.cache.VersionedNode; import org.jboss.cache.commands.WriteCommand; 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.OptimisticTransactionContext; import org.jboss.cache.transaction.TransactionTable; import org.jboss.cache.util.TestingUtil; 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.concurrent.CountDownLatch; @Test(groups = {"functional", "optimistic"}, sequential = true, testName = "optimistic.CacheTest") 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) TestingUtil.killCaches((Cache) 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 { TestingUtil.killCaches((Cache) 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 { TestingUtil.killCaches((Cache) 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(); OptimisticTransactionContext entry = (OptimisticTransactionContext) table.get(gtx); assertNotNull(mgr.getTransaction()); WriteCommand command = entry.getModifications().get(0); mgr.commit(); GlobalTransaction remoteGtx = new GlobalTransaction(); Address mockAddress = EasyMock.createNiceMock(Address.class); remoteGtx.setAddress(mockAddress); //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, (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 { TestingUtil.killCaches((Cache) 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")); TestingUtil.killCaches((Cache) cache); TestingUtil.killCaches((Cache) cache2); } public void testTwoWayRemoteCacheBroadcast() throws Exception { TestingUtil.killCaches((Cache) 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")); TestingUtil.killCaches((Cache) cache); TestingUtil.killCaches((Cache) 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(); TestingUtil.killCaches((Cache) 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(); TestingUtil.killCaches((Cache) c); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/ValidatorInterceptorTest.java0000755000175000017500000003564211120422045032424 0ustar moellermoeller/* * 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.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionContext; import org.jboss.cache.transaction.TransactionTable; import org.jboss.cache.util.CachePrinter; import org.jboss.cache.util.TestingUtil; 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", "optimistic"}, sequential = true, testName = "optimistic.ValidatorInterceptorTest") 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); } @AfterMethod public void tearDown() { TestingUtil.killCaches(cache); cache = null; } 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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(), (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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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(), (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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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(), (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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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(), (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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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(), (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-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/AsyncFullStackInterceptorTest.java0000644000175000017500000003505711134665336033403 0ustar moellermoellerpackage org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Cache; 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.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionContext; import org.jboss.cache.transaction.TransactionTable; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.replicationlisteners.ReplicationListener; 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", "optimistic"}, testName = "optimistic.AsyncFullStackInterceptorTest") public class AsyncFullStackInterceptorTest extends AbstractOptimisticTestCase { 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")); TestingUtil.killCaches((Cache) 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")); TestingUtil.killCaches((Cache) 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")); TestingUtil.killCaches((Cache) cache); } public void test2InstanceCommit() throws Exception { groupIncreaser++; CacheSPI cache = createAsyncReplicatedCache(); CacheSPI cache2 = createAsyncReplicatedCache(); ReplicationListener replListener2 = ReplicationListener.getReplicationListener(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.expect(PutKeyValueCommand.class); cache.put("/one/two", "key1", pojo); mgr.commit(); replListener2.waitForReplicationToOccur(); // 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")); TestingUtil.killCaches((Cache) cache); TestingUtil.killCaches((Cache) cache2); } public void test2InstanceRemove() throws Exception { groupIncreaser++; CacheSPI cache = createAsyncReplicatedCache(); CacheSPI cache2 = createAsyncReplicatedCache(); ReplicationListener replListener2 = ReplicationListener.getReplicationListener(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.expect(PutKeyValueCommand.class); 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); replListener2.expect(); cache.removeNode("/one/two"); replListener2.waitForReplicationToOccur(); 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")); TestingUtil.killCaches((Cache) cache); TestingUtil.killCaches((Cache) cache2); } public void testValidationFailCommit2Instances() throws Exception { groupIncreaser++; CacheSPI cache = createAsyncReplicatedCache(); ReplicationListener replListener = ReplicationListener.getReplicationListener(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(); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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")); TestingUtil.killCaches((Cache) cache); TestingUtil.killCaches((Cache) cache2); } protected CacheSPI createAsyncReplicatedCache() throws Exception { return createReplicatedCache("temp" + groupIncreaser, Configuration.CacheMode.REPL_ASYNC); } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/NodeInterceptorTransactionTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/NodeInterceptorTransactionTest.jav0000755000175000017500000000371511111047320033424 0ustar moellermoeller/* * Created on 17-Feb-2005 * * * */ package org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.interceptors.OptimisticCreateIfNotExistsInterceptor; import org.jboss.cache.interceptors.OptimisticNodeInterceptor; 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; /** * @author xenephon */ @SuppressWarnings("unchecked") @Test(groups = {"functional", "optimistic"}, sequential = true, testName = "optimistic.NodeInterceptorTransactionTest") public class NodeInterceptorTransactionTest extends AbstractOptimisticTestCase { CacheSPI cache; MockInterceptor dummy; @BeforeMethod public void setUp() throws Exception { cache = createCache(); CommandInterceptor interceptor = new OptimisticCreateIfNotExistsInterceptor(); CommandInterceptor nodeInterceptor = new OptimisticNodeInterceptor(); dummy = new MockInterceptor(); interceptor.setNext(nodeInterceptor); nodeInterceptor.setNext(dummy); TestingUtil.replaceInterceptorChain(cache, interceptor); } @AfterMethod public void tearDown() { TestingUtil.killCaches(cache); cache = null; } public void testNoTransactionCRUDMethod() throws Exception { try { cache.put("/one/two", "key1", new Object()); fail(); } catch (Throwable t) { assertTrue(true); } assertEquals(null, dummy.getCalledCommand()); } public void testNoTransactionGetMethod() throws Exception { boolean fail = false; try { assertEquals(null, cache.get("/one/two", "key1")); } catch (Exception e) { fail = true; } assertTrue(fail); assertEquals(null, dummy.getCalledCommand()); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/NodeInterceptorPutEraseTest.java0000755000175000017500000001236711111047320033033 0ustar moellermoellerpackage 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.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionContext; import org.jboss.cache.transaction.TransactionTable; 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.HashMap; import java.util.Map; /** * @author xenephon */ @SuppressWarnings("unchecked") @Test(groups = {"functional", "optimistic"}, sequential = true, testName = "optimistic.NodeInterceptorPutEraseTest") public class NodeInterceptorPutEraseTest extends AbstractOptimisticTestCase { private CacheSPI cache; private TransactionManager mgr; private MockInterceptor dummy; @BeforeMethod public void setUp() throws Exception { cache = createCache(); 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); cache = null; } 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/PersistingTransientStateTest.java0000644000175000017500000000065211111047320033265 0ustar moellermoellerpackage org.jboss.cache.optimistic; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.testng.annotations.Test; @Test(groups = "functional", testName = "optimistic.PersistingTransientStateTest") public class PersistingTransientStateTest extends org.jboss.cache.statetransfer.PersistingTransientStateTest { public PersistingTransientStateTest() { nls = NodeLockingScheme.OPTIMISTIC; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/NodeInterceptorKeyValTest.java0000755000175000017500000002152311111047320032470 0ustar moellermoellerpackage 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.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionContext; import org.jboss.cache.transaction.TransactionTable; 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; /** * @author xenephon */ @Test(groups = {"functional", "optimistic"}, sequential = true, testName = "optimistic.NodeInterceptorKeyValTest") public class NodeInterceptorKeyValTest extends AbstractOptimisticTestCase { private CacheSPI cache; private MockInterceptor dummy; private TransactionManager mgr; @BeforeMethod public void setUp() throws Exception { cache = createCache(); 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); cache = null; } 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) table.get(gtx); @SuppressWarnings("unchecked") TransactionWorkspace workspace = entry.getTransactionWorkSpace(); //resume the suspended transaction GlobalTransaction gtx2 = table.get(tx2); OptimisticTransactionContext entry2 = (OptimisticTransactionContext) 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-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/MockFailureInterceptor.java0000755000175000017500000000375711111047320032041 0ustar moellermoellerpackage 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-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/DataVersionPersistenceTest.java0000644000175000017500000001221311121730272032673 0ustar moellermoellerpackage org.jboss.cache.optimistic; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.NodeSPI; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.loader.testloaders.DummySharedInMemoryCacheLoader; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.jboss.cache.util.TestingUtil; 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", "optimistic"}, sequential = true, testName = "optimistic.DataVersionPersistenceTest") public class DataVersionPersistenceTest { private Cache cache; private CacheLoader loader; @BeforeMethod public void setUp() throws IOException { cache = new UnitTestCacheFactory().createCache(false, getClass()); 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 \n bin=" + getClass()); 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).wipeBin(); TestingUtil.killCaches(cache); cache = null; } 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; } } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/DataVersionTransferTest.java0000644000175000017500000001641011120367722032203 0ustar moellermoellerpackage org.jboss.cache.optimistic; import org.jboss.cache.Cache; import org.jboss.cache.Fqn; import org.jboss.cache.NodeSPI; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.jboss.cache.util.TestingUtil; 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", "optimistic"}, sequential = true, testName = "optimistic.DataVersionTransferTest") public class DataVersionTransferTest { private List> caches = null; @BeforeMethod public void setUp() { Configuration c = new Configuration(); c.setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); c.setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); c.setCacheMode(Configuration.CacheMode.REPL_SYNC); caches = new ArrayList>(2); caches.add(new UnitTestCacheFactory().createCache(c, false, getClass())); caches.get(0).start(); c = new Configuration(); c.setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); c.setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); c.setCacheMode(Configuration.CacheMode.REPL_SYNC); caches.add(new UnitTestCacheFactory().createCache(c, false, getClass())); } @AfterMethod public void tearDown() { if (caches != null) { for (Cache cache : caches) TestingUtil.killCaches(cache); caches = null; } } 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-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/ThreadedCacheAccessTest.java0000644000175000017500000000625711134665336032064 0ustar moellermoeller/* * 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.jboss.cache.Cache; import org.jboss.cache.util.TestingUtil; 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 AT jboss DOT org) */ @Test(groups = {"functional", "optimistic"}, sequential = true, testName = "optimistic.ThreadedCacheAccessTest") 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(); TestingUtil.killCaches((Cache) cache); cache = null; threads = null; } 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 its 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-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/OptimisticVersioningTest.java0000644000175000017500000001175211134665336032462 0ustar moellermoeller/* * 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.Cache; import org.jboss.cache.util.TestingUtil; 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 AT jboss DOT org) */ @SuppressWarnings("unchecked") @Test(groups = {"functional", "optimistic"}, sequential = true, testName = "optimistic.OptimisticVersioningTest") 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(); TestingUtil.killCaches((Cache) cache1); TestingUtil.killCaches((Cache) cache2); cache1 = null; cache2 = null; } public void testVersionPropagation() { 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); // 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 = cache1.getNode(fqn).getVersion(); v2 = 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-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/OptimisticWithCacheLoaderTest.java0000644000175000017500000002220211120422045033274 0ustar moellermoeller/* * 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 AT jboss DOT org) */ @Test(groups = {"functional", "transaction", "optimistic"}, sequential = true, testName = "optimistic.OptimisticWithCacheLoaderTest") 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 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)); } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/NodeInterceptorRemoveKeyValTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/NodeInterceptorRemoveKeyValTest.ja0000755000175000017500000001223311111047320033315 0ustar moellermoeller/* * Created on 17-Feb-2005 * * * */ package 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.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionContext; import org.jboss.cache.transaction.TransactionTable; 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.HashMap; import java.util.Map; /** * @author xenephon */ @SuppressWarnings("unchecked") @Test(groups = {"functional", "optimistic"}, testName = "optimistic.NodeInterceptorRemoveKeyValTest") public class NodeInterceptorRemoveKeyValTest extends AbstractOptimisticTestCase { private CacheSPI cache; private TransactionManager mgr; private MockInterceptor dummy; @BeforeMethod public void setUp() throws Exception { cache = createCache(); 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); cache = null; } 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/NodeInterceptorGetKeysTest.java0000644000175000017500000001172411111047320032647 0ustar moellermoellerpackage 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.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionContext; import org.jboss.cache.transaction.TransactionTable; 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; /** * @author xenephon */ @Test(groups = {"functional", "optimistic"}, sequential = true, testName = "optimistic.NodeInterceptorGetKeysTest") public class NodeInterceptorGetKeysTest extends AbstractOptimisticTestCase { private CacheSPI cache; private TransactionManager mgr; private MockInterceptor dummy; @BeforeMethod public void setUp() throws Exception { cache = createCache(); 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); cache = null; } 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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()); } } ././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/NodeInterceptorGetChildrenNamesTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/NodeInterceptorGetChildrenNamesTes0000644000175000017500000001414411111047320033343 0ustar moellermoellerpackage org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; 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.OptimisticTransactionContext; import org.jboss.cache.transaction.TransactionTable; 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.Iterator; /** * @author xenephon */ @Test(groups = {"functional", "optimistic"}, sequential = true, testName = "optimistic.NodeInterceptorGetChildrenNamesTest") 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); cache = null; } @SuppressWarnings("unchecked") 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) table.get(gtx); 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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()); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/AsyncCacheTest.java0000644000175000017500000001075711134665336030277 0ustar moellermoeller/* * Created on 17-Feb-2005 * */ package org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Cache; import org.jboss.cache.config.Configuration; import org.jboss.cache.loader.SamplePojo; import org.jboss.cache.transaction.TransactionSetup; 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; @Test(groups = {"functional", "transaction", "optimistic"}, testName = "optimistic.AsyncCacheTest") 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(); TestingUtil.killCaches((Cache) cache); TestingUtil.killCaches((Cache) 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) 5000); 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) 5000); 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-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/ValidationFailureTest.java0000644000175000017500000000342711134665336031674 0ustar moellermoeller/* * 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.Cache; import org.jboss.cache.util.TestingUtil; 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 AT jboss DOT org) */ @Test(groups = {"functional", "optimistic"}, sequential = true, testName = "optimistic.ValidationFailureTest") 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()); TestingUtil.killCaches((Cache) cache); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/AbstractOptimisticTestCase.java0000644000175000017500000002440111240012635032653 0ustar moellermoeller/** * */ package org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.commands.VersionedDataCommand; import org.jboss.cache.commands.WriteCommand; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestConfigurationFactory; 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.testloaders.DummyInMemoryCacheLoader; import org.jboss.cache.loader.testloaders.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.util.TestingUtil; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.LinkedList; import java.util.List; import java.util.Random; /** * @author manik */ @Test(groups = {"functional", "optimistic"}, testName = "optimistic.AbstractOptimisticTestCase") 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 UnitTestCacheFactory().createCache(UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL), false, getClass()); 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().setTransactionContext(cache.getTransactionTable().get(gtx)); } protected CacheLoaderConfig getCacheLoaderConfig(boolean shared, boolean passivation) throws Exception { String cacheLoaderClass = shared ? DummySharedInMemoryCacheLoader.class.getName() : DummyInMemoryCacheLoader.class.getName(); String props = ""; if (shared) { props = "bin = " + getClass().getName(); } return UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(passivation, null, cacheLoaderClass, props, false, (!shared), shared, false, false); } 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 CacheSPI createPessimisticCache() throws Exception { Configuration c = new Configuration(); c.setClusterName("name"); c.setStateRetrievalTimeout(5000); c.setCacheMode(Configuration.CacheMode.REPL_SYNC); c.setIsolationLevel(IsolationLevel.REPEATABLE_READ); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); CacheSPI cache = (CacheSPI) new UnitTestCacheFactory().createCache(c, false, getClass()); cache.create(); cache.start(); return cache; } protected CacheSPI createPessimisticCacheLocal() throws Exception { Configuration c = new Configuration(); c.setClusterName("name"); c.setStateRetrievalTimeout(5000); c.setCacheMode(Configuration.CacheMode.LOCAL); c.setIsolationLevel(IsolationLevel.REPEATABLE_READ); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); CacheSPI cache = (CacheSPI) new UnitTestCacheFactory().createCache(false, getClass()); 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 { Configuration c = new Configuration(); 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()); CacheSPI cache = (CacheSPI) new UnitTestCacheFactory().createCache(c, false, getClass()); cache.getConfiguration().setSerializationExecutorPoolSize(0); 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 { Configuration c = new Configuration(); 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)); CacheSPI cache = (CacheSPI) new UnitTestCacheFactory().createCache(c, false, getClass()); 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 } new UnitTestCacheFactory().cleanUp(); } 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 (WriteCommand c : modifications) { if (c instanceof VersionedDataCommand) { ((VersionedDataCommand) c).setDataVersion(new DefaultDataVersion()); } // Object[] oa = c.getArgs(); // Object[] na = new Object[oa.length + 1]; // System.arraycopy(oa, 0, na, 0, oa.length); // na[oa.length] = new DefaultDataVersion(); // newList.add(MethodCallFactory.create(MethodDeclarations.getVersionedMethodId(c.getMethodId()), na)); } return modifications; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/RemoveBeforeCreateTest.java0000644000175000017500000000564011134665336031775 0ustar moellermoellerpackage org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.NodeSPI; import org.jboss.cache.Cache; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.config.Configuration; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.replicationlisteners.ReplicationListener; 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", "optimistic"}, testName = "optimistic.RemoveBeforeCreateTest") public class RemoveBeforeCreateTest extends AbstractOptimisticTestCase { CacheSPI[] c = null; ReplicationListener[] replListeners; TransactionManager t; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { c = new CacheSPI[2]; replListeners = new ReplicationListener[2]; c[0] = createReplicatedCache(Configuration.CacheMode.REPL_ASYNC); c[1] = createReplicatedCache(Configuration.CacheMode.REPL_ASYNC); TestingUtil.blockUntilViewsReceived(c, 20000); replListeners[0] = ReplicationListener.getReplicationListener(c[0]); replListeners[1] = ReplicationListener.getReplicationListener(c[1]); t = c[0].getTransactionManager(); } @AfterMethod(alwaysRun = true) public void tearDown() { if (c != null) { TestingUtil.killCaches((Cache) c[0]); TestingUtil.killCaches((Cache) c[1]); c = null; } } @SuppressWarnings("unchecked") public void testControl() throws Exception { replListeners[1].expectWithTx(PutKeyValueCommand.class); t.begin(); c[0].put("/control", "key", "value"); t.commit(); replListeners[1].waitForReplicationToOccur(); 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)); replListeners[1].expectWithTx(PutKeyValueCommand.class); t.begin(); c[0].removeNode(f); // should NOT barf!!! t.commit(); replListeners[1].waitForReplicationToOccur(); assertNull(c[0].getNode(f)); assertNull(c[1].getNode(f)); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/FullStackInterceptorTest.java0000644000175000017500000006673411134665336032413 0ustar moellermoellerpackage 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.Cache; import org.jboss.cache.config.Configuration; import org.jboss.cache.loader.SamplePojo; import org.jboss.cache.lock.LockManager; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionContext; import org.jboss.cache.transaction.TransactionTable; import org.jboss.cache.util.TestingUtil; 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", "optimistic"}, sequential = true, testName = "optimistic.FullStackInterceptorTest") 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 TestingUtil.killCaches((Cache) 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()); TestingUtil.killCaches((Cache) 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")); TestingUtil.killCaches((Cache) 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")); TestingUtil.killCaches((Cache) 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")); TestingUtil.killCaches((Cache) 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")); TestingUtil.killCaches((Cache) 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")); TestingUtil.killCaches((Cache) cache); TestingUtil.killCaches((Cache) 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")); TestingUtil.killCaches((Cache) cache); TestingUtil.killCaches((Cache) 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(); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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")); TestingUtil.killCaches((Cache) cache); TestingUtil.killCaches((Cache) 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(); assertEquals(pojo1, cache.get("/one/two", "key1")); // start another mgr.suspend(); mgr.begin(); 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")); // resume the suspended one mgr.resume(tx); // assert we can't see the change from tx2 as we already touched the node assertEquals(null, cache.get("/one/two", "key2")); mgr.commit(); TestingUtil.killCaches((Cache) 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(); TestingUtil.killCaches((Cache) 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(); 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(); 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(); 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) { } keys = cache.getNode("/one/two").getKeys(); assertEquals(2, keys.size());// key1 and key2 TestingUtil.killCaches((Cache) cache); } 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)); } TestingUtil.killCaches((Cache) cache1); TestingUtil.killCaches((Cache) 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)); TestingUtil.killCaches((Cache) cache1); TestingUtil.killCaches((Cache) cache2); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/MockInterceptor.java0000755000175000017500000000324611111047320030522 0ustar moellermoellerpackage 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; } }jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/LockParentVersionTest.java0000644000175000017500000000063511111047320031656 0ustar moellermoellerpackage org.jboss.cache.optimistic; import org.testng.annotations.Test; /** * @author Manik Surtani * @since 2.0.0 */ @Test(groups = {"functional", "optimistic"}, testName = "optimistic.LockParentVersionTest") public class LockParentVersionTest extends ParentVersionTest { public LockParentVersionTest() { lockParentForChildInsertRemove = true; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/TxInterceptorTest.java0000755000175000017500000006514511240012635031076 0ustar moellermoeller/* * Created on 17-Feb-2005 * * * */ package org.jboss.cache.optimistic; import org.easymock.EasyMock; import org.jboss.cache.CacheSPI; import org.jboss.cache.commands.WriteCommand; 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.OptimisticTransactionContext; import org.jboss.cache.transaction.TransactionSetup; import org.jboss.cache.transaction.TransactionTable; import org.jboss.cache.util.TestingUtil; 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; @Test(groups = {"functional", "transaction", "optimistic"}, sequential = true, testName = "optimistic.TxInterceptorTest") 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()); TestingUtil.killCaches(cache); } 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()); TestingUtil.killCaches(cache); } 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()); TestingUtil.killCaches(cache); } 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()); TestingUtil.killCaches(cache); } 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()); TestingUtil.killCaches(cache); } 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()); TestingUtil.killCaches(cache); } 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(); OptimisticTransactionContext entry = (OptimisticTransactionContext) table.get(gtx); assertNotNull(mgr.getTransaction()); WriteCommand 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(EasyMock.createNiceMock(Address.class)); //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()), (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()); TestingUtil.killCaches(cache); } 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(); OptimisticTransactionContext entry = (OptimisticTransactionContext) table.get(gtx); assertEquals(tx, mgr.getTransaction()); //now send the remote prepare GlobalTransaction remoteGtx = new GlobalTransaction(); remoteGtx.setAddress(EasyMock.createNiceMock(Address.class)); //hack the method call to make it have the remote globalTransaction WriteCommand command = entry.getModifications().get(0); command.setGlobalTransaction(remoteGtx); //call our remote method OptimisticPrepareCommand prepareCommand = new OptimisticPrepareCommand(remoteGtx, injectDataVersion(entry.getModifications()), (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()); TestingUtil.killCaches(cache); } 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(); OptimisticTransactionContext entry = (OptimisticTransactionContext) table.get(gtx); assertEquals(tx, mgr.getTransaction()); //now send the remote prepare GlobalTransaction remoteGtx = new GlobalTransaction(); remoteGtx.setAddress(EasyMock.createNiceMock(Address.class)); //hack the method call to make it have the remote globalTransaction WriteCommand command = entry.getModifications().get(0); command.setGlobalTransaction(remoteGtx); //call our remote method OptimisticPrepareCommand prepareCommand = new OptimisticPrepareCommand(remoteGtx, injectDataVersion(entry.getModifications()), (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()); TestingUtil.killCaches(cache); } 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(); OptimisticTransactionContext entry = (OptimisticTransactionContext) table.get(gtx); assertEquals(tx, mgr.getTransaction()); //now send the remote prepare GlobalTransaction remoteGtx = new GlobalTransaction(); remoteGtx.setAddress(EasyMock.createNiceMock(Address.class)); //hack the method call to make it have the remote globalTransaction WriteCommand command = entry.getModifications().get(0); command.setGlobalTransaction(remoteGtx); //call our remote method OptimisticPrepareCommand prepareCommand = new OptimisticPrepareCommand(remoteGtx, injectDataVersion(entry.getModifications()), (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()); TestingUtil.killCaches(cache); } 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(); OptimisticTransactionContext entry = (OptimisticTransactionContext) table.get(gtx); assertNotNull(mgr.getTransaction()); WriteCommand 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(EasyMock.createNiceMock(Address.class)); // 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()), (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()); TestingUtil.killCaches(cache); } 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(); OptimisticTransactionContext entry = (OptimisticTransactionContext) table.get(gtx); assertNotNull(mgr.getTransaction()); WriteCommand 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(EasyMock.createNiceMock(Address.class)); command.setGlobalTransaction(remoteGtx); //call our remote method OptimisticPrepareCommand prepareCommand = new OptimisticPrepareCommand(remoteGtx, injectDataVersion(entry.getModifications()), (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()); TestingUtil.killCaches(cache); } 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)); TestingUtil.killCaches(cache); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/VersioningOnReadTest.java0000644000175000017500000000663111134665336031506 0ustar moellermoellerpackage org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Cache; import org.jboss.cache.util.TestingUtil; 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", "optimistic"}, sequential = true, testName = "optimistic.VersioningOnReadTest") 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(); TestingUtil.killCaches((Cache) cache); cache = null; } 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(); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/NodeInterceptorRemoveDataTest.java0000755000175000017500000001512611111047320033326 0ustar moellermoellerpackage 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.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionContext; import org.jboss.cache.transaction.TransactionTable; 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.HashMap; import java.util.Map; /** * @author xenephon */ @SuppressWarnings("unchecked") @Test(groups = {"functional", "optimistic"}, sequential = true, testName = "optimistic.NodeInterceptorRemoveDataTest") public class NodeInterceptorRemoveDataTest extends AbstractOptimisticTestCase { private CacheSPI cache; private TransactionManager mgr; private MockInterceptor dummy; @BeforeMethod public void setUp() throws Exception { cache = createCache(); 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); cache = null; } 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/OptimisticLockInterceptorTest.java0000644000175000017500000001420711111047320033422 0ustar moellermoellerpackage 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.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.jboss.cache.util.TestingUtil; 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 AT jboss DOT org) */ @Test(groups = {"functional", "optimistic"}, sequential = true, testName = "optimistic.OptimisticLockInterceptorTest") 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() { TestingUtil.killCaches(cache); cache = null; } 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); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/HasChildTest.java0000644000175000017500000000411311134665336027742 0ustar moellermoellerpackage org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Cache; import org.jboss.cache.util.TestingUtil; 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", "optimistic"}, sequential = true, testName = "optimistic.HasChildTest") 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(); TestingUtil.killCaches((Cache) cache); cache = null; } 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-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/ChildMapLazyLoadingTest.java0000644000175000017500000001774411120367722032111 0ustar moellermoellerpackage org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionContext; import org.jboss.cache.util.TestingUtil; 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", "optimistic"}, sequential = true, testName = "optimistic.ChildMapLazyLoadingTest") 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 UnitTestCacheFactory().createCache(false, getClass()); 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() { TestingUtil.killCaches(cache); cache = null; } 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); OptimisticTransactionContext te = (OptimisticTransactionContext) cache.getTransactionTable().get(gtx); TransactionWorkspace tw = te.getTransactionWorkSpace(); return tw.getNode(fqn); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/NodeInterceptorGetKeyValTest.java0000644000175000017500000002077111111047320033131 0ustar moellermoellerpackage 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.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionContext; import org.jboss.cache.transaction.TransactionTable; 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; /** * @author xenephon */ @Test(groups = {"functional", "optimistic"}, sequential = true, testName = "optimistic.NodeInterceptorGetKeyValTest") public class NodeInterceptorGetKeyValTest extends AbstractOptimisticTestCase { private CacheSPI cache; private TransactionManager mgr; private MockInterceptor dummy; @BeforeMethod public void setUp() throws Exception { cache = createCache(); 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); cache = null; } 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) table.get(gtx); @SuppressWarnings("unchecked") TransactionWorkspace workspace = entry.getTransactionWorkSpace(); //resume the suspended transaction GlobalTransaction gtx2 = table.get(tx2); OptimisticTransactionContext entry2 = (OptimisticTransactionContext) 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-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/TestListener.java0000755000175000017500000000204411111047320030032 0ustar moellermoeller/* * 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; } } ././@LongLink0000000000000000000000000000016200000000000011564 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/OptimisticCreateIfNotExistsInterceptorTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/OptimisticCreateIfNotExistsInterce0000755000175000017500000001122111111047320033404 0ustar moellermoeller/* * 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.transaction.DummyTransactionManager; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionContext; import org.jboss.cache.transaction.TransactionTable; 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; /** * @author xenephon */ @SuppressWarnings("unchecked") @Test(groups = {"functional", "optimistic"}, sequential = true, testName = "optimistic.OptimisticCreateIfNotExistsInterceptorTest") public class OptimisticCreateIfNotExistsInterceptorTest extends AbstractOptimisticTestCase { protected TransactionManager txManager; protected Transaction tx; protected GlobalTransaction gtx; protected TransactionTable table; protected OptimisticTransactionContext entry; protected TransactionWorkspace workspace; CacheSPI cache; MockInterceptor dummy; SamplePojo pojo; @BeforeMethod public void setUp() throws Exception { pojo = new SamplePojo(21, "test"); cache = createCache(); CommandInterceptor interceptor = new OptimisticCreateIfNotExistsInterceptor(); dummy = new MockInterceptor(); interceptor.setNext(dummy); TestingUtil.replaceInterceptorChain(cache, interceptor); setupTransactionsInInvocationCtx(cache); } @AfterMethod public void tearDown() { TestingUtil.killCaches(cache); cache = null; } 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 = (OptimisticTransactionContext) table.get(gtx); workspace = entry.getTransactionWorkSpace(); setupTransactions(cache, tx); } public void testNodeCreation() throws Exception { 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(); } public void testInvalidTransaction() throws Exception { 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().setTransactionContext(null); try { cache.put("/one/two/three", "key1", pojo); assertTrue("Should never be reched", false); } catch (Throwable t) { assertTrue(true); } } public void testMultiplePut() throws Exception { 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(); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/NodeInterceptorRemoveNodeTest.java0000755000175000017500000003274311120422045033347 0ustar moellermoeller/* * 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.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.OptimisticTransactionContext; 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; import org.jboss.cache.util.TestingUtil; /** * @author xenephon */ @Test(groups = {"functional", "optimistic"}, sequential = true, testName = "optimistic.NodeInterceptorRemoveNodeTest") @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(); cache.addInterceptor(dummy, CallInterceptor.class); cache.removeInterceptor(CallInterceptor.class); mgr = cache.getTransactionManager(); } @AfterMethod(alwaysRun = true) public void tearDown() { super.tearDown(); TestingUtil.killCaches(cache); cache = null; } 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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")).isRemoved()); assertNotNull(workspace.getNode(Fqn.fromString("/one"))); assertEquals(false, workspace.getNode(Fqn.fromString("/one")).isRemoved()); List> mergedChildren = workspace.getNode(Fqn.fromString("/one")).getMergedChildren(); assertEquals(1, mergedChildren.get(1).size()); 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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")).isRemoved()); assertNotNull(workspace.getNode(Fqn.fromString("/one"))); assertEquals(true, workspace.getNode(Fqn.fromString("/one")).isRemoved()); 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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")).isRemoved()); assertNotNull(workspace.getNode(Fqn.fromString("/one"))); assertEquals(true, workspace.getNode(Fqn.fromString("/one")).isRemoved()); //now put /one/two back in cache.removeNode("/one"); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertEquals(true, workspace.getNode(Fqn.fromString("/one/two")).isRemoved()); assertNotNull(workspace.getNode(Fqn.fromString("/one"))); assertEquals(true, workspace.getNode(Fqn.fromString("/one")).isRemoved()); 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) table.get(gtx); TransactionWorkspace workspace = entry.getTransactionWorkSpace(); WorkspaceNode one = workspace.getNode(Fqn.fromString("/one")); WorkspaceNode 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")).isRemoved()); assertNotNull(workspace.getNode(Fqn.fromString("/one"))); assertEquals(true, workspace.getNode(Fqn.fromString("/one")).isRemoved()); //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.isRemoved()); assertSame(two, twoAfter); assertEquals(false, twoAfter.isRemoved()); 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) table.get(gtx); TransactionWorkspace workspace = entry.getTransactionWorkSpace(); WorkspaceNode one = workspace.getNode(Fqn.fromString("/one")); WorkspaceNode 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")).isRemoved()); assertNotNull(workspace.getNode(Fqn.fromString("/one"))); assertEquals(true, workspace.getNode(Fqn.fromString("/one")).isRemoved()); //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.isRemoved()); assertEquals(two, twoAfter); assertEquals(true, twoAfter.isRemoved()); 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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")).isRemoved()); assertNotNull(workspace.getNode(Fqn.fromString("/one"))); assertEquals(false, workspace.getNode(Fqn.fromString("/one")).isRemoved()); 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-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/ConcurrentTransactionTest.java0000644000175000017500000002447611120422045032610 0ustar moellermoeller/* * 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 AT jboss DOT org) */ @Test(groups = {"functional", "optimistic"}, sequential = true, testName = "optimistic.ConcurrentTransactionTest") 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() { TestingUtil.killCaches(cache); 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(); } 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() { 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); } } } } ././@LongLink0000000000000000000000000000017200000000000011565 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/ThreadedOptimisticCreateIfNotExistsInterceptorTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/ThreadedOptimisticCreateIfNotExist0000755000175000017500000001346611111047320033365 0ustar moellermoeller/* * Created on 17-Feb-2005 * * * */ package 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.base.CommandInterceptor; import org.jboss.cache.loader.SamplePojo; import org.jboss.cache.transaction.DummyTransactionManager; import org.jboss.cache.transaction.OptimisticTransactionContext; import org.jboss.cache.util.TestingUtil; 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", "optimistic"}, sequential = true, testName = "optimistic.ThreadedOptimisticCreateIfNotExistsInterceptorTest") 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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-3.2.8.GA/src/test/java/org/jboss/cache/optimistic/NodeInterceptorPutMapTest.java0000755000175000017500000001277411111047320032513 0ustar moellermoellerpackage 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.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionContext; import org.jboss.cache.transaction.TransactionTable; 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.HashMap; import java.util.Map; /** * @author xenephon */ @SuppressWarnings("unchecked") @Test(groups = {"functional", "optimistic"}, sequential = true, testName = "optimistic.NodeInterceptorPutMapTest") public class NodeInterceptorPutMapTest extends AbstractOptimisticTestCase { private CacheSPI cache; private TransactionManager mgr; private MockInterceptor dummy; @BeforeMethod public void setUp() throws Exception { cache = createCache(); 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); cache = null; } 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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); OptimisticTransactionContext entry = (OptimisticTransactionContext) 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-3.2.8.GA/src/test/java/org/jboss/cache/TreeNodeTest.java0000644000175000017500000000266611120367722025610 0ustar moellermoellerpackage 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 7284 2008-12-12 05:00:02Z mircea.markus $ */ @Test(groups = {"functional"}, sequential = true, testName = "TreeNodeTest") public class TreeNodeTest { CacheSPI cache; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache = (CacheSPI) new UnitTestCacheFactory().createCache(getClass()); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { cache.stop(); cache.destroy(); cache = null; } 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-3.2.8.GA/src/test/java/org/jboss/cache/GlobalTransactionTest.java0000644000175000017500000001133111120422620027463 0ustar moellermoeller/* * 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 7305 2008-12-12 08:49:20Z mircea.markus $ */ public class GlobalTransactionTest { @Test(groups = {"functional"}, testName = "GlobalTransactionTest") public void testEquality() throws UnknownHostException { IpAddress a1 = new IpAddress("localhost", 4444); GlobalTransaction tx1, tx2; tx1 = GlobalTransaction.create(a1); tx2 = GlobalTransaction.create(a1); assertTrue(tx1.equals(tx2) == false); tx2 = tx1; assertTrue(tx1.equals(tx2)); } @Test(groups = {"functional"}, testName = "GlobalTransactionTest") public void testEqualityWithOtherObject() throws UnknownHostException { IpAddress a1 = new IpAddress("localhost", 4444); GlobalTransaction tx1 = GlobalTransaction.create(a1); assertFalse(tx1.equals(Thread.currentThread())); } @Test(groups = {"functional"}, testName = "GlobalTransactionTest") public void testEqualityWithNull() throws UnknownHostException { IpAddress a1 = new IpAddress("localhost", 4444); GlobalTransaction tx1 = GlobalTransaction.create(a1); assertFalse(tx1.equals(null)); } @Test(groups = {"functional"}, testName = "GlobalTransactionTest") public void testHashcode() throws UnknownHostException { IpAddress a1 = new IpAddress("localhost", 4444); GlobalTransaction tx1, tx2; tx1 = GlobalTransaction.create(a1); tx2 = GlobalTransaction.create(a1); 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"}, testName = "GlobalTransactionTest") 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(); 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"}, testName = "GlobalTransactionTest") 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"}, testName = "GlobalTransactionTest") 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)); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/ComparatorTest.java0000755000175000017500000001032211111047320026166 0ustar moellermoellerpackage org.jboss.cache; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Tests {@link FqnComparator}. * * @author xenephon */ @Test(groups = "unit", testName = "ComparatorTest") 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(Collections.emptyList()); Fqn fqn2 = Fqn.fromList(Collections.emptyList()); 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(Collections.emptyList()); 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(Collections.emptyList()); 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"; } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/lock/0000755000175000017500000000000011675221362023322 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/lock/PessimisticAcquireAllTest.java0000644000175000017500000000564111120367722031266 0ustar moellermoellerpackage org.jboss.cache.lock; import org.jboss.cache.CacheSPI; import org.jboss.cache.UnitTestCacheFactory; 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: PessimisticAcquireAllTest.java 7284 2008-12-12 05:00:02Z mircea.markus $ */ @Test(groups = {"functional"}, testName = "lock.PessimisticAcquireAllTest") public class PessimisticAcquireAllTest { 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); cache = null; cache2 = null; } 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 UnitTestCacheFactory().createCache(false, getClass()); c.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); c.getConfiguration().setCacheMode(mode); c.getConfiguration().setIsolationLevel(level); c.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); c.create(); c.start(); return c; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/lock/ReadWriteLockTest.java0000644000175000017500000000673211120421675027526 0ustar moellermoellerpackage 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 7295 2008-12-12 08:41:33Z mircea.markus $ */ @Test(groups = { "functional" }, sequential = true, testName = "lock.ReadWriteLockTest") 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 { lock.writeLock().lock(); sleep(1000); } catch (InterruptedException e) { ex = e; } finally { lock.writeLock().unlock(); } } } class Reader extends Thread { public Reader(String name) { super(name); } public void run() { try { lock.readLock().lock(); sleep(500); } catch (InterruptedException e) { ex = e; } finally { lock.readLock().unlock(); } } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/lock/UpgradeLockTest.java0000644000175000017500000001267711120367722027236 0ustar moellermoeller/* * * 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.config.Configuration; 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; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.util.TestingUtil; /** * Tests upgrade locks from read -> write * * @author Bela Ban * @version $Id: UpgradeLockTest.java 7284 2008-12-12 05:00:02Z mircea.markus $ */ @Test(groups = {"functional"}, sequential = true, testName = "lock.UpgradeLockTest") 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) { TestingUtil.killCaches(cache); cache = null; } // We just can't kill DummyTransactionManager. We are sharing single instance in more tests. TestingUtil.killTransaction(DummyTransactionManager.getInstance()); /* 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 UnitTestCacheFactory().createCache(false, getClass()); c.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); 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-3.2.8.GA/src/test/java/org/jboss/cache/lock/LockParentRootFlagTest.java0000644000175000017500000000517511120367722030531 0ustar moellermoeller/* * 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.UnitTestCacheFactory; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.Test; @Test(groups = "functional", testName = "lock.LockParentRootFlagTest") public class LockParentRootFlagTest { // to test https://jira.jboss.org/jira/browse/JBCACHE-1420 public void testMVCCSet() { doTest(NodeLockingScheme.MVCC, true); } public void testMVCCUnset() { doTest(NodeLockingScheme.MVCC, false); } 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 UnitTestCacheFactory().createCache(false, getClass()); c.getConfiguration().setNodeLockingScheme(nls); c.getConfiguration().setLockParentForChildInsertRemove(set); if (nls.isVersionedScheme()) c.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); c.start(); assert c.getRoot().isLockForChildInsertRemove() == set; } finally { TestingUtil.killCaches(c); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/lock/IdentityLockTest.java0000644000175000017500000003357411136035650027436 0ustar moellermoeller/* * 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: 7567 $ */ @Test(groups = {"functional"}, sequential = true, testName = "lock.IdentityLockTest") 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(); // // // 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(); // // // 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(); // // // // o1 must be the owner of the lock // assertTrue(lock_.isOwner(o1)); // // t3.start(); // assertTrue(lock_.isOwner(o1)); // // log("o1 releasing lock"); // lock_.release(o1); // // 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(); // 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(); // 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) { 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++) { lockMap.addReader(new Object()); } } }; final boolean[] flags = new boolean[]{false, false}; //{testFailure, stopTheThread} Thread toStringProcessor = new Thread() { public void run() { for (int i = 0; i < opCount; i++) { try { iLock.toString(new StringBuilder(), false); } catch (Exception e) { e.printStackTrace(); flags[0] = true; break; } if (flags[1]) break; } } }; toStringProcessor.start(); readLockChanger.start(); readLockChanger.join(); flags[1] = true;//stopping the toStringProcessor toStringProcessor.join(); assertFalse(flags[0]); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/lock/ReadWriteLockWithUpgradeTest.java0000644000175000017500000005330511135045410031663 0ustar moellermoellerpackage 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.CountDownLatch; 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" }, testName = "lock.ReadWriteLockWithUpgradeTest") 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(); } /***************************************************************/ /* 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. */ class ReadThread extends Thread { private String caseNum; private String name; volatile CountDownLatch notifyBeforeSecondOp = new CountDownLatch(1); volatile CountDownLatch notifyBeforeFinish = new CountDownLatch(1); volatile CountDownLatch waitLatch; private String errMsg; private int secondOP; ReadThread(String caseNum, String name, String errMsg, int secondOP, CountDownLatch waitLatch) { this.caseNum = caseNum; this.name = name; this.errMsg = errMsg; this.secondOP = secondOP; this.waitLatch = waitLatch; } public void run() { Lock rlock = lock_.readLock(); try { if (!rlock.tryLock(0, TimeUnit.MILLISECONDS)) { String str = caseNum + "-" + name + "-RL-0"; postLockingResult(str); processNotifications(); return; } // OK, read lock obtained, sleep and release it. String str = caseNum + "-" + name + "-RL-1"; postLockingResult(str); processNotifications(); if (secondOP == INVOKE_READ) { acquireReadLock(caseNum, name, errMsg); } else if (secondOP == INVOKE_WRITE) { acquireWriteLock(caseNum, name, errMsg); } else if (secondOP == INVOKE_UPGRADE) { acquireUpgradeLock(caseNum, name, errMsg); } rlock.unlock(); notifyBeforeFinish.countDown(); } catch (Exception ex) { } } private void processNotifications() throws InterruptedException { notifyBeforeSecondOp.countDown(); if (waitLatch != null) waitLatch.await(10, TimeUnit.SECONDS); } } private class WriteThread extends Thread { String caseNum; String name; volatile CountDownLatch notifyLatch = new CountDownLatch(1); volatile CountDownLatch waitLatch; String errMsg; int secondOP; WriteThread(String caseNum, String name, String errMsg, int secondOP, CountDownLatch waitLatch) { this.caseNum = caseNum; this.name = name; this.errMsg = errMsg; this.secondOP = secondOP; this.waitLatch = waitLatch; } public void run() { try { Lock wlock = lock_.writeLock(); if (!wlock.tryLock(0, TimeUnit.MILLISECONDS)) { String str = caseNum + "-" + name + "-WL-0"; postLockingResult(str); processNotifications(); return; } // OK, write lock obtained, sleep and release it. String str = caseNum + "-" + name + "-WL-1"; postLockingResult(str); processNotifications(); if (secondOP == INVOKE_READ) { acquireReadLock(caseNum, name, errMsg); } else if (secondOP == INVOKE_WRITE) { acquireWriteLock(caseNum, name, errMsg); } else if (secondOP == INVOKE_UPGRADE) { acquireUpgradeLock(caseNum, name, errMsg); } wlock.unlock(); } catch (Exception ex) { ex.printStackTrace(); } } private void processNotifications() throws InterruptedException { notifyLatch.countDown(); if (waitLatch != null) waitLatch.await(10, TimeUnit.SECONDS); } } class UpgradeThread extends Thread { String caseNum; String name; private CountDownLatch notifyBeforeFinish = new CountDownLatch(1); private CountDownLatch notifyBeforeSecondOp = new CountDownLatch(1); private CountDownLatch beforeUpgrateWait; private CountDownLatch beforeFinishWait; String errMsg; int secondOP; UpgradeThread(String caseNum, String name, String errMsg, int secondOP, CountDownLatch beforeUpgrateWait) { this.caseNum = caseNum; this.name = name; this.errMsg = errMsg; this.secondOP = secondOP; this.beforeUpgrateWait = beforeUpgrateWait; } public void run() { try { Lock rlock = lock_.readLock(); Lock wlock; if (!rlock.tryLock(0, TimeUnit.MILLISECONDS)) { String str = caseNum + "-" + name + "-RL-0"; postLockingResult(str); notifyBeforeFinish.countDown(); if (beforeUpgrateWait != null) beforeUpgrateWait.await(10, TimeUnit.SECONDS); return; } // OK, read lock obtained, sleep and upgrade it later. notifyBeforeSecondOp.countDown(); if (beforeUpgrateWait != null) beforeUpgrateWait.await(10, TimeUnit.SECONDS); String str = caseNum + "-" + name + "-UL-"; if ((wlock = lock_.upgradeLockAttempt(0)) == null) { str += "0"; } else { str += "1"; } postLockingResult(str); // Sleep again and then release the lock. TestingUtil.sleepThread(SLEEP_MSECS); if (wlock != null) { wlock.unlock(); } rlock.unlock(); notifyBeforeFinish.countDown(); if (beforeFinishWait != null) beforeFinishWait.await(10, TimeUnit.SECONDS); } 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. 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 String errMsg) { try { Lock rlock = lock_.readLock(); if (!rlock.tryLock(0, TimeUnit.MILLISECONDS)) { String str = caseNum + "-" + name + "-RL-0"; postLockingResult(str); return; } // OK, read lock obtained, sleep and release it. String str = caseNum + "-" + name + "-RL-1"; postLockingResult(str); TestingUtil.sleepThread(SLEEP_MSECS); rlock.unlock(); } 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 String errMsg) { try { Lock wlock = lock_.writeLock(); if (!wlock.tryLock(0, TimeUnit.MILLISECONDS)) { String str = caseNum + "-" + name + "-WL-0"; postLockingResult(str); return; } // OK, write lock obtained, sleep and release it. String str = caseNum + "-" + name + "-WL-1"; postLockingResult(str); TestingUtil.sleepThread(SLEEP_MSECS); wlock.unlock(); } 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 String errMsg) { try { Lock ulock = null; if ((ulock = lock_.upgradeLockAttempt(0)) == null) { String str = caseNum + "-" + name + "-UL-0"; postLockingResult(str); return; } // OK, write lock obtained, sleep and release it. String str = caseNum + "-" + name + "-UL-1"; postLockingResult(str); TestingUtil.sleepThread(SLEEP_MSECS); ulock.unlock(); } 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) { // 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; } } 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"; ReadThread t1 = new ReadThread(caseNum, "t1", "1st read lock attempt failed", NO_MORE_OP, null); ReadThread t2 = new ReadThread(caseNum, "t2", "2nd read lock attempt failed", INVOKE_WRITE, t1.notifyBeforeSecondOp); 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"; ReadThread t1 = new ReadThread(caseNum, "t1", "1st read lock attempt failed", INVOKE_WRITE, null); ReadThread t2 = new ReadThread(caseNum, "t2", "2nd read lock attempt failed", NO_MORE_OP, t1.notifyBeforeSecondOp); 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 = new ReadThread(caseNum, "t1", "1st read lock attempt failed", INVOKE_UPGRADE, null); 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", "1st read lock attempt failed"); acquireWriteLock(caseNum, "t1.1", "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", "1st write lock attempt failed"); acquireReadLock(caseNum, "t1.1", "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 = new ReadThread(caseNum, "t1", "1st read lock attempt failed", NO_MORE_OP, null); Thread t2 = new ReadThread(caseNum, "t2", "2nd read lock attempt failed", NO_MORE_OP, null); 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"; CountDownLatch waitFor = new CountDownLatch(1); ReadThread t1 = new ReadThread(caseNum, "t1", "1st write lock attempt failed", NO_MORE_OP, waitFor); WriteThread t2 = new WriteThread(caseNum, "t2", "2nd read lock attempt failed", NO_MORE_OP, waitFor); t1.start(); t2.start(); t1.notifyBeforeSecondOp.await(); t2.notifyLatch.await(); waitFor.countDown(); waitFor.countDown(); 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"; CountDownLatch waitFor = new CountDownLatch(1); WriteThread t1 = new WriteThread(caseNum, "t1", "1st write lock attempt failed", NO_MORE_OP, waitFor); ReadThread t2 = new ReadThread(caseNum, "t2", "2nd read lock attempt failed", NO_MORE_OP, waitFor); t1.start(); t2.start(); t1.notifyLatch.await(); t2.notifyBeforeSecondOp.await(); waitFor.countDown(); 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"; CountDownLatch waitFor = new CountDownLatch(1); WriteThread t1 = new WriteThread(caseNum, "t1", "1st write lock attempt failed", NO_MORE_OP, waitFor); WriteThread t2 = new WriteThread(caseNum, "t2", "2nd write lock attempt failed", NO_MORE_OP, waitFor); t1.start(); t2.start(); t1.notifyLatch.await(); t2.notifyLatch.await(); waitFor.countDown(); 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"; CountDownLatch waitFor = new CountDownLatch(1); ReadThread t1 = new ReadThread(caseNum, "t1", "1st read lock attempt failed", NO_MORE_OP, waitFor); UpgradeThread t2 = new UpgradeThread(caseNum, "t2", "2nd upgrade lock attempt failed", NO_MORE_OP, t1.notifyBeforeSecondOp); t2.beforeFinishWait = waitFor; t1.start(); t2.start(); t2.notifyBeforeFinish.await(10, TimeUnit.SECONDS); waitFor.countDown(); t1.join(3000); t2.join(3000); assertTrue(checkLockingResult(caseNum + "-t1-RL-1")); assertTrue(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"; CountDownLatch waitFor = new CountDownLatch(1); ReadThread t1 = new ReadThread(caseNum, "t1", "1st read lock attempt failed", NO_MORE_OP, waitFor); ReadThread t2 = new ReadThread(caseNum, "t2", "2nd read lock attempt failed", INVOKE_UPGRADE, t1.notifyBeforeSecondOp); t1.start(); t2.start(); t1.notifyBeforeSecondOp.await(10, TimeUnit.SECONDS); t2.notifyBeforeFinish.await(10, TimeUnit.SECONDS); waitFor.countDown(); 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-3.2.8.GA/src/test/java/org/jboss/cache/lock/AbstractLockManagerRecordingTest.java0000644000175000017500000000600011111047320032506 0ustar moellermoellerpackage org.jboss.cache.lock; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.UnversionedNode; import org.jboss.cache.factories.context.ContextFactory; import org.jboss.cache.invocation.InvocationContextContainer; import org.jboss.cache.invocation.NodeInvocationDelegate; import static org.jboss.cache.lock.LockType.WRITE; import org.jboss.cache.transaction.DummyTransaction; import org.jboss.cache.transaction.DummyTransactionManager; import org.jboss.cache.transaction.GlobalTransaction; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import javax.transaction.RollbackException; import javax.transaction.SystemException; @Test(groups = "unit") public abstract class AbstractLockManagerRecordingTest { protected class AbstractLockManagerRecordingTestTL { public LockManager lm; public InvocationContextContainer icc; protected ContextFactory contextFactory; } protected ThreadLocal threadLocal = new ThreadLocal(); protected boolean fqnBasedLocking = true; @AfterMethod public void tearDown() { AbstractLockManagerRecordingTestTL tl = threadLocal.get(); if (tl != null) { tl.lm = null; threadLocal.set(null); } } public void testRecordingLocksNoTx() throws InterruptedException { AbstractLockManagerRecordingTestTL tl = threadLocal.get(); LockManager lm = tl.lm; Fqn fqn = Fqn.fromString("/a/b/c"); NodeSPI node = createNode(fqn); InvocationContext ctx = tl.icc.get(); // lock and record. lm.lockAndRecord(node, WRITE, ctx); assert ctx.getLocks().contains(fqnBasedLocking ? fqn : node.getLock()); assert ctx.getLocks().size() == 1; assert lm.isLocked(node) : "Should be locked"; lm.unlock(ctx); assert !lm.isLocked(node) : "Should not be locked"; } public void testRecordingLocksWithTx() throws InterruptedException, SystemException, RollbackException { AbstractLockManagerRecordingTestTL tl = threadLocal.get(); LockManager lm = tl.lm; Fqn fqn = Fqn.fromString("/a/b/c"); NodeSPI node = createNode(fqn); InvocationContext ctx = tl.icc.get(); ctx.setGlobalTransaction(new GlobalTransaction()); ctx.setTransaction(new DummyTransaction(DummyTransactionManager.getInstance())); ctx.setTransactionContext(tl.contextFactory.createTransactionContext(ctx.getTransaction())); // lock and record. lm.lockAndRecord(node, WRITE, ctx); assert ctx.getLocks().contains(fqnBasedLocking ? fqn : node.getLock()); assert ctx.getTransactionContext().getLocks().size() == 1; assert lm.isLocked(node) : "Should be locked"; lm.unlock(ctx); assert !lm.isLocked(node) : "Should not be locked"; } protected NodeSPI createNode(Fqn fqn) { UnversionedNode un = new UnversionedNode(fqn); return new NodeInvocationDelegate(un); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/lock/MVCCLockManagerNoTxTest.java0000644000175000017500000000061511111047320030455 0ustar moellermoellerpackage org.jboss.cache.lock; import org.testng.annotations.Test; import javax.transaction.TransactionManager; @Test(groups = {"unit", "mvcc"}, testName = "lock.MVCCLockManagerNoTxTest") public class MVCCLockManagerNoTxTest extends MVCCLockManagerTest { @Override protected TransactionManager getTransactionManager() { return null; // force the use of JDK ReentrantLocks. } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/lock/PessimisticLockTest.java0000644000175000017500000000776411120367722030144 0ustar moellermoellerpackage org.jboss.cache.lock; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.NodeSPI; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import static org.jboss.cache.lock.LockType.READ; import static org.jboss.cache.lock.LockType.WRITE; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.jboss.cache.util.TestingUtil; 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", sequential = true, testName = "lock.PessimisticLockTest") 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 UnitTestCacheFactory().createCache(false, getClass()); cache.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); cache.start(); tm = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); lockManager = TestingUtil.extractLockManager(cache); } @AfterMethod(alwaysRun = true) public void tearDown() { TestingUtil.killCaches(cache); cache = null; } 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.getParentDirect(), READ)); assertFalse(lockManager.isLocked(n.getParentDirect(), WRITE)); assertTrue(lockManager.isLocked(n.getParentDirect().getParentDirect(), READ)); assertFalse(lockManager.isLocked(n.getParentDirect().getParentDirect(), 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.getParentDirect(), READ)); assertFalse(lockManager.isLocked(n.getParentDirect(), WRITE)); assertTrue(lockManager.isLocked(n.getParentDirect().getParentDirect(), READ)); assertFalse(lockManager.isLocked(n.getParentDirect().getParentDirect(), 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.getParentDirect(), READ)); assertFalse(lockManager.isLocked(n.getParentDirect(), WRITE)); assertTrue(lockManager.isLocked(n.getParentDirect().getParentDirect(), READ)); assertFalse(lockManager.isLocked(n.getParentDirect().getParentDirect(), WRITE)); tm.commit(); assertNoStaleLocks(); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/lock/NonBlockingWriterLockTest.java0000644000175000017500000005460111120421675031236 0ustar moellermoellerpackage 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, testName = "lock.NonBlockingWriterLockTest") // 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"); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { } // For debugging purpose private static void logX(String 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-3.2.8.GA/src/test/java/org/jboss/cache/lock/WriteLockOnParentTest.java0000644000175000017500000001704211120367722030377 0ustar moellermoellerpackage org.jboss.cache.lock; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration.NodeLockingScheme; 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 org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.util.TestingUtil; @Test(groups = {"functional"}, sequential = true, testName = "lock.WriteLockOnParentTest") 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 UnitTestCacheFactory().createCache(false, getClass()); cache.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache.getConfiguration().setNodeLockingScheme(NodeLockingScheme.PESSIMISTIC); // 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. } } TestingUtil.killCaches(cache); cache = null; } 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-3.2.8.GA/src/test/java/org/jboss/cache/lock/pessimistic/0000755000175000017500000000000011675221357025662 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/lock/pessimistic/ConcurrentPutRemoveTest.java0000644000175000017500000000775511120421675033362 0ustar moellermoellerpackage 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.UnitTestCacheFactory; import org.jboss.cache.Fqn; 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.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, testName = "lock.pessimistic.ConcurrentPutRemoveTest") // 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 UnitTestCacheFactory().createCache(false, getClass()); 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); cache = null; 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 { 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; } } } } ././@LongLink0000000000000000000000000000016200000000000011564 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/lock/ReentrantWriterPreference2Readers1WriterLockTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/lock/ReentrantWriterPreference2Readers1Writer0000644000175000017500000001145211120421675033246 0ustar moellermoeller/* * 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 7295 2008-12-12 08:41:33Z mircea.markus $ */ @Test(groups = {"functional"}, enabled = false, testName = "lock.ReentrantWriterPreference2Readers1WriterLockTest") // 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; } 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 { rl.lock(); synchronized (this) { this.wait(); } } catch (InterruptedException e) { } finally { rl.unlock(); } } } private class Upgrader extends Thread { boolean upgradeSuccessful = false; public Upgrader(String name) { super(name); } public boolean wasUpgradeSuccessful() { return upgradeSuccessful; } public void run() { try { rl.lock(); synchronized (this) { this.wait(); } wl.lock(); upgradeSuccessful = true; synchronized (this) { this.wait(); } rl.unlock(); } catch (InterruptedException e) { } finally { wl.unlock(); rl.unlock(); } } } static void sleepThread(long timeout) { try { Thread.sleep(timeout); } catch (InterruptedException e) { } } } ././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/lock/ReentrantWriterPreferenceReadWriteLockTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/lock/ReentrantWriterPreferenceReadWriteLockTe0000755000175000017500000001422611120421675033316 0ustar moellermoellerpackage 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; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * Tests ReentrantWriterPreferenceReadWriteLock * * @author Bela Ban * @version $Id: ReentrantWriterPreferenceReadWriteLockTest.java 7295 2008-12-12 08:41:33Z mircea.markus $ */ @Test(groups = {"functional"}, sequential = true, testName = "lock.ReentrantWriterPreferenceReadWriteLockTest") public class ReentrantWriterPreferenceReadWriteLockTest { ReentrantReadWriteLock lock; Lock rl, wl; Exception thread_ex = null; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { // lock=new ReentrantWriterPreferenceReadWriteLock(); lock = new ReentrantReadWriteLock(); 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) { 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) { reader.notify(); } TestingUtil.sleepThread(500); synchronized (writer) { writer.notify(); } writer.join(); reader.join(); } class Reader extends Thread { public Reader(String name) { super(name); } public void run() { try { rl.lock(); synchronized (this) { this.wait(); } rl.unlock(); } catch (InterruptedException e) { } } } class Writer extends Thread { public Writer(String name) { super(name); } public void run() { try { wl.lock(); synchronized (this) { this.wait(); } wl.unlock(); } 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 { rl.lock(); synchronized (this) { this.wait(); } // rl.unlock(); wl.lock(); upgradeSuccessful = true; wl.unlock(); } catch (InterruptedException e) { } } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/lock/StripedLockTest.java0000644000175000017500000000337611120421675027253 0ustar moellermoellerpackage 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" }, testName = "lock.StripedLockTest") 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); } } 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); } return f; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/lock/NodeBasedLockManagerRecordingTest.java0000644000175000017500000000266311111047320032602 0ustar moellermoellerpackage org.jboss.cache.lock; import org.jboss.cache.Fqn; import org.jboss.cache.NodeSPI; import org.jboss.cache.PessimisticUnversionedNode; import org.jboss.cache.factories.context.PessimisticContextFactory; import org.jboss.cache.invocation.InvocationContextContainer; import org.jboss.cache.invocation.NodeInvocationDelegate; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ @Test(groups = "unit", testName = "lock.NodeBasedLockManagerRecordingTest") public class NodeBasedLockManagerRecordingTest extends AbstractLockManagerRecordingTest { @BeforeMethod public void setUp() { AbstractLockManagerRecordingTestTL tl = new AbstractLockManagerRecordingTestTL(); threadLocal.set(tl); tl.icc = new InvocationContextContainer(); tl.lm = new NodeBasedLockManager(); PessimisticContextFactory pcf = new PessimisticContextFactory(); tl.icc.injectContextFactory(pcf); tl.contextFactory = pcf; fqnBasedLocking = false; } @Override protected NodeSPI createNode(Fqn fqn) { PessimisticUnversionedNode un = new PessimisticUnversionedNode(fqn.getLastElement(), fqn, null, null); un.injectDependencies(null, null, null); un.injectLockStrategyFactory(new LockStrategyFactory()); return new NodeInvocationDelegate(un); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/lock/LockMapTest.java0000644000175000017500000000262611111047320026342 0ustar moellermoeller/* * 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"}, sequential = true, testName = "lock.LockMapTest") 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-3.2.8.GA/src/test/java/org/jboss/cache/lock/BreakDeadMemberLocksTest.java0000644000175000017500000001351211131613416030745 0ustar moellermoeller/* * 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.Fqn; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.util.TestingUtil; 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; import org.jboss.cache.UnitTestCacheFactory; /** * Tests the breaking of locks held by dead members. * * @author Brian Stansberry * @version $Revision: 7422 $ */ @Test(groups = {"functional"}, sequential = true, testName = "lock.BreakDeadMemberLocksTest") 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 UnitTestCacheFactory().createCache(UnitTestConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), false, getClass()); cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); 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-3.2.8.GA/src/test/java/org/jboss/cache/lock/LockTest.java0000644000175000017500000003423711135444225025722 0ustar moellermoellerpackage 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 7552 2009-01-20 21:56:37Z mircea.markus $ */ @Test(groups = {"functional"}, testName = "lock.LockTest") 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) *
*/ @Test (invocationCount = 5, successPercentage = 80) //relies on thread.sleep, might fail from time to time 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(); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/lock/MVCCLockManagerRecordingTest.java0000644000175000017500000000363211120421675031514 0ustar moellermoellerpackage org.jboss.cache.lock; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.context.MVCCContextFactory; import org.jboss.cache.invocation.InvocationContextContainer; import org.jboss.cache.transaction.DummyTransactionManager; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.concurrent.locks.LockContainer; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.TransactionManager; import java.util.ArrayList; import java.util.List; /** * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ @Test(groups = {"unit", "mvcc"}, testName = "lock.MVCCLockManagerRecordingTest") public class MVCCLockManagerRecordingTest extends AbstractLockManagerRecordingTest { @BeforeMethod public void setUp() { AbstractLockManagerRecordingTestTL tl = new AbstractLockManagerRecordingTestTL(); threadLocal.set(tl); tl.icc = new InvocationContextContainer(); MVCCLockManager mvccLockManager = new MVCCLockManager(); TransactionManager tm = DummyTransactionManager.getInstance(); mvccLockManager.injectConfiguration(new Configuration()); mvccLockManager.injectDependencies(null, null, tm, tl.icc); mvccLockManager.startLockManager(); tl.lm = mvccLockManager; tl.contextFactory = new MVCCContextFactory(); tl.icc.injectContextFactory(tl.contextFactory); } public void testFqnHashing() { AbstractLockManagerRecordingTestTL tl = threadLocal.get(); LockContainer lc = (LockContainer) TestingUtil.extractField(tl.lm, "lockContainer"); List fqns = new ArrayList(); fqns.add(Fqn.ROOT); fqns.add(Fqn.fromString("/1")); fqns.add(Fqn.fromString("/1/2")); fqns.add(Fqn.fromString("/1/2/3")); fqns.add(Fqn.fromString("/a/b/c/d")); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/lock/LockReleaseTest.java0000644000175000017500000002250211121730503027203 0ustar moellermoeller/* * * 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.Fqn; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.transaction.GenericTransactionManagerLookup; 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; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.util.TestingUtil; /** * Verifies that there are no read locks held when a transaction ends. * * @author Bela Ban * @version $Id: LockReleaseTest.java 7333 2008-12-16 13:46:43Z mircea.markus $ */ @Test(groups = {"functional"}, sequential = true, testName = "lock.LockReleaseTest") 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) { TestingUtil.killCaches(cache); cache = null; } // We just can't kill DummyTransactionManager. We are sharing single instance in more tests. TestingUtil.killTransaction(TransactionSetup.getManager()); if (tx != null) { try { tx.rollback(); } catch (Throwable t) { } tx = null; } } CacheSPI createCache(IsolationLevel level) throws Exception { CacheSPI c = (CacheSPI) new UnitTestCacheFactory().createCache(false, getClass()); c.getConfiguration().setClusterName("test"); c.getConfiguration().setStateRetrievalTimeout(10000); c.getConfiguration().setTransactionManagerLookupClass(GenericTransactionManagerLookup.class.getName()); c.getConfiguration().setLockAcquisitionTimeout(500); c.getConfiguration().setIsolationLevel(level); c.getConfiguration().setNodeLockingScheme(NodeLockingScheme.PESSIMISTIC); 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(); 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(); 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()); cache.getNode(NODE1); assertEquals("print() called outside the TX should have released all locks", 0, cache.getNumberOfLocksHeld()); tx.begin(); cache.getNode(NODE1); assertEquals("we should hold 1 read locks now (for print()): ", 2, cache.getNumberOfLocksHeld()); 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. */ 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()); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/lock/MVCCLockManagerTest.java0000644000175000017500000000567711120421675027672 0ustar moellermoellerpackage org.jboss.cache.lock; import org.jboss.cache.Fqn; import org.jboss.cache.NodeSPI; import org.jboss.cache.UnversionedNode; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.context.MVCCContextFactory; import org.jboss.cache.invocation.InvocationContextContainer; import org.jboss.cache.invocation.NodeInvocationDelegate; import static org.jboss.cache.lock.LockType.READ; import static org.jboss.cache.lock.LockType.WRITE; import org.jboss.cache.transaction.DummyTransactionManager; 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; @Test(groups = {"unit", "mvcc"}, sequential = true, testName = "lock.MVCCLockManagerTest") public class MVCCLockManagerTest { MVCCLockManager lm; InvocationContextContainer icc; @BeforeMethod public void setUp() { icc = new InvocationContextContainer(); icc.injectContextFactory(new MVCCContextFactory()); lm = new MVCCLockManager(); TransactionManager tm = getTransactionManager(); lm.injectConfiguration(new Configuration()); lm.injectDependencies(null, null, tm, icc); lm.startLockManager(); } protected TransactionManager getTransactionManager() { return DummyTransactionManager.getInstance(); // use this to ensure the lock manager uses ownable reentrant locks } @AfterMethod public void tearDown() { lm = null; } public void testUsingReadLocks() { // using any READ locks should be no-ops. lm.isLocked(null, READ); lm.getReadOwners(Fqn.ROOT); try { lm.getReadOwners((NodeSPI) null); } catch (NullPointerException npe) { // since we're passing in a null. } } public void testLockReentrancy() throws InterruptedException { Fqn fqn = Fqn.fromString("/a/b/c"); NodeSPI node = new NodeInvocationDelegate(new UnversionedNode(fqn)); assert lm.lock(fqn, WRITE, null); assert lm.isLocked(node); assert lm.lock(node, WRITE, null); assert lm.isLocked(node); lm.unlock(node, null); assert lm.isLocked(node) : "Should still be locked"; assert lm.ownsLock(node, Thread.currentThread()); lm.unlock(fqn, null); assert !lm.isLocked(node) : "Should not be locked"; } public void testSpreadingOfLocks() { List fqns = new ArrayList(11); fqns.add(Fqn.fromString("/")); fqns.add(Fqn.fromString("/a")); fqns.add(Fqn.fromString("/a/b")); fqns.add(Fqn.fromString("/a/b/c")); fqns.add(Fqn.fromString("/a/b/c/d")); fqns.add(Fqn.fromString("/a/b/c/e")); fqns.add(Fqn.fromString("/A")); fqns.add(Fqn.fromString("/A/B")); fqns.add(Fqn.fromString("/A/B/C")); fqns.add(Fqn.fromString("/A/B/C/D")); fqns.add(Fqn.fromString("/A/B/C/E")); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/profiling/0000755000175000017500000000000011675221366024367 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/profiling/testinternals/0000755000175000017500000000000011675221363027263 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/profiling/testinternals/Generator.java0000644000175000017500000000155611121734326032055 0ustar moellermoellerpackage org.jboss.cache.profiling.testinternals; import org.jboss.cache.Fqn; import java.util.ArrayList; import java.util.List; import java.util.Random; public class Generator { private static final Random r = new Random(); public static 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(); } public static T getRandomElement(List list) { return list.get(r.nextInt(list.size())); } public static Fqn createRandomFqn(int depth) { List fqnElements = new ArrayList(depth); for (int i = 0; i < depth; i++) fqnElements.add(Integer.toHexString(r.nextInt(Integer.MAX_VALUE))); return Fqn.fromList(fqnElements, true); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/profiling/testinternals/TaskRunner.java0000644000175000017500000000216311267624730032226 0ustar moellermoellerpackage org.jboss.cache.profiling.testinternals; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** * Essentially a delegate to an ExecutorService, but a special one that is only used by perf tests so it can be ignored * when profiling. */ public class TaskRunner { ExecutorService exec; public TaskRunner(int numThreads) { final ThreadGroup tg = new ThreadGroup(Thread.currentThread().getThreadGroup(), "LoadGenerators"); final AtomicInteger ai = new AtomicInteger(1); this.exec = Executors.newFixedThreadPool(numThreads, new ThreadFactory() { public Thread newThread(Runnable r) { return new Thread(tg, r, "LoadGenerator-" + ai.getAndIncrement()); } }); } public void execute(Runnable r) { exec.execute(r); } public void stop() throws InterruptedException { exec.shutdown(); while (!exec.awaitTermination(30, TimeUnit.SECONDS)) Thread.sleep(30); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/profiling/ProfileTest.java0000644000175000017500000003177011267624730027501 0ustar moellermoellerpackage 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.profiling.testinternals.Generator; import org.jboss.cache.profiling.testinternals.TaskRunner; 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.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! *

*

* Importnat - make sure you inly enable these tests locally! * * @author Manik Surtani (manik AT jboss DOT org) * @since 2.1.0 */ @Test(groups = "profiling", testName = "profiling.ProfileTest", enabled = false) 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 = 2000; protected static final int WARMUP_LOOPS = 20000; protected static final boolean USE_SLEEP = false; // throttle generation a bit protected static final boolean SKIP_WARMUP = true; private List fqns = new ArrayList(MAX_OVERALL_NODES); Log log = LogFactory.getLog(ProfileTest.class); public static void main(String[] args) throws Exception { ProfileTest pst = new ProfileTest(); pst.startedInCmdLine = true; String mode = args[0]; try { if (args.length > 1) pst.clusterNameOverride = args[1]; if (mode.equals("replsync")) { pst.testReplSync(); } else if (mode.equals("replasync")) { pst.testReplAsync(); } } finally { pst.tearDown(); } } @Test(enabled = false) public void testLocalModePess() throws Exception { Configuration cfg = cache.getConfiguration(); cfg.setCacheMode(Configuration.CacheMode.LOCAL); cfg.setConcurrencyLevel(2000); cfg.setLockAcquisitionTimeout(120000); cfg.setLockParentForChildInsertRemove(true); cfg.setIsolationLevel(IsolationLevel.READ_COMMITTED); cfg.setNodeLockingScheme(Configuration.NodeLockingScheme.MVCC); 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 { initTest(); cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.MVCC); cache.getConfiguration().setSerializationExecutorPoolSize(0); cache.getConfiguration().setConcurrencyLevel(5000); runCompleteTest(); } @Test(enabled = false) public void testReplAsync() throws Exception { initTest(); cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.MVCC); cache.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_ASYNC); cache.getConfiguration().setSerializationExecutorPoolSize(0); cache.getConfiguration().setConcurrencyLevel(5000); 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.MVCC); cache.getConfiguration().setConcurrencyLevel(500); 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(); testFinishedMarker(); // 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 = Generator.createRandomFqn(MAX_DEPTH); while (fqns.contains(fqn)) fqn = Generator.createRandomFqn(MAX_DEPTH); if (i % 10 == 0) { log.trace("Generated " + i + " fqns"); } fqns.add(fqn); } System.gc(); long duration = System.currentTimeMillis() - startTime; log.warn("Finished init() phase. " + printDuration(duration)); } 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 { if (SKIP_WARMUP) { log.info("Skipping warmup; taking a nap."); Thread.sleep(5000); log.info("Waking up"); return; } long startTime = System.currentTimeMillis(); TaskRunner exec = new TaskRunner(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 = Generator.getRandomElement(fqns); cache.get(f, ""); cache.put(f, "k", "v"); cache.remove(f, "k"); } }); } exec.stop(); long duration = System.currentTimeMillis() - startTime; log.warn("Finished warmup. " + printDuration(duration)); //cache.removeNode(Fqn.ROOT); cache.stop(); startup(); } private void testFinishedMarker() { System.out.println("Test finished"); } private void doTest() throws Exception { TaskRunner exec = new TaskRunner(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.stop(); // 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() { try { String k = Generator.getRandomString(); Fqn f = Generator.getRandomElement(fqns); long d = 0, st = 0; switch (mode) { case PUT: st = System.nanoTime(); cache.put(f, k, Generator.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); } catch (Exception e) { log.warn("Caught ", e); } } } 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; } } protected 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-3.2.8.GA/src/test/java/org/jboss/cache/profiling/AbstractProfileTest.java0000644000175000017500000001703411267624730031162 0ustar moellermoellerpackage org.jboss.cache.profiling; import org.jboss.cache.Cache; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.parsing.JGroupsStackParser; import org.jboss.cache.config.parsing.XmlConfigHelper; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; /** * Importnat - make sure you inly enable these tests locally! */ @Test(groups = "profiling",enabled = false) public abstract class AbstractProfileTest { protected Cache cache; @BeforeTest public void setUp() { Configuration cfg = UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.REPL_SYNC); cache = new UnitTestCacheFactory().createCache(cfg, false, getClass()); } @AfterTest public void tearDown() { TestingUtil.killCaches(cache); cache = null; } boolean startedInCmdLine = false; String clusterNameOverride = null; protected void initTest() { System.out.println("Setting up test params!"); if (startedInCmdLine) { setUp(); } } 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 String 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 new JGroupsStackParser().parseClusterConfigXml(XmlConfigHelper.stringToElement(udp)); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/profiling/ProfileMapViewTest.java0000644000175000017500000002065411146273534030767 0ustar moellermoellerpackage org.jboss.cache.profiling; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Cache; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.util.Caches; import org.jboss.cache.util.Caches.HashKeySelector; 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.List; import java.util.Map; import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; /** * Importnat - make sure you inly enable these tests locally! */ @Test(groups = "profiling", enabled = false, testName = "profiling.ProfileMapViewTest") public class ProfileMapViewTest { /* Test configuration options */ protected static final long DURATION = 60 * 1000; // 1 min of GENERATION = a lot of processing. :-) protected static final int NUM_THREADS = 15; protected static final int MAX_RANDOM_SLEEP_MILLIS = 1; protected static final int MAX_ENTRIES = 200; protected static final int WARMUP_LOOPS = 20000; protected static final boolean USE_SLEEP = false; // throttle generation a bit private List keys = new ArrayList(MAX_ENTRIES); private Random r = new Random(); private Cache cache; private Map map; private Log log = LogFactory.getLog(ProfileTest.class); @BeforeTest public void setUp() { Configuration cfg = UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL); cfg.setNodeLockingScheme(NodeLockingScheme.MVCC); cfg.setConcurrencyLevel(500); cache = new UnitTestCacheFactory().createCache(cfg, false, getClass()); } @AfterTest public void tearDown() { cache.stop(); } public void testLocalModeMVCC_RC() throws Exception { cache.getConfiguration().setIsolationLevel(IsolationLevel.READ_COMMITTED); runCompleteTest(); } public void testLocalModeMVCC_RR() throws Exception { cache.getConfiguration().setIsolationLevel(IsolationLevel.REPEATABLE_READ); runCompleteTest(); } 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"); keys.clear(); for (int i = 0; i < MAX_ENTRIES; i++) { String key = createRandomKey(r); while (keys.contains(key)) key = createRandomKey(r); if (i % 10 == 0) { log.warn("Generated " + i + " fqns"); } keys.add(key); } System.gc(); long duration = System.currentTimeMillis() - startTime; log.warn("Finished init() phase. " + printDuration(duration)); } private String createRandomKey(Random r) { StringBuilder s = new StringBuilder("/"); int depth = r.nextInt(3); for (int i = 0; i < depth; i++) { s.append(r.nextInt(Integer.MAX_VALUE)).append("/"); } return s.toString(); } private Map createMap(Cache cache) { return Caches.asPartitionedMap(cache.getRoot(), new HashKeySelector(128)); } private void startup() { long startTime = System.currentTimeMillis(); log.warn("Starting cache"); cache.start(); map = createMap(cache); 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 String key : keys) { exec.execute(new Runnable() { public void run() { // this will create the necessary nodes. // cache.put(f, Collections.emptyMap()); map.put(key, "value"); } }); } // 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_ENTRIES)); String key = keys.get(r.nextInt(MAX_ENTRIES)); // cache.get(f, ""); // cache.put(f, "k", "v"); // cache.remove(f, "k"); map.get(key); map.put(key, "value"); map.remove(key); } }); } exec.shutdown(); exec.awaitTermination(360, TimeUnit.SECONDS); long duration = System.currentTimeMillis() - startTime; log.warn("Finished warmup. " + printDuration(duration)); //cache.removeNode(Fqn.ROOT); cache.stop(); cache.start(); map = createMap(cache); } private void doTest() throws Exception { ExecutorService exec = Executors.newFixedThreadPool(NUM_THREADS); long end = System.currentTimeMillis() + DURATION; long startTime = System.currentTimeMillis(); log.warn("Starting test"); int i = 0; while (System.currentTimeMillis() < end) { MyRunnable r = null; switch (i % 3) { case 0: r = new Putter(i++); break; case 1: r = new Getter(i++); break; case 2: r = new Remover(i++); break; } 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(); exec.awaitTermination(((long) i), TimeUnit.SECONDS); // wait up to 1 sec for each call? long duration = System.currentTimeMillis() - startTime; log.warn("Finished test. " + printDuration(duration)); } enum Mode { PUT, GET, REMOVE } private abstract class MyRunnable implements Runnable { int id; Mode mode; public void run() { if (id % 100 == 0) log.warn("Processing iteration " + id); String k = getRandomString(); String key = keys.get(r.nextInt(MAX_ENTRIES)); switch (mode) { case PUT: // cache.put(f, k, getRandomString()); map.put(key, getRandomString()); break; case GET: // cache.get(f, k); map.get(key); break; case REMOVE: // cache.remove(f, k); map.remove(key); break; } } } private class Putter extends MyRunnable { private Putter(int id) { this.id = id; mode = Mode.PUT; } } private class Getter extends MyRunnable { private Getter(int id) { this.id = id; mode = Mode.GET; } } private class Remover extends MyRunnable { private Remover(int id) { this.id = id; 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(); } private String printDuration(long duration) { if (duration > 2000) { double dSecs = ((double) duration / (double) 1000); return "Duration: " + dSecs + " seconds"; } else { return "Duration: " + duration + " millis"; } } }jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/profiling/ConstructionTest.java0000644000175000017500000000222011120367722030550 0ustar moellermoellerpackage org.jboss.cache.profiling; import org.jboss.cache.Cache; import org.jboss.cache.UnitTestCacheFactory; import org.testng.annotations.Test; /** * Profile LOCAL mode operation * Importnat - make sure you inly enable these tests locally! * @author Manik Surtani (manik AT jboss DOT org) * @since 2.2.0 */ @Test(groups = "profiling", testName = "profiling.ConstructionTest", enabled = false) 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 UnitTestCacheFactory().createCache(getClass()); 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 UnitTestCacheFactory().createCache(getClass()); if (i % 100 == 0) System.out.println("In loop num " + i); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/profiling/MockAsyncReplTest.java0000644000175000017500000000632711146273534030611 0ustar moellermoellerpackage 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; /** * Importnat - make sure you inly enable these tests locally! * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ @Test(groups = "profiling", enabled = false, testName="profiling.MockAsyncReplTest") public class MockAsyncReplTest extends ProfileTest { @Override @Test(enabled = false) 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-3.2.8.GA/src/test/java/org/jboss/cache/profiling/ProfileSlaveTest.java0000644000175000017500000001170011267624730030463 0ustar moellermoellerpackage 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. Importnat - make sure you inly enable these tests locally! * * @author Manik Surtani (manik AT jboss DOT org) * @since 2.1.0 */ @Test(groups = "profiling", testName = "profiling.ProfileSlaveTest", enabled = false) public class ProfileSlaveTest extends AbstractProfileTest { public static void main(String[] args) throws Exception { ProfileSlaveTest pst = new ProfileSlaveTest(); pst.startedInCmdLine = true; String mode = args[0]; try { if (args.length > 1) pst.clusterNameOverride = args[1]; if (mode.equals("replsync")) { pst.testReplSync(); } else if (mode.equals("replasync")) { pst.testReplAsync(); } } finally { pst.tearDown(); } } private void waitForTest() throws Exception { System.out.println("Slave listening for remote connections. Hit Enter when done."); System.in.read(); if (clusterNameOverride != null) cache.getConfiguration().setClusterName(clusterNameOverride); } @Test(enabled = false) public void testReplSync() throws Exception { initTest(); cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.MVCC); cache.getConfiguration().setConcurrencyLevel(5000); cache.start(); System.out.println("My Address is " + cache.getLocalAddress()); doTest(); waitForTest(); } private void doTest() { // this is here to act as a JProfiler trigger } @Test(enabled = false) public void testReplAsync() throws Exception { initTest(); cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.MVCC); cache.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_ASYNC); cache.getConfiguration().setConcurrencyLevel(5000); // 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.MVCC); cache.getConfiguration().setConcurrencyLevel(500); 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-3.2.8.GA/src/test/java/org/jboss/cache/profiling/MemConsumptionTest.java0000644000175000017500000000752211111047320031032 0ustar moellermoeller/* * 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.profiling; import org.jboss.cache.Cache; import org.jboss.cache.CacheException; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.Test; import java.io.IOException; import java.text.NumberFormat; import java.util.Arrays; import java.util.Random; /** * Importnat - make sure you inly enable these tests locally! */ @Test(groups = "profiling", testName = "profiling.MemConsumptionTest", enabled = false) public class MemConsumptionTest { // adjust the next 4 values int numNodes = 1000000; int payloadSize = 20; // characters int keySize = 10; // characters PayloadType payloadType = PayloadType.STRINGS; enum PayloadType {STRINGS, BYTE_ARRAYS} int bytesPerCharacter = 2; Random r = new Random(); public void testMemConsumption() throws IOException { int kBytesCached = (bytesPerCharacter * numNodes * (payloadSize + keySize)) / 1024; System.out.println("Bytes to be cached: " + NumberFormat.getIntegerInstance().format(kBytesCached) + " kb"); Cache c = new DefaultCacheFactory().createCache(false); // default LOCAL cache c.start(); for (int i = 0; i < numNodes; i++) { switch (payloadType) { case STRINGS: c.put(Fqn.fromString("/node" + i), generateRandomString(keySize), generateRandomString(payloadSize)); break; case BYTE_ARRAYS: c.put(Fqn.fromString("/node" + i), generateUniqueKey(i, keySize), generateBytePayload(payloadSize)); break; default: throw new CacheException("Unknown payload type"); } if (i % 1000 == 0) System.out.println("Added " + i + " entries"); } System.out.println("Calling System.gc()"); System.gc(); // clear any unnecessary objects TestingUtil.sleepThread(1000); // wait for gc // wait for manual test exit System.out.println("Cache populated; check mem usage using jconsole, etc.!"); System.in.read(); } private String generateRandomString(int stringSize) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < stringSize; i++) { sb.append(r.nextInt(9)); // single digit } assert sb.length() == stringSize; return sb.toString(); } private byte[] generateUniqueKey(int runNumber, int keySize) { byte[] b = new byte[keySize]; b[0] = (byte) (runNumber >>> 0); b[1] = (byte) (runNumber >>> 8); b[2] = (byte) (runNumber >>> 16); b[3] = (byte) (runNumber >>> 24); for (int i = 4; i < keySize; i++) b[i] = 0; return b; } private byte[] generateBytePayload(int payloadSize) { byte[] b = new byte[payloadSize]; Arrays.fill(b, (byte) 0); return b; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/profiling/MemoryFootprintTest.java0000644000175000017500000000234311120367722031241 0ustar moellermoellerpackage org.jboss.cache.profiling; import org.jboss.cache.Cache; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.lock.IsolationLevel; import org.testng.annotations.Test; import java.io.IOException; /** * Importnat - make sure you inly enable these tests locally! */ @Test(groups = "profiling", testName = "profiling.MemoryFootprintTest", enabled = false) public class MemoryFootprintTest { int numFqns = 100000; public void testLocal() throws IOException { Cache c = new UnitTestCacheFactory().createCache(false, getClass()); c.getConfiguration().setNodeLockingScheme(NodeLockingScheme.MVCC); c.getConfiguration().setIsolationLevel(IsolationLevel.READ_COMMITTED); // c.getConfiguration().setIsolationLevel(IsolationLevel.REPEATABLE_READ); c.start(); for (int i = 100000; i < 100000 + numFqns; i++) { String key = "keyX" + i; String value = "valX" + i; Fqn fqn = Fqn.fromElements(i); c.put(fqn, key, value); } System.out.println("Hit enter when done"); System.in.read(); c.stop(); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/passivation/0000755000175000017500000000000011675221373024734 5ustar moellermoeller././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/passivation/LocalPassivationIntegrationTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/passivation/LocalPassivationIntegrationTest.j0000644000175000017500000001000311121730272033405 0ustar moellermoeller/* * 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.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.parsing.XmlConfigurationParser; import org.jboss.cache.loader.testloaders.DummyInMemoryCacheLoader; 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 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"}, sequential = true, testName = "passivation.LocalPassivationIntegrationTest") public class LocalPassivationIntegrationTest { CacheSPI cache; protected final static Log log = LogFactory.getLog(LocalPassivationIntegrationTest.class); long 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 { UnitTestCacheFactory instance = new UnitTestCacheFactory(); cache = (CacheSPI) instance.createCache(new XmlConfigurationParser().parseFile("configs/local-passivation.xml"), false, getClass()); 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().getWakeupInterval(); if (wakeupIntervalMillis <= 0) { fail("testEviction(): eviction thread wake up interval is illegal " + wakeupIntervalMillis); } } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache); cache = null; } /** */ 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()); } @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++; } } @NodePassivated public void nodePassivated(NodeEvent ne) { } @NodeLoaded public void nodeLoaded(Event e) { if (!e.isPre()) { loadedCounter++; } } } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/passivation/PassivationToJDBCCacheLoaderTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/passivation/PassivationToJDBCCacheLoaderTest.0000644000175000017500000000405611131613416033071 0ustar moellermoeller/* * 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.Fqn; import org.jboss.cache.config.CacheLoaderConfig; import static org.jboss.cache.factories.UnitTestConfigurationFactory.buildSingleCacheLoaderConfig; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.UnitTestDatabaseManager; import org.testng.annotations.AfterMethod; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; 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 7422 2009-01-09 09:21:18Z mircea.markus $ */ @Test(groups = "functional", testName = "passivation.PassivationToJDBCCacheLoaderTest") public class PassivationToJDBCCacheLoaderTest extends PassivationTestsBase { private Properties props; private long durration; @BeforeTest public void createDatabase() { durration = System.currentTimeMillis(); props = UnitTestDatabaseManager.getTestDbProperties(); } @AfterTest public void shutDownDatabase() { UnitTestDatabaseManager.shutdownInMemoryDatabase(props); } private Properties getJDBCProps() { return props; } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { log.info("**** TEARING DOWN ****"); if (loader != null) loader.remove(Fqn.ROOT); TestingUtil.killCaches(cache); UnitTestDatabaseManager.shutdownInMemoryDatabase(props); } protected void configureCache() throws Exception { CacheLoaderConfig loaderConfig = buildSingleCacheLoaderConfig(true, null, "org.jboss.cache.loader.JDBCCacheLoader", getJDBCProps(), false, false, false, false, false); cache.getConfiguration().setCacheLoaderConfig(loaderConfig); } } ././@LongLink0000000000000000000000000000015500000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/passivation/ReplicatedPassivationIntegrationTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/passivation/ReplicatedPassivationIntegrationT0000644000175000017500000001627211131613416033502 0ustar moellermoeller/* * 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.CacheSPI; import org.jboss.cache.UnitTestCacheFactory; 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.LRUAlgorithmConfig; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.loader.testloaders.DummyInMemoryCacheLoader; import org.jboss.cache.loader.testloaders.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; @Test(groups = "functional", testName = "passivation.ReplicatedPassivationIntegrationTest") public class ReplicatedPassivationIntegrationTest { private CacheSPI cache1; private CacheSPI cache2; protected final static Log log = LogFactory.getLog(ReplicatedPassivationIntegrationTest.class); long 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 { UnitTestCacheFactory instance = new UnitTestCacheFactory(); cache1 = (CacheSPI) instance.createCache(getCfg(), false, getClass()); cache1.getConfiguration().setUseRegionBasedMarshalling(true); cache1.start(); cache2 = (CacheSPI) instance.createCache(getCfg(), false, getClass()); cache2.getConfiguration().setUseRegionBasedMarshalling(true); cache2.start(); cache2.getNotifier().addCacheListener(listener); listener.resetCounter(); wakeupIntervalMillis = cache2.getConfiguration().getEvictionConfig().getWakeupInterval(); log("wakeupInterval is " + wakeupIntervalMillis); if (wakeupIntervalMillis <= 0) { fail("testEviction(): eviction thread wake up interval is illegal " + wakeupIntervalMillis); } } Configuration getCfg() throws Exception { Configuration cfg = UnitTestConfigurationFactory.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.setWakeupInterval(1000); cfg.setDefaultEventQueueSize(200000); EvictionRegionConfig region1 = new EvictionRegionConfig(); region1.setRegionFqn(Fqn.ROOT); LRUAlgorithmConfig epc1 = new LRUAlgorithmConfig(); epc1.setMaxNodes(5000); epc1.setTimeToLive(3000); region1.setEvictionAlgorithmConfig(epc1); cfg.setDefaultEvictionRegionConfig(region1); EvictionRegionConfig region2 = new EvictionRegionConfig(); region2.setRegionFqn(base); LRUAlgorithmConfig epc2 = new LRUAlgorithmConfig(); epc2.setMaxNodes(100); epc2.setTimeToLive(3000); region2.setEvictionAlgorithmConfig(epc2); cfg.addEvictionRegionConfig(region2); return cfg; } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache1, cache2); cache1 = null; cache2 = null; } 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) { } @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++; } } @NodePassivated public void nodePassivated(NodeEvent ne) { if (ne.isPre()) { } } @NodeLoaded public void nodeLoaded(NodeEvent ne) { if (!ne.isPre()) { loadedCounter++; } } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/passivation/BasicPassivationTest.java0000644000175000017500000001005211134650765031700 0ustar moellermoeller/* * 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.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.parsing.XmlConfigurationParser; import org.jboss.cache.loader.testloaders.DummyInMemoryCacheLoader; 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 org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.EvictionController; 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: 7496 $ */ @Test(groups = {"functional"}, testName = "passivation.BasicPassivationTest") public class BasicPassivationTest { CacheSPI cache; 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; private EvictionController ec; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { UnitTestCacheFactory instance = new UnitTestCacheFactory(); cache = (CacheSPI) instance.createCache(new XmlConfigurationParser().parseFile("configs/local-passivation.xml"), false, getClass()); cache.getConfiguration().getEvictionConfig().setWakeupInterval(0); cache.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); Object listener = new TestCacheListener(); cache.getConfiguration().getCacheLoaderConfig().getFirstCacheLoaderConfig().setClassName(DummyInMemoryCacheLoader.class.getName()); cache.start(); ec = new EvictionController(cache); cache.getNotifier().addCacheListener(listener); t1_ex = t2_ex = null; isTrue = true; } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache); cache = null; } public void testBasic() { activationCount = 0; passivationCount = 0; cache.put(FQNSTR, FQNSTR, FQNSTR); TestingUtil.sleepThread(1100); ec.startEviction(); 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")); } @CacheListener public class TestCacheListener { @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. if (ne.getType() == Event.Type.NODE_ACTIVATED) activationCount++; else if (ne.getType() == Event.Type.NODE_PASSIVATED) passivationCount++; } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/passivation/ConcurrentPassivationTest.java0000644000175000017500000000560611121730272032776 0ustar moellermoeller/* * 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.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.parsing.XmlConfigurationParser; import org.jboss.cache.loader.testloaders.DummyInMemoryCacheLoader; 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; /** * Tests cache behavior in the presence of concurrent passivation. * * @author Brian Stansberry * @version $Revision: 7332 $ */ @Test(groups = {"functional"}, testName = "passivation.ConcurrentPassivationTest") public class ConcurrentPassivationTest { private CacheSPI cache; private long wakeupIntervalMillis = 0; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { initCaches(); wakeupIntervalMillis = cache.getConfiguration().getEvictionConfig().getWakeupInterval(); if (wakeupIntervalMillis < 0) { fail("testEviction(): eviction thread wake up interval is illegal " + wakeupIntervalMillis); } } private void initCaches() { UnitTestCacheFactory instance = new UnitTestCacheFactory(); cache = (CacheSPI) instance.createCache(new XmlConfigurationParser().parseFile("configs/local-passivation.xml"), false, getClass()); 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 { TestingUtil.killCaches(cache); 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); 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)); } } } } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/passivation/PassivationToBdbjeCacheLoaderTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/passivation/PassivationToBdbjeCacheLoaderTest0000644000175000017500000000363711131613416033323 0ustar moellermoellerpackage org.jboss.cache.passivation; import org.testng.annotations.Test; import static org.jboss.cache.factories.UnitTestConfigurationFactory.*; import java.io.File; import java.io.FileFilter; import org.jboss.cache.util.TestingUtil; /** * Runs the same tests as {@link PassivationToFileCacheLoaderTest}, but with * Berkeley DB instead of a file-based CacheLoader * * @author {Hany Mesha} * @version $Id: PassivationToBdbjeCacheLoaderTest.java 7422 2009-01-09 09:21:18Z mircea.markus $ */ @Test(groups = "functional", testName = "passivation.PassivationToBdbjeCacheLoaderTest") public class PassivationToBdbjeCacheLoaderTest extends PassivationTestsBase { private String tmp_location = TestingUtil.TEST_FILES; 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); } } } } String tmpDir = TestingUtil.TEST_FILES; String threadId = Thread.currentThread().getName(); String tmpCLLoc = tmpDir + "/JBossCache-PassivationToBdbjeCacheLoaderTest-" + threadId; cache.getConfiguration().setCacheLoaderConfig(buildSingleCacheLoaderConfig(true, null, "org.jboss.cache.loader.bdbje.BdbjeCacheLoader", "location=" + tmpCLLoc, false, false, false, false, false)); } } ././@LongLink0000000000000000000000000000016000000000000011562 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/passivation/ReplAndStateTransferWithPassivationTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/passivation/ReplAndStateTransferWithPassivati0000644000175000017500000001732111135445153033431 0ustar moellermoeller/* * 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.passivation; import org.jboss.cache.*; import org.jboss.cache.commands.remote.DataGravitationCleanupCommand; import org.jboss.cache.buddyreplication.BuddyFqnTransformer; import org.jboss.cache.buddyreplication.BuddyReplicationTestsBase; 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.config.Configuration.NodeLockingScheme; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.loader.testloaders.DummySharedInMemoryCacheLoader; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.replicationlisteners.ReplicationListener; import org.testng.annotations.Test; import java.util.HashSet; import java.util.Set; @Test(groups = "functional", testName = "passivation.ReplAndStateTransferWithPassivationTest") public class ReplAndStateTransferWithPassivationTest { public void testStateTransferOfPassivatedState() throws Exception { doTest(NodeLockingScheme.MVCC, false); } public void testStateTransferOfPassivatedStatePessimistic() throws Exception { doTest(NodeLockingScheme.PESSIMISTIC, false); } public void testStateTransferOfPassivatedPartialState() throws Exception { doPartialStateTransferTest(NodeLockingScheme.MVCC); } public void testStateTransferOfPassivatedPartialStatePessimistic() throws Exception { doPartialStateTransferTest(NodeLockingScheme.PESSIMISTIC); } public void testStateTransferOfPassivatedPartialStateBR() throws Exception { doTest(NodeLockingScheme.MVCC, true); } public void testStateTransferOfPassivatedPartialStateBRPessimistic() throws Exception { doTest(NodeLockingScheme.PESSIMISTIC, true); } public void testStateTransferOfPassivatedPartialStateBRForceRemote() throws Exception { doTest(NodeLockingScheme.MVCC, false); } public void testStateTransferOfPassivatedPartialStateBRPessimisticForceRemote() throws Exception { doTest(NodeLockingScheme.PESSIMISTIC, false); } private void doPartialStateTransferTest(NodeLockingScheme nls) throws Exception { CacheSPI cache1=null, cache2=null; String subtree = "/SESSIONS"; try { Set nameSet = new HashSet(); nameSet.add("a"); nameSet.add("b"); nameSet.add("c"); cache1 = (CacheSPI) new UnitTestCacheFactory().createCache(buildConf(nls, "cache1", true, false, true), getClass()); cache2 = (CacheSPI) new UnitTestCacheFactory().createCache(buildConf(nls, "cache2", true, false, true), getClass()); Region r1 = cache1.getRegionManager().getRegion(subtree, true); Region r2 = cache2.getRegionManager().getRegion(subtree, true); r1.registerContextClassLoader(getClass().getClassLoader()); r2.registerContextClassLoader(getClass().getClassLoader()); r1.activate(); cache1.put(subtree + "/a", "k", "v"); cache1.put(subtree + "/b", "k", "v"); cache1.put(subtree + "/c", "k", "v"); Node root1 = cache1.getNode(subtree); assert root1.getChildrenNames().equals(nameSet) : "Expecting " + nameSet + " but got " + root1.getChildrenNames(); cache1.evict(Fqn.fromString("/a")); r2.activate(); Node root2 = cache1.getNode(subtree); assert root2.getChildrenNames().equals(nameSet) : "Expecting " + nameSet + " but got " + root2.getChildrenNames(); } finally { TestingUtil.killCaches(cache1, cache2); } } private void doTest(NodeLockingScheme nls, boolean useBR) throws Exception { Cache cache1=null, cache2=null; Fqn fqn = useBR ? Fqn.fromString("/someFqn") : Fqn.ROOT; Fqn A = Fqn.fromRelativeElements(fqn, "a"), B = Fqn.fromRelativeElements(fqn, "b"), C = Fqn.fromRelativeElements(fqn, "c"); try { Set nameSet = new HashSet(); nameSet.add(A.getLastElement()); nameSet.add(B.getLastElement()); nameSet.add(C.getLastElement()); cache1 = new UnitTestCacheFactory().createCache(buildConf(nls, "cache1", false, useBR, true), getClass()); cache1.put(A, "k", "v"); cache1.put(B, "k", "v"); cache1.put(C, "k", "v"); assert cache1.getNode(fqn).getChildrenNames().equals(nameSet); cache1.evict(A); cache2 = new UnitTestCacheFactory().createCache(buildConf(nls, "cache2", false, useBR, true), getClass()); TestingUtil.blockUntilViewReceived((CacheSPI)cache2, 2, 10000); Thread.sleep(2000); if (useBR) { BuddyReplicationTestsBase.waitForSingleBuddy(cache1, cache2); Set backupNameSet = new HashSet(nameSet); backupNameSet.remove(BuddyFqnTransformer.BUDDY_BACKUP_SUBTREE); ReplicationListener replListener1 = ReplicationListener.getReplicationListener(cache1); replListener1.expect(DataGravitationCleanupCommand.class); cache2.getInvocationContext().getOptionOverrides().setForceDataGravitation(true); Node backupNode = cache2.getNode(fqn); replListener1.waitForReplicationToOccur(); assert backupNode.getChildrenNames().equals(backupNameSet) : "Expecting " + backupNameSet + " but got " + backupNode.getChildrenNames(); } else { assert cache2.getRoot().getChildrenNames().equals(nameSet) : "Expecting " + nameSet + " but got " + cache2.getRoot().getChildrenNames(); } } finally { TestingUtil.killCaches(cache1, cache2); } } private Configuration buildConf(NodeLockingScheme nls, String name, boolean regionbased, boolean useBR, boolean brSearchSubtrees) throws Exception { Configuration c = new Configuration(); if (regionbased) { c.setUseRegionBasedMarshalling(true); c.setInactiveOnStartup(true); } c.setCacheMode(CacheMode.REPL_SYNC); c.setNodeLockingScheme(nls); CacheLoaderConfig clc = UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", DummySharedInMemoryCacheLoader.class.getName(), "bin=" + name, false, true, false, false, false); clc.setPassivation(true); c.setCacheLoaderConfig(clc); if (useBR) { BuddyReplicationConfig brc = new BuddyReplicationConfig(); brc.setEnabled(true); brc.setAutoDataGravitation(false); brc.setDataGravitationSearchBackupTrees(brSearchSubtrees); brc.setDataGravitationRemoveOnFind(true); c.setBuddyReplicationConfig(brc); } return c; } } ././@LongLink0000000000000000000000000000016200000000000011564 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/passivation/PassivationToDummyInMemoryCacheLoaderTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/passivation/PassivationToDummyInMemoryCacheLo0000644000175000017500000000152511121730272033365 0ustar moellermoellerpackage org.jboss.cache.passivation; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.loader.testloaders.DummyInMemoryCacheLoader; import org.testng.annotations.Test; @Test(groups = {"functional"}, testName = "passivation.PassivationToDummyInMemoryCacheLoaderTest") 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"); } } ././@LongLink0000000000000000000000000000016400000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/passivation/PassivationToLocalDelegatingCacheLoaderTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/passivation/PassivationToLocalDelegatingCache0000644000175000017500000000434411120367722033344 0ustar moellermoellerpackage org.jboss.cache.passivation; import org.jboss.cache.CacheSPI; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.loader.LocalDelegatingCacheLoaderConfig; import org.jboss.cache.util.TestingUtil; 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 7284 2008-12-12 05:00:02Z mircea.markus $ */ @Test(groups = "functional", testName = "passivation.PassivationToLocalDelegatingCacheLoaderTest") public class PassivationToLocalDelegatingCacheLoaderTest extends PassivationTestsBase { ThreadLocal delegating_cacheTL = new ThreadLocal(); //ThreadLocal cache_loaderTL = new ThreadLocal(); protected void configureCache() throws Exception { CacheSPI delegating_cache = (CacheSPI) new UnitTestCacheFactory().createCache(false, getClass()); delegating_cache.getConfiguration().setCacheMode(Configuration.CacheMode.LOCAL); delegating_cache.create(); delegating_cache.start(); delegating_cacheTL.set(delegating_cache); 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(); CacheSPI delegating_cache = delegating_cacheTL.get(); delegating_cacheTL.set(null); TestingUtil.killCaches(delegating_cache); delegating_cache = null; } public void testLoadAndStore() throws Exception { //TODO intentional overload since this test does not pass //http://jira.jboss.com/jira/browse/JBCACHE-851 } } ././@LongLink0000000000000000000000000000015700000000000011570 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/passivation/PassivationActivationCallbacksTestCase.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/passivation/PassivationActivationCallbacksTes0000644000175000017500000001617611121730272033455 0ustar moellermoeller/* * 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.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.eviction.LRUAlgorithmConfig; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.loader.testloaders.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.HashSet; import java.util.Set; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.util.TestingUtil; /** * Tests that the TreeCacheListener implementation used by EJB3 SFSBs works. * * @author Brian Stansberry */ @Test(groups = {"functional"}, sequential = true, testName = "passivation.PassivationActivationCallbacksTestCase") 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 { UnitTestCacheFactory instance = new UnitTestCacheFactory(); cache = (CacheSPI) instance.createCache(false, getClass()); 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.setWakeupInterval(1000); LRUAlgorithmConfig lru = new LRUAlgorithmConfig(); lru.setMaxNodes(0); lru.setTimeToLive(5000); ec.setDefaultEvictionRegionConfig(new EvictionRegionConfig(Fqn.ROOT, lru)); lru = new LRUAlgorithmConfig(); lru.setMaxNodes(0); lru.setTimeToLive(1000); ec.addEvictionRegionConfig(new EvictionRegionConfig(BASE, lru)); cache.getConfiguration().setEvictionConfig(ec); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { cache.removeNode(Fqn.ROOT); loader.remove(Fqn.fromString("/")); TestingUtil.killCaches(cache); cache = null; } 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); if (listener.activationException != null) throw listener.activationException; if (listener.passivationException != null) throw listener.passivationException; 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; @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()); } @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; } } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/passivation/PassivationTestsBase.java0000644000175000017500000014443111147260547031724 0ustar moellermoellerpackage org.jboss.cache.passivation; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.*; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.loader.SamplePojo; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.statetransfer.DefaultStateTransferManager; import org.jboss.cache.transaction.DummyTransactionManager; import org.jboss.cache.util.TestingUtil; 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.Serializable; import java.util.*; /** * Base tests for passivation using any of the cache loaders * * @author {Hany Mesha} * @version $Id: PassivationTestsBase.java 7735 2009-02-19 13:40:55Z manik.surtani@jboss.com $ */ @Test(groups = "functional", testName = "passivation.PassivationTestsBase") abstract public class PassivationTestsBase { Log log = LogFactory.getLog(getClass()); //Cache Loader fields static final Fqn FQN = Fqn.fromString("/key"); protected CacheLoader loader; protected CacheSPI cache; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache = (CacheSPI) new UnitTestCacheFactory().createCache(false, getClass()); 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 { log.info("**** TEARING DOWN ****"); if (loader != null) 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; 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(); retval = cache.put(NODE, KEY, 30);// activate node then does put in memory assertFalse(loader.exists(NODE)); assertEquals(20, retval); } public void testPut2Passivation() throws Exception { 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(); assertTrue(loader.exists(Fqn.fromString("/a/b/c"))); retval = cache.put(NODE, KEY, 30);// activate node, put in memory new value assertFalse(loader.exists(NODE)); 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"); 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 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")); 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() { 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()); } 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); 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 assert !exists("/a"); 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); 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"))); assertFalse(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); 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); 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); 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); 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"); assertFalse(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")); mgr.commit(); } 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 testTxReadCommit() 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"))); // now do a READ in a TX mgr.begin(); assert cache.get("/one/two/three", "key1").equals("val1"); assert cache.get("/one/two/three/four", "key2").equals("val2"); mgr.commit(); // these should NOT exist in the CL anymore! assert !loader.exists(Fqn.fromString("/one/two/three/four")); } 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"}); } /** * 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); 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(); Map map = new HashMap(); map.put("five", "six"); map.put("seven", "eight"); Modification mod = new Modification(); mod.setType(Modification.ModificationType.PUT_DATA); mod.setFqn(FQN); mod.setData(map); list.add(mod); 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); 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: for (Object key : mod.getData().keySet()) { assertEquals(mod.getData().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 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("/")); /* 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(DefaultStateTransferManager.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(); } } } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/passivation/PassivationToFileCacheLoaderTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/passivation/PassivationToFileCacheLoaderTest.0000644000175000017500000000245111131613416033243 0ustar moellermoellerpackage org.jboss.cache.passivation; import org.jboss.cache.util.TestingUtil; import static org.jboss.cache.factories.UnitTestConfigurationFactory.buildSingleCacheLoaderConfig; import org.testng.annotations.Test; /** * tests passivation using file cache loader * * @author {Hany Mesha} * @version $Id: PassivationToFileCacheLoaderTest.java 7422 2009-01-09 09:21:18Z mircea.markus $ */ @Test(groups = "functional", testName = "passivation.PassivationToFileCacheLoaderTest") public class PassivationToFileCacheLoaderTest extends PassivationTestsBase { protected void configureCache() throws Exception { String tmpLocation = null; String OS = System.getProperty("os.name").toLowerCase(); // if (OS.contains("win") || OS.contains("nt")) // { // tmpLocation = TestingUtil.TEST_FILES; // } // else // { tmpLocation = TestingUtil.TEST_FILES; // } String threadId = Thread.currentThread().getName(); String tmpCLLoc = tmpLocation + "/JBossCache-PassivationToFileCacheLoaderTest-" + threadId; cache.getConfiguration().setCacheLoaderConfig(buildSingleCacheLoaderConfig(true, null, "org.jboss.cache.loader.FileCacheLoader", "location=" + tmpCLLoc, false, false, false, false, false)); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/AbstractCacheTest.java0000644000175000017500000000332511133403360026554 0ustar moellermoellerpackage org.jboss.cache; import org.jboss.cache.loader.CacheLoaderManager; /** * @author Mircea.Markus@jboss.com */ public class AbstractCacheTest { /** * Importnat: do not reset RegionManager (rm.reset()) here, as this woulfd cause eviction region to be clered. * If that is needed, do it in @BeforeMethods */ public void clearContent(CacheSPI cache) { clearRunningTx(cache); if (!cache.getCacheStatus().allowInvocations()) return; removeInMemoryData(cache); clearCacheLoader(cache); //impoortant!!! keep invocation ctxt cleanup as the last line in the cleanup process, prev calls modify // OptionOverrides cache.getInvocationContext().reset(); } private void clearCacheLoader(CacheSPI cache) { CacheLoaderManager cacheLoaderManager = cache.getCacheLoaderManager(); if (cacheLoaderManager != null && cacheLoaderManager.getCacheLoader() != null) { try { cacheLoaderManager.getCacheLoader().remove(Fqn.ROOT); } catch (Exception e) { throw new RuntimeException(e); } } } private void removeInMemoryData(CacheSPI cache) { if (cache.getRoot() != null) { cache.getRoot().clearDataDirect(); cache.getRoot().removeChildrenDirect(); } } private void clearRunningTx(CacheSPI cache) { if (cache != null && cache.getTransactionManager() != null) { try { cache.getTransactionManager().rollback(); } catch (Exception e) { // don't care } } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/notifications/0000755000175000017500000000000011675221400025234 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/notifications/CacheListenerRemovalTest.java0000644000175000017500000000320611132627432033002 0ustar moellermoellerpackage org.jboss.cache.notifications; import org.jboss.cache.Cache; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.notifications.annotation.CacheListener; import org.jboss.cache.notifications.annotation.NodeVisited; import org.jboss.cache.notifications.event.Event; import org.testng.annotations.Test; import java.util.concurrent.atomic.AtomicInteger; /** * @author Manik Surtani */ @Test(groups = "functional", testName = "notifications.CacheListenerRemovalTest") public class CacheListenerRemovalTest { public void testListenerRemoval() { Cache cache = new UnitTestCacheFactory().createCache(getClass()); AtomicInteger i = new AtomicInteger(0); try { assert 0 == cache.getCacheListeners().size(); Listener l = new Listener(i); cache.addCacheListener(l); assert 1 == cache.getCacheListeners().size(); assert cache.getCacheListeners().iterator().next() == l; assert 0 == i.get(); cache.get(Fqn.ROOT, "x"); assert 1 == i.get(); // remove the listener cache.removeCacheListener(l); assert 0 == cache.getCacheListeners().size(); i.set(0); assert 0 == i.get(); cache.get(Fqn.ROOT, "x"); assert 0 == i.get(); } finally { cache.stop(); } } @CacheListener public static class Listener { AtomicInteger i; private Listener(AtomicInteger i) { this.i = i; } @NodeVisited public void listen(Event e) { if (e.isPre()) i.incrementAndGet(); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/notifications/NotifierTest.java0000644000175000017500000003076611236536237030544 0ustar moellermoellerpackage org.jboss.cache.notifications; import static org.easymock.EasyMock.*; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.buddyreplication.BuddyGroup; import org.jboss.cache.config.Configuration; import org.jboss.cache.invocation.MVCCInvocationContext; import org.jboss.cache.notifications.annotation.*; import org.jboss.cache.notifications.event.*; import org.jgroups.View; 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; /** * Tester class for {@link org.jboss.cache.notifications.NotifierImpl}. * * @author Mircea.Markus@jboss.com * @since 2.2 */ @Test(groups = "unit", sequential = true, testName = "notifications.NotifierTest") public class NotifierTest { private NotifierImpl notifier; private InvocationContext ctx; private AllEventsListener allEventsListener; private Fqn fqn = Fqn.fromString("/a/b/c"); @BeforeMethod public void setUp() { notifier = new NotifierImpl(); CacheSPI cacheSPI = createNiceMock(CacheSPI.class); expect(cacheSPI.getInvocationContext()).andStubReturn(new MVCCInvocationContext()); replay(cacheSPI); notifier.injectDependencies(cacheSPI, new Configuration()); notifier.start(); ctx = new MVCCInvocationContext(); allEventsListener = new AllEventsListener(); notifier.addCacheListener(allEventsListener); } @AfterMethod public void tearDown() { notifier.stop(); notifier.destroy(); notifier = null; } 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 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 testNotifyCacheStoppedPre() { assert allEventsListener.cacheStoppedEvent == null; notifier.notifyCacheStoppedPre(); assert allEventsListener.cacheStoppedEvent != null; assert allEventsListener.cacheStoppedEvent.getType() == Event.Type.CACHE_STOPPED; assert allEventsListener.cacheStoppedEvent.isPre(); } public void testNotifyCacheStoppedPost() { assert allEventsListener.cacheStoppedEvent == null; notifier.notifyCacheStoppedPost(); assert allEventsListener.cacheStoppedEvent != null; assert allEventsListener.cacheStoppedEvent.getType() == Event.Type.CACHE_STOPPED; assert !allEventsListener.cacheStoppedEvent.isPre(); } 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; } } } ././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/notifications/BuddyGroupChangeNotificationTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/notifications/BuddyGroupChangeNotificationTes0000644000175000017500000000445111132625723033404 0ustar moellermoellerpackage org.jboss.cache.notifications; import org.jboss.cache.Cache; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.buddyreplication.BuddyReplicationTestsBase; import org.jboss.cache.config.BuddyReplicationConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * @author Manik Surtani (manik AT jboss DOT org) * @since 2.1.0 */ @Test(groups = "functional", testName = "notifications.BuddyGroupChangeNotificationTest") public class BuddyGroupChangeNotificationTest extends BuddyReplicationTestsBase { Cache c1, c2, c3; static boolean stage2 = false; static boolean notificationsReceived = true; @BeforeMethod public void setUp() throws CloneNotSupportedException { UnitTestCacheFactory cf = new UnitTestCacheFactory(); Configuration conf = new Configuration(); conf.setCacheMode(Configuration.CacheMode.REPL_SYNC); BuddyReplicationConfig brc = new BuddyReplicationConfig(); brc.setEnabled(true); conf.setBuddyReplicationConfig(brc); c1 = cf.createCache(conf, false, getClass()); c2 = cf.createCache(conf.clone(), false, getClass()); c3 = cf.createCache(conf.clone(), false, getClass()); c1.start(); c2.start(); c3.start(); // make sure views are received and groups are formed first TestingUtil.blockUntilViewsReceived(60000, c1, c2, c3); } @AfterMethod public void tearDown() throws Exception { super.tearDown(); TestingUtil.killCaches(c1, c2, c3); c1 = null; c2 = null; c3 = null; } @Test(timeOut = 60000) public void testChangingGroups() throws Exception { // initial state waitForBuddy(c1, c2, true, 60000); waitForBuddy(c2, c3, true, 60000); waitForBuddy(c3, c1, true, 60000); // kill c3 c3.stop(); waitForBuddy(c1, c2, true, 60000); waitForBuddy(c2, c1, true, 60000); stage2 = true; c3.start(); waitForBuddy(c1, c2, true, 60000); waitForBuddy(c2, c3, true, 60000); waitForBuddy(c3, c1, true, 60000); assert notificationsReceived; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/notifications/EventLog.java0000644000175000017500000000325611111047320027620 0ustar moellermoellerpackage org.jboss.cache.notifications; import org.jboss.cache.notifications.annotation.CacheListener; 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.NodeInvalidated; 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.TransactionCompleted; import org.jboss.cache.notifications.annotation.TransactionRegistered; 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 @NodeInvalidated 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); } }jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/notifications/NotifyNodeInvalidatedTest.java0000644000175000017500000000412011131613416033156 0ustar moellermoellerpackage org.jboss.cache.notifications; import org.jboss.cache.Cache; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.notifications.event.Event; import static org.jboss.cache.notifications.event.Event.Type.NODE_INVALIDATED; import org.jboss.cache.notifications.event.EventImpl; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.List; import org.jboss.cache.UnitTestCacheFactory; @Test(groups = "functional", testName = "notifications.NotifyNodeInvalidatedTest") public class NotifyNodeInvalidatedTest { public void testInvalidatedCallback() throws CloneNotSupportedException { Cache c1 = null, c2 = null; try { Configuration cfg = UnitTestConfigurationFactory.createConfiguration(CacheMode.INVALIDATION_SYNC, false); cfg.setNodeLockingScheme(NodeLockingScheme.MVCC); c1 = new UnitTestCacheFactory().createCache(cfg.clone(), getClass()); c2 = new UnitTestCacheFactory().createCache(cfg.clone(), getClass()); EventLog eventLog = new EventLog(); c2.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); c2.put("/a/b/c", "x", "y"); c2.addCacheListener(eventLog); c1.put("/a/b/c", "k", "v"); List expected = new ArrayList(); expected.add(new EventImpl(true, c2, null, null, Fqn.fromString("/a/b/c"), null, false, null, false, null, NODE_INVALIDATED)); expected.add(new EventImpl(false, c2, null, null, Fqn.fromString("/a/b/c"), null, false, null, false, null, NODE_INVALIDATED)); assert expected.equals(eventLog.events) : "Expected " + expected + " but got " + eventLog.events; assert c2.getNode("/a/b/c") == null; } finally { TestingUtil.killCaches(c1, c2); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/notifications/AsyncNotificationTest.java0000644000175000017500000000434411120367722032373 0ustar moellermoellerpackage org.jboss.cache.notifications; import org.jboss.cache.Cache; import org.jboss.cache.notifications.annotation.CacheListener; import org.jboss.cache.notifications.annotation.NodeCreated; import org.jboss.cache.notifications.event.NodeCreatedEvent; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.Test; import java.util.concurrent.CountDownLatch; import org.jboss.cache.UnitTestCacheFactory; @Test(groups = "functional", testName = "notifications.AsyncNotificationTest") public class AsyncNotificationTest { public void testAsyncNotification() throws InterruptedException { Cache c = null; try { c = new UnitTestCacheFactory().createCache(getClass()); CountDownLatch latch = new CountDownLatch(2); AbstractListener syncListener = new Listener(latch); AbstractListener asyncListener = new AsyncListener(latch); c.addCacheListener(syncListener); c.addCacheListener(asyncListener); c.put("/a", "k", "v"); latch.await(); assert syncListener.caller == Thread.currentThread(); assert asyncListener.caller != Thread.currentThread(); } finally { TestingUtil.killCaches(c); } } public abstract static class AbstractListener { Thread caller; CountDownLatch latch; protected AbstractListener(CountDownLatch latch) { this.latch = latch; } } @CacheListener(sync = true) public static class Listener extends AbstractListener { public Listener(CountDownLatch latch) { super(latch); } @NodeCreated public void handle(NodeCreatedEvent e) { if (e.isPre()) { caller = Thread.currentThread(); latch.countDown(); } } } @CacheListener(sync = false) public static class AsyncListener extends AbstractListener { public AsyncListener(CountDownLatch latch) { super(latch); } @NodeCreated public void handle(NodeCreatedEvent e) { if (e.isPre()) { caller = Thread.currentThread(); latch.countDown(); } } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/notifications/RemoteCacheListenerTest.java0000644000175000017500000007267011132625723032644 0ustar moellermoeller/* * 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.*; 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.*; /** * Remote conterpart of CacheListenerTest. Main difference is event is originating as local. * * @since 2.0.0 */ @Test(groups = "functional", sequential = true, testName = "notifications.RemoteCacheListenerTest") public class RemoteCacheListenerTest { protected boolean optLocking = false; private Cache cache1, cache2; @SuppressWarnings("unused") private TransactionManager tm1; private EventLog eventLog1 = new EventLog(), eventLog2 = new EventLog(); private final 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); else c.setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); 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); UnitTestCacheFactory instance = new UnitTestCacheFactory(); cache1 = instance.createCache(c, getClass()); cache2 = instance.createCache(c.clone(), getClass()); eventLog1.events.clear(); eventLog2.events.clear(); cache1.addCacheListener(eventLog1); cache2.addCacheListener(eventLog2); tm1 = cache1.getConfiguration().getRuntimeConfig().getTransactionManager(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache1, cache2); cache1 = null; cache2 = null; } /** * 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.size(), eventLog1.events.size()); 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 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 UnitTestCacheFactory instance = new UnitTestCacheFactory(); cache2 = instance.createCache(cache1.getConfiguration().clone(), false, getClass()); 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(); 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); 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-3.2.8.GA/src/test/java/org/jboss/cache/notifications/CacheListenerTest.java0000644000175000017500000006713711144362365031475 0ustar moellermoeller/***************************************** * * * 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.Fqn; import org.jboss.cache.Node; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Option; 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; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.util.TestingUtil; /** * 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 * @author Galder Zamarreno */ @Test(groups = "functional", sequential = true, testName = "notifications.CacheListenerTest") 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); c.setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); if (optLocking) c.setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache = new UnitTestCacheFactory().createCache(c, getClass()); 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(); TestingUtil.killCaches(cache); cache = null; } // simple tests first public void testCreation() { creation(false); eventLog.events.clear(); creation(true); } protected void creation(boolean supressEventNotification) { assertEquals("Event log should be empty", Collections.emptyList(), eventLog.events); if (supressEventNotification) { setSuppressEventNotification(); } cache.put(fqn, "key", "value"); Map data = new HashMap(); data.put("key", "value"); //expected List expected = new ArrayList(); if (!supressEventNotification) { 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() { onlyModification(false); eventLog.events.clear(); onlyModification(true); } protected void onlyModification(boolean supressEventNotification) { 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 if (supressEventNotification) { setSuppressEventNotification(); } cache.put(fqn, "key", "value2"); Map newData = new HashMap(); newData.put("key", "value2"); //expected List expected = new ArrayList(); if (!supressEventNotification) { 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() { onlyRemoval(false); eventLog.events.clear(); onlyRemoval(true); } protected void onlyRemoval(boolean supressEventNotification) { 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 if (supressEventNotification) { setSuppressEventNotification(); } cache.removeNode(fqn); //expected List expected = new ArrayList(); if (!supressEventNotification) { 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() { nonexistentRemove(false); eventLog.events.clear(); nonexistentRemove(true); } protected void nonexistentRemove(boolean supressEventNotification) { if (supressEventNotification) { setSuppressEventNotification(); } cache.removeNode("/does/not/exist"); List expected = new ArrayList(); if (!supressEventNotification) { 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() { removeData(false); eventLog.events.clear(); removeData(true); } protected void removeData(boolean supressEventNotification) { 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 if (supressEventNotification) { setSuppressEventNotification(); } cache.remove(fqn, "key2"); Map removedData = new HashMap(); removedData.put("key2", "value2"); //expected List expected = new ArrayList(); if (!supressEventNotification) { 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 { putMap(false); eventLog.events.clear(); putMap(true); } protected void putMap(boolean supressEventNotification) { 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 if (supressEventNotification) { setSuppressEventNotification(); } cache.put(fqn, oldData); //expected List expected = new ArrayList(); if (!supressEventNotification) { 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() { move(false); eventLog.events.clear(); move(true); } protected void move(boolean supressEventNotification) { 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); if (supressEventNotification) { setSuppressEventNotification(); } cache.move(n1.getFqn(), n2.getFqn()); //expected Fqn newFqn = Fqn.fromRelativeElements(newParent, fqn.getLastElement()); List expected = new ArrayList(); if (!supressEventNotification) { 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 { txNonexistentRemove(false); eventLog.events.clear(); txNonexistentRemove(true); } protected void txNonexistentRemove(boolean supressEventNotification) throws Exception { if (supressEventNotification) { setSuppressEventNotification(); } tm.begin(); Transaction tx = tm.getTransaction(); cache.removeNode("/does/not/exist"); if (supressEventNotification) { setSuppressEventNotification(); } tm.commit(); List expected = new ArrayList(); if (!supressEventNotification) { 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 { txCreationCommit(false); eventLog.events.clear(); txCreationCommit(true); } protected void txCreationCommit(boolean supressEventNotification) throws Exception { assertEquals("Event log should be empty", Collections.emptyList(), eventLog.events); if (supressEventNotification) { setSuppressEventNotification(); } tm.begin(); Transaction tx = tm.getTransaction(); if (supressEventNotification) { setSuppressEventNotification(); } cache.put(fqn, "key", "value"); //expected Map data = new HashMap(); data.put("key", "value"); List expected = new ArrayList(); if (!supressEventNotification) { 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); if (supressEventNotification) { setSuppressEventNotification(); } tm.commit(); if (!supressEventNotification) { 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 { txCreationRollback(false); eventLog.events.clear(); txCreationRollback(true); } protected void txCreationRollback(boolean supressEventNotification) throws Exception { assertEquals("Event log should be empty", Collections.emptyList(), eventLog.events); if (supressEventNotification) { setSuppressEventNotification(); } tm.begin(); Transaction tx = tm.getTransaction(); if (supressEventNotification) { setSuppressEventNotification(); } cache.put(fqn, "key", "value"); //expected Map data = new HashMap(); data.put("key", "value"); List expected = new ArrayList(); if (!supressEventNotification) { 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); if (supressEventNotification) { setSuppressEventNotification(); } tm.rollback(); if (!supressEventNotification) { 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 { txOnlyModification(false); eventLog.events.clear(); txOnlyModification(true); } protected void txOnlyModification(boolean supressEventNotification) 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 if (supressEventNotification) { setSuppressEventNotification(); } tm.begin(); Transaction tx = tm.getTransaction(); if (supressEventNotification) { setSuppressEventNotification(); } cache.put(fqn, "key", "value2"); Map newData = new HashMap(); newData.put("key", "value2"); //expected List expected = new ArrayList(); if (!supressEventNotification) { 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); if (supressEventNotification) { setSuppressEventNotification(); } tm.commit(); if (!supressEventNotification) { 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 { txOnlyRemoval(false); eventLog.events.clear(); txOnlyRemoval(true); } protected void txOnlyRemoval(boolean supressEventNotification) 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 if (supressEventNotification) { setSuppressEventNotification(); } tm.begin(); Transaction tx = tm.getTransaction(); if (supressEventNotification) { setSuppressEventNotification(); } cache.removeNode(fqn); //expected List expected = new ArrayList(); if (!supressEventNotification) { 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); if (supressEventNotification) { setSuppressEventNotification(); } tm.commit(); if (!supressEventNotification) { 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 { txRemoveData(false); eventLog.events.clear(); txRemoveData(true); } protected void txRemoveData(boolean supressEventNotification) 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 if (supressEventNotification) { setSuppressEventNotification(); } tm.begin(); Transaction tx = tm.getTransaction(); if (supressEventNotification) { setSuppressEventNotification(); } cache.remove(fqn, "key2"); Map removedData = new HashMap(); removedData.put("key2", "value2"); //expected List expected = new ArrayList(); if (!supressEventNotification) { 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)); } if (supressEventNotification) { setSuppressEventNotification(); } tm.commit(); if (!supressEventNotification) { 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); } protected void txMove(boolean supressEventNotification) 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); if (supressEventNotification) { setSuppressEventNotification(); } tm.begin(); Transaction tx = tm.getTransaction(); if (supressEventNotification) { setSuppressEventNotification(); } cache.move(n1.getFqn(), n2.getFqn()); //expected Fqn newFqn = Fqn.fromRelativeElements(newParent, fqn.getLastElement()); List expected = new ArrayList(); if (!supressEventNotification) { 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); if (supressEventNotification) { setSuppressEventNotification(); } tm.commit(); if (!supressEventNotification) { expected.add(new EventImpl(false, cache, null, null, null, tx, true, null, true, null, TRANSACTION_COMPLETED)); } assertEquals(expected, eventLog.events); } protected void setSuppressEventNotification() { Option option = new Option(); option.setSuppressEventNotification(true); cache.getInvocationContext().setOptionOverrides(option); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/notifications/ConcurrentNotificationTest.java0000644000175000017500000000774711132625723033453 0ustar moellermoellerpackage 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.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.notifications.annotation.*; import org.jboss.cache.notifications.event.Event; 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 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"}, sequential = true, testName = "notifications.ConcurrentNotificationTest") 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() { UnitTestCacheFactory instance = new UnitTestCacheFactory(); cache = instance.createCache(getClass()); listener = new Listener(); cache.addCacheListener(listener); } @AfterMethod(alwaysRun = true) public void tearDown() { TestingUtil.killCaches(cache); cache = null; } 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(); } } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/notifications/CacheListenerPassivationTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/notifications/CacheListenerPassivationTest.ja0000644000175000017500000002532311121730272033345 0ustar moellermoellerpackage org.jboss.cache.notifications; import org.jboss.cache.Cache; 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.testloaders.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; import org.jboss.cache.UnitTestCacheFactory; @Test(groups = "functional", sequential = true, testName = "notifications.CacheListenerPassivationTest") 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 UnitTestCacheFactory().createCache(c, getClass()); eventLog.events.clear(); cache.addCacheListener(eventLog); tm = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache); cache = null; } 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, 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, null, Collections.emptyMap(), fqn, null, true, null, false, null, NODE_ACTIVATED)); 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, 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, 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"); } } ././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/notifications/RemoteCacheListenerOptimisticTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/notifications/RemoteCacheListenerOptimisticTe0000644000175000017500000000116311111047320033373 0ustar moellermoellerpackage 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", "optimistic"}, testName = "notifications.RemoteCacheListenerOptimisticTest") public class RemoteCacheListenerOptimisticTest extends RemoteCacheListenerTest { public RemoteCacheListenerOptimisticTest() { optLocking = true; } @Override public void testStateTransfer() throws Exception { // no op } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/notifications/NodeMapDiffTest.java0000644000175000017500000001060411111047320031044 0ustar moellermoellerpackage org.jboss.cache.notifications; import org.jboss.cache.util.Util; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.fail; import org.testng.annotations.Test; import java.util.HashMap; import java.util.Map; /** * Tests the diffs between maps. * * @author Manik Surtani * @since 2.0.0 */ @Test(groups = {"functional"}, testName = "notifications.NodeMapDiffTest") 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)); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/notifications/NotifierAnnotationsTest.java0000644000175000017500000002135511120421765032743 0ustar moellermoellerpackage org.jboss.cache.notifications; import static org.easymock.EasyMock.createNiceMock; import org.jboss.cache.CacheSPI; 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.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.AfterMethod; 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"}, sequential = true, testName = "notifications.NotifierAnnotationsTest") public class NotifierAnnotationsTest { private NotifierImpl n; @BeforeMethod(alwaysRun = true) public void setUp() { n = new NotifierImpl(); n.injectDependencies(createNiceMock(CacheSPI.class), new Configuration()); n.start(); } @AfterMethod public void tearDown() { n.stop(); n.destroy(); n = null; } 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.cacheStartedListeners; assertEquals(1, invocations.size()); invocations = n.cacheStoppedListeners; assertEquals(1, invocations.size()); assertEquals(1, n.getCacheListeners().size()); } public void testMultipleAnnotationsOneMethod() { Object l = new TestMultipleAnnotationsOneMethodListener(); n.addCacheListener(l); List invocations = n.cacheStartedListeners; assertEquals(1, invocations.size()); invocations = n.cacheStoppedListeners; assertEquals(1, invocations.size()); assertEquals(1, n.getCacheListeners().size()); } public void testMultipleMethodsOneAnnotation() { Object l = new TestMultipleMethodsOneAnnotationListener(); n.addCacheListener(l); List invocations = n.cacheStartedListeners; assertEquals(2, invocations.size()); assertEquals(1, n.getCacheListeners().size()); } @CacheListener public class TestControlListener { @CacheStarted @CacheStopped public void callback(Event e) { } } @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) { } } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/notifications/CacheListenerOptimisticTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/notifications/CacheListenerOptimisticTest.jav0000644000175000017500000000070211111047320033343 0ustar moellermoeller/* * 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", "optimistic"}, testName = "notifications.CacheListenerOptimisticTest") public class CacheListenerOptimisticTest extends CacheListenerTest { public CacheListenerOptimisticTest() { optLocking = true; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/notifications/NotificationThreadTest.java0000644000175000017500000001552711131613416032526 0ustar moellermoellerpackage org.jboss.cache.notifications; import org.jboss.cache.Cache; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.loader.testloaders.DummyInMemoryCacheLoader; import org.jboss.cache.notifications.annotation.*; import org.jboss.cache.notifications.event.Event; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.assertNotSame; import static org.testng.AssertJUnit.assertSame; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.TransactionManager; import java.util.LinkedList; import java.util.List; import java.util.Properties; /** * Tests the threading model used when calling notifications * * @author Manik Surtani * @since 2.0.0 */ @Test(groups = "functional", sequential = true, testName = "notifications.NotificationThreadTest") public class NotificationThreadTest { private Cache cache1, cache2; private TestCacheListener listener; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { // need 2 caches to test viewChange notifications UnitTestCacheFactory instance = new UnitTestCacheFactory(); Configuration conf1 = new Configuration(); Configuration conf2 = new Configuration(); conf1.setCacheMode(Configuration.CacheMode.REPL_SYNC); conf2.setCacheMode(Configuration.CacheMode.REPL_SYNC); conf1.setSyncCommitPhase(true); conf2.setSyncCommitPhase(true); conf1.setSyncRollbackPhase(true); conf2.setSyncRollbackPhase(true); conf1.setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); conf2.setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); conf1.setCacheLoaderConfig(UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", DummyInMemoryCacheLoader.class.getName(), (Properties) null, false, false, false, false, false)); cache1 = instance.createCache(conf1, false, getClass()); cache2 = instance.createCache(conf2, false, getClass()); listener = new TestCacheListener(); cache1.addCacheListener(listener); } @AfterMethod(alwaysRun = true) public void tearDown() { TestingUtil.killCaches(cache1, cache2); cache1 = null; cache2 = null; } 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-3.2.8.GA/src/test/java/org/jboss/cache/invocation/0000755000175000017500000000000011675221401024535 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/invocation/InterceptorChainTest.java0000644000175000017500000001677411111047320031507 0ustar moellermoellerpackage 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; import org.testng.annotations.AfterMethod; /** * Tests functionality defined by InterceptorChain. * * @author Mircea.Markus@jboss.com * @since 2.2 */ @Test(groups = {"unit"}, testName = "invocation.InterceptorChainTest") 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); } @AfterMethod public void tearDown() { icInterceptor = null; invalidationInterceptor = null; txInterceptor = null; pessimisticInterceptor = null; callInterceptor = null; chain = null; } 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 testRemoveInterceptorWithtType() { 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 testAddInterceptorWithType() { assert chain.addAfterInterceptor(invalidationInterceptor, icInterceptor.getClass()); assert icInterceptor.getNext().equals(invalidationInterceptor); chain.addAfterInterceptor(txInterceptor, icInterceptor.getClass()); assert icInterceptor.getNext().equals(txInterceptor); assert txInterceptor.getNext().equals(invalidationInterceptor); } public void testGetInterceptorsWithClassName() { chain.appendIntereceptor(invalidationInterceptor); chain.appendIntereceptor(callInterceptor); chain.appendIntereceptor(pessimisticInterceptor); chain.appendIntereceptor(create(CallInterceptor.class)); assert chain.getInterceptorsWithClassName(InvocationContextInterceptor.class.getName()).size() == 1; assert chain.getInterceptorsWithClassName(InvalidationInterceptor.class.getName()).size() == 1; assert chain.getInterceptorsWithClassName(PessimisticLockInterceptor.class.getName()).size() == 1; assert chain.getInterceptorsWithClassName(CallInterceptor.class.getName()).size() == 2; assert chain.getInterceptorsWithClassName(CommandInterceptor.class.getName()).size() == 0; } private CommandInterceptor create(Class toInstantiate) { try { return toInstantiate.newInstance(); } catch (Throwable th) { throw new RuntimeException(th); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/AbstractSingleCacheTest.java0000644000175000017500000000302011130442355027712 0ustar moellermoellerpackage org.jboss.cache; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.AfterClass; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; /** * @author Mircea.Markus@jboss.com */ @Test(groups = {"functional", "unit"}) public abstract class AbstractSingleCacheTest extends AbstractCacheTest { protected CacheSPI cache; /** * This method will always be called before {@link #create()}. If you override this, make sure you annotate the * overridden method with {@link org.testng.annotations.BeforeClass}. * * @throws Exception Just in case */ @BeforeClass public void preCreate() throws Exception { // no op, made for overriding. } // Due to some weirdness with TestNG, it always appends the package and class name to the method names // provided on dependsOnMethods unless it thinks there already is a package. This does accept regular expressions // though so .*. works. Otherwise it won't detect overridden methods in subclasses. @BeforeClass(dependsOnMethods = "org.jboss.*.preCreate") protected void create() throws Exception { cache = createCache(); } @AfterClass protected void destroy() { TestingUtil.killCaches(cache); } @AfterMethod protected void clearContent() { super.clearContent(cache); } protected abstract CacheSPI createCache() throws Exception; } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/multiplexer/0000755000175000017500000000000011675221405024742 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/multiplexer/Buddy4Nodes2BackupsTest.java0000644000175000017500000000454011121730272032160 0ustar moellermoeller/* * 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; import org.testng.annotations.BeforeTest; /** * Repeats the superclass tests, but with the multiplexer enabled. * * @author Brian Stansberry * @version $Revision: 7332 $ */ @Test(enabled = true, testName = "multiplexer.Buddy4Nodes2BackupsTest") public class Buddy4Nodes2BackupsTest extends org.jboss.cache.buddyreplication.Buddy4Nodes2BackupsTest { private MultiplexerTestHelper muxHelper; @BeforeTest 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-3.2.8.GA/src/test/java/org/jboss/cache/multiplexer/AsyncReplTest.java0000644000175000017500000000527111111047320030335 0ustar moellermoeller/* * 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: 7168 $ */ @Test(groups = {"functional", "jgroups"}, testName = "multiplexer.AsyncReplTest") public class AsyncReplTest extends org.jboss.cache.replicated.AsyncReplTest { private ThreadLocal muxHelperTL = new ThreadLocal(); @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { muxHelperTL.set(new MultiplexerTestHelper()); super.setUp(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { try { super.tearDown(); } finally { if (muxHelperTL.get() != null) { muxHelperTL.get().tearDown(); muxHelperTL.set(null); } } } @Test(enabled = true) public void testPutShouldNotReplicateToDifferentCluster() { super.testPutShouldNotReplicateToDifferentCluster(); } protected void configureMultiplexer(Cache cache) throws Exception { muxHelperTL.get().configureCacheForMux(cache); } protected void validateMultiplexer(Cache cache) { AssertJUnit.assertTrue("Cache is using multiplexer", cache.getConfiguration().isUsingMultiplexer()); } @Override // workaround for JBCACHE-1434 public void testStateTransfer() { super.testStateTransfer(); } } ././@LongLink0000000000000000000000000000015700000000000011570 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/multiplexer/MultiplexerBuddy3NodesWithFailoverTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/multiplexer/MultiplexerBuddy3NodesWithFailove0000644000175000017500000000501711131431461033400 0ustar moellermoeller/* * 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.buddyreplication.Buddy3NodesWithFailoverTest; 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: 7414 $ */ @Test(groups={"functional", "jgroups"}, enabled = true, testName = "multiplexer.MultiplexerBuddy3NodesWithFailoverTest") public class MultiplexerBuddy3NodesWithFailoverTest extends Buddy3NodesWithFailoverTest { private ThreadLocal muxHelperTL = new ThreadLocal(); @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { muxHelperTL.set(new MultiplexerTestHelper()); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { try { super.tearDown(); } finally { if (muxHelperTL.get() != null) muxHelperTL.get().tearDown(); muxHelperTL.set(null); } } @Override protected void configureMultiplexer(Cache cache) throws Exception { muxHelperTL.get().configureCacheForMux(cache); } @Override protected void validateMultiplexer(Cache cache) { assertTrue("Cache is using multiplexer", cache.getConfiguration().isUsingMultiplexer()); } } ././@LongLink0000000000000000000000000000016400000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/multiplexer/Buddy2NodesBackupActivationInactivationTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/multiplexer/Buddy2NodesBackupActivationInacti0000644000175000017500000000474511120273541033312 0ustar moellermoeller/* * 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: 7283 $ */ @Test(enabled = true, testName = "multiplexer.Buddy2NodesBackupActivationInactivationTest") public class Buddy2NodesBackupActivationInactivationTest extends org.jboss.cache.buddyreplication.Buddy2NodesBackupActivationInactivationTest { private ThreadLocal muxHelperTL = new ThreadLocal(); @BeforeMethod(alwaysRun = true) protected void setUp() throws Exception { muxHelperTL.set(new MultiplexerTestHelper()); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { try { super.tearDown(); } finally { if (muxHelperTL.get() != null) muxHelperTL.get().tearDown(); muxHelperTL.set(null); } } @Override protected void configureMultiplexer(Cache cache) throws Exception { muxHelperTL.get().configureCacheForMux(cache); } @Override protected void validateMultiplexer(Cache cache) { assertTrue("Cache is using multiplexer", cache.getConfiguration().isUsingMultiplexer()); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/multiplexer/StateTransferTest.java0000644000175000017500000000475511111047320031230 0ustar moellermoeller/* * 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: 7168 $ */ @Test(groups = {"functional", "jgroups"}, testName = "multiplexer.StateTransferTest") public class StateTransferTest extends StateTransfer200Test { private ThreadLocal muxHelperTL = new ThreadLocal(); @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { muxHelperTL.set(new MultiplexerTestHelper()); super.setUp(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { try { super.tearDown(); } finally { if (muxHelperTL.get() != null) { muxHelperTL.get().tearDown(); muxHelperTL.set(null); } } } @Override protected void configureMultiplexer(Cache cache) throws Exception { muxHelperTL.get().configureCacheForMux(cache); } @Override protected void validateMultiplexer(Cache cache) { AssertJUnit.assertTrue("Cache is using multiplexer", cache.getConfiguration().isUsingMultiplexer()); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/multiplexer/BadMuxConfigTest.java0000644000175000017500000000624011131613416030747 0ustar moellermoellerpackage org.jboss.cache.multiplexer; import org.jboss.cache.Cache; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestConfigurationFactory; 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; /** * Tests that JBC starts correctly even if the multiplexer * configuration is incorrect. * * @author Brian Stansberry * @version $Revision: 7422 $ */ @Test(groups = {"functional", "jgroups"}, enabled = true, sequential = true, testName = "multiplexer.BadMuxConfigTest") 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 = UnitTestConfigurationFactory.getEmptyConfiguration(); config.setCacheMode(Configuration.CacheMode.REPL_SYNC); cache = new UnitTestCacheFactory().createCache(config, false, getClass()); cacheStarted = false; } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { try { if (cache != null) { TestingUtil.killCaches(cache); cache = null; } } 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-3.2.8.GA/src/test/java/org/jboss/cache/multiplexer/BuddyAssignmentStateTransferTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/multiplexer/BuddyAssignmentStateTransferTest.0000644000175000017500000000470311111047320033400 0ustar moellermoeller/* * 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: 7168 $ */ @Test(enabled = true, testName = "multiplexer.BuddyAssignmentStateTransferTest") public class BuddyAssignmentStateTransferTest extends org.jboss.cache.buddyreplication.BuddyAssignmentStateTransferTest { private ThreadLocal muxHelperTL = new ThreadLocal(); @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { muxHelperTL.set(new MultiplexerTestHelper()); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { try { super.tearDown(); } finally { if (muxHelperTL.get() != null) muxHelperTL.get().tearDown(); muxHelperTL.set(null); } } @Override protected void configureMultiplexer(Cache cache) throws Exception { muxHelperTL.get().configureCacheForMux(cache); } @Override protected void validateMultiplexer(Cache cache) { assertTrue("Cache is using multiplexer", cache.getConfiguration().isUsingMultiplexer()); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/multiplexer/PessimisticSyncReplTxTest.java0000644000175000017500000000462611111047320032730 0ustar moellermoeller/* * 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: 7168 $ */ @Test(groups = {"functional", "jgroups"}, enabled = true, testName = "multiplexer.PessimisticSyncReplTxTest") public class PessimisticSyncReplTxTest extends org.jboss.cache.replicated.PessimisticSyncReplTxTest { 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()); } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/multiplexer/ChannelInjectionPreferenceTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/multiplexer/ChannelInjectionPreferenceTest.ja0000644000175000017500000001040511131613416033322 0ustar moellermoellerpackage org.jboss.cache.multiplexer; import org.jboss.cache.Cache; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.RuntimeConfig; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.util.TestingUtil; import org.jgroups.Channel; import org.jgroups.JChannel; import org.jgroups.conf.XmlConfigurator; import org.jgroups.conf.ConfiguratorFactory; 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: 7422 $ */ @Test(groups = {"functional", "jgroups"}, enabled = true, sequential = true, testName = "multiplexer.ChannelInjectionPreferenceTest") 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 = UnitTestConfigurationFactory.getEmptyConfiguration(); config.setCacheMode(Configuration.CacheMode.REPL_SYNC); cache = new UnitTestCacheFactory().createCache(config, false, getClass()); cacheStarted = false; } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { try { if (cacheStarted && cache != null) { TestingUtil.killCaches(cache); cache = null; } } finally { if (muxHelper != null) { muxHelper.tearDown(); muxHelper = null; } } } public void testChannelInjectionPreference() throws Exception { muxHelper.configureCacheForMux(cache); Channel channel = new JChannel(new UnitTestCacheFactory().mangleClusterConfigurationUdp( getClusterConfigFromProperties(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()); } } } /** * 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 properties config properties * @return a String */ public static String getClusterConfigFromProperties(String properties) { try { XmlConfigurator conf = XmlConfigurator.getInstance(ConfiguratorFactory.getConfigStream(properties)); 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"); return tmp; } catch (Exception e) { throw new RuntimeException("Problems with properties " + properties, e); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/multiplexer/MultiplexerTestHelper.java0000644000175000017500000001536611131613416032124 0ustar moellermoeller/* * 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; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.factories.UnitTestConfigurationFactory; /** * Utility class that can associate a cache with a multiplexer-enabled * JGroups ChannelFactory. * * @author Brian Stansberry * @version $Revision: 7422 $ */ 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 + Thread.currentThread().getName()); } } /** * 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 ? UnitTestConfigurationFactory.getEmptyConfiguration().getClusterConfig() : 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) { muxConfig = new UnitTestCacheFactory().mangleClusterConfiguration(muxConfig); 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 + Thread.currentThread().getName()); 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-3.2.8.GA/src/test/java/org/jboss/cache/UnitTestCacheFactory.java0000644000175000017500000003207011132756165027274 0ustar moellermoeller/* * 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.ConfigurationException; import org.jboss.cache.config.parsing.XmlConfigurationParser; import org.jboss.cache.config.parsing.XmlConfigurationParser2x; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.util.TestingUtil; import org.jgroups.stack.GossipRouter; import java.io.IOException; import java.io.InputStream; import java.net.InetSocketAddress; import java.net.MulticastSocket; import java.net.SocketAddress; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * @author Dominik Pospisil (dpospisi@redhat.com) */ public class UnitTestCacheFactory { public UnitTestCacheFactory() { } private final Log log = LogFactory.getLog(UnitTestCacheFactory.class); public static AtomicInteger replCacheCount = new AtomicInteger(0); public static AtomicInteger localCacheCount = new AtomicInteger(0); /** * Holds unique mcast_addr for each thread used for JGroups channel construction. */ private static final ThreadLocal threadMcastIP = new ThreadLocal() { private final AtomicInteger uniqueAddr = new AtomicInteger(11); @Override protected String initialValue() { return "228.10.10." + uniqueAddr.getAndIncrement(); } }; private static final ThreadLocal threadTcpStartPort = new ThreadLocal() { private final AtomicInteger uniqueAddr = new AtomicInteger(7900); @Override protected String initialValue() { return uniqueAddr.getAndAdd(50) + ""; } }; /** * Holds unique mcast_port for each thread used for JGroups channel construction. */ private static final ThreadLocal threadMcastPort = new ThreadLocal() { private final AtomicInteger uniquePort = new AtomicInteger(45589); @Override protected Integer initialValue() { return uniquePort.getAndIncrement(); } }; /** * For each thread holds list of caches created using this factory. */ private static final ThreadLocal> threadCaches = new ThreadLocal>() { @Override protected List initialValue() { return new ArrayList(); } }; private final static List allCaches = new ArrayList(); /** * For each thread holds the name of the test class which executed createCache factory method. */ private static final ThreadLocal threadTestName = new ThreadLocal(); // factory methods public Cache createCache(Class ownerClass) throws ConfigurationException { return createCache(true, ownerClass); } public Cache createCache(boolean start, Class ownerClass) throws ConfigurationException { Configuration config = UnitTestConfigurationFactory.getEmptyConfiguration(); return createCache(config, start, ownerClass); } public Cache createCache(Configuration.CacheMode mode, Class ownerClass) throws ConfigurationException { Configuration config = UnitTestConfigurationFactory.getEmptyConfiguration(); config.setCacheMode(mode); return createCache(config, ownerClass); } public Cache createCache(String configFileName, Class ownerClass) throws ConfigurationException { return createCache(configFileName, true, ownerClass); } public Cache createCache(String configFileName, boolean start, Class ownerClass) throws ConfigurationException { Configuration c = getConfigurationFromFile(configFileName); c.setClusterConfig(UnitTestConfigurationFactory.getEmptyConfiguration().getClusterConfig()); return createCache(c, start, ownerClass); } public Configuration getConfigurationFromFile(String configFileName) { XmlConfigurationParser parser = new XmlConfigurationParser(); Configuration c; try { c = parser.parseFile(configFileName); } catch (ConfigurationException e) { XmlConfigurationParser2x oldParser = new XmlConfigurationParser2x(); c = oldParser.parseFile(configFileName); } return c; } public Cache createCache(Configuration configuration, Class ownerClass) throws ConfigurationException { return createCache(configuration, true, ownerClass); } public Cache createCache(InputStream is, Class ownerClass) throws ConfigurationException { return createCache(is, true, ownerClass); } public Cache createCache(InputStream is, boolean start, Class ownerClass) throws ConfigurationException { XmlConfigurationParser parser = new XmlConfigurationParser(); Configuration c = parser.parseStream(is); return createCache(c, start, ownerClass); } public Cache createCache(Configuration configuration, boolean start, Class ownerClass) throws ConfigurationException { checkCaches(ownerClass); // tryMCastAddress(); switch (configuration.getCacheMode()) { case LOCAL: // local cache, no channel used localCacheCount.incrementAndGet(); break; case REPL_SYNC: case REPL_ASYNC: case INVALIDATION_ASYNC: case INVALIDATION_SYNC: // replicated cache, update channel setup mangleConfiguration(configuration); replCacheCount.incrementAndGet(); break; default: log.info("Unknown cache mode!"); throw new IllegalStateException("Unknown cache mode!"); } Cache cache = new DefaultCacheFactory().createCache(configuration, start); List caches = threadCaches.get(); caches.add(cache); synchronized (allCaches) { allCaches.add(cache); } return cache; } private void tryMCastAddress() { String useIpV4 = System.getProperty("java.net.preferIPv4Stack"); log.info("++++++++++++++++++++++++++++++ useIpV4 property=" + useIpV4); SocketAddress socketAddress = new InetSocketAddress("224.10.10.10", 45588); try { MulticastSocket ms = new MulticastSocket(socketAddress); } catch (IOException e) { log.info("+++++++++++++++++++++++++++ Error : " + e.getMessage(), e); } } /** * Destroys all caches created by this factory in the current thread. * * @return true if some cleanup was actually performed */ public boolean cleanUp() { List caches = new ArrayList(threadCaches.get()); boolean ret = false; for (Cache cache : caches) { TestingUtil.killCaches(cache); ret = true; } return ret; } public void removeCache(Cache c) { List caches = threadCaches.get(); synchronized (allCaches) { if (caches.contains(c)) { caches.remove(c); allCaches.remove(c); } else if (allCaches.contains(c)) { System.err.println("[" + Thread.currentThread().getName() + "] WARNING! Remove cache called from different thread."); Thread.dumpStack(); } } } /** * Updates cluster configuration to ensure mutual thread isolation. * * @param configuration Configuration to update. */ public void mangleConfiguration(Configuration configuration) { String clusterConfig = configuration.getClusterConfig(); clusterConfig = mangleClusterConfiguration(clusterConfig); configuration.setClusterConfig(clusterConfig); // Check if the cluster name contains thread id. If not, append. // We can not just append the threadId, since some of the tests are crating instances // using configurations derived from configurations returned by this factory. String clusterName = configuration.getClusterName(); // append thread id if (clusterName.indexOf(Thread.currentThread().getName()) == -1) { clusterName = clusterName + "-" + Thread.currentThread().getName(); } configuration.setClusterName(clusterName); } public static String mangleClusterConfiguration(String clusterConfig) { if (clusterConfig == null) { if (UnitTestConfigurationFactory.JGROUPS_CHANNEL.equals("udp")) { return mangleClusterConfigurationUdp(null); } else { return mangleClusterConfigurationTcp(null); } } if (clusterConfig.contains("UDP(")) { clusterConfig = mangleClusterConfigurationUdp(clusterConfig); } else { clusterConfig = mangleClusterConfigurationTcp(clusterConfig); } return clusterConfig; } /** * Updates cluster configuration to ensure mutual thread isolation. */ public static String mangleClusterConfigurationUdp(String clusterConfig) { if (clusterConfig == null) { // No explicit cluster configuration found. we need to resolve the default config // now in orded to be able to update it before the cache (and the channel) starts. clusterConfig = UnitTestConfigurationFactory.getEmptyConfiguration().getClusterConfig(); } // replace mcast_addr Pattern pattern = Pattern.compile("mcast_addr=[^;]*"); Matcher m = pattern.matcher(clusterConfig); if (m.find()) { String newAddr = threadMcastIP.get(); clusterConfig = m.replaceFirst("mcast_addr=" + newAddr); } else { Thread.dumpStack(); throw new IllegalStateException(); } // replace mcast_port pattern = Pattern.compile("mcast_port=[^;]*"); m = pattern.matcher(clusterConfig); if (m.find()) { // String origPort = m.group().substring(m.group().indexOf("=") + 1); String newPort = threadMcastPort.get().toString(); clusterConfig = m.replaceFirst("mcast_port=" + newPort); } return clusterConfig; } /** * Updates cluster configuration to ensure mutual thread isolation. */ public static String mangleClusterConfigurationTcp(String clusterConfig) { if (clusterConfig == null) { clusterConfig = UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.REPL_SYNC).getClusterConfig(); } // replace mcast_addr Pattern pattern = Pattern.compile("start_port=[^;]*"); Matcher m = pattern.matcher(clusterConfig); String newStartPort; if (m.find()) { newStartPort = threadTcpStartPort.get(); clusterConfig = m.replaceFirst("start_port=" + newStartPort); } else { System.out.println("Config is:" + clusterConfig); Thread.dumpStack(); throw new IllegalStateException(); } if (clusterConfig.indexOf("TCPGOSSIP") < 0) //onluy adjust for TCPPING { // replace mcast_port pattern = Pattern.compile("initial_hosts=[^;]*"); m = pattern.matcher(clusterConfig); if (m.find()) { clusterConfig = m.replaceFirst("initial_hosts=" + "127.0.0.1[" + newStartPort + "]"); } } return clusterConfig; } // private String getThreadId() // { // return "[" + Thread.currentThread().getName() + "]"; // } private void checkCaches(Class ownerClass) { Class lastTestClass = threadTestName.get(); if ((lastTestClass != null) && (!lastTestClass.equals(ownerClass))) { String threadId = "[" + Thread.currentThread().getName() + "] "; // we are running new test class // check if there is a cache(s) instance left & kill it if possitive if (cleanUp()) { System.err.print(threadId + "WARNING! "); System.err.print(threadId + " A test method in " + lastTestClass + " did not clean all cache instances properly. "); System.err.println(threadId + " Use UnitTestCacheFactory.cleanUp() or TestngUtil.killCaches(...) "); } } threadTestName.set(ownerClass); } private String extractTestName() { StackTraceElement[] stack = Thread.currentThread().getStackTrace(); if (stack.length == 0) return null; for (int i = stack.length - 1; i > 0; i--) { StackTraceElement e = stack[i]; String className = e.getClassName(); if (className.indexOf("org.jboss.cache") != -1) return className; //+ "." + e.getMethodName(); } return null; } public static void main(String[] args) throws Exception { GossipRouter router = new GossipRouter(12000, "localhost"); router.start(); } }jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/interceptors/0000755000175000017500000000000011675221406025112 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/interceptors/LegacyInterceptorTest.java0000644000175000017500000000622311120367722032240 0ustar moellermoellerpackage org.jboss.cache.interceptors; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.marshall.MethodCall; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; 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 AT jboss DOT org) * @since 2.2.0 */ @Test(groups = "functional", sequential = true, testName = "interceptors.LegacyInterceptorTest") public class LegacyInterceptorTest { Cache cache; static CountDownLatch interceptorResumeLatch, interceptorInvokedLatch; TestInterceptor testInterceptor; Executor testRunner; @BeforeMethod public void createLatches() { cache = new UnitTestCacheFactory().createCache(getClass()); testInterceptor = new TestInterceptor(); ((CacheSPI) cache).addInterceptor(testInterceptor, TxInterceptor.class); testRunner = Executors.newSingleThreadExecutor(); interceptorResumeLatch = new CountDownLatch(1); interceptorInvokedLatch = new CountDownLatch(1); } @AfterMethod public void tearDown() { TestingUtil.killCaches(cache); cache = null; } 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. MethodCall methodCall = testInterceptor.methodCall; interceptorResumeLatch.countDown(); assert methodCall.getMethodId() == PutKeyValueCommand.METHOD_ID; assert methodCall.getArgs()[0] == null; // gtx assert methodCall.getArgs()[1].equals(Fqn.fromString("/a")); // fqn assert methodCall.getArgs()[2].equals("k"); // key assert methodCall.getArgs()[3].equals("v"); // value assert methodCall.getArgs()[4] == Boolean.FALSE; //last boolean value } public static class TestInterceptor extends Interceptor { MethodCall methodCall; @Override public Object invoke(InvocationContext ctx) throws Throwable { if (ctx.isOriginLocal()) { // copy the context so tests can inspect it this.methodCall = ctx.getMethodCall(); // signal to the test that this has been invoked. interceptorInvokedLatch.countDown(); // wait for tests to finish interceptorResumeLatch.await(); // wipe class-level context variable this.methodCall = null; } // the "old-style" of passing up the interceptor chain return super.invoke(ctx); } } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/interceptors/MarshalledValueInterceptorTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/interceptors/MarshalledValueInterceptorTest.j0000644000175000017500000001431711120421506033407 0ustar moellermoellerpackage org.jboss.cache.interceptors; import org.jboss.cache.CacheSPI; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.marshall.MarshalledValueHelper; import org.jboss.cache.util.CachePrinter; import org.jboss.cache.util.TestingUtil; 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 AT jboss DOT org) * @since 2.1.0 */ @Test(groups = "functional", sequential = true, testName = "interceptors.MarshalledValueInterceptorTest") public class MarshalledValueInterceptorTest { CacheSPI c; @AfterMethod public void tearDown() { TestingUtil.killCaches(c); c = null; } public void testDefaultInterceptorStack() { c = (CacheSPI) new UnitTestCacheFactory().createCache(getClass()); assert TestingUtil.findInterceptor(c, MarshalledValueInterceptor.class) == null; TestingUtil.killCaches(c); c = (CacheSPI) new UnitTestCacheFactory().createCache(false, getClass()); 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 UnitTestCacheFactory().createCache(cfg, getClass()); assert TestingUtil.findInterceptor(c, MarshalledValueInterceptor.class) != null; } public void testDisabledInterceptorStack() { Configuration cfg = new Configuration(); cfg.setUseLazyDeserialization(false); c = (CacheSPI) new UnitTestCacheFactory().createCache(cfg, getClass()); 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-3.2.8.GA/src/test/java/org/jboss/cache/integration/0000755000175000017500000000000011675221416024715 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/hibernate/0000755000175000017500000000000011675221407026656 5ustar moellermoeller././@LongLink0000000000000000000000000000015700000000000011570 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/hibernate/HibernateIntegrationTestUtil.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/hibernate/HibernateIntegrationTes0000644000175000017500000000326411111047320033350 0ustar moellermoeller/* * 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.integration.hibernate; import org.jboss.cache.Fqn; /** * Utilities related to the Hibernate integration tests. * * @author Brian Stansberry */ public class HibernateIntegrationTestUtil { public static final Fqn 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-3.2.8.GA/src/test/java/org/jboss/cache/integration/hibernate/UpdateTimestampsCachingTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/hibernate/UpdateTimestampsCaching0000644000175000017500000001110711132625723033344 0ustar moellermoellerpackage 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.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import static org.jboss.cache.integration.hibernate.HibernateIntegrationTestUtil.*; 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.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", sequential = true, testName = "integration.hibernate.UpdateTimestampsCachingTest") 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) TestingUtil.killCaches(cache); caches = null; } private Cache createCache(boolean optimistic) { UnitTestCacheFactory cf = new UnitTestCacheFactory(); Cache cache = cf.createCache("configs/local-tx.xml", false, getClass()); 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-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/0000755000175000017500000000000011675221410027070 5ustar moellermoeller././@LongLink0000000000000000000000000000016000000000000011562 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/BuddyReplicationFailoverTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/BuddyReplicationFailov0000644000175000017500000006675311142423305033432 0ustar moellermoeller/* * 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.integration.websession; import org.jboss.cache.Cache; import org.jboss.cache.CacheManager; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.buddyreplication.BuddyReplicationTestsBase; import org.jboss.cache.commands.write.PutDataMapCommand; import org.jboss.cache.commands.write.RemoveNodeCommand; import org.jboss.cache.commands.remote.DataGravitationCleanupCommand; import org.jboss.cache.integration.websession.util.*; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.loader.FileCacheLoaderConfig; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.replicationlisteners.ReplicationListener; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * @author Brian Stansberry * * This test is disabled because of following: * There seem to be some assumptions in the above tests that do not look right to me. * I have an example I've investigated(logs etc), haven't studied all possible failure scenarios, though I can imagine some others. * One of them is the next one, on testInvalidateOnFailoverToBackup: * This is the code I am talking about: * InvalidationServlet invs = new InvalidationServlet(); * MultipleActionServlet mas = new MultipleActionServlet(sas, invs); * (....) * req = new Request(mgr0, sessionId, mas); * *1+2* req.execute(); (...) * *3*BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr0.getCache()); *And some explanation *1) manager0.invalidatesSession (i.e. cache0.remove) *2) manager0 tries to data gravitate session (i.e. cache0.get with forceDataGrav set to true). This is done last line in Request.execute(). *3) asserts that session is no longer present on cache0 *Now, the assumption at 3 is not necessarily valid. With some funny threading and using ASYNC replication, you might have the remote removeNode command originated from cache0 to run *after* gravitateData originated at step2. I.e. RemoveNodeCommand being executed after GravitateDataCommand on cache*1* (buddy of cache0). */ @Test(groups = "integration",enabled = false, testName = "integration.websession.BuddyReplicationFailoverTest") public class BuddyReplicationFailoverTest extends WebSessionTestBase { public static final String KEY = "key"; public static final String FILE_CL_ROOT_DIR = "./testFiles/BuddyReplicationFailoverTest"; private static int FOLDER_INDEX = 0; ReplicationListener[] replListeners; @Override protected String getCacheConfigName() { return "br-standard-session-cache"; } @Override protected int getNumCacheManagers() { return 4; } @Override protected boolean getCreateManagersInSetup() { return true; } @BeforeClass(alwaysRun = true) public void beforeClass() throws Exception { super.beforeClass(); replListeners = new ReplicationListener[getNumCacheManagers()]; /* Make sure that the buddy group is formed before starting the tests */ List createdCaches = new ArrayList(); for (int i = 0; i < getCacheManagers().size(); i++) { Cache cache = getCacheManagers().get(i).getCache(getCacheConfigName(), false); createdCaches.add(cache); replListeners[i] = ReplicationListener.getReplicationListener(cache); } BuddyReplicationTestsBase.waitForSingleBuddy(createdCaches); } protected void amendCacheBeforeStartup(Cache cache) { FileCacheLoaderConfig config = (FileCacheLoaderConfig) cache.getConfiguration().getCacheLoaderConfig().getFirstCacheLoaderConfig(); config.setLocation(FILE_CL_ROOT_DIR + "/session" + FOLDER_INDEX ++); } @BeforeMethod public void clearCacheLoader() throws Exception { for (CacheManager mgr: getCacheManagers()) { CacheSPI cache = (CacheSPI) mgr.getCache(getCacheConfigName(), false); CacheLoader cl = cache.getCacheLoaderManager().getCacheLoader(); cl.remove(Fqn.ROOT); } } @AfterTest public void removeFileClassLoaderDirs() { TestingUtil.recursiveFileRemove(FILE_CL_ROOT_DIR); } public void testFailoverAndImmediateInvalidate() throws Exception { int attr = 0; SessionManager mgr0 = getSessionManagers().get(0); SessionManager mgr1 = getSessionManagers().get(1); SessionManager mgr2 = getSessionManagers().get(2); SessionManager mgr3 = getSessionManagers().get(3); String contextHostName = mgr0.getContextHostName(); // Create the session SetAttributesServlet sas = new SetAttributesServlet(Collections.singletonMap(KEY, getAttributeValue(attr++))); replListeners[1].expectWithTx(PutDataMapCommand.class); Request req = new Request(mgr0, null, sas); req.execute(); replListeners[1].waitForReplicationToOccur(); String sessionId = sas.getSessionId(); assert sessionId != null : "session id is null"; // validate cache contents BuddyReplicationAssertions.assertBuddyBackup(contextHostName, sessionId, mgr0.getCache(), mgr1.getCache()); // Modify the session sas = new SetAttributesServlet(Collections.singletonMap(KEY, getAttributeValue(attr++))); replListeners[1].expectWithTx(PutDataMapCommand.class); req = new Request(mgr0, sessionId, sas); req.execute(); replListeners[1].waitForReplicationToOccur(); // Fail over; request reads the session and then modifies the session GetAttributesServlet gas = new GetAttributesServlet(Collections.singleton(KEY)); sas = new SetAttributesServlet(Collections.singletonMap(KEY, getAttributeValue(attr++))); MultipleActionServlet mas = new MultipleActionServlet(gas, sas); replListeners[0].expectWithTx(PutDataMapCommand.class); replListeners[0].expect(DataGravitationCleanupCommand.class); replListeners[1].expect(DataGravitationCleanupCommand.class); req = new Request(mgr3, sessionId, mas); req.execute(); replListeners[0].waitForReplicationToOccur(); replListeners[1].waitForReplicationToOccur(); assert sessionId.equals(mas.getSessionId()) : "wrong session id; expected " + sessionId + " got " + mas.getSessionId(); Integer integer = (Integer) gas.getReadAttributes().get(KEY); assert integer != null : "null attribute value"; assert integer.intValue() == (attr - 2) : "wrong val " + integer + " expected " + (attr -2); BuddyReplicationAssertions.assertBuddyBackup(contextHostName, sessionId, mgr3.getCache(), mgr0.getCache()); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr1.getCache()); // Invalidate the session InvalidationServlet invs = new InvalidationServlet(); replListeners[0].expectWithTx(RemoveNodeCommand.class); req = new Request(mgr3, sessionId, invs); req.execute(); replListeners[0].waitForReplicationToOccur(); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr0.getCache()); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr1.getCache()); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr2.getCache()); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr3.getCache()); } public void testFailoverToOwnerAndImmediatelyInvalidate() throws Exception { int attr = 0; SessionManager mgr0 = getSessionManagers().get(0); SessionManager mgr1 = getSessionManagers().get(1); SessionManager mgr2 = getSessionManagers().get(2); SessionManager mgr3 = getSessionManagers().get(3); String contextHostName = mgr0.getContextHostName(); // Create the session SetAttributesServlet sas = new SetAttributesServlet(Collections.singletonMap(KEY, getAttributeValue(attr++))); replListeners[2].expectWithTx(PutDataMapCommand.class); Request req = new Request(mgr1, null, sas); req.execute(); replListeners[2].waitForReplicationToOccur(); String sessionId = sas.getSessionId(); assert sessionId != null : "session id is null"; // validate cache contents BuddyReplicationAssertions.assertBuddyBackup(contextHostName, sessionId, mgr1.getCache(), mgr2.getCache()); // Modify the session sas = new SetAttributesServlet(Collections.singletonMap(KEY, getAttributeValue(attr++))); replListeners[2].expectWithTx(PutDataMapCommand.class); req = new Request(mgr1, sessionId, sas); req.execute(); replListeners[2].waitForReplicationToOccur(); // Fail over; request reads the session and then modifies the session GetAttributesServlet gas = new GetAttributesServlet(Collections.singleton(KEY)); sas = new SetAttributesServlet(Collections.singletonMap(KEY, getAttributeValue(attr++))); MultipleActionServlet mas = new MultipleActionServlet(gas, sas); replListeners[1].expectWithTx(PutDataMapCommand.class); replListeners[1].expect(DataGravitationCleanupCommand.class);//data is removed from owner replListeners[2].expect(DataGravitationCleanupCommand.class);//backup tree is removed req = new Request(mgr0, sessionId, mas); req.execute(); replListeners[1].waitForReplicationToOccur(); replListeners[2].waitForReplicationToOccur(); assert sessionId.equals(mas.getSessionId()) : "wrong session id; expected " + sessionId + " got " + mas.getSessionId(); Integer integer = (Integer) gas.getReadAttributes().get(KEY); assert integer != null : "null attribute value"; assert integer.intValue() == (attr - 2) : "wrong val " + integer + " expected " + (attr -2); BuddyReplicationAssertions.assertBuddyBackup(contextHostName, sessionId, mgr0.getCache(), mgr1.getCache()); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr2.getCache()); // Invalidate the session InvalidationServlet invs = new InvalidationServlet(); replListeners[1].expectWithTx(RemoveNodeCommand.class); req = new Request(mgr0, sessionId, invs); req.execute(); replListeners[1].waitForReplicationToOccur(); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr0.getCache()); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr1.getCache()); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr2.getCache()); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr3.getCache()); } public void testFailoverAndInvalidate() throws Exception { int attr = 0; SessionManager mgr0 = getSessionManagers().get(0); SessionManager mgr1 = getSessionManagers().get(1); SessionManager mgr2 = getSessionManagers().get(2); SessionManager mgr3 = getSessionManagers().get(3); String contextHostName = mgr0.getContextHostName(); // Create the session SetAttributesServlet sas = new SetAttributesServlet(Collections.singletonMap(KEY, getAttributeValue(attr++))); replListeners[1].expectWithTx(PutDataMapCommand.class); Request req = new Request(mgr0, null, sas); req.execute(); replListeners[1].waitForReplicationToOccur(); String sessionId = sas.getSessionId(); assert sessionId != null : "session id is null"; // validate cache contents BuddyReplicationAssertions.assertBuddyBackup(contextHostName, sessionId, mgr0.getCache(), mgr1.getCache()); // Modify the session sas = new SetAttributesServlet(Collections.singletonMap(KEY, getAttributeValue(attr++))); replListeners[1].expectWithTx(PutDataMapCommand.class); req = new Request(mgr0, sessionId, sas); req.execute(); replListeners[1].waitForReplicationToOccur(); // Fail over; request reads the session and then modifies the session GetAttributesServlet gas = new GetAttributesServlet(Collections.singleton(KEY)); sas = new SetAttributesServlet(Collections.singletonMap(KEY, getAttributeValue(attr++))); MultipleActionServlet mas = new MultipleActionServlet(gas, sas); replListeners[0].expectWithTx(PutDataMapCommand.class); replListeners[0].expect(DataGravitationCleanupCommand.class); replListeners[1].expect(DataGravitationCleanupCommand.class); req = new Request(mgr3, sessionId, mas); req.execute(); replListeners[0].waitForReplicationToOccur(); replListeners[1].waitForReplicationToOccur(); assert sessionId.equals(mas.getSessionId()) : "wrong session id; expected " + sessionId + " got " + mas.getSessionId(); Integer integer = (Integer) gas.getReadAttributes().get(KEY); assert integer != null : "null attribute value"; assert integer.intValue() == (attr - 2) : "wrong val " + integer + " expected " + (attr -2); BuddyReplicationAssertions.assertBuddyBackup(contextHostName, sessionId, mgr3.getCache(), mgr0.getCache()); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr1.getCache()); // Modify the session again sas = new SetAttributesServlet(Collections.singletonMap(KEY, getAttributeValue(attr++))); replListeners[0].expectWithTx(PutDataMapCommand.class); req = new Request(mgr3, sessionId, sas); req.execute(); replListeners[0].waitForReplicationToOccur(); // Invalidate the session InvalidationServlet invs = new InvalidationServlet(); replListeners[0].expectWithTx(RemoveNodeCommand.class); req = new Request(mgr3, sessionId, invs); req.execute(); replListeners[0].waitForReplicationToOccur(); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr0.getCache()); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr1.getCache()); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr2.getCache()); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr3.getCache()); } public void testFailoverToOwnerAndInvalidate() throws Exception { int attr = 0; SessionManager mgr0 = getSessionManagers().get(0); SessionManager mgr1 = getSessionManagers().get(1); SessionManager mgr2 = getSessionManagers().get(2); SessionManager mgr3 = getSessionManagers().get(3); String contextHostName = mgr0.getContextHostName(); // Create the session SetAttributesServlet sas = new SetAttributesServlet(Collections.singletonMap(KEY, getAttributeValue(attr++))); replListeners[2].expectWithTx(PutDataMapCommand.class); Request req = new Request(mgr1, null, sas); req.execute(); replListeners[2].waitForReplicationToOccur(); String sessionId = sas.getSessionId(); assert sessionId != null : "session id is null"; // validate cache contents BuddyReplicationAssertions.assertBuddyBackup(contextHostName, sessionId, mgr1.getCache(), mgr2.getCache()); // Modify the session sas = new SetAttributesServlet(Collections.singletonMap(KEY, getAttributeValue(attr++))); replListeners[2].expectWithTx(PutDataMapCommand.class); req = new Request(mgr1, sessionId, sas); req.execute(); replListeners[2].waitForReplicationToOccur(); // Fail over; request reads the session and then modifies the session GetAttributesServlet gas = new GetAttributesServlet(Collections.singleton(KEY)); sas = new SetAttributesServlet(Collections.singletonMap(KEY, getAttributeValue(attr++))); MultipleActionServlet mas = new MultipleActionServlet(gas, sas); replListeners[1].expectWithTx(PutDataMapCommand.class); replListeners[2].expect(DataGravitationCleanupCommand.class); req = new Request(mgr0, sessionId, mas); req.execute(); replListeners[1].waitForReplicationToOccur(); replListeners[2].waitForReplicationToOccur(); assert sessionId.equals(mas.getSessionId()) : "wrong session id; expected " + sessionId + " got " + mas.getSessionId(); Integer integer = (Integer) gas.getReadAttributes().get(KEY); assert integer != null : "null attribute value"; assert integer.intValue() == (attr - 2) : "wrong val " + integer + " expected " + (attr -2); BuddyReplicationAssertions.assertBuddyBackup(contextHostName, sessionId, mgr0.getCache(), mgr1.getCache()); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr2.getCache()); // Modify the session again sas = new SetAttributesServlet(Collections.singletonMap(KEY, getAttributeValue(attr++))); replListeners[1].expectWithTx(PutDataMapCommand.class); req = new Request(mgr0, sessionId, sas); req.execute(); replListeners[1].waitForReplicationToOccur(); // Invalidate the session InvalidationServlet invs = new InvalidationServlet(); replListeners[1].expectWithTx(RemoveNodeCommand.class); req = new Request(mgr0, sessionId, invs); req.execute(); replListeners[1].waitForReplicationToOccur(); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr0.getCache()); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr1.getCache()); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr2.getCache()); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr3.getCache()); } public void testFailoverAndFailBack() throws Exception { int attr = 0; SessionManager mgr0 = getSessionManagers().get(0); SessionManager mgr1 = getSessionManagers().get(1); SessionManager mgr2 = getSessionManagers().get(2); SessionManager mgr3 = getSessionManagers().get(3); String contextHostName = mgr0.getContextHostName(); // Create the session SetAttributesServlet sas = new SetAttributesServlet(Collections.singletonMap(KEY, getAttributeValue(attr++))); replListeners[1].expectWithTx(PutDataMapCommand.class); Request req = new Request(mgr0, null, sas); req.execute(); replListeners[1].waitForReplicationToOccur(); System.out.println("First put on 0, attr=" + attr); TestingUtil.dumpCacheContents(mgr0.getCache(), mgr1.getCache(), mgr2.getCache(), mgr3.getCache()); String sessionId = sas.getSessionId(); assert sessionId != null : "session id is null"; // validate cache contents BuddyReplicationAssertions.assertBuddyBackup(contextHostName, sessionId, mgr0.getCache(), mgr1.getCache()); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr2.getCache()); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr3.getCache()); // Modify the session sas = new SetAttributesServlet(Collections.singletonMap(KEY, getAttributeValue(attr++))); replListeners[1].expectWithTx(PutDataMapCommand.class); req = new Request(mgr0, sessionId, sas); req.execute(); replListeners[1].waitForReplicationToOccur(); System.out.println("Second put on 0, attr=" + attr); TestingUtil.dumpCacheContents(mgr0.getCache(), mgr1.getCache(), mgr2.getCache(), mgr3.getCache()); // Fail over; request reads the session and then modifies the session GetAttributesServlet gas = new GetAttributesServlet(Collections.singleton(KEY)); sas = new SetAttributesServlet(Collections.singletonMap(KEY, getAttributeValue(attr++))); MultipleActionServlet mas = new MultipleActionServlet(gas, sas); replListeners[0].expectWithTx(PutDataMapCommand.class); replListeners[1].expect(DataGravitationCleanupCommand.class); req = new Request(mgr3, sessionId, mas); req.execute(); replListeners[0].waitForReplicationToOccur(); replListeners[1].waitForReplicationToOccur(); System.out.println("First put on 3, attr=" + attr); TestingUtil.dumpCacheContents(mgr0.getCache(), mgr1.getCache(), mgr2.getCache(), mgr3.getCache()); assert sessionId.equals(mas.getSessionId()) : "wrong session id; expected " + sessionId + " got " + mas.getSessionId(); Integer integer = (Integer) gas.getReadAttributes().get(KEY); assert integer != null : "null attribute value"; assert integer.intValue() == (attr - 2) : "wrong val " + integer + " expected " + (attr -2); BuddyReplicationAssertions.assertBuddyBackup(contextHostName, sessionId, mgr3.getCache(), mgr0.getCache()); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr1.getCache()); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr2.getCache()); // Modify the session again sas = new SetAttributesServlet(Collections.singletonMap(KEY, getAttributeValue(attr++))); replListeners[0].expectWithTx(PutDataMapCommand.class); req = new Request(mgr3, sessionId, sas); req.execute(); replListeners[0].waitForReplicationToOccur(); // Fail back; request reads the session and then modifies the session gas = new GetAttributesServlet(Collections.singleton(KEY)); sas = new SetAttributesServlet(Collections.singletonMap(KEY, getAttributeValue(attr++))); mas = new MultipleActionServlet(gas, sas); replListeners[1].expectWithTx(PutDataMapCommand.class); replListeners[3].expect(DataGravitationCleanupCommand.class); req = new Request(mgr0, sessionId, mas); req.execute(); replListeners[1].waitForReplicationToOccur(); replListeners[3].waitForReplicationToOccur(); assert sessionId.equals(mas.getSessionId()) : "wrong session id; expected " + sessionId + " got " + mas.getSessionId(); integer = (Integer) gas.getReadAttributes().get(KEY); assert integer != null : "null attribute value"; assert integer.intValue() == attr - 2 : "wrong val " + integer + " expected " + (attr -2); BuddyReplicationAssertions.assertBuddyBackup(contextHostName, sessionId, mgr0.getCache(), mgr1.getCache()); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr3.getCache()); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr2.getCache()); // Invalidate the session InvalidationServlet invs = new InvalidationServlet(); replListeners[1].expectWithTx(RemoveNodeCommand.class); req = new Request(mgr0, sessionId, invs); req.execute(); replListeners[1].waitForReplicationToOccur(); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr0.getCache()); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr1.getCache()); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr2.getCache()); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr3.getCache()); gas = new GetAttributesServlet(Collections.singleton(KEY)); req = new Request(mgr0, sessionId, gas); assert gas.getReadAttributes().get(KEY) == null : "session not cleaned up"; } public void testInvalidateOnFailoverToBackup() throws Exception { int attr = 0; SessionManager mgr0 = getSessionManagers().get(0); SessionManager mgr1 = getSessionManagers().get(1); SessionManager mgr2 = getSessionManagers().get(2); SessionManager mgr3 = getSessionManagers().get(3); String contextHostName = mgr0.getContextHostName(); // Create the session SetAttributesServlet sas = new SetAttributesServlet(Collections.singletonMap(KEY, getAttributeValue(attr++))); ReplicationListener replListener0 = replListeners[0]; replListener0.expectWithTx(PutDataMapCommand.class); Request req = new Request(mgr3, null, sas); req.execute(); replListener0.waitForReplicationToOccur(); String sessionId = sas.getSessionId(); assert sessionId != null : "session id is null"; // validate cache contents BuddyReplicationAssertions.assertBuddyBackup(contextHostName, sessionId, mgr3.getCache(), mgr0.getCache()); // Modify the session sas = new SetAttributesServlet(Collections.singletonMap(KEY, getAttributeValue(attr++))); replListener0.expectWithTx(PutDataMapCommand.class); req = new Request(mgr3, sessionId, sas); req.execute(); replListener0.waitForReplicationToOccur(); // Passivate the session mgr0.passivate(sessionId); mgr1.passivate(sessionId); mgr2.passivate(sessionId); mgr3.passivate(sessionId); // Reactivate the session sas = new SetAttributesServlet(Collections.singletonMap(KEY, getAttributeValue(attr++))); replListener0.expectWithTx(PutDataMapCommand.class); req = new Request(mgr3, sessionId, sas); req.execute(); replListener0.waitForReplicationToOccur(); // Invalidate the session sas = new SetAttributesServlet(Collections.singletonMap(KEY, getAttributeValue(attr++))); InvalidationServlet invs = new InvalidationServlet(); MultipleActionServlet mas = new MultipleActionServlet(sas, invs); ReplicationListener replListener1 = replListeners[1]; replListener1.expectWithTx(PutDataMapCommand.class, RemoveNodeCommand.class); replListener1.expect(DataGravitationCleanupCommand.class); req = new Request(mgr0, sessionId, mas); req.execute(); replListener1.waitForReplicationToOccur(); TestingUtil.dumpCacheContents(mgr0.getCache(), mgr1.getCache(), mgr2.getCache(), mgr3.getCache()); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr0.getCache()); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr1.getCache()); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr2.getCache()); BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr3.getCache()); GetAttributesServlet gas = new GetAttributesServlet(Collections.singleton(KEY)); req = new Request(mgr0, sessionId, gas); assert gas.getReadAttributes().get(KEY) == null : "session not cleaned up"; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/util/0000755000175000017500000000000011675221415030052 5ustar moellermoeller././@LongLink0000000000000000000000000000015500000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/util/DirtyMetadataServlet.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/util/DirtyMetadataServ0000644000175000017500000000250311112450520033354 0ustar moellermoeller/* * 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.integration.websession.util; /** * A Servlet the marks the session metadata dirty. * * @author Brian Stansberry */ public class DirtyMetadataServlet extends AbstractServlet { public void handleRequest(Request request) { extractSession(request).setMetadataDirty(); } } ././@LongLink0000000000000000000000000000015500000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/util/SetAttributesServlet.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/util/SetAttributesServ0000644000175000017500000000325611112450520033430 0ustar moellermoeller/* * 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.integration.websession.util; import java.util.HashMap; import java.util.Map; /** * A Servlet that sets attributes on the session. * * @author Brian Stansberry */ public class SetAttributesServlet extends AbstractServlet { private final Map toSet; public SetAttributesServlet(Map toSet) { this.toSet = new HashMap(toSet); } public void handleRequest(Request request) { Session session = extractSession(request); for (Map.Entry entry : toSet.entrySet()) { session.setAttribute(entry.getKey(), entry.getValue()); } } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/util/AbstractServlet.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/util/AbstractServlet.j0000644000175000017500000000322711112450520033324 0ustar moellermoeller/* * 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.integration.websession.util; /** * Basic Servlet impl. Caches a reference to the session. * @author Brian Stansberry * */ public abstract class AbstractServlet implements Servlet { private Session session; protected Session extractSession(Request request) { if (session == null) { session = request.getSession(true); } return session; } protected void clearSession() { session = null; } public Session getSession() { return session; } public String getSessionId() { return session == null ? null : session.getId(); } } ././@LongLink0000000000000000000000000000016000000000000011562 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/util/RemoveAttributesServlet.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/util/RemoveAttributesS0000644000175000017500000000316611112450520033415 0ustar moellermoeller/* * 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.integration.websession.util; import java.util.HashSet; import java.util.Set; /** * A Servlet that removes attributes from the session. * * @author Brian Stansberry */ public class RemoveAttributesServlet extends AbstractServlet { private final Set toRemove; public RemoveAttributesServlet(Set toRemove) { this.toRemove = new HashSet(toRemove); } public void handleRequest(Request request) { Session session = extractSession(request); for (String key : toRemove) { session.removeAttribute(key); } } } ././@LongLink0000000000000000000000000000015600000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/util/SessionManagerSupport.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/util/SessionManagerSup0000644000175000017500000000446311131166252033404 0ustar moellermoeller/* * 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.integration.websession.util; import java.util.ArrayList; import java.util.List; import org.jboss.cache.CacheManager; /** * @author Brian Stansberry * */ public class SessionManagerSupport { /** * For each thread holds list of session managers created using this factory. */ private static final ThreadLocal> threadSessionManagers = new ThreadLocal>() { @Override protected List initialValue() { return new ArrayList(); } }; public static List createSessionManagers(List cacheManagers, WebAppMetadata metadata) { List existing = threadSessionManagers.get(); existing.clear(); for (CacheManager cm : cacheManagers) { SessionManager sm = new SessionManager(cm, metadata); existing.add(sm); } return new ArrayList(existing); } public static void tearDown() throws Exception { List existing = threadSessionManagers.get(); for (SessionManager sm : existing) { if (sm.isStarted()) sm.stop(); } existing.clear(); } } ././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/util/WebSessionTestBase.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/util/WebSessionTestBas0000644000175000017500000001251711142423305033341 0ustar moellermoeller/* * 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.integration.websession.util; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import org.jboss.cache.Cache; import org.jboss.cache.CacheManager; import org.jboss.cache.CacheStatus; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.integration.CacheManagerSupport; import org.jboss.cache.integration.UnitTestCacheFactoryConfigurationRegistry; import org.jboss.cache.integration.websession.util.WebAppMetadata.Granularity; import org.jboss.cache.util.internals.replicationlisteners.ReplicationListener; import org.testng.annotations.AfterClass; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; /** * Base class that handles standard before/after class/method stuff. * * @author Brian Stansberry */ public abstract class WebSessionTestBase { public static final String DEFAULT_CACHE_CONFIG_FILE_NAME = "configs/integration/web-session-cache-configs.xml"; private AtomicInteger testCount = new AtomicInteger(); private List cacheManagers; private List sessionManagers; protected ReplicationListener[] replListeners; @BeforeClass(alwaysRun = true) public void beforeClass() throws Exception { cacheManagers = CacheManagerSupport.createCacheManagers(getNumCacheManagers(), getCacheConfigFileName(), getStacksXmlFileName()); if (getStartCachesInBeforeClass() && getCacheConfigName() != null) { String inUseProtocolStack = UnitTestConfigurationFactory.getEmptyConfiguration().getClusterConfig(); replListeners = new ReplicationListener[getNumCacheManagers()]; for (int i =0; i < cacheManagers.size(); i++) { CacheManager cm = cacheManagers.get(i); Cache cache = cm.getCache(getCacheConfigName(), true); amendCacheBeforeStartup(cache); if (cache.getCacheStatus() != CacheStatus.STARTED) { cache.getConfiguration().setClusterConfig(inUseProtocolStack); cache.start(); } replListeners[i] = ReplicationListener.getReplicationListener(cache); } } } protected void amendCacheBeforeStartup(Cache cache) { //do nothing } @AfterClass(alwaysRun = true) public void afterClass() { CacheManagerSupport.tearDown(); } @BeforeMethod(alwaysRun = true) public void setup() { testCount.incrementAndGet(); if (getCreateManagersInSetup() && getWebAppMetaData() != null) { sessionManagers = SessionManagerSupport.createSessionManagers(cacheManagers, getWebAppMetaData()); for (SessionManager mgr : sessionManagers) { mgr.start(); } } } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { SessionManagerSupport.tearDown(); } protected abstract int getNumCacheManagers(); protected String getCacheConfigFileName() { return DEFAULT_CACHE_CONFIG_FILE_NAME; } protected String getStacksXmlFileName() { return UnitTestCacheFactoryConfigurationRegistry.DEFAULT_STACKS_XML_RESOURCE; } protected boolean getStartCachesInBeforeClass() { return true; } protected abstract String getCacheConfigName(); protected boolean getCreateManagersInSetup() { return false; } protected boolean getUsePassivation() { return true; } protected Granularity getGranularity() { return Granularity.SESSION; } protected WebAppMetadata getWebAppMetaData() { return new WebAppMetadata(getWarNameForTest(), getCacheConfigName(), getUsePassivation(), getGranularity()); } protected List getCacheManagers() { return cacheManagers; } protected List getSessionManagers() { return sessionManagers; } protected int getTestCount() { return testCount.get(); } protected String getWarNameForTest() { return getClass().getSimpleName() + getTestCount(); } protected Object getAttributeValue(int value) { return Integer.valueOf(value); } } ././@LongLink0000000000000000000000000000016300000000000011565 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/util/BuddyReplicationAssertions.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/util/BuddyReplicationA0000644000175000017500000001061311132261276033335 0ustar moellermoeller/* * 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.integration.websession.util; import java.util.Map; import org.jboss.cache.Cache; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.CacheSPI; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.loader.CacheLoaderManager; import org.jboss.cache.buddyreplication.BuddyFqnTransformer; import org.jboss.cache.buddyreplication.BuddyManager; /** * @author Brian Stansberry * */ public class BuddyReplicationAssertions { public static void assertBuddyBackup(String contextHostName, String sessionId, Cache owner, Cache backup) throws Exception { Fqn fqn = Fqn.fromElements(FqnUtil.JSESSION, contextHostName, sessionId); Map owned = owner.getData(fqn); assert owned != null : "owned is null"; Fqn bFqn = new BuddyFqnTransformer().getBackupFqn(owner.getLocalAddress(), fqn); Map backed = owner.getData(fqn); assert backed != null : "backed is null"; assert owned.size() == backed.size() : "sizes differ; owned = " + owned.size() + " backed = " + backed.size(); for (Map.Entry entry : owned.entrySet()) { Object backVal = backed.get(entry.getKey()); assert backVal != null : "null backVal for " + entry.getKey(); assert backVal.equals(entry.getValue()) : "differing val for " + entry.getKey() + " " + entry.getValue() + " vs. " + backVal; } assertMainTreeClear(contextHostName, sessionId, backup); assertBuddyTreeClear(contextHostName, sessionId, owner); } public static void assertUnrelated(String contextHostName, String sessionId, Cache cache) throws Exception { assertMainTreeClear(contextHostName, sessionId, cache); assertBuddyTreeClear(contextHostName, sessionId, cache); } public static void assertMainTreeClear(String contextHostName, String sessionId, Cache cache) throws Exception { Fqn fqn = Fqn.fromElements(FqnUtil.JSESSION, contextHostName, sessionId); verifyCacheLoader(cache, fqn); assert cache.getNode(fqn) == null : "found node for " + fqn + " on cache instance " + cache.getLocalAddress(); } public static void assertBuddyTreeClear(String contextHostName, String sessionId, Cache cache) throws Exception { Fqn fqn = Fqn.fromElements(FqnUtil.JSESSION, contextHostName, sessionId); verifyCacheLoader(cache, fqn); Node bbRoot = cache.getNode(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN); if (bbRoot != null) { for(Node child : bbRoot.getChildren()) { Node bad = child.getChild(fqn); assert bad == null : "found bad node at " + Fqn.fromRelativeFqn(child.getFqn(), fqn); } } } private static void verifyCacheLoader(Cache cache, Fqn fqn) throws Exception { CacheLoaderManager loaderManager = ((CacheSPI) cache).getCacheLoaderManager(); if (loaderManager != null && loaderManager.getCacheLoader() != null) { CacheLoader cl = loaderManager.getCacheLoader(); assert !cl.exists(fqn) : "found node for " + fqn + " on cache loader of cache " + cache.getLocalAddress(); } } private BuddyReplicationAssertions() {} } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/util/SessionManager.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/util/SessionManager.ja0000644000175000017500000004316511142423305033304 0ustar moellermoeller/* * 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.integration.websession.util; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import javax.transaction.SystemException; import javax.transaction.TransactionManager; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.*; import org.jboss.cache.loader.CacheLoaderManager; import org.jboss.cache.buddyreplication.BuddyManager; import org.jboss.cache.config.BuddyReplicationConfig; import org.jboss.cache.integration.websession.util.WebAppMetadata.Granularity; 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.NodeModifiedEvent; import org.jboss.cache.notifications.event.NodeRemovedEvent; /** * Mock version of a JBoss AS web session manager. * * @author Brian Stansberry */ @CacheListener public class SessionManager { private static final Integer VERSION = Integer.valueOf(0); private static final Integer TIMESTAMP = Integer.valueOf(1); private static final Integer METADATA = Integer.valueOf(2); private static final Integer ATTRIBUTES = Integer.valueOf(3); private final CacheManager cacheManager; private final WebAppMetadata appMetadata; private final String contextHostName; private final Fqn baseFqn; private final Map sessions = new ConcurrentHashMap(); private Cache cache; private TransactionManager tm; private boolean buddyReplication; private boolean started; private final boolean fieldBased; private final boolean attributeBased; private final Log log = LogFactory.getLog(getClass()); // ------------------------------------------------------------ Constructors public SessionManager(CacheManager cacheManager, WebAppMetadata metadata) { this.cacheManager = cacheManager; this.appMetadata = metadata; this.contextHostName = appMetadata.warName + "_localhost"; this.baseFqn = Fqn.fromElements(FqnUtil.JSESSION, contextHostName); this.fieldBased = appMetadata.granularity == Granularity.FIELD; this.attributeBased = appMetadata.granularity == Granularity.ATTRIBUTE; } // --------------------------------------------------------- Test Driver API public boolean isStarted() { return started; } public void start() { this.started = true; try { this.cache = cacheManager.getCache(appMetadata.cacheConfigName, false); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } cache.addCacheListener(this); if (cache.getCacheStatus() != CacheStatus.STARTED) { cache.start(); } this.tm = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); if (tm == null) { throw new IllegalStateException("tm is null"); } BuddyReplicationConfig brc = cache.getConfiguration().getBuddyReplicationConfig(); this.buddyReplication = brc != null && brc.isEnabled(); } public void stop() throws Exception { if (started) { if (cache != null) { cache.removeCacheListener(this); // FIXME see if we need more sophisticated cache cleanup removeInMemoryData((CacheSPI)cache); clearCacheLoader((CacheSPI)cache); cacheManager.releaseCache(appMetadata.cacheConfigName); cache = null; } } } private void clearCacheLoader(CacheSPI cache) { CacheLoaderManager cacheLoaderManager = cache.getCacheLoaderManager(); if (cacheLoaderManager != null && cacheLoaderManager.getCacheLoader() != null) { try { cacheLoaderManager.getCacheLoader().remove(Fqn.ROOT); } catch (Exception e) { throw new RuntimeException(e); } } } private void removeInMemoryData(CacheSPI cache) { if (cache.getRoot() != null) { cache.getRoot().clearDataDirect(); cache.getRoot().removeChildrenDirect(); } } /** * Allows test driver to mock Tomcat background processes' expiration * of an overage session. * * @param id the session id */ public void expireSession(String id) { Session session = removeSession(id, true, true); if (buddyReplication) { cleanBuddyBackupTree(id, false); } if (session != null) session.getMetadata().valid = false; } /** * Allows test driver to mock Tomcat background processes' passivation * of a session. * * @param id the session id */ public void passivate(String id) { if (!appMetadata.passivation) throw new IllegalStateException("passivation not supported"); sessions.remove(id); cache.evict(getSessionFqn(id), true); if (buddyReplication) { cleanBuddyBackupTree(id, true); } } public Cache getCache() { return cache; } public String getContextHostName() { return contextHostName; } // ----------------------------------------------------------- CacheListener @NodeRemoved public void nodeRemoved(NodeRemovedEvent event) { if (event.isPre()) return; boolean local = event.isOriginLocal(); if (!fieldBased && local) return; @SuppressWarnings("unchecked") Fqn fqn = event.getFqn(); boolean isBuddy = FqnUtil.isBuddyFqn(fqn); if (!local && FqnUtil.isFqnSessionRootSized(fqn, isBuddy) && FqnUtil.isFqnForOurWebapp(fqn, contextHostName, isBuddy)) { // A session has been invalidated from another node; // need to inform manager String sessId = FqnUtil.getIdFromFqn(fqn, isBuddy); removeSession(sessId, true, false); } else if (local && !isBuddy && FqnUtil.isPossibleInternalPojoFqn(fqn) && FqnUtil.isFqnForOurWebapp(fqn, contextHostName, isBuddy)) { // One of our sessions' pojos is modified; need to inform // the manager so it can mark the session dirty String sessId = FqnUtil.getIdFromFqn(fqn, isBuddy); notifyLocalAttributeModification(sessId); } } @NodeModified public void nodeModified(NodeModifiedEvent event) { if (event.isPre()) return; boolean local = event.isOriginLocal(); if (!fieldBased && local) return; @SuppressWarnings("unchecked") Fqn fqn = event.getFqn(); boolean isBuddy = FqnUtil.isBuddyFqn(fqn); if (!local && FqnUtil.isFqnSessionRootSized(fqn, isBuddy) && FqnUtil.isFqnForOurWebapp(fqn, contextHostName, isBuddy)) { // Query if we have version value in the distributed cache. // If we have a version value, compare the version and invalidate if necessary. @SuppressWarnings("unchecked") Map data = event.getData(); AtomicInteger version = (AtomicInteger) data.get(VERSION); if(version != null) { String realId = FqnUtil.getIdFromFqn(fqn, isBuddy); String owner = isBuddy ? FqnUtil.getBuddyOwner(fqn) : null; AtomicLong timestamp = (AtomicLong) data.get(TIMESTAMP); if (timestamp == null) { log.warn("No timestamp attribute found in " + fqn); } else { // Notify the manager that a session has been updated boolean updated = sessionChangedInDistributedCache(realId, owner, version.get()); if (!updated && !isBuddy) { log.warn("Possible concurrency problem: Replicated version id " + version + " is less than or equal to in-memory version for session " + realId); } /*else { We have a local session but got a modification for the buddy tree. This means another node is in the process of taking over the session; we don't worry about it } */ } } else if (!attributeBased) // other granularities can modify attributes only { log.warn("No version attribute found in " + fqn); } } else if (local && !isBuddy && FqnUtil.isPossibleInternalPojoFqn(fqn) && FqnUtil.isFqnForOurWebapp(fqn, contextHostName, isBuddy)) { // One of our sessions' pojos is modified; need to inform // the manager so it can mark the session dirty String sessId = FqnUtil.getIdFromFqn(fqn, isBuddy); notifyLocalAttributeModification(sessId); } } // ------------------------------------------------------------ Internal API protected boolean isBatchStarted() { try { return tm.getTransaction() != null; } catch (SystemException e) { throw new RuntimeException("failed checking for tx", e); } } protected void startBatch() { try { tm.begin(); } catch (Exception e) { throw new RuntimeException("failed starting tx", e); } } protected void endBatch() { try { tm.commit(); } catch (Exception e) { throw new RuntimeException("failed committing tx", e); } } protected Session findSession(String id) { Session session = sessions.get(id); if (session == null || session.isOutdated()) { session = loadSession(id); if (session != null) { sessions.put(id, session); } } return session; } protected Session createSession() { Session session = createEmptySession(); sessions.put(session.getId(), session); return session; } protected Session removeSession(String id, boolean localOnly, boolean localCall) { Session session = sessions.remove(id); if (localCall) { // TODO mock the bit where each individual attribute is removed first // Remove the session node if (localOnly) { cache.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); } log("cache.removeNode(" + getSessionFqn(id) + ") locally? " + localOnly); cache.removeNode(getSessionFqn(id)); } return session; } protected Granularity getGranularity() { return appMetadata.granularity; } @SuppressWarnings("unchecked") protected Map loadSessionAttributes(String id, Map sessionNodeData) { Map result = new HashMap(); switch (appMetadata.granularity) { case SESSION: result.putAll((Map) sessionNodeData.get(ATTRIBUTES)); break; case ATTRIBUTE: for (Map.Entry entry : sessionNodeData.entrySet()) { if (entry.getKey() instanceof String) { result.put((String) entry.getKey(), entry.getValue()); } } break; case FIELD: throw new IllegalStateException("implement"); } return result; } /** * JBC write for granularity SESSION. */ protected void storeSession(String id, AtomicInteger version, AtomicLong timestamp, SessionMetadata metadata, Map attributes) { if (version == null) throw new IllegalArgumentException("version is null"); if (timestamp == null) throw new IllegalArgumentException("timestamp is null"); Fqn fqn = getSessionFqn(id); Map data = new HashMap(); data.put(VERSION, version); data.put(TIMESTAMP, timestamp); if (metadata != null) { data.put(METADATA, metadata); } if (attributes != null) { data.put(ATTRIBUTES, attributes); } log("cache.put(" + fqn +"," + data +")"); cache.put(fqn, data); } /** * JBC write for granularity ATTRIBUTE. */ protected void storeSession(String id, AtomicInteger version, AtomicLong timestamp, SessionMetadata metadata, Map modifiedAttributes, Set removedAttributes) { storeSession(id, version, timestamp, metadata, null); Fqn fqn = getSessionFqn(id); if (modifiedAttributes != null) { log("cache.put(" + fqn+"," + modifiedAttributes+ ")"); cache.put(fqn, modifiedAttributes); } if (removedAttributes != null) { for (String key : removedAttributes) { log("cache.remove(" + fqn + "," + key + ")"); cache.remove(fqn, key); } } } // ----------------------------------------------------------------- Private private Fqn getSessionFqn(String id) { return Fqn.fromRelativeElements(baseFqn, id); } private Session createEmptySession() { Session session = null; switch (appMetadata.granularity) { case SESSION: case ATTRIBUTE: session = new Session(this); break; case FIELD: throw new IllegalStateException("implement"); } return session; } /** * JBC read of a session. */ private Session loadSession(String id) { Session session = null; boolean startTx = !isBatchStarted(); if (startTx) startBatch(); Map data = null; try { if (buddyReplication) { cache.getInvocationContext().getOptionOverrides().setForceDataGravitation(true); } log ("cache.getData(" + getSessionFqn(id) + ") with ForceDataGravitation(true)"); data = cache.getData(getSessionFqn(id)); } finally { if (startTx) endBatch(); } if (data != null) { session = createEmptySession(); AtomicInteger version = (AtomicInteger) data.get(VERSION); AtomicLong timestamp = (AtomicLong) data.get(TIMESTAMP); SessionMetadata metadata = (SessionMetadata) data.get(METADATA); Map attributes = loadSessionAttributes(id, data); session.update(version, timestamp, metadata, attributes); } return session; } private boolean sessionChangedInDistributedCache(String realId, String owner, int version) { Session session = sessions.get(realId); if (session != null) { session.setOutdated(true); if (session.getVersion().get() >= version) return false; } return true; } private void notifyLocalAttributeModification(String sessId) { Session session = sessions.get(sessId); if (session != null) { session.setOutdated(true); } } private void cleanBuddyBackupTree(String id, boolean evict) { Fqn mainFqn = getSessionFqn(id); Node root = cache.getNode(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN); if (root != null) { Set> children = root.getChildren(); for (Node child : children) { @SuppressWarnings("unchecked") Fqn backupFqn = Fqn.fromRelativeFqn(child.getFqn(), mainFqn); if (evict) { log("cache.evict(" + backupFqn + ", true)"); cache.evict(backupFqn, true); } else { cache.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache.removeNode(backupFqn); } } } } private void log(String what) { System.out.println("[" + cache.getLocalAddress() + "] " + what); } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/util/WebAppMetadata.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/util/WebAppMetadata.ja0000644000175000017500000000361311112450520033173 0ustar moellermoeller/* * 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.integration.websession.util; /** * Metadata about a webapp; mocks the object created from a jboss-web.xml. * * @author Brian Stansberry */ public class WebAppMetadata { public static final String DEFAULT_CACHE_CONFIG = "standard-session-cache"; public enum Granularity { SESSION, ATTRIBUTE, FIELD }; public final String warName; public final String cacheConfigName; public final boolean passivation; public final Granularity granularity; public WebAppMetadata(String warName) { this(warName, DEFAULT_CACHE_CONFIG, true, Granularity.SESSION); } public WebAppMetadata(String warName, String cacheConfigName, boolean passivation, Granularity granularity) { this.warName = warName; this.cacheConfigName = cacheConfigName; this.passivation = passivation; this.granularity = granularity; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/util/FqnUtil.java0000644000175000017500000001201111112450520032255 0ustar moellermoeller/* * 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.integration.websession.util; import org.jboss.cache.Fqn; import org.jboss.cache.buddyreplication.BuddyManager; /** * @author Brian Stansberry * */ public class FqnUtil { public static final String JSESSION = "JSESSION"; public static final String ATTRIBUTE = "ATTRIBUTE"; private static final int JSESSION_FQN_INDEX = 0; private static final int WEBAPP_FQN_INDEX = 1; private static final int SESSION_ID_FQN_INDEX = 2; private static final int SESSION_FQN_SIZE = SESSION_ID_FQN_INDEX + 1; private static final int BUDDY_BACKUP_ROOT_OWNER_INDEX = BuddyManager.BUDDY_BACKUP_SUBTREE_FQN.size(); private static final int BUDDY_BACKUP_ROOT_OWNER_SIZE = BUDDY_BACKUP_ROOT_OWNER_INDEX + 1; // Element within an FQN that is the root of a Pojo attribute map private static final int POJO_ATTRIBUTE_FQN_INDEX = SESSION_ID_FQN_INDEX + 1; // Element within an FQN that is the root of an individual Pojo attribute private static final int POJO_KEY_FQN_INDEX = POJO_ATTRIBUTE_FQN_INDEX + 1; // Element within an FQN that is the root of a session's internal pojo storage area private static final int POJO_INTERNAL_FQN_INDEX = SESSION_ID_FQN_INDEX + 1; // Minimum size of an FQN that is below the root of a session's internal pojo storage area private static final int POJO_INTERNAL_FQN_SIZE = POJO_INTERNAL_FQN_INDEX + 1; public static boolean isBuddyFqn(Fqn fqn) { try { return BuddyManager.BUDDY_BACKUP_SUBTREE.equals(fqn.get(0)); } catch (IndexOutOfBoundsException e) { // Can only happen if fqn is ROOT, and we shouldn't get // notifications for ROOT. // If it does, just means it's not a buddy return false; } } public static boolean isFqnSessionRootSized(Fqn fqn, boolean isBuddy) { return fqn.size() == (isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + SESSION_FQN_SIZE : SESSION_FQN_SIZE); } public static String getPojoKeyFromFqn(Fqn fqn, boolean isBuddy) { return (String) fqn.get(isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + POJO_KEY_FQN_INDEX: POJO_KEY_FQN_INDEX); } /** * Check if the fqn is big enough to be in the internal pojo area but * isn't in the regular attribute area. * * Structure in the cache is: * * /JSESSION * ++ /contextPath_hostname * ++++ /sessionid * ++++++ /ATTRIBUTE * ++++++ /_JBossInternal_ * ++++++++ etc etc * * If the Fqn size is big enough to be "etc etc" or lower, but the 4th * level is not "ATTRIBUTE", it must be under _JBossInternal_. We discriminate * based on != ATTRIBUTE to avoid having to code to the internal PojoCache * _JBossInternal_ name. * * @param fqn * @return */ public static boolean isPossibleInternalPojoFqn(Fqn fqn) { return (fqn.size() > POJO_INTERNAL_FQN_SIZE && ATTRIBUTE.equals(fqn.get(POJO_INTERNAL_FQN_INDEX)) == false); } public static String getIdFromFqn(Fqn fqn, boolean isBuddy) { return (String)fqn.get(isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + SESSION_ID_FQN_INDEX : SESSION_ID_FQN_INDEX); } /** * Extracts the owner portion of an buddy subtree Fqn. * * @param fqn An Fqn that is a child of the buddy backup root node. */ public static String getBuddyOwner(Fqn fqn) { return (String) fqn.get(BUDDY_BACKUP_ROOT_OWNER_INDEX); } public static boolean isFqnForOurWebapp(Fqn fqn, String contextHostPath, boolean isBuddy) { try { if (contextHostPath.equals(fqn.get(isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + WEBAPP_FQN_INDEX : WEBAPP_FQN_INDEX)) && JSESSION.equals(fqn.get(isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + JSESSION_FQN_INDEX : JSESSION_FQN_INDEX))) return true; } catch (IndexOutOfBoundsException e) { // can't be ours; too small; just fall through } return false; } private FqnUtil() {} } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/util/Session.java0000644000175000017500000001242711112450520032331 0ustar moellermoeller/* * 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.integration.websession.util; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; /** * Mock version of a JBoss AS clustered session. * * @author Brian Stansberry */ public class Session { private volatile SessionMetadata metadata = new SessionMetadata(); private final AtomicInteger version = new AtomicInteger(); private final AtomicLong timestamp = new AtomicLong(metadata.creationTime); private final Map attributes = new ConcurrentHashMap(); private final Set modifiedKeys = new HashSet(); private final Set removedKeys = new HashSet(); private final SessionManager manager; private boolean outdated; private boolean metadataDirty = true; public Session(SessionManager manager) { this.manager = manager; } public String getId() { return metadata.id; } public long getCreationTime() { return metadata.creationTime; } public boolean isValid() { return metadata.valid; } public void invalidate() { this.metadata.valid = false; manager.removeSession(getId(), false, true); } public Object setAttribute(String key, Object value) { modifiedKeys.add(key); removedKeys.remove(key); return attributes.put(key, value); } public Object removeAttribute(String key) { removedKeys.add(key); modifiedKeys.remove(key); return attributes.remove(key); } public Object getAttribute(String key) { return attributes.get(key); } public AtomicLong getTimestamp() { return timestamp; } public void setMetadataDirty() { this.metadataDirty = true; } public void access() { this.timestamp.set(System.currentTimeMillis()); } public void store() { version.incrementAndGet(); switch (manager.getGranularity()) { case SESSION: manager.storeSession(getId(), version, timestamp, getMetadataForReplication(), getAttributesForReplication()); break; case ATTRIBUTE: Map modified = new HashMap(); for (String key : modifiedKeys) { modified.put(key, attributes.get(key)); } manager.storeSession(getId(), version, timestamp, getMetadataForReplication(), modified, removedKeys); break; case FIELD: throw new UnsupportedOperationException("implement me"); } this.metadataDirty = false; } public SessionManager getManager() { return manager; } protected Map getAttributes() { return attributes; } protected SessionMetadata getMetadata() { return metadata; } protected AtomicInteger getVersion() { return version; } protected Map getAttributesForReplication() { return attributes; } private SessionMetadata getMetadataForReplication() { return metadataDirty ? metadata : null; } protected void replicateAttributeChanges() { // no-op } protected boolean isOutdated() { return outdated; } protected void setOutdated(boolean outdated) { this.outdated = outdated; } protected void update(AtomicInteger version, AtomicLong timestamp, SessionMetadata metadata, Map attributes) { if (version == null) throw new IllegalArgumentException("version is null"); if (timestamp == null) throw new IllegalArgumentException("timestamp is null"); if (metadata == null) throw new IllegalArgumentException("metadata is null"); if (attributes == null) throw new IllegalArgumentException("attributes is null"); this.version.set(version.get()); this.timestamp.set(version.get()); this.metadata = metadata; this.attributes.clear(); this.attributes.putAll(attributes); this.outdated = false; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/util/Servlet.java0000644000175000017500000000240111112450520032321 0ustar moellermoeller/* * 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.integration.websession.util; /** * Implementations of this interface will actually do something with * the session. * * @author Brian Stansberry */ public interface Servlet { void handleRequest(Request request); } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/util/SessionMetadata.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/util/SessionMetadata.j0000644000175000017500000000336411142423305033306 0ustar moellermoeller/* * 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.integration.websession.util; import java.io.Serializable; /** * Metadata about a session that is only replicated infrequently. * * @author Brian Stansberry */ public class SessionMetadata implements Serializable { /** The serialVersionUID */ private static final long serialVersionUID = 1L; private static volatile int counter; public final String id = String.valueOf(++counter); public final long creationTime = System.currentTimeMillis(); public boolean valid = true; @Override public String toString() { return "SessionMetadata{" + "id='" + id + '\'' + ", creationTime=" + creationTime + ", valid=" + valid + '}'; } } ././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/util/InvalidationServlet.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/util/InvalidationServl0000644000175000017500000000246411112450520033423 0ustar moellermoeller/* * 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.integration.websession.util; /** * A Servlet that invalidates the session. * * @author Brian Stansberry */ public class InvalidationServlet extends AbstractServlet { public void handleRequest(Request request) { extractSession(request).invalidate(); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/util/Request.java0000644000175000017500000000521511113416365032345 0ustar moellermoeller/* * 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.integration.websession.util; /** * Mocks the JBoss AS clustered webapp request handling. * * @author Brian Stansberry */ public class Request { private final SessionManager manager; private final String sessionId; private final Servlet servlet; private Session session; public Request(SessionManager manager, String sessionId, Servlet servlet) { this.manager = manager; this.sessionId = sessionId; this.servlet = servlet; } public void execute() { // Gravitate the session outside of the batch getSession(false); manager.startBatch(); try { servlet.handleRequest(this); } finally { try { if (session != null && session.isValid()) session.store(); } finally { manager.endBatch(); } } // StandardHostValve calls getSession(false) on the way out, so... getSession(false); } /** * Meant for internal use in this class and by a Servlet; test driver * should get a session ref from a Servlet impl. */ public Session getSession(boolean create) { if (session == null && sessionId != null) { session = manager.findSession(sessionId); } if (session != null && !session.isValid()) { session = null; getSession(create); } if (create && session == null) session = manager.createSession(); if (session != null) session.access(); return session; } } ././@LongLink0000000000000000000000000000015500000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/util/GetAttributesServlet.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/util/GetAttributesServ0000644000175000017500000000352311112450520033411 0ustar moellermoeller/* * 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.integration.websession.util; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * A Servlet that reads attributes from the session. * * @author Brian Stansberry */ public class GetAttributesServlet extends AbstractServlet { private final Set toGet; private final Map readAttributes = new HashMap(); public GetAttributesServlet(Set toGet) { this.toGet = new HashSet(toGet); } public void handleRequest(Request request) { Session session = extractSession(request); for (String key : toGet) { readAttributes.put(key, session.getAttribute(key)); } } public Map getReadAttributes() { return readAttributes; } } ././@LongLink0000000000000000000000000000015600000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/util/MultipleActionServlet.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/websession/util/MultipleActionSer0000644000175000017500000000312211112450520033361 0ustar moellermoeller/* * 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.integration.websession.util; import java.util.Arrays; import java.util.List; /** * A Servlet that call other servlets in order. * * @author Brian Stansberry */ public class MultipleActionServlet extends AbstractServlet { private final List actions; public MultipleActionServlet(Servlet... action) { this.actions = Arrays.asList(action); } public void handleRequest(Request request) { extractSession(request); for (Servlet action : actions) { action.handleRequest(request); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/JGroupsUtil.java0000644000175000017500000001244711112450520030001 0ustar moellermoeller/* * 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.integration; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.util.FileLookup; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * JGroups-related utilities. * * @author Brian Stansberry */ public class JGroupsUtil { private final static String PROTOCOL_STACKS = "protocol_stacks"; private final static String STACK = "stack"; private static final String NAME = "name"; private static final String CONFIG = "config"; public static Map getStackConfigs(String stacksXmlResource) throws Exception { FileLookup fileLookup = new FileLookup(); InputStream is = fileLookup.lookupFile(stacksXmlResource); if (is == null) { throw new ConfigurationException("Unable to find config file " + stacksXmlResource + " either in classpath or on the filesystem!"); } return getStackConfigs(is); } /** * Parses a set if "config" elements out of a JGroups stacks.xml file. * * @param input * @return * @throws Exception */ public static Map getStackConfigs(InputStream input) throws Exception { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(false); //for now DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.parse(input); // The root element of the document should be the "config" element, // but the parser(Element) method checks this so a check is not // needed here. Element configElement = document.getDocumentElement(); return getStackConfigs(configElement); } private static Map getStackConfigs(Element root) throws Exception { Map result = new HashMap(); String root_name = root.getNodeName(); if (!PROTOCOL_STACKS.equals(root_name.trim().toLowerCase())) { String error = "XML protocol stack configuration does not start with a '' element; " + "maybe the XML configuration needs to be converted to the new format ?\n" + "use 'java org.jgroups.conf.XmlConfigurator -new_format' to do so"; throw new IOException("invalid XML configuration: " + error); } NodeList tmp_stacks = root.getChildNodes(); for (int i = 0; i < tmp_stacks.getLength(); i++) { Node node = tmp_stacks.item(i); if (node.getNodeType() != Node.ELEMENT_NODE) continue; Element stack = (Element) node; String tmp = stack.getNodeName(); if (!STACK.equals(tmp.trim().toLowerCase())) { throw new IOException("invalid configuration: didn't find a \"" + STACK + "\" element under \"" + PROTOCOL_STACKS + "\""); } NamedNodeMap attrs = stack.getAttributes(); Node name = attrs.getNamedItem(NAME); String st_name = name.getNodeValue(); NodeList configs = stack.getChildNodes(); for (int j = 0; j < configs.getLength(); j++) { Node tmp_config = configs.item(j); if (tmp_config.getNodeType() != Node.ELEMENT_NODE) continue; Element cfg = (Element) tmp_config; tmp = cfg.getNodeName(); if (!CONFIG.equals(tmp)) throw new IOException("invalid configuration: didn't find a \"" + CONFIG + "\" element under \"" + STACK + "\""); if (!result.containsKey(st_name)) { result.put(st_name, cfg); } else { throw new IllegalStateException("didn't add config '" + st_name + " because one of the same name already existed"); } } } return result; } /** * Prevent instantiation. */ private JGroupsUtil() { throw new UnsupportedOperationException("just a static util class"); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/MockChannelFactory.java0000644000175000017500000000545511112450520031265 0ustar moellermoeller/* * 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.integration; import java.io.File; import java.net.URL; import org.jgroups.Channel; import org.jgroups.ChannelException; import org.jgroups.ChannelFactory; import org.w3c.dom.Element; /** * @author Brian Stansberry * */ public class MockChannelFactory implements ChannelFactory { public static final MockChannelFactory INSTANCE = new MockChannelFactory(); private MockChannelFactory() {} public Channel createChannel() throws ChannelException { throw new UnsupportedOperationException(); } public Channel createChannel(Object props) throws ChannelException { throw new UnsupportedOperationException(); } public Channel createChannel(String stack_name) throws Exception { throw new UnsupportedOperationException(); } public Channel createMultiplexerChannel(String stack_name, String id) throws Exception { throw new UnsupportedOperationException(); } public Channel createMultiplexerChannel(String stack_name, String id, boolean register_for_state_transfer, String substate_id) throws Exception { throw new UnsupportedOperationException(); } public void setMultiplexerConfig(Object properties) throws Exception { throw new UnsupportedOperationException(); } public void setMultiplexerConfig(File properties) throws Exception { throw new UnsupportedOperationException(); } public void setMultiplexerConfig(Element properties) throws Exception { throw new UnsupportedOperationException(); } public void setMultiplexerConfig(URL properties) throws Exception { throw new UnsupportedOperationException(); } public void setMultiplexerConfig(String properties) throws Exception { throw new UnsupportedOperationException(); } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/UnitTestCacheFactoryCacheManager.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/UnitTestCacheFactoryCacheManager.0000644000175000017500000000506311120367722033170 0ustar moellermoeller/* * 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.integration; import org.jboss.cache.Cache; import org.jboss.cache.CacheManagerImpl; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; /** * CacheManager implementation that integrates with UnitTestCacheFactory. * * @author Brian Stansberry */ public class UnitTestCacheFactoryCacheManager extends CacheManagerImpl { private final String cacheConfigFileName; private final String stacksXMLFileName; /** * Create a new UnitTestCacheFactoryCacheManager. * * @param configFileName * @param factory */ public UnitTestCacheFactoryCacheManager(String cacheConfigFileName, String stacksXMLFileName) { super(new UnitTestCacheFactoryConfigurationRegistry(cacheConfigFileName, stacksXMLFileName), MockChannelFactory.INSTANCE); this.cacheConfigFileName = cacheConfigFileName; this.stacksXMLFileName = stacksXMLFileName; } public String getCacheConfigFileName() { return cacheConfigFileName; } public String getStacksXMLFileName() { return stacksXMLFileName; } @Override public void start() throws Exception { ((UnitTestCacheFactoryConfigurationRegistry) getConfigurationRegistry()).start(); super.start(); } /** * Overrides superclass to use UnitTestCacheFactory to create the cache. */ @Override protected Cache createCache(Configuration config) { return new UnitTestCacheFactory().createCache(config, false, getClass()); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/CacheManagerSupport.java0000644000175000017500000000557311112450520031447 0ustar moellermoeller/* * 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.integration; import java.util.ArrayList; import java.util.List; import org.jboss.cache.CacheManager; /** * @author Brian Stansberry */ public class CacheManagerSupport { /** * For each thread holds list of cache managers created using this factory. */ private static final ThreadLocal> threadCacheManagers = new ThreadLocal>() { @Override protected List initialValue() { return new ArrayList(); } }; public static List createCacheManagers(int count, String cacheConfigFileName, String stacksXmlFileName) { List existing = threadCacheManagers.get(); List result = new ArrayList(count); int added = 0; for (UnitTestCacheFactoryCacheManager cm : existing) { if (cacheConfigFileName.equals(cm.getCacheConfigFileName()) && stacksXmlFileName.equals(cm.getStacksXMLFileName())) { result.add(cm); added++; } } for (; added < count; added++) { UnitTestCacheFactoryCacheManager cm = new UnitTestCacheFactoryCacheManager(cacheConfigFileName, stacksXmlFileName); try { cm.start(); } catch (Exception e) { throw new RuntimeException(e); } result.add(cm); existing.add(cm); } return result; } public static void tearDown() { List existing = threadCacheManagers.get(); for (UnitTestCacheFactoryCacheManager cm : existing) { cm.stop(); } threadCacheManagers.remove(); } } ././@LongLink0000000000000000000000000000016200000000000011564 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/UnitTestCacheFactoryConfigurationRegistry.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/integration/UnitTestCacheFactoryConfiguration0000644000175000017500000001247411112450520033415 0ustar moellermoeller/* * 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.integration; import java.util.HashSet; import java.util.Hashtable; import java.util.Map; import java.util.Set; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.ConfigurationRegistry; import org.jboss.cache.config.parsing.CacheConfigsXmlParser; import org.w3c.dom.Element; /** * XmlParsingConfigurationRegistry variant that replaces any MuxStackName in * configurations with a corresponding Element parsed out of the provided * JGroups stacks.xml file. UnitTestCacheFactory can then mangle the element. * * @author Brian Stansberry * @version $Revision: 7168 $ */ public class UnitTestCacheFactoryConfigurationRegistry implements ConfigurationRegistry { public static final String DEFAULT_STACKS_XML_RESOURCE = "configs/integration/jgroups-channelfactory-stacks.xml"; private final CacheConfigsXmlParser parser; private final String configResource; private final Map stacks; private final Map configs = new Hashtable(); private boolean started; public UnitTestCacheFactoryConfigurationRegistry(String cacheConfigResource) { this(cacheConfigResource, DEFAULT_STACKS_XML_RESOURCE); } public UnitTestCacheFactoryConfigurationRegistry(String cacheConfigResource, String stacksXmlResource) { parser = new CacheConfigsXmlParser(); this.configResource = cacheConfigResource; try { this.stacks = JGroupsUtil.getStackConfigs(stacksXmlResource); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException("problem parsing JGroups stacks", e); } } public void start() throws Exception { if (!started) { if (configResource != null) { Map parsed = parser.parseConfigs(configResource); for (Map.Entry entry : parsed.entrySet()) { registerConfiguration(entry.getKey(), entry.getValue()); } } 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"); Configuration clone = config.clone(); fixJGroupsConfig(clone); configs.put(configName, 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); } } /** Replace a stack name with a stack element that UnitTestCacheFactory can mangle */ private void fixJGroupsConfig(Configuration clone) { String stackName = clone.getMultiplexerStack(); if (stackName != null) { clone.setMultiplexerStack(null); Element e = stacks.get(stackName); if (e == null) { throw new IllegalStateException("unknown stack " + stackName); } clone.setClusterConfig(e); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/0000755000175000017500000000000011675221430024713 5ustar moellermoeller././@LongLink0000000000000000000000000000017400000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/IsolationLevelReadCommittedNodeCreationRollbackTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/IsolationLevelReadCommittedNodeCr0000644000175000017500000001443411132625723033333 0ustar moellermoeller/* * 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.Fqn; import org.jboss.cache.UnitTestCacheFactory; 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 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 7451 2009-01-12 11:38:59Z mircea.markus $ */ @Test(groups = {"functional", "transaction"}, sequential = true, testName = "transaction.IsolationLevelReadCommittedNodeCreationRollbackTest") 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; UnitTestCacheFactory instance = new UnitTestCacheFactory(); Configuration c = new Configuration(); c.setCacheMode(Configuration.CacheMode.LOCAL); c.setIsolationLevel(IsolationLevel.READ_COMMITTED); c.setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); cache = (CacheSPI) instance.createCache(c, false, getClass()); cache.start(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache); 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(); // 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); 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 { 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(); // 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 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 { 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; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/TransactionSetup.java0000644000175000017500000000766011111047320031062 0ustar moellermoeller/* * 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-3.2.8.GA/src/test/java/org/jboss/cache/transaction/DeadlockTest.java0000755000175000017500000003115511132625723030135 0ustar moellermoeller/* * * 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.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; 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.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 7451 2009-01-12 11:38:59Z mircea.markus $ */ @Test(groups = {"functional", "transaction"}, sequential = true, enabled = false, testName = "transaction.DeadlockTest") 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 { UnitTestCacheFactory instance = new UnitTestCacheFactory(); Configuration c = new Configuration(); c.setStateRetrievalTimeout(10000); c.setClusterName("test"); c.setCacheMode(Configuration.CacheMode.LOCAL); c.setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); c.setIsolationLevel(IsolationLevel.REPEATABLE_READ); c.setLockParentForChildInsertRemove(true); c.setLockAcquisitionTimeout(3000); cache = (CacheSPI) instance.createCache(c, false, getClass()); cache.create(); cache.start(); thread_ex = null; } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { if (cache != null) { TestingUtil.killCaches(cache); cache = null; } 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); 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("DeadlockTestPutter", NODE); ContinuousRemover remover = new ContinuousRemover("DeadlockTestRemover", PARENT_NODE); putter.start(); remover.start(); TestingUtil.sleepThread((long) 5000); putter.looping = false; remover.looping = false; putter.join(); remover.join(); } public void testPutsAndRemovesOnParentAndChildNodesReversed() throws InterruptedException { ContinuousPutter putter = new ContinuousPutter("DeadlockTestPutter", PARENT_NODE); ContinuousRemover remover = new ContinuousRemover("DeadlockTestRemover", NODE); putter.start(); remover.start(); TestingUtil.sleepThread((long) 5000); putter.looping = false; remover.looping = false; putter.join(); remover.join(); } public void testPutsAndRemovesOnSameNode() throws InterruptedException { ContinuousPutter putter = new ContinuousPutter("DeadlockTestPutter", NODE); ContinuousRemover remover = new ContinuousRemover("DeadlockTestRemover", NODE); putter.start(); remover.start(); TestingUtil.sleepThread((long) 5000); putter.looping = false; 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) { 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(); 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(); 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(); cache.get(fqn, "bla");// acquires RL synchronized (this) { wait(); } cache.put(fqn, "key", "val");// need to upgrade RL to WL tm.commit(); } } 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 { cache.get(fqn, "bla");// acquires RL synchronized (lock) { lock.wait(); } cache.put(fqn, "key", "val");// need to upgrade RL to WL tm.commit(); } catch (UpgradeException upge) { 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) { tm.rollback(); } catch (TimeoutException timeoutEx) { tm.rollback(); } } } 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(); cache.put(fqn1, "key", "val");// need to upgrade RL to WL synchronized (this) { wait(); } cache.put(fqn2, "key", "val");// need to upgrade RL to WL tm.commit(); } } 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) { tm.rollback(); } } } private TransactionManager startTransaction() throws SystemException, NotSupportedException { TransactionManager mgr = cache.getTransactionManager(); mgr.begin(); return mgr; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/RemoveOnTxTest.java0000644000175000017500000001146111120423026030455 0ustar moellermoeller/* * 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.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 and 2.x, but didn't reproduce on 3.x. * Anyway, the tests is here just in case... * * @author Mircea.Markus@jboss.com */ @Test (groups = "functional", testName = "transaction.RemoveOnTxTest") 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 UnitTestCacheFactory().createCache(configuration, getClass()); dataContainer = (DataContainerImpl) cache.getComponentRegistry().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(); cache.put("/a/b/c", "test", "test"); assertTrue(cache.get("/a/b/c", "test").equals("test")); 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")); 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(); try { tm.begin(); Transaction t = tm.suspend(); try { cache.putForExternalRead(Fqn.fromString("/a/b/c"), "test", "test"); } catch (Exception ignore) { ignore.printStackTrace(); } tm.resume(t); cache.put("/a/b/c", "test", "test"); tm.commit(); } catch (Exception ex) { tm.rollback(); throw ex; } } public void testReal() throws Exception { TransactionManager tm = cache.getTransactionManager(); tm.begin(); cache.put("/a/b/c", "test", "test"); assertTrue(cache.get("/a/b/c", "test").equals("test")); 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")); 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(); 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-3.2.8.GA/src/test/java/org/jboss/cache/transaction/NotifyingTransactionManager.java0000644000175000017500000000433711111047320033221 0ustar moellermoeller/* * 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 AT jboss DOT 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, TransactionContext transactionContext) 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-3.2.8.GA/src/test/java/org/jboss/cache/transaction/PrepareCommitContentionTest.java0000644000175000017500000001356211144464602033236 0ustar moellermoellerpackage org.jboss.cache.transaction; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.RPCManager; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.RPCManagerImpl.FlushTracker; 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.UnitTestConfigurationFactory; 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; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * 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 AT jboss DOT org) * @since 2.1.0 */ @Test(groups = "functional", sequential = true, testName = "transaction.PrepareCommitContentionTest") public class PrepareCommitContentionTest { CacheSPI c1; @BeforeMethod public void setUp() throws CloneNotSupportedException { c1 = (CacheSPI) new UnitTestCacheFactory().createCache(UnitTestConfigurationFactory.createConfiguration(REPL_SYNC), getClass()); } @AfterMethod public void tearDown() { TestingUtil.killCaches(c1); c1 = null; } 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(); } public Address getLastStateTransferSource() { return delegate.getLastStateTransferSource(); } public FlushTracker getFlushTracker() { return delegate.getFlushTracker(); } } } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/ReplicatedTransactionDeadlockTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/ReplicatedTransactionDeadlockTest0000644000175000017500000002013411131613416033403 0ustar moellermoellerpackage org.jboss.cache.transaction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.UnitTestConfigurationFactory; 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 org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.util.TestingUtil; /** * 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: mircea.markus $ * @version $Date: 2008-11-06 19:07:10 -0600 (Thu, 06 Nov 2008) $ */ @Test(groups = {"functional", "transaction"}, testName = "transaction.ReplicatedTransactionDeadlockTest") 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; UnitTestCacheFactory instance = new UnitTestCacheFactory(); // setup and start the source cache Configuration c = UnitTestConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC); c.setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); c.setCacheMode(Configuration.CacheMode.REPL_SYNC); c.setSyncCommitPhase(true); c.setLockAcquisitionTimeout(LOCK_ACQUISITION_TIMEOUT); srcCache = (CacheSPI) instance.createCache(c, false, getClass()); srcCache.create(); srcCache.start(); // setup and start the destination cache c = UnitTestConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); c.setCacheMode(Configuration.CacheMode.REPL_SYNC); c.setSyncCommitPhase(true); c.setLockAcquisitionTimeout(LOCK_ACQUISITION_TIMEOUT); dstCache = (CacheSPI) instance.createCache(c, false, getClass()); dstCache.create(); dstCache.start(); } /** * {@inheritDoc} */ @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(srcCache, dstCache); srcCache = null; dstCache = null; // We just can't kill DummyTransactionManager. We are sharing single instance in more tests. TestingUtil.killTransaction(TransactionSetup.getManager()); } /** * 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: mircea.markus $ * @version $Date: 2008-11-06 19:07:10 -0600 (Thu, 06 Nov 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; } } } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/IsolationLevelRepeatableReadTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/IsolationLevelRepeatableReadTest.0000644000175000017500000001104011132625723033263 0ustar moellermoellerpackage org.jboss.cache.transaction; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; 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 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 7451 2009-01-12 11:38:59Z mircea.markus $ */ @Test(groups = {"functional", "transaction"}, sequential = true, testName = "transaction.IsolationLevelRepeatableReadTest") 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; UnitTestCacheFactory instance = new UnitTestCacheFactory(); Configuration c = new Configuration(); c.setCacheMode("LOCAL"); c.setIsolationLevel(IsolationLevel.REPEATABLE_READ); c.setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); c.setLockAcquisitionTimeout(1000); cache = (CacheSPI) instance.createCache(c, false, getClass()); cache.start(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache); 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 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 { 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 { 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-3.2.8.GA/src/test/java/org/jboss/cache/transaction/pessimistic/0000755000175000017500000000000011675221425027253 5ustar moellermoeller././@LongLink0000000000000000000000000000015700000000000011570 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/pessimistic/PessimisticTransactionTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/pessimistic/PessimisticTransactio0000644000175000017500000007331211132625723033525 0ustar moellermoeller/* * * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.transaction.pessimistic; import org.jboss.cache.*; import org.jboss.cache.config.Configuration; 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.transaction.GlobalTransaction; import org.jboss.cache.transaction.TransactionSetup; 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.*; 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: PessimisticTransactionTest.java 7451 2009-01-12 11:38:59Z mircea.markus $ */ @Test(groups = {"functional", "transaction"}, sequential = true, testName = "transaction.pessimistic.PessimisticTransactionTest") public class PessimisticTransactionTest { CacheSPI cache = null; UserTransaction tx = null; Exception exception; LockManager lockManager; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { UnitTestCacheFactory instance = new UnitTestCacheFactory(); cache = (CacheSPI) instance.createCache(false, getClass()); cache.getConfiguration().setClusterName("test"); cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); 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) { TestingUtil.killCaches(cache); cache = null; } // BW. kind of a hack to destroy jndi binding and thread local tx before next run. Transaction c = TransactionSetup.getManager().getTransaction(); if (c != null) c.rollback(); 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(); cache.put("/bela/ban", "key", "value"); tx.rollback(); 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(); 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(); cache.get("/1/2/3", "foo");// acquires RLs on all 3 nodes sleep(2000); thread_tx.commit();// releases RLs } 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(); cache.put("/1", "foo", "bar2");// needs to acquired a WL on /1 thread_tx.commit(); } 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 { 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")); } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/pessimistic/AsyncRollbackTxTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/pessimistic/AsyncRollbackTxTest.j0000644000175000017500000001616511136157371033342 0ustar moellermoellerpackage org.jboss.cache.transaction.pessimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.transaction.AsyncRollbackTransactionManager; import org.jboss.cache.config.Configuration; 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 org.apache.commons.logging.LogFactory; import org.apache.commons.logging.Log; import javax.transaction.SystemException; import javax.transaction.TransactionManager; import javax.transaction.Status; /** * Test behaviour of async rollback timeouted transaction * * @author Jacek Halat * @since 1.4.0 */ @Test(groups = {"functional"}, testName = "transaction.pessimistic.AsyncRollbackTxTest") public class AsyncRollbackTxTest { private CacheSPI cache; private AsyncRollbackTransactionManager 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"); c.setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); c.setSerializationExecutorPoolSize(0); UnitTestCacheFactory instance = new UnitTestCacheFactory(); cache = (CacheSPI) instance.createCache(c, getClass()); tm = (AsyncRollbackTransactionManager) 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 } TestingUtil.killCaches(cache); 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 Throwable { 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 Throwable { doTest(false, false); } public void testCommitCreationInDifferentTxReadLock() throws Throwable { doTest(true, false); } public void testRollbackCreationInDifferentTxWriteLock() throws Throwable { doTest(false, true); } public void testCommitCreationInDifferentTxWriteLock() throws Throwable { 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 Throwable { 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 { Throwable e = null; boolean commit, writeLock; public SeparateThread(boolean commit, boolean writeLock) { this.commit = commit; this.writeLock = writeLock; } public Throwable 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 } for (int i =0; i < 100; i++) //max 50 secs { if (tm.getTransaction().getStatus() == Status.STATUS_ROLLEDBACK) { break; } Thread.sleep(500); } if (commit) { tm.commit(); } else { tm.rollback(); } assertEquals(0, cache.getNumberOfLocksHeld()); } catch (Throwable e) { this.e = e; } } } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/pessimistic/StatusUnknownTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/pessimistic/StatusUnknownTest.jav0000644000175000017500000001223511120367722033457 0ustar moellermoellerpackage org.jboss.cache.transaction.pessimistic; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.transaction.DummyBaseTransactionManager; import org.jboss.cache.transaction.DummyTransaction; import org.jboss.cache.transaction.DummyTransactionManager; import org.jboss.cache.transaction.TransactionManagerLookup; 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.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; /** * This test checks how the cache behaves when a JTA STATUS_UNKNOWN is passed in to the cache during afterCompletion(). * * @author Manik Surtani */ @Test(groups = "functional", sequential = true, testName = "transaction.pessimistic.StatusUnknownTest") public class StatusUnknownTest { private Cache cache; private TransactionManager tm; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache = new UnitTestCacheFactory().createCache(false, getClass()); cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); cache.getConfiguration().setTransactionManagerLookupClass(HeuristicFailingDummyTransactionManagerLookup.class.getName()); cache.start(); tm = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); } @AfterMethod(alwaysRun = true) public void tearDown() { TestingUtil.killCaches(cache); cache = null; tm = null; } 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; // we can not share an instance with DummyTransactionManager private static DummyTransactionManager instance; @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(); } } } ././@LongLink0000000000000000000000000000016000000000000011562 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/pessimistic/ConcurrentTransactionalTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/pessimistic/ConcurrentTransaction0000644000175000017500000001226311136146636033534 0ustar moellermoeller/* * * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.transaction.pessimistic; 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.transaction.TransactionSetup; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestConfigurationFactory; 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; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.util.TestingUtil; /** * Unit test for local CacheSPI. Use locking and multiple threads to test * concurrent access to the tree. * * @version $Id: ConcurrentTransactionalTest.java 7571 2009-01-22 19:48:46Z mircea.markus $ */ @Test(groups = {"functional", "transaction"}, testName = "transaction.pessimistic.ConcurrentTransactionalTest") public class ConcurrentTransactionalTest { private volatile CacheSPI cache; private Log logger_ = LogFactory.getLog(ConcurrentTransactionalTest.class); private volatile Throwable thread_ex = null; private static final int NUM = 1000; Log log = LogFactory.getLog(ConcurrentTransactionalTest.class); private void createCache(IsolationLevel level) { Configuration conf = UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, false); conf.setCacheMode(Configuration.CacheMode.LOCAL); conf.setIsolationLevel(level); conf.setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); conf.setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); cache = (CacheSPI) new UnitTestCacheFactory().createCache(conf, getClass()); cache.put("/a/b/c", null); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache); cache = null; thread_ex = null; // We just can't kill DummyTransactionManager. We are sharing single instance in more tests. TestingUtil.killTransaction(TransactionSetup.getManager()); } 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(30000); two.join(30000); if (thread_ex != null) { throw thread_ex; } long now = System.currentTimeMillis(); log("*** Time elapsed: " + (now - current)); Set keys = cache.getNode(Fqn.fromString("/a/b/c")).getKeys(); if (keys.size() != NUM) { scanForNullValues(keys); try { List l = new LinkedList(keys); Collections.sort(l); LinkedList duplicates = new LinkedList(); for (Integer integer : l) { if (!duplicates.contains(integer)) { duplicates.add(integer); } } } catch (Exception e1) { e1.printStackTrace(); } } assertEquals(NUM, keys.size()); } catch (Exception e) { log.error("Exception here: " + e, 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(cache + " adding data i=" + i); tx.begin(); cache.put("/a/b/c", i, val); tx.commit(); yield(); } } catch (Throwable t) { log.error("cache = " + cache + ", tx = " + tx+ t); thread_ex = t; } } } } ././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/pessimistic/IsolationLevelNoneTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/pessimistic/IsolationLevelNoneTes0000755000175000017500000001041511132625723033424 0ustar moellermoellerpackage org.jboss.cache.transaction.pessimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.transaction.TransactionSetup; import org.jboss.cache.util.TestingUtil; 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 7451 2009-01-12 11:38:59Z mircea.markus $ */ @Test(groups = {"functional", "transaction"}, sequential = true, testName = "transaction.pessimistic.IsolationLevelNoneTest") 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) { TestingUtil.killCaches(cache); cache = null; } } public void testWithoutTransactions() throws Exception { UnitTestCacheFactory instance = new UnitTestCacheFactory(); cache = (CacheSPI) instance.createCache(false, getClass()); cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); 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 { UnitTestCacheFactory instance = new UnitTestCacheFactory(); cache = (CacheSPI) instance.createCache(false, getClass()); cache.getConfiguration().setCacheMode(Configuration.CacheMode.LOCAL); cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); 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 { UnitTestCacheFactory instance = new UnitTestCacheFactory(); cache = (CacheSPI) instance.createCache(false, getClass()); cache.getConfiguration().setCacheMode(Configuration.CacheMode.LOCAL); cache.getConfiguration().setIsolationLevel(IsolationLevel.REPEATABLE_READ); cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); 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; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/pessimistic/PrepareTxTest.java0000644000175000017500000001126511132625723032672 0ustar moellermoellerpackage org.jboss.cache.transaction.pessimistic; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.transaction.TransactionSetup; import org.jboss.cache.transaction.TransactionTable; 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.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"}, sequential = true, testName = "transaction.pessimistic.PrepareTxTest") public class PrepareTxTest { CacheSPI cache; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { UnitTestCacheFactory instance = new UnitTestCacheFactory(); cache = (CacheSPI) instance.createCache(false, getClass()); cache.getConfiguration().setCacheMode("local"); cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); cache.getConfiguration().setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); cache.create(); cache.start(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache); cache = null; } /** * 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(); 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(); assertEquals(num_local_txs, num_global_txs); assertEquals(0, num_local_txs); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/pessimistic/AbortionTest.java0000644000175000017500000001606711132354355032542 0ustar moellermoeller/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.transaction.pessimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.interceptors.OrderedSynchronizationHandler; import org.jboss.cache.transaction.DummyTransactionManager; import org.jboss.cache.transaction.NotifyingTransactionManager; import org.jboss.cache.transaction.NotifyingTransactionManager.Notification; import org.jboss.cache.transaction.TransactionContext; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.replicationlisteners.ReplicationListener; 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 AT jboss DOT org) */ @Test(groups = {"functional"}, testName = "transaction.pessimistic.AbortionTest") public class AbortionTest { private CacheSPI cache1, cache2, cache3; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache1 = initCache(false); cache2 = initCache(false); cache3 = initCache(true); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache3, cache2, cache1); cache1 = null; cache2 = null; cache3 = null; } private CacheSPI initCache(boolean notifying) throws Exception { Configuration conf = UnitTestConfigurationFactory.getEmptyConfiguration(); conf.setCacheMode(Configuration.CacheMode.REPL_SYNC); conf.setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); conf.setFetchInMemoryState(false); CacheSPI c = (CacheSPI) new UnitTestCacheFactory().createCache(conf, false, getClass()); if (!notifying) { c.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); } else { c.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.NotifyingTransactionManager"); } c.start(); return c; } 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); ReplicationListener cacheLister2 = ReplicationListener.getReplicationListener(cache2); ReplicationListener cacheLister3 = ReplicationListener.getReplicationListener(cache3); cacheLister2.expect(PutKeyValueCommand.class); cacheLister3.expect(PutKeyValueCommand.class); cache1.put("/test", "key", "value"); cacheLister2.waitForReplicationToOccur(); cacheLister3.waitForReplicationToOccur(); assertEquals("value", cache1.get("/test", "key")); assertEquals("value", cache2.get("/test", "key")); assertEquals("value", cache3.get("/test", "key")); mgr3.setNotification(new TestNotification(abortBeforeCompletion)); cacheLister2.expectWithTx(PutKeyValueCommand.class); cacheLister3.expectWithTx(PutKeyValueCommand.class); mgr1.begin(); cache1.put("/test", "key", "value2"); mgr1.commit(); cacheLister2.waitForReplicationToOccur(); cacheLister3.waitForReplicationToOccur(); // 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, TransactionContext transactionContext) throws SystemException, RollbackException { OrderedSynchronizationHandler osh = transactionContext.getOrderedSynchronizationHandler(); final Transaction finalTx = tx; // add an aborting sync handler. Synchronization abort = new Synchronization() { public void beforeCompletion() { if (abortBeforeCompletion) { cache3.getConfiguration().getRuntimeConfig().getChannel().close(); 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(); throw new RuntimeException("Dummy exception"); } } }; osh.registerAtHead(abort); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/ConcurrentBankTest.java0000644000175000017500000001506511131613416031337 0ustar moellermoellerpackage 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.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.lock.TimeoutException; import org.jboss.cache.util.TestingUtil; 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; import org.jboss.cache.UnitTestCacheFactory; /** * 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: 7422 $ */ @Test(groups = {"functional", "transaction"}, enabled = true, testName = "transaction.ConcurrentBankTest") 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 = UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); UnitTestCacheFactory instance = new UnitTestCacheFactory(); cache = (CacheSPI) instance.createCache(conf, false, getClass()); cache.getConfiguration().setIsolationLevel(IsolationLevel.SERIALIZABLE); cache.getConfiguration().setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); cache.create(); cache.start(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache); cache = null; // We just can't kill DummyTransactionManager. We are sharing single instance in more tests. TestingUtil.killTransaction(TransactionSetup.getManager()); } 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(); assert !testFailedinThread; } 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); // 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) { tx.rollback(); again = true; } catch (Throwable e) { 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 { int act; // debit act = cache.get(NODE, from); cache.put(NODE, from, act - amount); // eventually rollback the transaction if ((int) (Math.random() * ROLLBACK_CHANCE) == 0) { tx.setRollbackOnly(); throw new Exception("Manually set rollback!"); } // credit act = cache.get(NODE, to); cache.put(NODE, to, act + amount); } /** * retrieving amounts of accounts */ public HashMap getAccounts() throws CacheException { 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; } } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/SimultaneousRollbackAndPutTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/SimultaneousRollbackAndPutTest.ja0000644000175000017500000000763211120367722033355 0ustar moellermoellerpackage 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.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.invocation.CacheInvocationDelegate; import org.jboss.cache.util.CachePrinter; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.AfterMethod; import org.testng.annotations.AfterTest; 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.ArrayList; import java.util.List; /** * To test JBCACHE-923 * * @author Manik Surtani */ @Test(groups = {"functional", "transaction"}, enabled = true, sequential = true, testName = "transaction.SimultaneousRollbackAndPutTest") // 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); @BeforeMethod(alwaysRun = true) protected void setUp() throws Exception { if (cache == null) { cache = new UnitTestCacheFactory().createCache(false, getClass()); 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); cache = null; } @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)); } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/IsolationLevelSerializableTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/IsolationLevelSerializableTest.ja0000644000175000017500000001115211132625723033350 0ustar moellermoellerpackage org.jboss.cache.transaction; import org.jboss.cache.Cache; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; 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 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.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 7451 2009-01-12 11:38:59Z mircea.markus $ */ @Test(groups = {"functional", "transaction"}, sequential = true, testName = "transaction.IsolationLevelSerializableTest") 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()); UnitTestCacheFactory instance = new UnitTestCacheFactory(); cache = instance.createCache(config, getClass()); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache); 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 { 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 { 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; } } ././@LongLink0000000000000000000000000000015600000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/AsyncRollbackTransactionManagerLookup.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/AsyncRollbackTransactionManagerLo0000644000175000017500000000047411111047320033353 0ustar moellermoellerpackage org.jboss.cache.transaction; import javax.transaction.TransactionManager; public class AsyncRollbackTransactionManagerLookup implements TransactionManagerLookup { public TransactionManager getTransactionManager() throws Exception { return AsyncRollbackTransactionManager.getInstance(); } }jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/InvocationContextCleanupTest.java0000644000175000017500000001063211120423026033374 0ustar moellermoeller/* * 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.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.interceptors.OrderedSynchronizationHandler; import org.jboss.cache.util.CachePrinter; import org.jboss.cache.util.TestingUtil; 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 AT jboss DOT org) */ @Test(groups = {"functional", "transaction"}, sequential = true, testName = "transaction.InvocationContextCleanupTest") public class InvocationContextCleanupTest { private CacheSPI[] caches; private CacheSPI createCache(boolean optimistic) { Configuration c = new Configuration(); c.setCacheMode(Configuration.CacheMode.REPL_SYNC); if (optimistic) c.setNodeLockingScheme("OPTIMISTIC"); c.setClusterName("InvocationContextCleanupTest"); c.setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); c.setLockAcquisitionTimeout(2000); CacheSPI cache = (CacheSPI) new UnitTestCacheFactory().createCache(c, false, getClass()); 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) { TestingUtil.killCaches(caches[i]); 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 { } 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 } } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/AsyncRollbackTransactionManager.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/AsyncRollbackTransactionManager.j0000644000175000017500000001434211111047320033307 0ustar moellermoellerpackage 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 { public TimedOutTransactionsChecker() { } public void run() { while (true) { try { Thread.sleep(50); 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-3.2.8.GA/src/test/java/org/jboss/cache/transaction/MarkAsRollbackTest.java0000644000175000017500000000645411146273534031264 0ustar moellermoellerpackage 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.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.Test; import javax.transaction.RollbackException; import javax.transaction.TransactionManager; @Test(groups = "functional", testName = "transaction.MarkAsRollbackTest") public class MarkAsRollbackTest { private static final Fqn fqn = Fqn.fromString("/a/b/c"); private static final Log log = LogFactory.getLog(MarkAsRollbackTest.class); public void testMarkAsRollbackAfterMods() throws Exception { Configuration c = new Configuration(); c.setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); CacheSPI cache = (CacheSPI) new UnitTestCacheFactory().createCache(c, MarkAsRollbackTest.class); try { TransactionManager tm = cache.getTransactionManager(); assert tm != null; tm.begin(); cache.put(fqn, "k", "v"); assert cache.get(fqn, "k").equals("v"); assert cache.getRoot().getChildren().size() == 1; tm.setRollbackOnly(); try { tm.commit(); assert false : "Should have rolled back"; } catch (RollbackException expected) { } assert tm.getTransaction() == null : "There should be no transaction in scope anymore!"; assert cache.get(fqn, "k") == null : "Expected a null but was " + cache.get(fqn, "k"); assert cache.getRoot().getChildren().size() == 0; } finally { log.warn("Cleaning up"); TestingUtil.killCaches(cache); } } public void testMarkAsRollbackBeforeMods() throws Exception { Configuration c = new Configuration(); c.setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); CacheSPI cache = (CacheSPI) new UnitTestCacheFactory().createCache(c, MarkAsRollbackTest.class); try { TransactionManager tm = cache.getTransactionManager(); assert tm != null; tm.begin(); tm.setRollbackOnly(); try { cache.put(fqn, "k", "v"); assert false : "Should have failed"; } catch (CacheException expected) { assert expected.getCause() instanceof RollbackException : "exception wrapped and thrown should be RollbackException. Exception class "+expected.getCause().getClass(); } try { tm.commit(); assert false : "Should have rolled back"; } catch (RollbackException expected) { } assert tm.getTransaction() == null : "There should be no transaction in scope anymore!"; assert cache.get(fqn, "k") == null : "Expected a null but was " + cache.get(fqn, "k"); assert cache.getRoot().getChildren().size() == 0; } finally { log.warn("Cleaning up"); TestingUtil.killCaches(cache); } } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/IsolationLevelReadCommittedTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/IsolationLevelReadCommittedTest.j0000644000175000017500000002264611132625723033334 0ustar moellermoellerpackage org.jboss.cache.transaction; import org.jboss.cache.Cache; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; 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 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.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 7451 2009-01-12 11:38:59Z mircea.markus $ */ @Test(groups = {"functional", "transaction"}, sequential = true, testName = "transaction.IsolationLevelReadCommittedTest") 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()); UnitTestCacheFactory instance = new UnitTestCacheFactory(); cache = instance.createCache(config, getClass()); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache); 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 { 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); tm.rollback(); } catch (AssertionError e) { writerError = e; } catch (Throwable t) { t.printStackTrace(); writerFailed = true; } finally { 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 { 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 { 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; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/isolationlevels/0000755000175000017500000000000011675221430030127 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/isolationlevels/NoneTest.java0000644000175000017500000000071111111047320032515 0ustar moellermoellerpackage 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"}, testName = "transaction.isolationlevels.NoneTest") public class NoneTest extends IsolationLevelTestBase { public NoneTest() { isolationLevel = IsolationLevel.NONE; } } ././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/isolationlevels/RepeatableReadTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/isolationlevels/RepeatableReadTes0000644000175000017500000000074111111047320033355 0ustar moellermoellerpackage 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", testName = "transaction.isolationlevels.RepeatableReadTest") public class RepeatableReadTest extends IsolationLevelTestBase { public RepeatableReadTest() { isolationLevel = IsolationLevel.REPEATABLE_READ; } } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/isolationlevels/ReadCommittedTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/isolationlevels/ReadCommittedTest0000644000175000017500000000073511111047320033425 0ustar moellermoellerpackage 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", testName = "transaction.isolationlevels.ReadCommittedTest") public class ReadCommittedTest extends IsolationLevelTestBase { public ReadCommittedTest() { isolationLevel = IsolationLevel.READ_COMMITTED; } } ././@LongLink0000000000000000000000000000015700000000000011570 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/isolationlevels/IsolationLevelTestBase.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/isolationlevels/IsolationLevelTes0000644000175000017500000002420011132625723033456 0ustar moellermoellerpackage org.jboss.cache.transaction.isolationlevels; import org.jboss.cache.Cache; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.lock.IsolationLevel; import static org.jboss.cache.lock.IsolationLevel.*; import org.jboss.cache.transaction.TransactionSetup; 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.Collection; import java.util.HashSet; /** * Base class for testing isolation levels. * * @author Manik Surtani * @since 2.0.0 */ @Test(groups = {"functional", "transaction"}, testName = "isolationlevels.IsolationLevelTestBase") 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() { UnitTestCacheFactory cf = new UnitTestCacheFactory(); cache = cf.createCache(false, getClass()); cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); 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 } } TestingUtil.killCaches(cache); 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-3.2.8.GA/src/test/java/org/jboss/cache/transaction/isolationlevels/ReadUncommittedTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/isolationlevels/ReadUncommittedTe0000644000175000017500000000074511111047320033422 0ustar moellermoellerpackage 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", testName = "transaction.isolationlevels.ReadUncommittedTest") public class ReadUncommittedTest extends IsolationLevelTestBase { public ReadUncommittedTest() { isolationLevel = IsolationLevel.READ_UNCOMMITTED; } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/isolationlevels/SerializableTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/isolationlevels/SerializableTest.0000644000175000017500000000073011111047320033363 0ustar moellermoellerpackage 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", testName = "transaction.isolationlevels.SerializableTest") public class SerializableTest extends IsolationLevelTestBase { public SerializableTest() { isolationLevel = IsolationLevel.SERIALIZABLE; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/transaction/SuspendTxTest.java0000644000175000017500000000514211132625723030356 0ustar moellermoellerpackage org.jboss.cache.transaction; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; 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; /** * Based on a contribution by Owen Taylor * * @author otaylor@redhat.com * @author Manik Surtani (manik AT jboss DOT org) */ @Test(groups = {"functional", "transaction"}, sequential = true, testName = "transaction.SuspendTxTest") public class SuspendTxTest { CacheSPI cache; TransactionManager mgr; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { UnitTestCacheFactory instance = new UnitTestCacheFactory(); cache = (CacheSPI) instance.createCache(false, getClass()); cache.getConfiguration().setCacheMode("local"); cache.getConfiguration().setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); cache.start(); mgr = cache.getTransactionManager(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache); 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-3.2.8.GA/src/test/java/org/jboss/cache/statetransfer/0000755000175000017500000000000011675221435025260 5ustar moellermoeller././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/statetransfer/StateTransferCompatibilityTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/statetransfer/StateTransferCompatibilityTest.0000644000175000017500000000750411142423305033433 0ustar moellermoellerpackage org.jboss.cache.statetransfer; import org.testng.annotations.Test; @Test(groups = {"functional"}, testName = "statetransfer.StateTransferCompatibilityTest") 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); } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/statetransfer/PersistingTransientStateTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/statetransfer/PersistingTransientStateTest.ja0000644000175000017500000001001011142423305033431 0ustar moellermoellerpackage org.jboss.cache.statetransfer; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.loader.testloaders.DummyInMemoryCacheLoader; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.Test; /** * Transient state SHOULD be persisted on the receiving cache IF fetchPersistentState is FALSE and the cache loader * is NOT shared. *

* Needs to be tested with PL, OL and MVCC. *

* Pertains to JBCACHE-131 *

*/ @Test(groups = "functional", testName = "statetransfer.PersistingTransientStateTest") public class PersistingTransientStateTest { protected NodeLockingScheme nls = NodeLockingScheme.PESSIMISTIC; protected Fqn fqn = Fqn.fromString("/a/b/c"); protected String k = "k", v = "v"; public void testPersistentStateTransfer() throws Exception { Cache c1 = null, c2 = null; try { UnitTestCacheFactory cf = new UnitTestCacheFactory(); Configuration cfg = UnitTestConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC); cfg.setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cfg.setFetchInMemoryState(true); // configure with CL IndividualCacheLoaderConfig iclc = new IndividualCacheLoaderConfig(); iclc.setClassName(DummyInMemoryCacheLoader.class.getName()); iclc.setFetchPersistentState(false); CacheLoaderConfig clc = new CacheLoaderConfig(); clc.addIndividualCacheLoaderConfig(iclc); cfg.setCacheLoaderConfig(clc); cfg.setNodeLockingScheme(nls); c1 = cf.createCache(cfg.clone(), getClass()); c1.put(fqn, k, v); assert c1.get(fqn, k).equals(v); assert getLoader(c1).get(fqn).get(k).equals(v); c2 = cf.createCache(cfg.clone(), getClass()); assert c2.get(fqn, k).equals(v); assert getLoader(c2).get(fqn).get(k).equals(v); } finally { TestingUtil.killCaches(c1, c2); } } public void testPersistentStateTransferShared() throws Exception { Cache c1 = null, c2 = null; try { Configuration cfg = UnitTestConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC); cfg.setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cfg.setFetchInMemoryState(true); // configure with CL IndividualCacheLoaderConfig iclc = new IndividualCacheLoaderConfig(); iclc.setClassName(DummyInMemoryCacheLoader.class.getName()); iclc.setFetchPersistentState(false); CacheLoaderConfig clc = new CacheLoaderConfig(); clc.addIndividualCacheLoaderConfig(iclc); clc.setShared(true); // even though it isn't really a shared CL cfg.setCacheLoaderConfig(clc); cfg.setNodeLockingScheme(nls); c1 = new UnitTestCacheFactory().createCache(cfg.clone(), getClass()); c1.put(fqn, k, v); assert c1.get(fqn, k).equals(v); assert getLoader(c1).get(fqn).get(k).equals(v); c2 = new UnitTestCacheFactory().createCache(cfg.clone(), getClass()); assert c2.get(fqn, k).equals(v); assert getLoader(c2).get(fqn) == null; } finally { TestingUtil.killCaches(c1, c2); } } protected CacheLoader getLoader(Cache c) { return ((CacheSPI) c).getCacheLoaderManager().getCacheLoader(); } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/statetransfer/StateTransferConcurrencyTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/statetransfer/StateTransferConcurrencyTest.ja0000644000175000017500000005164611143073554033445 0ustar moellermoeller/* * 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.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.RegionImpl; import org.jboss.cache.eviction.LRUAlgorithmConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.factories.UnitTestConfigurationFactory; 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.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", testName = "statetransfer.StateTransferConcurrencyTest") public class StateTransferConcurrencyTest extends StateTransferTestBase { protected String getReplicationVersion() { return "3.0.0.GA"; } /** * 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); } /** * //todo - create a mvn profile and allow tests to run on more than 2 caches * Starts 2 caches and then concurrently activates the same region under * all 2, causing each to attempt a partial state transfer from the other. * 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 cache 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. */ private void concurrentActivationTest(boolean sync) { String[] names = {"A", "B"}; int count = names.length; CacheActivator[] activators = new CacheActivator[count]; long start = System.currentTimeMillis(); 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[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 while (semaphore.availablePermits() != 0) TestingUtil.sleepThread(100); // 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); } System.out.println("System.currentTimeMillis()-st = " + (System.currentTimeMillis()-start)); // 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)); } } } 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(5000); } } /** * 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 cacheCount = names.length; int regionsToActivate = 3; int sleepTimeBetweenNodeStarts = 1000; StaggeredWebDeployerActivator[] activators = new StaggeredWebDeployerActivator[cacheCount]; try { // Create a semaphore and take all its tickets Semaphore semaphore = new Semaphore(cacheCount); semaphore.acquire(cacheCount); // Create activation threads that will block on the semaphore CacheSPI[] caches = new CacheSPI[cacheCount]; for (int i = 0; i < cacheCount; 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 < cacheCount; 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(cacheCount, caches); } // Ensure the caches held by the activators see all the values for (int i = 0; i < cacheCount; 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 < cacheCount; 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"}; int count = names.length; CacheStressor[] stressors = new CacheStressor[count]; try { // The first cache we create is inactivated. CacheSPI cacheA = createCache(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(); 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) { cacheA.getRegion(Fqn.fromString("/" + stressor.getName()), true).activate(); stressor.stopPuts(); // 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 = UnitTestConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC, false); additionalConfiguration(c); Cache cache1 = new UnitTestCacheFactory().createCache(c, getClass()); caches.put("evict1", cache1); cache1.put(Fqn.fromString("/a/b/c"), "key", "value"); c = UnitTestConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC, true); additionalConfiguration(c); c.getEvictionConfig().setWakeupInterval(-1); Cache cache2 = new UnitTestCacheFactory().createCache(c, getClass()); caches.put("evict2", cache2); RegionImpl region = (RegionImpl) cache2.getRegion(Fqn.ROOT, false); // We expect a VISIT event for / and ADD events for /a, /a/b and /a/b/c int nodeEventQueueSize = region.getEvictionEventQueue().size(); assertTrue("Saw the expected number of node events", nodeEventQueueSize >= 4); //one event happens on read root } /** * Further test for JBCACHE-913 */ public void testEvictionAfterStateTransfer() throws Exception { Configuration c = UnitTestConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC, false); additionalConfiguration(c); Cache cache1 = new UnitTestCacheFactory().createCache(c, getClass()); caches.put("evict1", cache1); for (int i = 0; i < 10; i++) { cache1.put(Fqn.fromString("/org/jboss/test/data/" + i), "key", "data" + i); } assert cache1.getRoot().getChild(Fqn.fromString("/org/jboss/test/data/")).getChildren().size() == 10; c = UnitTestConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC, true); c.getEvictionConfig().setWakeupInterval(-1); EvictionRegionConfig evictionRegionConfig = c.getEvictionConfig().getEvictionRegionConfig("/org/jboss/test/data"); LRUAlgorithmConfig evictionAlgorithmConfig = (LRUAlgorithmConfig) evictionRegionConfig.getEvictionAlgorithmConfig(); evictionAlgorithmConfig.setTimeToLive(-1); additionalConfiguration(c); final Cache cache2 = new UnitTestCacheFactory().createCache(c, getClass()); EvictionController ec2 = new EvictionController(cache2); caches.put("evict2", cache2); assert cache2.getRoot().getChild(Fqn.fromString("/org/jboss/test/data/")).getChildren().size() == 10; ec2.startEviction(); assert cache2.getRoot().getChild(Fqn.fromString("/org/jboss/test/data/")).getChildren().size() == 5; } private class CacheActivator extends CacheUser { CacheActivator(Semaphore semaphore, String name, boolean sync) throws Exception { super(semaphore, name, sync, false, 120000); } @SuppressWarnings("unchecked") void useCache() throws Exception { TestingUtil.sleepRandom(500); createAndActivateRegion(cache, A_B); 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"); } } 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; 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++; } 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(); } } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/statetransfer/StateTransferTestBase.java0000644000175000017500000004075611142423305032344 0ustar moellermoeller/* * 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.Fqn; import org.jboss.cache.Region; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.loader.FileCacheLoader; import org.jboss.cache.marshall.SelectedClassnameClassLoader; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.UnitTestDatabaseManager; 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: 7646 $ */ @Test(groups = {"functional"}, testName = "statetransfer.StateTransferTestBase") public abstract class StateTransferTestBase { 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 = new HashMap(); ClassLoader orig_TCL; private static int cacheCount = 0; protected abstract String getReplicationVersion(); protected CacheSPI createCache(boolean sync, boolean useMarshalling, boolean useCacheLoader) throws Exception { return createCache(sync, useMarshalling, useCacheLoader, false, true, true); } protected CacheSPI createCache(boolean sync, boolean useMarshalling, boolean useCacheLoader, boolean fetchPersistentState) throws Exception { return createCache(sync, useMarshalling, useCacheLoader, false, true, fetchPersistentState); } protected CacheSPI createCache(boolean sync, boolean useMarshalling, boolean useCacheLoader, boolean cacheLoaderAsync, boolean startCache, boolean fetchPersistentState) throws Exception { if (useCacheLoader) { return createCache(sync, useMarshalling, getDefaultCacheLoader(), cacheLoaderAsync, startCache, fetchPersistentState); } else { return createCache(sync, useMarshalling, null, cacheLoaderAsync, startCache, fetchPersistentState); } } protected CacheSPI createCache(boolean sync, boolean useMarshalling, String cacheLoaderClass, boolean cacheLoaderAsync, boolean startCache, boolean fetchPersistentState) throws Exception { String cacheID = getNextUniqueCacheName(); if (caches.get(cacheID) != null) { throw new IllegalStateException(cacheID + " already created"); } CacheMode mode = sync ? CacheMode.REPL_SYNC : CacheMode.REPL_ASYNC; Configuration c = UnitTestConfigurationFactory.createConfiguration(mode); if (sync) { c.setSyncRollbackPhase(true); c.setSyncCommitPhase(true); } 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); } additionalConfiguration(c); CacheSPI tree = (CacheSPI) new UnitTestCacheFactory().createCache(c, false, getClass()); 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.start(); } return tree; } protected void additionalConfiguration(Configuration c) { // to be overridden c.setNodeLockingScheme(NodeLockingScheme.PESSIMISTIC); } 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, getDefaultCacheLoader(), 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 = UnitTestDatabaseManager.getTestDbProperties(); CacheLoaderConfig clc = UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", "org.jboss.cache.loader.JDBCCacheLoader", prop, false, true, false, false, false); clc.getFirstCacheLoaderConfig().setPurgeOnStartup(true); c.setCacheLoaderConfig(clc); } else if (cacheloaderClass.equals(FileCacheLoader.class.getName())) { String tmpLocation = getTempLocation(cacheID); File file = new File(tmpLocation); cleanFile(file); file.mkdir(); tmpLocation = escapeWindowsPath(tmpLocation); String props = "location = " + tmpLocation + "\n"; c.setCacheLoaderConfig(UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", cacheloaderClass, props, async, fetchPersistentState, false, false, false)); } else { assert !cacheloaderClass.equals(FileCacheLoader.class.getName()); c.setCacheLoaderConfig(UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", cacheloaderClass, "", async, fetchPersistentState, false, false, false)); } } } protected void initialStateTferWithLoaderTest(String cacheLoaderClass1, String cacheLoaderClass2, boolean asyncLoader) throws Exception { CacheSPI cache1 = createCache(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(false, false, cacheLoaderClass2, asyncLoader, true, true); // Pause to give caches time to see each other TestingUtil.blockUntilViewsReceived(new CacheSPI[]{cache1, cache2}, 60000); if (asyncLoader) TestingUtil.sleepThread(100); CacheLoader loader = cache2.getCacheLoaderManager().getCacheLoader(); long start = System.currentTimeMillis(); while (asyncLoader && System.currentTimeMillis() - start < 10000) { try { doAssertion(cache2, loader); break; } catch (Throwable ae) { //allow this within the timeout } } doAssertion(cache2, loader); } private void doAssertion(CacheSPI cache2, CacheLoader loader) throws Exception { 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 = TestingUtil.TEST_FILES; 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 { // 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 } } // repeat. Make sure everything is properly STOPPED!!! for (Cache c : caches.values()) { TestingUtil.killCaches(c); } } protected void stopCache(Cache cache) { if (cache != null) { try { TestingUtil.killCaches(cache); } catch (Exception e) { 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(sync, true, false); this.semaphore = semaphore; this.name = name; if (activateRoot) { cache.getRegion(Fqn.ROOT, true).activate(); } } CacheUser(Semaphore semaphore, String name, boolean sync, boolean activateRoot, long stateRetrievalTimeout) throws Exception { cache = createCache(sync, true, false, false, false, true); cache.getConfiguration().setStateRetrievalTimeout(stateRetrievalTimeout); cache.start(); 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"); } useCache(); } catch (Exception e) { 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(); } } } protected String getDefaultCacheLoader() { return org.jboss.cache.loader.testloaders.DummyInMemoryCacheLoader.class.getName(); } private String getNextUniqueCacheName() { return getClass().getSimpleName() + cacheCount++; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/statetransfer/NBSTCacheLoaderTest.java0000644000175000017500000000664711254446772031627 0ustar moellermoellerpackage org.jboss.cache.statetransfer; import org.jboss.cache.CacheSPI; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.loader.testloaders.DummySharedInMemoryCacheLoaderConfig; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNull; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import java.io.IOException; @Test(groups = "functional", testName = "statetransfer.NBSTCacheLoaderTest", enabled = true) public class NBSTCacheLoaderTest extends NonBlockingStateTransferTest { int id; ThreadLocal sharedCacheLoader = new ThreadLocal() { protected Boolean initialValue() { return false; } }; @AfterMethod public void resetThreadLocal() { sharedCacheLoader.set(false); } @Override protected CacheSPI createCache(String name, boolean start) throws IOException { CacheSPI c = super.createCache(name, false); CacheLoaderConfig clmc = new CacheLoaderConfig(); DummySharedInMemoryCacheLoaderConfig clc = new DummySharedInMemoryCacheLoaderConfig("store number " + id++); clc.setFetchPersistentState(true); clc.setProperties("debug=true"); clmc.setShared(sharedCacheLoader.get()); clmc.addIndividualCacheLoaderConfig(clc); c.getConfiguration().setCacheLoaderConfig(clmc); if (start) c.start(); return c; } @Override protected void writeInitialData(final CacheSPI cache) { super.writeInitialData(cache); cache.evict(A_B); cache.evict(A_C); cache.evict(A_D); } protected void verifyInitialDataOnLoader(CacheSPI c) throws Exception { CacheLoader l = TestingUtil.getCacheLoader(c); assertEquals("Incorrect name for /a/b on loader", JOE, l.get(A_B).get("name")); assertEquals("Incorrect age for /a/b on loader", TWENTY, l.get(A_B).get("age")); assertEquals("Incorrect name for /a/c on loader", BOB, l.get(A_C).get("name")); assertEquals("Incorrect age for /a/c on loader", FORTY, l.get(A_C).get("age")); } protected void verifyNoData(CacheSPI c) { assert c.getRoot().getChildrenNames().isEmpty(): "Cache should be empty!"; } protected void verifyNoDataOnLoader(CacheSPI c) throws Exception { CacheLoader l = TestingUtil.getCacheLoader(c); assertNull("Node /a/b should not exist on loader", l.get(A_B)); assertNull("Node /a/c should not exist on loader", l.get(A_C)); assertNull("Node /a/d should not exist on loader", l.get(A_D)); } public void testSharedLoader() throws Exception { sharedCacheLoader.set(true); CacheSPI c1=null, c2= null; try { c1 = createCache("testSharedLoader", true); writeInitialData(c1); // starting the second cache would initialize an in-memory state transfer but not a persistent one since the loader is shared c2 = createCache("testSharedLoader", true); TestingUtil.blockUntilViewsReceived(60000, c1, c2); verifyInitialDataOnLoader(c1); verifyInitialData(c1); verifyNoDataOnLoader(c2); verifyNoData(c2); } finally { TestingUtil.killCaches(c1, c2); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/statetransfer/CorruptedFileCacheLoader.java0000644000175000017500000000170111111047320032724 0ustar moellermoellerpackage 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-3.2.8.GA/src/test/java/org/jboss/cache/statetransfer/StateTransferUnderLoadTest.java0000644000175000017500000001256611142423305033345 0ustar moellermoellerpackage org.jboss.cache.statetransfer; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.transaction.DummyTransactionManager; import org.jboss.cache.util.TestingUtil; 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 7646 2009-02-04 23:37:41Z mircea.markus $ */ @Test(groups = {"functional"}, enabled = false, description = "Disabled because this test depends on JBCACHE-315 being resolved.", testName = "statetransfer.StateTransferUnderLoadTest") 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) { TestingUtil.killCaches(cache2); cache2 = null; } if (cache1 != null) { TestingUtil.killCaches(cache1); cache1 = null; } // We just can't kill DummyTransactionManager. We are sharing single instance in more tests. TestingUtil.killTransaction(DummyTransactionManager.getInstance()); /* 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 = UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.REPL_SYNC); cfg2 = UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.REPL_SYNC); 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 UnitTestCacheFactory().createCache(cfg1, true, getClass()); cache2 = new UnitTestCacheFactory().createCache(cfg2, false, getClass()); 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-3.2.8.GA/src/test/java/org/jboss/cache/statetransfer/StateTransfer200Test.java0000644000175000017500000004637511254446772032017 0ustar moellermoeller/* * 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.buddyreplication.BuddyFqnTransformer; import org.jboss.cache.buddyreplication.BuddyManager; import org.jboss.cache.buddyreplication.BuddyReplicationTestsBase; import org.jboss.cache.config.BuddyReplicationConfig; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.marshall.MarshalledValue; import org.jboss.cache.util.TestingUtil; 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: 8233 $ */ @Test(groups = {"functional"}, testName = "statetransfer.StateTransfer200Test", enabled = true) public class StateTransfer200Test extends StateTransferTestBase { protected String getReplicationVersion() { return "2.0.0.GA"; } public void testBuddyBackupExclusion() throws Exception { CacheSPI cache1 = createCacheWithBr("testBuddyBackupExclusion_1"); 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 = createCacheWithBr("StateTransferTestBase_2"); // Pause to give caches time to see each other BuddyReplicationTestsBase.waitForSingleBuddy(cache1, cache2); 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 = createCacheWithBr("testBuddyIntegration_1"); // 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 = createCacheWithBr("testBuddyIntegration_2"); // Pause to give caches time to see each other BuddyReplicationTestsBase.waitForSingleBuddy(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(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(false, false, true, false, false, true); 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(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(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(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(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(getDefaultCacheLoader(), getDefaultCacheLoader() , asyncLoader); } public void testPartialStateTransfer() throws Exception { CacheSPI cache1 = createCache(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(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 testLocksAndStateTransfer() throws Exception { CacheSPI cache1 = createCache(false, true, false); createAndActivateRegion(cache1, A); cache1.put(A_B, "name", JOE); CacheSPI cache2 = createCache(false, true, false); // Pause to give caches time to see each other TestingUtil.blockUntilViewsReceived(new CacheSPI[]{cache1, cache2}, 60000); createAndActivateRegion(cache2, A_B); assertEquals("Incorrect name for /a/b", JOE, cache2.get(A_B, "name")); assert cache1.getNumberOfLocksHeld() == 0; cache1.getRegion(A, false).deactivate(); assert cache1.getNumberOfLocksHeld() == 0; createAndActivateRegion(cache1, A_B); assertEquals("Incorrect name for /a/b", JOE, cache1.get(A_B, "name")); } public void testPartialStateTferWithLoader() throws Exception { CacheSPI cache1 = createCache(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(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( 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( 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(true, false, true, true); 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(true, false, true, true); c2.put(B, "K", "V"); assert c1.getConfiguration().isFetchInMemoryState(); assert c1.getConfiguration().getCacheLoaderConfig().isFetchPersistentState(); c1.start(); assert c1.get(B, "K").equals("V"); assert c1.get(A, "K") == null; } 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; } private CacheSPI createCacheWithBr(String cacheName) throws Exception { CacheSPI cache1 = createCache(false, false, false, false, false, true); cache1.getConfiguration().setBuddyReplicationConfig(getBuddyConfig()); cache1.start(); return cache1; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/statetransfer/ForcedStateTransferTest.java0000644000175000017500000006202311142423305032663 0ustar moellermoeller/* * 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: 7646 $ */ @Test(groups = {"functional"}, enabled = false, testName = "statetransfer.ForcedStateTransferTest", description = "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; 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(replSync, useMarshalling, false, false, false, true); result.getConfiguration().setStateRetrievalTimeout(0); result.getConfiguration().setLockAcquisitionTimeout(1000); result.getConfiguration().setIsolationLevel(isolationLevel); if (startCache) result.start(); return result; } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/statetransfer/NonBlockingStateTransferTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/statetransfer/NonBlockingStateTransferTest.ja0000644000175000017500000003314011256437261033347 0ustar moellermoeller/* * 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.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.util.CachePrinter; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.Test; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; @Test(groups="functional", testName = "statetransfer.NonBlockingStateTransferTest", enabled = true) public class NonBlockingStateTransferTest { 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; private volatile int testCount = 0; private static final Log log = LogFactory.getLog(NonBlockingStateTransferTest.class); public static class DelayTransfer implements Serializable { private transient int count; private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); } private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); // RPC is first serialization, ST is second if (count++ == 0) return; try { // This sleep is not required for the test to function, // however it improves the possibility of finding errors // (since it keeps the tx log going) Thread.sleep(2000); } catch (InterruptedException e) { } } } private static class WritingRunner implements Runnable { private final Cache cache; private final boolean tx; private volatile boolean stop; private volatile int end; private volatile int start = 0; private int cleanupRange = 1000; WritingRunner(Cache cache, boolean tx) { this.cache = cache; this.tx = tx; } WritingRunner(Cache cache, boolean tx, int cleanupRange) { this.cache = cache; this.tx = tx; this.cleanupRange = cleanupRange; } public synchronized int end() { return end; } public synchronized int start() { return start; } public void run() { end = 0; while (!stop) { try { if (end == cleanupRange) { startTxIfNeeded(); for (int i=0; i createCache(String name) throws IOException { return createCache(name, true); } protected CacheSPI createCache(String name, boolean start) throws IOException { Configuration config = UnitTestConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC); config.setSyncCommitPhase(true); config.setClusterName(name + "-" + Thread.currentThread().getName()); config.setNonBlockingStateTransfer(true); config.setSyncReplTimeout(30000); CacheSPI cache = (CacheSPI) new UnitTestCacheFactory().createCache(config, false, getClass()); cache.create(); if (start) cache.start(); return cache; } public void testInitialStateTransfer() throws Exception { testCount++; log.info("testInitialStateTransfer start - " + testCount); CacheSPI cache1 = null, cache2 = null; try { cache1 = createCache("nbst"); writeInitialData(cache1); cache2 = createCache("nbst"); // Pause to give caches time to see each other TestingUtil.blockUntilViewsReceived(new CacheSPI[] { cache1, cache2 }, 60000); verifyInitialData(cache2); } finally { TestingUtil.killCaches(cache1, cache2); } log.info("testInitialStateTransfer end - " + testCount); } public void testActivateRegionTransfer() throws Exception { testCount++; log.info("testActivateRegionTransfer start - " + testCount); CacheSPI cache1 = null, cache2 = null; try { cache1 = createCache("nbst", false); cache1.getConfiguration().setUseRegionBasedMarshalling(true); Region region = cache1.getRegion(Fqn.fromString("/region1"), true); region.registerContextClassLoader(getClass().getClassLoader()); cache1.start(); writeInitialData(cache1); cache2 = createCache("nbst", false); cache2.getConfiguration().setUseRegionBasedMarshalling(true); region = cache2.getRegion(Fqn.fromString("/region1"), true); region.registerContextClassLoader(getClass().getClassLoader()); cache2.start(); region.deactivate(); // Pause to give caches time to see each other TestingUtil.blockUntilViewsReceived(new CacheSPI[] { cache1, cache2 }, 60000); cache1.put("/region1/blah", "blah", "blah"); cache1.put("/region1/blah2", "blah", "blah"); assertEquals(null, cache2.get("/region1/blah", "blah")); assertEquals(null, cache2.get("/region1/blah2", "blah")); region.activate(); assertEquals("blah", cache2.get("/region1/blah", "blah")); assertEquals("blah", cache2.get("/region1/blah2", "blah")); verifyInitialData(cache2); } finally { TestingUtil.killCaches(cache1, cache2); } log.info("testActivateRegionTransfer end - " + testCount); } public void testConcurrentStateTransfer() throws Exception { testCount++; log.info("testConcurrentStateTransfer start - " + testCount); CacheSPI cache1 = null, cache2 = null, cache3 = null, cache4 = null; try { cache1 = createCache("nbst"); writeInitialData(cache1); cache2 = createCache("nbst"); cache1.put("/delay", "delay", new DelayTransfer()); // Pause to give caches time to see each other TestingUtil.blockUntilViewsReceived(new CacheSPI[] { cache1, cache2 }, 60000); verifyInitialData(cache2); final CacheSPIc3 = cache3 = createCache("nbst", false); final CacheSPIc4 = cache4 = createCache("nbst", false); Thread t1 = new Thread(new Runnable() { public void run() { c3.start(); } }); t1.start(); Thread t2 = new Thread(new Runnable() { public void run() { c4.start(); } }); t2.start(); t1.join(); t2.join(); TestingUtil.blockUntilViewsReceived(new CacheSPI[] { cache1, cache2, cache3, cache4 }, 60000); verifyInitialData(cache3); verifyInitialData(cache4); } finally { TestingUtil.killCaches(cache1, cache2, cache3, cache4); } log.info("testConcurrentStateTransfer end - " + testCount); } public void testSTWithThirdWritingNonTxCache() throws Exception { testCount++; log.info("testSTWithThirdWritingNonTxCache start - " + testCount); thirdWritingCacheTest(false, "nbst1"); log.info("testSTWithThirdWritingNonTxCache end - " + testCount); } public void testSTWithThirdWritingTxCache() throws Exception { testCount++; log.info("testSTWithThirdWritingTxCache start - " + testCount); thirdWritingCacheTest(true, "nbst2"); log.info("testSTWithThirdWritingTxCache end - " + testCount); } public void testSTWithWritingNonTxThread() throws Exception { testCount++; log.info("testSTWithWritingNonTxThread start - " + testCount); writingThreadTest(false, "nbst3"); log.info("testSTWithWritingNonTxThread end - " + testCount); } public void testSTWithWritingTxThread() throws Exception { testCount++; log.info("testSTWithWritingTxThread start - " + testCount); writingThreadTest(true, "nbst4"); log.info("testSTWithWritingTxThread end - " + testCount); } private void thirdWritingCacheTest(boolean tx, String name) throws InterruptedException, IOException { CacheSPI cache1 = null, cache2 = null, cache3 = null; try { cache1 = createCache(name); writeInitialData(cache1); cache3 = createCache(name); TestingUtil.blockUntilViewsReceived(new CacheSPI[] { cache1, cache3 }, 10000); verifyInitialData(cache3); // Delay the transient copy, so that we get a more thorough log test cache1.put("/delay", "delay", new DelayTransfer()); WritingRunner writer = new WritingRunner(cache3, tx); Thread writerThread = new Thread(writer); writerThread.setDaemon(true); writerThread.start(); cache2 = createCache(name); // Pause to give caches time to see each other TestingUtil.blockUntilViewsReceived(new CacheSPI[] { cache1, cache2, cache3 }, 120000); // this could take a while since WritingRunner is creating a lot of stuff writer.stop(); writerThread.interrupt(); writerThread.join(); verifyInitialData(cache2); int end = writer.end(); int start = writer.start(); log.trace("Cache content is: " + CachePrinter.printCacheDetails(cache2)); for (int c = start; c < end; c++) assertEquals(c, cache2.get("/test" + c, "test")); } finally { TestingUtil.killCaches(cache1, cache2, cache3); } } protected void verifyInitialData(CacheSPI cache2) { 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 void writeInitialData(final CacheSPI cache1) { cache1.put(A_B, "name", JOE); cache1.put(A_B, "age", TWENTY); cache1.put(A_C, "name", BOB); cache1.put(A_C, "age", FORTY); } private void writingThreadTest(boolean tx, String name) throws InterruptedException, IOException { CacheSPI cache1 = null, cache2 = null; try { cache1 = createCache(name); writeInitialData(cache1); // Delay the transient copy, so that we get a more thorough log test cache1.put("/delay", "delay", new DelayTransfer()); WritingRunner writer = new WritingRunner(cache1, tx, 500); Thread writerThread = new Thread(writer); writerThread.setDaemon(true); writerThread.start(); cache2 = createCache(name); // Pause to give caches time to see each other TestingUtil.blockUntilViewsReceived(new CacheSPI[] { cache1, cache2 }, 60000); writer.stop(); writerThread.interrupt(); writerThread.join(); verifyInitialData(cache2); int start = writer.start(); int end = writer.end(); for (int c = start; c < end; c++) assertEquals(c, cache2.get("/test" + c, "test")); } finally { TestingUtil.killCaches(cache1, cache2); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/statetransfer/FailedStateTransferTest.java0000644000175000017500000001133711142423305032647 0ustar moellermoeller/* * 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.Version; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.factories.annotations.NonVolatile; import org.jboss.cache.lock.TimeoutException; import org.jboss.cache.remoting.jgroups.ChannelMessageListener; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.fail; import org.testng.annotations.Test; import java.io.InputStream; import java.util.Map; import org.jboss.cache.Cache; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; /** * A FailedStateTransferTest. * * @author Brian Stansberry * @version $Revision: 7646 $ */ @Test(groups = {"functional"}, testName = "statetransfer.FailedStateTransferTest") public class FailedStateTransferTest extends StateTransferTestBase { public void testFailedStateTransfer() throws Exception { Configuration c = UnitTestConfigurationFactory.createConfiguration(CacheMode.REPL_ASYNC); //c.setClusterName("VersionedTestBase"); c.setReplVersionString(getReplicationVersion()); // Use a long timeout to facilitate setting debugger breakpoints c.setStateRetrievalTimeout(60000); CacheSPI cache = (CacheSPI) new UnitTestCacheFactory().createCache(c, false, getClass()); // 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(); c= UnitTestConfigurationFactory.createConfiguration(CacheMode.REPL_ASYNC); //c.setClusterName("VersionedTestBase"); c.setReplVersionString(getReplicationVersion()); // Use a long timeout to facilitate setting debugger breakpoints c.setStateRetrievalTimeout(60000); CacheSPI recipient = (CacheSPI) new UnitTestCacheFactory().createCache(c, false, getClass()); //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-3.2.8.GA/src/test/java/org/jboss/cache/TreeCacheFunctionalTest.java0000755000175000017500000000635711120367722027755 0ustar moellermoellerpackage 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 7284 2008-12-12 05:00:02Z mircea.markus $ */ @Test(groups = "functional", sequential = true, testName = "TreeCacheFunctionalTest") public class TreeCacheFunctionalTest { CacheSPI cache = null; final Fqn FQN = Fqn.fromString("/myNode"); @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache = (CacheSPI) new UnitTestCacheFactory().createCache(false, getClass()); 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-3.2.8.GA/src/test/java/org/jboss/cache/mvcc/0000755000175000017500000000000011675221436023324 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/mvcc/MVCCFullStackTest.java0000644000175000017500000000445711120367722027374 0ustar moellermoellerpackage org.jboss.cache.mvcc; import org.jboss.cache.CacheSPI; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.interceptors.MVCCLockingInterceptor; import org.jboss.cache.lock.IsolationLevel; import static org.jboss.cache.lock.IsolationLevel.*; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; /** * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ @Test(groups = "functional", sequential = true, testName = "mvcc.MVCCFullStackTest") public class MVCCFullStackTest { CacheSPI cache; @AfterMethod public void tearDown() { TestingUtil.killCaches(cache); cache = null; } public void testDefaultConfiguration() { cache = (CacheSPI) new UnitTestCacheFactory().createCache(getClass()); assert TestingUtil.findInterceptor(cache, MVCCLockingInterceptor.class) != null : "MVCC interceptor should be in stack"; assert cache.getConfiguration().getNodeLockingScheme() == NodeLockingScheme.MVCC; } public void testIsolationLevelUpgrade1() { isoLevelTest(NONE, READ_COMMITTED); } public void testIsolationLevelUpgrade2() { isoLevelTest(READ_UNCOMMITTED, READ_COMMITTED); } public void testIsolationLevelDowngrade() { isoLevelTest(SERIALIZABLE, REPEATABLE_READ); } public void testIsolationLevelNoUpgrade1() { isoLevelTest(READ_COMMITTED, READ_COMMITTED); } public void testIsolationLevelNoUpgrade2() { isoLevelTest(REPEATABLE_READ, REPEATABLE_READ); } private void isoLevelTest(IsolationLevel configuredWith, IsolationLevel expected) { Configuration c = new Configuration(); c.setNodeLockingScheme(NodeLockingScheme.MVCC); c.setIsolationLevel(configuredWith); cache = (CacheSPI) new UnitTestCacheFactory().createCache(c, getClass()); assert cache.getConfiguration().getIsolationLevel() == expected : "Expected to change isolation level from " + configuredWith + " to " + expected + " but was " + cache.getConfiguration().getIsolationLevel(); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/mock/0000755000175000017500000000000011675221436023325 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/mock/MockNodesFixture.java0000644000175000017500000000422611111047320027404 0ustar moellermoellerpackage 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-3.2.8.GA/src/test/java/org/jboss/cache/mock/NodeSpiMock.java0000644000175000017500000002271311160132061026330 0ustar moellermoellerpackage org.jboss.cache.mock; import org.jboss.cache.CacheSPI; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.InternalNode; import org.jboss.cache.InvocationContext; 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 boolean containsKeyDirect(Object key) { return data != null && data.containsKey(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 getParentDirect() { return parent; } public Node 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 boolean isNullNode() { throw new UnsupportedOperationException(); } public void markForUpdate(DataContainer container, boolean writeSkewCheck) { throw new UnsupportedOperationException(); } public void commitUpdate(InvocationContext ctx, DataContainer container) { throw new UnsupportedOperationException(); } public boolean isChanged() { throw new UnsupportedOperationException(); } public boolean isCreated() { throw new UnsupportedOperationException(); } public InternalNode getDelegationTarget() { throw new UnsupportedOperationException(); } public void setCreated(boolean created) { throw new UnsupportedOperationException(); } public void rollbackUpdate() { throw new UnsupportedOperationException(); } 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) { } public boolean isLeaf() { return false; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/options/0000755000175000017500000000000011675221444024066 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/options/ForceWriteLockTest.java0000644000175000017500000001104611132625723030451 0ustar moellermoellerpackage org.jboss.cache.options; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.NodeSPI; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.lock.LockManager; import org.jboss.cache.lock.LockType; import org.jboss.cache.transaction.DummyTransactionManagerLookup; 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", "pessimistic"}, sequential = true, testName = "options.ForceWriteLockTest") public class ForceWriteLockTest { protected CacheSPI cache; private Fqn fqn = Fqn.fromString("/a/b"); private TransactionManager tm; protected NodeLockingScheme nodeLockingScheme = NodeLockingScheme.PESSIMISTIC; @BeforeMethod(alwaysRun = true) public void setUp() { Configuration c = new Configuration(); c.setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); c.setNodeLockingScheme(nodeLockingScheme); UnitTestCacheFactory instance = new UnitTestCacheFactory(); cache = (CacheSPI) instance.createCache(c, getClass()); tm = cache.getTransactionManager(); } @AfterMethod(alwaysRun = true) public void tearDown() { TestingUtil.killCaches(cache); cache = null; } 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(); } protected void assertNotLocked(Fqn fqn) { assert !TestingUtil.extractLockManager(cache).isLocked(cache.peek(fqn, true)) : "Node " + fqn + " is locked!!"; } protected 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-3.2.8.GA/src/test/java/org/jboss/cache/options/cachemodelocal/0000755000175000017500000000000011675221441027006 5ustar moellermoeller././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/options/cachemodelocal/AsyncReplOptLocksTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/options/cachemodelocal/AsyncReplOptLocksTest.0000644000175000017500000000142711111047320033215 0ustar moellermoeller/* * 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.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.CacheSPI; import org.testng.annotations.Test; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNull; @Test(groups = {"functional", "jgroups"}, testName = "options.cachemodelocal.AsyncReplOptLocksTest") public class AsyncReplOptLocksTest extends CacheModeLocalTestBase { public AsyncReplOptLocksTest() { cacheMode = Configuration.CacheMode.REPL_ASYNC; nodeLockingScheme = "OPTIMISTIC"; } } ././@LongLink0000000000000000000000000000016100000000000011563 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/options/cachemodelocal/SyncInvalidationPessLocksTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/options/cachemodelocal/SyncInvalidationPessLo0000644000175000017500000000462011120367722033335 0ustar moellermoeller/* * 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.*; import org.testng.annotations.Test; import static org.testng.AssertJUnit.assertEquals; @Test(groups = {"functional", "jgroups"}, testName = "options.cachemodelocal.SyncInvalidationPessLocksTest") public class SyncInvalidationPessLocksTest extends CacheModeLocalTestBase { public SyncInvalidationPessLocksTest() { cacheMode = Configuration.CacheMode.INVALIDATION_SYNC; nodeLockingScheme = "PESSIMISTIC"; isInvalidation = true; } public void testMoveInvalidations() throws Exception { Node rootNode = cache1.getRoot(); Node nodeA = rootNode.addChild(Fqn.fromString("/a")); Node nodeB = nodeA.addChild(Fqn.fromString("/b")); nodeA.put("key", "valueA"); nodeB.put("key", "valueB"); assertEquals("valueA", cache1.getRoot().getChild(Fqn.fromString("/a")).get("key")); assertEquals("valueB", cache1.getRoot().getChild(Fqn.fromString("/a")).getChild(Fqn.fromString("/b")).get("key")); assertInvalidated(cache2, Fqn.fromString("/a"), "Should be invalidated"); assertInvalidated(cache2, Fqn.fromRelativeElements(Fqn.fromString("/a"), Fqn.fromString("/b").getLastElement()), "Should be invalidated"); // now move... cache1.move(nodeB.getFqn(), Fqn.ROOT); assertEquals("valueA", cache1.getRoot().getChild(Fqn.fromString("/a")).get("key")); assertEquals("valueB", cache1.getRoot().getChild(Fqn.fromString("/b")).get("key")); assertInvalidated(cache2, Fqn.fromString("/a"), "Should be invalidated"); assertInvalidated(cache2, Fqn.fromString("/b"), "Should be invalidated"); // now make sure a node exists on cache 2 cache2.getRoot().addChild(Fqn.fromString("/a")).put("k2", "v2"); // te invalidation will happen in afterCompletion, hence no exception! try { cache1.move(Fqn.fromString("/b"), Fqn.fromString("/a"));// should throw an NPE } catch (Exception expected) { } } private void assertInvalidated(Cache cache, Fqn fqn, String msg) { assert cache.getRoot().getChild(fqn) == null : msg; NodeSPI n = ((CacheSPI) cache).peek(fqn, true, true); } } ././@LongLink0000000000000000000000000000016200000000000011564 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/options/cachemodelocal/AsyncInvalidationPessLocksTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/options/cachemodelocal/AsyncInvalidationPessL0000644000175000017500000000134711111047320033307 0ustar moellermoeller/* * 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.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.CacheSPI; import org.testng.annotations.Test; @Test(groups = "functional", testName = "options.cachemodelocal.AsyncInvalidationPessLocksTest") public class AsyncInvalidationPessLocksTest extends CacheModeLocalTestBase { public AsyncInvalidationPessLocksTest() { cacheMode = Configuration.CacheMode.INVALIDATION_ASYNC; nodeLockingScheme = "PESSIMISTIC"; isInvalidation = true; } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/options/cachemodelocal/SyncReplPessLocksTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/options/cachemodelocal/SyncReplPessLocksTest.0000644000175000017500000000122111111047320033214 0ustar moellermoeller/* * 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.commands.ReplicableCommand; import org.jboss.cache.CacheSPI; import org.testng.annotations.Test; @Test(groups = {"functional", "jgroups"}, testName = "options.cachemodelocal.SyncReplPessLocksTest") public class SyncReplPessLocksTest extends CacheModeLocalTestBase { public SyncReplPessLocksTest() { cacheMode = Configuration.CacheMode.REPL_SYNC; nodeLockingScheme = "PESSIMISTIC"; } } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/options/cachemodelocal/AsyncReplPessLocksTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/options/cachemodelocal/AsyncReplPessLocksTest0000644000175000017500000000264311111047320033310 0ustar moellermoeller/* * 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.internals.replicationlisteners.ReplicationListener; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.legacy.write.PessPutDataMapCommand; import org.jboss.cache.commands.legacy.write.PessPutKeyValueCommand; import org.jboss.cache.commands.legacy.write.PessRemoveKeyCommand; import org.jboss.cache.commands.legacy.write.PessRemoveNodeCommand; 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.CacheSPI; import org.jboss.cache.Cache; import org.testng.annotations.Test; import java.util.Map; import java.util.HashMap; @Test(groups = {"functional", "jgroups"}, testName = "options.cachemodelocal.AsyncReplPessLocksTest") public class AsyncReplPessLocksTest extends CacheModeLocalTestBase { ReplicationListener current; Map cache2Listener = new HashMap(2); public AsyncReplPessLocksTest() { cacheMode = Configuration.CacheMode.REPL_ASYNC; nodeLockingScheme = "PESSIMISTIC"; } } ././@LongLink0000000000000000000000000000016000000000000011562 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/options/cachemodelocal/SyncInvalidationOptLocksTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/options/cachemodelocal/SyncInvalidationOptLoc0000644000175000017500000000500611120367722033327 0ustar moellermoeller/* * 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.*; import org.testng.annotations.Test; import static org.testng.AssertJUnit.assertEquals; @Test(groups = {"functional", "jgroups"}, testName = "options.cachemodelocal.SyncInvalidationOptLocksTest") public class SyncInvalidationOptLocksTest extends CacheModeLocalTestBase { public SyncInvalidationOptLocksTest() { cacheMode = Configuration.CacheMode.INVALIDATION_SYNC; nodeLockingScheme = "OPTIMISTIC"; isInvalidation = true; } private void assertInvalidated(Cache cache, Fqn fqn, String msg) { assert cache.getRoot().getChild(fqn) == null : msg; NodeSPI n = ((CacheSPI) cache).peek(fqn, true, true); assert true : msg; assert !n.isValid() : msg; } public void testMoveInvalidations() throws Exception { Node rootNode = cache1.getRoot(); Node nodeA = rootNode.addChild(Fqn.fromString("/a")); Node nodeB = nodeA.addChild(Fqn.fromString("/b")); nodeA.put("key", "valueA"); nodeB.put("key", "valueB"); assertEquals("valueA", cache1.getRoot().getChild(Fqn.fromString("/a")).get("key")); assertEquals("valueB", cache1.getRoot().getChild(Fqn.fromString("/a")).getChild(Fqn.fromString("/b")).get("key")); assertInvalidated(cache2, Fqn.fromString("/a"), "Should be invalidated"); assertInvalidated(cache2, Fqn.fromRelativeElements(Fqn.fromString("/a"), Fqn.fromString("/b").getLastElement()), "Should be invalidated"); // now move... cache1.move(nodeB.getFqn(), Fqn.ROOT); assertEquals("valueA", cache1.getRoot().getChild(Fqn.fromString("/a")).get("key")); assertEquals("valueB", cache1.getRoot().getChild(Fqn.fromString("/b")).get("key")); assertInvalidated(cache2, Fqn.fromString("/a"), "Should be invalidated"); assertInvalidated(cache2, Fqn.fromString("/b"), "Should be invalidated"); // now make sure a node exists on cache 2 cache2.getRoot().addChild(Fqn.fromString("/a")).put("k2", "v2"); // te invalidation will happen in afterCompletion, hence no exception! try { cache1.move(Fqn.fromString("/b"), Fqn.fromString("/a"));// should throw an NPE } catch (Exception expected) { assert false : "Should not have thrown an exception!"; } } } ././@LongLink0000000000000000000000000000016100000000000011563 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/options/cachemodelocal/AsyncInvalidationOptLocksTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/options/cachemodelocal/AsyncInvalidationOptLo0000644000175000017500000000116011146273534033327 0ustar moellermoeller/* * 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", testName = "options.cachemodelocal.AsyncInvalidationOptLocksTest") public class AsyncInvalidationOptLocksTest extends CacheModeLocalTestBase { public AsyncInvalidationOptLocksTest() { cacheMode = Configuration.CacheMode.INVALIDATION_ASYNC; nodeLockingScheme = "OPTIMISTIC"; isInvalidation = true; } } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/options/cachemodelocal/CacheModeLocalTestBase.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/options/cachemodelocal/CacheModeLocalTestBase0000644000175000017500000006446111363314565033166 0ustar moellermoeller/* * 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.*; import org.jboss.cache.commands.ReplicableCommand; 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.Configuration; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.replicationlisteners.ReplicationListener; 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 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 AT jboss DOT org) */ @Test(groups = {"functional"}, testName = "cachemodelocal.CacheModeLocalTestBase") 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; CacheSPI cache1; CacheSPI cache2; NodeSPI root1; NodeSPI root2; ReplicationListener replListener1; ReplicationListener replListener2; private final Fqn fqn = Fqn.fromString("/a"); private final String key = "key"; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { UnitTestCacheFactory instance = new UnitTestCacheFactory(); Configuration c = new Configuration(); c.setClusterName("test"); c.setStateRetrievalTimeout(10000); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); c.setNodeLockingScheme(nodeLockingScheme); c.setCacheMode(cacheMode); c.setSerializationExecutorPoolSize(0); cache1 = (CacheSPI) instance.createCache(c, false, getClass()); cache1.start(); c = new Configuration(); c.setClusterName("test"); c.setStateRetrievalTimeout(10000); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); c.setNodeLockingScheme(nodeLockingScheme); c.setCacheMode(cacheMode); c.setSerializationExecutorPoolSize(0); cache2 = (CacheSPI) instance.createCache(c, false, getClass()); cache2.start(); root1 = cache1.getRoot(); root2 = cache2.getRoot(); replListener1 = ReplicationListener.getReplicationListener(cache1); replListener2 = ReplicationListener.getReplicationListener(cache2); } @AfterMethod(alwaysRun = true) public void tearDown() { TestingUtil.killCaches(cache1, cache2); } public void testPutKeyValue() throws Exception { cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache1.put(fqn, key, "value"); Thread.sleep(500); // 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 replListener2.expect(PutKeyValueCommand.class); cache1.getInvocationContext().getOptionOverrides().reset(); cache1.put(fqn, key, "value"); replListener2.waitForReplicationToOccur(); // 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"); Thread.sleep(500); assertEquals("value2", cache2.get(fqn, key)); assertEquals("value", cache1.get(fqn, key)); replListener1.expect(PutKeyValueCommand.class); cache2.getInvocationContext().getOptionOverrides().reset(); cache2.put(fqn, key, "value2"); replListener1.waitForReplicationToOccur(); 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 = root1.addChild(fqn); cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); node1.put(key, "value"); Thread.sleep(500); // 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 replListener2.expect(PutKeyValueCommand.class); cache1.getInvocationContext().getOptionOverrides().reset(); node1.put(key, "value"); replListener2.waitForReplicationToOccur(); // 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 = root2.addChild(fqn); cache2.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); node2.put(key, "value2"); Thread.sleep(500); assertEquals("value2", cache2.get(fqn, key)); assertEquals("value", cache1.get(fqn, key)); replListener1.expect(PutKeyValueCommand.class); cache2.getInvocationContext().getOptionOverrides().reset(); node2.put(key, "value2"); replListener1.waitForReplicationToOccur(); 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); Thread.sleep(500); // 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 replListener2.expect(PutDataMapCommand.class); cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(false); cache1.put(fqn, map); replListener2.waitForReplicationToOccur(); // 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); Thread.sleep(500); assertEquals("value2", cache2.get(fqn, key)); assertEquals("value", cache1.get(fqn, key)); replListener1.expect(PutKeyValueCommand.class); cache2.getInvocationContext().getOptionOverrides().reset(); cache2.put(fqn, key, "value2"); replListener1.waitForReplicationToOccur(); 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 = root1.addChild(fqn); cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); node1.putAll(map); Thread.sleep(500); // 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 replListener2.expect(PutDataMapCommand.class); cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(false); node1.putAll(map); replListener2.waitForReplicationToOccur(); // 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 = root2.addChild(fqn); map.put(key, "value2"); cache2.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); node2.putAll(map); Thread.sleep(500); assertEquals("value2", cache2.get(fqn, key)); assertEquals("value", cache1.get(fqn, key)); replListener1.expect(PutKeyValueCommand.class); cache2.getInvocationContext().getOptionOverrides().reset(); node2.put(key, "value2"); replListener1.waitForReplicationToOccur(); 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. replListener2.expect(PutKeyValueCommand.class); cache1.getInvocationContext().setOptionOverrides(null); cache1.put(fqn, key, "value"); replListener2.waitForReplicationToOccur(); 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); Thread.sleep(500); // 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 replListener2.expect(PutKeyValueCommand.class); cache1.put(fqn, key, "value"); replListener2.waitForReplicationToOccur(); 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 replListener2.expect(RemoveNodeCommand.class); cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(false); cache1.removeNode(fqn); replListener2.waitForReplicationToOccur(); // both should be null assertNull("should be null", cache1.get(fqn, key)); assertNull("should be null", cache2.get(fqn, key)); } public void testRemoveRootNode() throws Exception { replListener2.expect(PutKeyValueCommand.class); cache1.getInvocationContext().setOptionOverrides(null); cache1.put(fqn, key, "value"); replListener2.waitForReplicationToOccur(); 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.ROOT); Thread.sleep(500); // 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)); } } public void testRemoveNodeViaNodeAPI() throws Exception { // put some stuff in the cache first // make sure we cleanup thread local vars. replListener2.expect(PutKeyValueCommand.class); cache1.getInvocationContext().setOptionOverrides(null); cache1.put(fqn, key, "value"); assertEquals("value", cache1.get(fqn, key)); replListener2.waitForReplicationToOccur(); if (isInvalidation) { assertNull("Should be null", cache2.get(fqn, key)); } else { assertEquals("value", cache2.get(fqn, key)); } cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); root1.removeChild(fqn); Thread.sleep(500); // 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 replListener2.expect(PutKeyValueCommand.class); cache1.put(fqn, key, "value"); replListener2.waitForReplicationToOccur(); assertEquals("value", cache1.get(fqn, key)); if (isInvalidation) { assertNull("Should be null", cache2.get(fqn, key)); } else { assertEquals("value", cache2.get(fqn, key)); } replListener2.expect(RemoveNodeCommand.class); // now try again with passing the default options cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(false); root1.removeChild(fqn); replListener2.waitForReplicationToOccur(); // 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 { replListener2.expect((Class) PutKeyValueCommand.class); // put some stuff in the cache first cache1.getInvocationContext().setOptionOverrides(null); cache1.put(fqn, key, "value"); replListener2.waitForReplicationToOccur(); 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); Thread.sleep(500); // 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)); } replListener2.expect(PutKeyValueCommand.class); // replace cache entries cache1.put(fqn, key, "value"); replListener2.waitForReplicationToOccur(); assertEquals("value", cache1.get(fqn, key)); if (isInvalidation) { assertNull("Should be null", cache2.get(fqn, key)); } else { assertEquals("value", cache2.get(fqn, key)); } replListener2.expect(RemoveKeyCommand.class); // now try again with passing the default options cache1.getInvocationContext().getOptionOverrides().reset(); cache1.remove(fqn, key); replListener2.waitForReplicationToOccur(); // 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 replListener2.expect(PutDataMapCommand.class, PutKeyValueCommand.class); Node node1 = root1.addChild(fqn); cache1.getInvocationContext().setOptionOverrides(null); node1.put(key, "value"); replListener2.waitForReplicationToOccur(); 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); Thread.sleep(500); // 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 replListener2.expect(PutKeyValueCommand.class); node1.put(key, "value"); replListener2.waitForReplicationToOccur(); 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 replListener2.expect(RemoveKeyCommand.class); cache1.getInvocationContext().getOptionOverrides().reset(); node1.remove(key); replListener2.waitForReplicationToOccur(); // 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(); replListener2.expectWithTx(PutKeyValueCommand.class); mgr.begin(); cache1.getInvocationContext().getOptionOverrides().reset(); cache1.put(fqn, key, "value1"); cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache1.put(fqn, key, "value2"); mgr.commit(); replListener2.waitForReplicationToOccur(); // 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)); } replListener2.expectWithTx(PutKeyValueCommand.class); // 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(); replListener2.waitForReplicationToOccur(); // 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 replListener1.expect(PutKeyValueCommand.class); 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(); replListener1.waitForReplicationToOccur(); assertEquals("value2", cache2.get(fqn, key)); if (!isInvalidation) { assertEquals("value3", cache1.get(fqn, key)); } else { assertNull(cache1.get(fqn, key)); } replListener1.expectWithTx(PutKeyValueCommand.class); mgr.begin(); cache2.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache2.put(fqn, key, "value2"); cache2.getInvocationContext().getOptionOverrides().reset(); cache2.put(fqn, key, "value4"); mgr.commit(); replListener1.waitForReplicationToOccur(); 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(); replListener2.expect(PutKeyValueCommand.class, PutKeyValueCommand.class); cache1.put("/a", key, "old"); cache1.put("/b", key, "old"); replListener2.waitForReplicationToOccur(); mgr.begin(); cache1.getInvocationContext().getOptionOverrides().reset(); cache1.put("/a", key, "value1"); cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache1.put("/b", key, "value2"); mgr.rollback(); Thread.sleep(500); // 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 { replListener2.expect(PutDataMapCommand.class); Node node1 = root1.addChild(fqn); replListener2.waitForReplicationToOccur(); replListener2.expectWithTx(PutKeyValueCommand.class); 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(); replListener2.waitForReplicationToOccur(); // 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 replListener2.expectWithTx(PutKeyValueCommand.class); mgr.begin(); cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); node1.put(key, "value3"); cache1.getInvocationContext().getOptionOverrides().reset(); node1.put(key, "value"); mgr.commit(); replListener2.waitForReplicationToOccur(); // 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)); } //do not expect replication for this one as the node is already thre Node node2 = root2.addChild(fqn); mgr = cache2.getTransactionManager(); replListener1.expectWithTx(PutKeyValueCommand.class); mgr.begin(); cache2.getInvocationContext().getOptionOverrides().setCacheModeLocal(false); node2.put(key, "value3"); cache2.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); node2.put(key, "value2"); mgr.commit(); replListener1.waitForReplicationToOccur(); assertEquals("value2", cache2.get(fqn, key)); if (!isInvalidation) { assertEquals("value3", cache1.get(fqn, key)); } else { assertNull(cache1.get(fqn, key)); } replListener1.expectWithTx(PutKeyValueCommand.class); mgr.begin(); cache2.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); node2.put(key, "value2"); cache2.getInvocationContext().getOptionOverrides().reset(); node2.put(key, "value4"); mgr.commit(); replListener1.waitForReplicationToOccur(); 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); root1.addChild(fqn); // cache1 should still have this assertTrue(root1.hasChild(fqn)); // cache 2 should not Node node2 = root2.getChild(fqn); assertTrue("Should be null", node2 == null || (isInvalidation && !node2.isValid())); // now try again with passing the default options replListener2.expect(RemoveNodeCommand.class); root1.removeChild(fqn); replListener2.waitForReplicationToOccur(); cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(false); replListener2.expect(PutDataMapCommand.class); root1.addChild(fqn); replListener2.waitForReplicationToOccur(); // cache1 should still have this assertTrue(root1.hasChild(fqn)); // cache 2 should as well if (!isInvalidation) { assertTrue(root2.hasChild(fqn)); } else { assertTrue("Should be null", node2 == null || !node2.isValid()); } } }././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/options/cachemodelocal/SyncReplOptLocksTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/options/cachemodelocal/SyncReplOptLocksTest.j0000644000175000017500000000126611111047320033227 0ustar moellermoeller/* * 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.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.CacheSPI; import org.testng.annotations.Test; @Test(groups = {"functional", "jgroups"}, testName = "options.cachemodelocal.SyncReplOptLocksTest") public class SyncReplOptLocksTest extends CacheModeLocalTestBase { public SyncReplOptLocksTest() { cacheMode = Configuration.CacheMode.REPL_SYNC; nodeLockingScheme = "OPTIMISTIC"; } } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/options/PessimisticLockAcquisitionTimeoutTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/options/PessimisticLockAcquisitionTimeoutTest0000644000175000017500000002314011120367722033531 0ustar moellermoeller/* * 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.UnitTestCacheFactory; 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; import org.jboss.cache.util.TestingUtil; /** * Test functionality of {@link Option#setLockAcquisitionTimeout(int)}. * * @author Brian Stansberry */ @Test(groups = {"functional"}, sequential = true, testName = "options.PessimisticLockAcquisitionTimeoutTest") public class PessimisticLockAcquisitionTimeoutTest { private static final Log log = LogFactory.getLog(PessimisticLockAcquisitionTimeoutTest.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 { Configuration c = new Configuration(); c.setCacheMode("REPL_SYNC"); c.setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache = (CacheSPI) new UnitTestCacheFactory().createCache(c, false, getClass()); cache.start(); option = new Option(); option.setLockAcquisitionTimeout(0); } @AfterMethod(alwaysRun = true) public void tearDown() { if (cache != null) { TestingUtil.killCaches(cache); 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-3.2.8.GA/src/test/java/org/jboss/cache/options/TestVersion.java0000644000175000017500000000250311111047320027176 0ustar moellermoellerpackage 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-3.2.8.GA/src/test/java/org/jboss/cache/options/ForceCacheModeTest.java0000644000175000017500000003475411120367722030370 0ustar moellermoeller/* * 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.Fqn; import org.jboss.cache.UnitTestCacheFactory; 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.NodeInvalidated; 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.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.TransactionManager; import java.util.concurrent.CountDownLatch; /** * Tests functionality of {@link Option#setForceAsynchronous(boolean)} and * {@link Option#setForceSynchronous(boolean)}. * * @author Manik Surtani (manik AT jboss DOT org) */ @Test(groups = {"functional"}, sequential = true, enabled = false, testName = "options.ForceCacheModeTest") 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) { Configuration c = new Configuration(); c.setNodeLockingScheme(scheme); c.setCacheMode(mode); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache1 = (CacheSPI) new UnitTestCacheFactory().createCache(c, false, getClass()); c = new Configuration(); c.setNodeLockingScheme(scheme); c.setCacheMode(mode); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache2 = (CacheSPI) new UnitTestCacheFactory().createCache(c, false, getClass()); 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) { TestingUtil.killCaches(cache1); cache1 = null; } if (cache2 != null) { if (listener != null) cache2.removeCacheListener(listener); TestingUtil.killCaches(cache2); 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 @NodeInvalidated public void block(NodeEvent event) { log.error("Received event notification " + event); if (!event.isPre() && FQNA.equals(event.getFqn())) { blocked = true; try { latch.await(); } catch (InterruptedException e) { } blocked = false; } } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/options/PessimisticSuppressLockingTest.java0000644000175000017500000001431611132625723033142 0ustar moellermoeller/* * 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.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; 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; import javax.transaction.TransactionManager; import java.util.HashMap; import java.util.Map; /** * Tests the suppression of locking nodes * * @author Manik Surtani (manik AT jboss DOT org) */ @Test(groups = {"functional"}, sequential = true, testName = "options.PessimisticSuppressLockingTest") public class PessimisticSuppressLockingTest { 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()); UnitTestCacheFactory instance = new UnitTestCacheFactory(); config.setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); cache = (CacheSPI) instance.createCache(config, getClass()); m = cache.getTransactionManager(); } @AfterMethod(alwaysRun = true) public void tearDown() { if (cache != null) { TestingUtil.killCaches(cache); 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-3.2.8.GA/src/test/java/org/jboss/cache/options/ExplicitVersionsTest.java0000644000175000017500000001616611132625723031111 0ustar moellermoeller/* * 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.*; import org.jboss.cache.config.Configuration; import org.jboss.cache.optimistic.DataVersion; import org.jboss.cache.optimistic.DefaultDataVersion; 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.TransactionManager; /** * Tests the passing in of explicit {@see DataVersion} instances when using optimistic locking. * * @author Manik Surtani (manik AT jboss DOT org) */ @Test(groups = {"functional", "optimistic"}, testName = "options.ExplicitVersionsTest") 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(); UnitTestCacheFactory instance = new UnitTestCacheFactory(); cache = (CacheSPI) instance.createCache(false, getClass()); 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) { TestingUtil.killCaches(cache); 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 = 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, 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, 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, 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, cache.getNode("/parent").getVersion()); assertEquals(vChild, cache.getNode("/parent/child").getVersion()); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/options/PessimisticFailSilentlyTest.java0000644000175000017500000001162511132625723032406 0ustar moellermoeller/* * 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.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; 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.HashMap; import java.util.Map; /** * Tests passing in the failSilently option in various scenarios. * * @author Manik Surtani (manik AT jboss DOT org) */ @Test(groups = {"functional"}, sequential = true, testName = "options.PessimisticFailSilentlyTest") public class PessimisticFailSilentlyTest { 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(); UnitTestCacheFactory instance = new UnitTestCacheFactory(); cache = (CacheSPI) instance.createCache(false, getClass()); // very short acquisition timeout cache.getConfiguration().setLockAcquisitionTimeout(100); cache.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache.getConfiguration().setCacheMode(Configuration.CacheMode.LOCAL); cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); 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) { TestingUtil.killCaches(cache); 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-3.2.8.GA/src/test/java/org/jboss/cache/options/CacheModeLocalSimpleTest.java0000644000175000017500000000640311120367722031524 0ustar moellermoeller/* * 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.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Option; import org.jboss.cache.util.TestingUtil; 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 AT jboss DOT org) */ @Test(groups = {"functional"}, sequential = true, testName = "options.CacheModeLocalSimpleTest") public class CacheModeLocalSimpleTest { private CacheSPI cache1, cache2; private Option cacheModeLocal; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { Configuration c = new Configuration(); c.setCacheMode("REPL_SYNC"); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache1 = (CacheSPI) new UnitTestCacheFactory().createCache(c, false, getClass()); c = new Configuration(); c.setCacheMode("REPL_SYNC"); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache2 = (CacheSPI) new UnitTestCacheFactory().createCache(c, false, getClass()); cacheModeLocal = new Option(); cacheModeLocal.setCacheModeLocal(true); } @AfterMethod(alwaysRun = true) public void tearDown() { TestingUtil.killCaches(cache1, cache2); cache1 = null; cache2 = null; } 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-3.2.8.GA/src/test/java/org/jboss/cache/options/ExplicitVersionsReplTest.java0000644000175000017500000003256311120422060031715 0ustar moellermoellerpackage org.jboss.cache.options; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.NodeSPI; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.optimistic.DataVersion; import org.jboss.cache.optimistic.DefaultDataVersion; 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.RollbackException; import javax.transaction.TransactionManager; /** * Tests the passing in of explicit {@see DataVersion} instances when using optimistic locking + replication. * * @author Manik Surtani (manik AT jboss DOT org) */ @Test(groups = {"functional", "optimistic"}, sequential = true, testName = "options.ExplicitVersionsReplTest") 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 UnitTestCacheFactory().createCache(c, getClass()); } @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. // 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, cache[0].getNode(fqn).getVersion()); assertEquals(expected, 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, cache[0].getNode(fqn).getVersion()); assertEquals(expected, 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, cache[0].getNode(fqn).getVersion()); assertEquals(expected, 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, cache[0].getNode(fqn).getVersion()); assertEquals(expected, 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, cache[0].getNode("/org/domain/Entity/EntityInstance#1").getVersion()); assertEquals(1, ((DefaultDataVersion) ((NodeSPI) cache[1].getNode("/org/domain/Entity")).getVersion()).getRawVersion()); assertEquals(v, 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, cache[0].getNode("/org/domain/Entity/EntityInstance#1").getVersion()); assertEquals(0, ((DefaultDataVersion) ((NodeSPI) cache[1].getNode("/org/domain/Entity")).getVersion()).getRawVersion()); assertEquals(v, 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, cache[0].getNode("/parent").getVersion()); assertEquals(0, ((DefaultDataVersion) ((NodeSPI) cache[1].getRoot()).getVersion()).getRawVersion()); assertEquals(vParent, 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, cache[0].getNode("/parent").getVersion()); assertEquals(vChild, cache[0].getNode("/parent/child").getVersion()); assertEquals(0, ((DefaultDataVersion) ((NodeSPI) cache[1].getRoot()).getVersion()).getRawVersion()); assertEquals(vParent, cache[1].getNode("/parent").getVersion()); assertEquals(vChild, cache[1].getNode("/parent/child").getVersion()); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/GetKeysTest.java0000644000175000017500000000507411120422620025437 0ustar moellermoeller/* * 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; import org.testng.annotations.AfterMethod; /** * @author Bela Ban * @version $Id: GetKeysTest.java 7305 2008-12-12 08:49:20Z mircea.markus $ */ @Test(groups = {"functional"}, sequential = true, testName = "GetKeysTest") public class GetKeysTest { CacheSPI cache; @AfterMethod public void tearDown() { if (cache != null) { cache.stop(); cache.destroy(); cache = null; } } @Test(groups = {"functional"}) public void testGetKeys() throws Exception { cache = (CacheSPI) new UnitTestCacheFactory().createCache(getClass()); 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(); 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 UnitTestCacheFactory().createCache(getClass()); 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(); 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 UnitTestCacheFactory().createCache(getClass()); 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 } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/TestNameVerifier.java0000644000175000017500000001776611146545046026473 0ustar moellermoellerpackage org.jboss.cache; import org.testng.annotations.Test; import java.util.regex.Pattern; import java.util.regex.Matcher; import java.util.ArrayList; import java.util.Arrays; import java.io.FilenameFilter; import java.io.File; import java.io.FileFilter; import java.io.PrintWriter; import java.io.BufferedReader; import java.io.FileReader; /** * Class that tests that test names are correclty set for each test. * * @author Mircea.Markus@jboss.com */ @Test(groups = "functional", testName = "TestNameVerifier") public class TestNameVerifier { String dir = "src/test/java/org/jboss/cache"; Pattern packageLinePattern = Pattern.compile("package org.jboss.cache[^;]*"); Pattern classLinePattern = Pattern.compile("(abstract\\s*)??(public\\s*)(abstract\\s*)??class [^\\s]*"); Pattern atAnnotationPattern = Pattern.compile("@Test[^)]*"); Pattern testNamePattern = Pattern.compile("testName\\s*=\\s*\"[^\"]*\""); String fileCache; FilenameFilter javaFilter = new FilenameFilter() { public boolean accept(File dir, String name) { if (dir.getAbsolutePath().contains("testng")) return false; return name.endsWith(".java"); } }; FileFilter onlyDirs = new FileFilter() { public boolean accept(File pathname) { return pathname.isDirectory(); } }; @Test(enabled = false, description = "Do not enable this unless you want your files to be updated with test names!!!") public void process() throws Exception { File[] javaFiles = getJavaFiles(); for (File file : javaFiles) { if (needsUpdate(file)) { System.out.println("Updating file: " + file.getAbsolutePath()); updateFile(file); } } } private void updateFile(File file) throws Exception { String javaString = fileCache; String testName = getTestName(javaString, file.getName()); String testNameStr = ", testName = \"" + testName + "\""; javaString = replaceAtTestAnnotation(javaString, testNameStr); persistNewFile(file, javaString); } private void persistNewFile(File file, String javaString) throws Exception { if (file.delete()) { System.out.println("!!!!!!!!!! error porcessing file " + file.getName()); return; } file.createNewFile(); PrintWriter writter = new PrintWriter(file); writter.append(javaString); writter.close(); } private String replaceAtTestAnnotation(String javaString, String testNameStr) { Matcher matcher = atAnnotationPattern.matcher(javaString); boolean found = matcher.find(); assert found; String theMatch = matcher.group(); return matcher.replaceFirst(theMatch + testNameStr); } private String getTestName(String javaString, String filename) { String classNamePart = getClassNamePart(javaString, filename); //abstract classes do not require test names if (classNamePart.indexOf("abstract") >= 0) return null; classNamePart = classNamePart.substring("public class ".length()); String packagePart = getPackagePart(javaString, filename); //if the test is in org.horizon package then make sure no . is prepanded String packagePrepend = ((packagePart != null) && (packagePart.length() > 0)) ? packagePart + "." : ""; return packagePrepend + classNamePart; } private String getClassNamePart(String javaString, String filename) { Matcher matcher = classLinePattern.matcher(javaString); boolean found = matcher.find(); assert found : "could not determine class name for file: " + filename; return matcher.group(); } private String getPackagePart(String javaString, String filename) { Matcher matcher = packageLinePattern.matcher(javaString); boolean found = matcher.find(); assert found : "Could not determine package name for file: " + filename; String theMatch = matcher.group(); String partial = theMatch.substring("package org.jboss.cache".length()); if (partial.trim().length() == 0) return partial.trim(); return partial.substring(1);//drop the leading dot. } private boolean needsUpdate(File file) throws Exception { String javaFileStr = getFileAsString(file); if (javaFileStr.indexOf(" testName = \"") > 0) return false; int atTestIndex = javaFileStr.indexOf("@Test"); int classDeclarationIndex = javaFileStr.indexOf("public class"); return atTestIndex > 0 && atTestIndex < classDeclarationIndex; } private String getFileAsString(File file) throws Exception { StringBuilder builder = new StringBuilder(); BufferedReader fileReader = new BufferedReader(new FileReader(file)); String line; while ((line = fileReader.readLine()) != null) { builder.append(line + "\n"); } this.fileCache = builder.toString(); // fileReader.close(); return fileCache; } private File[] getJavaFiles() { File file = new File(dir); assert file.isDirectory(); ArrayList result = new ArrayList(); addJavaFiles(file, result); return result.toArray(new File[0]); } private void addJavaFiles(File file, ArrayList result) { assert file.isDirectory(); File[] javaFiles = file.listFiles(javaFilter); // printFiles(javaFiles); result.addAll(Arrays.asList(javaFiles)); for (File dir : file.listFiles(onlyDirs)) { addJavaFiles(dir, result); } } private void printFiles(File[] javaFiles) { for (File f : javaFiles) { System.out.println(f.getAbsolutePath()); } } public void verifyTestName() throws Exception { File[] javaFiles = getJavaFiles(); StringBuilder errorMessage = new StringBuilder("Following test class(es) do not have an appropriate test name: \n"); boolean hasErrors = false; for (File file : javaFiles) { String expectedName = incorrectTestName(file); if (expectedName != null) { errorMessage.append(file.getAbsoluteFile()).append(" expected test name is: '").append(expectedName).append("' \n"); hasErrors = true; } } assert !hasErrors : errorMessage.append("The rules for writting UTs are being descibed here: https://www.jboss.org/community/docs/DOC-13315"); } private String incorrectTestName(File file) throws Exception { String fileAsStr = getFileAsString(file); boolean containsTestAnnotation = atAnnotationPattern.matcher(fileAsStr).find(); if (!containsTestAnnotation) return null; String expectedTestName = getTestName(fileAsStr, file.getName()); if (expectedTestName == null) return null; //this happens when the class is abstract Matcher matcher = this.testNamePattern.matcher(fileAsStr); if (!matcher.find()) return expectedTestName; String name = matcher.group().trim(); int firstIndexOfQuote = name.indexOf('"'); String existingTestName = name.substring(firstIndexOfQuote + 1, name.length() - 1); //to ignore last quote if (!existingTestName.equals(expectedTestName)) return expectedTestName; return null; } @Test(enabled = false) public static void main(String[] args) throws Exception { File file = new File("C:\\jboss\\coding\\za_trunk\\src\\test\\java\\org\\jboss\\cache\\statetransfer\\ForcedStateTransferTest.java"); String incorrectName = new TestNameVerifier().incorrectTestName(file); System.out.println("incorrectName = " + incorrectName); // new TestNameVerifier().process(); // Pattern classLinePattern = Pattern.compile("@Test[^)]*"); // String totest = "aaaa\n" + "@Test(groups = {\"functional\", \"pessimistic\"})\n" + "{ dsadsadsa"; // Matcher matcher = classLinePattern.matcher(totest); // boolean found = matcher.find(); // System.out.println("found = " + found); // String theMatch = matcher.group(); // String result = matcher.replaceFirst(theMatch + ", testName=\"alaBala\""); // System.out.println("theMatch = " + theMatch); // System.out.println("result = " + result); } }jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/DataContainerTest.java0000644000175000017500000002314111157534263026614 0ustar moellermoellerpackage org.jboss.cache; import org.jboss.cache.buddyreplication.BuddyFqnTransformer; import org.jboss.cache.marshall.NodeData; import org.jboss.cache.mock.MockNodesFixture; import org.testng.annotations.AfterMethod; 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", sequential = true, testName = "DataContainerTest") 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()); } @AfterMethod public void tearDown() { container = null; nodes = null; } /** * 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#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 DataContainer#buildNodeData(java.util.List} */ public void testBuildNodeData() { nodes.abNode.put("ab", "ab"); nodes.abcNode.put("abc", "abc"); List result = new ArrayList(); container.buildNodeData(result, nodes.abNode, true); assert result.size() == 2; assert result.contains(new NodeData(nodes.ab, nodes.abNode.getData(), true)); assert result.contains(new NodeData(nodes.abc, nodes.abcNode.getData(), true)); } /** * 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() { int i; assert (i=container.getNumberOfNodes()) == -1 : "-1 nodes expected, was " + i; container.started = true; assert (i=container.getNumberOfNodes()) == 8 : "8 nodes expected, was " + i; } /** * 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-3.2.8.GA/src/test/java/org/jboss/cache/jmx/0000755000175000017500000000000011675221450023166 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/jmx/deprecated/0000755000175000017500000000000011675221450025266 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/jmx/deprecated/CacheJmxWrapperTest.java0000644000175000017500000003357711146273534032036 0ustar moellermoellerpackage org.jboss.cache.jmx.deprecated; import org.jboss.cache.Cache; import org.jboss.cache.CacheException; import org.jboss.cache.CacheStatus; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.jmx.CacheJmxWrapper; import org.jboss.cache.jmx.CacheJmxWrapperMBean; import org.jboss.cache.jmx.JmxRegistrationManager; 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.jboss.cache.util.CachePrinter; 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", testName = "jmx.deprecated.CacheJmxWrapperTest") 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", JmxRegistrationManager.REPLICATED_CACHE_PREFIX + clusterName, 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(CachePrinter.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(CachePrinter.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); } @Test public void testGetMembers() throws Exception { Configuration c = createConfiguration(); c.setCacheMode(CacheMode.REPL_ASYNC); // This cache instance does not go through UnitTestCacheFactory so we need to modify // it's config explicitelly. new UnitTestCacheFactory().mangleConfiguration(c); CacheJmxWrapperMBean wrapper = registerWrapper(c); wrapper.start(); Address addr = wrapper.getLocalAddress(); assertNotNull("Got an Address", addr); List members = wrapper.getMembers(); assertNotNull("Got members", addr); assertEquals("Got correct number of members", 1, members.size()); assertTrue("Got an IpAddress", wrapper.getLocalAddress() instanceof IpAddress); assertTrue("I am a member", members.contains(addr)); } 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()); cache.destroy(); cache.start(); 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: 7696 $ */ @Test (groups = "functional", testName = "jmx.deprecated.LifecycleNotificationTest") public class LifecycleNotificationTest extends CacheJmxWrapperTestBase { public void testGetStateAndStateNotification() throws Exception { CacheJmxWrapper wrapper = createWrapper(createConfiguration()); StateNotificationListener listener = new StateNotificationListener(); wrapper.addNotificationListener(listener, null, null); AssertJUnit.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()); 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); } } } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/jmx/deprecated/NotificationTest.java0000644000175000017500000003754311131613416031425 0ustar moellermoellerpackage org.jboss.cache.jmx.deprecated; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.jmx.CacheJmxWrapper; import org.jboss.cache.jmx.JmxRegistrationManager; import org.jboss.cache.jmx.CacheNotificationBroadcaster; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.loader.CacheLoader; import static org.testng.AssertJUnit.*; 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.Notification; import javax.management.NotificationListener; import javax.management.ObjectName; import java.util.EnumSet; import java.util.HashMap; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.util.TestingUtil; /** * Functional tests for CacheJmxWrapper broadcast of cache event notifications * * @author Jerry Gauthier * @version $Id: NotificationTest.java 7422 2009-01-09 09:21:18Z mircea.markus $ */ @Test(groups = {"functional"}, testName = "jmx.deprecated.NotificationTest") 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(); TestingUtil.killCaches(cache); cache = null; } } protected ObjectName getWrapperObjectName() throws Exception { return new ObjectName(JmxRegistrationManager.REPLICATED_CACHE_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)); 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); config.setCacheMode(CacheMode.LOCAL); UnitTestCacheFactory factory = new UnitTestCacheFactory(); CacheSPI cache = (CacheSPI) factory.createCache(config, false, getClass()); cache.create(); // start the cache after the listener has been registered //cache.start(); return cache; } protected Configuration createConfiguration(String clusterName) throws Exception { Configuration config = UnitTestConfigurationFactory.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 { return UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(true, "", "org.jboss.cache.loader.FileCacheLoader", properties, false, false, true, false, false); } 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; } } } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/jmx/deprecated/InterceptorRegistrationTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/jmx/deprecated/InterceptorRegistrationTest.ja0000644000175000017500000002423111146273534033340 0ustar moellermoeller/* * 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.deprecated; import org.jboss.cache.config.Configuration; import org.jboss.cache.jmx.CacheJmxWrapperMBean; import org.jboss.cache.jmx.CacheJmxWrapper; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.Test; /** * Tests the interceptor registration function of CacheJmxWrapper. * * @author Brian Stansberry * @version $Revision: 7696 $ */ @Test(groups = "functional", testName = "jmx.deprecated.InterceptorRegistrationTest") 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.setRegisterJmxResource(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.setRegisterJmxResource(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); } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/jmx/deprecated/OptimisticNotificationTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/jmx/deprecated/OptimisticNotificationTest.jav0000644000175000017500000000103111146273534033321 0ustar moellermoeller/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.jmx.deprecated; import org.testng.annotations.Test; /** * @author Manik Surtani (manik AT jboss DOT org) */ @Test (groups = "functional", testName = "jmx.deprecated.OptimisticNotificationTest") public class OptimisticNotificationTest extends NotificationTest { public OptimisticNotificationTest() { optimistic = true; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/jmx/deprecated/LegacyConfigurationTest.java0000644000175000017500000006605111236270336032735 0ustar moellermoeller/* * 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.deprecated; import org.jboss.cache.Version; 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; 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.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.config.RuntimeConfig; import org.jboss.cache.config.parsing.XmlConfigHelper; import org.jboss.cache.eviction.FIFOAlgorithm; import org.jboss.cache.eviction.FIFOAlgorithmConfig; import org.jboss.cache.eviction.LRUAlgorithm; import org.jboss.cache.eviction.LRUAlgorithmConfig; import org.jboss.cache.eviction.MRUAlgorithm; import org.jboss.cache.eviction.MRUAlgorithmConfig; import org.jboss.cache.jmx.CacheJmxWrapper; import org.jboss.cache.jmx.CacheJmxWrapperMBean; 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.transaction.BatchModeTransactionManagerLookup; import org.jgroups.ChannelFactory; import org.jgroups.JChannelFactory; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertSame; import org.testng.annotations.Test; import org.w3c.dom.Element; import javax.management.MBeanServerInvocationHandler; 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: 8150 $ */ @Test(groups = "functional", testName = "jmx.deprecated.LegacyConfigurationTest") public class LegacyConfigurationTest extends CacheJmxWrapperTestBase { public void testLocalCache() throws Exception { doTest(false); } public void testLocalCacheWithLegacyXML() throws Exception { doTest(true); } @SuppressWarnings({"deprecation", "unchecked"}) private void doTest(boolean legacy) throws Exception { CacheJmxWrapperMBean wrapper = new CacheJmxWrapper(); registerWrapper(wrapper); wrapper = (CacheJmxWrapperMBean) MBeanServerInvocationHandler.newProxyInstance(mBeanServer, mBeanName, CacheJmxWrapperMBean.class, false); wrapper.setBuddyReplicationConfig(getBuddyReplicationConfig(legacy)); wrapper.setCacheLoaderConfig(getCacheLoaderConfig(legacy)); wrapper.setCacheMode("REPL_SYNC"); wrapper.setClusterName("LocalTest"); wrapper.setClusterConfig(getClusterConfig()); wrapper.setEvictionPolicyConfig(getEvictionPolicyConfig(legacy)); 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(legacy).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(legacy).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(legacy).toString(), wrapper.getEvictionPolicyConfig().toString()); EvictionConfig ec = c.getEvictionConfig(); assertEquals("EC queue size", 1000, ec.getDefaultEvictionRegionConfig().getEventQueueSize()); assertEquals("EC wakeup", 5000, ec.getWakeupInterval()); assertEquals("EC default pol", LRUAlgorithm.class.getName(), ec.getDefaultEvictionRegionConfig().getEvictionAlgorithmConfig().getEvictionAlgorithmClassName()); List ercs = ec.getEvictionRegionConfigs(); EvictionRegionConfig erc = ercs.get(0); assertEquals("ERC1 name", "/org/jboss/data", erc.getRegionName()); assertEquals("ERC1 queue size", 1000, erc.getEventQueueSize()); FIFOAlgorithmConfig fifo = (FIFOAlgorithmConfig) erc.getEvictionAlgorithmConfig(); assertEquals("EPC1 pol", FIFOAlgorithm.class.getName(), fifo.getEvictionAlgorithmClassName()); assertEquals("EPC1 maxnodes", 5000, fifo.getMaxNodes()); erc = ercs.get(1); assertEquals("ERC2 name", "/test", erc.getRegionName()); assertEquals("ERC2 queue size", 1000, erc.getEventQueueSize()); MRUAlgorithmConfig mru = (MRUAlgorithmConfig) erc.getEvictionAlgorithmConfig(); assertEquals("EPC2 pol", MRUAlgorithm.class.getName(), mru.getEvictionAlgorithmClassName()); assertEquals("EPC2 maxnodes", 10000, mru.getMaxNodes()); erc = ercs.get(2); assertEquals("ERC3 name", "/maxAgeTest", erc.getRegionName()); assertEquals("ERC3 queue size", 1000, erc.getEventQueueSize()); LRUAlgorithmConfig lru = (LRUAlgorithmConfig) erc.getEvictionAlgorithmConfig(); assertEquals("EPC3 maxnodes", 10000, lru.getMaxNodes()); assertEquals("EPC3 maxage", 10000, lru.getMaxAge()); assertEquals("EPC3 ttl", 8000, lru.getTimeToLive()); } @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 + Thread.currentThread().getName()); // // 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()); // // //wrapper.stop(); // //wrapper.destroy(); // // } protected static Element getBuddyReplicationConfig(boolean legacy) throws Exception { if (legacy) { String xmlStr = "\n" + " true\n" + " org.jboss.cache.buddyreplication.TestBuddyLocator\n" + " \n" + " numBuddies = 2\n" + " \n" + " testpool\n" + " 600000\n" + " false\n" + " false\n" + " false\n" + " "; return XmlConfigHelper.stringToElement(xmlStr); } else { String xmlStr = " \n" + " \n" + " \n" + " \n" + " numBuddies = 2\n" + " \n" + " \n" + " "; return XmlConfigHelper.stringToElementInCoreNS(xmlStr); } } protected static Element getCacheLoaderConfig(boolean legacy) throws Exception { if (legacy) { String xmlStr = "\n" + " false\n" + " /foo\n" + " true\n" + " \n" + " org.jboss.cache.loader.FileCacheLoader\n" + " \n" + " location=/tmp\n " + " \n" + " false\n" + " true\n" + " true\n" + " true\n" + " \n" + " true\n" + " \n" + " \n " + " \n" + " org.jboss.cache.loader.jdbm.JdbmCacheLoader\n" + " \n" + " location=/home/bstansberry\n " + " \n" + " true\n" + " false\n" + " false\n" + " false\n" + " \n" + " false\n" + " \n" + " \n" + " "; return XmlConfigHelper.stringToElement(xmlStr); } else { String xmlStr = " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " location=/tmp\n " + " \n" + " \n" + " \n" + " \n" + " \n" + " location=/home/bstansberry\n" + " \n" + " \n" + " \n" + " "; return XmlConfigHelper.stringToElementInCoreNS(xmlStr); } } protected static Element getEvictionPolicyConfig(boolean legacy) throws Exception { if (legacy) { String xmlStr = " \n" + " 5\n" + " 1000\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" + " "; return XmlConfigHelper.stringToElement(xmlStr); } else { String xmlStr = " \n" + " \n" + " \n" + " \n" + " \n" + "\n" + " \n" + "\n" + "\n" + " \n" + "\n" + "\n" + " \n" + " \n" + " \n" + "\n" + " "; return XmlConfigHelper.stringToElementInCoreNS(xmlStr); } } protected static Element getClusterConfig() throws Exception { String xml = "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ""; return XmlConfigHelper.stringToElementInCoreNS(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-3.2.8.GA/src/test/java/org/jboss/cache/jmx/deprecated/CacheJmxWrapperTestBase.java0000644000175000017500000001307111132625723032610 0ustar moellermoellerpackage org.jboss.cache.jmx.deprecated; import org.jboss.cache.Cache; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.jmx.CacheJmxWrapper; import org.jboss.cache.jmx.CacheJmxWrapperMBean; import org.jboss.cache.jmx.JmxRegistrationManager; 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.*; /** * Tests the JMX wrapper class around the cache. * * @author Manik Surtani * @author Brian Stansberry */ @Test(groups = "functional", testName = "jmx.deprecated.CacheJmxWrapperTestBase") public abstract class CacheJmxWrapperTestBase { public static final String CLUSTER_NAME_BASE = "CacheMBeanTest"; protected String clusterName; protected Cache cache; protected CacheJmxWrapperMBean jmxWrapper; protected MBeanServer mBeanServer; protected ObjectName mBeanName; protected String mBeanNameStr; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { clusterName = CLUSTER_NAME_BASE + "-" + Thread.currentThread().getName(); mBeanServer = MBeanServerFactory.createMBeanServer("CacheMBeanTest"); mBeanNameStr = JmxRegistrationManager.REPLICATED_CACHE_PREFIX + clusterName; 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 { ObjectName on = new ObjectName(mBeanNameStr); if (!mBeanServer.isRegistered(on)) { mBeanServer.registerMBean(wrapper, on); } 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(); config.setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); wrapper.setConfiguration(config); return wrapper; } protected Cache createCache(Configuration config) { UnitTestCacheFactory factory = new UnitTestCacheFactory(); cache = factory.createCache(config, false, getClass()); return cache; } protected Configuration createConfiguration() { Configuration c = new Configuration(); c.setClusterName(clusterName); c.setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); 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 + JmxRegistrationManager.JMX_RESOURCE_KEY + "TxInterceptor"), new ObjectName(baseName + JmxRegistrationManager.JMX_RESOURCE_KEY + "CacheMgmtInterceptor"), }; 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-3.2.8.GA/src/test/java/org/jboss/cache/jmx/JmxRegistrationManagerTest.java0000644000175000017500000001207711135057137031324 0ustar moellermoeller/* * 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.jmx; 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.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.interceptors.CacheMgmtInterceptor; 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.ObjectName; /** * Tester class for {@link JmxRegistrationManager}. * * @author Mircea.Markus@jboss.com * @author Galder Zamarreno * @since 3.0 */ @Test (groups = "functional", testName = "jmx.JmxRegistrationManagerTest") public class JmxRegistrationManagerTest { private static final Log log = LogFactory.getLog(JmxRegistrationManagerTest.class); private UnitTestCacheFactory cacheFactory = new UnitTestCacheFactory(); private MBeanServer mBeanServer; @BeforeMethod public void setUp() { mBeanServer = MBeanServerFactory.createMBeanServer(); } @AfterMethod public void tearDown() { MBeanServerFactory.releaseMBeanServer(mBeanServer); } public void testRegisterLocalCache() throws Exception { registerLocalCache(); } public void testRegisterLocalCacheWithDifferentThreadNames() throws Exception { String prevName = Thread.currentThread().getName(); try { Thread.currentThread().setName("onecolon:"); registerLocalCache(); Thread.currentThread().setName("twocolons::"); registerLocalCache(); } finally { Thread.currentThread().setName(prevName); } } public void testRegisterReplicatedCache() throws Exception { Configuration localConfig = UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.REPL_SYNC); localConfig.setExposeManagementStatistics(true); Cache cache = cacheFactory.createCache(localConfig, getClass()); JmxRegistrationManager regManager = new JmxRegistrationManager(mBeanServer, cache, (ObjectName)null); assert regManager.getObjectNameBase().indexOf(JmxRegistrationManager.REPLICATED_CACHE_PREFIX) == 0; regManager.registerAllMBeans(); String name = regManager.getObjectName(CacheMgmtInterceptor.class.getSimpleName()); assert mBeanServer.isRegistered(new ObjectName(name)); regManager.unregisterAllMBeans(); assert !mBeanServer.isRegistered(new ObjectName(name)); cache.stop(); } protected void registerLocalCache() throws Exception { Configuration localConfig = UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL); localConfig.setExposeManagementStatistics(true); Cache cache = cacheFactory.createCache(localConfig, getClass()); JmxRegistrationManager regManager = new JmxRegistrationManager(mBeanServer, cache, (ObjectName)null); assert regManager.getObjectNameBase().indexOf(JmxRegistrationManager.LOCAL_CACHE_PREFIX) == 0; regManager.registerAllMBeans(); String name = regManager.getObjectName(CacheMgmtInterceptor.class.getSimpleName()); assert mBeanServer.isRegistered(new ObjectName(name)); regManager.unregisterAllMBeans(); assert !mBeanServer.isRegistered(new ObjectName(name)); cache.stop(); } /** * This is useful when wanting to startup jconsole... */ @Test(enabled = false) public static void main(String[] args) { Configuration localConfig = UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.REPL_SYNC); localConfig.setExposeManagementStatistics(true); CacheFactory cacheFactory = new DefaultCacheFactory(); Cache cache = cacheFactory.createCache(localConfig); JmxRegistrationManager regManager = new JmxRegistrationManager(cache); while (true){} } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/jmx/JmxManualTest.java0000644000175000017500000000456611146273534026603 0ustar moellermoellerpackage org.jboss.cache.jmx; import org.jboss.cache.Cache; 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.config.EvictionRegionConfig; import org.jboss.cache.eviction.FIFOAlgorithmConfig; import org.testng.annotations.Test; import java.io.IOException; import java.util.concurrent.TimeUnit; // do NOT enable this test in SVN as it will cause Hudson (or any other continuous integration test harness) to get // stuck. @Test(groups = "maual", enabled = false, testName = "jmx.JmxManualTest") public class JmxManualTest { public void testLocal() throws IOException { Configuration c = new Configuration(); Cache cache = new DefaultCacheFactory().createCache(c); cache.put("/a/b/c", "a", "b"); cache.put("/a/b/c", "c", "d"); cache.put("/a/b/d", "a", "b"); cache.put("/a/b/e", "c", "d"); System.in.read(); } public void testLocalNoJMX() throws IOException { Configuration c = new Configuration(); c.setExposeManagementStatistics(false); Cache cache = new DefaultCacheFactory().createCache(c); cache.put("/a/b/c", "a", "b"); cache.put("/a/b/c", "c", "d"); cache.put("/a/b/d", "a", "b"); cache.put("/a/b/e", "c", "d"); System.in.read(); } public void testLocalWithEviction() throws IOException { Configuration c = new Configuration(); EvictionConfig ec = new EvictionConfig(); ec.setWakeupInterval(250, TimeUnit.MILLISECONDS); EvictionRegionConfig erc = new EvictionRegionConfig(); erc.setEvictionAlgorithmConfig(new FIFOAlgorithmConfig(2)); erc.setRegionFqn(Fqn.ROOT); ec.setDefaultEvictionRegionConfig(erc); c.setEvictionConfig(ec); Cache cache = new DefaultCacheFactory().createCache(c); cache.put("/a/b/c", "a", "b"); cache.put("/a/b/c", "c", "d"); cache.put("/a/b/d", "a", "b"); cache.put("/a/b/e", "c", "d"); System.in.read(); } public void testLocalWithEvictionXML() throws IOException { Cache cache = new DefaultCacheFactory().createCache("config-samples/eviction-enabled.xml"); cache.put("/a/b/c", "a", "b"); cache.put("/a/b/c", "c", "d"); cache.put("/a/b/d", "a", "b"); cache.put("/a/b/e", "c", "d"); System.in.read(); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/jmx/ResourceDMBeanTest.java0000644000175000017500000000360611111047320027457 0ustar moellermoeller/* * 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.jmx; import org.testng.annotations.Test; import org.jboss.cache.interceptors.ActivationInterceptor; import org.jboss.cache.jmx.annotations.ManagedOperation; /** * Tester class for {@link ResourceDMBean}. * * @author Mircea.Markus@jboss.com * @since 3.0 */ @Test (groups = "unit", testName = "jmx.ResourceDMBeanTest") public class ResourceDMBeanTest { /** * If we have a method in the base class that is annotated as @ManagedOperation, will this be seen the same * way in the inherited class? */ public void testInheritedMethod() { Bbb bbb = new Bbb(); ResourceDMBean resourceDMBean = new ResourceDMBean(bbb); assert resourceDMBean.isOperationRegistred("baseMethod"); } static class Aaa { @ManagedOperation public void baseMethod() {} } static class Bbb extends Aaa { public void localMethod() {} } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/commands/0000755000175000017500000000000011675221455024176 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/commands/TestContextBase.java0000644000175000017500000000100211111047320030067 0ustar moellermoellerpackage org.jboss.cache.commands; import org.jboss.cache.DataContainer; import org.jboss.cache.InvocationContext; import org.jboss.cache.invocation.LegacyInvocationContext; import org.jboss.cache.invocation.MVCCInvocationContext; public class TestContextBase { protected InvocationContext createMVCCInvocationContext() { return new MVCCInvocationContext(); } protected InvocationContext createLegacyInvocationContext(DataContainer dc) { return new LegacyInvocationContext(dc); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/commands/RollbackOnNoOpTest.java0000644000175000017500000000414211131613416030511 0ustar moellermoellerpackage org.jboss.cache.commands; import org.jboss.cache.CacheSPI; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestConfigurationFactory; 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; import org.jboss.cache.UnitTestCacheFactory; /** * @author Mircea.Markus@jboss.com * @since 2.2 */ @Test(groups = "functional", sequential = true, testName = "commands.RollbackOnNoOpTest") public class RollbackOnNoOpTest { private CacheSPI cache; private TransactionManager txMgr; @BeforeMethod(alwaysRun = true) public void setUp() { Configuration cacheConfig = UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, false); cache = (CacheSPI) new UnitTestCacheFactory().createCache(cacheConfig, false, getClass()); cache.start(); txMgr = cache.getTransactionManager(); } @AfterMethod(alwaysRun = true) public void tearDown() { TestingUtil.killCaches(cache); cache = null; txMgr = null; } 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-3.2.8.GA/src/test/java/org/jboss/cache/commands/legacy/0000755000175000017500000000000011675221454025441 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/commands/legacy/write/0000755000175000017500000000000011675221454026573 5ustar moellermoeller././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/commands/legacy/write/ClearDataCommandTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/commands/legacy/write/ClearDataCommandTest.ja0000644000175000017500000000675111111047320033056 0ustar moellermoellerpackage org.jboss.cache.commands.legacy.write; import static org.easymock.EasyMock.expect; import org.jboss.cache.commands.write.AbstractVersionedDataCommand; import org.jboss.cache.commands.write.AbstractVersionedDataCommandTest; import org.jboss.cache.notifications.event.NodeModifiedEvent; import org.testng.annotations.Test; import java.util.Collections; /** * tester class for {@link org.jboss.cache.commands.write.ClearDataCommand}. * * @author Mircea.Markus@jboss.com * @since 2.2 */ @Test(groups = "unit", sequential = true, testName = "commands.legacy.write.ClearDataCommandTest") public class ClearDataCommandTest extends AbstractVersionedDataCommandTest { PessClearDataCommand command; public AbstractVersionedDataCommand moreSetUp() { command = new PessClearDataCommand(globalTransaction, fqn); command.setDataVersion(dataVersion); return command; } public void testNonexistentNode() { // will happen twice - once in the Pess subclass. expect(container.peek(fqn)).andReturn(null); expect(container.peek(fqn)).andReturn(null); control.replay(); assert null == command.perform(ctx); control.verify(); } public void testExistentDataVersioned() { nodes.adfgNode.put("key", "value"); nodes.adfgNode.setVersion(dataVersion); // will happen twice - once in the Pess subclass. expect(container.peek(fqn)).andReturn(nodes.adfgNode); expect(container.peek(fqn)).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"); } public void testExistentDataUnversioned() { command.setDataVersion(null); nodes.adfgNode.put("key", "value"); // will happen twice - once in the Pess subclass. expect(container.peek(fqn)).andReturn(nodes.adfgNode); expect(container.peek(fqn)).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"; } } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/commands/legacy/write/PutDataMapCommandTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/commands/legacy/write/PutDataMapCommandTest.j0000644000175000017500000000540411146273534033107 0ustar moellermoellerpackage org.jboss.cache.commands.legacy.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.InvocationContext; import org.jboss.cache.commands.TestContextBase; 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 org.jboss.cache.commands.write.PutDataMapCommand} * * @author Mircea.Markus@jboss.com * @since 2.2 */ @Test(groups = "unit", testName = "commands.legacy.write.PutDataMapCommandTest") public class PutDataMapCommandTest extends TestContextBase { Fqn testFqn = Fqn.fromString("/testfqn"); PessPutDataMapCommand command; GlobalTransaction gtx; Notifier notifier; DataContainer container; Map dataMap; IMocksControl control; NodeSpiMock node; InvocationContext ctx; @BeforeMethod protected void setUp() { gtx = new GlobalTransaction(); dataMap = new HashMap(); command = new PessPutDataMapCommand(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"); ctx = createLegacyInvocationContext(container); } public void testAddDataNoErase() { // will happen twice - once in the Pess subclass. expect(container.peek(testFqn)).andReturn(node); expect(container.peek(testFqn)).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(), ctx); expect(notifier.shouldNotifyOnNodeModified()).andReturn(true); notifier.notifyNodeModified(testFqn, false, NodeModifiedEvent.ModificationType.PUT_MAP, expected, ctx); control.replay(); assert null == command.perform(ctx) : "null result is always expected"; assert command.oldData.size() == 1; assert command.oldData.get("k").equals("v"); control.verify(); } public void testRollbackNonexistentNode() { expect(container.peek(testFqn, false, true)).andReturn(null); control.replay(); command.rollback(); control.verify(); } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/commands/legacy/write/RemoveNodeCommandTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/commands/legacy/write/RemoveNodeCommandTest.j0000644000175000017500000000627311111047320033137 0ustar moellermoellerpackage org.jboss.cache.commands.legacy.write; import static org.easymock.EasyMock.expect; import org.jboss.cache.commands.write.AbstractVersionedDataCommand; import org.jboss.cache.commands.write.AbstractVersionedDataCommandTest; import org.jboss.cache.transaction.GlobalTransaction; import org.testng.annotations.Test; /** * tester for {@link org.jboss.cache.commands.write.RemoveNodeCommand}. * * @author Mircea.Markus@jboss.com * @since 2.2 */ @Test(groups = "unit", sequential = true, testName = "commands.legacy.write.RemoveNodeCommandTest") public class RemoveNodeCommandTest extends AbstractVersionedDataCommandTest { PessRemoveNodeCommand command; public AbstractVersionedDataCommand moreSetUp() { command = new PessRemoveNodeCommand(globalTransaction, fqn); command.setDataVersion(dataVersion); return command; } public void testNonExistentNode() { expect(container.peek(fqn)).andReturn(null); // again expect(container.peek(fqn)).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"); nodes.adfNode.setVersion(dataVersion); ctx.setGlobalTransaction(new GlobalTransaction()); //check perform expect(container.peek(fqn)).andReturn(nodes.adfNode); // again expect(container.peek(fqn)).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 nodes.adfNode.setVersion(dataVersion); expect(container.peek(fqn)).andReturn(nodes.adfNode); // again expect(container.peek(fqn)).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 nodes.adfNode.setVersion(dataVersion); expect(container.peek(fqn)).andReturn(nodes.adfNode); // again expect(container.peek(fqn)).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-3.2.8.GA/src/test/java/org/jboss/cache/commands/legacy/write/MoveCommandTest.java0000644000175000017500000000430011146273534032477 0ustar moellermoellerpackage org.jboss.cache.commands.legacy.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.commands.write.MoveCommand; 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", testName = "commands.legacy.write.MoveCommandTest") 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 PessMoveCommand(source, destination); command.initialize(notifier, container); nodes = new MockNodesFixture(); } public void testFailsOnMissingSource() { control.checkOrder(false); expect(container.peek(source)).andReturn(null); expect(container.peek(destination)).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)).andReturn(nodes.adfgNode); expect(container.peek(destination)).andReturn(null); control.replay(); try { command.perform(ctx); assert false : "should have thrown an exception as the source is null"; } catch (NodeNotExistsException e) { //expected } } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/commands/legacy/write/RemoveKeyCommandTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/commands/legacy/write/RemoveKeyCommandTest.ja0000644000175000017500000000726211111047320033142 0ustar moellermoellerpackage org.jboss.cache.commands.legacy.write; import static org.easymock.EasyMock.expect; import org.jboss.cache.commands.write.AbstractVersionedDataCommand; import org.jboss.cache.commands.write.AbstractVersionedDataCommandTest; import org.jboss.cache.notifications.event.NodeModifiedEvent; import org.testng.annotations.Test; import java.util.HashMap; import java.util.Map; /** * tester class for {@link org.jboss.cache.commands.write.RemoveKeyCommand}. * * @author Mircea.Markus@jboss.com * @since 2.2 */ @Test(groups = "unit", sequential = true, testName = "commands.legacy.write.RemoveKeyCommandTest") public class RemoveKeyCommandTest extends AbstractVersionedDataCommandTest { PessRemoveKeyCommand command; private String key; public AbstractVersionedDataCommand moreSetUp() { key = "key"; command = new PessRemoveKeyCommand(globalTransaction, fqn, key); return command; } public void testNonexistentNode() { expect(container.peek(fqn)).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)).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(); // won't do the peek if the oldValue was null. Nothing to set anyway, why peek. // 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)).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(); command.rollback(); } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/commands/legacy/write/CreateNodeCommandTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/commands/legacy/write/CreateNodeCommandTest.j0000644000175000017500000000450511111047320033101 0ustar moellermoellerpackage org.jboss.cache.commands.legacy.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", sequential = true, testName = "commands.legacy.write.CreateNodeCommandTest") 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); } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/commands/legacy/write/PutKeyValueCommandTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/commands/legacy/write/PutKeyValueCommandTest.0000644000175000017500000000713111111047320033132 0ustar moellermoellerpackage org.jboss.cache.commands.legacy.write; import static org.easymock.EasyMock.expect; import org.jboss.cache.NodeNotExistsException; import org.jboss.cache.commands.write.AbstractVersionedDataCommand; import org.jboss.cache.commands.write.AbstractVersionedDataCommandTest; import org.jboss.cache.notifications.event.NodeModifiedEvent; import org.testng.annotations.Test; import java.util.HashMap; import java.util.Map; /** * tester class for {@link org.jboss.cache.commands.write.PutKeyValueCommand}. * * @author Mircea.Markus@jboss.com * @since 2.2 */ @Test(groups = "unit", sequential = true, testName = "commands.legacy.write.PutKeyValueCommandTest") public class PutKeyValueCommandTest extends AbstractVersionedDataCommandTest { PessPutForExternalReadCommand command; public AbstractVersionedDataCommand moreSetUp() { command = new PessPutForExternalReadCommand(globalTransaction, fqn, "k", "v"); return command; } public void testInexistentNode() { expect(container.peek(fqn)).andReturn(null); // simulate node not existing. control.replay(); try { command.perform(ctx); assert false : "exception should have been thrown as data does not exists."; } catch (NodeNotExistsException e) { //expected } control.verify(); } public void testAddNewData() { nodes.adfNode.put("existingKey", "existingValue"); expect(container.peek(fqn)).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.peek(fqn)).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(); } } ././@LongLink0000000000000000000000000000016100000000000011563 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/commands/legacy/write/VersionedInvalidateCommandTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/commands/legacy/write/VersionedInvalidateComm0000644000175000017500000001047111146273534033273 0ustar moellermoellerpackage org.jboss.cache.commands.legacy.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.DataVersioningException; 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 VersionedInvalidateCommand}. * * @author Mircea.Markus@jboss.com * @since 2.2 */ @Test(groups = "unit", testName = "commands.legacy.write.VersionedInvalidateCommandTest") public class VersionedInvalidateCommandTest extends AbstractDataCommandTest { DataVersion dataVersion; VersionedInvalidateCommand 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 VersionedInvalidateCommand(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.setVersion(dataVersion); nodes.adfNode.setDataLoaded(true); expect(spiMock.getNode(testFqn)).andReturn(nodes.adfNode); notifier.notifyNodeInvalidated(testFqn, true, ctx); notifier.notifyNodeInvalidated(testFqn, false, ctx); 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); nodes.adfNode.setVersion(new DefaultDataVersion(100)); expect(spiMock.getNode(testFqn)).andReturn(nodes.adfNode); control.replay(); try { command.perform(ctx); assert false : "exception expected"; } catch (DataVersioningException 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 testExistingTombstone() { nodes.adfNode.setValid(false, true); nodes.adfNode.setVersion(dataVersion); expect(spiMock.getNode(testFqn)).andReturn(null); expect(container.peek(testFqn, true, true)).andReturn(nodes.adfNode); notifier.notifyNodeInvalidated(testFqn, true, ctx); notifier.notifyNodeInvalidated(testFqn, false, ctx); 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 testCreateTombstone() 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 testCreateTombstoneNoTx() throws Exception { expect(tmMock.suspend()).andReturn(null); spiMock.put(testFqn, Collections.EMPTY_MAP); control.replay(); command.createTombstone(ctx); control.verify(); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/commands/legacy/read/0000755000175000017500000000000011675221454026354 5ustar moellermoeller././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/commands/legacy/read/GravitateDataCommandTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/commands/legacy/read/GravitateDataCommandTest0000644000175000017500000001373611111047320033147 0ustar moellermoellerpackage org.jboss.cache.commands.legacy.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.invocation.LegacyInvocationContext; 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", sequential = true, testName = "commands.legacy.read.GravitateDataCommandTest") public class GravitateDataCommandTest { LegacyGravitateDataCommand 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 LegacyGravitateDataCommand(fqn, true, new IpAddress()); command.initialize(containerMock, spiMock, new BuddyFqnTransformer()); ctx = new LegacyInvocationContext(containerMock); } 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, false)).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, false)).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-3.2.8.GA/src/test/java/org/jboss/cache/commands/StructuralNodesOnRollbackTest.java0000644000175000017500000000442411131613416033002 0ustar moellermoellerpackage org.jboss.cache.commands; import org.jboss.cache.CacheSPI; import org.jboss.cache.NodeSPI; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestConfigurationFactory; 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; import java.util.HashMap; import org.jboss.cache.UnitTestCacheFactory; /** * 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", sequential = true, testName = "commands.StructuralNodesOnRollbackTest") public class StructuralNodesOnRollbackTest { private CacheSPI cache; private TransactionManager txMgr; @BeforeMethod(alwaysRun = true) public void setUp() { Configuration cacheConfig = UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, false); cache = (CacheSPI) new UnitTestCacheFactory().createCache(cacheConfig, getClass()); txMgr = cache.getTransactionManager(); } @AfterMethod(alwaysRun = true) public void tearDown() { TestingUtil.killCaches(cache); cache = null; txMgr = null; } public void testNoTx() throws Exception { txMgr.begin(); cache.put("/a/b/c", "k","v"); NodeSPI root = cache.getRoot(); assert root.getChild("a") != null; assert root.getChild(Fqn.fromString("/a/b/c")) != null; assert cache.exists("/a/b"); txMgr.rollback(); } 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-3.2.8.GA/src/test/java/org/jboss/cache/commands/write/0000755000175000017500000000000011675221455025330 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/commands/write/InvalidateCommandTest.java0000644000175000017500000000561711111047320032401 0ustar moellermoellerpackage 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", sequential = true, testName = "commands.write.InvalidateCommandTest") 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.notifyNodeInvalidated(testFqn, true, ctx); expect(container.evict(testFqn)).andReturn(Boolean.TRUE); notifier.notifyNodeInvalidated(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.notifyNodeInvalidated(Fqn.ROOT, true, ctx); expect(container.evict(Fqn.ROOT)).andReturn(Boolean.TRUE); notifier.notifyNodeInvalidated(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-3.2.8.GA/src/test/java/org/jboss/cache/commands/write/EvictCommandTest.java0000644000175000017500000000565511142561344031410 0ustar moellermoellerpackage 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.legacy.write.LegacyEvictCommand; 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", sequential = true, testName = "commands.write.EvictCommandTest") 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 LegacyEvictCommand(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 testShouldReturnTrueIndicatingNodeIsAbsentIfNodeDoesntExist() { expect(container.peek(testFqn, false, true)).andReturn(null); 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); notifier.notifyNodeEvicted(testFqn, false, ctx); control.replay(); assert Boolean.FALSE == 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 expect(container.peek(nodes.a, false, true)).andReturn(nodes.aNode); notifier.notifyNodeEvicted(nodes.a, true, ctx); notifier.notifyNodeEvicted(nodes.a, false, ctx); //evict b expect(container.peek(nodes.ab, false, true)).andReturn(nodes.abNode); notifier.notifyNodeEvicted(nodes.ab, true, ctx); notifier.notifyNodeEvicted(nodes.ab, false, ctx); control.replay(); assert Boolean.TRUE == command.perform(ctx); control.verify(); } } ././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/commands/write/AbstractVersionedDataCommandTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/commands/write/AbstractVersionedDataCommandTe0000644000175000017500000000331011111047320033232 0ustar moellermoellerpackage 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.commands.TestContextBase; 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 extends TestContextBase { protected Notifier notifier; protected DataContainer container; protected IMocksControl control; protected MockNodesFixture nodes; protected DataVersion dataVersion; protected GlobalTransaction globalTransaction; protected Fqn fqn = Fqn.fromString("/test/fqn"); protected 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 = createLegacyInvocationContext(container); AbstractVersionedDataCommand command = moreSetUp(); command.initialize(notifier, container); } public abstract AbstractVersionedDataCommand moreSetUp(); } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/commands/read/0000755000175000017500000000000011675221457025113 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/commands/read/GetKeyValueCommandTest.java0000644000175000017500000000533711111047320032266 0ustar moellermoellerpackage 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.InvocationContext; import org.jboss.cache.commands.TestContextBase; 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", sequential = true, testName = "commands.read.GetKeyValueCommandTest") public class GetKeyValueCommandTest extends TestContextBase { private IMocksControl control; Notifier notifierMock; DataContainer containerMock; GetKeyValueCommand command; Fqn fqn = Fqn.fromString("/dummy"); String key = "key"; InvocationContext ctx; @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); ctx = createLegacyInvocationContext(containerMock); } public void testNonexistentNodeNoNotifications() { expect(containerMock.peek(fqn)).andReturn(null); control.replay(); assert null == command.perform(ctx); } 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(ctx)); } /** * 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(ctx); } 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, ctx); expect(containerMock.peek(fqn)).andReturn(node); notifierMock.notifyNodeVisited(fqn, false, ctx); control.replay(); assert value.equals(command.perform(ctx)); control.verify(); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/commands/read/GetDataMapCommandTest.java0000644000175000017500000000263411111047320032045 0ustar moellermoellerpackage 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", sequential = true, testName = "commands.read.GetDataMapCommandTest") 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(ctx); } 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(ctx); 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 } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/commands/read/GetKeysCommandTest.java0000644000175000017500000000227611111047320031453 0ustar moellermoellerpackage 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", sequential = true, testName = "commands.read.GetKeysCommandTest") 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(ctx); } 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(ctx); assert 2 == result.size(); assert result.contains("k1"); assert result.contains("k2"); } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/commands/read/GetChildrenNamesCommandTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/commands/read/GetChildrenNamesCommandTest.jav0000644000175000017500000000375311111047320033114 0ustar moellermoellerpackage org.jboss.cache.commands.read; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.replay; import org.jboss.cache.commands.legacy.read.PessGetChildrenNamesCommand; 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", sequential = true, testName = "commands.read.GetChildrenNamesCommandTest") public class GetChildrenNamesCommandTest extends AbstractDataCommandTest { private GetChildrenNamesCommand command; private MockNodesFixture nodes; protected void moreSetup() { nodes = new MockNodesFixture(); command = new PessGetChildrenNamesCommand(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(ctx); assert result.isEmpty() : "empty result expected"; } public void testPerformInexistingNode() { expect(container.peek(testFqn)).andReturn(null); replay(container); Set result = (Set) command.perform(ctx); assert result == null : "empty result expected"; } public void testNodeWithChildren() { expect(container.peek(testFqn)).andReturn(nodes.adfNode); replay(container); Set result = (Set) command.perform(ctx); 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(ctx); assert result.size() == 1; assert result.contains("h"); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/commands/read/AbstractDataCommandTest.java0000644000175000017500000000177111111047320032434 0ustar moellermoellerpackage 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.jboss.cache.commands.TestContextBase; 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 extends TestContextBase { protected Fqn testFqn = Fqn.fromString("/testfqn"); protected DataContainer container; protected InvocationContext ctx; @BeforeMethod final public void setUp() { container = createMock(DataContainer.class); moreSetup(); ctx = createLegacyInvocationContext(container); } /** * called by setUp after initializing the testFqn and containeMock. */ protected abstract void moreSetup(); } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/commands/read/ExistsCommandTest.java0000644000175000017500000000166111111047320031354 0ustar moellermoellerpackage org.jboss.cache.commands.read; import org.jboss.cache.InvocationContext; import org.jboss.cache.invocation.MVCCInvocationContext; import org.jboss.cache.mock.NodeSpiMock; 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", testName = "commands.read.ExistsCommandTest") public class ExistsCommandTest extends AbstractDataCommandTest { private ExistsCommand command; protected void moreSetup() { command = new ExistsCommand(testFqn); command.initialize(container); } public void testPerform() { InvocationContext ctx = new MVCCInvocationContext(); ctx.putLookedUpNode(testFqn, null); assert !((Boolean) command.perform(ctx)); ctx.putLookedUpNode(testFqn, new NodeSpiMock(testFqn)); assert Boolean.TRUE == command.perform(ctx); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/0000755000175000017500000000000011675221473025736 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/GravitationCleanupTest.java0000644000175000017500000000427211120421070033221 0ustar moellermoellerpackage org.jboss.cache.buddyreplication; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.commands.remote.DataGravitationCleanupCommand; import org.jboss.cache.config.Configuration; import org.jboss.cache.util.CachePrinter; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.replicationlisteners.ReplicationListener; import org.testng.annotations.Test; import javax.transaction.TransactionManager; import java.util.List; /** * @author Manik Surtani (manik AT jboss DOT org) * @since 2.1.0 */ @Test(groups = "functional", testName = "buddyreplication.GravitationCleanupTest") public class GravitationCleanupTest extends BuddyReplicationTestsBase { Fqn fqn = Fqn.fromString("/a/b/c"); Object key = "key", value = "value"; BuddyFqnTransformer fqnTransformer = new BuddyFqnTransformer(); /** * UT for https://jira.jboss.org/jira/browse/JBCACHE-1445. */ public void testDataGravitationCleanup1Pc() throws Exception { CacheSPI cache0 = createCache(1, null, true, false); cache0.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_ASYNC); CacheSPI cache1 = createCache(1, null, true, false); cache1.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_ASYNC); cache0.start(); cache1.start(); try { waitForSingleBuddy(cache0, cache1); Fqn fqn = Fqn.fromString("/a/b/c"); assert null == cache0.get(fqn, "k"); cache0.put(fqn, "key", "val"); ReplicationListener replicationListener = ReplicationListener.getReplicationListener(cache0); replicationListener.expect(DataGravitationCleanupCommand.class); TransactionManager transactionManager = cache1.getTransactionManager(); transactionManager.begin(); assert cache1.get(fqn, "key").equals("val"); transactionManager.commit(); replicationListener.waitForReplicationToOccur(1000); assert !cache0.exists(fqn); } finally { TestingUtil.killCaches(cache0, cache1); } } private void cleanupDelay() { TestingUtil.sleepThread(250); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyFqnTransformerTest.java0000644000175000017500000000366411176277175033417 0ustar moellermoellerpackage org.jboss.cache.buddyreplication; import org.jboss.cache.Fqn; import org.testng.annotations.Test; /** * @author Manik Surtani (manik AT jboss DOT org) * @since 2.2.0 */ @Test(groups = "unit", testName = "buddyreplication.BuddyFqnTransformerTest") 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); } public void testGetActualFqnOnDeadBackup() { Fqn deadBackup = Fqn.fromString("/_BUDDY_BACKUP_/1.2.3.4_5678:DEAD/5/a/b/c/d"); Fqn actualFqn = Fqn.fromString("/a/b/c/d"); assert actualFqn.equals(buddyFqnTransformer.getActualFqn(deadBackup)); } public void testGetActualFqnOnIncompleteDeadBackup() { Fqn deadBackup = Fqn.fromString("/_BUDDY_BACKUP_/1.2.3.4_5678:DEAD"); Fqn actualFqn = Fqn.ROOT; assert actualFqn.equals(buddyFqnTransformer.getActualFqn(deadBackup)); } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/AbstractNodeBasedBuddyTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/AbstractNodeBasedBuddyTest.j0000644000175000017500000000157611120273541033246 0ustar moellermoellerpackage org.jboss.cache.buddyreplication; import org.jboss.cache.CacheSPI; import org.testng.annotations.AfterClass; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeClass; import org.testng.annotations.AfterMethod; import java.util.List; /** * @author Mircea.Markus@jboss.com */ public abstract class AbstractNodeBasedBuddyTest extends BuddyReplicationTestsBase { protected List> caches; protected final String key = "key"; protected final String value = "value"; @BeforeClass public abstract void createCaches() throws Exception; @AfterClass public void killCaches() throws Exception { super.cleanupCaches(caches, true); } @AfterMethod public void tearDown() throws Exception { super.cleanupCaches(caches, false); } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/Buddy2NodesNoBuddyPoolTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/Buddy2NodesNoBuddyPoolTest.j0000644000175000017500000002021511264726770033216 0ustar moellermoellerpackage org.jboss.cache.buddyreplication; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Cache; import org.jboss.cache.config.Configuration; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.CachePrinter; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import org.testng.annotations.*; import java.util.List; import java.util.ArrayList; /** * @author Mircea.Markus@jboss.com */ @Test(groups = "functional", testName = "buddyreplication.Buddy2NodesNoBuddyPoolTest") public class Buddy2NodesNoBuddyPoolTest extends AbstractNodeBasedBuddyTest { @BeforeClass public void createCaches() throws Exception { caches = createCaches(2, false); } @AfterMethod @Override public void tearDown() throws Exception { if (caches.size() > 2 && caches.get(2) != null) { Cache cache = caches.get(2); caches.remove(2); cache.stop(); cache.destroy(); } super.tearDown(); } @Test(dependsOnMethods = "testBuddyJoin") public void testAddingNewCaches() throws Exception { // get some data in there. caches.get(0).put("/cache0", "k", "v"); caches.get(1).put("/cache1", "k", "v"); waitForBuddy(caches.get(0), caches.get(1), true); waitForBuddy(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); waitForBuddy(caches.get(0), caches.get(1), true); waitForBuddy(caches.get(1), caches.get(2), true); waitForBuddy(caches.get(2), 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 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!"; } public void testBuddyJoin() throws Exception { String key = "key"; String value = "value"; 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 cachePool' "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 caches.add(createCache(1, null)); // allow this cache a few msecs to join TestingUtil.blockUntilViewsReceived(3000, caches.get(0), caches.get(1), caches.get(2)); TestingUtil.sleepThread(2000); // allow buddy group reorg List> dump = new ArrayList>(caches); dump.add(caches.get(2)); // now cachePool.get(1)'s buddy should be cache2, not cache[0] assertIsBuddy(caches.get(1), caches.get(2), true); // this should still be the same assertIsBuddy(caches.get(0), caches.get(1), true); // and cache2's buddy should be cache[0] assertIsBuddy(caches.get(2), 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, caches.get(2).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", caches.get(2).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", caches.get(2).exists(backupFqn)); assertNoStaleLocks(caches); } /** * Test for https://jira.jboss.org/jira/browse/JBCACHE-1549 * * @throws Exception */ public void testStateTransferOnBuddyRestart() throws Exception { String key = "key"; String value = "value"; Fqn fqn = Fqn.fromString("/test"); Fqn backupFqn = fqnTransformer.getBackupFqn(caches.get(0).getLocalAddress(), fqn); assertNoStaleLocks(caches); // put something in cache 0 caches.get(0).put(fqn, key, value); assertNoStaleLocks(caches); // this should be in neither of the other cachePool' "main" trees assertEquals(value, caches.get(0).get(fqn, key)); assertFalse("Should be false", caches.get(1).exists(fqn)); // check the backup trees assertEquals("Buddy should have data in backup tree", value, caches.get(1).get(backupFqn, key)); assertNoStaleLocks(caches); // now re-start cache 1 caches.get(1).stop(); TestingUtil.blockUntilViewsReceived(3000, caches.get(0)); caches.get(1).start(); // allow this cache a few msecs to join TestingUtil.blockUntilViewsReceived(3000, caches.get(0), caches.get(1)); TestingUtil.sleepThread(2000); // allow buddy group reorg // check the backup tree to confirm that original key/value was transferred // This is the test that fails with JBCACHE-1549 assertEquals("Buddy should have data in backup tree", value, caches.get(1).get(backupFqn, key)); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/EmptyRegionTest.java0000644000175000017500000000566011120421070031666 0ustar moellermoellerpackage org.jboss.cache.buddyreplication; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.UnitTestCacheFactory; 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.AfterMethod; import org.testng.annotations.BeforeMethod; 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 AT jboss DOT org) */ @Test(groups = "functional", testName = "buddyreplication.EmptyRegionTest") 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(2); @BeforeMethod public void setUp() throws Exception { c1 = createCache(1, null, false, false, false); c1.getConfiguration().setUseRegionBasedMarshalling(true); c1.getConfiguration().setFetchInMemoryState(true); c2 = (CacheSPI) new UnitTestCacheFactory().createCache(c1.getConfiguration().clone(), false, getClass()); 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()); } @AfterMethod public void tearDown() { TestingUtil.killCaches(c1, c2); c1 = null; c2 = null; } 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"; c1.addCacheListener(new BuddyJoinListener()); // now start c2 c2.start(); // wait for buddy join notifications to complete. buddyJoinLatch.await(60, TimeUnit.SECONDS); // should not throw any exceptions!! // 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(); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyManagerTest.java0000644000175000017500000002122511111047320031763 0ustar moellermoeller/* * 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.CommandsFactoryImpl; 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.config.parsing.XmlConfigHelper; import org.jboss.cache.config.parsing.element.BuddyElementParser; 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 AT jboss DOT org) */ @Test(groups = "functional", testName = "buddyreplication.BuddyManagerTest") public class BuddyManagerTest { private static final String DUMMY_LOCAL_ADDRESS = "myLocalAddress:12345"; /** * Constructs a buddy manager using the default buddy locator but with some specific properties. * * @throws Exception */ public void testConstruction1() throws Exception { String xmlConfig = " \n" + " \n" + " \n" + " numBuddies = 3\n" + " \n" + " \n" + " "; BuddyReplicationConfig config = getBuddyReplicationConfig(xmlConfig); 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()); } private BuddyReplicationConfig getBuddyReplicationConfig(String xmlConfig) throws Exception { Element element = XmlConfigHelper.stringToElementInCoreNS(xmlConfig); BuddyElementParser replicationElementParser = new BuddyElementParser(); return replicationElementParser.parseBuddyElement(element); } /** * Constructs a buddy manager using a nonexistent buddy locator but with some specific properties. * * @throws Exception */ public void testConstruction2() throws Exception { String xmlConfig = " \n" + " \n" + " \n" + " numBuddies = 3\n" + " \n" + " \n" + " "; BuddyReplicationConfig config = getBuddyReplicationConfig(xmlConfig); 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 = ""; BuddyReplicationConfig config = getBuddyReplicationConfig(xmlConfig); 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 = ""; BuddyReplicationConfig config = getBuddyReplicationConfig(xmlConfig); 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 { String xmlConfig = ""; BuddyReplicationConfig config = getBuddyReplicationConfig(xmlConfig); bm = new BuddyManager(config); bm.injectDependencies(null, null, null, null, null, null, null, null, new BuddyFqnTransformer()); CommandsFactoryImpl commandsFactory = new CommandsFactoryImpl(); 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() { BuddyFqnTransformer fqnTransformer = new BuddyFqnTransformer(); Fqn x = Fqn.fromString("/x"); Fqn backup = fqnTransformer.getBackupFqn("y", x); assertEquals(x, fqnTransformer.getActualFqn(backup)); } } ././@LongLink0000000000000000000000000000017100000000000011564 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/Buddy2NodesBackupActivationInactivationTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/Buddy2NodesBackupActivationI0000644000175000017500000001150711131613416033255 0ustar moellermoeller/* * 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.Region; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.BuddyReplicationConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.*; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.util.ArrayList; /** * Tests handling of the buddy backup region during region * activation and inactivation * * @author Brian Stansberry */ @Test(groups = "functional", testName = "buddyreplication.Buddy2NodesBackupActivationInactivationTest") public class Buddy2NodesBackupActivationInactivationTest extends AbstractNodeBasedBuddyTest { public static final Fqn A = Fqn.fromString("/a"); public static final Fqn A_B = Fqn.fromString("/a/b"); public static final String JOE = "JOE"; @BeforeClass public void createCaches() throws Exception { super.caches = new ArrayList>(2); caches.add(createCache()); caches.add(createCache()); } public void testBuddyBackupActivation() throws Exception { CacheSPI cache1 = caches.get(0); CacheSPI cache2 = caches.get(1); Fqn A = Fqn.fromString("/a"); TestingUtil.blockUntilViewsReceived(VIEW_BLOCK_TIMEOUT, cache1, cache2); // create the regions on the two cachePool 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); waitForBuddy(cache2, cache1, true); waitForBuddy(cache1, cache2, true); // TestingUtil.sleepThread(getSleepTimeout()); c2.activate(); 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 = caches.get(0); CacheSPI cache2 = caches.get(1); 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); 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 = caches.get(0); 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() throws Exception { CacheMode mode = CacheMode.REPL_SYNC; Configuration c = UnitTestConfigurationFactory.createConfiguration(mode); CacheSPI cache = (CacheSPI) new UnitTestCacheFactory().createCache(c, false, getClass()); cache.getConfiguration().setUseRegionBasedMarshalling(true); cache.getConfiguration().setInactiveOnStartup(true); cache.getConfiguration().setBuddyReplicationConfig(getBuddyConfig()); cache.create(); cache.start(); return cache; } private BuddyReplicationConfig getBuddyConfig() throws Exception { BuddyReplicationConfig brc = new BuddyReplicationConfig(); brc.setEnabled(true); brc.setAutoDataGravitation(false); return brc; } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/EvictionOfBuddyBackupsTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/EvictionOfBuddyBackupsTest.j0000644000175000017500000000646111135437774033332 0ustar moellermoellerpackage org.jboss.cache.buddyreplication; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.eviction.LRUAlgorithmConfig; import org.jboss.cache.eviction.NullEvictionAlgorithmConfig; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.EvictionController; import org.jboss.cache.util.internals.replicationlisteners.ReplicationListener; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Tests the eviction of buddy backup regions * * @author Manik Surtani (manik AT jboss DOT org) * @since 2.2.0 */ @Test(groups = "functional", testName = "buddyreplication.EvictionOfBuddyBackupsTest") public class EvictionOfBuddyBackupsTest extends BuddyReplicationTestsBase { private CacheSPI cache1, cache2; private Fqn fqn = Fqn.fromString("/a/b/c"); private EvictionController ec1; private EvictionController ec2; @BeforeMethod public void setUp() throws Exception { cache1 = createCache(1, null, true, false); cache1.getConfiguration().setEvictionConfig(getEvictionConfig()); cache1.start(); ec1 = new EvictionController(cache1); cache2 = (CacheSPI) new UnitTestCacheFactory().createCache(cache1.getConfiguration().clone(), getClass()); ec2 = new EvictionController(cache2); waitForSingleBuddy(cache1, cache2); TestingUtil.blockUntilViewsReceived(60000, cache1, cache2); } @AfterMethod @Override public void tearDown() throws Exception { super.tearDown(); TestingUtil.killCaches(cache1, cache2); cache1 = null; cache2 = null; } private EvictionConfig getEvictionConfig() { EvictionConfig c = new EvictionConfig(); EvictionRegionConfig defaultRegion = new EvictionRegionConfig(Fqn.ROOT, new NullEvictionAlgorithmConfig()); c.setDefaultEvictionRegionConfig(defaultRegion); c.setWakeupInterval(0); LRUAlgorithmConfig lru = new LRUAlgorithmConfig(1000, 1000); EvictionRegionConfig subregion = new EvictionRegionConfig(fqn, lru); c.addEvictionRegionConfig(subregion); return c; } public void testEvictionOfBackupRegions() throws Exception { ReplicationListener replicationListener2 = ReplicationListener.getReplicationListener(cache2); replicationListener2.expect(PutKeyValueCommand.class); cache1.put(fqn, "k", "v"); replicationListener2.waitForReplicationToOccur(); 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(1100); ec1.startEviction(); ec2.startEviction(); 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"; } } ././@LongLink0000000000000000000000000000016700000000000011571 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/DataGravitationCleanupFromDefunctTreeTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/DataGravitationCleanupFromDe0000644000175000017500000001102111242441121033322 0ustar moellermoellerpackage org.jboss.cache.buddyreplication; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.notifications.annotation.CacheListener; import org.jboss.cache.notifications.annotation.NodeCreated; import org.jboss.cache.notifications.event.NodeCreatedEvent; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.Test; /** * Test for handling of JBCACHE-1530. * * @author Brian Stansberry */ @Test(groups = "functional", testName = "buddyreplication.DataGravitationCleanupFromDefunctTreeTest") public class DataGravitationCleanupFromDefunctTreeTest extends BuddyReplicationTestsBase { @CacheListener public static class GravitationBlocker { private final Fqn toBlock; private final CountDownLatch toTrigger; private final CountDownLatch toAwait; private boolean blocked; GravitationBlocker(Fqn toBlock, CountDownLatch toTrigger, CountDownLatch toAwait) { this.toBlock = toBlock; this.toTrigger = toTrigger; this.toAwait = toAwait; } @NodeCreated public void nodeAdded(NodeCreatedEvent event) { if (!blocked && event.isOriginLocal() && event.getFqn().equals(toBlock)) { blocked = true; toTrigger.countDown(); try { // System.out.println("blocking " + System.currentTimeMillis()); toAwait.await(10, TimeUnit.SECONDS); // System.out.println("released " + System.currentTimeMillis()); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } } public void testOwnerDiesInMidGravitation() throws Exception { final List> caches = createCaches(1, 4, false, false, false, false); cachesTL.set(caches); for (CacheSPI c : caches) { c.getConfiguration().setFetchInMemoryState(false); c.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); c.start(); } waitForBuddy(caches.get(0), caches.get(1), false); waitForBuddy(caches.get(1), caches.get(2), false); waitForBuddy(caches.get(2), caches.get(3), false); waitForBuddy(caches.get(3), caches.get(0), false); Thread.sleep(2000);//wait for state transfer final Fqn fqn = Fqn.fromString("/0"); caches.get(0).put(fqn, "k", "v"); Fqn backup = fqnTransformer.getBackupFqn(caches.get(0).getLocalAddress(), fqn); assert (caches.get(1).exists(backup)); CountDownLatch toTrigger = new CountDownLatch(1); CountDownLatch toAwait = new CountDownLatch(1); GravitationBlocker blocker = new GravitationBlocker(fqn, toTrigger, toAwait); caches.get(2).addCacheListener(blocker); Future future = Executors.newSingleThreadExecutor().submit(new Runnable() { public void run() { // Trigger a gravitation caches.get(2).getInvocationContext().getOptionOverrides().setForceDataGravitation(true); caches.get(2).get(fqn, "k"); } }); assert (toTrigger.await(5, TimeUnit.SECONDS)); // Gravitation is now blocking on adding data to cache2 caches.get(0).stop(); // System.out.println("stopped 0 " + System.currentTimeMillis()); // TODO need a way to know when this is done TestingUtil.sleepThread(1000); // buddy group re-formation is async toAwait.countDown(); // gravitation can now complete assert (future.get(5, TimeUnit.SECONDS) == null); // and it now is complete assert (blocker.blocked); backup = fqnTransformer.getBackupFqn(caches.get(2).getLocalAddress(), fqn); assert (caches.get(3).exists(backup)); assert ("v".equals(caches.get(2).put(fqn, "k", "v1"))); // Now we gravitate to cache1. IF it has original "v" in its :DEAD tree // it will gravitate that rather than asking cache3 and the assert will fail caches.get(1).getInvocationContext().getOptionOverrides().setForceDataGravitation(true); Object val = caches.get(1).get(fqn, "k"); assert "v1".equals(val) : val + " is 'v1'"; } } ././@LongLink0000000000000000000000000000015600000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyAssignmentStateTransferTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyAssignmentStateTransfer0000644000175000017500000002033211111047320033445 0ustar moellermoeller/* * 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.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * Tests how groups are formed and disbanded * * @author Manik Surtani (manik AT jboss DOT org) */ @Test(groups = {"functional", "jgroups"}, testName = "buddyreplication.BuddyAssignmentStateTransferTest") 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(); cachesTL.get().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 { List> caches = new ArrayList>(); cachesTL.set(caches); 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(2); //first cache should also wait for buddy groups changes caches.get(0).addCacheListener(new BuddyJoinedListener(latch)); 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 { List> caches = new ArrayList>(); cachesTL.set(caches); 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 { List> caches = new ArrayList>(); cachesTL.set(caches); 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")); } }././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/Buddy3NodesWithFailoverTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/Buddy3NodesWithFailoverTest.0000644000175000017500000002163011136045351033237 0ustar moellermoeller/* * 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.commands.remote.DataGravitationCleanupCommand; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.replicationlisteners.ReplicationListener; import org.jgroups.JChannel; import org.jgroups.Address; import org.jgroups.protocols.DISCARD; import static org.testng.AssertJUnit.*; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.Test; import java.util.List; /** * Tests behaviour when data owners fail - essentially this tests data gravitation * * @author Manik Surtani (manik AT jboss DOT org) */ @Test(groups = "functional", testName = "buddyreplication.Buddy3NodesWithFailoverTest") public class Buddy3NodesWithFailoverTest extends BuddyReplicationTestsBase { protected boolean optimisticLocks = false; protected String key = "key"; protected String value = "value"; BuddyFqnTransformer fqnTransformer = new BuddyFqnTransformer(); public void testSubtreeRetrieval() throws Exception { List> caches = createCaches(3, false, true, optimisticLocks); cachesTL.set(caches); 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: ReplicationListener replListener0 = ReplicationListener.getReplicationListener(caches.get(0)); ReplicationListener replListener1 = ReplicationListener.getReplicationListener(caches.get(1)); replListener0.expect(DataGravitationCleanupCommand.class); replListener1.expect(DataGravitationCleanupCommand.class); caches.get(2).getNode(fqn); // expectWithTx entire subtree to gravitate. replListener0.waitForReplicationToOccur(); // cleanup commands are async replListener1.waitForReplicationToOccur(); // also wait untill the backup is cleaned 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); } public void testDataGravitationKillOwner() throws Exception { List> caches = createCaches(3, false, true); Fqn fqn = Fqn.fromString("/test"); Fqn backupFqn = fqnTransformer.getBackupFqn(caches.get(2).getLocalAddress(), fqn); TestingUtil.dumpCacheContents(caches); ReplicationListener replListener0 = ReplicationListener.getReplicationListener(caches.get(0)); replListener0.expect(PutKeyValueCommand.class); caches.get(2).put(fqn, key, value); replListener0.waitForReplicationToOccur(); TestingUtil.dumpCacheContents(caches); assertEquals("Value should exist", value, caches.get(2).get(fqn, key)); TestingUtil.dumpCacheContents(caches); // use exists instead of get() to prevent going up the interceptor stack assertTrue("Should be false", !caches.get(0).exists(fqn)); assertTrue("Should be false", !caches.get(1).exists(fqn)); assertTrue("Value be true", caches.get(0).exists(backupFqn)); assertFalse("Should be false", caches.get(1).exists(backupFqn)); assertFalse("Should be false", caches.get(2).exists(backupFqn)); Address cache2Addr = caches.get(2).getLocalAddress(); // forcefully kill data owner. caches.get(2).stop(); caches.get(2).destroy(); TestingUtil.blockUntilViewsReceived(10000, caches.get(0), caches.get(1)); waitForSingleBuddy(caches.get(0), caches.get(1)); // TestingUtil.dumpCacheContents(cachePool); // assert that the remaining cachePool have picked new buddies. Cache 1 should have cache 2's backup data. assert caches.get(0).peek(fqnTransformer.getBackupRoot(caches.get(1).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(0).peek(fqnTransformer.getBackupRoot(cache2Addr), false) == null : "Should not have dead node as a backup root."; assert caches.get(0).peek(Fqn.fromRelativeElements(fqnTransformer.getDeadBackupRoot(cache2Addr), 1), false) != null : "Should have dead node as a defunct 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 have new buddy's backup root."; assert caches.get(1).peek(fqnTransformer.getBackupRoot(cache2Addr), false) == null : "Should not have dead node as a backup root."; assert caches.get(1).peek(Fqn.fromRelativeElements(fqnTransformer.getDeadBackupRoot(cache2Addr), 1), false) == null : "Should not have dead node as a defunct backup root."; replListener0.expect(DataGravitationCleanupCommand.class); // 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(1).get(fqn, key)); replListener0.waitForReplicationToOccur(); // use exists instead of get() to prevent going up the interceptor stack assert caches.get(0).peek(fqnTransformer.getBackupRoot(caches.get(1).getLocalAddress()), false) != null : "Should have new buddy's backup root."; assert caches.get(0).peek(fqnTransformer.getBackupRoot(caches.get(0).getLocalAddress()), false) == null : "Should not have self as a backup root."; assert caches.get(0).peek(fqnTransformer.getBackupRoot(cache2Addr), false) == null : "Should not have dead node as a backup root."; assert caches.get(0).peek(Fqn.fromRelativeElements(fqnTransformer.getDeadBackupRoot(cache2Addr), 1), false) == null : "Should not have dead node as a defunct backup root."; assert caches.get(0).peek(fqnTransformer.getDeadBackupRoot(cache2Addr), false) == null : "Should not have dead node as a defunct 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 have new buddy's backup root."; assert caches.get(1).peek(fqnTransformer.getBackupRoot(cache2Addr), false) == null : "Should not have dead node as a backup root."; assert caches.get(1).peek(Fqn.fromRelativeElements(fqnTransformer.getDeadBackupRoot(cache2Addr), 1), false) == null : "Should not have dead node as a defunct backup root."; assertTrue("Should be false", !caches.get(0).exists(fqn)); // the old backup should no longer exist assertFalse("Should be null", caches.get(0).exists(backupFqn)); assertFalse("Should be null", caches.get(1).exists(backupFqn)); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/mvcc/0000755000175000017500000000000011675221467026671 5ustar moellermoeller././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/mvcc/Buddy4Nodes2BackupsTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/mvcc/Buddy4Nodes2BackupsTest0000644000175000017500000000073411120273541033160 0ustar moellermoellerpackage org.jboss.cache.buddyreplication.mvcc; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "buddyreplication.mvcc.Buddy4Nodes2BackupsTest") public class Buddy4Nodes2BackupsTest extends org.jboss.cache.buddyreplication.Buddy4Nodes2BackupsTest { @Override protected NodeLockingScheme getNonOptimisticLockingScheme() { return NodeLockingScheme.MVCC; } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/mvcc/GravitationCleanupTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/mvcc/GravitationCleanupTest.0000644000175000017500000000073111120273541033313 0ustar moellermoellerpackage org.jboss.cache.buddyreplication.mvcc; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "buddyreplication.mvcc.GravitationCleanupTest") public class GravitationCleanupTest extends org.jboss.cache.buddyreplication.GravitationCleanupTest { @Override protected NodeLockingScheme getNonOptimisticLockingScheme() { return NodeLockingScheme.MVCC; } } ././@LongLink0000000000000000000000000000016200000000000011564 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/mvcc/MvccBuddy3NodesWithFailoverTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/mvcc/MvccBuddy3NodesWithFail0000644000175000017500000000102411131431461033155 0ustar moellermoellerpackage org.jboss.cache.buddyreplication.mvcc; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.buddyreplication.Buddy3NodesWithFailoverTest; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "buddyreplication.mvcc.MvccBuddy3NodesWithFailoverTest") public class MvccBuddy3NodesWithFailoverTest extends Buddy3NodesWithFailoverTest { @Override protected NodeLockingScheme getNonOptimisticLockingScheme() { return NodeLockingScheme.MVCC; } } ././@LongLink0000000000000000000000000000015500000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/mvcc/Buddy2NodesNoBuddyPoolTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/mvcc/Buddy2NodesNoBuddyPoolT0000644000175000017500000000105111120273541033157 0ustar moellermoellerpackage org.jboss.cache.buddyreplication.mvcc; import org.jboss.cache.config.Configuration; import org.testng.annotations.Test; /** * * @author Mircea.Markus@jboss.com */ @Test (groups = "functional", testName = "buddyreplication.mvcc.Buddy2NodesNoBuddyPoolTest") public class Buddy2NodesNoBuddyPoolTest extends org.jboss.cache.buddyreplication.Buddy2NodesNoBuddyPoolTest { @Override protected Configuration.NodeLockingScheme getNonOptimisticLockingScheme() { return Configuration.NodeLockingScheme.MVCC; } } ././@LongLink0000000000000000000000000000017600000000000011571 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/mvcc/Buddy2NodesBackupActivationInactivationTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/mvcc/Buddy2NodesBackupActiva0000644000175000017500000000103011120273541033167 0ustar moellermoellerpackage org.jboss.cache.buddyreplication.mvcc; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "buddyreplication.mvcc.Buddy2NodesBackupActivationInactivationTest") public class Buddy2NodesBackupActivationInactivationTest extends org.jboss.cache.buddyreplication.Buddy2NodesBackupActivationInactivationTest { @Override protected NodeLockingScheme getNonOptimisticLockingScheme() { return NodeLockingScheme.MVCC; } } ././@LongLink0000000000000000000000000000016500000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/mvcc/Buddy3NodesFailedRemotePrepareTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/mvcc/Buddy3NodesFailedRemote0000644000175000017500000000077611236274723033226 0ustar moellermoellerpackage org.jboss.cache.buddyreplication.mvcc; import org.jboss.cache.config.Configuration; import org.testng.annotations.Test; @Test(groups = "functional", testName = "buddyreplication.mvcc.Buddy3NodesFailedRemotePrepareTest") public class Buddy3NodesFailedRemotePrepareTest extends org.jboss.cache.buddyreplication.Buddy3NodesFailedRemotePrepareTest { @Override protected Configuration.NodeLockingScheme getNonOptimisticLockingScheme() { return Configuration.NodeLockingScheme.MVCC; } } ././@LongLink0000000000000000000000000000015500000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/mvcc/Buddy3NodesNoStateTransfer.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/mvcc/Buddy3NodesNoStateTrans0000644000175000017500000000104311120273541033224 0ustar moellermoellerpackage org.jboss.cache.buddyreplication.mvcc; import org.jboss.cache.config.Configuration; import org.testng.annotations.Test; /** * @author Mircea.Markus@jboss.com */ @Test(groups = "functional", testName = "buddyreplication.mvcc.Buddy3NodesNoStateTransfer") public class Buddy3NodesNoStateTransfer extends org.jboss.cache.buddyreplication.Buddy3NodesNoStateTransfer { @Override protected Configuration.NodeLockingScheme getNonOptimisticLockingScheme() { return Configuration.NodeLockingScheme.MVCC; } } ././@LongLink0000000000000000000000000000016300000000000011565 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/mvcc/BuddyAssignmentStateTransferTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/mvcc/BuddyAssignmentStateTra0000644000175000017500000000076711111047320033351 0ustar moellermoellerpackage org.jboss.cache.buddyreplication.mvcc; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "buddyreplication.mvcc.BuddyAssignmentStateTransferTest") public class BuddyAssignmentStateTransferTest extends org.jboss.cache.buddyreplication.BuddyAssignmentStateTransferTest { @Override protected NodeLockingScheme getNonOptimisticLockingScheme() { return NodeLockingScheme.MVCC; } } ././@LongLink0000000000000000000000000000017300000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/mvcc/Buddy3NodesNoPoolWithDataGravitationTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/mvcc/Buddy3NodesNoPoolWithDa0000644000175000017500000000111611120273541033147 0ustar moellermoellerpackage org.jboss.cache.buddyreplication.mvcc; import org.testng.annotations.Test; import org.jboss.cache.config.Configuration; /** * @author Mircea.Markus@jboss.com */ @Test(groups = "functional", testName = "buddyreplication.mvcc.Buddy3NodesNoPoolWithDataGravitationTest") public class Buddy3NodesNoPoolWithDataGravitationTest extends org.jboss.cache.buddyreplication.Buddy3NodesNoPoolWithDataGravitationTest { @Override protected Configuration.NodeLockingScheme getNonOptimisticLockingScheme() { return Configuration.NodeLockingScheme.MVCC; } } ././@LongLink0000000000000000000000000000017300000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/mvcc/Buddy3NodesWithPoolNoDataGravitationTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/mvcc/Buddy3NodesWithPoolNoDa0000644000175000017500000000112011146273534033154 0ustar moellermoellerpackage org.jboss.cache.buddyreplication.mvcc; import org.jboss.cache.config.Configuration; import org.testng.annotations.Test; /** * @author Mircea.Markus@jboss.com */ @Test(groups = "functional", testName = "buddyreplication.mvcc.Buddy3NodesWithPoolNoDataGravitationTest") public class Buddy3NodesWithPoolNoDataGravitationTest extends org.jboss.cache.buddyreplication.Buddy3NodesWithPoolNoDataGravitationTest { @Override protected Configuration.NodeLockingScheme getNonOptimisticLockingScheme() { return Configuration.NodeLockingScheme.MVCC; } } ././@LongLink0000000000000000000000000000015500000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/mvcc/BuddyReplicationRejoinTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/mvcc/BuddyReplicationRejoinT0000644000175000017500000000074511111047320033331 0ustar moellermoellerpackage org.jboss.cache.buddyreplication.mvcc; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "buddyreplication.mvcc.BuddyReplicationRejoinTest") public class BuddyReplicationRejoinTest extends org.jboss.cache.buddyreplication.BuddyReplicationRejoinTest { @Override protected NodeLockingScheme getNonOptimisticLockingScheme() { return NodeLockingScheme.MVCC; } } ././@LongLink0000000000000000000000000000016600000000000011570 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/mvcc/BuddyReplicationWithCacheLoaderTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/mvcc/BuddyReplicationWithCac0000644000175000017500000000100011111047320033262 0ustar moellermoellerpackage org.jboss.cache.buddyreplication.mvcc; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "buddyreplication.mvcc.BuddyReplicationWithCacheLoaderTest") public class BuddyReplicationWithCacheLoaderTest extends org.jboss.cache.buddyreplication.BuddyReplicationWithCacheLoaderTest { @Override protected NodeLockingScheme getNonOptimisticLockingScheme() { return NodeLockingScheme.MVCC; } } ././@LongLink0000000000000000000000000000017100000000000011564 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/mvcc/Buddy3NodesNoPoolNoDataGravitationTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/mvcc/Buddy3NodesNoPoolNoData0000644000175000017500000000111211135117554033140 0ustar moellermoellerpackage org.jboss.cache.buddyreplication.mvcc; import org.jboss.cache.config.Configuration; import org.testng.annotations.Test; /** * @author Mircea.Markus@jboss.com */ @Test(groups = "functional", testName = "buddyreplication.mvcc.Buddy3NodesNoPoolNoDataGravitationTest") public class Buddy3NodesNoPoolNoDataGravitationTest extends org.jboss.cache.buddyreplication.Buddy3NodesNoPoolNoDataGravitationTest { @Override protected Configuration.NodeLockingScheme getNonOptimisticLockingScheme() { return Configuration.NodeLockingScheme.MVCC; } } ././@LongLink0000000000000000000000000000016600000000000011570 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/mvcc/BuddyReplicationWithPassivationTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/mvcc/BuddyReplicationWithPas0000644000175000017500000000100011111047320033317 0ustar moellermoellerpackage org.jboss.cache.buddyreplication.mvcc; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "buddyreplication.mvcc.BuddyReplicationWithPassivationTest") public class BuddyReplicationWithPassivationTest extends org.jboss.cache.buddyreplication.BuddyReplicationWithPassivationTest { @Override protected NodeLockingScheme getNonOptimisticLockingScheme() { return NodeLockingScheme.MVCC; } } ././@LongLink0000000000000000000000000000016100000000000011563 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyReplicationWithCacheLoaderTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyReplicationWithCacheLoa0000644000175000017500000003425311135332460033333 0ustar moellermoeller/* * 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.UnitTestCacheFactory; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.commands.write.PutDataMapCommand; import org.jboss.cache.config.Configuration; import org.jboss.cache.loader.CacheLoader; import static org.jboss.cache.util.SingleBuddyGravitationHelper.expectGravitation; import static org.jboss.cache.util.SingleBuddyGravitationHelper.*; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.SingleBuddyGravitationHelper; import static org.jboss.cache.util.TestingUtil.dumpCacheContents; import org.jboss.cache.util.internals.replicationlisteners.ReplicationListener; 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 use of the data gravitator alongside other cache loaders as well as data gravitator options such as removeOnFind. * * @author Manik Surtani (manik AT jboss DOT org) */ @Test(groups = "functional", testName = "buddyreplication.BuddyReplicationWithCacheLoaderTest") 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 cachePool List> caches = createCachesWithCacheLoader(3, autoGravitate, true, passivation); cachesTL.set(caches); List replicationListeners = new ArrayList(); replicationListeners.add(ReplicationListener.getReplicationListener(caches.get(0))); replicationListeners.add(ReplicationListener.getReplicationListener(caches.get(1))); replicationListeners.add(ReplicationListener.getReplicationListener(caches.get(2))); CacheLoader[] loaders = getLoaders(caches); // cleanup for (int i = 0; i < 3; i++) loaders[i].remove(Fqn.ROOT); // put stuff in cache0 replicationListeners.get(1).expect(PutKeyValueCommand.class); caches.get(0).put(fqn, key, value); replicationListeners.get(1).waitForReplicationToOccur(); // make sure there are no locks. assertNoLocks(caches); dumpCacheContents(caches); // request data from cache2 if (!autoGravitate) caches.get(2).getInvocationContext().getOptionOverrides().setForceDataGravitation(true); inReplicationListeners(replicationListeners).dataWillGravitateFrom(0).to(2); // should cause a gravitation event assertEquals(value, caches.get(2).get(fqn, key)); expectGravitation(); assertNoLocks(caches); 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. */ public void testWithDataGravitationEvictOnFindNoAuto() throws Exception { dataGravitationEvictionTest(false); } private void dataGravitationEvictionTest(boolean autoGravitate) throws Exception { // create 3 cachePool List> caches = createCachesWithCacheLoader(3, autoGravitate, false, passivation); ReplicationListener replListener0 = ReplicationListener.getReplicationListener(caches.get(0)); ReplicationListener replListener1 = ReplicationListener.getReplicationListener(caches.get(1)); ReplicationListener replListener2 = ReplicationListener.getReplicationListener(caches.get(2)); cachesTL.set(caches); 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 replListener1.expect(PutKeyValueCommand.class); caches.get(0).put(fqn, key, value); replListener1.waitForReplicationToOccur(); // request data from cache2 if (!autoGravitate) caches.get(2).getInvocationContext().getOptionOverrides().setForceDataGravitation(true); // should cause a gravitation event SingleBuddyGravitationHelper.inReplicationListeners(replListener0, replListener1, replListener2).dataWillGravitateFrom(0).to(2); assertEquals(value, caches.get(2).get(fqn, key)); expectGravitation(); // USE REPLICATION LISTENERS!!!! // 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(); Configuration cfg0 = cfg1.clone(); CacheSPI cache0 = (CacheSPI) new UnitTestCacheFactory().createCache(cfg0, false, getClass()); // Store them for the teardown method List> caches = new ArrayList>(); cachesTL.set(caches); 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 cache0.evict(foo); cache1.evict(backupFoo); // 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 its 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(); Configuration cfg1 = cfg0.clone(); CacheSPI cache1 = (CacheSPI) new UnitTestCacheFactory().createCache(cfg1, false, getClass()); Configuration cfg2 = cfg0.clone(); CacheSPI cache2 = (CacheSPI) new UnitTestCacheFactory().createCache(cfg2, false, getClass()); // Store them for the teardown method List> caches = new ArrayList>(); cachesTL.set(caches); caches.add(cache0); caches.add(cache1); caches.add(cache2); cache0.start(); cache1.start(); cache2.start(); TestingUtil.blockUntilViewsReceived(caches.toArray(new Cache[caches.size()]), 60000); 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"; cache0.evict(foo); cache1.evict(backupFoo); // 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")); } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyReplicationRejoinTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyReplicationRejoinTest.j0000644000175000017500000001703611146273534033366 0ustar moellermoellerpackage 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.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.BuddyReplicationConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.util.CachePrinter; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; 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(groups = "functional", testName = "buddyreplication.BuddyReplicationRejoinTest") 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; @BeforeMethod 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 UnitTestCacheFactory().createCache(c, false, getClass()); cache2 = new UnitTestCacheFactory().createCache(c.clone(), false, getClass()); } @AfterMethod public void tearDown() throws Exception { super.tearDown(); TestingUtil.killCaches(cache1, cache2); cache1 = null; cache2 = null; } /** * 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. */ 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 UnitTestCacheFactory().createCache(cfg, getClass()); 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"); } } } ././@LongLink0000000000000000000000000000016600000000000011570 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/Buddy3NodesNoPoolWithDataGravitationTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/Buddy3NodesNoPoolWithDataGra0000644000175000017500000005175611135575150033224 0ustar moellermoellerpackage org.jboss.cache.buddyreplication; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.commands.remote.DataGravitationCleanupCommand; import org.jboss.cache.commands.write.PutKeyValueCommand; 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.TestingUtil; import org.jboss.cache.util.internals.replicationlisteners.ReplicationListener; import static org.testng.AssertJUnit.*; import static org.jboss.cache.util.SingleBuddyGravitationHelper.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import javax.transaction.TransactionManager; import java.util.List; import java.util.ArrayList; /** * @author Mircea.Markus@jboss.com */ @Test(groups = "functional", testName = "buddyreplication.Buddy3NodesNoPoolWithDataGravitationTest") public class Buddy3NodesNoPoolWithDataGravitationTest extends AbstractNodeBasedBuddyTest { private Fqn fqn = Fqn.fromString("test"); private String key = "key"; private String value = "value"; ReplicationListener replicationListener0; ReplicationListener replicationListener1; ReplicationListener replicationListener2; List listeners; @BeforeClass public void createCaches() throws Exception { caches = createCaches(3, false, true); replicationListener0 = ReplicationListener.getReplicationListener(caches.get(0)); replicationListener1 = ReplicationListener.getReplicationListener(caches.get(1)); replicationListener2 = ReplicationListener.getReplicationListener(caches.get(2)); listeners = new ArrayList(); listeners.add(replicationListener0); listeners.add(replicationListener1); listeners.add(replicationListener2); } //this is needed here as tesng ignores the call from base class @AfterMethod @Override public void tearDown() throws Exception { super.tearDown(); } public void testDataGravitationDontKillOwner() throws Exception { Fqn fqn = Fqn.fromString("/test"); Fqn backupFqn = fqnTransformer.getBackupFqn(caches.get(0).getLocalAddress(), fqn); TestingUtil.dumpCacheContents(caches); caches.get(0).put(fqn, key, value); assertEquals("Value should exist", value, caches.get(0).get(fqn, key)); // 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)); inReplicationListeners(listeners).dataWillGravitateFrom(0).to(2); // 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)); expectGravitation(); // 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 assertTrue("Should be false", !caches.get(0).exists(fqn)); assertTrue("Should be false", !caches.get(1).exists(fqn)); // the old backup should no longer exist 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) 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)); } public void testTransactionsCommit() throws Exception { 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"); 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)); replicationListener0.expect(DataGravitationCleanupCommand.class); replicationListener1.expect(DataGravitationCleanupCommand.class); txman.begin(); caches.get(2).get(fqn, key); 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(); replicationListener0.waitForReplicationToOccur(); replicationListener1.waitForReplicationToOccur(); 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); } public void testTransactionsRollback() throws Exception { TestingUtil.dumpCacheContents(caches.get(0)); TestingUtil.dumpCacheContents(caches.get(1)); TestingUtil.dumpCacheContents(caches.get(2)); 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"); 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(); caches.get(2).get(fqn, key); 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(); 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); } public void testSubtreeRetrieval() throws Exception { 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: inReplicationListeners(listeners).dataWillGravitateFrom(0).to(2); caches.get(2).getNode(fqn); // expect entire subtree to gravitate. expectGravitation(); 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)); for (CacheSPI cache : caches) { assertTrue(!cache.exists(backupFqn)); assertTrue(!cache.exists(backupFqn2)); } assertNoLocks(caches); } public void testStaleRegionOnDataOwner() throws Exception { // add some stuff on the primary CacheSPI first = caches.get(0); CacheSPI second = caches.get(1); CacheSPI third = caches.get(2); first.put(fqn, key, value); assert first.peek(fqn, false) != null : "Should have data"; assert first.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(third.getLocalAddress())), false) != null : "Should have backup node for second"; assert first.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(first.getLocalAddress())), false) == null : "Should NOT have backup node for self!"; assert second.peek(fqn, false) == null : "Should not have data"; assert second.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(second.getLocalAddress())), false) == null : "Should NOT have backup node for self!"; assert second.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(first.getLocalAddress())), false) != null : "Should have backup node for second"; assert second.peek(fqnTransformer.getBackupFqn(first.getLocalAddress(), fqn), false) != null : "Should have backup data"; inReplicationListeners(listeners).dataWillGravitateFrom(0).to(1); // now do a gravitate call. assert second.get(fqn, key).equals(value) : "Data should have gravitated!"; // gravitation cleanup calls are async. expectGravitation(); assert second.peek(fqn, false) != null : "Should have data"; assert second.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(first.getLocalAddress())), false) != null : "Should have backup node for second"; assert second.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(second.getLocalAddress())), false) == null : "Should NOT have backup node for self!"; assert third.peek(fqn, false) == null : "Should not have data"; assert third.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(third.getLocalAddress())), false) == null : "Should NOT have backup node for self!"; assert third.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(second.getLocalAddress())), false) != null : "Should have backup node for second"; assert third.peek(fqnTransformer.getBackupFqn(second.getLocalAddress(), fqn), false) != null : "Should have backup data"; } public void testStaleRegionOnBuddy() throws Exception { Fqn fqn = Fqn.fromString("/a/b/c"); Object key = "key", value = "value"; // 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); 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"; inReplicationListeners(listeners).dataWillGravitateFrom(0).to(2); // now do a gravitate call. assert thirdInstance.get(fqn, key).equals(value) : "Data should have gravitated!"; expectGravitation(); 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"; } public void testConcurrency() throws Exception { Fqn f = Fqn.fromString("/a/b/c"); String k = "k"; String v = "v"; for (int i = 0; i < 10; i++) { caches.get(0).put(Fqn.fromRelativeElements(f, i), k, v); assert v.equals(caches.get(1).get(Fqn.fromRelativeElements(f, i), k)); assert v.equals(caches.get(1).get(Fqn.fromRelativeElements(f, i), k)); } } @CacheListener public static class CacheBlockListener { private int blocks = 0; @CacheBlocked public void processBlock(Event e) { if (e.isPre()) { synchronized (this) { blocks++; notifyAll(); } } } @CacheUnblocked public void processUnblock(Event e) { if (e.isPre()) { 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; */ } } } @Test(enabled = true, dependsOnMethods = {"testConcurrency", "testStaleRegionOnBuddy", "testStaleRegionOnDataOwner", "testDataGravitationDontKillOwner", "testTransactionsCommit", "testTransactionsRollback", "testSubtreeRetrieval"}) public void testCompleteStateSurvival() throws Exception { replicationListener1.expect(PutKeyValueCommand.class); caches.get(0).put("/0", "key", "value"); replicationListener1.waitForReplicationToOccur(); replicationListener2.expect(PutKeyValueCommand.class); caches.get(1).put("/1", "key", "value"); replicationListener2.waitForReplicationToOccur(); replicationListener0.expect(PutKeyValueCommand.class); caches.get(2).put("/2", "key", "value"); replicationListener0.waitForReplicationToOccur(); caches.get(2).stop(); TestingUtil.blockUntilViewsReceived(60000, true, caches.get(0), caches.get(1)); assertEquals("value", caches.get(0).get("/2", "key")); 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. assertEquals("value", caches.get(0).get("/1", "key")); assertEquals("value", caches.get(0).get("/2", "key")); caches.get(0).stop(); //uncoment this if you want to run this test more than once // caches = createCaches(3, false, true); // replicationListener0 = ReplicationListener.getReplicationListener(caches.get(0)); // replicationListener1 = ReplicationListener.getReplicationListener(caches.get(1)); // replicationListener2 = ReplicationListener.getReplicationListener(caches.get(2)); // waitForSingleBuddy(caches); } } ././@LongLink0000000000000000000000000000016600000000000011570 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/Buddy3NodesWithPoolNoDataGravitationTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/Buddy3NodesWithPoolNoDataGra0000644000175000017500000000574211120421070033177 0ustar moellermoellerpackage org.jboss.cache.buddyreplication; import org.jboss.cache.CacheSPI; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.*; import java.util.List; import java.util.Map; /** * @author Mircea.Markus@jboss.com */ @Test(groups = "functional", testName = "buddyreplication.Buddy3NodesWithPoolNoDataGravitationTest") public class Buddy3NodesWithPoolNoDataGravitationTest extends AbstractNodeBasedBuddyTest { @BeforeClass public void createCaches() throws Exception { caches = createCaches(3, true, false); } public void testChangingBuddyPoolMembership() throws Exception { 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(2).stop(); CacheSPI newlyCreated = createCache(1, "Z"); waitForSingleBuddy(caches.get(0), caches.get(1), newlyCreated); // TestingUtil.blockUntilViewsReceived(cachePool.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", "B", map.get(caches.get(1).getLocalAddress())); assertEquals("Failed on cache 1", "Z", map.get(newlyCreated.getLocalAddress())); // now test against each other checkConsistentPoolState(caches); newlyCreated.stop(); newlyCreated.destroy(); waitForSingleBuddy(caches.get(0), caches.get(1)); caches.get(2).start(); waitForSingleBuddy(caches.get(0), caches.get(1), caches.get(2)); } public void test3CachesWithPoolNames() throws Exception { long st = System.currentTimeMillis(); 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())); } public void testBuddyPoolSync() throws Exception { 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); } } ././@LongLink0000000000000000000000000000016400000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/Buddy3NodesNoPoolNoDataGravitationTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/Buddy3NodesNoPoolNoDataGravi0000644000175000017500000001413311136163156033210 0ustar moellermoellerpackage org.jboss.cache.buddyreplication; import org.jboss.cache.Cache; import org.jboss.cache.Fqn; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.replicationlisteners.ReplicationListener; import static org.testng.AssertJUnit.*; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import static org.jboss.cache.util.SingleBuddyGravitationHelper.*; import java.util.ArrayList; import java.util.List; /** * @author Mircea.Markus@jboss.com */ @Test(groups = "functional", invocationCount = 10, testName = "buddyreplication.Buddy3NodesNoPoolNoDataGravitationTest") public class Buddy3NodesNoPoolNoDataGravitationTest extends AbstractNodeBasedBuddyTest { protected List replListener = new ArrayList(); @BeforeClass public void createCaches() throws Exception { caches = createCaches(3, false, false); for (Cache c : caches) { ReplicationListener listener = ReplicationListener.getReplicationListener(c); replListener.add(listener); } } public void testSingleBuddy() throws Exception { waitForBuddy(caches.get(0), caches.get(1), true); waitForBuddy(caches.get(1), caches.get(2), true); waitForBuddy(caches.get(2), caches.get(0), true); } public void testSimplePut() throws Exception { 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 cachePool' "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); } public void testPutAndRemove() throws Exception { 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 cachePool' "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 testDataReplicationSuppression() throws Exception { Fqn fqn = Fqn.fromString("/test"); Fqn backupFqn = fqnTransformer.getBackupFqn(caches.get(0).getLocalAddress(), fqn); replListener.get(1).expect(PutKeyValueCommand.class); caches.get(0).put(fqn, key, value); replListener.get(1).waitForReplicationToOccur(); 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); inReplicationListeners(replListener).dataWillGravitateFrom(0).to(1); caches.get(1).getInvocationContext().getOptionOverrides().setForceDataGravitation(true); log.info("Before gravitation call..."); assertEquals("value", caches.get(1).get(fqn, key)); expectGravitation(); 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)); } @Test(dependsOnMethods = {"testSingleBuddy", "testSimplePut", "testPutAndRemove", "testDataReplicationSuppression"}) public void testRemovalFromClusterSingleBuddy() throws Exception { try { waitForBuddy(caches.get(0), caches.get(1), true); waitForBuddy(caches.get(1), caches.get(2), true); waitForBuddy(caches.get(2), caches.get(0), true); // now remove a cache from the cluster caches.get(2).stop(); TestingUtil.sleepThread(getSleepTimeout()); // now test new buddy groups waitForBuddy(caches.get(0), caches.get(1), true); waitForBuddy(caches.get(1), caches.get(0), true); assertNoLocks(caches); } finally { caches.get(2).start(); replListener.set(2, ReplicationListener.getReplicationListener(caches.get(2))); } } } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/GravitationFromDyingNodeTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/GravitationFromDyingNodeTest0000644000175000017500000000702311236536237033437 0ustar moellermoellerpackage 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.notifications.annotation.CacheListener; import org.jboss.cache.notifications.annotation.CacheStopped; import org.jboss.cache.notifications.annotation.NodeVisited; import org.jboss.cache.notifications.event.CacheStoppedEvent; import org.jboss.cache.notifications.event.NodeVisitedEvent; import org.jboss.cache.util.CachePrinter; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.Test; import java.util.List; import java.util.concurrent.CountDownLatch; @Test(testName = "buddyreplication.GravitationFromDyingNodeTest", groups = "functional") public class GravitationFromDyingNodeTest extends BuddyReplicationTestsBase { static Log log = LogFactory.getLog(GravitationFromDyingNodeTest.class); public void testGravitationFromDyingNode() throws Exception { // this test starts 4 nodes, each with 2 backups. // the data owner is stopped. // the node with no backup requests state from the other 2. // one of the other two should be shutting down, and the other should be slow to respond // so that the illegal cache state exception is propagated to the caller // this encapsulates what is seen in JBCACHE-1528 List> caches = createCaches(2, 4, false, true, false, true); final CacheSPI c1 = caches.get(0), c2 = caches.get(1), c3 = caches.get(2), c4 = caches.get(3); c1.put("/a/b/c", "k", "v"); System.out.println("BEFORE: " + CachePrinter.printCacheDetails(c1, c2, c3, c4)); // kill c1 c1.stop(); log.error("** Stopped C1!"); // now stop c2, but make sure it takes time to shut down. final StopListener sl = new StopListener(); c2.addCacheListener(sl); final GetNodeListener gnl = new GetNodeListener(); c3.addCacheListener(gnl); Thread stopper = new Thread("Stopper") { @Override public void run() { log.error("** Stopping C2!"); c2.stop(); log.error("** Stopped C2!"); } }; stopper.start(); Thread gateOpener = new Thread("GateOpener") { @Override public void run() { TestingUtil.sleepThread(1000); // Yuk log.error("** Opening gates!!"); sl.openGate(); gnl.openGate(); } }; gateOpener.start(); log.error("** Starting gravitation!"); c4.getInvocationContext().getOptionOverrides().setForceDataGravitation(true); assert null != c4.getNode("/a/b/c"); } static abstract class GatedListener { CountDownLatch gate = new CountDownLatch(1); public void openGate() { gate.countDown(); } void waitForGate() { try { gate.await(); } catch (InterruptedException e) { e.printStackTrace(); } } } @CacheListener public static class StopListener extends GatedListener { @CacheStopped public void stop(CacheStoppedEvent e) { if (e.isPre()) { log.error("** Waiting on gate to stop C2!"); waitForGate(); } } } @CacheListener public static class GetNodeListener extends GatedListener { @NodeVisited public void visit(NodeVisitedEvent e) { if (e.isPre()) { log.error("** Waiting on gate to read C3!"); waitForGate(); } } } } ././@LongLink0000000000000000000000000000016100000000000011563 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyReplicationWithPassivationTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyReplicationWithPassivat0000644000175000017500000000111111111047320033441 0ustar moellermoeller/* * 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 AT jboss DOT org) */ @Test(groups = "functional", testName = "buddyreplication.BuddyReplicationWithPassivationTest") public class BuddyReplicationWithPassivationTest extends BuddyReplicationWithCacheLoaderTest { public BuddyReplicationWithPassivationTest() { passivation = true; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/Buddy4Nodes2BackupsTest.java0000644000175000017500000001117611120273541033152 0ustar moellermoeller/* * 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.util.TestingUtil; import org.testng.annotations.Test; import org.testng.annotations.BeforeTest; import org.testng.annotations.BeforeClass; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNull; /** * Tests how groups are formed and disbanded * * @author Manik Surtani (manik AT jboss DOT org) */ @Test(groups = {"functional", "jgroups"}, testName = "buddyreplication.Buddy4Nodes2BackupsTest") public class Buddy4Nodes2BackupsTest extends AbstractNodeBasedBuddyTest { @BeforeClass public void createCaches() throws Exception { caches = createCaches(2, 4, false); } private Log log = LogFactory.getLog(Buddy4Nodes2BackupsTest.class); BuddyFqnTransformer fqnTransformer = new BuddyFqnTransformer(); public void test2Buddies() throws Exception { TestingUtil.blockUntilViewsReceived(5000, caches.toArray(new Cache[0])); waitForBuddy(caches.get(0), caches.get(1), false); waitForBuddy(caches.get(0), caches.get(2), false); waitForBuddy(caches.get(1), caches.get(2), false); waitForBuddy(caches.get(1), caches.get(3), false); waitForBuddy(caches.get(2), caches.get(3), false); waitForBuddy(caches.get(2), caches.get(0), false); waitForBuddy(caches.get(3), caches.get(0), false); waitForBuddy(caches.get(3), caches.get(1), false); } public void testPutAndRemove2() throws Exception { 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 cachePool' "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); } @Test (dependsOnMethods = {"test2Buddies", "testPutAndRemove2"} ) public void testRemovalFromCluster2Buddies() throws Throwable { assertNoLocks(caches); TestingUtil.sleepThread(getSleepTimeout()); waitForBuddy(caches.get(0), caches.get(1), false); waitForBuddy(caches.get(0), caches.get(2), false); waitForBuddy(caches.get(1), caches.get(2), false); waitForBuddy(caches.get(1), caches.get(3), false); waitForBuddy(caches.get(2), caches.get(3), false); waitForBuddy(caches.get(2), caches.get(0), false); waitForBuddy(caches.get(3), caches.get(0), false); waitForBuddy(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 waitForBuddy(caches.get(0), caches.get(2), false); waitForBuddy(caches.get(0), caches.get(3), false); waitForBuddy(caches.get(2), caches.get(3), false); waitForBuddy(caches.get(2), caches.get(0), false); waitForBuddy(caches.get(3), caches.get(0), false); waitForBuddy(caches.get(3), caches.get(2), false); assertNoLocks(caches); } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyReplicationTestsBase.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyReplicationTestsBase.ja0000644000175000017500000004200411176306660033327 0ustar moellermoeller/* * 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.*; import org.jboss.cache.config.BuddyReplicationConfig; import org.jboss.cache.config.CacheLoaderConfig; 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.factories.UnitTestConfigurationFactory; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.loader.CacheLoaderManager; import org.jboss.cache.loader.testloaders.DummyInMemoryCacheLoader; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.jboss.cache.util.TestingUtil; import org.jgroups.Address; import static org.testng.AssertJUnit.*; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.AfterMethod; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import javax.transaction.TransactionManager; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * Base class for BR tests * * @author Manik Surtani (manik AT jboss DOT org) */ public abstract class BuddyReplicationTestsBase { protected final ThreadLocal>> cachesTL = new ThreadLocal>>(); protected final BuddyFqnTransformer fqnTransformer = new BuddyFqnTransformer(); protected final Log log = LogFactory.getLog(getClass()); @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { System.setProperty("org.jboss.cache.shutdown.force", "true"); List> caches = cachesTL.get(); 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(); } cleanupCaches(caches, true); } cachesTL.set(null); System.gc(); new UnitTestCacheFactory().cleanUp(); } protected void cleanupCaches(List> caches, boolean 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(); } } if (stop) { TestingUtil.killCaches(c); } else { if (c.getComponentRegistry().getState().allowInvocations()) c.removeNode(Fqn.ROOT); } } } } 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 UnitTestCacheFactory().createCache(UnitTestConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC, false, false, true), false, getClass()); String threadId = Thread.currentThread().getName(); //c.getConfiguration().setClusterName("BuddyReplicationTest-" + threadId); BuddyReplicationConfig brc = new BuddyReplicationConfig(); if (buddyPoolName != null) brc.setBuddyPoolName(buddyPoolName); brc.setEnabled(true); brc.setDataGravitationRemoveOnFind(removeOnFind); brc.setDataGravitationSearchBackupTrees(true); brc.setAutoDataGravitation(useDataGravitation); NextMemberBuddyLocatorConfig nextMemberBuddyLocatorConfig = new NextMemberBuddyLocatorConfig(); nextMemberBuddyLocatorConfig.setNumBuddies(numBuddies); brc.setBuddyLocatorConfig(nextMemberBuddyLocatorConfig); c.getConfiguration().setBuddyReplicationConfig(brc); c.getConfiguration().setFetchInMemoryState(true); c.getConfiguration().setNodeLockingScheme(optimisticLocks ? NodeLockingScheme.OPTIMISTIC : getNonOptimisticLockingScheme()); c.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); 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; } protected NodeLockingScheme getNonOptimisticLockingScheme() { return NodeLockingScheme.PESSIMISTIC; } /** * 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 { List> spiList = createCaches(1, numCaches, useBuddyPool, false); waitForSingleBuddy(spiList); return spiList; } protected List> createCaches(int numCaches, boolean useBuddyPool, boolean useDataGravitation, boolean optimisticLocks) throws Exception { List> spiList = createCaches(1, numCaches, useBuddyPool, useDataGravitation, optimisticLocks); waitForSingleBuddy(spiList); return spiList; } protected List> createCaches(int numCaches, boolean useBuddyPool, boolean useDataGravitation) throws Exception { List> spiList = createCaches(1, numCaches, useBuddyPool, useDataGravitation); waitForSingleBuddy(spiList); return spiList; } 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 cachePool to start up and discover each other TestingUtil.blockUntilViewsReceived(caches.toArray(new Cache[0]), VIEW_BLOCK_TIMEOUT); waitForSingleBuddy(caches); 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); CacheLoaderConfig config = new CacheLoaderConfig(); IndividualCacheLoaderConfig iclc = new IndividualCacheLoaderConfig(); iclc.setClassName(DummyInMemoryCacheLoader.class.getName()); iclc.setFetchPersistentState(fetchPersistent); config.addIndividualCacheLoaderConfig(iclc); config.setShared(false); config.setPassivation(passivation); cache.getConfiguration().setCacheLoaderConfig(config); 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 cachePool to start up and discover each other TestingUtil.blockUntilViewsReceived(caches.toArray(new Cache[0]), VIEW_BLOCK_TIMEOUT); TestingUtil.sleepThread(getSleepTimeout()); } return caches; } /** * This is to allow for any state transfers involved (when assigning a buddy) to complete */ protected int getSleepTimeout() { return 1000; } protected static void assertIsBuddy(Cache dataOwner, Cache buddy, boolean onlyBuddy) { Address dataOwnerLocalAddress = dataOwner.getLocalAddress(); Address buddyLocalAddress = buddy.getLocalAddress(); 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" + getViewsString(dataOwner, buddy), 1, dataOwnerBuddyManager.getBuddyAddresses().size()); assertTrue(buddyLocalAddress + " should be a buddy to " + dataOwnerLocalAddress + getViewsString(dataOwner, buddy), dataOwnerBuddyManager.getBuddyAddresses().contains(buddyLocalAddress)); // and now on the buddy end BuddyGroup group = buddyBuddyManager.buddyGroupsIParticipateIn.get(dataOwnerLocalAddress); assertTrue("buddy's list of groups it participates in should contain data owner's group name" + getViewsString(dataOwner, buddy), buddyBuddyManager.buddyGroupsIParticipateIn.containsKey(dataOwnerLocalAddress)); if (onlyBuddy) assertEquals(1, group.getBuddies().size()); assertTrue(buddyLocalAddress + " should be a buddy to " + group.getGroupName() + getViewsString(dataOwner, buddy), group.getBuddies().contains(buddyLocalAddress)); } private static String getViewsString(Cache dataOwner, Cache buddy) { return "[dataOwnerView: {" + dataOwner.getMembers()+ "}, buddyViewIs: {" + buddy.getMembers() + "}]"; } public static void waitForBuddy(Cache dataOwner, Cache buddy, boolean onlyBuddy) throws Exception { waitForBuddy(dataOwner, buddy, onlyBuddy, 60000); } public static void waitForSingleBuddy(List caches) throws Exception { Cache[] array = (Cache[]) caches.toArray(new Cache[0]); waitForSingleBuddy(array); } /** * Will wait for 60 secs + 10sec * caches.length for the given caches to become buddys. * The caches should be ordered as per underlying view. */ public static void waitForSingleBuddy(Cache... caches) throws Exception { long timeout = 60000 + caches.length; for (int i = 0; i < caches.length - 1; i++) { waitForBuddy(caches[i], caches[i + 1], true, timeout); } waitForBuddy(caches[caches.length - 1], caches[0], true, timeout); } public static void waitForBuddy(Cache dataOwner, Cache buddy, boolean onlyBuddy, long timeout) throws Exception { long start = System.currentTimeMillis(); while ((System.currentTimeMillis() - start) < timeout) { if (isBuddy(dataOwner, buddy, onlyBuddy)) return; Thread.sleep(50); } //give it a last chance, just to have a nice printed message assertIsBuddy(dataOwner, buddy, onlyBuddy); } private static boolean isBuddy(Cache dataOwner, Cache buddy, boolean onlyBuddy) { Address dataOwnerLocalAddress = dataOwner.getLocalAddress(); Address buddyLocalAddress = buddy.getLocalAddress(); BuddyManager dataOwnerBuddyManager = ((CacheSPI) dataOwner).getBuddyManager(); BuddyManager buddyBuddyManager = ((CacheSPI) buddy).getBuddyManager(); boolean result = true; // lets test things on the data owner's side of things if (onlyBuddy) result = result && (1 == dataOwnerBuddyManager.getBuddyAddresses().size()); result = result && dataOwnerBuddyManager.getBuddyAddresses().contains(buddyLocalAddress); // and now on the buddy end BuddyGroup group = buddyBuddyManager.buddyGroupsIParticipateIn.get(dataOwnerLocalAddress); result = result & buddyBuddyManager.buddyGroupsIParticipateIn.containsKey(dataOwnerLocalAddress); if (onlyBuddy) result = result && group.getBuddies().size() == 1; result = result & group != null && group.getBuddies() != null && group.getBuddies().contains(buddyLocalAddress); return result; } protected void assertNoLocks(List> caches) { for (Cache cache : caches) { if (cache != null) assert ((CacheSPI) cache).getNumberOfLocksHeld() < 1 : cache.getLocalAddress() + " still holds locks"; } } public 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()); } protected 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)); } } } } } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/NextMemberBuddyLocatorTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/NextMemberBuddyLocatorTest.j0000644000175000017500000003202411111047320033312 0ustar moellermoeller/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.buddyreplication; import org.jgroups.Address; import org.jgroups.stack.IpAddress; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; 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; /** * Tests the NextMemberBuddyLocator * * @author Manik Surtani (manik AT jboss DOT org) */ @Test(groups = "functional", sequential = true, testName = "buddyreplication.NextMemberBuddyLocatorTest") 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)); } } ././@LongLink0000000000000000000000000000016000000000000011562 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/Buddy3NodesFailedRemotePrepareTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/Buddy3NodesFailedRemotePrepa0000644000175000017500000000646611236333631033262 0ustar moellermoellerpackage org.jboss.cache.buddyreplication; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; 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.NodeModifiedEvent; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.testng.annotations.Test; import javax.transaction.RollbackException; import javax.transaction.TransactionManager; import java.util.List; /** * Tests that a failed PREPARE on a buddy results in a proper rollback. * * @author Brian Stansberry * @since 3.1.12 */ @Test(groups = "functional", testName = "buddyreplication.Buddy3NodesFailedRemotePrepareTest") public class Buddy3NodesFailedRemotePrepareTest extends BuddyReplicationTestsBase { boolean optimistic = false; @CacheListener public static class FailureListener { private boolean rejected; @NodeModified public void nodeModified(NodeModifiedEvent event) { if (!rejected && !event.isOriginLocal() && event.getFqn().hasElement("0")) { rejected = true; throw new RuntimeException("rejected"); } } } public void testFailedPrepare() throws Exception { List> caches = createCaches(2, 3, false, false, optimistic, false); cachesTL.set(caches); int cacheNumber = 0; for (CacheSPI c : caches) { c.getConfiguration().setFetchInMemoryState(false); c.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); c.start(); c.put("/" + cacheNumber++, "k", "v"); } waitForBuddy(caches.get(0), caches.get(1), false); waitForBuddy(caches.get(0), caches.get(2), false); waitForBuddy(caches.get(1), caches.get(2), false); waitForBuddy(caches.get(1), caches.get(0), false); waitForBuddy(caches.get(2), caches.get(0), false); waitForBuddy(caches.get(2), caches.get(1), false); Thread.sleep(2000);//wait for state transfer caches.get(0).put("/0", "k", "v"); Fqn backup = fqnTransformer.getBackupFqn(caches.get(0).getLocalAddress(), Fqn.fromString("/0")); assert (caches.get(1).exists(backup)); assert (caches.get(2).exists(backup)); caches.get(2).addCacheListener(new FailureListener()); TransactionManager tm = caches.get(0).getTransactionManager(); tm.begin(); caches.get(0).put("/0", "k", "v1"); try { tm.commit(); assert false : "Put should have thrown an exception"; } catch (RollbackException expected) { expected.printStackTrace(System.out); } Object val = caches.get(0).get("/0", "k"); assert "v".equals(val) : "cache 0 had " + val; caches.get(2).getInvocationContext().getOptionOverrides().setForceDataGravitation(true); val = caches.get(2).get("/0", "k"); TestingUtil.sleepThread(100); assert "v".equals(val) : "cache 2 had " + val; caches.get(1).getInvocationContext().getOptionOverrides().setForceDataGravitation(true); val = caches.get(1).get("/0", "k"); TestingUtil.sleepThread(100); assert "v".equals(val) : "cache 1 had " + val; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyPoolBroadcastTest.java0000644000175000017500000000267011134645517033171 0ustar moellermoeller/* * 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 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; import java.util.concurrent.TimeUnit; /** * Tests basic group membership semantics * * @author Manik Surtani (manik AT jboss DOT org) */ @Test(groups = "functional", testName = "buddyreplication.BuddyPoolBroadcastTest") public class BuddyPoolBroadcastTest extends BuddyReplicationTestsBase { private Log log = LogFactory.getLog(BuddyPoolBroadcastTest.class); @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { super.tearDown(); } public void test2CachesWithPoolNames() throws Exception { List> caches = createCaches(2, true); cachesTL.set(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())); } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/Buddy3NodesNoStateTransfer.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/Buddy3NodesNoStateTransfer.j0000644000175000017500000000526611120421070033224 0ustar moellermoellerpackage org.jboss.cache.buddyreplication; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.util.CachePrinter; import org.testng.annotations.Test; import java.util.List; /** * This is to test JBCACHE-1229 * * @author Manik Surtani (manik AT jboss DOT org) * @since 2.1.0 */ @Test(groups = "functional", testName = "buddyreplication.Buddy3NodesNoStateTransfer") public class Buddy3NodesNoStateTransfer extends BuddyReplicationTestsBase { public void testCachesWithoutStateTransfer() throws Exception { List> caches = createCaches(1, 3, false, false, false, false); cachesTL.set(caches); int cacheNumber = 0; for (CacheSPI c : caches) { c.getConfiguration().setFetchInMemoryState(false); c.start(); c.put("/" + cacheNumber++, "k", "v"); } waitForBuddy(caches.get(0), caches.get(1), true); waitForBuddy(caches.get(1), caches.get(2), true); waitForBuddy(caches.get(2), caches.get(0), true); Thread.sleep(2000);//wait for state transfer 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 + ")"; } } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyReplicationConfigTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyReplicationConfigTest.j0000644000175000017500000001213011120421070033311 0ustar moellermoeller/* * 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.UnitTestCacheFactory; import org.jboss.cache.config.BuddyReplicationConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.parsing.XmlConfigHelper; import org.jboss.cache.config.parsing.XmlConfigurationParser; import org.jboss.cache.config.parsing.element.BuddyElementParser; import org.jboss.cache.interceptors.LegacyDataGravitatorInterceptor; 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.Test; import org.w3c.dom.Element; /** * Tests basic configuration options by passing stuff into the CacheImpl. * * @author Manik Surtani (manik AT jboss DOT org) */ @Test(groups = {"functional", "jgroups"}, sequential = true, testName = "buddyreplication.BuddyReplicationConfigTest") public class BuddyReplicationConfigTest { private CacheSPI cache; @AfterMethod(alwaysRun = true) public void tearDown() { TestingUtil.killCaches(cache); cache = null; } public void testNullConfig() throws Exception { cache = (CacheSPI) new UnitTestCacheFactory().createCache(false, getClass()); cache.getConfiguration().setBuddyReplicationConfig(null); assertNull(cache.getBuddyManager()); } public void testDisabledConfig() throws Exception { String xmlConfig = ""; BuddyReplicationConfig config = getBuddyReplicationConfig(xmlConfig); cache = (CacheSPI) new UnitTestCacheFactory().createCache(false, getClass()); cache.getConfiguration().setBuddyReplicationConfig(config); assertNull(cache.getBuddyManager()); } public void testBasicConfig() throws Exception { String xmlConfig = ""; BuddyReplicationConfig config = getBuddyReplicationConfig(xmlConfig); Configuration c = new Configuration(); c.setCacheMode(Configuration.CacheMode.REPL_SYNC); c.setBuddyReplicationConfig(config); cache = (CacheSPI) new UnitTestCacheFactory().createCache(c, false, getClass()); 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()); } private BuddyReplicationConfig getBuddyReplicationConfig(String xmlConfig) throws Exception { Element element = XmlConfigHelper.stringToElementInCoreNS(xmlConfig); BuddyElementParser elementParser = new BuddyElementParser(); BuddyReplicationConfig config = elementParser.parseBuddyElement(element); return config; } public void testXmlConfig() throws Exception { cache = (CacheSPI) new UnitTestCacheFactory().createCache(new XmlConfigurationParser().parseFile("configs/buddy-replication-cache.xml"), false, getClass()); 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 LegacyDataGravitatorInterceptor); } assertTrue("Should have a data gravitator!!", hasDG); } public void testLocalModeConfig() throws Exception { String xmlConfig = ""; BuddyReplicationConfig config = getBuddyReplicationConfig(xmlConfig); cache = (CacheSPI) new UnitTestCacheFactory().createCache(false, getClass()); 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! } }././@LongLink0000000000000000000000000000017200000000000011565 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/Buddy3NodesFailedRemotePrepareOptimisticTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/buddyreplication/Buddy3NodesFailedRemotePrepa0000644000175000017500000000057611236274723033264 0ustar moellermoellerpackage org.jboss.cache.buddyreplication; import org.testng.annotations.Test; @Test(groups = "functional", testName = "buddyreplication.Buddy3NodesFailedRemotePrepareOptimisticTest") public class Buddy3NodesFailedRemotePrepareOptimisticTest extends Buddy3NodesFailedRemotePrepareTest { public Buddy3NodesFailedRemotePrepareOptimisticTest() { optimistic = true; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/marshall/0000755000175000017500000000000011675221506024175 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/marshall/MySet.java0000644000175000017500000000025311111047320026062 0ustar moellermoellerpackage org.jboss.cache.marshall; import java.util.HashSet; public class MySet extends HashSet { private static final long serialVersionUID = 2164839240744662205L; } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/marshall/CacheMarshaller300Test.java0000644000175000017500000000510011111047320031116 0ustar moellermoellerpackage org.jboss.cache.marshall; import org.jboss.cache.Fqn; import org.testng.annotations.Test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; @Test(groups = {"functional"}, testName = "marshall.CacheMarshaller300Test") public class CacheMarshaller300Test { public void testArrayTypes() throws Exception { Marshaller m = new CacheMarshaller300(); ByteArrayOutputStream bout = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bout); byte[] s = {1, 2, 3, 4}; m.objectToObjectStream(s, out); out.close(); ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bin); Object o = m.objectFromObjectStream(ois); ois.close(); assert o instanceof byte[]; byte[] oS = (byte[]) o; assert oS.length == 4; assert oS[0] == 1; assert oS[1] == 2; assert oS[2] == 3; assert oS[3] == 4; } public void testBoxedArrayTypes() throws Exception { Marshaller m = new CacheMarshaller300(); ByteArrayOutputStream bout = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bout); Byte[] s = new Byte[]{1, 2, 3, 4}; m.objectToObjectStream(s, out); out.close(); ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bin); Object o = m.objectFromObjectStream(ois); ois.close(); assert o instanceof Byte[]; Byte[] oS = (Byte[]) o; assert oS.length == 4; assert oS[0] == 1; assert oS[1] == 2; assert oS[2] == 3; assert oS[3] == 4; } public void testMixedArrayTypes() throws Exception { Marshaller m = new CacheMarshaller300(); ByteArrayOutputStream bout = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bout); Object[] s = {"Hello", Fqn.fromString("/a"), 1, null}; m.objectToObjectStream(s, out); out.close(); ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bin); Object o = m.objectFromObjectStream(ois); ois.close(); assert o instanceof Object[]; Object[] oS = (Object[]) o; assert oS.length == 4; assert oS[0].equals("Hello"); assert oS[1].equals(Fqn.fromString("/a")); assert oS[2].equals(1); assert oS[3] == null; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/marshall/Foo.notjava0000644000175000017500000000166510535375655026325 0ustar moellermoellerpackage 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(); } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/marshall/InvalidRegionForStateTransferTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/marshall/InvalidRegionForStateTransferTest.ja0000644000175000017500000000760211131613416033255 0ustar moellermoellerpackage org.jboss.cache.marshall; import org.jboss.cache.Cache; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.replicationlisteners.ReplicationListener; 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 AT jboss DOT org) * @since 2.1.0 */ @Test(groups = "functional", sequential = true, testName = "marshall.InvalidRegionForStateTransferTest") public class InvalidRegionForStateTransferTest { Cache c1, c2; ReplicationListener replListener2; @BeforeMethod public void setUp() throws CloneNotSupportedException { c1 = new UnitTestCacheFactory().createCache(UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.REPL_ASYNC), false, getClass()); c1.getConfiguration().setNodeLockingScheme(NodeLockingScheme.PESSIMISTIC); 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;"); c1.getConfiguration().setClusterConfig(jgroupsCfg); c1.getConfiguration().setUseRegionBasedMarshalling(true); c1.start(); c2 = new UnitTestCacheFactory().createCache(c1.getConfiguration().clone(), getClass()); replListener2 = ReplicationListener.getReplicationListener(c2); TestingUtil.blockUntilViewsReceived(60000, c1, c2); } @AfterMethod public void tearDown() { TestingUtil.killCaches(c1, c2); c1 = null; c2 = null; } 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.expect(PutKeyValueCommand.class); // 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"); } } ././@LongLink0000000000000000000000000000016000000000000011562 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/marshall/ReturnValueMarshallingMarshalledValuesTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/marshall/ReturnValueMarshallingMarshalledValu0000644000175000017500000000075411111047320033372 0ustar moellermoellerpackage org.jboss.cache.marshall; import org.testng.annotations.Test; /** * @author Manik Surtani (manik AT jboss DOT org) * @since 2.1.0 */ @Test(groups = {"functional", "jgroups"}, testName = "marshall.ReturnValueMarshallingMarshalledValuesTest") public class ReturnValueMarshallingMarshalledValuesTest extends ReturnValueMarshallingTest { public ReturnValueMarshallingMarshalledValuesTest() { useMarshalledValues = true; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/marshall/SyncReplMarshalledValuesTest.java0000644000175000017500000000103511111047320032574 0ustar moellermoellerpackage org.jboss.cache.marshall; import org.testng.annotations.Test; /** * @author Manik Surtani (manik AT jboss DOT org) * @since 2.1.0 */ @Test(groups = {"functional", "jgroups"}, testName = "marshall.SyncReplMarshalledValuesTest") public class SyncReplMarshalledValuesTest extends SyncReplTest { public SyncReplMarshalledValuesTest() { useMarshalledValues = true; } @Override public void testCustomFqn() { // meaningless when using marshalled values } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/marshall/AsyncReplMarshalledValuesTest.java0000644000175000017500000000101511111047320032733 0ustar moellermoellerpackage org.jboss.cache.marshall; import org.testng.annotations.Test; /** * @author Manik Surtani (manik AT jboss DOT org) * @since 2.1.0 */ @Test(groups = {"functional", "jgroups"}, testName = "marshall.AsyncReplMarshalledValuesTest") public class AsyncReplMarshalledValuesTest extends AsyncReplTest { public AsyncReplMarshalledValuesTest() { useMarshalledValues = true; } @Override public void testCustomFqn() { // don't test this case } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/marshall/VersionAwareMarshallerTest.java0000644000175000017500000000747211236543451032331 0ustar moellermoeller/* * 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 AT jboss DOT org) */ @Test(groups = {"functional"}, testName = "marshall.VersionAwareMarshallerTest") public class VersionAwareMarshallerTest extends AbstractVersionAwareMarshallerTest { @BeforeMethod public void setUp() { this.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(CacheMarshaller300.class, marshaller.defaultMarshaller.getClass()); marshaller = createVAMandRestartCache("1.5.0.GA"); assertEquals(CacheMarshaller300.class, marshaller.defaultMarshaller.getClass()); marshaller = createVAMandRestartCache("1.3.0.GA"); assertEquals(CacheMarshaller300.class, marshaller.defaultMarshaller.getClass()); marshaller = createVAMandRestartCache("1.3.0.SP2"); assertEquals(CacheMarshaller300.class, marshaller.defaultMarshaller.getClass()); marshaller = createVAMandRestartCache("1.3.1.GA"); assertEquals(CacheMarshaller300.class, marshaller.defaultMarshaller.getClass()); marshaller = createVAMandRestartCache("1.2.4.SP2"); assertEquals(CacheMarshaller300.class, marshaller.defaultMarshaller.getClass()); marshaller = createVAMandRestartCache("1.2.3"); assertEquals(CacheMarshaller300.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 '32'", 32, 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-3.2.8.GA/src/test/java/org/jboss/cache/marshall/Foo.clazz0000644000175000017500000000212510535375655025776 0ustar moellermoellerÊþº¾.=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-3.2.8.GA/src/test/java/org/jboss/cache/marshall/data/0000755000175000017500000000000011675221500025100 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/marshall/data/Address.java0000644000175000017500000000314011120421724027321 0ustar moellermoeller/* * 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() { return city; } public void setCity(String 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-3.2.8.GA/src/test/java/org/jboss/cache/marshall/data/Debug.java0000644000175000017500000000621711111047320026766 0ustar moellermoellerpackage 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-3.2.8.GA/src/test/java/org/jboss/cache/marshall/data/Person.java0000644000175000017500000000273111111047320027203 0ustar moellermoeller/* * 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-3.2.8.GA/src/test/java/org/jboss/cache/marshall/SelectedClassnameClassLoader.java0000644000175000017500000001717411111047320032527 0ustar moellermoellerpackage 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-3.2.8.GA/src/test/java/org/jboss/cache/marshall/ReturnValueMarshallingTest.java0000644000175000017500000001404111120367722032332 0ustar moellermoellerpackage org.jboss.cache.marshall; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.buddyreplication.GravitateResult; import org.jboss.cache.commands.CommandsFactory; import org.jboss.cache.commands.CommandsFactoryImpl; 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.config.Configuration.NodeLockingScheme; 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"}, testName = "marshall.ReturnValueMarshallingTest") 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 CommandsFactoryImpl(); @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { super.setUp(); Configuration c1 = new Configuration(); c1.setNodeLockingScheme(NodeLockingScheme.PESSIMISTIC); c1.setUseLazyDeserialization(useMarshalledValues); c1.setUseRegionBasedMarshalling(!useMarshalledValues); c1.setCacheMode(Configuration.CacheMode.REPL_SYNC); c1.setSyncReplTimeout(60000);// to aid with debugging cache1 = (CacheSPI) new UnitTestCacheFactory().createCache(c1, false, getClass()); cache1.start(); Configuration c2 = new Configuration(); c2.setNodeLockingScheme(NodeLockingScheme.PESSIMISTIC); c2.setUseLazyDeserialization(useMarshalledValues); c2.setUseRegionBasedMarshalling(!useMarshalledValues); c2.setCacheMode(Configuration.CacheMode.REPL_SYNC); c2.setSyncReplTimeout(60000);// to aid with debugging cache2 = (CacheSPI) new UnitTestCacheFactory().createCache(c2, false, getClass()); 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() throws Exception { TestingUtil.killCaches(cache1, cache2); cache1 = null; cache2 = null; super.tearDown(); } @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-3.2.8.GA/src/test/java/org/jboss/cache/marshall/ActiveInactiveTest.java0000644000175000017500000001342411120367722030576 0ustar moellermoeller/* * 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.Fqn; import org.jboss.cache.Region; import org.jboss.cache.RegionManager; import org.jboss.cache.UnitTestCacheFactory; 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.util.TestingUtil; 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: 7284 $ */ @Test(groups = "functional", sequential = true, testName = "marshall.ActiveInactiveTest") 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 UnitTestCacheFactory().createCache(false, getClass()); c = cache.getConfiguration(); c.setUseRegionBasedMarshalling(true); c.setFetchInMemoryState(false); cache.start(); this.cr = TestingUtil.extractComponentRegistry(cache); rman = TestingUtil.extractComponentRegistry(cache).getComponent(RegionManager.class); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache); cache = null; } 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-3.2.8.GA/src/test/java/org/jboss/cache/marshall/CacheLoaderMarshallingJDBCTest.java0000644000175000017500000001027211130373015032626 0ustar moellermoellerpackage org.jboss.cache.marshall; import org.jboss.cache.Cache; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.UnitTestCacheFactory; 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.LRUAlgorithmConfig; import org.jboss.cache.loader.JDBCCacheLoaderConfig; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.UnitTestDatabaseManager; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import java.util.Collections; 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", testName = "marshall.CacheLoaderMarshallingJDBCTest") public class CacheLoaderMarshallingJDBCTest extends RegionBasedMarshallingTestBase { private static final String className = "org.jboss.cache.marshall.MyUUID"; private Cache cache; private Fqn fqn = Fqn.fromString("/a"); @AfterMethod(alwaysRun = true) @Override public void tearDown() throws Exception { TestingUtil.killCaches(cache); Properties props = cache.getConfiguration().getCacheLoaderConfig().getFirstCacheLoaderConfig().getProperties(); UnitTestDatabaseManager.shutdownInMemoryDatabase(props); super.tearDown(); cache = null; } @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(originalClassLoaderTL.get()); 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 = UnitTestDatabaseManager.getTestDbProperties(); // ensure cleanup after each test prop.setProperty("cache.jdbc.table.drop", "true"); Cache cache = new UnitTestCacheFactory().createCache(false, getClass()); Configuration config = cache.getConfiguration(); config.setUseRegionBasedMarshalling(useRegionBased); config.setInactiveOnStartup(useRegionBased); int wakeupInterval = 1000000; // a long time; really disabled EvictionConfig ec = new EvictionConfig( new EvictionRegionConfig( Fqn.ROOT, new LRUAlgorithmConfig(1000000, 0, 1000) ), wakeupInterval ); 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; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/marshall/SyncReplTest.java0000644000175000017500000004236411131613416027440 0ustar moellermoeller/* * * 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.Fqn; import org.jboss.cache.Region; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.marshall.data.Address; import org.jboss.cache.marshall.data.Person; 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.NotSupportedException; import javax.transaction.SystemException; import javax.transaction.TransactionManager; import java.lang.reflect.Method; import java.util.HashMap; import org.jboss.cache.UnitTestCacheFactory; /** * Test case for marshalling using Sync mode. * * @author Ben Wang * @version $Revision: 7422 $ */ @Test(groups = {"functional", "jgroups"}, testName = "marshall.SyncReplTest") public class SyncReplTest extends RegionBasedMarshallingTestBase { protected class SyncReplTestTL { CacheSPI cache1, cache2; String props = null; Person ben_; Address addr_; Throwable ex_; } protected ThreadLocal threadLocal = new ThreadLocal(); private Fqn aop = Fqn.fromString("/aop"); protected boolean useMarshalledValues = false; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { super.setUp(); SyncReplTestTL srtl = new SyncReplTestTL(); threadLocal.set(srtl); srtl.cache1 = createCache("TestCache"); srtl.cache2 = createCache("TestCache"); srtl.addr_ = new Address(); srtl.addr_.setCity("San Jose"); srtl.ben_ = new Person(); srtl.ben_.setName("Ben"); srtl.ben_.setAddress(srtl.addr_); // Pause to give caches time to see each other TestingUtil.blockUntilViewsReceived(new CacheSPI[]{srtl.cache1, srtl.cache2}, 60000); } private CacheSPI createCache(String name) { CacheSPI cache = (CacheSPI) new UnitTestCacheFactory().createCache(UnitTestConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), false, getClass()); cache.getConfiguration().setNodeLockingScheme(NodeLockingScheme.PESSIMISTIC); cache.getConfiguration().setClusterName(name + "-" + Thread.currentThread().getName()); // Use marshaller cache.getConfiguration().setUseLazyDeserialization(useMarshalledValues); cache.getConfiguration().setUseRegionBasedMarshalling(!useMarshalledValues); cache.create(); cache.start(); return cache; } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { SyncReplTestTL srtl = threadLocal.get(); CacheSPI cache1 = srtl.cache1; CacheSPI cache2 = srtl.cache2; cache1.removeNode(Fqn.ROOT); TestingUtil.killCaches(cache1, cache2); super.tearDown(); threadLocal.set(null); } public void testPlainPut() throws Exception { SyncReplTestTL srtl = threadLocal.get(); CacheSPI cache1 = srtl.cache1; CacheSPI cache2 = srtl.cache2; cache1.put(aop, "person", srtl.ben_); Person ben2 = (Person) cache2.get(aop, "person"); assertNotNull("Person from 2nd cache should not be null ", ben2); assertEquals(srtl.ben_.toString(), ben2.toString()); } public void testCCE() throws Exception { SyncReplTestTL srtl = threadLocal.get(); CacheSPI cache1 = srtl.cache1; CacheSPI cache2 = srtl.cache2; 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", srtl.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 { SyncReplTestTL srtl = threadLocal.get(); CacheSPI cache1 = srtl.cache1; CacheSPI cache2 = srtl.cache2; 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", srtl.ben_); Object ben2; // Can't cast it to Person. CCE will result. if (useMarshalledValues) Thread.currentThread().setContextClassLoader(clb); ben2 = cache2.get(aop, "person"); assertEquals(srtl.ben_.toString(), ben2.toString()); } public void testCLSet() throws Exception { SyncReplTestTL srtl = threadLocal.get(); CacheSPI cache1 = srtl.cache1; CacheSPI cache2 = srtl.cache2; 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", srtl.ben_); Object ben2; // Can't cast it to Person. CCE will result. if (useMarshalledValues) Thread.currentThread().setContextClassLoader(clb); ben2 = cache2.get(aop, "person"); assertEquals(srtl.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 { SyncReplTestTL srtl = threadLocal.get(); CacheSPI cache1 = srtl.cache1; CacheSPI cache2 = srtl.cache2; 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", srtl.ben_); Object ben2; // Can't cast it to Person. CCE will result. if (useMarshalledValues) Thread.currentThread().setContextClassLoader(clb); ben2 = cache2.get(aop, "person"); assertEquals(srtl.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 { SyncReplTestTL srtl = threadLocal.get(); CacheSPI cache1 = srtl.cache1; CacheSPI cache2 = srtl.cache2; 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", srtl.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(srtl.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 { SyncReplTestTL srtl = threadLocal.get(); CacheSPI cache1 = srtl.cache1; CacheSPI cache2 = srtl.cache2; 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", srtl.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 { SyncReplTestTL srtl = threadLocal.get(); CacheSPI cache1 = srtl.cache1; CacheSPI cache2 = srtl.cache2; 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", srtl.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 { SyncReplTestTL srtl = threadLocal.get(); CacheSPI cache1 = srtl.cache1; CacheSPI cache2 = srtl.cache2; 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", srtl.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(srtl.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?? } public void testCustomFqn() throws Exception { SyncReplTestTL srtl = threadLocal.get(); CacheSPI cache1 = srtl.cache1; CacheSPI cache2 = srtl.cache2; 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 { SyncReplTestTL srtl = threadLocal.get(); CacheSPI cache1 = srtl.cache1; 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(); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/marshall/RegionBasedMarshallingTestBase.java0000644000175000017500000000334511111047320033025 0ustar moellermoellerpackage org.jboss.cache.marshall; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; 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 ThreadLocal originalClassLoaderTL = new ThreadLocal() { @Override protected ClassLoader initialValue() { return Thread.currentThread().getContextClassLoader(); } }; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { originalClassLoaderTL.get(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { resetContextClassLoader(); } 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(originalClassLoaderTL.get()); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/marshall/MethodIdPreservationTest.java0000644000175000017500000000673111111047320031767 0ustar moellermoellerpackage org.jboss.cache.marshall; import org.jboss.cache.Fqn; import org.jboss.cache.commands.CommandsFactory; import org.jboss.cache.commands.CommandsFactoryImpl; import org.jboss.cache.commands.WriteCommand; import org.jboss.cache.commands.tx.PrepareCommand; import org.jboss.cache.commands.write.PutDataMapCommand; 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.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"}, sequential = true, testName = "marshall.MethodIdPreservationTest") public class MethodIdPreservationTest { private Marshaller m; private ObjectOutputStream stream; private ByteArrayOutputStream byteStream; private WriteCommand command1; private List list; 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 = new ArrayList(2); 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 CommandsFactoryImpl(); cm210.injectCommandsFactory(factory); m = cm210; } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { byteStream = null; stream = null; command1 = null; list = null; prepareComand = null; m = null; } 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-3.2.8.GA/src/test/java/org/jboss/cache/marshall/FooClassLoader.java0000644000175000017500000001605711120421724027676 0ustar moellermoellerpackage 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(); int i=0; for (byte b : bytes) { i++; System.out.print(b); System.out.print(", "); } }*/ } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/marshall/CacheMarshaller200Test.java0000644000175000017500000002372211120421724031133 0ustar moellermoeller/* * 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.RegionManagerImpl; import org.jboss.cache.RegionRegistry; 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; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.ComponentRegistry; @Test(groups = {"functional"}, testName = "marshall.CacheMarshaller200Test") 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 { CacheMarshallerTestBaseTL tl = threadLocal.get(); CacheMarshaller200 cm200 = new CacheMarshaller200(); tl.c.setUseRegionBasedMarshalling(true); RegionManagerImpl rmi = new RegionManagerImpl(); rmi.injectDependencies(null, tl.c, null, null, null, new RegionRegistry()); cm200.injectDependencies(rmi, tl.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(); 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 { CacheMarshallerTestBaseTL tl = threadLocal.get(); Configuration c = tl.c; // 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); ComponentRegistry cr = this.cr; 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-3.2.8.GA/src/test/java/org/jboss/cache/marshall/AsyncReplTest.java0000644000175000017500000002620211131613416027572 0ustar moellermoeller/* * * 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.Fqn; import org.jboss.cache.Region; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.factories.UnitTestConfigurationFactory; 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.replicationlisteners.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: 7422 $ */ @Test(groups = {"functional", "jgroups"}, testName = "marshall.AsyncReplTest") 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 { super.setUp(); cache1 = createCache("TestCache"); cache2 = createCache("TestCache"); replListener1 = ReplicationListener.getReplicationListener(cache1); replListener2 = ReplicationListener.getReplicationListener(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) { Configuration c = UnitTestConfigurationFactory.createConfiguration(CacheMode.REPL_ASYNC); c.setNodeLockingScheme(NodeLockingScheme.PESSIMISTIC); c.setClusterName(name + "-" + Thread.currentThread().getName()); // Use marshaller c.setUseLazyDeserialization(useMarshalledValues); c.setUseRegionBasedMarshalling(!useMarshalledValues); CacheSPI cache = (CacheSPI) new UnitTestCacheFactory().createCache(c, false, getClass()); cache.create(); cache.start(); return cache; } @AfterMethod(alwaysRun = true) @Override public void tearDown() throws Exception { TestingUtil.killCaches(cache1, cache2); super.tearDown(); cache1 = null; cache2 = null; } /** * 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(); replListener2.expect(PutKeyValueCommand.class); cache1.put(Fqn.fromString("/alias"), "person", ben); replListener2.waitForReplicationToOccur(); 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(); 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(); replListener2.expect(PutKeyValueCommand.class); cache1.put(Fqn.fromString("/aop/2"), "person", scopedBen1); replListener2.waitForReplicationToOccur(); 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.expect(PutKeyValueCommand.class); beginTransaction(); cache1.put(aop, "person", ben); commit(); replListener2.waitForReplicationToOccur(); 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.expect(PutKeyValueCommand.class); beginTransaction(); cache1.put(aop, "person", ben); commit(); replListener2.waitForReplicationToOccur(); 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.expect(PutKeyValueCommand.class); cache2.put(aop, "person", ben2); if (useMarshalledValues) resetContextClassLoader(); replListener1.waitForReplicationToOccur(); 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.expect(PutKeyValueCommand.class); cache1.put(fqn, "key", "value"); replListener2.waitForReplicationToOccur(); 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(); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/marshall/CacheMarshaller210Test.java0000644000175000017500000000736411120421724031140 0ustar moellermoellerpackage org.jboss.cache.marshall; import org.jboss.cache.Fqn; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.commands.remote.ReplicateCommand; 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"}, testName = "marshall.CacheMarshaller210Test") public class CacheMarshaller210Test extends CacheMarshaller200Test { public CacheMarshaller210Test() { currentVersion = "2.1.0.GA"; currentVersionShort = 21; expectedMarshallerClass = CacheMarshaller210.class; } protected void doMapTest(int size) throws Exception { CacheMarshallerTestBaseTL tl = threadLocal.get(); VersionAwareMarshaller marshaller = tl.marshaller; 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 { CacheMarshallerTestBaseTL tl = threadLocal.get(); VersionAwareMarshaller marshaller = tl.marshaller; 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) { getAndTestSize(cm200, i); getAndTestSize(cm210, i); } } public void testVLongs() throws IOException { CacheMarshallerTestBaseTL tl = threadLocal.get(); VersionAwareMarshaller marshaller = tl.marshaller; 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) { getAndTestSize(cm200, 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-3.2.8.GA/src/test/java/org/jboss/cache/marshall/ReplicateToInactiveRegionTest.java0000644000175000017500000000614311120367722032742 0ustar moellermoellerpackage org.jboss.cache.marshall; import org.jboss.cache.CacheSPI; 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; import java.util.ArrayList; import java.util.List; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; @Test(groups = {"functional", "jgroups"}, sequential = true, testName = "marshall.ReplicateToInactiveRegionTest") public class ReplicateToInactiveRegionTest { List> caches; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { caches = new ArrayList>(2); caches.add(createCache()); caches.add(createCache()); TestingUtil.blockUntilViewsReceived(caches.toArray(new CacheSPI[]{}), 10000); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(caches.get(0), caches.get(1)); caches = null; } private CacheSPI createCache() { Configuration c = new Configuration(); c.setCacheMode("REPL_SYNC"); c.setUseRegionBasedMarshalling(true); CacheSPI cache = (CacheSPI) new UnitTestCacheFactory().createCache(c, false, getClass()); cache.start(); return cache; } public void testTransferToInactiveRegion() { Fqn f = Fqn.fromString("/a/b"); caches.get(0).put(f, "k", "v"); assertEquals("v", caches.get(0).get(f, "k")); assertEquals("v", caches.get(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.get(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.get(0).getRegionManager().getAllRegions(Region.Type.MARSHALLING).contains(region0)); // now create a region on cache 1, as above. Region region1 = caches.get(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.get(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.get(0).put(f, "k", "v2"); assertEquals("v2", caches.get(0).get(f, "k")); assertNull(caches.get(1).get(f, "k")); } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/marshall/AbstractVersionAwareMarshallerTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/marshall/AbstractVersionAwareMarshallerTest.j0000644000175000017500000000232111111047320033273 0ustar moellermoellerpackage org.jboss.cache.marshall; import org.jboss.cache.CacheStatus; import org.jboss.cache.RegionManager; import org.jboss.cache.RegionManagerImpl; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.ComponentRegistry; /** * @author Manik Surtani (manik AT jboss DOT org) * @since 2.1.0 */ public abstract class AbstractVersionAwareMarshallerTest { protected ComponentRegistry cr = null; protected VersionAwareMarshaller createVAMandRestartCache(String replVersion) { ComponentRegistry cr = this.cr; Configuration c = cr.getComponent(Configuration.class); c.setReplVersionString(replVersion); return createVAMandRestartCache(new RegionManagerImpl()); } protected VersionAwareMarshaller createVAMandRestartCache(RegionManager rm) { ComponentRegistry cr = this.cr; 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-3.2.8.GA/src/test/java/org/jboss/cache/marshall/MarshalledValueTest.java0000644000175000017500000003205611131613416030747 0ustar moellermoellerpackage org.jboss.cache.marshall; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.UnitTestCacheFactory; 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.UnitTestConfigurationFactory; import org.jboss.cache.interceptors.MarshalledValueInterceptor; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.invocation.CacheInvocationDelegate; import org.jboss.cache.loader.testloaders.DummyInMemoryCacheLoader; import org.jboss.cache.notifications.annotation.CacheListener; import org.jboss.cache.notifications.annotation.NodeModified; import org.jboss.cache.notifications.event.NodeModifiedEvent; import org.jboss.cache.util.TestingUtil; 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 AT jboss DOT org) * @since 2.1.0 */ @Test(groups = "functional", sequential = true, testName = "marshall.MarshalledValueTest") public class MarshalledValueTest { private CacheSPI cache1, cache2; private MarshalledValueListenerInterceptor mvli; @BeforeMethod public void setUp() throws CloneNotSupportedException { cache1 = (CacheSPI) new UnitTestCacheFactory().createCache(UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.REPL_SYNC, false), false, getClass()); if (cache1.getConfiguration().getBuddyReplicationConfig() != null) cache1.getConfiguration().setBuddyReplicationConfig(null); cache1.getConfiguration().setUseLazyDeserialization(true); cache2 = (CacheSPI) new UnitTestCacheFactory().createCache(cache1.getConfiguration().clone(), false, getClass()); 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); cache1 = null; cache2 = null; 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 UnitTestCacheFactory().createCache(UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.REPL_SYNC), false, getClass()); cache2 = (CacheSPI) new UnitTestCacheFactory().createCache(UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.REPL_SYNC), false, getClass()); 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++; } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/marshall/CacheMarshallerTestBase.java0000644000175000017500000002151711120421724031504 0ustar moellermoeller/* * 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.RegionManagerImpl; 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", testName = "marshall.CacheMarshallerTestBase") public abstract class CacheMarshallerTestBase extends AbstractVersionAwareMarshallerTest { protected String currentVersion; protected int currentVersionShort; protected Class expectedMarshallerClass, latestMarshallerClass = CacheMarshaller300.class; protected ThreadLocal threadLocal = new ThreadLocal(); protected class CacheMarshallerTestBaseTL { protected VersionAwareMarshaller marshaller; protected RegionManager regionManager; protected Configuration c; } @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { CacheMarshallerTestBaseTL tl = new CacheMarshallerTestBaseTL(); threadLocal.set(tl); tl.c = new Configuration(); tl.c.setUseRegionBasedMarshalling(false); tl.c.setInactiveOnStartup(false); tl.c.setReplVersionString(currentVersion); ComponentRegistry cr = new ComponentRegistry(tl.c, new CacheInvocationDelegate()); this.cr = cr; tl.marshaller = createVAMandRestartCache(new RegionManagerImpl()); tl.regionManager = cr.getComponent(RegionManager.class); } @AfterMethod(alwaysRun = true) public void tearDown() { threadLocal.set(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() { CacheMarshallerTestBaseTL tl = threadLocal.get(); VersionAwareMarshaller marshaller = tl.marshaller; 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() == 3 : "Should have 3 marshallers now"; } public void testStringBasedFqn() throws Exception { CacheMarshallerTestBaseTL tl = threadLocal.get(); VersionAwareMarshaller marshaller = tl.marshaller; Fqn fqn = Fqn.fromElements("JSESSIONID", "1010.10.5:3000", "1234567890", "1"); byte[] asBytes = marshaller.objectToByteBuffer(fqn); Object o2 = marshaller.objectFromByteBuffer(asBytes); assertEquals(fqn, o2); } public void testNonStringBasedFqn() throws Exception { CacheMarshallerTestBaseTL tl = threadLocal.get(); VersionAwareMarshaller marshaller = tl.marshaller; Fqn fqn = Fqn.fromElements(3, false); byte[] asBytes = marshaller.objectToByteBuffer(fqn); Object o2 = marshaller.objectFromByteBuffer(asBytes); assertEquals(fqn, o2); } public void testMethodCall() throws Exception { CacheMarshallerTestBaseTL tl = threadLocal.get(); VersionAwareMarshaller marshaller = tl.marshaller; 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 { CacheMarshallerTestBaseTL tl = threadLocal.get(); VersionAwareMarshaller marshaller = tl.marshaller; 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 { CacheMarshallerTestBaseTL tl = threadLocal.get(); VersionAwareMarshaller marshaller = tl.marshaller; 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 { CacheMarshallerTestBaseTL tl = threadLocal.get(); VersionAwareMarshaller marshaller = tl.marshaller; tl.c.setUseRegionBasedMarshalling(true); marshaller.init(); doReplicationQueueTest(); } protected void doReplicationQueueTest() throws Exception { CacheMarshallerTestBaseTL tl = threadLocal.get(); VersionAwareMarshaller marshaller = tl.marshaller; // 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-3.2.8.GA/src/test/java/org/jboss/cache/marshall/SimpleArrayReplTest.java0000644000175000017500000000267211120367722030756 0ustar moellermoellerpackage org.jboss.cache.marshall; import org.jboss.cache.Cache; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.Test; import java.util.Arrays; import java.util.Random; import org.jboss.cache.UnitTestCacheFactory; @Test(groups = "functional", testName = "marshall.SimpleArrayReplTest") public class SimpleArrayReplTest { public void testArrayRepl() throws CloneNotSupportedException { int streamsize = 11000; byte[] b = new byte[streamsize]; new Random().nextBytes(b); Cache cache1 = null, cache2 = null; try { Configuration c = new Configuration(); c.setCacheMode(CacheMode.REPL_SYNC); c.setNodeLockingScheme(NodeLockingScheme.MVCC); cache1 = new UnitTestCacheFactory().createCache(c.clone(), getClass()); cache2 = new UnitTestCacheFactory().createCache(c.clone(), getClass()); TestingUtil.blockUntilViewsReceived(60000, cache1, cache2); cache1.put(Fqn.fromString("/a"), "test", b); byte[] bytesBack = cache2.get(Fqn.fromString("/a"), "test"); assert Arrays.equals(b, bytesBack); } finally { TestingUtil.killCaches(cache1, cache2); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/marshall/CacheLoaderMarshallingTest.java0000644000175000017500000001202211120367722032205 0ustar moellermoellerpackage org.jboss.cache.marshall; import org.jboss.cache.Cache; 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.LRUAlgorithmConfig; 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.Test; import java.io.File; import java.util.Collections; import org.jboss.cache.UnitTestCacheFactory; /** * 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", testName = "marshall.CacheLoaderMarshallingTest") public class CacheLoaderMarshallingTest extends RegionBasedMarshallingTestBase { private static final String tmpDir = TestingUtil.TEST_FILES + File.separatorChar + "CacheLoaderMarshallingTest"; private Cache cache; private Fqn fqn = Fqn.fromString("/a"); @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache); cache = null; File f = new File(tmpDir); if (f.exists()) if (!f.delete()) f.deleteOnExit(); super.tearDown(); } 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(originalClassLoaderTL.get()); 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) { Configuration config = new Configuration(); config.setUseRegionBasedMarshalling(useRegionBased); config.setInactiveOnStartup(useRegionBased); Cache cache = new UnitTestCacheFactory().createCache(config, false, getClass()); EvictionConfig ec = new EvictionConfig(); ec.setWakeupInterval(1000000); // a long time; really disabled EvictionRegionConfig erc = new EvictionRegionConfig(); erc.setRegionFqn(Fqn.ROOT); LRUAlgorithmConfig lruAlgorithmConfig = new LRUAlgorithmConfig(); lruAlgorithmConfig.setMaxNodes(1000); lruAlgorithmConfig.setTimeToLive(1000000); erc.setEvictionAlgorithmConfig(lruAlgorithmConfig); ec.addEvictionRegionConfig(erc); 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-3.2.8.GA/src/test/java/org/jboss/cache/marshall/RegionManagerTest.java0000644000175000017500000001540711120367722030421 0ustar moellermoellerpackage org.jboss.cache.marshall; 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.Configuration; 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.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.jboss.cache.UnitTestCacheFactory; /** * Test on ERegionManager class, from a marshalling perspective. */ @Test(groups = {"functional"}, sequential = true, testName = "marshall.RegionManagerTest") 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 UnitTestCacheFactory().createCache(getClass()); 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-3.2.8.GA/src/test/java/org/jboss/cache/marshall/MyList.java0000644000175000017500000000025711111047320026246 0ustar moellermoellerpackage org.jboss.cache.marshall; import java.util.ArrayList; public class MyList extends ArrayList { private static final long serialVersionUID = 773098444170102370L; } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/marshall/UnmarshalledReferencesTest.java0000644000175000017500000000304011111047320032277 0ustar moellermoellerpackage 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"}, testName = "marshall.UnmarshalledReferencesTest") 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-3.2.8.GA/src/test/java/org/jboss/cache/marshall/MyMap.java0000644000175000017500000000025311111047320026044 0ustar moellermoellerpackage org.jboss.cache.marshall; import java.util.HashMap; public class MyMap extends HashMap { private static final long serialVersionUID = -269748324316844637L; } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/marshall/RedeploymentEmulationTest.java0000644000175000017500000001217211120421724032215 0ustar moellermoeller/* * 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.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; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.util.TestingUtil; /** * Unit test demonstrating usability of marshalling for application redeployment in application server. * * @author Galder Zamarreno */ @Test(groups = {"functional"}, enabled = false, sequential = true, testName = "marshall.RedeploymentEmulationTest") // 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 UnitTestCacheFactory().createCache(false, getClass()); cache.getConfiguration().setCacheMode(Configuration.CacheMode.LOCAL); cache.getConfiguration().setUseRegionBasedMarshalling(true); } @AfterMethod(alwaysRun = true) public void tearDown() { log.info("**** IN TEAR DOWN ***"); TestingUtil.killCaches(cache); cache = null; } 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) { 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-3.2.8.GA/src/test/java/org/jboss/cache/marshall/CustomCollectionTest.java0000644000175000017500000002300011120367722031155 0ustar moellermoellerpackage org.jboss.cache.marshall; import org.jboss.cache.Cache; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.lock.IsolationLevel; import static org.jboss.cache.marshall.CustomCollectionTest.MarshallingMode.*; 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.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 AT jboss DOT org) */ @Test(groups = {"functional"}, testName = "marshall.CustomCollectionTest") 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 { super.setUp(); cache1 = createCache(); cache2 = createCache(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache1, cache2); cache1 = null; cache2 = null; super.tearDown(); } 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() { Configuration c = new Configuration(); c.setCacheMode(Configuration.CacheMode.REPL_SYNC); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); c.setIsolationLevel(IsolationLevel.REPEATABLE_READ); Cache cache = new UnitTestCacheFactory().createCache(c, false, getClass()); return cache; } private static Fqn fqn(String fqn) { return Fqn.fromString(fqn); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/FqnTest.java0000644000175000017500000002602011245222023024605 0ustar moellermoeller/* * 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: 8207 $ */ @Test(groups = "unit", sequential = true, testName = "FqnTest") 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; assert 0 == fqn.size(); int hcode = fqn.hashCode(); assert hcode != -1; } public void testOne() { Fqn fqn = Fqn.fromElements(22); 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"); assert 3 == fqn.size(); Fqn fqn2 = Fqn.fromElements("a", "b", "c"); assert 3 == fqn.size(); assert fqn.equals(fqn2); assert fqn.hashCode() == fqn2.hashCode(); } public void testHereogeneousNames() { Fqn fqn = Fqn.fromElements("string", 38, true); 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"); 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 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 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); } 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 = (String) 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 testRemovalNonString() throws Exception { Fqn f = Fqn.fromElements("test", 1); Configuration c = new Configuration(); c.setCacheMode("LOCAL"); cache = new UnitTestCacheFactory().createCache(c, getClass()); 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); } 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 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 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 } } public void testReplacingDirectAncestor() { Fqn fqn = Fqn.fromString("/a/b/c"); Fqn newParent = Fqn.fromString("/hot/dog"); Fqn expectedNewChild = Fqn.fromString("/hot/dog/c"); assert expectedNewChild.equals(fqn.replaceAncestor(fqn.getParent(), newParent)); } public void testReplacingindirectAncestor() { Fqn fqn = Fqn.fromString("/a/b/c"); Fqn newParent = Fqn.fromString("/hot/dog"); Fqn expectedNewChild = Fqn.fromString("/hot/dog/b/c"); Fqn replaced = fqn.replaceAncestor(fqn.getParent().getParent(), newParent); assert expectedNewChild.equals(replaced) : "Expected " + expectedNewChild + " but was " + replaced; } public void testDifferentFactories() { Fqn[] fqns = new Fqn[6]; 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 Object[]{"a", "b", "c"})); fqns[i++] = Fqn.fromRelativeList(Fqn.ROOT, Arrays.asList(new Object[]{"a", "b", "c"})); fqns[i] = Fqn.fromRelativeFqn(Fqn.ROOT, Fqn.fromString("/a/b/c")); // 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-3.2.8.GA/src/test/java/org/jboss/cache/mgmt/0000755000175000017500000000000011675221511023332 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/mgmt/MgmtTestBase.java0000644000175000017500000000757611131613416026547 0ustar moellermoellerpackage org.jboss.cache.mgmt; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import static org.jboss.cache.factories.UnitTestConfigurationFactory.buildSingleCacheLoaderConfig; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.loader.testloaders.DummyInMemoryCacheLoader; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.HashMap; import java.util.Map; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.util.TestingUtil; @Test(groups = "functional", testName = "mgmt.MgmtTestBase") 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) { TestingUtil.killCaches(cache); 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 { UnitTestCacheFactory instance = new UnitTestCacheFactory(); Configuration c = new Configuration(); c.setNodeLockingScheme(NodeLockingScheme.MVCC); c.setCacheMode(Configuration.CacheMode.LOCAL); c.setCacheLoaderConfig(getCacheLoaderConfig()); c.setExposeManagementStatistics(true); CacheSPI cache = (CacheSPI) instance.createCache(c, false, getClass()); cache.create(); cache.start(); return cache; } private CacheLoaderConfig getCacheLoaderConfig() throws Exception { return buildSingleCacheLoaderConfig(passivation, null, DummyInMemoryCacheLoader.class.getName(), "debug=true", false, false, false, false, false); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/mgmt/PassivationTest.java0000644000175000017500000002162711147260547027353 0ustar moellermoellerpackage org.jboss.cache.mgmt; import org.jboss.cache.interceptors.ActivationInterceptor; import org.jboss.cache.interceptors.PassivationInterceptor; import org.jboss.cache.util.TestingUtil; 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 org.testng.annotations.Test; import java.util.HashMap; /** * Simple functional tests for ActivationInterceptor and PassivationInterceptor statistics * * @author Jerry Gauthier * @version $Id: PassivationTest.java 7735 2009-02-19 13:40:55Z manik.surtani@jboss.com $ */ @Test(groups = "functional", testName = "mgmt.PassivationTest") 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); 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+= 3; 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()); cache.put(POLAND, new HashMap()); cache.put(POLAND, CAPITAL, "Warsaw"); cache.put(POLAND, CURRENCY, "Zloty"); miss ++; 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+=3; 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+=3; 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-3.2.8.GA/src/test/java/org/jboss/cache/mgmt/InvalidationTest.java0000644000175000017500000001525111131613416027456 0ustar moellermoellerpackage org.jboss.cache.mgmt; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.interceptors.InvalidationInterceptor; 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; /** * Simple functional tests for InvalidationInterceptor statistics * * @author Jerry Gauthier * @version $Id: InvalidationTest.java 7422 2009-01-09 09:21:18Z mircea.markus $ */ @Test(groups = {"functional"}, sequential = true, testName = "mgmt.InvalidationTest") 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) { TestingUtil.killCaches(cache1); cache1 = null; } if (cache2 != null) { TestingUtil.killCaches(cache2); 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 = UnitTestConfigurationFactory.createConfiguration(CacheMode.INVALIDATION_SYNC); c.setCacheMode(Configuration.CacheMode.INVALIDATION_SYNC); c.setExposeManagementStatistics(true); c.setClusterName(clusterName); return (CacheSPI) new UnitTestCacheFactory().createCache(c, getClass()); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/mgmt/CacheLoaderTest.java0000644000175000017500000001643111147260547027202 0ustar moellermoellerpackage 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.assertEquals; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertNull; import org.testng.annotations.Test; import java.util.HashMap; /** * Simple functional tests for CacheLoaderInterceptor and CacheStoreInterceptor statistics * * @author Jerry Gauthier * @version $Id: CacheLoaderTest.java 7735 2009-02-19 13:40:55Z manik.surtani@jboss.com $ */ @Test(groups = "functional", sequential = true, testName = "mgmt.CacheLoaderTest") 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 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-3.2.8.GA/src/test/java/org/jboss/cache/mgmt/TxTest.java0000644000175000017500000002477611135611316025445 0ustar moellermoellerpackage org.jboss.cache.mgmt; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.factories.UnitTestConfigurationFactory; 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; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.util.TestingUtil; /** * Simple functional tests for TxInterceptor statistics * * @author Jerry Gauthier * @version $Id: TxTest.java 7557 2009-01-21 12:19:26Z mircea.markus $ */ @Test(groups = {"functional"}, testName = "mgmt.TxTest") 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) { TestingUtil.killCaches(cache1); cache1 = null; } if (cache2 != null) { TestingUtil.killCaches(cache2); 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) { Configuration c = UnitTestConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC); c.setSyncCommitPhase(true); c.setSyncRollbackPhase(true); c.setUseRegionBasedMarshalling(false); c.setCacheMode(Configuration.CacheMode.REPL_SYNC); c.setNodeLockingScheme(NodeLockingScheme.PESSIMISTIC); c.setExposeManagementStatistics(true); c.setClusterName(clusterName); CacheSPI cache = (CacheSPI) new UnitTestCacheFactory().createCache(c, false, getClass()); 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-3.2.8.GA/src/test/java/org/jboss/cache/mgmt/MgmtCoreTest.java0000644000175000017500000002742311120367722026562 0ustar moellermoellerpackage org.jboss.cache.mgmt; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; 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; import org.jboss.cache.util.TestingUtil; /** * Simple functional tests for CacheMgmtInterceptor * * @author Jerry Gauthier * @version $Id: MgmtCoreTest.java 7284 2008-12-12 05:00:02Z mircea.markus $ */ @Test(groups = {"functional"}, sequential = true, testName = "mgmt.MgmtCoreTest") 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 UnitTestCacheFactory().createCache(false, getClass()); 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) { TestingUtil.killCaches(cache); 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-3.2.8.GA/src/test/java/org/jboss/cache/testng/0000755000175000017500000000000011675221513023674 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/testng/Other.java0000644000175000017500000000464211131613416025620 0ustar moellermoellerpackage org.jboss.cache.testng; import org.testng.annotations.Test; import org.testng.annotations.BeforeClass; import org.testng.annotations.AfterClass; import org.jboss.cache.Cache; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestConfigurationFactory; /** * @author Mircea.Markus@jboss.com */ @Test(groups = "functional", testName = "Same") public class Other { Cache cache; @BeforeClass public void beforeTest() { // System.out.println("Base.beforeTest"); Configuration config = UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.REPL_SYNC, true); cache = new UnitTestCacheFactory().createCache(config, true, getClass()); System.out.println("Other:::" + cache.getConfiguration().getClusterConfig()); } @AfterClass public void afterTest() { // System.out.println("Base.afterTest"); cache.stop(); } public void testNoTest(){} } //One:::UDP(discard_incompatible_packets=true;enable_bundling=false;enable_diagnostics=true;ip_ttl=2;loopback=false;ma //x_bundle_size=64000;max_bundle_timeout=30;mcast_addr=228.10.10.11;mcast_port=45589;mcast_recv_buf_size=25000000;mcas //t_send_buf_size=640000;oob_thread_pool.enabled=true;oob_thread_pool.keep_alive_time=10000;oob_thread_pool.max_thread //s=4;oob_thread_pool.min_threads=1;oob_thread_pool.queue_enabled=false;oob_thread_pool.queue_max_size=10;oob_thread_p //ool.rejection_policy=Run;thread_naming_pattern=pl;thread_pool.enabled=true;thread_pool.keep_alive_time=30000;thread_ //pool.max_threads=25;thread_pool.min_threads=1;thread_pool.queue_enabled=false;thread_pool.queue_max_size=100;thread_ //pool.rejection_policy=Run;tos=8;ucast_recv_buf_size=20000000;ucast_send_buf_size=640000;use_concurrent_stack=true;us //e_incoming_packet_handler=true):PING(num_initial_members=3;timeout=2000):MERGE2(max_interval=30000;min_interval=1000 //0):FD_SOCK:FD(max_tries=2;shun=true;timeout=1000):VERIFY_SUSPECT(timeout=250):pbcast.NAKACK(discard_delivered_msgs=t //rue;gc_lag=0;retransmit_timeout=300,600,900,1200;use_mcast_xmit=false):UNICAST(timeout=300,600,900,1200):pbcast.STAB //LE(desired_avg_gossip=50000;max_bytes=400000;stability_delay=1000):pbcast.GMS(join_timeout=1000;print_local_addr=fal //se;shun=false;view_ack_collection_timeout=1000;view_bundling=true):FRAG2(frag_size=60000):pbcast.STREAMING_STATE_TRA //NSFER:pbcast.FLUSH(timeout=0) jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/testng/other/0000755000175000017500000000000011675221512025014 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/testng/other/Derived.java0000644000175000017500000000121711120422552027232 0ustar moellermoellerpackage org.jboss.cache.testng.other; import org.testng.annotations.Test; import org.jboss.cache.testng.Base; /** * @author Mircea.Markus@jboss.com */ @Test(groups = "functional" , testName = "testng.other.Derived") public class Derived extends Base { public void testDerivedCcccc() { System.out.println(getThreadName() + "ccccccccccccccccc"); } public void testDerivedDdddd() { System.out.println(getThreadName() + "dddddddddddddddddddd"); } protected String getThreadName() { return "[" + getClass() + " ************ -> " + Thread.currentThread().getName() + "] "; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/testng/other/third/0000755000175000017500000000000011675221513026127 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/testng/other/third/SecondDerived.java0000644000175000017500000000046111120422552031500 0ustar moellermoellerpackage org.jboss.cache.testng.other.third; import org.jboss.cache.testng.other.Derived; import org.testng.annotations.Test; /** * @author Mircea.Markus@jboss.com */ @Test (groups = "functional", testName = "tesng.other.third.SecondDerived") public class SecondDerived extends Derived { } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/testng/Base.java0000644000175000017500000000776611131613416025423 0ustar moellermoellerpackage org.jboss.cache.testng; import org.testng.annotations.*; import org.jboss.cache.Cache; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestConfigurationFactory; /** * @author Mircea.Markus@jboss.com */ @Test(groups = "functional", testName = "Base") public class Base { Cache cache; @BeforeClass public void beforeTest() { // System.out.println("Base.beforeTest"); Configuration config = UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.REPL_SYNC, true); cache = new UnitTestCacheFactory().createCache(config, true, getClass()); System.out.println("Base:::" + cache.getConfiguration().getClusterConfig()); } @AfterClass public void afterTest() { // System.out.println("Base.afterTest"); cache.stop(); } @BeforeMethod public void beforeMethod() { // System.out.println("Base.beforeMethod"); } public void testNoTest() {} protected String getThreadName() { return "[" + getClass() + " ************ -> " + Thread.currentThread().getName() + "] "; } } //Base:::UDP(discard_incompatible_packets=true;enable_bundling=false;enable_diagnostics=true;ip_ttl=2;loopback=false;m //ax_bundle_size=64000;max_bundle_timeout=30;mcast_addr=228.10.10.11;mcast_port=45589;mcast_recv_buf_size=25000000;mca //st_send_buf_size=640000;oob_thread_pool.enabled=true;oob_thread_pool.keep_alive_time=10000;oob_thread_pool.max_threa //ds=4;oob_thread_pool.min_threads=1;oob_thread_pool.queue_enabled=false;oob_thread_pool.queue_max_size=10;oob_thread_ //pool.rejection_policy=Run;thread_naming_pattern=pl;thread_pool.enabled=true;thread_pool.keep_alive_time=30000;thread //_pool.max_threads=25;thread_pool.min_threads=1;thread_pool.queue_enabled=false;thread_pool.queue_max_size=100;thread //_pool.rejection_policy=Run;tos=8;ucast_recv_buf_size=20000000;ucast_send_buf_size=640000;use_concurrent_stack=true;u //se_incoming_packet_handler=true):PING(num_initial_members=3;timeout=2000):MERGE2(max_interval=30000;min_interval=100 //00):FD_SOCK:FD(max_tries=2;shun=true;timeout=1000):VERIFY_SUSPECT(timeout=250):pbcast.NAKACK(discard_delivered_msgs= //true;gc_lag=0;retransmit_timeout=300,600,900,1200;use_mcast_xmit=false):UNICAST(timeout=300,600,900,1200):pbcast.STA //BLE(desired_avg_gossip=50000;max_bytes=400000;stability_delay=1000):pbcast.GMS(join_timeout=1000;print_local_addr=fa //lse;shun=false;view_ack_collection_timeout=1000;view_bundling=true):FRAG2(frag_size=60000):pbcast.STREAMING_STATE_TR //ANSFER:pbcast.FLUSH(timeout=0) //Other:::UDP(discard_incompatible_packets=true;enable_bundling=false;enable_diagnostics=true;ip_ttl=2;loopback=false; //max_bundle_size=64000;max_bundle_timeout=30;mcast_addr=228.10.10.12;mcast_port=45590;mcast_recv_buf_size=25000000;mc //ast_send_buf_size=640000;oob_thread_pool.enabled=true;oob_thread_pool.keep_alive_time=10000;oob_thread_pool.max_thre //ads=4;oob_thread_pool.min_threads=1;oob_thread_pool.queue_enabled=false;oob_thread_pool.queue_max_size=10;oob_thread //_pool.rejection_policy=Run;thread_naming_pattern=pl;thread_pool.enabled=true;thread_pool.keep_alive_time=30000;threa //d_pool.max_threads=25;thread_pool.min_threads=1;thread_pool.queue_enabled=false;thread_pool.queue_max_size=100;threa //d_pool.rejection_policy=Run;tos=8;ucast_recv_buf_size=20000000;ucast_send_buf_size=640000;use_concurrent_stack=true; //use_incoming_packet_handler=true):PING(num_initial_members=3;timeout=2000):MERGE2(max_interval=30000;min_interval=10 //000):FD_SOCK:FD(max_tries=2;shun=true;timeout=1000):VERIFY_SUSPECT(timeout=250):pbcast.NAKACK(discard_delivered_msgs //=true;gc_lag=0;retransmit_timeout=300,600,900,1200;use_mcast_xmit=false):UNICAST(timeout=300,600,900,1200):pbcast.ST //ABLE(desired_avg_gossip=50000;max_bytes=400000;stability_delay=1000):pbcast.GMS(join_timeout=1000;print_local_addr=f //alse;shun=false;view_ack_collection_timeout=1000;view_bundling=true):FRAG2(frag_size=60000):pbcast.STREAMING_STATE_T //RANSFER:pbcast.FLUSH(timeout=0)jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/config/0000755000175000017500000000000011675221520023633 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/config/ConfigurationCloningTest.java0000644000175000017500000001134111111047320031444 0ustar moellermoeller/* * 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.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.config.parsing.XmlConfigurationParser; import org.jboss.cache.eviction.LRUAlgorithmConfig; import static org.testng.AssertJUnit.*; import org.testng.annotations.Test; import java.util.List; import java.util.Properties; /** * Tests the ability to clone Configuration elements and end up with * independently modifiable configurations. * * @author Brian Stansberry */ @Test(groups = {"functional"}, testName = "config.ConfigurationCloningTest") public class ConfigurationCloningTest { /** * A file that includes every configuration element I could think of */ public static final String DEFAULT_CONFIGURATION_FILE = "configs/clonable-config.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.REPL_SYNC, clone.getCacheMode()); assertEquals("CloneCluster", clone.getClusterName()); assertEquals(c.getClusterConfig(), clone.getClusterConfig()); assertEquals(3, clone.getStateRetrievalTimeout()); // Eviction EvictionConfig ec1 = c.getEvictionConfig(); EvictionConfig ec2 = clone.getEvictionConfig(); assertFalse(ec1 == ec2); assertEquals(4, ec2.getDefaultEvictionRegionConfig().getEventQueueSize()); assertEquals(45000, ec2.getWakeupInterval()); assert ec2.getDefaultEvictionRegionConfig().getEvictionAlgorithmConfig() instanceof LRUAlgorithmConfig; 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()); EvictionAlgorithmConfig epc1 = erc1.getEvictionAlgorithmConfig(); EvictionAlgorithmConfig epc2 = erc2.getEvictionAlgorithmConfig(); 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-3.2.8.GA/src/test/java/org/jboss/cache/config/parsing/0000755000175000017500000000000011675221517025304 5ustar moellermoeller././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/config/parsing/Eviction2xto3xCompatibilityTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/config/parsing/Eviction2xto3xCompatibilityTes0000644000175000017500000002425311120421271033273 0ustar moellermoeller/* * 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.config.parsing; import org.jboss.cache.Fqn; import org.jboss.cache.config.EvictionAlgorithmConfig; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.config.parsing.element.EvictionElementParser; import org.jboss.cache.eviction.MRUAlgorithmConfig; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.w3c.dom.Element; /** * Eviction was internally changed from version 2.x to version 3.x. * This is a tests to check eviction compatibility between these two versions. * * @author Mircea.Markus@jboss.com * @since 3.0 */ @Test(groups = "unit", testName = "config.parsing.Eviction2xto3xCompatibilityTest") public class Eviction2xto3xCompatibilityTest { private EvictionElementParser evictionElementParser; @BeforeMethod public void setUp() { evictionElementParser = new EvictionElementParser(); } public void testDefaultValues1() throws Exception { String oldFormat = " \n" + " \n" + " 5\n" + " 200000\n" + " org.jboss.cache.eviction.LRUPolicy\n" + " \n" + " 5001\n" + " 1001\n" + " \n" + " \n" + " 1000\n" + " 5000\n" + " \n" + " \n" + " "; Element oldEl = XmlConfigHelper.stringToElementInCoreNS(oldFormat); EvictionConfig oldEvConfig = XmlConfigurationParser2x.parseEvictionConfig(oldEl); //this will be transformed in root region, so make sure that the root region will be corectly set up MRUAlgorithmConfig defaultAlgorithmConfig = (MRUAlgorithmConfig) oldEvConfig.getDefaultEvictionRegionConfig().getEvictionAlgorithmConfig(); assert oldEvConfig.getDefaultEvictionRegionConfig().getEventQueueSize() == 1234; assert defaultAlgorithmConfig.getEvictionAlgorithmClassName().equals("org.jboss.cache.eviction.MRUAlgorithm"); assert defaultAlgorithmConfig.getMaxNodes() == 5001; assert defaultAlgorithmConfig.getMinTimeToLive() == 1001000; assert oldEvConfig.getEvictionRegionConfigs().size() == 1; EvictionRegionConfig orgJbossData = oldEvConfig.getEvictionRegionConfigs().get(0); assert orgJbossData.getRegionFqn().equals(Fqn.fromString("org/jboss/data")); assert orgJbossData.getEvictionAlgorithmConfig().getEvictionAlgorithmClassName().equals("org.jboss.cache.eviction.LRUAlgorithm"); assert orgJbossData.getEventQueueSize() == 200000; } public void simpleTest() throws Exception { String oldFormat = " \n" + " \n" + " 5\n" + " 200000\n" + " org.jboss.cache.eviction.LRUPolicy\n" + " \n" + " 5000\n" + " 1000\n" + " \n" + " \n" + " 1000\n" + " 5000\n" + " \n" + " \n" + " "; String newFormat = "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ""; Element oldEl = XmlConfigHelper.stringToElementInCoreNS(oldFormat); Element newEl = XmlConfigHelper.stringToElementInCoreNS(newFormat); EvictionConfig oldEvConfig = XmlConfigurationParser2x.parseEvictionConfig(oldEl); EvictionConfig newEvConfig = evictionElementParser.parseEvictionElement(newEl); assert oldEvConfig.getDefaultEvictionRegionConfig().equals(newEvConfig.getDefaultEvictionRegionConfig()); EvictionRegionConfig oldRegionConfig = oldEvConfig.getEvictionRegionConfigs().get(0); EvictionRegionConfig newRegionConfig = newEvConfig.getEvictionRegionConfigs().get(0); EvictionAlgorithmConfig oldEvictionAlgorithmConfig = oldRegionConfig.getEvictionAlgorithmConfig(); EvictionAlgorithmConfig newEvictionAlgorithmConfig = newRegionConfig.getEvictionAlgorithmConfig(); assert oldEvictionAlgorithmConfig.equals(newEvictionAlgorithmConfig); assert oldEvConfig.equals(newEvConfig); } public void testFailureOnCustomEvictionPolicy() throws Exception { String oldFormat = " \n" + " \n" + " 5\n" + " 200000\n" + " org.jboss.cache.eviction.LRUPolicy\n" + " \n" + " 5000\n" + " 1000\n" + " \n" + " \n" + " 1000\n" + " 5000\n" + " \n" + " \n" + " "; Element element = XmlConfigHelper.stringToElementInCoreNS(oldFormat); try { XmlConfigurationParser2x.parseEvictionConfig(element); assert false : "custom eviction config not supported for the old parser"; } catch (Exception e) { } } public void testFailureOnCustomDefaultEvictionPolicy() throws Exception { String oldFormat = " \n" + " \n" + " 5\n" + " 200000\n" + " org.custom.eviction.policy.LFUPolicy\n" + " \n" + " 5000\n" + " 1000\n" + " \n" + " \n" + " 1000\n" + " 5000\n" + " \n" + " \n" + " "; Element element = XmlConfigHelper.stringToElementInCoreNS(oldFormat); try { XmlConfigurationParser2x.parseEvictionConfig(element); assert false : "default custom eviction config not supported for the old parser"; } catch (Exception e) { } } } ././@LongLink0000000000000000000000000000015700000000000011570 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/config/parsing/CustomInterceptorsElementParserTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/config/parsing/CustomInterceptorsElementParse0000644000175000017500000001416411146273534033415 0ustar moellermoeller/* * 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.config.parsing; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.config.CustomInterceptorConfig; import org.jboss.cache.config.parsing.custominterceptors.AaaCustomInterceptor; import org.jboss.cache.config.parsing.custominterceptors.BbbCustomInterceptor; import org.jboss.cache.config.parsing.element.CustomInterceptorsElementParser; import org.testng.annotations.Test; import org.w3c.dom.Element; import java.util.List; /** * Tester class for {@link CustomInterceptorsElementParser}. * * @author Mircea.Markus@jboss.com * @since 3.0 */ @Test (groups = "unit", testName = "config.parsing.CustomInterceptorsElementParserTest") public class CustomInterceptorsElementParserTest { CustomInterceptorsElementParser parser = new CustomInterceptorsElementParser(); /** * Tests a correct configuration having all possible elements/attributes. * @throws Exception */ public void testFullConfiguration() throws Exception { String xml = " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " "; Element element = XmlConfigHelper.stringToElementInCoreNS(xml); List configs = parser.parseCustomInterceptors(element); assert configs.size() == 5; CustomInterceptorConfig one = configs.get(0); assert one.isFirst(); assert one.getInterceptor() instanceof AaaCustomInterceptor; assert ((AaaCustomInterceptor)one.getInterceptor()).getAttrOne().equals("value1"); assert ((AaaCustomInterceptor)one.getInterceptor()).getAttrTwo().equals("value2"); assert ((AaaCustomInterceptor)one.getInterceptor()).getAttrThree().equals("value3"); CustomInterceptorConfig two = configs.get(1); assert !two.isFirst(); assert two.isLast(); assert two.getInterceptor() instanceof BbbCustomInterceptor; CustomInterceptorConfig three = configs.get(2); assert !three.isFirst(); assert !three.isLast(); assert three.getIndex() == 3; CustomInterceptorConfig four = configs.get(3); assert !four.isFirst(); assert !four.isLast(); assert four.getBeforeClass().equals("org.jboss.cache.interceptors.CallInterceptor"); CustomInterceptorConfig five = configs.get(4); assert !five.isFirst(); assert !five.isLast(); assert five.getAfterClass().equals("org.jboss.cache.interceptors.CallInterceptor"); } /** * trying to specify an attribute that does not exist on the interceptor class. */ public void testWrongAttribute() throws Exception { String xml = " \n" + " \n" + " \n" + " \n" + " "; Element el = XmlConfigHelper.stringToElementInCoreNS(xml); try { parser.parseCustomInterceptors(el); assert false: "exception expected"; } catch (ConfigurationException e) { //expected } } /** * If the interceptor class is incorrect (e.g. does not extend the CommandInterceptor base class) then parser should fail. */ public void testBadInterceptorClass() throws Exception { String xml = " \n" + " \n" + " \n" + " \n" + " "; Element el = XmlConfigHelper.stringToElementInCoreNS(xml); try { parser.parseCustomInterceptors(el); assert false: "exception expected"; } catch (ConfigurationException e) { //expected } } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/config/parsing/CacheLoadersElementParserTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/config/parsing/CacheLoadersElementParserTest.0000644000175000017500000001651511150773520033153 0ustar moellermoellerpackage org.jboss.cache.config.parsing; import org.testng.annotations.Test; import org.w3c.dom.Element; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.parsing.element.LoadersElementParser; import java.util.List; /** * Tester class for {@link org.jboss.cache.config.parsing.element.LoadersElementParser} * * @author Mircea.Markus@jboss.com * @since 3.0 */ @Test(groups = "unit", testName = "config.parsing.CacheLoadersElementParserTest") public class CacheLoadersElementParserTest { LoadersElementParser parser = new LoadersElementParser(); public void simpleParse() throws Exception { String xmlStr = " " + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " cache.jdbc.table.name=jbosscache\n" + " cache.jdbc.table.create=true\n" + " cache.jdbc.table.drop=true\n" + " \n" + " \n" + " "; CacheLoaderConfig config = getCacheLoaderConfig(xmlStr); assert !config.isPassivation(); assert !config.isShared(); assert config.getPreload().equals("/"); assert config.getFirstCacheLoaderConfig().getClassName().equals("org.jboss.cache.loader.JDBCCacheLoader"); assert config.getFirstCacheLoaderConfig().isAsync(); assert !config.getFirstCacheLoaderConfig().isFetchPersistentState(); assert !config.getFirstCacheLoaderConfig().isIgnoreModifications(); assert !config.getFirstCacheLoaderConfig().isPurgeOnStartup(); } /** * Tests that if no values are specified the parser sets default config values. */ public void testDefaultValues() throws Exception { String xmlStr = " \n" + " \n" + " " + " \n" + " "+ " "; CacheLoaderConfig config = getCacheLoaderConfig(xmlStr); assert config.getPreload().equals("/") : "the default value for preload is root"; assert !config.getFirstCacheLoaderConfig().isAsync() : "by default CL are sync"; assert !config.isShared() : "by default the cl are not sared"; assert !config.getFirstCacheLoaderConfig().isIgnoreModifications(); assert !config.getFirstCacheLoaderConfig().isPurgeOnStartup(); assert !config.getFirstCacheLoaderConfig().getSingletonStoreConfig().isSingletonStoreEnabled(); assert config.getFirstCacheLoaderConfig().getSingletonStoreConfig().getSingletonStoreClass().equals("org.jboss.cache.loader.SingletonStoreCacheLoader"); assert config.getFirstCacheLoaderConfig().getSingletonStoreConfig().getSingletonStoreClass().equals("org.jboss.cache.loader.SingletonStoreCacheLoader"); } public void testMultiplePreloadNodes() throws Exception { String xmlStr = " " + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " " + ""; CacheLoaderConfig config = getCacheLoaderConfig(xmlStr); assert config.getPreload().equals("/,/a,/a/b"); assert config.getFirstCacheLoaderConfig().getSingletonStoreConfig() == null; } public void testMultipleCacheLoaders() throws Exception { String xml = " \n" + " \n" + " \n" + " \n" + " \n" + " "; CacheLoaderConfig clConfig = getCacheLoaderConfig(xml); List indClConfigs = clConfig.getIndividualCacheLoaderConfigs(); assert indClConfigs.size() == 3; assert indClConfigs.get(0).getClassName().equals("org.jboss.cache.loader.JDBCCacheLoader"); assert indClConfigs.get(1).getClassName().equals("org.jboss.cache.loader.bdbje.BdbjeCacheLoader"); assert indClConfigs.get(2).getClassName().equals("org.jboss.cache.loader.FileCacheLoader"); } public void testSingletonStoreDisabled() throws Exception { String xml = " \n" + " \n" + " \n" + " \n" + " \n" + " pushStateWhenCoordinator=some\n" + " pushStateWhenCoordinatorTimeout=cus\n" + " \n" + " \n" + " \n" + " "; CacheLoaderConfig clc = getCacheLoaderConfig(xml); CacheLoaderConfig.IndividualCacheLoaderConfig icl = clc.getFirstCacheLoaderConfig(); CacheLoaderConfig.IndividualCacheLoaderConfig.SingletonStoreConfig singletonStoreConfig = icl.getSingletonStoreConfig(); assert singletonStoreConfig != null; assert !singletonStoreConfig.isSingletonStoreEnabled(); assert singletonStoreConfig.getSingletonStoreClass().equals("org.jboss.cache.loader.SingletonStoreCacheLoader"); assert singletonStoreConfig.getProperties().size() == 2; assert singletonStoreConfig.getProperties().get("pushStateWhenCoordinator").equals("some"); assert singletonStoreConfig.getProperties().get("pushStateWhenCoordinatorTimeout").equals("cus"); } private CacheLoaderConfig getCacheLoaderConfig(String xmlStr) throws Exception { Element element = XmlConfigHelper.stringToElementInCoreNS(xmlStr); return parser.parseLoadersElement(element); } } ././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/config/parsing/SampleConfigFilesCorrectnessTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/config/parsing/SampleConfigFilesCorrectnessTe0000644000175000017500000001342311147030762033262 0ustar moellermoeller/* * 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.config.parsing; import org.apache.log4j.AppenderSkeleton; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.apache.log4j.spi.LoggingEvent; import org.jboss.cache.Cache; import org.jboss.cache.DefaultCacheFactory; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.io.File; import java.io.FilenameFilter; import java.util.Arrays; /** * The purpose of this test is to make sure that the config files we ship are correct both according to xml schema and * according to the used JGroups/JBossCache version. For the latter, we start a cache instance and make sure that no * configuration warnings are being logged by both JGroups and JBossCache. * * @author Mircea.Markus@jboss.com * @since 3.0 */ @Test(groups = "functional", testName = "config.parsing.SampleConfigFilesCorrectnessTest") public class SampleConfigFilesCorrectnessTest { public static final String CONFIG_ROOT = "src/main/resources/config-samples"; public static final String XSD_FILE = "src/main/resources/jbosscache-config-3.1.xsd"; private InMemoryAppender appender; private Level oldLevel; @BeforeMethod public void setUpTest() { Logger log4jLogger = Logger.getRootLogger(); oldLevel = log4jLogger.getLevel(); log4jLogger.setLevel(Level.WARN); appender = new InMemoryAppender(); log4jLogger.addAppender(appender); File f = new File("."); } @AfterMethod public void tearDownTest() { Logger log4jLogger = Logger.getRootLogger(); log4jLogger.setLevel(oldLevel); log4jLogger.removeAppender(appender); appender.close(); } public void testSchemaValidity() { System.setProperty("jbosscache.config.schemaLocation", XSD_FILE); XmlConfigurationSchemaTest.ExceptionCountingErrorHandler errorHandler = new XmlConfigurationSchemaTest.ExceptionCountingErrorHandler(); XmlConfigurationParser parser = new XmlConfigurationParser(errorHandler); String[] configFiles = getConfigFileNames(); for (String aConfFile : configFiles) { parser.parseFile(CONFIG_ROOT + "/" + aConfFile); } assert errorHandler.noErrors(); } public void testConfigWarnings() { DefaultCacheFactory ucf = new DefaultCacheFactory(); for (String aConfFile : getConfigFileNames()) { assert !appender.isFoundUnknownWarning(); Cache cache = ucf.createCache(CONFIG_ROOT + "/" + aConfFile, true); cache.stop(); cache.destroy(); assert !appender.isFoundUnknownWarning(); } } private String[] getConfigFileNames() { File file = new File(CONFIG_ROOT); return file.list(new FilenameFilter() { public boolean accept(File dir, String name) { return name.indexOf("xml") > 0; } }); } private static class InMemoryAppender extends AppenderSkeleton { String[] TOLERABLE_WARNINGS = { "DummyTransactionManager", "not recommended for sync replication", "could not bind to /", //this is a binding excpetion that might appear on some linuxes... "failed to join /" //this might appear on linux + jdk6 }; boolean foundUnknownWarning = false; /** * As this test runs in parallel with other tests tha also log information, we should disregard * other possible warnings from other threads and only consider warnings issues within this test class's test. * * @see #isExpectedThread() */ private Thread loggerThread = Thread.currentThread(); protected void append(LoggingEvent event) { if (event.getLevel().equals(Level.WARN) && isExpectedThread()) { boolean skipPrinting = false; foundUnknownWarning = true; for (String knownWarn : TOLERABLE_WARNINGS) { if (event.getMessage().toString().indexOf(knownWarn) >= 0) { skipPrinting = true; foundUnknownWarning = false; } } if (!skipPrinting) { System.out.println("InMemoryAppender ****** " + event.getMessage().toString()); System.out.println("TOLERABLE_WARNINGS: " + Arrays.toString(TOLERABLE_WARNINGS)); } } } public boolean requiresLayout() { return false; } public void close() { //do nothing } public boolean isFoundUnknownWarning() { return foundUnknownWarning; } public boolean isExpectedThread() { return loggerThread.equals(Thread.currentThread()); } } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/config/parsing/XmlConfigurationParserTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/config/parsing/XmlConfigurationParserTest.jav0000644000175000017500000002570411147030762033315 0ustar moellermoellerpackage org.jboss.cache.config.parsing; 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.CustomInterceptorConfig; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.config.parsing.custominterceptors.AaaCustomInterceptor; import org.jboss.cache.config.parsing.custominterceptors.BbbCustomInterceptor; import org.jboss.cache.eviction.LRUAlgorithmConfig; import org.jboss.cache.eviction.MRUAlgorithmConfig; import org.jboss.cache.lock.IsolationLevel; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import java.util.List; /** * Parses a 'normal' configuration file and makes sure that the resulted Configuration object has * the expected state. * * @author Mircea.Markus@jboss.com * @since 3.0 */ @Test(groups = "functional", testName = "config.parsing.XmlConfigurationParserTest") public class XmlConfigurationParserTest { Configuration syncConfig, asyncConfig; @BeforeTest public void setUp() { syncConfig = new XmlConfigurationParser(false, null).parseFile("configs/parser-test.xml"); asyncConfig = new XmlConfigurationParser(false, null).parseFile("configs/parser-test-async.xml"); } public void testParseOldConfigFile() { System.setProperty("jbosscache.config.validate", "false"); XmlConfigurationParser parser = new XmlConfigurationParser(); try { parser.parseFile("configs/conf2x/pess-local.xml"); assert false : "exception expected"; } catch (ConfigurationException e) { //expectd } finally { System.setProperty("jbosscache.config.validate", "true"); } } public void testTransactionManagerLookupClass() { assert syncConfig.getTransactionManagerLookupClass().equals("org.jboss.cache.transaction.GenericTransactionManagerLookup"); } public void testIsolationLevel() { assert syncConfig.getIsolationLevel().equals(IsolationLevel.REPEATABLE_READ); } public void testCacheMode() { assert syncConfig.getCacheMode().equals(Configuration.CacheMode.REPL_SYNC) : "Was " + syncConfig.getCacheMode(); assert asyncConfig.getCacheMode().equals(Configuration.CacheMode.REPL_ASYNC): "Was " + syncConfig.getCacheMode(); } public void testAsyncSerializationExecutorSize() { assert asyncConfig.getSerializationExecutorPoolSize() == 250; assert asyncConfig.getSerializationExecutorQueueSize() == 5000000; } public void testUseReplQueue() { assert !syncConfig.isUseReplQueue(); assert !asyncConfig.isUseReplQueue(); } public void testClusterName() { assert syncConfig.getClusterName().equals("JBossCache-cluster"); } public void testGetClusterConfig() { assert asyncConfig.getClusterConfig().indexOf("MERGE2") >= 0; } public void testFetchInMemoryState() { assert syncConfig.isFetchInMemoryState(); } public void testStateRetrievalTimeout() { assert syncConfig.getStateRetrievalTimeout() == 15124; } public void testNonBlockingStateTransfer() { assert syncConfig.isNonBlockingStateTransfer(); assert !asyncConfig.isNonBlockingStateTransfer(); } public void testSyncReplTimeout() { assert syncConfig.getSyncReplTimeout() == 15421; } public void testLockAcquisitionTimeout() { assert syncConfig.getLockAcquisitionTimeout() == 10234; } public void testUseLazyDeserialization() { assert syncConfig.isUseLazyDeserialization(); } public void testObjectInputStreamPoolSize() { assert 12 == syncConfig.getObjectInputStreamPoolSize(); } public void testObjectOutputStreamPoolSize() { assert 14 == syncConfig.getObjectOutputStreamPoolSize(); } public void testShutdownHookBehavior() { assert Configuration.ShutdownHookBehavior.REGISTER == syncConfig.getShutdownHookBehavior(); } public void testSyncRollbackPhase() { assert syncConfig.isSyncRollbackPhase(); } public void testSyncCommitPhase() { assert syncConfig.isSyncCommitPhase(); } public void testUseReplicationVersion() { assert syncConfig.getReplicationVersion() == 124; } public void testGetMultiplexerStack() { assert "file_name".equals(syncConfig.getMultiplexerStack()); } public void testMarshallerClass() { assert "some.Clazz".equals(syncConfig.getMarshallerClass()); } public void testLockParentForChildInsertRemove() { assert syncConfig.isLockParentForChildInsertRemove(); } public void testInactiveOnStartup() { assert syncConfig.isInactiveOnStartup(); } public void testExposeManagementStatistics() { assert !syncConfig.getExposeManagementStatistics(); } public void testCacheLoaderConfiguration() { CacheLoaderConfig clc = syncConfig.getCacheLoaderConfig(); assert null != clc; assert clc.isPassivation(); assert clc.isShared(); assert "/a/b/c,/f/r/s".equals(clc.getPreload()); CacheLoaderConfig.IndividualCacheLoaderConfig first = clc.getFirstCacheLoaderConfig(); assert "org.jboss.cache.loader.JDBCCacheLoader".equals(first.getClassName()); assert first.isAsync(); assert first.isFetchPersistentState(); assert first.isIgnoreModifications(); assert first.isPurgeOnStartup(); assert first.getProperties().get("cache.jdbc.table.name").equals("jbosscache"); assert first.getProperties().get("cache.jdbc.table.create").equals("true"); assert first.getProperties().get("cache.jdbc.table.drop").equals("true"); } public void testBuddyReplicationConfig() { BuddyReplicationConfig brConfig = syncConfig.getBuddyReplicationConfig(); assert brConfig.isEnabled(); BuddyReplicationConfig.BuddyLocatorConfig locatorConfig = brConfig.getBuddyLocatorConfig(); assert "org.jboss.cache.buddyreplication.NextMemberBuddyLocator".equals(locatorConfig.getBuddyLocatorClass()); assert locatorConfig.getBuddyLocatorProperties().get("numBuddies").equals("1"); assert locatorConfig.getBuddyLocatorProperties().get("ignoreColocatedBuddies").equals("true"); assert brConfig.getBuddyPoolName().equals("myBuddyPoolReplicationGroup"); assert brConfig.getBuddyCommunicationTimeout() == 2000; assert brConfig.isAutoDataGravitation(); assert brConfig.isDataGravitationRemoveOnFind(); assert brConfig.isDataGravitationSearchBackupTrees(); } public void testUseRegionBasedMarshalling() { assert syncConfig.isUseRegionBasedMarshalling(); } public void testEvictionPolicyConfig() { EvictionConfig evictionConfig = syncConfig.getEvictionConfig(); assert "org.jboss.cache.eviction.LRUAlgorithm".equals(evictionConfig.getDefaultEvictionRegionConfig().getEvictionAlgorithmConfig().getEvictionAlgorithmClassName()); assert 200000 == evictionConfig.getDefaultEvictionRegionConfig().getEventQueueSize(); assert 5 == evictionConfig.getWakeupInterval(); List regionConfigs = evictionConfig.getEvictionRegionConfigs(); assert regionConfigs.size() == 2; EvictionRegionConfig first = evictionConfig.getDefaultEvictionRegionConfig(); assert first.getRegionName().equals("/"); assert first.getEvictionAlgorithmConfig() instanceof LRUAlgorithmConfig; LRUAlgorithmConfig firstConfiguration = (LRUAlgorithmConfig) first.getEvictionAlgorithmConfig(); assert firstConfiguration.getMaxAge() <= 0; assert firstConfiguration.getTimeToLive() == 1000; assert firstConfiguration.getMaxNodes() == 5000; EvictionRegionConfig second = regionConfigs.get(0); LRUAlgorithmConfig secondConfiguration = (LRUAlgorithmConfig) second.getEvictionAlgorithmConfig(); assert secondConfiguration.getMaxAge() == -1; assert secondConfiguration.getTimeToLive() == 1002; assert secondConfiguration.getMaxNodes() == 5000; EvictionRegionConfig third = regionConfigs.get(1); MRUAlgorithmConfig thirdConfiguration = (MRUAlgorithmConfig) third.getEvictionAlgorithmConfig(); assert thirdConfiguration.getMaxNodes() == 2103; assert thirdConfiguration.getMinTimeToLive() == 22; assert third.getEventQueueSize() == 21; } public void testCustomInterceptors() { List interceptorConfigs = syncConfig.getCustomInterceptors(); assert interceptorConfigs.size() == 5; assert interceptorConfigs.get(0).getInterceptor() instanceof AaaCustomInterceptor; AaaCustomInterceptor a = (AaaCustomInterceptor) interceptorConfigs.get(0).getInterceptor(); assert a.getAttrOne().equals("value1"); assert a.getAttrTwo().equals("value2"); assert a.getAttrThree() == null; assert interceptorConfigs.get(1).getInterceptor() instanceof BbbCustomInterceptor; assert interceptorConfigs.get(2).getInterceptor() instanceof AaaCustomInterceptor; assert interceptorConfigs.get(3).getInterceptor() instanceof BbbCustomInterceptor; assert interceptorConfigs.get(4).getInterceptor() instanceof AaaCustomInterceptor; assert interceptorConfigs.get(0).isFirst(); assert !interceptorConfigs.get(0).isLast(); assert interceptorConfigs.get(1).isLast(); assert interceptorConfigs.get(2).getIndex() == 3; assert interceptorConfigs.get(3).getBeforeClass().equals("org.jboss.cache.interceptors.CallInterceptor"); assert interceptorConfigs.get(4).getAfterClass().equals("org.jboss.cache.interceptors.CallInterceptor"); } public void testSingletonStore() { CacheLoaderConfig.IndividualCacheLoaderConfig clc = syncConfig.getCacheLoaderConfig().getFirstCacheLoaderConfig(); assert clc != null; CacheLoaderConfig.IndividualCacheLoaderConfig.SingletonStoreConfig singlStoreConf = clc.getSingletonStoreConfig(); assert singlStoreConf != null; assert singlStoreConf.isSingletonStoreEnabled(); assert singlStoreConf.getSingletonStoreClass().equals("org.jboss.cache.loader.SingletonStoreCacheLoader"); assert singlStoreConf.getProperties().size() == 2; assert singlStoreConf.getProperties().get("pushStateWhenCoordinator").equals("true"); assert singlStoreConf.getProperties().get("pushStateWhenCoordinatorTimeout").equals("20000"); } public void testMvccAttributes() { assert !syncConfig.isWriteSkewCheck(); assert syncConfig.getConcurrencyLevel() == 21; } public void testListenerAsyncThreads() { assert syncConfig.getListenerAsyncPoolSize() == 5; assert syncConfig.getListenerAsyncQueueSize() == 50000; // the default assert asyncConfig.getListenerAsyncPoolSize() == 5; assert asyncConfig.getListenerAsyncQueueSize() == 100000; } public void testInvocationBatching() { assert syncConfig.isInvocationBatchingEnabled(); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/config/parsing/EvictionElementParserTest.java0000644000175000017500000003737311111047320033252 0ustar moellermoellerpackage org.jboss.cache.config.parsing; import org.jboss.cache.Fqn; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.config.MissingPolicyException; import org.jboss.cache.config.parsing.element.EvictionElementParser; import org.jboss.cache.eviction.DefaultEvictionActionPolicy; import org.jboss.cache.eviction.FIFOAlgorithmConfig; import org.jboss.cache.eviction.LFUAlgorithmConfig; import org.jboss.cache.eviction.LRUAlgorithmConfig; import org.jboss.cache.eviction.MRUAlgorithmConfig; import org.jboss.cache.eviction.NullEvictionAlgorithm; import org.jboss.cache.eviction.NullEvictionAlgorithmConfig; import org.jboss.cache.eviction.RemoveOnEvictActionPolicy; import org.testng.annotations.Test; import org.w3c.dom.Element; /** * Tester class for {@link org.jboss.cache.config.parsing.element.EvictionElementParser}. * * @author Mircea.Markus@jboss.com * @since 3.0 */ @Test(groups = "unit", testName = "config.parsing.EvictionElementParserTest") public class EvictionElementParserTest { EvictionElementParser parser = new EvictionElementParser(); /** * Test that when nodes are not specified the config defaults to certain values. */ public void testDefaults() { String xml = " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " "; EvictionConfig evictionConfig = getEvictionConfig(xml, false); assert evictionConfig.getDefaultEvictionRegionConfig().getEventQueueSize() == EvictionConfig.EVENT_QUEUE_SIZE_DEFAULT; assert evictionConfig.getDefaultEvictionRegionConfig().getEvictionActionPolicyClassName().equals(DefaultEvictionActionPolicy.class.getName()); } public void testRegionWithNoProperties() { String xml = " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " "; EvictionConfig evictionConfig = getEvictionConfig(xml, false); assert evictionConfig.getDefaultEvictionRegionConfig().getEventQueueSize() == EvictionConfig.EVENT_QUEUE_SIZE_DEFAULT; assert evictionConfig.getDefaultEvictionRegionConfig().getEvictionActionPolicyClassName().equals(DefaultEvictionActionPolicy.class.getName()); EvictionRegionConfig abc = evictionConfig.getEvictionRegionConfig("/org/jboss/abc"); EvictionRegionConfig xyz = evictionConfig.getEvictionRegionConfig("/org/jboss/xyz"); assert abc.getEventQueueSize() == 21; assert abc.getEvictionAlgorithmConfig() instanceof MRUAlgorithmConfig; assert ((MRUAlgorithmConfig) abc.getEvictionAlgorithmConfig()).getMaxNodes() == 10; assert ((MRUAlgorithmConfig) abc.getEvictionAlgorithmConfig()).getMinTimeToLive() == 10; assert xyz.getEventQueueSize() == EvictionConfig.EVENT_QUEUE_SIZE_DEFAULT; assert xyz.getEvictionAlgorithmConfig() instanceof FIFOAlgorithmConfig; assert ((FIFOAlgorithmConfig) xyz.getEvictionAlgorithmConfig()).getMaxNodes() == -1; assert ((FIFOAlgorithmConfig) xyz.getEvictionAlgorithmConfig()).getMinTimeToLive() == -1; } /** * test unnecessary propertys */ public void testUnnecessaryAttributes() { String xml = " \n" + " \n" + " \n" + " \n" + " \n" + " "; try { EvictionConfig config = getEvictionConfig(xml, false); assert false : "Should throw ConfigurationException!"; } catch (ConfigurationException good) { // expected } } /** * test an happy flow. */ public void testNormalConfig() { String xml = " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " "; EvictionConfig config = getEvictionConfig(xml, false); //tests the defaults assert config.getWakeupInterval() == 5; assert config.getDefaultEvictionRegionConfig().getEvictionAlgorithmConfig() instanceof MRUAlgorithmConfig; assert config.getDefaultEvictionRegionConfig().getEventQueueSize() == 123456; assert config.getEvictionRegionConfigs().size() == 2; //test first region config EvictionRegionConfig erConfig1 = config.getDefaultEvictionRegionConfig(); erConfig1.getRegionFqn().equals(Fqn.ROOT); MRUAlgorithmConfig defaultPolicyConfig = (MRUAlgorithmConfig) erConfig1.getEvictionAlgorithmConfig(); assert defaultPolicyConfig.getMaxNodes() == 6; assert defaultPolicyConfig.getMinTimeToLive() == 7; //test second region config EvictionRegionConfig erConfig2 = config.getEvictionRegionConfigs().get(0); assert erConfig2.getEventQueueSize() == 123456 : "Got " + erConfig2.getEventQueueSize(); assert erConfig2.getRegionFqn().equals(Fqn.fromString("/org/jboss/data")); MRUAlgorithmConfig mruConfiguration = (MRUAlgorithmConfig) erConfig2.getEvictionAlgorithmConfig(); assert mruConfiguration.getMinTimeToLive() == 1002; assert mruConfiguration.getMaxNodes() == 2021; //test 3rd region config EvictionRegionConfig erConfig3 = config.getEvictionRegionConfigs().get(1); assert erConfig3.getEventQueueSize() == 21; assert erConfig3.getRegionFqn().equals(Fqn.fromString("/org/jboss/xyz")); LRUAlgorithmConfig lruConfiguration = (LRUAlgorithmConfig) erConfig3.getEvictionAlgorithmConfig(); assert lruConfiguration.getTimeToLive() == 22; assert lruConfiguration.getMaxNodes() == 2103; assert config.getDefaultEvictionRegionConfig().getEvictionActionPolicyClassName().equals(DefaultEvictionActionPolicy.class.getName()); } public void testLruConfig() { String xml = " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " "; EvictionConfig evConfig = getEvictionConfig(xml, false); EvictionRegionConfig evictionRegionConfig = evConfig.getDefaultEvictionRegionConfig(); assert evictionRegionConfig.getRegionName().equals(Fqn.ROOT.toString()) : "Was " + evictionRegionConfig.getRegionName(); assert ((LRUAlgorithmConfig) evictionRegionConfig.getEvictionAlgorithmConfig()).getTimeToLive() == 1000000; assert evConfig.getDefaultEvictionRegionConfig().getEvictionActionPolicyClassName().equals(DefaultEvictionActionPolicy.class.getName()); } /** * This is an mandatory parameter, if it is not present then an configuration exception is expected. */ public void testMissingWakeUpInterval() throws Exception { String xml = " \n" + " \n" + " 5000\n" + " 1000\n" + " \n" + " \n" + " 1002\n" + " \n" + " "; try { getEvictionConfig(xml, false); assert false : "exception expected as wake up interval is not set"; } catch (ConfigurationException e) { //expected } } /** * If no policy is configured at region basis, and also no policy is configured at region basis, * exception is expected. */ public void testMissingPolicyOnRegion() { String xml = " \n" + " \n" + " 1002\n" + " \n" + " "; try { getEvictionConfig(xml, false); assert false : "missing policy in both default and region, exception expected."; } catch (MissingPolicyException e) { //expected } } /** * Same as above, except no queue size is specified. SHould NOT fail. */ public void testMissingQueueSizeOnRegion() { String xml = " \n" + " \n" + " 1002\n" + " \n" + " "; EvictionConfig ec = getEvictionConfig(xml, false); assert ec.getEvictionRegionConfigs().get(0).getEventQueueSize() == EvictionConfig.EVENT_QUEUE_SIZE_DEFAULT; } private EvictionConfig getEvictionConfig(String xml, boolean validate) { Element el; try { el = XmlConfigHelper.stringToElementInCoreNS(xml); } catch (Exception e) { throw new ConfigurationException(e); } EvictionConfig cfg = parser.parseEvictionElement(el); if (validate) { cfg.getDefaultEvictionRegionConfig().validate(); for (EvictionRegionConfig erc : cfg.getEvictionRegionConfigs()) erc.validate(); } return cfg; } public void testMissingDefaultEvictionClass() throws Exception { String xml = " \n" + " \n" + " 5000\n" + " 1000\n" + " \n" + " \n" + " 5000\n" + " 1000\n" + " \n" + " "; try { getEvictionConfig(xml, true); assert false : " exception expected as default element does not have a eviction policy defined"; } catch (MissingPolicyException e) { //expected } } public void testDifferentEvictionActionPolicyClasses() throws Exception { String xml = " \n" + " \n" + " \n" + " \n" + " 5000\n" + " 1000\n" + " \n" + " \n" + " \n" + " "; EvictionConfig config = getEvictionConfig(xml, false); // default region assert config.getDefaultEvictionRegionConfig().getEvictionAlgorithmConfig() instanceof NullEvictionAlgorithmConfig; assert config.getDefaultEvictionRegionConfig().getEvictionActionPolicyClassName().equals(RemoveOnEvictActionPolicy.class.getName()); // region /one assert findRegionConfig(config, "/one").getEvictionAlgorithmConfig() instanceof LFUAlgorithmConfig; assert findRegionConfig(config, "/one").getEvictionActionPolicyClassName().equals(RemoveOnEvictActionPolicy.class.getName()); // region /two assert findRegionConfig(config, "/two").getEvictionAlgorithmConfig() instanceof NullEvictionAlgorithmConfig; assert findRegionConfig(config, "/two").getEvictionActionPolicyClassName().equals(DefaultEvictionActionPolicy.class.getName()); } private EvictionRegionConfig findRegionConfig(EvictionConfig evictionConfig, String fqn) { for (EvictionRegionConfig erc : evictionConfig.getEvictionRegionConfigs()) { if (erc.getRegionFqn().equals(Fqn.fromString(fqn))) return erc; } return null; } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/config/parsing/XmlConfigurationSchemaTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/config/parsing/XmlConfigurationSchemaTest.jav0000644000175000017500000000624711147030762033262 0ustar moellermoellerpackage org.jboss.cache.config.parsing; import org.testng.annotations.Test; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import java.io.File; import java.util.ArrayList; import java.util.List; /** * Tests that all the xml file used within tests are correct with respect to the schema definition. * * @author Mircea.Markus@jboss.com * @since 3.0 */ @Test(groups = "functional", testName = "config.parsing.XmlConfigurationSchemaTest") public class XmlConfigurationSchemaTest { public static final String BASE_DIR_FOR_CONFIG = "./configs"; private String[] testFiles = { "buddy-replication-cache.xml", "clonable-config.xml", "local-lru-eviction.xml", "local-passivation.xml", "local-tx.xml", "mixedPolicy-eviction.xml", "mux.xml", "parser-test.xml", "parser-test-async.xml", "policyPerRegion-eviction.xml", "replSync.xml", "string-property-replaced.xml", "mvcc-repl-sync-br.xml" }; /** * Simple test to prove that validation works. */ public void testSimpleFile() { ExceptionCountingErrorHandler handler = new ExceptionCountingErrorHandler(); System.setProperty("jbosscache.config.schemaLocation", "src/main/resources/jbosscache-config-3.1.xsd"); XmlConfigurationParser parser = new XmlConfigurationParser(handler); for (String file : testFiles) { parser.parseFile(BASE_DIR_FOR_CONFIG + File.separator + file); for (Exception e : handler.exceptionList) e.printStackTrace(); assert handler.noErrors() : "error during parsing (file " + file + ")"; } } /** * Test that when the jbc.config.validation is set to true the parser is not validating by default. */ public void testValidationDisbaledOnSystemProperty() { XmlConfigurationParser parser = new XmlConfigurationParser(); assert parser.isValidating() : "by default we have a validating parser"; System.setProperty(RootElementBuilder.VALIDATING_SYSTEM_PROPERTY, "false"); parser = new XmlConfigurationParser(); assert !parser.isValidating(); System.setProperty(RootElementBuilder.VALIDATING_SYSTEM_PROPERTY, "true"); parser = new XmlConfigurationParser(); assert parser.isValidating(); } public static class ExceptionCountingErrorHandler implements ErrorHandler { List exceptionList = new ArrayList(); public void warning(SAXParseException exception) throws SAXException { handleDefault(exception); } public void error(SAXParseException exception) throws SAXException { handleDefault(exception); } public void fatalError(SAXParseException exception) throws SAXException { handleDefault(exception); } private void handleDefault(SAXParseException exception) { exceptionList.add(exception); } public boolean noErrors() { return exceptionList.isEmpty(); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/config/parsing/ParseFileWith30SchemaTest.java0000644000175000017500000000201011150773520032764 0ustar moellermoellerpackage org.jboss.cache.config.parsing; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.ConfigurationException; import org.testng.annotations.Test; @Test(groups = "unit", testName = "config.parsing.ParseFileWith30SchemaTest") public class ParseFileWith30SchemaTest { public void testFileWithDeclared30Schema() { String fileToTest = "configs/simple-3_0-config-file.xml"; XmlConfigurationParser parser = new XmlConfigurationParser(); assert parser.isValidating(); Configuration c = parser.parseFile(fileToTest); assert c.getLockAcquisitionTimeout() == 500; } @Test (expectedExceptions = ConfigurationException.class) public void testFileWithDeclared30SchemaWith31Elements() { String fileToTest = "configs/incorrect-3_0-config-file.xml"; XmlConfigurationParser parser = new XmlConfigurationParser(); assert parser.isValidating(); Configuration c = parser.parseFile(fileToTest); assert false : "Should throw exception!"; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/config/parsing/CacheConfigsTest.java0000644000175000017500000001361411237040647031325 0ustar moellermoellerpackage org.jboss.cache.config.parsing; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.lock.IsolationLevel; import org.testng.annotations.Test; import java.io.ByteArrayInputStream; import java.util.HashMap; import java.util.Map; /** * Tests the {@link org.jboss.cache.config.parsing.CacheConfigsXmlParser}. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ @Test(groups = "unit", testName = "config.parsing.CacheConfigsTest") public class CacheConfigsTest { public void testNewFormat() throws CloneNotSupportedException { String xml = "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ""; ByteArrayInputStream bais = new ByteArrayInputStream(xml.getBytes()); CacheConfigsXmlParser ccxp = new CacheConfigsXmlParser(); Map map = ccxp.parseConfigs(bais, null); Map toExpect = buildExpectedValues(); assert map.equals(toExpect) : "Expected " + toExpect + " but was " + map; } public void testLegacyFormat() throws CloneNotSupportedException { String xml = "\n" + " \n" + " REPEATABLE_READ\n" + " 15000\n" + " org.jboss.cache.transaction.GenericTransactionManagerLookup\n" + " 20000\n" + " REPL_SYNC"+ " \n" + "\n" + " \n" + " READ_COMMITTED\n" + " 15000\n" + " org.jboss.cache.transaction.GenericTransactionManagerLookup\n" + " 20000\n" + " REPL_SYNC"+ " \n" + "\n" + " \n" + " READ_COMMITTED\n" + " 100\n" + " 100\n" + " REPL_SYNC"+ " \n" + ""; ByteArrayInputStream bais = new ByteArrayInputStream(xml.getBytes()); CacheConfigsXmlParser ccxp = new CacheConfigsXmlParser(); Map map = ccxp.parseConfigs(bais, null); Map toExpect = buildExpectedValues(); assert map.equals(toExpect); } private Map buildExpectedValues() { Map map = new HashMap(3); Configuration cfg = new Configuration(); map.put("A", cfg); cfg.setIsolationLevel(IsolationLevel.REPEATABLE_READ); cfg.setLockAcquisitionTimeout(15000); cfg.setTransactionManagerLookupClass("org.jboss.cache.transaction.GenericTransactionManagerLookup"); cfg.setStateRetrievalTimeout(20000); cfg.setCacheMode(CacheMode.REPL_SYNC); cfg = new Configuration(); map.put("B", cfg); cfg.setIsolationLevel(IsolationLevel.READ_COMMITTED); cfg.setLockAcquisitionTimeout(15000); cfg.setTransactionManagerLookupClass("org.jboss.cache.transaction.GenericTransactionManagerLookup"); cfg.setStateRetrievalTimeout(20000); cfg.setCacheMode(CacheMode.REPL_SYNC); cfg = new Configuration(); map.put("C", cfg); cfg.setIsolationLevel(IsolationLevel.READ_COMMITTED); cfg.setLockAcquisitionTimeout(100); cfg.setStateRetrievalTimeout(100); cfg.setCacheMode(CacheMode.REPL_SYNC); return map; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/config/parsing/JGroupsStackParserTest.java0000644000175000017500000001062611162403335032537 0ustar moellermoellerpackage org.jboss.cache.config.parsing; import org.testng.annotations.Test; import org.w3c.dom.Element; import org.jboss.cache.config.parsing.JGroupsStackParser; /** * Tester class for {@link JGroupsStackParser} * * @author Mircea.Markus@jboss.com * @since 3.0 */ @Test(groups = "unit", testName = "config.parsing.JGroupsStackParserTest") public class JGroupsStackParserTest { private JGroupsStackParser parser = new JGroupsStackParser(); public void testSimpleParse() throws Exception { String xml = "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ""; Element element = XmlConfigHelper.stringToElementInCoreNS(xml); String result = parser.parseClusterConfigXml(element); assert result.indexOf("ucast_recv_buf_size=20000000") > 0; assert result.indexOf("num_initial_members=3") > 0; assert result.indexOf("min_interval=10000") > 0; } public void testParsingEmptyConfig() throws Exception { String xml = ""; Element element = XmlConfigHelper.stringToElementInCoreNS(xml); String result = parser.parseClusterConfigXml(element); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/config/parsing/custominterceptors/0000755000175000017500000000000011675221517031260 5ustar moellermoeller././@LongLink0000000000000000000000000000016300000000000011565 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/config/parsing/custominterceptors/BbbCustomInterceptor.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/config/parsing/custominterceptors/BbbCustomIn0000644000175000017500000000244211111047320033333 0ustar moellermoeller/* * 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.config.parsing.custominterceptors; import org.jboss.cache.interceptors.base.CommandInterceptor; /** * Used for testing custom interceptors construction. * * @author Mircea.Markus@jboss.com * @since 3.0 */ public class BbbCustomInterceptor extends CommandInterceptor { } ././@LongLink0000000000000000000000000000016500000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/config/parsing/custominterceptors/WrongCustomInterceptor.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/config/parsing/custominterceptors/WrongCustom0000644000175000017500000000244711111047320033460 0ustar moellermoeller/* * 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.config.parsing.custominterceptors; /** * Used for testing custom interceptors construction. * This is a incorrect interceptor implementation as it does not extend CommandInterceptor. * * @author Mircea.Markus@jboss.com * @since 3.0 */ public class WrongCustomInterceptor { } ././@LongLink0000000000000000000000000000016300000000000011565 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/config/parsing/custominterceptors/AaaCustomInterceptor.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/config/parsing/custominterceptors/AaaCustomIn0000644000175000017500000000347011111047320033332 0ustar moellermoeller/* * 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.config.parsing.custominterceptors; import org.jboss.cache.interceptors.base.CommandInterceptor; /** * Used for testing custom interceptors construction. * * @author Mircea.Markus@jboss.com * @since 3.0 */ public class AaaCustomInterceptor extends CommandInterceptor { private String attrOne; private String attrTwo; private String attrThree; public String getAttrOne() { return attrOne; } public String getAttrTwo() { return attrTwo; } public String getAttrThree() { return attrThree; } public void setAttrOne(String attrOne) { this.attrOne = attrOne; } public void setAttrTwo(String attrTwo) { this.attrTwo = attrTwo; } public void setAttrThree(String attrThree) { this.attrThree = attrThree; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/config/parsing/BuddyElementParserTest.java0000644000175000017500000000650311111047320032530 0ustar moellermoellerpackage org.jboss.cache.config.parsing; import org.testng.annotations.Test; import org.w3c.dom.Element; import org.jboss.cache.config.BuddyReplicationConfig; import org.jboss.cache.config.parsing.element.BuddyElementParser; import org.jboss.cache.buddyreplication.NextMemberBuddyLocator; /** * Tester class for {@link org.jboss.cache.config.parsing.element.BuddyElementParser}. * * @author Mircea.Markus@jboss.com * @since 3.0 */ @Test(groups = "unit", sequential = true, testName = "config.parsing.BuddyElementParserTest") public class BuddyElementParserTest { /** one instance per all tests as it is stateless */ BuddyElementParser parser = new BuddyElementParser(); /** * Test default values for unspecified elements. */ public void testDefaultValues() throws Exception { String xmlConfig = ""; Element element = XmlConfigHelper.stringToElementInCoreNS(xmlConfig); BuddyReplicationConfig brConfig = parser.parseBuddyElement(element); assert brConfig.getBuddyLocatorConfig().getClassName().equals(NextMemberBuddyLocator.class.getName()) : "default buddy locator class is NextMemberBuddyLocator"; assert brConfig.getBuddyLocatorConfig().getBuddyLocatorProperties().isEmpty(); assert brConfig.isDataGravitationRemoveOnFind() : "default to true"; assert brConfig.isDataGravitationSearchBackupTrees() : "default to true"; assert brConfig.isAutoDataGravitation() : "default to false"; } /** * If NextMemberBuddyLocator is set as buddy locator, but no params are being specified for it, make sure that * default values for numBudies and ignoreColocatedBuddies are present. */ public void testDefaultParamsForNextMemberBuddyLocator() throws Exception { String xmlConfig = " \n" + " \n" + " \n" + " numBuddies = 3\n" + " \n" + " \n" + " "; Element element = XmlConfigHelper.stringToElementInCoreNS(xmlConfig); BuddyReplicationConfig brConfig = parser.parseBuddyElement(element); assert brConfig.getBuddyLocatorConfig().getClassName().equals(NextMemberBuddyLocator.class.getName()) : "default buddy locator class is NextMemberBuddyLocator"; assert brConfig.getBuddyLocatorConfig().getBuddyLocatorProperties().get("numBuddies").equals("3"); assert brConfig.getBuddyLocatorConfig().getBuddyLocatorProperties().size() == 1; } public void testNormalConfig() throws Exception { String xmlConfig = " \n" + " \n" + " \n" + " numBuddies = 3\n" + " \n" + " \n" + " "; Element element = XmlConfigHelper.stringToElementInCoreNS(xmlConfig); BuddyReplicationConfig brConfig = parser.parseBuddyElement(element); assert brConfig.isEnabled(); assert brConfig.getBuddyPoolName().equals("groupOne"); assert brConfig.getBuddyLocatorConfig().getBuddyLocatorProperties().get("numBuddies").equals("3"); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/config/ConfigurationTransformerTest.java0000644000175000017500000001225111146273534032376 0ustar moellermoellerpackage org.jboss.cache.config; import org.jboss.cache.config.parsing.ConfigFilesConvertor; import org.jboss.cache.config.parsing.XmlConfigurationParser; import org.jboss.cache.config.parsing.XmlConfigurationParser2x; import org.jboss.cache.eviction.LRUAlgorithmConfig; import org.testng.annotations.Test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; /** * Test how xsl for migrating config files from 2.x to 3.x works. * For each existing config file in 2.x it does the following: *

    *
  1. it transforms it into an 3.x file using the xslt transformer *
  2. it parses the file with 2.x parser *
  3. it parses the transform with a 3.x parser *
  4. checks that the two resulting Configuration objects are equal. *
* * @author Mircea.Markus@jboss.com * @since 3.0 */ @Test(groups = "unit", testName = "config.ConfigurationTransformerTest") public class ConfigurationTransformerTest { public static final String XSLT_FILE = "config2to3.xslt"; private static final String BASE_DIR = "configs/conf2x"; ConfigFilesConvertor convertor = new ConfigFilesConvertor(); /** * Useful when {@link testEqualityOnTransformedFiles} fails and you need to isolate a failure. */ public void testSingleFile() throws Exception { String fileName = getFileName("/policyPerRegion-eviction.xml"); ByteArrayOutputStream baos = new ByteArrayOutputStream(); convertor.parse(fileName, baos, XSLT_FILE); XmlConfigurationParser newParser = new XmlConfigurationParser(); XmlConfigurationParser2x oldParser = new XmlConfigurationParser2x(); Configuration newConfig = newParser.parseStream(new ByteArrayInputStream(baos.toByteArray())); Configuration oldConfig = oldParser.parseFile(fileName); assert oldConfig.equals(newConfig); } public void testEqualityOnTransformedFiles() throws Exception { String[] fileNames = { "buddy-replication-cache.xml", "local-cache.xml", "multiplexer-enabled-cache.xml", "total-replication-cache.xml", }; for (String file : fileNames) { String fileName = getFileName(file); ByteArrayOutputStream baos = new ByteArrayOutputStream(); convertor.parse(fileName, baos, XSLT_FILE); XmlConfigurationParser newParser = new XmlConfigurationParser(); XmlConfigurationParser2x oldParser = new XmlConfigurationParser2x(); Configuration newConfig = newParser.parseStream(new ByteArrayInputStream(baos.toByteArray())); Configuration oldConfig = oldParser.parseFile(fileName); assert newConfig.equals(oldConfig); } } /** * Not like the rest of elements, eviction was also changed in 3.x. * As the parser produces different results, we semantically check here that eviction is transformed corectly. */ public void testEqualityOnEvictionTransformedFiles() throws Exception { String[] fileNames = { "cacheloader-enabled-cache.xml", "clonable-config.xml", "default-test-config2x.xml", "eviction-enabled-cache.xml", "optimistically-locked-cache.xml", "policyPerRegion-eviction.xml", }; for (String file : fileNames) { String fileName = getFileName(file); ByteArrayOutputStream baos = new ByteArrayOutputStream(); convertor.parse(fileName, baos, XSLT_FILE); XmlConfigurationParser newParser = new XmlConfigurationParser(); XmlConfigurationParser2x oldParser = new XmlConfigurationParser2x(); Configuration newConfig = newParser.parseStream(new ByteArrayInputStream(baos.toByteArray())); Configuration oldConfig = oldParser.parseFile(fileName); assert newConfig.equals(oldConfig); } } public void testUnlimitedValues() throws Exception { // in 3.x, unlimited values in eviction are denoted by -1 and not 0! String fileName = getFileName("/zeroTTL.xml"); ByteArrayOutputStream baos = new ByteArrayOutputStream(); convertor.parse(fileName, baos, XSLT_FILE); XmlConfigurationParser newParser = new XmlConfigurationParser(); XmlConfigurationParser2x oldParser = new XmlConfigurationParser2x(); Configuration newConfig = newParser.parseStream(new ByteArrayInputStream(baos.toByteArray())); Configuration oldConfig = oldParser.parseFile(fileName); for (EvictionRegionConfig erc : oldConfig.getEvictionConfig().getEvictionRegionConfigs()) { correctUnlimitedValues(erc); } correctUnlimitedValues(oldConfig.getEvictionConfig().getDefaultEvictionRegionConfig()); assert oldConfig.equals(newConfig); } private void correctUnlimitedValues(EvictionRegionConfig erc) { LRUAlgorithmConfig eac = (LRUAlgorithmConfig) erc.getEvictionAlgorithmConfig(); if (eac.getMaxAge() <= 0) eac.setMaxAge(-1); if (eac.getMaxNodes() <= 0) eac.setMaxNodes(-1); if (eac.getMinTimeToLive() <= 0) eac.setMinTimeToLive(-1); if (eac.getTimeToLive() <= 0) eac.setTimeToLive(-1); } private String getFileName(String s) { return BASE_DIR + File.separator + s; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/config/ChannelInjectionTest.java0000644000175000017500000000453111132625723030555 0ustar moellermoellerpackage org.jboss.cache.config; import org.jboss.cache.Cache; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.factories.UnitTestConfigurationFactory; 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: 7451 $ */ @Test(groups = {"functional"}, testName = "config.ChannelInjectionTest") public class ChannelInjectionTest { private Set> caches = new HashSet>(); @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { for (Cache cache : caches) { try { TestingUtil.killCaches(cache); } catch (Exception e) { e.printStackTrace(System.out); } } caches = null; } public void testChannelInjectionPreference() throws Exception { Cache cache1 = createCache(); Cache cache2 = createCache(); Configuration conf = UnitTestConfigurationFactory.getEmptyConfiguration(); JChannel ch1 = new JChannel(conf.getClusterConfig()); cache1.getConfiguration().getRuntimeConfig().setChannel(ch1); JChannel ch2 = new JChannel(conf.getClusterConfig()); 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); UnitTestCacheFactory instance = new UnitTestCacheFactory(); Cache cache = instance.createCache(config, false, getClass()); caches.add(cache); return cache; } }jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/config/StringPropertyReplacementTest.java0000644000175000017500000001013011111047320032511 0ustar moellermoeller/* * 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.config.BuddyReplicationConfig.BuddyLocatorConfig; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.config.parsing.XmlConfigurationParser; import org.jboss.cache.eviction.LRUAlgorithmConfig; import static org.testng.AssertJUnit.*; 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"}, testName = "config.StringPropertyReplacementTest") public class StringPropertyReplacementTest { public static final String STRING_REPLACED_FILE = "configs/string-property-replaced.xml"; private static final String PROP_BASE = "test.property."; 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 numBuddies; private String syncCommitPhase; private String maxNodes; private String buddyPoolName; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { 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 (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(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.MVCC, cfg.getNodeLockingScheme()); assertFalse(cfg.isSyncCommitPhase()); assertTrue(cfg.isSyncRollbackPhase()); assertEquals(15000, cfg.getLockAcquisitionTimeout()); String clusterCfg = cfg.getClusterConfig(); assertTrue(clusterCfg == null || clusterCfg.length() == 0); EvictionConfig ec = cfg.getEvictionConfig(); assert ec.getDefaultEvictionRegionConfig().getEvictionAlgorithmConfig() instanceof LRUAlgorithmConfig; EvictionRegionConfig erc = ec.getDefaultEvictionRegionConfig(); LRUAlgorithmConfig epc = (LRUAlgorithmConfig) erc.getEvictionAlgorithmConfig(); 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-3.2.8.GA/src/test/java/org/jboss/cache/config/EvictionRegionConfigurationTest.java0000644000175000017500000000350011111047320032775 0ustar moellermoeller/* * 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.Fqn; import org.jboss.cache.RegionManagerImpl; import org.jboss.cache.eviction.EvictionPolicy; import org.jboss.cache.eviction.LRUPolicy; import static org.testng.AssertJUnit.fail; import org.testng.annotations.Test; import java.util.HashSet; import java.util.List; import java.util.Set; /** * @author Brian Stansberry */ @Test(groups = {"functional", "jgroups", "transaction"}, testName = "config.EvictionRegionConfigurationTest") 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(RegionManagerImpl.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-3.2.8.GA/src/test/java/org/jboss/cache/loader/0000755000175000017500000000000011675221542023640 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/CacheLoaderWithRollbackTest.java0000644000175000017500000000527711132627432032012 0ustar moellermoellerpackage org.jboss.cache.loader; import org.jboss.cache.Cache; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.loader.testloaders.DummyInMemoryCacheLoader; import org.jboss.cache.transaction.DummyTransactionManager; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.Test; import javax.transaction.TransactionManager; @Test(groups = "functional", testName = "loader.CacheLoaderWithRollbackTest") public class CacheLoaderWithRollbackTest { final Fqn fqn = Fqn.fromString("/a/b"); final String key = "key"; protected Configuration.NodeLockingScheme getNodeLockingScheme() { return Configuration.NodeLockingScheme.MVCC; } public Cache init(boolean passivation) throws Exception { CacheLoaderConfig cacheLoaderConfig = UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(passivation, "", DummyInMemoryCacheLoader.class.getName(), "", false, true, false, false, false); Configuration cfg = new Configuration(); cfg.setNodeLockingScheme(getNodeLockingScheme()); cfg.setCacheLoaderConfig(cacheLoaderConfig); cfg.getRuntimeConfig().setTransactionManager(new DummyTransactionManager()); Cache cache = new UnitTestCacheFactory().createCache(cfg, getClass()); cache.put(fqn, key, "value"); // evict the node, so we have to go to the loader to do anything with it cache.evict(fqn); return cache; } public void testWithPassivation() throws Exception { doTest(true); } public void testWithoutPassivation() throws Exception { doTest(false); } private void doTest(boolean passivation) throws Exception { Cache cache = null; try { cache = init(passivation); TransactionManager tm = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); tm.begin(); assert cache.getNode(fqn.getParent().getParent()).getChildrenNames().size() == 1; assert cache.getNode(fqn.getParent()).getChildrenNames().size() == 1; tm.rollback(); // in the fail scenario the rollback corrupts the parent node tm.begin(); assert cache.getNode(fqn.getParent().getParent()).getChildrenNames().size() == 1; // watch here: int sz = cache.getNode(fqn.getParent()).getChildrenNames().size(); assert sz == 1 : "Expecting 1, was " + sz; tm.commit(); } finally { TestingUtil.killCaches(cache); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/TcpCacheLoaderTest.java0000644000175000017500000002264711136161154030151 0ustar moellermoellerpackage org.jboss.cache.loader; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestConfigurationFactory; 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.jboss.cache.util.TestingUtil; import org.testng.annotations.AfterClass; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import javax.transaction.Synchronization; import java.util.*; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * Tests the TcpDelegatingCacheLoader * * @author Bela Ban * @version $Id: TcpCacheLoaderTest.java 7573 2009-01-22 21:17:32Z mircea.markus $ */ @Test(groups = "functional", testName = "loader.TcpCacheLoaderTest") public class TcpCacheLoaderTest extends CacheLoaderTestsBase { protected static final String TCP_CACHE_SERVER_HOST = "127.0.0.1"; protected static final int TCP_CACHE_SERVER_PORT = 12121; protected static final int CACHE_SERVER_RESTART_DELAY_MS = 250; protected static final int TCP_CACHE_LOADER_TIMEOUT_MS = 1000; protected static int START_COUNT = 0; static volatile TcpCacheServer cacheServer = null; @Override @BeforeClass public void preCreate() { if (cacheServer != null) stopCacheServer(); startCacheServer(); } private static void startCacheServer() { final CountDownLatch startedSignal = new CountDownLatch(1); Thread t = new Thread() { public void run() { try { cacheServer = new TcpCacheServer(); cacheServer.setBindAddress(TCP_CACHE_SERVER_HOST); cacheServer.setPort(TCP_CACHE_SERVER_PORT); Configuration config = UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); // disable eviction!! config.setEvictionConfig(null); CacheSPI cache = (CacheSPI) new UnitTestCacheFactory().createCache(config, getClass()); 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) { cacheServer.stop(); cacheServer = null; } } @AfterMethod public void removeRestarters() { if (cache != null) { Set restarters = new HashSet(); for (Object listener : cache.getCacheListeners()) { if (listener instanceof CacheServerRestarter) restarters.add(listener); } try { for (Object restarter : restarters) cache.removeCacheListener(restarter); } catch (Exception ignored) { // ignored } } } protected static void restartCacheServer() { stopCacheServer(); startCacheServer(); } @Override public void testPartialLoadAndStore() { // do nothing } @Override public void testBuddyBackupStore() { // do nothing } protected void configureCache(CacheSPI cache) throws Exception { CacheLoaderConfig clc = new CacheLoaderConfig(); TcpDelegatingCacheLoaderConfig tcpCfg = new TcpDelegatingCacheLoaderConfig(TCP_CACHE_SERVER_HOST, TCP_CACHE_SERVER_PORT, TCP_CACHE_LOADER_TIMEOUT_MS); tcpCfg.setReconnectWaitTime(CACHE_SERVER_RESTART_DELAY_MS); tcpCfg.setFetchPersistentState(false); clc.addIndividualCacheLoaderConfig(tcpCfg); cache.getConfiguration().setCacheLoaderConfig(clc); } // 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 < START_COUNT : "Cache server should have restarted! old = " + oldStartCount + " and count = " + START_COUNT; 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 < 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) throws InterruptedException { if (e.isPre()) { if (restart) { restartCacheServer(); } else if (delayedRestart) { stopCacheServer(); } } else { if (delayedRestart && startAfter>0) { Thread.sleep(startAfter); startCacheServer(); } } } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/ConcurrentPutRemoveEvictTest.java0000644000175000017500000000652211131613416032324 0ustar moellermoellerpackage org.jboss.cache.loader; import org.jboss.cache.Cache; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.loader.testloaders.DummyInMemoryCacheLoader; import org.jboss.cache.factories.UnitTestConfigurationFactory; 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 AT jboss DOT org) * @since 2.2.0 */ @Test(groups = "functional", enabled = false, testName = "loader.ConcurrentPutRemoveEvictTest") // TODO: 2.2.0: Figure out why this occasionally hangs!! public class ConcurrentPutRemoveEvictTest { 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 = UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", DummyInMemoryCacheLoader.class.getName(), "", false, false, false, false, false); Configuration cfg = new Configuration(); cfg.setCacheLoaderConfig(cacheLoaderConfig); cache = new UnitTestCacheFactory().createCache(cfg, getClass()); cache.put(fqn, key, "value"); } @AfterTest public void tearDown() { TestingUtil.killCaches(cache); cache = null; } 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); } 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-3.2.8.GA/src/test/java/org/jboss/cache/loader/JDBCCacheLoaderHsqldbDSTest.java0000644000175000017500000001034311130373015031473 0ustar moellermoeller/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import org.hsqldb.jdbc.jdbcDataSource; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.transaction.DummyTransactionManager; import org.jboss.cache.util.UnitTestDatabaseManager; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertNull; import org.testng.annotations.AfterTest; import org.testng.annotations.Test; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NameNotFoundException; import java.util.Properties; /** * This test runs a JDBC cache loader, obtaining connections from a JDBC DataSource. *

* This test uses HSQL and an in-memory DB. */ @Test(groups = {"functional"}, testName = "loader.JDBCCacheLoaderHsqldbDSTest") public class JDBCCacheLoaderHsqldbDSTest extends CacheLoaderTestsBase { private final String FACTORY = "org.jboss.cache.transaction.DummyContextFactory"; private final String JNDI_NAME = "java:/TestDS"; private Properties prop; private jdbcDataSource ds; protected void configureCache(CacheSPI cache) throws Exception { if (ds == null) { 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 } prop = UnitTestDatabaseManager.getTestDbProperties(); ds = new jdbcDataSource(); ds.setDatabase(prop.getProperty("cache.jdbc.url")); ds.setUser("sa"); context.bind(JNDI_NAME, ds); assertNotNull(JNDI_NAME + " bound", context.lookup(JNDI_NAME)); } Properties p = new Properties(); p.setProperty("cache.jdbc.datasource", JNDI_NAME); p.setProperty("cache.jdbc.node.type", prop.getProperty("cache.jdbc.node.type")); p.setProperty("cache.jdbc.table.name", prop.getProperty("cache.jdbc.table.name")); p.setProperty("cache.jdbc.table.primarykey", prop.getProperty("cache.jdbc.table.primarykey")); JDBCCacheLoaderConfig jdbcConfig = new JDBCCacheLoaderConfig(); jdbcConfig.setFetchPersistentState(true); jdbcConfig.setProperties(p); CacheLoaderConfig config = new CacheLoaderConfig(); config.addIndividualCacheLoaderConfig(jdbcConfig); cache.getConfiguration().setCacheLoaderConfig(config); cache.create(); } @AfterTest protected void destroyDbAfterTest() throws Exception { Properties icProps = new Properties(); icProps.setProperty(Context.INITIAL_CONTEXT_FACTORY, FACTORY); Context ctx = new InitialContext(icProps); ctx.unbind(JNDI_NAME); UnitTestDatabaseManager.shutdownInMemoryDatabase(prop); } public void testLargeObject() throws Exception { 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)); } @Override public void testTransactionRollback() throws Exception { // no-op } @Override public void testIntegratedTransactionRollback() throws Exception { // no-op } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/JdbmCacheLoaderTest.java0000644000175000017500000000225111131613416030262 0ustar moellermoellerpackage org.jboss.cache.loader; import org.jboss.cache.CacheSPI; import org.jboss.cache.factories.UnitTestConfigurationFactory; 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 7422 2009-01-09 09:21:18Z mircea.markus $ */ @Test (groups = {"functional"}, testName = "loader.JdbmCacheLoaderTest") public class JdbmCacheLoaderTest extends CacheLoaderTestsBase { protected void configureCache(CacheSPI cache) throws Exception { String tmpCLLoc = TestingUtil.TEST_FILES + "/JdbmCacheLoaderTest"; cache.getConfiguration().setCacheLoaderConfig(UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", "org.jboss.cache.loader.jdbm.JdbmCacheLoader", "location=" + tmpCLLoc, false, true, false, false, false)); TestingUtil.recursiveFileRemove(tmpCLLoc); } public void testCacheLoaderThreadSafety() throws Exception { } public void testCacheLoaderThreadSafetyMultipleFqns() throws Exception { } public void testIgnoreModifications() throws Exception { } } ././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/DummyInMemoryCacheLoaderPessimisticTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/DummyInMemoryCacheLoaderPessimisticTes0000644000175000017500000000252711131613416033300 0ustar moellermoellerpackage org.jboss.cache.loader; import org.jboss.cache.CacheSPI; import org.jboss.cache.loader.testloaders.DummySharedInMemoryCacheLoader; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration.NodeLockingScheme; 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.testloaders.DummyInMemoryCacheLoader} as a cache * loader stub then we need to make sure it behaves as a valid cache loader. */ @Test(groups = {"functional"}, testName = "loader.DummyInMemoryCacheLoaderPessimisticTest") public class DummyInMemoryCacheLoaderPessimisticTest extends CacheLoaderTestsBase { protected void configureCache(CacheSPI cache) 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 = UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", DummySharedInMemoryCacheLoader.class.getName(), "debug=true \n bin=" + getClass().getName(), false, true, false, false, false); cache.getConfiguration().setCacheLoaderConfig(clc); cache.getConfiguration().setNodeLockingScheme(NodeLockingScheme.PESSIMISTIC); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/AddRemoveNodeDeadlockTest.java0000644000175000017500000001317611165104604031447 0ustar moellermoellerpackage org.jboss.cache.loader; import org.jboss.cache.AbstractSingleCacheTest; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.loader.testloaders.DummyInMemoryCacheLoader; import org.testng.annotations.Test; import javax.transaction.TransactionManager; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; 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; @Test(groups = "functional", testName = "loader.AddRemoveNodeDeadlockTest") public class AddRemoveNodeDeadlockTest extends AbstractSingleCacheTest { private static final Fqn FQN = Fqn.fromElements("a"); private CacheSPI cache; private TransactionManager tm; public CacheSPI createCache() throws Exception { UnitTestCacheFactory cf = new UnitTestCacheFactory(); cache = (CacheSPI) cf.createCache("configs/local-tx.xml", false, getClass()); cache.getConfiguration().setLockAcquisitionTimeout(4000); cache.getConfiguration().setEvictionConfig(null); // ! cache.getConfiguration().setLockParentForChildInsertRemove(true); // ! CacheLoaderConfig cacheLoaderConfig = UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", DummyInMemoryCacheLoader.class.getName(), "", false, true, false, false, false); cache.getConfiguration().setCacheLoaderConfig(cacheLoaderConfig); cache.start(); tm = cache.getTransactionManager(); return cache; } private void await(CountDownLatch latch, boolean expect, String message) throws InterruptedException, TimeoutException { assert latch.await(cache.getConfiguration().getLockAcquisitionTimeout() / 2, TimeUnit.MILLISECONDS) == expect : message; } public void testAdd() throws Exception { cache.put(FQN.getParent(), "x", "a"); doTest(true); } public void testRemove() throws Exception { cache.put(FQN, "x", "a"); doTest(false); } private void doTest(final boolean isAdd) throws Exception { ExecutorService exec = Executors.newFixedThreadPool(2); try { final CountDownLatch init = new CountDownLatch(1); final CountDownLatch parentLocked = new CountDownLatch(1); final CountDownLatch childLocked = new CountDownLatch(1); final CountDownLatch done = new CountDownLatch(1); /* * The procedure: * 1) t1 locks the parent node * 2) t2 tries to add/remove the child * Under normal circumstances t2 should block on parent before adding/removing the child. * If there's a deadlock condition, it will lock the child first and then proceed to lock the parent (and block). * 3) t1 adds/removes the child * Normally this should succeed as t1 already holds parent's lock. * Under deadlock condition it will be blocked by t2's lock on child. */ // t1 Future f1 = exec.submit(new Callable() { public Void call() throws Exception { tm.begin(); try { await(init, true, "init t1"); cache.put(FQN.getParent(), "x", "b"); parentLocked.countDown(); // this will time out because there's no one who can ping the latch, but that's expected await(childLocked, false, "child was locked by t2"); cache.put(FQN, "x", "b"); done.countDown(); tm.commit(); } finally { if (tm.getTransaction() != null) { tm.rollback(); } } return null; } }); // t2 Future f2 = exec.submit(new Callable() { public Void call() throws Exception { tm.begin(); try { init.countDown(); await(parentLocked, true, "t2 parent lock"); // uncomment following line to simulate proper locking order // cache.put(FQN.getParent(), "x", "a"); if (isAdd) { // the deadlock exception would be here: cache.put(FQN, "x", "a"); } else { cache.removeNode(FQN); } childLocked.countDown(); await(done, true, "t2 done"); tm.commit(); } finally { if (tm.getTransaction() != null) { tm.rollback(); } } return null; } }); f1.get(cache.getConfiguration().getLockAcquisitionTimeout() * 3, TimeUnit.MILLISECONDS); f2.get(cache.getConfiguration().getLockAcquisitionTimeout() * 3, TimeUnit.MILLISECONDS); } finally { exec.shutdown(); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/CacheLoaderPurgingTest.java0000644000175000017500000001240311131613416031021 0ustar moellermoeller/* * 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.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.loader.testloaders.DummySharedInMemoryCacheLoader; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.parsing.XmlConfigHelper; import org.jboss.cache.config.parsing.element.LoadersElementParser; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; /** * @author Manik Surtani (manik AT jboss DOT org) */ @Test(groups = {"functional"}, sequential = true, testName = "loader.CacheLoaderPurgingTest") public class CacheLoaderPurgingTest { 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); TestingUtil.killCaches(cache); cache = null; } } public void testSingleLoaderNoPurge() throws Exception { cache = (CacheSPI) new UnitTestCacheFactory().createCache(false, getClass()); Configuration c = cache.getConfiguration(); String s = "bin=" + getClass().getName(); c.setCacheLoaderConfig(UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", DummySharedInMemoryCacheLoader.class.getName(), s, false, false, 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 UnitTestCacheFactory().createCache(false, getClass()); Configuration c = cache.getConfiguration(); String s = "bin=" + getClass().getName(); c.setCacheLoaderConfig(UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", DummySharedInMemoryCacheLoader.class.getName(), s, false, false, false, true, 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(); assertTrue(cache.getCacheLoaderManager().getCacheLoaderConfig().getFirstCacheLoaderConfig().isPurgeOnStartup()); assertNull(cache.getNode(fqn)); assertNull(loader.get(fqn)); } public void testTwoLoadersPurge() throws Exception { cache = (CacheSPI) new UnitTestCacheFactory().createCache(false, getClass()); String xml = " \n" + " \n" + " bin=" + getClass() + "bin1\n" + " " + " \n" + " bin=" + getClass()+ "bin2\n" + " " + " "; LoadersElementParser parser = new LoadersElementParser(); CacheLoaderConfig cacheLoaderConfig = parser.parseLoadersElement(XmlConfigHelper.stringToElementInCoreNS(xml)); Configuration c = cache.getConfiguration(); c.setCacheLoaderConfig(cacheLoaderConfig); 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)); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/C3p0JDBCCacheLoaderTest.java0000644000175000017500000000227411131613416030543 0ustar moellermoeller/* * 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.factories.UnitTestConfigurationFactory; import org.testng.annotations.Test; import org.testng.annotations.BeforeTest; /** * Unit test that runs the the tests defined JDBCCacheLoaderTest using a standalone * connection pool factory based on c3p0 library. * * @author Galder Zamarreno */ @Test(groups = "functional", testName = "loader.C3p0JDBCCacheLoaderTest") public class C3p0JDBCCacheLoaderTest extends JDBCCacheLoaderTest { private static final String CF_CLASS = "org.jboss.cache.loader.C3p0ConnectionFactory"; @BeforeTest public void createDatabase() { super.createDatabase(); props.put("cache.jdbc.connection.factory", CF_CLASS); } protected void configureCache(CacheSPI cache) throws Exception { cache.getConfiguration().setCacheLoaderConfig(UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", "org.jboss.cache.loader.JDBCCacheLoader", props, false, true, false, false, false)); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/deadlock/0000755000175000017500000000000011675221541025405 5ustar moellermoeller././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/deadlock/ConcurrentCreationDeadlockTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/deadlock/ConcurrentCreationDeadlockTes0000644000175000017500000003013411131613416033234 0ustar moellermoellerpackage org.jboss.cache.loader.deadlock; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.loader.testloaders.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; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.util.TestingUtil; /** * 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 = false, testName = "loader.deadlock.ConcurrentCreationDeadlockTest") // Disabling since this has issues with ReadWriteWithUpgradeLock. See JBCACHE-461 public class ConcurrentCreationDeadlockTest { /** * 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 = UnitTestConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache = (CacheSPI) new UnitTestCacheFactory().createCache(c, false, getClass()); } /** * {@inheritDoc} */ @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { // We just can't destroy DummyTransactionManager. We are sharing single instance in more tests. TestingUtil.killTransaction(DummyTransactionManager.getInstance()); TestingUtil.killCaches(cache); 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(UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", cacheLoaderClass, "", false, false, 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"); } /** * A worker thread that applies the concurrent modifications. * * @author Marian Nikolov * @author $Author: mircea.markus $ * @version $Date: 2008-11-06 19:07:10 -0600 (Thu, 06 Nov 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; 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; } } catch (Exception 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 return true; } m_state++; return false; } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/ClusteredCacheLoaderTest.java0000644000175000017500000002771111176306660031361 0ustar moellermoeller/* * 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.*; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.lock.TimeoutException; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.*; 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 AT jboss DOT org) */ @Test(groups = {"functional"}, sequential = true, testName = "loader.ClusteredCacheLoaderTest") public class ClusteredCacheLoaderTest extends AbstractMultipleCachesTest { 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; protected void createCaches() throws Throwable { Configuration c1 = new Configuration(); Configuration c2 = new Configuration(); c1.setStateRetrievalTimeout(2000); c2.setStateRetrievalTimeout(2000); c1.setCacheMode(Configuration.CacheMode.REPL_SYNC); c2.setCacheMode(Configuration.CacheMode.REPL_SYNC); c1.setCacheLoaderConfig(UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", "org.jboss.cache.loader.ClusteredCacheLoader", "timeout=5000", false, false, false, false, false)); c2.setCacheLoaderConfig(UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", "org.jboss.cache.loader.ClusteredCacheLoader", "timeout=5000", false, false, false, false, false)); c1.setUseRegionBasedMarshalling(useRegionBasedMarshalling); c2.setUseRegionBasedMarshalling(useRegionBasedMarshalling); cache1 = (CacheSPI) new UnitTestCacheFactory().createCache(c1, false, getClass()); cache2 = (CacheSPI) new UnitTestCacheFactory().createCache(c2, false, getClass()); cache1.getConfiguration().setSerializationExecutorPoolSize(0); cache2.getConfiguration().setSerializationExecutorPoolSize(0); 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.getConfiguration().setNodeLockingScheme(NodeLockingScheme.PESSIMISTIC); cache2.getConfiguration().setNodeLockingScheme(NodeLockingScheme.PESSIMISTIC); cache1.start(); cache2.start(); loader1 = cache1.getCacheLoaderManager().getCacheLoader(); loader2 = cache2.getCacheLoaderManager().getCacheLoader(); registerCaches(cache1, cache2); } 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()); // cache1.evict(child1); // cache1.evict(child2); // cache1.evict(child3); cache1.evict(fqn, true); // cache2.evict(child1); // cache2.evict(child2); // cache2.evict(child3); // cache2.evict(fqn, true); childNames = loader2.getChildrenNames(fqn); assertNull("should be null", childNames); // calling a get on cache1 should cause the loader to retrieve the node from cache2 cache1.evict(fqn, true); 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)); } //todo - mmarkus - extract these as testng.xml configs withe different values for different environements (for this test e.g. number of loops) public void testCacheLoaderThreadSafety() throws Throwable { 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 = 25; // was 300 final Set exceptions = new CopyOnWriteArraySet(); Thread evictor = new Thread("Evictor") { public void run() { try { latch.await(); for (int i = 0; i < loops; 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++) { 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++) { 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++) { 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++) { 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-3.2.8.GA/src/test/java/org/jboss/cache/loader/CacheLoaderMethodCallCounterTest.java0000644000175000017500000000521611131613416032766 0ustar moellermoeller/* * 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.UnitTestCacheFactory; import org.jboss.cache.loader.testloaders.DummyCountingCacheLoader; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.config.Configuration.NodeLockingScheme; 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 AT jboss DOT org) */ @Test(groups = "functional", sequential = true, testName = "loader.CacheLoaderMethodCallCounterTest") public class CacheLoaderMethodCallCounterTest { private CacheSPI cache; private DummyCountingCacheLoader dummyLoader; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { if (cache != null) tearDown(); cache = (CacheSPI) new UnitTestCacheFactory().createCache(false, getClass()); cache.getConfiguration().setNodeLockingScheme(NodeLockingScheme.PESSIMISTIC); cache.getConfiguration().setCacheLoaderConfig(UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", DummyCountingCacheLoader.class.getName(), "", false, false, false, false, false)); cache.start(); dummyLoader = (DummyCountingCacheLoader) cache.getCacheLoaderManager().getCacheLoader(); } @AfterMethod(alwaysRun = true) public void tearDown() { TestingUtil.killCaches(cache); cache = null; } public void testPut() { cache.put("/node", "key", "value"); } public void testGet() { cache.get("/node", "key"); } public void testRemove() { cache.remove("/node", "key"); } 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-3.2.8.GA/src/test/java/org/jboss/cache/loader/BdbjeTest.java0000644000175000017500000007424011121730272026347 0ustar moellermoellerpackage 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.Fqn; import org.jboss.cache.Modification; import org.jboss.cache.loader.bdbje.BdbjeCacheLoaderConfig; import org.jboss.cache.statetransfer.DefaultStateTransferManager; 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; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.util.TestingUtil; /** * 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: 7332 $ */ @Test(groups = {"functional"}, testName = "loader.BdbjeTest") 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 { TestingUtil.killCaches(cache); } 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 UnitTestCacheFactory().createCache(false, getClass()); 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); long start = System.currentTimeMillis(); 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(DefaultStateTransferManager.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(DefaultStateTransferManager.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(DefaultStateTransferManager.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(DefaultStateTransferManager.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(DefaultStateTransferManager.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(DefaultStateTransferManager.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-3.2.8.GA/src/test/java/org/jboss/cache/loader/LocalDelegatingCacheLoaderTest.java0000644000175000017500000000336611121730272032433 0ustar moellermoellerpackage org.jboss.cache.loader; import org.jboss.cache.CacheSPI; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.testng.annotations.Test; @Test(groups = {"functional"}, sequential = true, testName = "loader.LocalDelegatingCacheLoaderTest") public class LocalDelegatingCacheLoaderTest extends CacheLoaderTestsBase { CacheSPI delegatingCache; protected void configureCache(CacheSPI cache) throws Exception { if (delegatingCache == null) { delegatingCache = (CacheSPI) new UnitTestCacheFactory().createCache(false, getClass()); delegatingCache.getConfiguration().setCacheMode(Configuration.CacheMode.LOCAL); delegatingCache.create(); delegatingCache.start(); } LocalDelegatingCacheLoaderConfig cfg = new LocalDelegatingCacheLoaderConfig(); cfg.setDelegate(delegatingCache); cfg.setAsync(false); cfg.setFetchPersistentState(false); CacheLoaderConfig cacheLoaderConfig = new CacheLoaderConfig(); cacheLoaderConfig.addIndividualCacheLoaderConfig(cfg); cache.getConfiguration().setCacheLoaderConfig(cacheLoaderConfig); } @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-3.2.8.GA/src/test/java/org/jboss/cache/loader/TxCacheLoaderTest.java0000644000175000017500000001372711131613416030013 0ustar moellermoellerpackage org.jboss.cache.loader; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.transaction.TransactionSetup; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; 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; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.AbstractMultipleCachesTest; import org.jboss.cache.loader.testloaders.DummyInMemoryCacheLoader; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.config.Configuration; /** * @author Bela Ban * @version $Id: TxCacheLoaderTest.java 7422 2009-01-09 09:21:18Z mircea.markus $ */ @Test(groups = {"functional", "transaction"}, sequential = true, testName = "loader.TxCacheLoaderTest") public class TxCacheLoaderTest extends AbstractMultipleCachesTest { CacheSPI cache1, cache2; private Fqn fqn = Fqn.fromString("/one/two/three"); protected void createCaches() throws Throwable { Configuration c1 = new Configuration(); c1.setCacheMode("repl_sync"); c1.setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); c1.setNodeLockingScheme(NodeLockingScheme.PESSIMISTIC); c1.setCacheLoaderConfig(UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", DummyInMemoryCacheLoader.class.getName(), "", false, false, false, false, false)); cache1 = (CacheSPI) new UnitTestCacheFactory().createCache(c1, false, getClass()); cache1.create(); cache1.start(); Configuration c2 = new Configuration(); c2.setCacheMode("repl_sync"); c2.setNodeLockingScheme(NodeLockingScheme.PESSIMISTIC); c2.setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); c2.setCacheLoaderConfig(UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", DummyInMemoryCacheLoader.class.getName(), "", false, false, false, false, false)); c2.setLockAcquisitionTimeout(2000); // cache2.setReplQueueInterval(3000); cache2 = (CacheSPI) new UnitTestCacheFactory().createCache(c2, false, getClass()); cache2.create(); cache2.start(); registerCaches(cache1, cache2); } @AfterMethod public void tearDown() throws Exception { // clean up cache loaders!! cache1.removeNode(Fqn.ROOT); } 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) { 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-3.2.8.GA/src/test/java/org/jboss/cache/loader/FileCacheLoaderTest.java0000644000175000017500000000760011131613416030270 0ustar moellermoellerpackage org.jboss.cache.loader; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.Test; import org.testng.annotations.BeforeTest; import org.testng.annotations.AfterTest; @Test(groups = {"functional"}, testName = "loader.FileCacheLoaderTest") public class FileCacheLoaderTest extends CacheLoaderTestsBase { private String tmpCLLoc = TestingUtil.TEST_FILES + getClass().getName(); @BeforeTest @AfterTest public void removeCacheLoaderFolder() { TestingUtil.recursiveFileRemove(tmpCLLoc); } protected void configureCache(CacheSPI cache) throws Exception { cache.getConfiguration().setCacheLoaderConfig(UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", "org.jboss.cache.loader.FileCacheLoader", "location=" + tmpCLLoc, false, true, false, false, false)); } 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/ 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-3.2.8.GA/src/test/java/org/jboss/cache/loader/JDBCCacheLoaderTest.java0000644000175000017500000001006211131613416030107 0ustar moellermoeller/* * 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.factories.UnitTestConfigurationFactory; import org.jboss.cache.util.UnitTestDatabaseManager; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.assertTrue; import static org.testng.AssertJUnit.fail; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeTest; 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: 7422 $ */ @Test(groups = {"functional"}, testName = "loader.JDBCCacheLoaderTest") public class JDBCCacheLoaderTest extends CacheLoaderTestsBase { protected Properties props; @BeforeTest public void createDatabase() { props = UnitTestDatabaseManager.getTestDbProperties(); } @AfterTest public void shutDownDatabase() { UnitTestDatabaseManager.shutdownInMemoryDatabase(props); } @BeforeMethod public void clearDatabase() throws Exception { loader.remove(Fqn.ROOT); } protected void configureCache(CacheSPI cache) throws Exception { String props = props2String(this.props); cache.getConfiguration().setCacheLoaderConfig(UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", JDBCCacheLoader.class.getName(), props, false, true, false, false, false)); } private String props2String(Properties prop) { StringBuilder p = new StringBuilder(); append("cache.jdbc.driver", prop, p); append("cache.jdbc.url", prop, p); append("cache.jdbc.user", prop, p); append("cache.jdbc.password", prop, p); append("cache.jdbc.node.type", prop, p); append("cache.jdbc.table.name", prop, p); append("cache.jdbc.table.primarykey", prop, p); return p.toString(); } private void append(String propertyName, Properties prop, StringBuilder builder) { if (prop.containsKey(propertyName)) { builder.append(propertyName).append("=").append(prop.getProperty(propertyName)).append("\n"); } } 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 testRootIsCreated() throws Exception { loader.put(Fqn.fromString("/a/b/c"), "a", "b"); assertTrue(loader.exists(Fqn.ROOT)); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/ChainingCacheLoaderFullTest.java0000644000175000017500000002327711131613416031764 0ustar moellermoeller/* * 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.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.loader.testloaders.DummyInMemoryCacheLoader; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNull; import org.testng.annotations.Test; import javax.transaction.TransactionManager; /** * Tests ignoreModifications and tests contents of individual loaders * * @author Manik Surtani (manik AT jboss DOT org) */ @Test(groups = {"functional"}, sequential = true, testName = "loader.ChainingCacheLoaderFullTest") 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 UnitTestCacheFactory().createCache(false, getClass()); 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); TestingUtil.killCaches(cache); cache = null; } protected CacheLoaderConfig getCacheLoaderConfig(boolean ignoreMods1, boolean ignoreMods2) throws Exception { CacheLoaderConfig clc = UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, null, DummyInMemoryCacheLoader.class.getName(), "", false, true, false, false, ignoreMods1); CacheLoaderConfig.IndividualCacheLoaderConfig ic = UnitTestConfigurationFactory.buildIndividualCacheLoaderConfig(null, DummyInMemoryCacheLoader.class.getName(), "", false, false, false, ignoreMods2); clc.addIndividualCacheLoaderConfig(ic); return clc; } 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-3.2.8.GA/src/test/java/org/jboss/cache/loader/FileCacheLoaderConfigTest.java0000644000175000017500000000277511111047320031417 0ustar moellermoeller/* * 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"}, testName = "loader.FileCacheLoaderConfigTest") 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-3.2.8.GA/src/test/java/org/jboss/cache/loader/BdbjeCacheLoaderTest.java0000644000175000017500000000357511131613416030426 0ustar moellermoellerpackage org.jboss.cache.loader; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.Test; import org.testng.annotations.BeforeMethod; /** * Runs the same tests as {@link FileCacheLoaderTest}, but with Berkeley DB instead of a file-based CacheLoader * * @author Bela Ban * @version $Id: BdbjeCacheLoaderTest.java 7422 2009-01-09 09:21:18Z mircea.markus $ */ @Test(groups = "functional", testName = "loader.BdbjeCacheLoaderTest") public class BdbjeCacheLoaderTest extends CacheLoaderTestsBase { @Override protected void configureCache(CacheSPI cache) throws Exception { String tmpDir = TestingUtil.TEST_FILES; String tmpCLLoc = tmpDir + "/JBossCache-BdbjeCacheLoaderTest"; cache.getConfiguration().setCacheLoaderConfig(UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", "org.jboss.cache.loader.bdbje.BdbjeCacheLoader", "location=" + tmpCLLoc, false, true, false, false, false)); TestingUtil.recursiveFileRemove(tmpCLLoc); } @BeforeMethod public void clearDatabase() throws Exception { loader.remove(Fqn.ROOT); } 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-3.2.8.GA/src/test/java/org/jboss/cache/loader/CacheLoaderManagerTest.java0000644000175000017500000005422111131613416030764 0ustar moellermoeller/* * 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.config.parsing.XmlConfigHelper; import org.jboss.cache.config.parsing.element.LoadersElementParser; import org.jboss.cache.factories.UnitTestConfigurationFactory; 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 AT jboss DOT org) * @author Galder Zamarreno */ @Test(groups = "functional", sequential = true, testName = "loader.CacheLoaderManagerTest") public class CacheLoaderManagerTest { private LoadersElementParser loadersElementParser = new LoadersElementParser(); 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 XmlConfigHelper.stringToElementInCoreNS(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 = UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", "org.jboss.cache.loader.FileCacheLoader", "location=" + getTempDir(), false, false, false, false, false); CacheLoaderManager mgr = new CacheLoaderManager(); mgr.setConfig(clc, null, null); CacheLoader cl = mgr.getCacheLoader(); assertEquals(FileCacheLoader.class, cl.getClass()); // with async clc = UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", "org.jboss.cache.loader.FileCacheLoader", "location=" + getTempDir(), true, false, false, false, false); mgr.setConfig(clc, null, null); cl = mgr.getCacheLoader(); assertEquals(AsyncCacheLoader.class, cl.getClass()); } public void testSingleCacheLoaderPassivationFromXml() throws Exception { CacheLoaderConfig clc = UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(true, "", "org.jboss.cache.loader.FileCacheLoader", "location=" + getTempDir(), false, false, false, false, false); CacheLoaderConfig bdbjeCl = UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(true, "", "org.jboss.cache.loader.bdbje.BdbjeCacheLoader", "location=" + getTempDir(), false, false, false, false, false); CacheLoaderConfig jdbCl = UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(true, "", "org.jboss.cache.loader.JDBCCacheLoader", "location=" + getTempDir(), false, false, false, false, false); clc.getIndividualCacheLoaderConfigs().add(bdbjeCl.getFirstCacheLoaderConfig()); clc.getIndividualCacheLoaderConfigs().add(jdbCl.getFirstCacheLoaderConfig()); CacheLoaderManager mgr = new CacheLoaderManager(); mgr.setConfig(clc, null, null); CacheLoader cl = mgr.getCacheLoader(); assertEquals(FileCacheLoader.class, cl.getClass()); clc = UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(true, "", "org.jboss.cache.loader.FileCacheLoader", "location=" + getTempDir(), true, false, false, false, false); bdbjeCl = UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(true, "", "org.jboss.cache.loader.bdbje.BdbjeCacheLoader", "location=" + getTempDir(), true, false, false, false, false); jdbCl = UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(true, "", "org.jboss.cache.loader.JDBCCacheLoader", "location=" + getTempDir(), true, false, false, false, false); clc.getIndividualCacheLoaderConfigs().add(bdbjeCl.getFirstCacheLoaderConfig()); clc.getIndividualCacheLoaderConfigs().add(jdbCl.getFirstCacheLoaderConfig()); 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 { CacheLoaderConfig clc = UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", "org.jboss.cache.loader.FileCacheLoader", "a=b", false, false, false, false, false); clc.addIndividualCacheLoaderConfig(UnitTestConfigurationFactory.buildIndividualCacheLoaderConfig("", "org.jboss.cache.loader.JDBCCacheLoader", "cache.jdbc.driver=com.mysql.jdbc.Driver\ncache.jdbc.url=jdbc:mysql://localhost/test\ncache.jdbc.user=user\ncache.jdbc.password=pwd", false, false, false, false)); 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 clc = UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", "org.jboss.cache.loader.FileCacheLoader", "a=b", false, false, false, false, false); clc.addIndividualCacheLoaderConfig(UnitTestConfigurationFactory.buildIndividualCacheLoaderConfig("", "org.jboss.cache.loader.JDBCCacheLoader", "cache.jdbc.driver=com.mysql.jdbc.Driver\ncache.jdbc.url=jdbc:mysql://localhost/test\ncache.jdbc.user=user\ncache.jdbc.password=pwd", true, false, false, false)); mgr = new CacheLoaderManager(); 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 testSingletonConfiguration() throws Exception { /************************************************************************************************************/ String conf; CacheLoaderConfig clc; CacheLoaderManager mgr; CacheLoaderConfig.IndividualCacheLoaderConfig iclc; conf = "\n" + " \n" + " " + " \n" + " " + ""; clc = loadersElementParser.parseLoadersElement(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 = "\n" + " \n" + " " + " \n" + " " + ""; clc = loadersElementParser.parseLoadersElement(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 = " \n" + " \n" + " \n" + " \n" + " \n" + " pushStateWhenCoordinator=false\n" + " \n" + " \n" + " \n" + " "; clc = loadersElementParser.parseLoadersElement(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 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 = " \n" + " \n" + " \n" + " \n" + " \n" + " pushStateWhenCoordinator = true\n" + " pushStateWhenCoordinatorTimeout = 5000\n" + " \n" + " \n" + " \n" + " "; clc = loadersElementParser.parseLoadersElement(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 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 = " \n" + " \n" + " \n" + " \n" + " \n" + " pushStateWhenCoordinator = true\n" + " pushStateWhenCoordinatorTimeout = 5000\n" + " \n" + " \n" + " \n" + " "; clc = loadersElementParser.parseLoadersElement(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 = " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " "; clc = loadersElementParser.parseLoadersElement(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 */ } } public static class MockSingletonStoreCacheLoader extends AbstractDelegatingCacheLoader { public MockSingletonStoreCacheLoader() { super(null); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/PreloadTest.java0000644000175000017500000000673111131613416026730 0ustar moellermoellerpackage org.jboss.cache.loader; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.loader.testloaders.DummySharedInMemoryCacheLoader; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; @Test(groups = "functional", sequential = true, testName = "loader.PreloadTest") public class PreloadTest { CacheSPI cache; Fqn fqn = Fqn.fromString("/a/b/c"); Object key = "key", value = "value"; @AfterMethod public void tearDown() { if (cache != null) TestingUtil.killCaches(cache); cache = null; } public void testPreload() throws Exception { Configuration c = UnitTestConfigurationFactory.createConfiguration(CacheMode.LOCAL); String props = "bin=" + getClass().getName(); c.setCacheLoaderConfig(UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "/a", DummySharedInMemoryCacheLoader.class.getName(), props, false, false, false, false, false)); cache = (CacheSPI) new UnitTestCacheFactory().createCache(c.clone(), getClass()); cache.put(fqn, key, value); assertExists(); cache.destroy(); cache = (CacheSPI) new UnitTestCacheFactory().createCache(c.clone(), getClass()); assertExists(); } public void testPreloadMultiRegions() throws Exception { Configuration c = UnitTestConfigurationFactory.createConfiguration(CacheMode.LOCAL); String props = "bin=" + getClass().getName(); c.setCacheLoaderConfig(UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "/a", DummySharedInMemoryCacheLoader.class.getName(), props, false, false, false, false, false)); cache = (CacheSPI) new UnitTestCacheFactory().createCache(c.clone(), getClass()); cache.put(fqn, key, value); assertExists(); cache.destroy(); c.setCacheLoaderConfig(UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "/c,/a,/b", DummySharedInMemoryCacheLoader.class.getName(), props, false, false, false, false, false)); cache = (CacheSPI) new UnitTestCacheFactory().createCache(c.clone(), getClass()); assertExists(); c.setCacheLoaderConfig(UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "/c, /a, /b", DummySharedInMemoryCacheLoader.class.getName(), props, false, false, false, false, false)); cache = (CacheSPI) new UnitTestCacheFactory().createCache(c.clone(), getClass()); assertExists(); c.setCacheLoaderConfig(UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, " /c, /a, /b", DummySharedInMemoryCacheLoader.class.getName(), props, false, false, false, false, false)); cache = (CacheSPI) new UnitTestCacheFactory().createCache(c.clone(), getClass()); 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-3.2.8.GA/src/test/java/org/jboss/cache/loader/DummyInMemoryCacheLoaderTest.java0000644000175000017500000000350411131613416032163 0ustar moellermoellerpackage org.jboss.cache.loader; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.loader.testloaders.DummySharedInMemoryCacheLoader; import org.jboss.cache.factories.UnitTestConfigurationFactory; 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.testloaders.DummyInMemoryCacheLoader} as a cache * loader stub then we need to make sure it behaves as a valid cache loader. */ @Test(groups = {"functional"}, testName = "loader.DummyInMemoryCacheLoaderTest") public class DummyInMemoryCacheLoaderTest extends CacheLoaderTestsBase { DummySharedInMemoryCacheLoader dimCl; protected void configureCache(CacheSPI cache) 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. String bin = "DummyInMemoryCacheLoader-" + Thread.currentThread().getName(); CacheLoaderConfig clc = UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", DummySharedInMemoryCacheLoader.class.getName(), "bin=" + bin, false, true, false, false, false); cache.getConfiguration().setCacheLoaderConfig(clc); } protected void postConfigure() { CacheLoader loader = cache.getCacheLoaderManager().getCacheLoader(); if (loader instanceof AbstractDelegatingCacheLoader) { dimCl = (DummySharedInMemoryCacheLoader) ((ReadOnlyDelegatingCacheLoader)loader).getCacheLoader(); } else { dimCl = (DummySharedInMemoryCacheLoader) loader; } } @Override protected void cleanup() throws Exception { if (dimCl != null) { dimCl.remove(Fqn.ROOT); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/AsyncFileCacheLoaderTest.java0000644000175000017500000001475011141570045031273 0ustar moellermoellerpackage org.jboss.cache.loader; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Modification; import org.jboss.cache.config.Configuration; import org.jboss.cache.statetransfer.DefaultStateTransferManager; 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 org.testng.annotations.Test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.Collections; import java.util.HashMap; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.util.TestingUtil; @Test(groups = "functional", testName = "loader.AsyncFileCacheLoaderTest") public class AsyncFileCacheLoaderTest { private CacheSPI cache; protected void configureCache() throws Exception { configureCache("", true); } protected void configureCache(String props, boolean async) throws Exception { cache = (CacheSPI) new UnitTestCacheFactory().createCache(false, getClass()); cache.getConfiguration().setCacheMode(Configuration.CacheMode.LOCAL); cache.getConfiguration().setCacheLoaderConfig(UnitTestConfigurationFactory .buildSingleCacheLoaderConfig(false, "", "org.jboss.cache.loader.FileCacheLoader", props, async, false, true, false, false)); cache.create(); cache.start(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { if (cache != null) { TestingUtil.killCaches(cache); cache = null; } } 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" + "", true); 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, map); loader.put(fqn, "a", "b"); 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" + "", true); 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"); } //this happens async, so expect this will happen at a further point in time long start = System.currentTimeMillis(); while (System.currentTimeMillis() - start < 50000) { if (loader.get(fqn) != null && loader.get(fqn).size() == 50) break; Thread.sleep(100); } 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" + "", true); CacheLoader loader = cache.getCacheLoaderManager().getCacheLoader(); 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("", false); Fqn X = Fqn.fromString("/x"); CacheLoader loader = cache.getCacheLoaderManager().getCacheLoader(); loader.remove(X); cache.put(X, "key1", "value1"); ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); MarshalledValueOutputStream os = new MarshalledValueOutputStream(baos); loader.loadEntireState(os); cache.getMarshaller().objectToObjectStream(DefaultStateTransferManager.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); } public void testMultipleThreads() throws Exception { configureCache( "cache.async.queueSize=1\n" + "cache.async.pollWait=10\n" + "cache.async.threadPoolSize=5", true); 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"); } //this happens async, so expect this will happen at a further point in time long start = System.currentTimeMillis(); while (System.currentTimeMillis() - start < 50000) { if (loader.get(fqn) != null && loader.get(fqn).size() == 50) break; Thread.sleep(100); } assertEquals(50, loader.get(fqn).size()); loader.remove(fqn); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/DataOverwriteMvccTest.java0000644000175000017500000000053111251246471030731 0ustar moellermoellerpackage org.jboss.cache.loader; import org.testng.annotations.Test; import org.jboss.cache.config.Configuration; @Test(groups = "functional", testName = "loader.DataOverwriteMvccTest") public class DataOverwriteMvccTest extends DataOverwriteTest { public DataOverwriteMvccTest() { nls = Configuration.NodeLockingScheme.MVCC; } }jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/SingletonStoreCacheLoaderTest.java0000644000175000017500000004344211325553351032402 0ustar moellermoeller/* * 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.Fqn; import org.jboss.cache.NodeSPI; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.UnitTestConfigurationFactory; 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.Map; import java.util.Set; 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; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.loader.testloaders.DummyInMemoryCacheLoader; import org.jboss.cache.util.TestingUtil; /** * Unit test class for SingletonStoreCacheLoader * * @author Galder Zamarreno */ @Test(groups = "functional", sequential = true, testName = "loader.SingletonStoreCacheLoaderTest") public class SingletonStoreCacheLoaderTest { 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 UnitTestCacheFactory().createCache(UnitTestConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), false, getClass()); cache2 = (CacheSPI) new UnitTestCacheFactory().createCache(UnitTestConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), false, getClass()); cache3 = (CacheSPI) new UnitTestCacheFactory().createCache(UnitTestConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), false, getClass()); } 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"))); stopCache1(true); 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"))); stopCache2(true); 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); initSingletonWithWaitingPushCache(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")); assertTrue(cl2.exists(fqn("/a"))); assertTrue(cl2.exists(fqn("/a"))); assertTrue(cl2.exists(fqn("/a/b"))); assertTrue(cl2.exists(fqn("/a/b"))); assertTrue(cl2.exists(fqn("/a/b/c"))); assertTrue(cl2.exists(fqn("/a/b/d"))); assertTrue(cl2.exists(fqn("/e"))); assertTrue(cl2.exists(fqn("/e/f/g"))); WaitForPushSingletonStoreCacheLoader scl2 = (WaitForPushSingletonStoreCacheLoader) cache2.getCacheLoaderManager().getCacheLoader(); CountDownLatch startPushLatch = new CountDownLatch(1); scl2.setStartPushLatch(startPushLatch); ViewChangeListener viewChangeListener = new ViewChangeListener(cache2); stopCache1(false); cache2.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache2.getInvocationContext().getOptionOverrides().setSuppressPersistence(true); cache2.put(fqn("/e/i"), "i-key", "i-value"); startPushLatch.countDown(); viewChangeListener.waitForViewChange(60, TimeUnit.SECONDS); waitForPushStateCompletion(scl2.getPushStateFuture()); assertTrue(cl2.exists(fqn("/a"))); assertTrue(cl2.exists(fqn("/a"))); assertTrue(cl2.exists(fqn("/a/b"))); assertTrue(cl2.exists(fqn("/a/b"))); assertTrue(cl2.exists(fqn("/a/b/c"))); assertTrue(cl2.exists(fqn("/a/b/d"))); assertTrue(cl2.exists(fqn("/e"))); assertTrue(cl2.exists(fqn("/e/f/g"))); assertTrue(cl2.exists(fqn("/e/i"))); 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")); assertTrue(cl2.get(fqn("/e/i")).containsKey("i-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")); assertTrue(cl3.exists(fqn("/a"))); assertTrue(cl3.exists(fqn("/a"))); assertTrue(cl3.exists(fqn("/a/b"))); assertTrue(cl3.exists(fqn("/a/b"))); assertTrue(cl3.exists(fqn("/a/b/c"))); assertTrue(cl3.exists(fqn("/a/b/d"))); assertTrue(cl3.exists(fqn("/e"))); assertTrue(cl3.exists(fqn("/e/f/g"))); assertTrue(cl3.exists(fqn("/e/f/h"))); assertTrue(cl3.exists(fqn("/i"))); viewChangeListener = new ViewChangeListener(cache3); stopCache2(false); 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(true); } 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, boolean isPush) throws Exception { CacheLoaderConfig clc = UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, null, cacheloaderClass, "", false, false, false, false, false); CacheLoaderConfig.IndividualCacheLoaderConfig.SingletonStoreConfig sc = new CacheLoaderConfig.IndividualCacheLoaderConfig.SingletonStoreConfig(); sc.setSingletonStoreEnabled(true); sc.setProperties("pushStateWhenCoordinator = " + isPush + "\n pushStateWhenCoordinatorTimeout = 50000\n"); clc.getFirstCacheLoaderConfig().setSingletonStoreConfig(sc); return clc; } protected CacheLoaderConfig getSingletonStoreCacheLoaderConfig(String cacheloaderClass, String singletonStoreClass) throws Exception { CacheLoaderConfig clc = UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, null, cacheloaderClass, "", false, false, false, false, false); CacheLoaderConfig.IndividualCacheLoaderConfig.SingletonStoreConfig sc = new CacheLoaderConfig.IndividualCacheLoaderConfig.SingletonStoreConfig(); sc.setSingletonStoreEnabled(true); sc.setSingletonStoreClass(singletonStoreClass); sc.setProperties("pushStateWhenCoordinator = true\n pushStateWhenCoordinatorTimeout = 50000\n"); clc.getFirstCacheLoaderConfig().setSingletonStoreConfig(sc); return clc; } private void initSingletonNonPushCache(CacheSPI cache) throws Exception { cache.getConfiguration().setCacheLoaderConfig(getSingletonStoreCacheLoaderConfig( SingletonDummyInMemoryCacheLoader.class.getName(), false)); } private void initSingletonWithPushCache(CacheSPI cache) throws Exception { cache.getConfiguration().setCacheLoaderConfig(getSingletonStoreCacheLoaderConfig( SingletonDummyInMemoryCacheLoader.class.getName(), true)); } private void initSingletonWithWaitingPushCache(CacheSPI cache) throws Exception { cache.getConfiguration().setCacheLoaderConfig(getSingletonStoreCacheLoaderConfig( SingletonDummyInMemoryCacheLoader.class.getName(), WaitForPushSingletonStoreCacheLoader.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(boolean clearCacheLoader) { if (cache1 != null) { TestingUtil.killCaches(clearCacheLoader, cache1); } cache1 = null; } private void stopCache2(boolean clearCacheLoader) { if (cache2 != null) { TestingUtil.killCaches(clearCacheLoader, cache2); } cache2 = null; } private void stopCache3(boolean clearCacheLoader) { if (cache3 != null) { TestingUtil.killCaches(clearCacheLoader, cache3); } cache3 = null; } @AfterMethod(alwaysRun = true) public void tearDown() { stopCache1(true); stopCache2(true); stopCache3(true); } 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; } } static class SingletonDummyInMemoryCacheLoader extends AbstractCacheLoader { static final DummyInMemoryCacheLoader singleton = new DummyInMemoryCacheLoader(); public boolean exists(Fqn name) throws Exception { return singleton.exists(name); } public Map get(Fqn name) throws Exception { return singleton.get(name); } public Set getChildrenNames(Fqn fqn) throws Exception { return singleton.getChildrenNames(fqn); } public IndividualCacheLoaderConfig getConfig() { return singleton.getConfig(); } public Object put(Fqn name, Object key, Object value) throws Exception { return singleton.put(name, key, value); } public void put(Fqn name, Map attributes) throws Exception { singleton.put(name, attributes); } public Object remove(Fqn fqn, Object key) throws Exception { return singleton.remove(fqn, key); } public void remove(Fqn fqn) throws Exception { singleton.remove(fqn); } public void removeData(Fqn fqn) throws Exception { singleton.removeData(fqn); } public void setConfig(IndividualCacheLoaderConfig config) { singleton.setConfig(config); } } static class WaitForPushSingletonStoreCacheLoader extends SingletonStoreCacheLoader { CountDownLatch startPushLatch; WaitForPushSingletonStoreCacheLoader() { } public void setStartPushLatch(CountDownLatch startPushLatch) { this.startPushLatch = startPushLatch; } @Override protected void pushState(NodeSPI node) throws Exception { startPushLatch.await(); super.pushState(node); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/ParentDataLoadedTest.java0000644000175000017500000000326511256644665030517 0ustar moellermoellerpackage org.jboss.cache.loader; import org.jboss.cache.AbstractSingleCacheTest; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.loader.testloaders.DummyInMemoryCacheLoader; import org.testng.annotations.Test; @Test(groups = "functional", testName = "loader.ParentDataLoadedTest") public class ParentDataLoadedTest extends AbstractSingleCacheTest { private static final Fqn FQN = Fqn.fromElements("a", "a", "a"); private CacheSPI cache; public CacheSPI createCache() throws Exception { UnitTestCacheFactory cf = new UnitTestCacheFactory(); cache = (CacheSPI) cf.createCache("configs/local-tx.xml", false, getClass()); cache.getConfiguration().setEvictionConfig(null); CacheLoaderConfig cacheLoaderConfig = UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", DummyInMemoryCacheLoader.class.getName(), "", false, true, false, false, false); cache.getConfiguration().setCacheLoaderConfig(cacheLoaderConfig); cache.start(); return cache; } public void testParents() { cache.put(FQN.getParent().getParent(), "test", "test"); cache.put(FQN.getParent(), "test", "test"); cache.put(FQN, "test", "test"); cache.evict(FQN.ROOT, true); cache.put(FQN, "test", "new"); assert cache.get(FQN, "test") != null; assert cache.get(FQN.getParent(), "test") != null; assert cache.get(FQN.getParent().getParent(), "test") != null; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/UnnecessaryLoadingTest.java0000644000175000017500000003042711120367722031142 0ustar moellermoellerpackage org.jboss.cache.loader; import static org.easymock.EasyMock.*; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.NodeSPI; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration.NodeLockingScheme; 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.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * @author Manik Surtani * @since 2.0.0 */ @Test(groups = {"functional"}, sequential = true, testName = "loader.UnnecessaryLoadingTest") 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 UnitTestCacheFactory().createCache(false, getClass()); CacheLoaderConfig clc = new CacheLoaderConfig(); CacheLoaderConfig.IndividualCacheLoaderConfig iclc = new CacheLoaderConfig.IndividualCacheLoaderConfig(); clc.addIndividualCacheLoaderConfig(iclc); cache.getConfiguration().setCacheLoaderConfig(clc); cache.getConfiguration().setNodeLockingScheme(NodeLockingScheme.PESSIMISTIC); 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); TestingUtil.killCaches(cache); cache = null; } 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); } 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); 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); Map toExpect = new HashMap(m); toExpect.put(k, v); mockCacheLoader.put(eq(parent), eq(toExpect)); expect(mockCacheLoader.get(eq(parent))).andReturn(toExpect); replay(mockCacheLoader); cache.put(parent, k, v); assertDataLoaded(parent); // now evict cache.evict(parent, false); assertDataNotLoaded(parent); // should not load cache.put(parent, m); 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-3.2.8.GA/src/test/java/org/jboss/cache/loader/DataSourceIntegrationTest.java0000644000175000017500000001221111131613416031566 0ustar moellermoeller/* * 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.UnitTestCacheFactory; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.transaction.DummyTransactionManager; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.UnitTestDatabaseManager; 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", sequential = true, testName = "loader.DataSourceIntegrationTest") public class DataSourceIntegrationTest { //private String old_factory = null; private final String FACTORY = "org.jboss.cache.transaction.DummyContextFactory"; private final String JNDI_NAME = "java:/MockDS"; private CacheSPI cache; private Properties props; @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(Properties jndi) throws Exception { Properties props = new Properties(jndi); props.put("cache.jdbc.datasource", JNDI_NAME); props.put("cache.jdbc.table.create", true); props.put("cache.jdbc.table.drop", true); return UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", "org.jboss.cache.loader.JDBCCacheLoader", props, false, false, false, false, false); } /** * 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 { props = null; Context context = new InitialContext(); try { Object obj = context.lookup(JNDI_NAME); assertNull(JNDI_NAME + " not bound", obj); } catch (NameNotFoundException n) { // expected } props = UnitTestDatabaseManager.getTestDbProperties(); cache = (CacheSPI) new UnitTestCacheFactory().createCache(false, getClass()); cache.getConfiguration().setCacheMode("local"); cache.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache.getConfiguration().setCacheLoaderConfig(getCacheLoaderConfig(props)); cache.create(); MockDataSource ds = new MockDataSource(props); context.bind(JNDI_NAME, ds); 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 (cache != null) { TestingUtil.killCaches(cache); UnitTestDatabaseManager.shutdownInMemoryDatabase(props); cache = null; } } private static class MockDataSource implements DataSource { private String userName; private String jdbcUrl; private String jdbcPassword; public MockDataSource(Properties properties) { try { Class.forName(properties.getProperty("cache.jdbc.driver")); } catch (ClassNotFoundException e) { throw new RuntimeException(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-3.2.8.GA/src/test/java/org/jboss/cache/loader/S3CacheLoaderTest.java0000644000175000017500000000570711131613416027704 0ustar moellermoellerpackage org.jboss.cache.loader; import static org.testng.AssertJUnit.assertNotNull; 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.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.config.CacheLoaderConfig; import org.testng.annotations.Test; import org.testng.annotations.AfterClass; /** * 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: S3CacheLoaderTest.java 7422 2009-01-09 09:21:18Z mircea.markus $ */ @Test(groups = {"functional"}, enabled = true, testName = "loader.S3CacheLoaderTest") public class S3CacheLoaderTest extends CacheLoaderTestsBase { private static final Log log = LogFactory.getLog(S3CacheLoaderTest.class); Server server; @Override protected void configureCache(CacheSPI cache) throws Exception { if (server == null) { server = new Server(); server.start(); } String accessKey = System.getProperty("accessKey"); String properties; if (accessKey == null) { log.info("Testing using S3CacheLoader using emulator"); 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 = UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", "org.jboss.cache.loader.s3.S3CacheLoader", properties, false, true, false, false, false); Properties p = config.getFirstCacheLoaderConfig().getProperties(); assertNotNull(p.get("cache.s3.accessKeyId")); assertNotNull(p.get("cache.s3.secretAccessKey")); cache.getConfiguration().setCacheLoaderConfig(config); } @AfterClass public void closeServerConnection() throws Exception { server.close(); } 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 { } public void testIgnoreModifications() throws Exception { //do nothing } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/TcpCacheServerTest.java0000644000175000017500000001325611133400374030202 0ustar moellermoellerpackage org.jboss.cache.loader; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestConfigurationFactory; import static org.jboss.cache.factories.UnitTestConfigurationFactory.buildSingleCacheLoaderConfig; import org.jboss.cache.jmx.CacheJmxWrapper; import org.jboss.cache.loader.tcp.TcpCacheServer; import org.jboss.cache.util.TestingUtil; 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 org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import java.net.UnknownHostException; /** * Tests various ways of setting up the TcpCacheServer * * @author Brian Stansberry * @version $Id: TcpCacheServerTest.java 7467 2009-01-14 15:13:00Z manik.surtani@jboss.com $ */ @Test(groups = {"functional"}, enabled = true, testName = "loader.TcpCacheServerTest") public class TcpCacheServerTest { static TcpCacheServer cache_server = null; private static final String SERVER_IP = "127.0.0.1"; private static final int SERVER_PORT = 13131; private CacheSPI cache; private CacheLoader loader; static { Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { if (cache_server != null) { 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 UnitTestCacheFactory().createCache(c, getClass()); cache.start(); loader = cache.getCacheLoaderManager().getCacheLoader(); } protected CacheLoaderConfig getCacheLoaderConfig() throws Exception { return buildSingleCacheLoaderConfig(false, null, "org.jboss.cache.loader.TcpDelegatingCacheLoader", "host=" + SERVER_IP + "\nport=" + SERVER_PORT, false, true, true, false, false); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { if (cache != null) { cache.stop(); cache.destroy(); cache = null; } if (cache_server != null) { cache_server.stop(); cache_server = null; } } private static void createTcpCacheServer() throws UnknownHostException { cache_server = new TcpCacheServer(); cache_server.setBindAddress(SERVER_IP); cache_server.setPort(SERVER_PORT); } private void startTcpCacheServer() { Thread runner = new Thread() { public void run() { try { 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 = UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); CacheSPI cacheSPI = (CacheSPI) new UnitTestCacheFactory().createCache(conf, getClass()); cache_server.setCache(cacheSPI); startTcpCacheServer(); createCacheAndLoader(); cacheCheck(); usabilityCheck(); } public void testInjectCache() throws Exception { createTcpCacheServer(); Configuration conf = UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); CacheSPI cacheSPI = (CacheSPI) new UnitTestCacheFactory().createCache(conf, getClass()); cache_server.setCache(cacheSPI); startTcpCacheServer(); createCacheAndLoader(); cacheCheck(); usabilityCheck(); } public void testInjectCacheJmxWrapper() throws Exception { createTcpCacheServer(); Configuration conf = UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); CacheSPI cacheSPI = (CacheSPI) new UnitTestCacheFactory().createCache(conf, getClass()); 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-3.2.8.GA/src/test/java/org/jboss/cache/loader/UnnecessaryLoadingSetDataTest.java0000644000175000017500000001233111146273534032407 0ustar moellermoellerpackage org.jboss.cache.loader; import static org.easymock.EasyMock.*; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.NodeSPI; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.util.CachePrinter; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.AfterMethod; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.util.HashMap; import java.util.Map; /** * TODO merge with {@link UnnecessaryLoadingTest}. * * @author Elias Ross * @since 3.0.0 */ @Test(groups = {"functional", "mvcc"}, enabled = false, testName = "loader.UnnecessaryLoadingSetDataTest", description = "To do with the setData() method on Cache, which will only be valid in 3.1.0.GA.") public class UnnecessaryLoadingSetDataTest { private CacheSPI cache; private CacheLoader mockCacheLoader; private Fqn parent = Fqn.fromString("/parent"); @DataProvider(name = "locking") public Object[][] createData1() { return new Object[][]{ // TODO // { NodeLockingScheme.PESSIMISTIC }, {NodeLockingScheme.MVCC}, }; } private void setUp(NodeLockingScheme locking) throws Exception { cache = (CacheSPI) new UnitTestCacheFactory().createCache(false, getClass()); CacheLoaderConfig clc = new CacheLoaderConfig(); CacheLoaderConfig.IndividualCacheLoaderConfig iclc = new CacheLoaderConfig.IndividualCacheLoaderConfig(); clc.addIndividualCacheLoaderConfig(iclc); cache.getConfiguration().setCacheLoaderConfig(clc); cache.getConfiguration().setNodeLockingScheme(locking); mockCacheLoader = createMockCacheLoader(); iclc.setCacheLoader(mockCacheLoader); cache.start(); reset(mockCacheLoader); } public static CacheLoader createMockCacheLoader() throws Exception { CacheLoader 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); return 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); TestingUtil.killCaches(cache); cache = null; } 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()); } // @Test(dataProvider = "locking", enabled = false) public void testDontLoadWithSetData(NodeLockingScheme locking) throws Exception { System.err.println(locking); System.err.println(locking); setUp(locking); Map m0 = new HashMap(); m0.put("replace", "replace"); Map m1 = new HashMap(); m1.put("new", "new"); mockCacheLoader.put(eq(parent), eq(m0)); mockCacheLoader.put(eq(parent), eq(m1)); mockCacheLoader.put(eq(parent), eq(m0)); mockCacheLoader.get(parent); expectLastCall().andStubThrow(new IllegalStateException("no need to call get()")); mockCacheLoader.exists(parent); expectLastCall().andStubThrow(new IllegalStateException("no need to call exists()")); replay(mockCacheLoader); assertDataNotLoaded(parent); // cache.setData(parent, m0); assertDataLoaded(parent); // cache.setData(parent, m1); assertEquals(m1, cache.peek(parent, false).getData()); // force removal, see if load happens cache.evict(parent); // cache.setData(parent, m0); // assertDataLoaded(parent); assertEquals(m0, cache.peek(parent, false).getData()); verify(mockCacheLoader); CachePrinter.printCacheDetails(cache); cache.toString(); } }jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/ReadOnlyClusteredCacheLoaderTest.java0000644000175000017500000000363311435716664033023 0ustar moellermoellerpackage org.jboss.cache.loader; import org.jboss.cache.AbstractMultipleCachesTest; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.testng.annotations.Test; /** * Test read only clustered cache loader. * * @author Galder Zamarreño * @since 3.2.6 */ @Test(groups = {"functional"}, testName = "loader.ReadOnlyClusteredCacheLoaderTest") public class ReadOnlyClusteredCacheLoaderTest extends AbstractMultipleCachesTest { private CacheSPI cache1, cache2; @Override protected void createCaches() throws Throwable { Configuration c1 = new Configuration(); Configuration c2 = new Configuration(); c1.setCacheMode(Configuration.CacheMode.REPL_SYNC); c2.setCacheMode(Configuration.CacheMode.REPL_SYNC); c1.setCacheLoaderConfig(UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", "org.jboss.cache.loader.ClusteredCacheLoader", "timeout=5000", false, false, false, false, true)); c2.setCacheLoaderConfig(UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", "org.jboss.cache.loader.ClusteredCacheLoader", "timeout=5000", false, false, false, false, true)); cache1 = (CacheSPI) new UnitTestCacheFactory().createCache(c1, false, getClass()); cache2 = (CacheSPI) new UnitTestCacheFactory().createCache(c2, false, getClass()); cache1.start(); cache2.start(); registerCaches(cache1, cache2); } public void testReadOnlyAccess() throws Exception { Node rootNode = cache1.getRoot(); Fqn fqn = Fqn.fromElements("blap"); Node child = rootNode.getChild(fqn); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/DataOverwriteOptTest.java0000644000175000017500000000053411251246471030606 0ustar moellermoellerpackage org.jboss.cache.loader; import org.testng.annotations.Test; import org.jboss.cache.config.Configuration; @Test(groups = "functional", testName = "loader.DataOverwriteOptTest") public class DataOverwriteOptTest extends DataOverwriteTest { public DataOverwriteOptTest() { nls = Configuration.NodeLockingScheme.OPTIMISTIC; } }jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/JDBCCacheLoaderConfigTest.java0000644000175000017500000000513411130373015031236 0ustar moellermoeller/* * 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; import java.util.Properties; /** * Unit test for JDBCCacheLoaderConfigTest * * @author Galder Zamarreno */ @Test(groups = {"unit"}, testName = "loader.JDBCCacheLoaderConfigTest") 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)); } public void testSetGetBatchInfo() { Properties props = new Properties(); props.put("cache.jdbc.table.name", "jbosscache"); props.put("cache.jdbc.table.create", "true"); props.put("cache.jdbc.table.drop", "true"); props.put("cache.jdbc.table.primarykey", "jbosscache_pk"); props.put("cache.jdbc.fqn.column", "fqn"); props.put("cache.jdbc.fqn.type", "varchar(255)"); props.put("cache.jdbc.node.column", "node"); props.put("cache.jdbc.node.type", "blob"); props.put("cache.jdbc.parent.column", "parent"); props.put("cache.jdbc.driver", "org.hsqldb.jdbcDriver"); props.put("cache.jdbc.url", "jdbc:hsqldb:mem:jbosscache"); props.put("cache.jdbc.user", "sa"); props.put("cache.jdbc.password", ""); props.put("cache.jdbc.batch.enable", "false"); props.put("cache.jdbc.batch.size", "1001"); JDBCCacheLoaderConfig config = new JDBCCacheLoaderConfig(); config.setProperties(props); assert !config.isBatchEnabled(); assert config.getBatchSize() == 1001; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/RootChildrenLoadedTest.java0000644000175000017500000000351011132627432031043 0ustar moellermoellerpackage org.jboss.cache.loader; import org.jboss.cache.Cache; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.loader.testloaders.DummyInMemoryCacheLoader; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; @Test(groups = "functional", testName = "loader.RootChildrenLoadedTest") public class RootChildrenLoadedTest { Cache cache; Fqn fqn = Fqn.fromElements("a", "a"); String key = "key"; @BeforeTest public void setUp() throws Exception { CacheLoaderConfig cacheLoaderConfig = UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", DummyInMemoryCacheLoader.class.getName(), "", false, true, false, false, false); // assign the cache loader explicitly so it will stick between restarts cacheLoaderConfig.getFirstCacheLoaderConfig().setCacheLoader(new DummyInMemoryCacheLoader()); Configuration cfg = new Configuration(); cfg.setCacheLoaderConfig(cacheLoaderConfig); cache = new UnitTestCacheFactory().createCache(cfg, getClass()); cache.put(fqn, key, "value"); // flush the cache and start with totally clean state cache.stop(); cache.start(); } @AfterTest public void tearDown() { TestingUtil.killCaches(cache); } public void doTest() throws Exception { // the workaround: // NodeInvocationDelegate root = (NodeInvocationDelegate) cache.getRoot(); // root.setChildrenLoaded(false); assert cache.getNode(Fqn.ROOT).getChildrenNames().size() == 1; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/CacheLoaderTestsBase.java0000755000175000017500000021767111142012274030461 0ustar moellermoellerpackage org.jboss.cache.loader; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.AbstractSingleCacheTest; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Modification; import org.jboss.cache.Node; import org.jboss.cache.NodeSPI; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.buddyreplication.BuddyManager; import org.jboss.cache.config.Configuration; import org.jboss.cache.statetransfer.DefaultStateTransferManager; import org.jboss.cache.transaction.TransactionSetup; import org.jboss.cache.util.TestingUtil; 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 7629 2009-02-03 09:56:12Z manik.surtani@jboss.com $ */ @Test(groups = {"functional"}) abstract public class CacheLoaderTestsBase extends AbstractSingleCacheTest { static final Log log = LogFactory.getLog(CacheLoaderTestsBase.class); 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); protected CacheLoader loader; protected CacheSPI createCache() throws Exception { cache = createUnstartedCache(); cache.start(); loader = cache.getCacheLoaderManager().getCacheLoader(); postConfigure(); return cache; } private CacheSPI createUnstartedCache() throws Exception { CacheSPI result = (CacheSPI) new UnitTestCacheFactory().createCache(false, getClass()); Configuration c = result.getConfiguration(); c.setEvictionConfig(null); c.setCacheMode(Configuration.CacheMode.LOCAL); c.setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); configureCache(result); return result; } @BeforeMethod public void clearLoader() throws Exception { loader.remove(Fqn.ROOT); } /** * Subclass if you need any further cfg after the cache starts. */ protected void postConfigure() { } abstract protected void configureCache(CacheSPI cache) throws Exception; @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { cleanup(); //if (loader != null) loader.remove(Fqn.ROOT); } protected void cleanup() throws Exception { // 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); assertEquals(m, cache.getNode(NODE).getData()); cache.get(NODE, "X"); cache.put(NODE, m2); Map data = cache.getNode(NODE).getData(); assertEquals("combined", 4, data.size()); } 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 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); } public void testGetNode() throws CacheException { final String NODE = "/a/b/c"; Node 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); assertEquals(10, retval.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"); 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")); 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")); } @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); 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); 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); 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); 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); 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); assertEquals("3 children weren't loaded", 3, children.size()); children = cache.getNode("/a").getChildrenNames(); assertNotNull("No children were loaded", 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; 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 = 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()); } 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 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(); assertEquals(3, children.size()); assertTrue(children.contains("1")); assertTrue(children.contains("2")); assertTrue(children.contains("3")); 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("/")); cache.put(FQN, "K", "V"); assert loader.get(FQN).get("K").equals("V"); assert cache.get(FQN, "K").equals("V"); // now modify K in a tx cache.getTransactionManager().begin(); cache.put(FQN, "K", "V2"); assert loader.get(FQN).get("K").equals("V"); assert cache.get(FQN, "K").equals("V2"); cache.getTransactionManager().rollback(); assert loader.get(FQN).get("K").equals("V"); assert cache.get(FQN, "K").equals("V"); } /** * 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(); // any call to putAll() will result in all the cached data being written map.put("one", "two"); map.put("three", "four"); 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) { } finally { cache.getMarshaller().objectToObjectStream(DefaultStateTransferManager.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) { } 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)); } public void testPutAllAfterEvict() throws Exception { Fqn fqn = Fqn.fromString("/a/b"); Map original = new HashMap(); Map toAdd = new HashMap(); Map all = new HashMap(); original.put("1", "One"); original.put("2", "Two"); toAdd.put("3", "Three"); toAdd.put("4", "Four"); all.putAll(original); all.putAll(toAdd); cache.put(fqn, original); assert loader.get(fqn).equals(original); cache.evict(fqn); assert loader.get(fqn).equals(original); cache.put(fqn, toAdd); assert loader.get(fqn).equals(all); cache.evict(fqn); assert loader.get(fqn).equals(all); } public void testPutAllAfterEvictWithChild() throws Exception { Fqn fqn = Fqn.fromString("/a/b"); Map original = new HashMap(); Map toAdd = new HashMap(); Map all = new HashMap(); original.put("1", "One"); original.put("2", "Two"); toAdd.put("3", "Three"); toAdd.put("4", "Four"); all.putAll(original); all.putAll(toAdd); cache.put(fqn, original); cache.put(Fqn.fromRelativeElements(fqn, "c"), "x", "y"); // put stuff in a child so that evicting fqn will only clear its data map. assert loader.get(fqn).equals(original); cache.evict(fqn); assert loader.get(fqn).equals(original); cache.put(fqn, toAdd); assert loader.get(fqn).equals(all); cache.evict(fqn); assert loader.get(fqn).equals(all); } /** * 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(DefaultStateTransferManager.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(DefaultStateTransferManager.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")); CacheSPI secondCache = createUnstartedCache(); secondCache.getConfiguration().getCacheLoaderConfig().getIndividualCacheLoaderConfigs().get(0).setIgnoreModifications(true); secondCache.start(); CacheLoader secondLoader = secondCache.getCacheLoaderManager().getCacheLoader(); postConfigure(); // CCL uses it's own mechanisms to ensure read-only behaviour if (!(secondLoader instanceof ChainingCacheLoader)) { // test that the cache loader is wrapped by a read-only delegate assert secondLoader instanceof ReadOnlyDelegatingCacheLoader; } // old state should be persisted. assert "v".equals(secondLoader.get(fqn).get("k")); assert "v".equals(secondCache.get(fqn, "k")); // the loader should now be read-only secondCache.put(fqn, "k", "v2"); assert "v2".equals(secondCache.get(fqn, "k")); assert "v".equals(loader.get(fqn).get("k")); TestingUtil.killCaches(secondCache); } public void testCacheLoaderThreadSafety() throws Exception { threadSafetyTest(true); } public void testCacheLoaderThreadSafetyMultipleFqns() throws Exception { threadSafetyTest(false); } //todo mmarkus add a parameter here to user grater values for different mvn profiles 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 = 100; 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(); } // TODO: re-enable once we have setData() implemented in 3.1.0 // public void testSetData() throws Exception // { // log.info("testSetData"); // Fqn key = Fqn.fromElements("key"); // Map map = new HashMap(); // Map loaderMap; // map.put("a", "a"); // map.put("c", "c"); // log.info("PUT"); // cache.put(key, "x", "x"); // cache.setData(key, map); // assertEquals(map, cache.getData(key)); // log.info("GET"); // loaderMap = loader.get(key); // assertEquals(map, loaderMap); // // assertNull(cache.get(key, "x")); // assertEquals("c", cache.get(key, "c")); // assertEquals("a", cache.get(key, "a")); // loaderMap = loader.get(key); // assertEquals(map, loaderMap); // } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/ClusteredCacheLoaderRegionBasedTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/ClusteredCacheLoaderRegionBasedTest.ja0000644000175000017500000000052611111047320033110 0ustar moellermoellerpackage org.jboss.cache.loader; import org.testng.annotations.Test; @Test(groups = {"functional"}, testName = "loader.ClusteredCacheLoaderRegionBasedTest") public class ClusteredCacheLoaderRegionBasedTest extends ClusteredCacheLoaderTest { public ClusteredCacheLoaderRegionBasedTest() { useRegionBasedMarshalling = true; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/JdbmCacheLoader2Test.java0000644000175000017500000000233311131613416030345 0ustar moellermoellerpackage org.jboss.cache.loader; import org.jboss.cache.CacheSPI; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.Test; /** * Tests {@link org.jboss.cache.loader.jdbm.JdbmCacheLoader2}. * * @author Elias Ross * @version $Id: JdbmCacheLoaderTest.java 6905 2008-10-13 09:35:27Z dpospisi@redhat.com $ */ @Test (groups = {"functional"}, testName = "loader.JdbmCacheLoader2Test") public class JdbmCacheLoader2Test extends CacheLoaderTestsBase { @Override protected void configureCache(CacheSPI cache) throws Exception { String tmpCLLoc = TestingUtil.TEST_FILES + "/JdbmCacheLoader2Test"; cache.getConfiguration().setCacheLoaderConfig(UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", "org.jboss.cache.loader.jdbm.JdbmCacheLoader2", "location=" + tmpCLLoc, false, true, false, false, false)); TestingUtil.recursiveFileRemove(tmpCLLoc); } @Override public void testCacheLoaderThreadSafety() throws Exception { } @Override public void testCacheLoaderThreadSafetyMultipleFqns() throws Exception { } public void testIgnoreModifications() throws Exception { } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/testloaders/0000755000175000017500000000000011675221535026173 5ustar moellermoeller././@LongLink0000000000000000000000000000016400000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/testloaders/DummySharedInMemoryCacheLoaderConfig.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/testloaders/DummySharedInMemoryCacheLo0000644000175000017500000000066111151527714033237 0ustar moellermoellerpackage org.jboss.cache.loader.testloaders; public class DummySharedInMemoryCacheLoaderConfig extends DummyInMemoryCacheLoaderConfig { public DummySharedInMemoryCacheLoaderConfig() { setClassName(DummySharedInMemoryCacheLoader.class.getName()); } public DummySharedInMemoryCacheLoaderConfig(String storeName) { super(storeName); setClassName(DummySharedInMemoryCacheLoader.class.getName()); } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/testloaders/DummyInMemoryCacheLoader.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/testloaders/DummyInMemoryCacheLoader.j0000644000175000017500000002031011151735442033164 0ustar moellermoeller/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader.testloaders; 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.loader.AbstractCacheLoader; 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 AT jboss DOT org) */ @ThreadSafe public class DummyInMemoryCacheLoader extends AbstractCacheLoader { private static Log log = LogFactory.getLog(DummyInMemoryCacheLoader.class); // Do NOT access this map directly. always use getNodesMap() since it may be overridden. protected Map nodes = new ConcurrentHashMap(); 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 { debugMessage("Calling getChildrenNames on Fqn " + fqn); 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; } 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; 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 == null ? NULL : 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); } n.data.clear(); // emulate cache loaders overwriting any internal data map with new data map passed in. 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 (log.isTraceEnabled()) log.trace(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(); } } ././@LongLink0000000000000000000000000000015600000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/testloaders/DummyInMemoryCacheLoaderConfig.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/testloaders/DummyInMemoryCacheLoaderCo0000644000175000017500000000171611151527714033230 0ustar moellermoellerpackage org.jboss.cache.loader.testloaders; import org.jboss.cache.config.CacheLoaderConfig; import java.util.Properties; import java.io.IOException; public class DummyInMemoryCacheLoaderConfig extends CacheLoaderConfig.IndividualCacheLoaderConfig { String storeName; public DummyInMemoryCacheLoaderConfig() { setClassName(DummyInMemoryCacheLoader.class.getName()); } public DummyInMemoryCacheLoaderConfig(String storeName) { setClassName(DummyInMemoryCacheLoader.class.getName()); this.storeName = storeName; } public void setProperties(Properties p) { if (storeName != null) { if (p == null) p = new Properties(); p.setProperty("bin", storeName); } super.setProperties(p); } public void setProperties(String s) throws IOException { super.setProperties(s); if (storeName != null) { this.properties.setProperty("bin", storeName); } } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/testloaders/DummyCountingCacheLoader.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/testloaders/DummyCountingCacheLoader.j0000644000175000017500000001304611121730272033214 0ustar moellermoeller/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader.testloaders; import org.jboss.cache.Fqn; import org.jboss.cache.Modification; import org.jboss.cache.loader.AbstractCacheLoader; 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 AT jboss DOT 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(); } }././@LongLink0000000000000000000000000000015600000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/testloaders/DummySharedInMemoryCacheLoader.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/testloaders/DummySharedInMemoryCacheLo0000644000175000017500000000351211121730272033225 0ustar moellermoellerpackage org.jboss.cache.loader.testloaders; 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 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"); if (bin == null) { throw new IllegalStateException("bin MUST be present for shared state CL. This is because tests might run concurrently!"); } } 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(); } public void wipeBin() { BINS.remove(bin); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/C3p0ConnectionFactoryTest.java0000644000175000017500000000665111133721130031413 0ustar moellermoeller/* * 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.PooledDataSource; import org.jboss.cache.util.UnitTestDatabaseManager; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.fail; import org.testng.annotations.AfterMethod; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import java.sql.Connection; import java.util.Properties; /** * Unit test for C3p0ConnectionFactory * * @author Galder Zamarreno */ @Test(groups = {"functional"}, testName = "loader.C3p0ConnectionFactoryTest") public class C3p0ConnectionFactoryTest { private C3p0ConnectionFactory cf; private AdjListJDBCCacheLoaderConfig config; private Properties props; @BeforeTest public void createDatabase() { props = UnitTestDatabaseManager.getTestDbProperties(); } @AfterTest public void shutDownDatabase() { UnitTestDatabaseManager.shutdownInMemoryDatabase(props); } @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { config = new AdjListJDBCCacheLoaderConfig(); config.setProperties(props); } @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()); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/InterceptorSynchronizationTest.java0000644000175000017500000001455211120367722032766 0ustar moellermoellerpackage org.jboss.cache.loader; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Modification; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.config.Configuration.NodeLockingScheme; 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; import org.jboss.cache.UnitTestCacheFactory; /** * 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"}, testName = "loader.InterceptorSynchronizationTest") 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 UnitTestCacheFactory().createCache(false, getClass()); //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.getConfiguration().setNodeLockingScheme(NodeLockingScheme.PESSIMISTIC); 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-3.2.8.GA/src/test/java/org/jboss/cache/loader/DataOverwriteTest.java0000644000175000017500000000750011251246471030123 0ustar moellermoellerpackage org.jboss.cache.loader; import org.jboss.cache.AbstractSingleCacheTest; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.loader.testloaders.DummyInMemoryCacheLoader; import org.testng.annotations.Test; import javax.transaction.TransactionManager; import java.util.Collections; import java.util.HashMap; import java.util.Map; @Test(groups = "functional", testName = "loader.DataOverwriteTest") public class DataOverwriteTest extends AbstractSingleCacheTest { private static final Fqn FQN = Fqn.fromElements("a", "a"); private static final Fqn FQN_PARENT = FQN.getParent(); private CacheSPI cache; private TransactionManager tm; protected Configuration.NodeLockingScheme nls = Configuration.NodeLockingScheme.PESSIMISTIC; public CacheSPI createCache() throws Exception { UnitTestCacheFactory cf = new UnitTestCacheFactory(); cache = (CacheSPI) cf.createCache("configs/local-tx.xml", false, getClass()); cache.getConfiguration().setLockAcquisitionTimeout(4000); // cache.getConfiguration().setEvictionConfig(null); cache.getConfiguration().setLockParentForChildInsertRemove(true); CacheLoaderConfig cacheLoaderConfig = UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", DummyInMemoryCacheLoader.class.getName(), "", false, true, false, false, false); cache.getConfiguration().setCacheLoaderConfig(cacheLoaderConfig); cache.getConfiguration().setNodeLockingScheme(nls); cache.start(); tm = cache.getTransactionManager(); return cache; } public void testOverwriteGetData() throws Exception { // this will put it in the cache loader tm.begin(); cache.put(FQN, "test", "test1"); cache.evict(FQN_PARENT); tm.commit(); tm.begin(); cache.put(FQN, "test", "test2"); cache.getChildrenNames(FQN_PARENT); Map test2 = cache.getData(FQN); assert test2.equals(Collections.singletonMap("test", "test2")) : test2; tm.commit(); } public void testOverwriteGet() throws Exception { // this will put it in the cache loader tm.begin(); cache.put(FQN, "test", "test1"); cache.evict(FQN_PARENT); tm.commit(); tm.begin(); cache.put(FQN, "test", "test2"); cache.getChildrenNames(FQN_PARENT); String test2 = cache.get(FQN, "test"); assert test2.equals("test2") : test2; tm.commit(); } public void testOverwriteGetData2() throws Exception { // this will put it in the cache loader tm.begin(); cache.put(FQN, "test", "test1"); cache.put(FQN, "old", "old"); cache.evict(FQN_PARENT); tm.commit(); tm.begin(); cache.put(FQN, "test", "test2"); cache.getChildrenNames(FQN_PARENT); Map test2 = cache.getData(FQN); Map m = new HashMap(); m.put("test", "test2"); m.put("old", "old"); assert test2.equals(m) : test2; tm.commit(); } public void testOverwriteGet2() throws Exception { // this will put it in the cache loader tm.begin(); cache.put(FQN, "test", "test1"); cache.put(FQN, "old", "old"); cache.evict(FQN_PARENT); tm.commit(); tm.begin(); cache.put(FQN, "test", "test2"); cache.getChildrenNames(FQN_PARENT); String test2 = cache.get(FQN, "test"); assert test2.equals("test2") : test2; String old = cache.get(FQN, "old"); assert old.equals("old") : old; tm.commit(); } }jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/CacheLoaderWithReplicationTest.java0000644000175000017500000002374011131613416032521 0ustar moellermoeller/* * 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.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.loader.testloaders.DummyInMemoryCacheLoader; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.replicationlisteners.ReplicationListener; 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 AT jboss DOT org) */ @Test(groups = "functional", testName = "loader.CacheLoaderWithReplicationTest") public class CacheLoaderWithReplicationTest { 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 UnitTestCacheFactory().createCache(false, getClass()); cache1.getConfiguration().setCacheLoaderConfig(UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", DummyInMemoryCacheLoader.class.getName(), "debug=true", false, true, false, false, false)); cache1.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cache2 = new UnitTestCacheFactory().createCache(false, getClass()); cache2.getConfiguration().setCacheLoaderConfig(UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", DummyInMemoryCacheLoader.class.getName(), "debug=true", false, true, false, false, false)); cache2.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); 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) { } TestingUtil.killCaches(cache1); } 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) { } TestingUtil.killCaches(cache2); } 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); } else { cache1.getConfiguration().setNodeLockingScheme(NodeLockingScheme.PESSIMISTIC); cache2.getConfiguration().setNodeLockingScheme(NodeLockingScheme.PESSIMISTIC); } 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 = ReplicationListener.getReplicationListener(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.expectWithTx(PutKeyValueCommand.class); mgr1.commit(); replListener.waitForReplicationToOccur(); 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 = ReplicationListener.getReplicationListener(cache2); mgr1.begin(); replListener.expectWithTx(PutKeyValueCommand.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(); 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-3.2.8.GA/src/test/java/org/jboss/cache/loader/ChainedClusteredCacheLoaderTest.java0000644000175000017500000001032311176306660032624 0ustar moellermoellerpackage org.jboss.cache.loader; import org.jboss.cache.AbstractMultipleCachesTest; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.loader.testloaders.DummySharedInMemoryCacheLoader; import org.jboss.cache.loader.testloaders.DummySharedInMemoryCacheLoaderConfig; import org.testng.annotations.Test; import java.util.HashSet; import java.util.Set; @Test(groups = {"functional"}, sequential = true, testName = "loader.ChainedClusteredCacheLoaderTest") public class ChainedClusteredCacheLoaderTest extends AbstractMultipleCachesTest { private CacheSPI cache1, cache2; private CacheLoader loader1, loader2; private Fqn fqn = Fqn.fromString("/a"); private Fqn fqn2 = Fqn.fromString("/a/b"); private String key = "key"; protected void createCaches() throws Throwable { Configuration c1 = new Configuration(); Configuration c2 = new Configuration(); c1.setStateRetrievalTimeout(2000); c2.setStateRetrievalTimeout(2000); c1.setCacheMode(Configuration.CacheMode.REPL_SYNC); c2.setCacheMode(Configuration.CacheMode.REPL_SYNC); c1.setCacheLoaderConfig(UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", "org.jboss.cache.loader.ClusteredCacheLoader", "timeout=5000", false, false, false, false, false)); c2.setCacheLoaderConfig(UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", "org.jboss.cache.loader.ClusteredCacheLoader", "timeout=5000", false, false, false, false, false)); DummySharedInMemoryCacheLoaderConfig cfg = new DummySharedInMemoryCacheLoaderConfig("cache-2"); c2.getCacheLoaderConfig().addIndividualCacheLoaderConfig(cfg); c1.setUseRegionBasedMarshalling(false); c2.setUseRegionBasedMarshalling(false); cache1 = (CacheSPI) new UnitTestCacheFactory().createCache(c1, false, getClass()); cache2 = (CacheSPI) new UnitTestCacheFactory().createCache(c2, false, getClass()); cache1.getConfiguration().setSerializationExecutorPoolSize(0); cache2.getConfiguration().setSerializationExecutorPoolSize(0); cache1.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); cache2.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); cache1.start(); cache2.start(); loader1 = cache1.getCacheLoaderManager().getCacheLoader(); loader2 = cache2.getCacheLoaderManager().getCacheLoader(); registerCaches(cache1, cache2); } public void testClusteredGetFromSecondLoader() throws Exception { cache1.put(fqn, key, "value"); assert loader1 instanceof ClusteredCacheLoader; assert loader2 instanceof ChainingCacheLoader; DummySharedInMemoryCacheLoader dummyLoader2 = (DummySharedInMemoryCacheLoader) ((ChainingCacheLoader) loader2).getCacheLoaders().get(1); assert loader1.get(fqn).containsKey(key); assert loader2.get(fqn).containsKey(key); assert dummyLoader2.get(fqn).containsKey(key); // evict from memory on all caches cache1.evict(fqn); cache2.evict(fqn); assert dummyLoader2.get(fqn).containsKey(key); assert "value".equals(cache1.get(fqn, key)); } public void testClusteredGetChildrenNamesFromSecondLoader() throws Exception { cache1.put(fqn, key, "value"); cache1.put(fqn2, key, "value"); assert loader1 instanceof ClusteredCacheLoader; assert loader2 instanceof ChainingCacheLoader; DummySharedInMemoryCacheLoader dummyLoader2 = (DummySharedInMemoryCacheLoader) ((ChainingCacheLoader) loader2).getCacheLoaders().get(1); assert loader1.get(fqn).containsKey(key); assert loader2.get(fqn).containsKey(key); assert dummyLoader2.get(fqn).containsKey(key); // evict from memory on all caches cache1.evict(fqn, true); cache2.evict(fqn, true); assert dummyLoader2.get(fqn).containsKey(key); Set s = new HashSet(); s.add("b"); assert s.equals(loader1.getChildrenNames(fqn)); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/SharedCacheLoaderTest.java0000644000175000017500000000660711131613416030625 0ustar moellermoeller/* * 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.UnitTestCacheFactory; import org.jboss.cache.loader.testloaders.DummyCountingCacheLoader; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.interceptors.CacheStoreInterceptor; import org.jboss.cache.interceptors.base.CommandInterceptor; 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 java.util.Iterator; /** * See http://www.jboss.com/index.html?module=bb&op=viewtopic&p=3919374#3919374 * * @author Manik Surtani (manik AT jboss DOT org) */ @Test(groups = "functional", sequential = true, testName = "loader.SharedCacheLoaderTest") public class SharedCacheLoaderTest { 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. Configuration c1 = new Configuration(); Configuration c2 = new Configuration(); c1.setCacheMode("REPL_SYNC"); c2.setCacheMode("REPL_SYNC"); c1.setCacheLoaderConfig(UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", DummyCountingCacheLoader.class.getName(), "", false, false, true, false, false)); c2.setCacheLoaderConfig(UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", DummyCountingCacheLoader.class.getName(), "", false, false, true, false, false)); cache1 = (CacheSPI) new UnitTestCacheFactory().createCache(c1, false, getClass()); cache2 = (CacheSPI) new UnitTestCacheFactory().createCache(c2, false, getClass()); 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) TestingUtil.killCaches(cache1); if (cache2 != null) TestingUtil.killCaches(cache2); 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()); } } ././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/AdjListJDBCCacheLoaderCompatibilityTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/AdjListJDBCCacheLoaderCompatibilityTes0000644000175000017500000002345311131613416033020 0ustar moellermoellerpackage org.jboss.cache.loader; import org.jboss.cache.AbstractMultipleCachesTest; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.marshall.NodeData; import org.jboss.cache.statetransfer.DefaultStateTransferManager; import org.jboss.cache.util.UnitTestDatabaseManager; import org.jboss.util.stream.MarshalledValueInputStream; import org.jboss.util.stream.MarshalledValueOutputStream; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; 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"}, testName = "loader.AdjListJDBCCacheLoaderCompatibilityTest") @SuppressWarnings("deprecation") public class AdjListJDBCCacheLoaderCompatibilityTest extends AbstractMultipleCachesTest { @SuppressWarnings("deprecation") private JDBCCacheLoaderOld oldImpl; private JDBCCacheLoader newImpl; private CacheSPI cache, cache2; protected void createCaches() throws Throwable { newImpl = getNewCacheLoader((Properties) props.clone()); oldImpl = getOldLoader((Properties) props.clone()); cache = (CacheSPI) new UnitTestCacheFactory().createCache(getClass()); cache2 = (CacheSPI) new UnitTestCacheFactory().createCache(getClass()); newImpl.setCache(cache);//this is needed for marshaller oldImpl.setCache(cache2); registerCaches(cache, cache2); oldImpl.start(); newImpl.start(); } @BeforeMethod public void beforeMethod() throws Exception { oldImpl.remove(Fqn.ROOT); } private Properties props; @BeforeTest public void createDatabase() { props = UnitTestDatabaseManager.getTestDbProperties(); } @AfterTest public void shutDownDatabase() { UnitTestDatabaseManager.shutdownInMemoryDatabase(props); } public void testCommonOperations() 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"); 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.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(DefaultStateTransferManager.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(DefaultStateTransferManager.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"); 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 Properties getProperties() throws Exception { return UnitTestDatabaseManager.getTestDbProperties(); } private JDBCCacheLoader getNewCacheLoader(Properties prop) throws Exception { String tablePkPrefix = prop.getProperty("cache.jdbc.table.primarykey", "jbosscache_pk"); prop.setProperty("cache.jdbc.table.primarykey", (tablePkPrefix + 1)); CacheLoaderConfig.IndividualCacheLoaderConfig base = UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", JDBCCacheLoader.class.getName(), prop, false, true, false, false, false).getFirstCacheLoaderConfig(); JDBCCacheLoader jdbcCacheLoader = new JDBCCacheLoader(); jdbcCacheLoader.setConfig(base); return jdbcCacheLoader; } private JDBCCacheLoaderOld getOldLoader(Properties prop) throws Exception { CacheLoaderConfig.IndividualCacheLoaderConfig base = UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", "org.jboss.cache.loader.JDBCCacheLoader", prop, false, true, false, false, false).getFirstCacheLoaderConfig(); JDBCCacheLoaderOld jdbcCacheLoader = new JDBCCacheLoaderOld(); jdbcCacheLoader.setConfig(base); return jdbcCacheLoader; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/JDBCCacheLoaderStateTransferTest.java0000644000175000017500000001156711132110773032627 0ustar moellermoeller/* * 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.loader; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.transaction.GenericTransactionManagerLookup; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.UnitTestDatabaseManager; import org.testng.annotations.AfterMethod; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import java.util.Properties; /** * UT for testing JDBCCacheLoader during state transfer. * * @author Mircea.Markus@jboss.com * @since 3.0 */ @Test(groups = "functional", testName = "loader.JDBCCacheLoaderStateTransferTest") public class JDBCCacheLoaderStateTransferTest { CacheSPI first; CacheSPI second; private Properties props1; private Properties props2; @BeforeTest public void createDatabase() { props1 = UnitTestDatabaseManager.getTestDbProperties(); props2 = UnitTestDatabaseManager.getTestDbProperties(); } @AfterTest public void shutDownDatabase() { UnitTestDatabaseManager.shutdownInMemoryDatabase(props1); UnitTestDatabaseManager.shutdownInMemoryDatabase(props2); } @AfterMethod public void tearDown() { if (first != null) TestingUtil.killCaches(first); if (second != null) TestingUtil.killCaches(second); } private Configuration getConfiguration(Properties props) throws Exception { Configuration c = new Configuration(); c.setTransactionManagerLookupClass(GenericTransactionManagerLookup.class.getName()); CacheLoaderConfig clc = UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "/", JDBCCacheLoader.class.getName(), props, false, true, false, false, false); clc.setPassivation(false); clc.getFirstCacheLoaderConfig().setPurgeOnStartup(true); c.setCacheLoaderConfig(clc); c.setCacheMode(CacheMode.REPL_SYNC); c.setStateRetrievalTimeout(1000 * 120);//allow 2 minutes for before state transfer timeouts return c; } public void testSimpleStateTransfer() throws Exception { first = (CacheSPI) new UnitTestCacheFactory().createCache(getConfiguration(props1), getClass()); first.put("/a/b/c", "key", "value"); first.put("/a/b/d", "key", "value"); first.put("/a/b/e", "key", "value"); second = (CacheSPI) new UnitTestCacheFactory().createCache(getConfiguration(props2), getClass()); assert second.get("/a/b/c", "key").equals("value"); assert second.get("/a/b/d", "key").equals("value"); assert second.get("/a/b/e", "key").equals("value"); JDBCCacheLoader cacheLoader = (JDBCCacheLoader) second.getCacheLoaderManager().getCacheLoader(); assert cacheLoader.exists(Fqn.fromString("/a")); assert cacheLoader.exists(Fqn.fromString("/a/b")); } //todo mmarkus this test takes forever on fedora 9/4 cpu. Fix it! @Test (enabled = false) public void testMoreState() throws Exception { long startTime = System.currentTimeMillis(); first = (CacheSPI) new UnitTestCacheFactory().createCache(getConfiguration(props1), getClass()); long cacheStartTime = System.currentTimeMillis() - startTime; for (int i = 0; i < 5012; i++) { first.put("a/b/" + i, "k", "v"); } startTime = System.currentTimeMillis(); second = (CacheSPI) new UnitTestCacheFactory().createCache(getConfiguration(props2), getClass()); long stateTranferTime = System.currentTimeMillis() - startTime - cacheStartTime; System.out.println("stateTranferTime = " + stateTranferTime); for (int i = 0; i < 5012; i += 100) { assert second.get("a/b/" + i, "k").equals("v"); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/loader/JDBCCacheLoaderConnectionTest.java0000644000175000017500000000404111131613416032127 0ustar moellermoellerpackage org.jboss.cache.loader; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.UnitTestDatabaseManager; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.sql.Connection; import java.util.Properties; /** * To test the closing of JDBC connections */ @Test(groups = "functional", sequential = true, testName = "loader.JDBCCacheLoaderConnectionTest") public class JDBCCacheLoaderConnectionTest { private Cache cache; private Properties props; @BeforeMethod public void setUp() throws Exception { props = UnitTestDatabaseManager.getTestDbProperties(); cache = new UnitTestCacheFactory().createCache(false, getClass()); cache.getConfiguration().setCacheLoaderConfig(UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", JDBCCacheLoader.class.getName(), props, false, false, true, false, false)); cache.start(); } @AfterMethod public void tearDown() { TestingUtil.killCaches(cache); UnitTestDatabaseManager.shutdownInMemoryDatabase(props); cache = null; } 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)); } 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-3.2.8.GA/src/test/java/org/jboss/cache/loader/ChainingCacheLoaderBasicTest.java0000644000175000017500000000436411121730272032076 0ustar moellermoeller/* * 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.loader.testloaders.DummySharedInMemoryCacheLoader; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.Test; import java.io.File; /** * Tests basic functionality of a chaining cache loader with 2 different loaders * * @author Manik Surtani (manik AT jboss DOT org) */ @Test(groups = {"functional"}, testName = "loader.ChainingCacheLoaderBasicTest") public class ChainingCacheLoaderBasicTest extends CacheLoaderTestsBase { private static final String loc1 = "JBossCache-ChainingCacheLoaderBasicTest-1"; private static final String loc2 = "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(CacheSPI cache) throws Exception { cache.getConfiguration().setCacheLoaderConfig(getCacheLoaderConfig(loc1, loc2)); TestingUtil.recursiveFileRemove(loc1); TestingUtil.recursiveFileRemove(loc2); } protected CacheLoaderConfig getCacheLoaderConfig(String loc1, String loc2) throws Exception { CacheLoaderConfig clc = new CacheLoaderConfig(); // clc 1 IndividualCacheLoaderConfig iclc = new IndividualCacheLoaderConfig(); iclc.setClassName(DummySharedInMemoryCacheLoader.class.getName()); iclc.setAsync(false); iclc.setFetchPersistentState(true); iclc.setPurgeOnStartup(false); iclc.setIgnoreModifications(false); iclc.setProperties("bin=" + loc1); clc.addIndividualCacheLoaderConfig(iclc); IndividualCacheLoaderConfig iclc2 = iclc.clone(); iclc2.setFetchPersistentState(false); iclc2.setProperties("bin=" + loc2); clc.addIndividualCacheLoaderConfig(iclc2); clc.setPassivation(false); clc.setShared(false); return clc; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/AbstractMultipleCachesTest.java0000644000175000017500000000223211121730272030451 0ustar moellermoellerpackage org.jboss.cache; import org.testng.annotations.*; import org.jboss.cache.util.TestingUtil; import java.util.Set; import java.util.HashSet; import java.util.Arrays; /** * @author Mircea.Markus@jboss.com */ @Test(groups = {"functional", "unit"}) public abstract class AbstractMultipleCachesTest extends AbstractCacheTest { protected Set> caches = new HashSet>(); @BeforeClass public void create() throws Throwable { createCaches(); } @AfterClass protected void destroy() { TestingUtil.killCaches(caches.toArray(new Cache[caches.size()])); } @AfterMethod protected void clearContent() throws Throwable { if (caches.isEmpty()) throw new IllegalStateException("No caches registered! Use registerCaches(Cache... caches) do that!"); for (CacheSPI cache : caches) { super.clearContent(cache); } } protected abstract void createCaches() throws Throwable; final protected void registerCaches(Cache... caches) { for (Cache c: caches) this.caches.add((CacheSPI) c); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/0000755000175000017500000000000011675221575023151 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/SyncReplTest.java0000644000175000017500000001144511131613416026402 0ustar moellermoeller/* * 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.*; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.*; import org.testng.annotations.Test; import java.util.HashMap; import java.util.Map; /** * @author Manik Surtani (manik AT jboss DOT org) */ @Test(groups = {"functional", "jgroups", "pessimistic"}, sequential = true, testName = "api.SyncReplTest") public class SyncReplTest extends AbstractMultipleCachesTest { private CacheSPI cache1, cache2; protected NodeLockingScheme nodeLockingScheme = NodeLockingScheme.PESSIMISTIC; protected void createCaches() { cache1 = (CacheSPI) new UnitTestCacheFactory().createCache(UnitTestConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), false, getClass()); cache2 = (CacheSPI) new UnitTestCacheFactory().createCache(UnitTestConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), false, getClass()); cache1.getConfiguration().setNodeLockingScheme(nodeLockingScheme); cache2.getConfiguration().setNodeLockingScheme(nodeLockingScheme); configure(cache1.getConfiguration()); configure(cache2.getConfiguration()); cache1.start(); cache2.start(); TestingUtil.blockUntilViewsReceived(new Cache[]{cache1, cache2}, 5000); registerCaches(cache1, cache2); } protected void configure(Configuration c) { // to be overridden } 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; control = ctx.copy(); control.reset(); assertEquals("Should be equal", control, ctx); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/NodeSPITest.java0000644000175000017500000000710011120367722026101 0ustar moellermoellerpackage org.jboss.cache.api; import org.jboss.cache.*; import org.jboss.cache.config.Configuration; import static org.testng.AssertJUnit.*; import org.testng.annotations.Test; import java.util.Set; /** * Tests NodeSPI specific APIs. */ @Test(groups = {"functional", "pessimistic"}, sequential = true, testName = "api.NodeSPITest") public class NodeSPITest extends AbstractSingleCacheTest { private NodeSPI root; public CacheSPI createCache() { cache = (CacheSPI) new UnitTestCacheFactory().createCache(false, getClass()); cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); cache.start(); root = cache.getRoot(); return cache; } 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-3.2.8.GA/src/test/java/org/jboss/cache/api/batch/0000755000175000017500000000000011675221544024226 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/batch/AbstractBatchTest.java0000644000175000017500000000124711120367722030434 0ustar moellermoellerpackage org.jboss.cache.api.batch; import org.jboss.cache.Cache; import org.jboss.cache.AbstractSingleCacheTest; import java.util.concurrent.atomic.AtomicReference; public abstract class AbstractBatchTest extends AbstractSingleCacheTest { protected String getOnDifferentThread(final Cache cache, final String fqn, final String key) throws InterruptedException { final AtomicReference ref = new AtomicReference(); Thread t = new Thread() { public void run() { ref.set(cache.get(fqn, key)); } }; t.start(); t.join(); return ref.get(); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/batch/BatchWithoutTM.java0000644000175000017500000000606411132625723027740 0ustar moellermoellerpackage org.jboss.cache.api.batch; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.Test; @Test(groups = "functional", testName = "api.batch.BatchWithoutTM") public class BatchWithoutTM extends AbstractBatchTest { @Test (enabled = true) public void testBatchWithoutCfg() { Cache localCache = createCache(false); try { try { localCache.startBatch(); assert false : "Should have failed"; } catch (ConfigurationException good) { // do nothing } try { localCache.endBatch(true); assert false : "Should have failed"; } catch (ConfigurationException good) { // do nothing } try { localCache.endBatch(false); assert false : "Should have failed"; } catch (ConfigurationException good) { // do nothing } } finally { TestingUtil.killCaches(localCache); } } public void testStartBatchIdempotency() { assert cache.getCacheStatus().allowInvocations(); cache.startBatch(); cache.put("/a/b/c", "k", "v"); cache.startBatch(); // again cache.put("/a/b/c", "k2", "v2"); cache.endBatch(true); assert "v".equals(cache.get("/a/b/c", "k")); assert "v2".equals(cache.get("/a/b/c", "k2")); } public void testBatchVisibility() throws InterruptedException { cache = createCache(true); cache.startBatch(); cache.put("/a/b/c", "k", "v"); assert getOnDifferentThread(cache, "/a/b/c", "k") == null : "Other thread should not see batch update till batch completes!"; cache.endBatch(true); assert "v".equals(getOnDifferentThread(cache, "/a/b/c", "k")); } public void testBatchRollback() throws Exception { cache.startBatch(); cache.put("/a/b/c", "k", "v"); cache.put("/a/b/c", "k2", "v2"); assert getOnDifferentThread(cache, "/a/b/c", "k") == null; assert getOnDifferentThread(cache, "/a/b/c", "k2") == null; cache.endBatch(false); assert getOnDifferentThread(cache, "/a/b/c", "k") == null; assert getOnDifferentThread(cache, "/a/b/c", "k2") == null; } public CacheSPI createCache() { return createCache(true); } private CacheSPI createCache(boolean enableBatch) { UnitTestCacheFactory cf = new UnitTestCacheFactory(); Configuration c = new Configuration(); c.setNodeLockingScheme(NodeLockingScheme.MVCC); c.setInvocationBatchingEnabled(enableBatch); return (CacheSPI) cf.createCache(c, getClass()); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/batch/BatchWithTM.java0000644000175000017500000000663611131613416027210 0ustar moellermoellerpackage org.jboss.cache.api.batch; import org.jboss.cache.Cache; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.testng.annotations.Test; import javax.transaction.TransactionManager; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.CacheSPI; @Test(groups = {"functional", "transaction"}, testName = "api.batch.BatchWithTM") public class BatchWithTM extends AbstractBatchTest { public void testBatchWithOngoingTM() throws Exception { TransactionManager tm = getTransactionManager(cache); tm.begin(); cache.put("/a/b/c", "k", "v"); cache.startBatch(); cache.put("/a/b/c", "k2", "v2"); tm.commit(); assert "v".equals(cache.get("/a/b/c", "k")); assert "v2".equals(cache.get("/a/b/c", "k2")); cache.endBatch(false); // should be a no op assert "v".equals(cache.get("/a/b/c", "k")); assert "v2".equals(cache.get("/a/b/c", "k2")); } public void testBatchWithoutOngoingTMSuspension() throws Exception { TransactionManager tm = getTransactionManager(cache); assert tm.getTransaction() == null : "Should have no ongoing txs"; cache.startBatch(); cache.put("/a/b/c", "k", "v"); assert tm.getTransaction() == null : "Should have no ongoing txs"; cache.put("/a/b/c", "k2", "v2"); assert getOnDifferentThread(cache, "/a/b/c", "k") == null; assert getOnDifferentThread(cache, "/a/b/c", "k2") == null; try { tm.commit(); // should have no effect } catch (Exception e) { // the TM may barf here ... this is OK. } assert tm.getTransaction() == null : "Should have no ongoing txs"; assert getOnDifferentThread(cache, "/a/b/c", "k") == null; assert getOnDifferentThread(cache, "/a/b/c", "k2") == null; cache.endBatch(true); // should be a no op assert "v".equals(getOnDifferentThread(cache, "/a/b/c", "k")); assert "v2".equals(getOnDifferentThread(cache, "/a/b/c", "k2")); } public void testBatchRollback() throws Exception { TransactionManager tm = getTransactionManager(cache); cache.startBatch(); cache.put("/a/b/c", "k", "v"); cache.put("/a/b/c", "k2", "v2"); assert getOnDifferentThread(cache, "/a/b/c", "k") == null; assert getOnDifferentThread(cache, "/a/b/c", "k2") == null; cache.endBatch(false); assert getOnDifferentThread(cache, "/a/b/c", "k") == null; assert getOnDifferentThread(cache, "/a/b/c", "k2") == null; } private TransactionManager getTransactionManager(Cache c) { return c.getConfiguration().getRuntimeConfig().getTransactionManager(); } @Override public CacheSPI createCache() { UnitTestCacheFactory cf = new UnitTestCacheFactory(); Configuration c = UnitTestConfigurationFactory.createConfiguration(CacheMode.LOCAL); // this should pick up any configured TM for the test c.setNodeLockingScheme(NodeLockingScheme.MVCC); c.setInvocationBatchingEnabled(true); assert c.getTransactionManagerLookupClass() != null : "Should have a transaction manager lookup class attached!!"; return (CacheSPI) cf.createCache(c, getClass()); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/optimistic/0000755000175000017500000000000011675221547025334 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/optimistic/SyncReplTxOptimisticTest.java0000644000175000017500000000073511111047320033140 0ustar moellermoellerpackage org.jboss.cache.api.optimistic; import org.jboss.cache.api.SyncReplTxTest; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.testng.annotations.Test; @Test(groups = {"functional", "jgroups", "transaction", "optimistic"}, testName = "api.optimistic.SyncReplTxOptimisticTest") public class SyncReplTxOptimisticTest extends SyncReplTxTest { public SyncReplTxOptimisticTest() { nodeLockingScheme = NodeLockingScheme.OPTIMISTIC; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/optimistic/NodeMoveOptimisticTest.java0000644000175000017500000000154011111047320032574 0ustar moellermoeller/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.api.optimistic; import org.jboss.cache.api.NodeMoveAPITest; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.testng.annotations.Test; @Test(groups = {"functional", "optimistic"}, testName = "api.optimistic.NodeMoveOptimisticTest") public class NodeMoveOptimisticTest extends NodeMoveAPITest { public NodeMoveOptimisticTest() { nodeLockingScheme = NodeLockingScheme.OPTIMISTIC; } @Override protected boolean isOptimistic() { return true; } @Override public void testLocks() { // no op } @Override public void testLocksDeepMove() { // no op } @Override public void testConcurrency() { // no op } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/optimistic/CacheAPIOptimisticTest.java0000644000175000017500000000104311111047320032413 0ustar moellermoellerpackage org.jboss.cache.api.optimistic; import org.jboss.cache.api.CacheAPITest; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.testng.annotations.Test; /** * Optimistically locked version of {@link org.jboss.cache.api.CacheAPITest} */ @Test(groups = {"functional", "optimistic"}, testName = "api.optimistic.CacheAPIOptimisticTest") public class CacheAPIOptimisticTest extends CacheAPITest { @Override protected NodeLockingScheme getNodeLockingScheme() { return NodeLockingScheme.OPTIMISTIC; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/optimistic/CacheSPIOptimisticTest.java0000644000175000017500000000067111111047320032443 0ustar moellermoellerpackage org.jboss.cache.api.optimistic; import org.jboss.cache.api.CacheSPITest; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.testng.annotations.Test; @Test(groups = {"functional", "optimistic"}, testName = "api.optimistic.CacheSPIOptimisticTest") public class CacheSPIOptimisticTest extends CacheSPITest { public CacheSPIOptimisticTest() { nodeLockingScheme = NodeLockingScheme.OPTIMISTIC; } } ././@LongLink0000000000000000000000000000016200000000000011564 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/optimistic/DeletedChildResurrectionOptimisticTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/optimistic/DeletedChildResurrectionOptimi0000644000175000017500000000101111111047320033325 0ustar moellermoellerpackage org.jboss.cache.api.optimistic; import org.jboss.cache.api.DeletedChildResurrectionTest; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.testng.annotations.Test; @Test(groups = {"functional", "optimistic"}, testName = "api.optimistic.DeletedChildResurrectionOptimisticTest") public class DeletedChildResurrectionOptimisticTest extends DeletedChildResurrectionTest { public DeletedChildResurrectionOptimisticTest() { nodeLockingScheme = NodeLockingScheme.OPTIMISTIC; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/optimistic/NodeAPIOptimisticTest.java0000644000175000017500000000504611120367722032317 0ustar moellermoellerpackage org.jboss.cache.api.optimistic; import org.jboss.cache.Fqn; import org.jboss.cache.api.NodeAPITest; import org.jboss.cache.config.Configuration.NodeLockingScheme; 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.testng.annotations.Test; import java.util.List; import java.util.Set; import org.jboss.cache.CacheSPI; /** * An optimistic version of {@link org.jboss.cache.api.NodeAPITest} */ @Test(groups = {"functional", "optimistic"}, testName = "api.optimistic.NodeAPIOptimisticTest") public class NodeAPIOptimisticTest extends NodeAPITest { protected NodeLockingScheme getNodeLockingScheme() { return NodeLockingScheme.OPTIMISTIC; } protected void assertNodeLockingScheme() { assert cache.getConfiguration().getNodeLockingScheme() == NodeLockingScheme.OPTIMISTIC; boolean interceptorChainOK = false; List chain = cache.getInterceptorChain(); for (CommandInterceptor i : chain) { if (i instanceof PessimisticLockInterceptor) assert false : "Not an optimistic locking chain!!"; if (i instanceof OptimisticNodeInterceptor) interceptorChainOK = true; } assert interceptorChainOK : "Not an optimistic locking chain!!"; } @Override protected void childrenUnderTxCheck() throws Exception { 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"; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/optimistic/SyncReplOptimisticTest.java0000644000175000017500000000070411111047320032620 0ustar moellermoellerpackage org.jboss.cache.api.optimistic; import org.jboss.cache.api.SyncReplTest; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.testng.annotations.Test; @Test(groups = {"functional", "jgroups", "optimistic"}, testName = "api.optimistic.SyncReplOptimisticTest") public class SyncReplOptimisticTest extends SyncReplTest { public SyncReplOptimisticTest() { nodeLockingScheme = NodeLockingScheme.OPTIMISTIC; } } ././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/optimistic/NodeMoveAPIWithCLOptimisticTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/optimistic/NodeMoveAPIWithCLOptimisticTes0000644000175000017500000000103111142045063033076 0ustar moellermoellerpackage org.jboss.cache.api.optimistic; import org.testng.annotations.Test; import org.jboss.cache.config.Configuration; import org.jboss.cache.api.NodeMoveAPIWithCLTest; /** * @author Mircea.Markus@jboss.com */ @Test(groups = {"functional", "optimistic"}, testName = "api.optimistic.NodeMoveAPIWithCLOptimisticTest") public class NodeMoveAPIWithCLOptimisticTest extends NodeMoveAPIWithCLTest { protected Configuration.NodeLockingScheme getNodeLockingScheme() { return Configuration.NodeLockingScheme.OPTIMISTIC; } } ././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/optimistic/NodeReplicatedMoveOptimisticTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/optimistic/NodeReplicatedMoveOptimisticTe0000644000175000017500000000115111111047320033300 0ustar moellermoeller/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.api.optimistic; import org.jboss.cache.api.NodeReplicatedMoveTest; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.testng.annotations.Test; @Test(groups = {"functional", "optimistic"}, testName = "api.optimistic.NodeReplicatedMoveOptimisticTest") public class NodeReplicatedMoveOptimisticTest extends NodeReplicatedMoveTest { public NodeReplicatedMoveOptimisticTest() { nodeLockingScheme = NodeLockingScheme.OPTIMISTIC; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/NodeMoveAPITest.java0000644000175000017500000003260211142045063026706 0ustar moellermoellerpackage org.jboss.cache.api; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.*; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.*; import org.testng.annotations.Test; import javax.transaction.TransactionManager; import java.util.Random; import java.util.concurrent.CountDownLatch; /** * Excercises and tests the new move() api * * @author Manik Surtani * @since 2.0.0 */ @Test(groups = {"functional", "pessimistic"}, testName = "api.NodeMoveAPITest") public class NodeMoveAPITest extends AbstractSingleCacheTest { protected final Log log = LogFactory.getLog(getClass()); 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 static final Object k = "key", vA = "valueA", vB = "valueB", vC = "valueC", vD = "valueD", vE = "valueE"; protected NodeLockingScheme nodeLockingScheme = NodeLockingScheme.PESSIMISTIC; private TransactionManager tm; protected CacheSPI createCache() { // start a single cache instance CacheSPI cache = (CacheSPI) new UnitTestCacheFactory().createCache("configs/local-tx.xml", false, getClass()); cache.getConfiguration().setNodeLockingScheme(nodeLockingScheme); cache.getConfiguration().setFetchInMemoryState(false); cache.getConfiguration().setEvictionConfig(null); configure(cache.getConfiguration()); cache.start(); tm = cache.getTransactionManager(); return cache; } protected void configure(Configuration c) { // to be overridden } public void testBasicMove() { Node rootNode = cache.getRoot(); Node nodeA = rootNode.addChild(A); nodeA.put(k, vA); Node nodeB = rootNode.addChild(B); nodeB.put(k, vB); Node 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; } public void testMoveWithChildren() { Node rootNode = cache.getRoot(); Node nodeA = rootNode.addChild(A); nodeA.put(k, vA); Node nodeB = rootNode.addChild(B); nodeB.put(k, vB); Node nodeC = nodeA.addChild(C); nodeC.put(k, vC); Node nodeD = nodeC.addChild(D); nodeD.put(k, vD); Node 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()); // 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()); } public void testTxCommit() throws Exception { Node rootNode = cache.getRoot(); Node nodeA = rootNode.addChild(A); Node 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()); } public void testTxRollback() throws Exception { Node rootNode = cache.getRoot(); Node nodeA = rootNode.addChild(A); Node 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 (nodeLockingScheme == NodeLockingScheme.PESSIMISTIC) { 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()); } public void testLocksDeepMove() throws Exception { Node rootNode = cache.getRoot(); Node nodeA = rootNode.addChild(A); Node nodeB = nodeA.addChild(B); Node nodeD = nodeB.addChild(D); Node nodeC = rootNode.addChild(C); Node nodeE = nodeC.addChild(E); assertEquals(0, cache.getNumberOfLocksHeld()); tm.begin(); cache.move(nodeC.getFqn(), nodeB.getFqn()); checkLocksDeep(); tm.commit(); assertEquals(0, cache.getNumberOfLocksHeld()); } public void testLocks() throws Exception { Node rootNode = cache.getRoot(); Node nodeA = rootNode.addChild(A); Node nodeB = nodeA.addChild(B); Node nodeC = rootNode.addChild(C); assertEquals(0, cache.getNumberOfLocksHeld()); tm.begin(); cache.move(nodeC.getFqn(), nodeB.getFqn()); checkLocks(); tm.commit(); assertNoLocks(); } protected void checkLocks() { assertEquals("ROOT should have a RL, nodeC should have a RL, nodeA should have a RL, nodeB should have a WL", 4, cache.getNumberOfLocksHeld()); } protected void checkLocksDeep() { 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", 6, cache.getNumberOfLocksHeld()); } protected void assertNoLocks() { assertEquals(0, cache.getNumberOfLocksHeld()); } public void testConcurrency() throws InterruptedException { Node rootNode = cache.getRoot(); final int N = 3;// number of threads final int loops = 1 << 4;// 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(Thread.currentThread().getName() + " -Mover-" + i) { public void run() { try { latch.await(); } catch (InterruptedException e) { } for (int counter = 0; counter < loops; counter++) { try { cache.move(NODE_X.getFqn(), NODES[r.nextInt(NODES.length)].getFqn()); } catch (NodeNotExistsException e) { // this may happen ... } TestingUtil.sleepRandom(50); try { cache.move(NODE_Y.getFqn(), NODES[r.nextInt(NODES.length)].getFqn()); } catch (NodeNotExistsException e) { // this may happen ... } TestingUtil.sleepRandom(50); } } }; 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); } public void testMoveInSamePlace() { Node rootNode = cache.getRoot(); 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); assert 0 == cache.getNumberOfLocksHeld(); } protected boolean isOptimistic() { return false; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/ResidentNodesTest.java0000644000175000017500000002233111131613416027405 0ustar moellermoellerpackage org.jboss.cache.api; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.eviction.LRUAlgorithmConfig; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.util.internals.EvictionController; import static org.testng.Assert.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.TransactionManager; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.util.TestingUtil; /** * Tester class for Node.isResident functionality. * * @author Mircea Markus * @since 2.1.0 */ @Test(groups = {"functional", "pessimistic"}, sequential = true, testName = "api.ResidentNodesTest") 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 = UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); cacheConfig.setCacheMode(Configuration.CacheMode.LOCAL); cache = (CacheSPI) new UnitTestCacheFactory().createCache(cacheConfig, false, getClass()); cache.getConfiguration().getEvictionConfig().setWakeupInterval(1000); 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); LRUAlgorithmConfig lruConfig = new LRUAlgorithmConfig(); lruConfig.setMaxAge(100000000); lruConfig.setTimeToLive(100000000); lruConfig.setMaxNodes(3); evRegConfig.setEvictionAlgorithmConfig(lruConfig); evConfig.addEvictionRegionConfig(evRegConfig); //end setting up region stuff } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache); if (caches != null) TestingUtil.killCaches(caches); cache = null; caches = null; evController = null; } /** * 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 = UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); config.setCacheMode(Configuration.CacheMode.LOCAL); config.setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); CacheSPI cache = (CacheSPI) new UnitTestCacheFactory().createCache(config, true, getClass()); 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-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/0000755000175000017500000000000011675221563024076 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/repeatable_read/0000755000175000017500000000000011675221556027177 5ustar moellermoeller././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/repeatable_read/NodeMoveWithCLMvccTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/repeatable_read/NodeMoveWithCLMvccTe0000644000175000017500000000107011142045063032773 0ustar moellermoellerpackage org.jboss.cache.api.mvcc.repeatable_read; import org.jboss.cache.config.Configuration; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.api.NodeMoveAPIWithCLTest; import org.testng.annotations.Test; /** * @author Mircea.Markus@jboss.com */ @Test(groups = {"functional", "mvcc"}, testName = "api.mvcc.repeatable_read.NodeMoveWithCLMvccTest") public class NodeMoveWithCLMvccTest extends NodeMoveAPIWithCLTest { @Override protected void configure(Configuration c) { c.setIsolationLevel(IsolationLevel.REPEATABLE_READ); } }jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/repeatable_read/PassivationTest.java0000644000175000017500000000325511131613416033172 0ustar moellermoellerpackage org.jboss.cache.api.mvcc.repeatable_read; import org.jboss.cache.api.mvcc.LockAssert; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.invocation.InvocationContextContainer; import org.jboss.cache.loader.testloaders.DummySharedInMemoryCacheLoader; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.lock.LockManager; import org.jboss.cache.passivation.PassivationTestsBase; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "api.mvcc.repeatable_read.PassivationTest") public class PassivationTest extends PassivationTestsBase { @Override protected void configureCache() throws Exception { cache.getConfiguration().setNodeLockingScheme(NodeLockingScheme.MVCC); cache.getConfiguration().setIsolationLevel(IsolationLevel.REPEATABLE_READ); CacheLoaderConfig clc = UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", DummySharedInMemoryCacheLoader.class.getName(), "bin=" + Thread.currentThread().getName() + getClass().getName(), false, true, false, false, false); clc.setPassivation(true); cache.getConfiguration().setCacheLoaderConfig(clc); } @AfterMethod public void postTest() { ComponentRegistry cr = TestingUtil.extractComponentRegistry(cache); LockAssert.assertNoLocks(cr.getComponent(LockManager.class), cr.getComponent(InvocationContextContainer.class)); } }././@LongLink0000000000000000000000000000016200000000000011564 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/repeatable_read/StateTransferConcurrencyTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/repeatable_read/StateTransferConcurr0000644000175000017500000000121611111047320033217 0ustar moellermoellerpackage org.jboss.cache.api.mvcc.repeatable_read; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.lock.IsolationLevel; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "api.mvcc.repeatable_read.StateTransferConcurrencyTest") public class StateTransferConcurrencyTest extends org.jboss.cache.statetransfer.StateTransferConcurrencyTest { @Override protected void additionalConfiguration(Configuration c) { c.setNodeLockingScheme(NodeLockingScheme.MVCC); c.setIsolationLevel(IsolationLevel.REPEATABLE_READ); } }././@LongLink0000000000000000000000000000016200000000000011564 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/repeatable_read/ConcurrentRepeatableReadTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/repeatable_read/ConcurrentRepeatable0000644000175000017500000002535211354162635033234 0ustar moellermoeller/* * JBoss, Home of Professional Open Source. * Copyright 2009, 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.api.mvcc.repeatable_read; import static org.testng.AssertJUnit.assertEquals; import java.io.Serializable; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import javax.transaction.Status; import javax.transaction.TransactionManager; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.AbstractSingleCacheTest; 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.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.Test; /** * ConcurrentRepeatableReadTest. * * @author Galder Zamarreño */ @Test(groups = { "functional" }, testName = "api.mvcc.repeatable_read.ConcurrentRepeatableReadTest") public class ConcurrentRepeatableReadTest extends AbstractSingleCacheTest { static final Log log = LogFactory.getLog(ConcurrentRepeatableReadTest.class); final ExecutorService executorService = Executors.newCachedThreadPool(); @Override protected CacheSPI createCache() throws Exception { UnitTestCacheFactory factory = new UnitTestCacheFactory(); Configuration cfg = UnitTestConfigurationFactory.createConfiguration(CacheMode.LOCAL); cfg.setIsolationLevel(IsolationLevel.REPEATABLE_READ); CacheSPI cache = (CacheSPI) factory.createCache(cfg, true, getClass()); return cache; } public void testConcurrentUpdatesNoWriteSkew(Method m) throws Exception { final int nbWriters = 10; log.debug(m.getName()); init(); CyclicBarrier barrier = new CyclicBarrier(nbWriters + 1); List> futures = new ArrayList>(nbWriters); for (int i = 0; i < nbWriters; i++) { log.debug("Schedule execution"); Future future = executorService.submit(new IncrementNoWriteSkew(barrier)); futures.add(future); } barrier.await(); // wait for all threads to be ready barrier.await(); // wait for all threads to finish log.debug("All threads finished, let's shutdown the executor and check whether any exceptions were reported"); for (Future future : futures) future.get(); assertEquals(nbWriters, get()); } public void testConcurrentUpdatesWriteSkew(Method m) throws Exception { final int nbWriters = 10; CacheSPI cache = null; try { log.debug(m.getName()); UnitTestCacheFactory factory = new UnitTestCacheFactory(); Configuration cfg = UnitTestConfigurationFactory.createConfiguration(CacheMode.LOCAL); cfg.setIsolationLevel(IsolationLevel.REPEATABLE_READ); cfg.setWriteSkewCheck(true); cache = (CacheSPI) factory.createCache(cfg, false, getClass()); cache.start(); assert cache.getConfiguration().isWriteSkewCheck(); init(); CyclicBarrier barrier = new CyclicBarrier(nbWriters + 1); List> futures = new ArrayList>(nbWriters); for (int i = 0; i < nbWriters; i++) { log.debug("Schedule execution"); Future future = executorService.submit(new IncrementWriteSkew(barrier)); futures.add(future); } barrier.await(); // wait for all threads to be ready barrier.await(); // wait for all threads to finish log.debug("All threads finished, let's shutdown the executor and check whether any exceptions were reported"); for (Future future : futures) future.get(); } finally { if (cache != null) TestingUtil.killCaches(cache); } } public void testConcurrentCreateRemove() throws Exception { final int totalElement = 100; final int totalTimes = 20; int writer = 10; int remover = 5; final CountDownLatch startSignalWriter = new CountDownLatch(1); final CountDownLatch startSignalOthers = new CountDownLatch(1); final CountDownLatch doneSignal = new CountDownLatch(writer + remover); final List errors = Collections.synchronizedList(new ArrayList()); for (int i = 0; i < writer; i++) { final int index = i; Thread thread = new Thread() { public void run() { try { startSignalWriter.await(); for (int j = 0; j < totalTimes; j++) { for (int i = 0; i < totalElement; i++) { cache.put(Fqn.fromElements("key" + i), "key" + i, "value" + i); if (index == 0 && j == 0) { // The cache is full, we can launch the others startSignalOthers.countDown(); } } sleep(50); } } catch (Exception e) { errors.add(e); } finally { doneSignal.countDown(); } } }; thread.start(); } startSignalWriter.countDown(); for (int i = 0; i < remover; i++) { Thread thread = new Thread() { public void run() { try { startSignalOthers.await(); for (int j = 0; j < totalTimes; j++) { for (int i = 0; i < totalElement; i++) { cache.removeNode(Fqn.fromElements("key" + i)); } sleep(50); } } catch (Exception e) { errors.add(e); } finally { doneSignal.countDown(); } } }; thread.start(); } doneSignal.await(); if (!errors.isEmpty()) { for (Exception e : errors) { e.printStackTrace(); } throw errors.get(0); } } private void init() throws Exception { TransactionManager tx = getTm(); tx.begin(); try { cache.put("/foo/mynode", "scalar", 0); } catch (Exception e) { tx.setRollbackOnly(); throw e; } finally { if (tx.getStatus() == Status.STATUS_ACTIVE) tx.commit(); else tx.rollback(); } } private void incrementNoWriteSkew() throws Exception { TransactionManager tx = getTm(); tx.begin(); try { cache.getInvocationContext().getOptionOverrides().setForceWriteLock(true); int tmp = (Integer) cache.get("/foo/mynode", "scalar"); tmp++; cache.put("/foo/mynode", "scalar", tmp); } catch (Exception e) { log.error("Unexpected", e); tx.setRollbackOnly(); throw e; } finally { if (tx.getStatus() == Status.STATUS_ACTIVE) tx.commit(); else tx.rollback(); } } private void incrementWriteSkew() throws Exception { TransactionManager tx = getTm(); tx.begin(); try { cache.put("/foo/mynode", "_lockthisplease_", "_lockthisplease_"); } catch (Exception e) { log.error("Unexpected", e); tx.setRollbackOnly(); throw e; } finally { if (tx.getStatus() == Status.STATUS_ACTIVE) tx.commit(); else tx.rollback(); } } public int get() throws Exception { TransactionManager tx = getTm(); tx.begin(); try { int ret = (Integer) cache.get("/foo/mynode", "scalar"); return ret; } catch (Exception e) { tx.setRollbackOnly(); throw e; } finally { if (tx.getStatus() == Status.STATUS_ACTIVE) tx.commit(); else tx.rollback(); } } private TransactionManager getTm() { return cache.getConfiguration().getRuntimeConfig().getTransactionManager(); } class IncrementNoWriteSkew implements Callable { private final CyclicBarrier barrier; public IncrementNoWriteSkew(CyclicBarrier barrier) { this.barrier = barrier; } public Void call() throws Exception { try { log.debug("Wait for all executions paths to be ready to perform calls"); barrier.await(); incrementNoWriteSkew(); return null; } finally { log.debug("Wait for all execution paths to finish"); barrier.await(); } } } class IncrementWriteSkew implements Callable { private final CyclicBarrier barrier; public IncrementWriteSkew(CyclicBarrier barrier) { this.barrier = barrier; } public Void call() throws Exception { try { log.debug("Wait for all executions paths to be ready to perform calls"); barrier.await(); incrementWriteSkew(); return null; } finally { log.debug("Wait for all execution paths to finish"); barrier.await(); } } } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/repeatable_read/CacheAPIMVCCTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/repeatable_read/CacheAPIMVCCTest.jav0000644000175000017500000000524311364023324032537 0ustar moellermoellerpackage org.jboss.cache.api.mvcc.repeatable_read; import org.jboss.cache.Fqn; import org.jboss.cache.api.CacheAPITest; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.lock.IsolationLevel; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; /** * MVCC version of {@link org.jboss.cache.api.CacheAPITest} */ @Test(groups = {"functional", "mvcc"}, testName = "api.mvcc.repeatable_read.CacheAPIMVCCTest") public class CacheAPIMVCCTest extends CacheAPITest { @Override protected void configure(Configuration c) { super.configure(c); c.setIsolationLevel(IsolationLevel.REPEATABLE_READ); } @Override protected NodeLockingScheme getNodeLockingScheme() { return NodeLockingScheme.MVCC; } public void testWriteSkewOnRemovalOfNullNode() throws Exception { TransactionManager tm = cache.getTransactionManager(); tm.begin(); cache.getNode("/a"); Transaction tx = tm.suspend(); cache.put("/a", "k", "v2"); assert cache.get("/a", "k").equals("v2"); tm.resume(tx); cache.removeNode("/a"); tx.commit(); assert cache.getNode("/a") == null; // this fails } public void testRemoveAndOperateOnUnexistingNode() throws Exception { TransactionManager tm = cache.getTransactionManager(); tm.begin(); cache.removeNode("/a"); cache.clearData("/a"); tm.commit(); assert cache.getNode("/a") == null; // this fails tm.begin(); cache.removeNode("/a"); cache.getNode("/a"); tm.commit(); assert cache.getNode("/a") == null; // this fails tm.begin(); cache.removeNode("/a"); cache.get("/a", "boo"); tm.commit(); assert cache.getNode("/a") == null; // this fails tm.begin(); cache.removeNode("/a"); cache.remove("/a", "boo"); tm.commit(); assert cache.getNode("/a") == null; // this fails tm.begin(); cache.removeNode("/a"); cache.evict(Fqn.fromString("/a")); tm.commit(); assert cache.getNode("/a") == null; // this fails tm.begin(); cache.removeNode("/a"); cache.evict(Fqn.fromString("/a")); tm.commit(); assert cache.getNode("/a") == null; // this fails tm.begin(); cache.removeNode("/a"); cache.move("/a", "/b"); tm.commit(); assert cache.getNode("/a") == null; // this fails tm.begin(); cache.removeNode("/a"); cache.removeRegion(Fqn.fromString("/a")); tm.commit(); assert cache.getNode("/a") == null; // this fails } }././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/repeatable_read/SyncReplMvccTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/repeatable_read/SyncReplMvccTest.jav0000644000175000017500000000122311111047320033063 0ustar moellermoellerpackage org.jboss.cache.api.mvcc.repeatable_read; import org.jboss.cache.api.SyncReplTest; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.lock.IsolationLevel; import org.testng.annotations.Test; @Test(groups = {"functional", "jgroups", "mvcc"}, testName = "api.mvcc.repeatable_read.SyncReplMvccTest") public class SyncReplMvccTest extends SyncReplTest { public SyncReplMvccTest() { nodeLockingScheme = NodeLockingScheme.MVCC; } @Override protected void configure(Configuration c) { c.setIsolationLevel(IsolationLevel.REPEATABLE_READ); } } ././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/repeatable_read/RepeatableReadLockTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/repeatable_read/RepeatableReadLockTe0000644000175000017500000000433011120367722033053 0ustar moellermoellerpackage org.jboss.cache.api.mvcc.repeatable_read; import org.jboss.cache.api.mvcc.LockTestBase; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import org.jboss.cache.Cache; @Test(groups = {"functional", "mvcc"}, testName = "api.mvcc.repeatable_read.RepeatableReadLockTest") public class RepeatableReadLockTest extends LockTestBase { public RepeatableReadLockTest() { repeatableRead = true; } public void testRepeatableReadWithRemove() throws Exception { cache.put(AB, "k", "v"); tm.begin(); assert cache.getNode(AB) != null; Transaction reader = tm.suspend(); tm.begin(); assert cache.removeNode(AB); assert cache.getNode(AB) == null; tm.commit(); assert cache.getNode(AB) == null; tm.resume(reader); assert cache.getNode(AB) != null; assert "v".equals(cache.get(AB, "k")); tm.commit(); assert cache.getNode(AB) == null; assertNoLocks(); } public void testRepeatableReadWithEvict() throws Exception { cache.put(AB, "k", "v"); tm.begin(); assert cache.getNode(AB) != null; Transaction reader = tm.suspend(); tm.begin(); cache.evict(AB); assert cache.getNode(AB) == null; tm.commit(); assert cache.getNode(AB) == null; tm.resume(reader); assert cache.getNode(AB) != null; assert "v".equals(cache.get(AB, "k")); tm.commit(); assert cache.getNode(AB) == null; assertNoLocks(); } public void testRepeatableReadWithNull() throws Exception { assert cache.getNode(AB) == null; tm.begin(); assert cache.getNode(AB) == null; Transaction reader = tm.suspend(); tm.begin(); cache.put(AB, "k", "v"); assert cache.getNode(AB) != null; assert "v".equals(cache.get(AB, "k")); tm.commit(); assert cache.getNode(AB) != null; assert "v".equals(cache.get(AB, "k")); tm.resume(reader); assert cache.getNode(AB) == null; assert cache.get(AB, "k") == null; tm.commit(); assert cache.getNode(AB) != null; assert "v".equals(cache.get(AB, "k")); assertNoLocks(); } } ././@LongLink0000000000000000000000000000016000000000000011562 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/repeatable_read/NodeReplicatedMoveMvccTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/repeatable_read/NodeReplicatedMoveMv0000644000175000017500000000147111120367722033130 0ustar moellermoeller/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.api.mvcc.repeatable_read; import org.jboss.cache.api.NodeReplicatedMoveTest; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.lock.IsolationLevel; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "api.mvcc.repeatable_read.NodeReplicatedMoveMvccTest") public class NodeReplicatedMoveMvccTest extends NodeReplicatedMoveTest { public NodeReplicatedMoveMvccTest() { nodeLockingScheme = NodeLockingScheme.MVCC; } @Override protected void configure(Configuration c) { c.setIsolationLevel(IsolationLevel.REPEATABLE_READ); } } ././@LongLink0000000000000000000000000000016200000000000011564 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/repeatable_read/RepeatableReadLockParentTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/repeatable_read/RepeatableReadLockPa0000644000175000017500000000200611111047320033026 0ustar moellermoellerpackage org.jboss.cache.api.mvcc.repeatable_read; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "api.mvcc.repeatable_read.RepeatableReadLockParentTest") public class RepeatableReadLockParentTest extends RepeatableReadLockTest { public RepeatableReadLockParentTest() { lockParentForChildInsertRemove = true; } @Override public void testOverwritingOnInsert() { // no op since a locked parent makes this test irrelevant. } @Override public void testOverwritingOnInsert2() { // no op since a locked parent makes this test irrelevant. } @Override public void testOverwritingOnInsert3() { // no op since a locked parent makes this test irrelevant. } @Override public void testConcurrentInsertRemove1() { // no op since a locked parent makes this test irrelevant. } @Override public void testConcurrentInsertRemove2() { // no op since a locked parent makes this test irrelevant. } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/repeatable_read/SyncReplTxMvccTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/repeatable_read/SyncReplTxMvccTest.j0000644000175000017500000000125411111047320033054 0ustar moellermoellerpackage org.jboss.cache.api.mvcc.repeatable_read; import org.jboss.cache.api.SyncReplTxTest; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.lock.IsolationLevel; import org.testng.annotations.Test; @Test(groups = {"functional", "jgroups", "transaction", "mvcc"}, testName = "api.mvcc.repeatable_read.SyncReplTxMvccTest") public class SyncReplTxMvccTest extends SyncReplTxTest { public SyncReplTxMvccTest() { nodeLockingScheme = NodeLockingScheme.MVCC; } @Override protected void configure(Configuration c) { c.setIsolationLevel(IsolationLevel.REPEATABLE_READ); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/repeatable_read/WriteSkewTest.java0000644000175000017500000001506411131613416032617 0ustar moellermoellerpackage org.jboss.cache.api.mvcc.repeatable_read; import org.jboss.cache.Cache; import org.jboss.cache.Fqn; import org.jboss.cache.api.mvcc.LockAssert; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.invocation.InvocationContextContainer; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.lock.LockManager; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.jboss.cache.util.TestingUtil; 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.Collection; import java.util.HashSet; import java.util.Set; import java.util.concurrent.CountDownLatch; import org.jboss.cache.UnitTestCacheFactory; @Test(groups = {"functional", "mvcc"}, sequential = true, testName = "api.mvcc.repeatable_read.WriteSkewTest") public class WriteSkewTest { protected Cache cache; protected TransactionManager tm; protected Fqn A = Fqn.fromString("/a"); protected Fqn AB = Fqn.fromString("/a/b"); protected Fqn ABC = Fqn.fromString("/a/b/c"); protected Fqn ABCD = Fqn.fromString("/a/b/c/d"); protected LockManager lockManager; protected InvocationContextContainer icc; protected boolean repeatableRead = true; @BeforeMethod public void setUp() { cache = new UnitTestCacheFactory().createCache(UnitTestConfigurationFactory.createConfiguration(CacheMode.LOCAL), false, getClass()); cache.getConfiguration().setNodeLockingScheme(NodeLockingScheme.MVCC); cache.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cache.getConfiguration().setIsolationLevel(repeatableRead ? IsolationLevel.REPEATABLE_READ : IsolationLevel.READ_COMMITTED); // reduce lock acquisition timeout so this doesn't take forever to run cache.getConfiguration().setLockAcquisitionTimeout(200); // 200 ms } @AfterMethod public void tearDown() { TestingUtil.killCaches(cache); cache = null; icc = null; lockManager = null; tm = null; } private void postStart() { lockManager = TestingUtil.extractComponentRegistry(cache).getComponent(LockManager.class); icc = TestingUtil.extractComponentRegistry(cache).getComponent(InvocationContextContainer.class); tm = TestingUtil.extractComponentRegistry(cache).getComponent(TransactionManager.class); } protected void assertNoLocks() { LockAssert.assertNoLocks(lockManager, icc); } public void testDontCheckWriteSkew() throws Exception { cache.getConfiguration().setWriteSkewCheck(false); cache.start(); postStart(); doTest(true); } public void testCheckWriteSkew() throws Exception { cache.getConfiguration().setWriteSkewCheck(true); cache.start(); postStart(); doTest(false); } private void doTest(final boolean allowWriteSkew) throws Exception { if (repeatableRead) { cache.put(AB, "k", "v"); final Set w1exceptions = new HashSet(); final Set w2exceptions = new HashSet(); final CountDownLatch w1Signal = new CountDownLatch(1); final CountDownLatch w2Signal = new CountDownLatch(1); final CountDownLatch threadSignal = new CountDownLatch(2); Thread w1 = new Thread("Writer-1") { public void run() { boolean didCoundDown = false; try { tm.begin(); assert "v".equals(cache.get(AB, "k")); threadSignal.countDown(); didCoundDown = true; w1Signal.await(); cache.put(AB, "k", "v2"); tm.commit(); } catch (Exception e) { w1exceptions.add(e); } finally { if (!didCoundDown) threadSignal.countDown(); } } }; Thread w2 = new Thread("Writer-2") { public void run() { boolean didCoundDown = false; try { tm.begin(); assert "v".equals(cache.get(AB, "k")); threadSignal.countDown(); didCoundDown = true; w2Signal.await(); cache.put(AB, "k", "v3"); tm.commit(); } catch (Exception e) { w2exceptions.add(e); // the exception will be thrown when doing a cache.put(). We should make sure we roll back the tx to release locks. if (!allowWriteSkew) { try { tm.rollback(); } catch (SystemException e1) { // do nothing. } } } finally { if (!didCoundDown) threadSignal.countDown(); } } }; w1.start(); w2.start(); threadSignal.await(); // now. both txs have read. // let tx1 start writing w1Signal.countDown(); w1.join(); w2Signal.countDown(); w2.join(); if (allowWriteSkew) { // should have no exceptions!! throwExceptions(w1exceptions, w2exceptions); assert w2exceptions.size() == 0; assert w1exceptions.size() == 0; assert "v3".equals(cache.get(AB, "k")) : "W2 should have overwritten W1's work!"; } else { // there should be a single exception from w2. assert w2exceptions.size() == 1; throwExceptions(w1exceptions); assert w1exceptions.size() == 0; assert "v2".equals(cache.get(AB, "k")) : "W2 should NOT have overwritten W1's work!"; } assertNoLocks(); } } private void throwExceptions(Collection... exceptions) throws Exception { for (Collection ce : exceptions) { for (Exception e : ce) throw e; } } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/repeatable_read/NodeMoveMvccTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/repeatable_read/NodeMoveMvccTest.jav0000644000175000017500000000120211111047320033035 0ustar moellermoeller/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.api.mvcc.repeatable_read; import org.jboss.cache.api.mvcc.NodeMoveMvccTestBase; import org.jboss.cache.config.Configuration; import org.jboss.cache.lock.IsolationLevel; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "api.mvcc.repeatable_read.NodeMoveMvccTest") public class NodeMoveMvccTest extends NodeMoveMvccTestBase { @Override protected void configure(Configuration c) { c.setIsolationLevel(IsolationLevel.REPEATABLE_READ); } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/repeatable_read/StateTransferTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/repeatable_read/StateTransferTest.ja0000644000175000017500000000121511111047320033113 0ustar moellermoellerpackage org.jboss.cache.api.mvcc.repeatable_read; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.statetransfer.StateTransfer200Test; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "api.mvcc.repeatable_read.StateTransferTest") public class StateTransferTest extends StateTransfer200Test { @Override protected void additionalConfiguration(Configuration c) { c.setNodeLockingScheme(NodeLockingScheme.MVCC); c.setIsolationLevel(IsolationLevel.REPEATABLE_READ); } }././@LongLink0000000000000000000000000000016600000000000011570 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/repeatable_read/DeletedChildResurrectionMvccTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/repeatable_read/DeletedChildResurrec0000644000175000017500000000133011111047320033120 0ustar moellermoellerpackage org.jboss.cache.api.mvcc.repeatable_read; import org.jboss.cache.api.DeletedChildResurrectionTest; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.lock.IsolationLevel; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "api.mvcc.repeatable_read.DeletedChildResurrectionMvccTest") public class DeletedChildResurrectionMvccTest extends DeletedChildResurrectionTest { public DeletedChildResurrectionMvccTest() { nodeLockingScheme = NodeLockingScheme.MVCC; } @Override protected void configure(Configuration c) { c.setIsolationLevel(IsolationLevel.REPEATABLE_READ); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/repeatable_read/NodeAPIMVCCTest.java0000644000175000017500000000422411120367722032563 0ustar moellermoellerpackage org.jboss.cache.api.mvcc.repeatable_read; import org.jboss.cache.CacheSPI; import org.jboss.cache.api.NodeAPITest; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.interceptors.MVCCLockingInterceptor; import org.jboss.cache.interceptors.OptimisticNodeInterceptor; import org.jboss.cache.interceptors.PessimisticLockInterceptor; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.lock.IsolationLevel; import org.testng.annotations.Test; import java.util.List; /** * An MVCC version of {@link org.jboss.cache.api.NodeAPITest} */ @Test(groups = {"functional", "mvcc"}, testName = "api.mvcc.repeatable_read.NodeAPIMVCCTest") public class NodeAPIMVCCTest extends NodeAPITest { protected NodeLockingScheme getNodeLockingScheme() { return NodeLockingScheme.MVCC; } @Override protected void configure(Configuration c) { c.setIsolationLevel(IsolationLevel.REPEATABLE_READ); c.setLockParentForChildInsertRemove(true); } protected void assertNodeLockingScheme() { assert cache.getConfiguration().getNodeLockingScheme() == NodeLockingScheme.MVCC; boolean interceptorChainOK = false; List chain = cache.getInterceptorChain(); for (CommandInterceptor i : chain) { if (i instanceof PessimisticLockInterceptor) assert false : "Not an MVCC locking chain!!"; if (i instanceof OptimisticNodeInterceptor) assert false : "Not an MVCC locking chain!!"; if (i instanceof MVCCLockingInterceptor) interceptorChainOK = true; } assert interceptorChainOK : "Not an MVCC locking chain!!"; } @Override public void testLocking() { // no op - this is tested separately } @Override protected void childrenUnderTxCheck() throws Exception { assert cache.getNode(A_B) != null; assert cache.getNode(A_C) != null; assert cache.getInvocationContext().getLocks().contains(A); assert cache.getInvocationContext().getLocks().contains(A_B); assert cache.getInvocationContext().getLocks().contains(A_C); } }jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/repeatable_read/CacheLoaderTest.java0000644000175000017500000000236111121730272033020 0ustar moellermoellerpackage org.jboss.cache.api.mvcc.repeatable_read; import org.jboss.cache.CacheSPI; import org.jboss.cache.api.mvcc.LockAssert; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.invocation.InvocationContextContainer; import org.jboss.cache.loader.DummyInMemoryCacheLoaderTest; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.lock.LockManager; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "api.mvcc.repeatable_read.CacheLoaderTest") public class CacheLoaderTest extends DummyInMemoryCacheLoaderTest { @Override protected void configureCache(CacheSPI cache) throws Exception { super.configureCache(cache); cache.getConfiguration().setNodeLockingScheme(NodeLockingScheme.MVCC); cache.getConfiguration().setIsolationLevel(IsolationLevel.REPEATABLE_READ); } @AfterMethod public void postTest() { ComponentRegistry cr = TestingUtil.extractComponentRegistry(cache); LockAssert.assertNoLocks(cr.getComponent(LockManager.class), cr.getComponent(InvocationContextContainer.class)); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/NodeMoveMvccTestBase.java0000644000175000017500000000740211120367722030715 0ustar moellermoellerpackage org.jboss.cache.api.mvcc; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.api.NodeMoveAPITest; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.invocation.InvocationContextContainer; import org.jboss.cache.lock.LockManager; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}) public abstract class NodeMoveMvccTestBase extends NodeMoveAPITest { static final Fqn A_B = Fqn.fromRelativeFqn(A, B); static final Fqn A_B_C = Fqn.fromRelativeFqn(A_B, C); static final Fqn A_B_C_E = Fqn.fromRelativeFqn(A_B_C, E); static final Fqn A_B_D = Fqn.fromRelativeFqn(A_B, D); static final Fqn C_E = Fqn.fromRelativeFqn(C, E); static final Fqn D_B = Fqn.fromRelativeFqn(D, B); static final Fqn D_B_C = Fqn.fromRelativeFqn(D_B, C); public NodeMoveMvccTestBase() { nodeLockingScheme = NodeLockingScheme.MVCC; } @Override protected void checkLocks() { ComponentRegistry cr = TestingUtil.extractComponentRegistry(cache); LockManager lm = cr.getComponent(LockManager.class); InvocationContextContainer icc = cr.getComponent(InvocationContextContainer.class); LockAssert.assertNotLocked(A, lm, icc); LockAssert.assertNotLocked(Fqn.ROOT, lm, icc); LockAssert.assertLocked(C, lm, icc); LockAssert.assertLocked(A_B, lm, icc); LockAssert.assertLocked(A_B_C, lm, icc); } @Override protected void checkLocksDeep() { ComponentRegistry cr = TestingUtil.extractComponentRegistry(cache); LockManager lm = cr.getComponent(LockManager.class); InvocationContextContainer icc = cr.getComponent(InvocationContextContainer.class); LockAssert.assertNotLocked(A, lm, icc); LockAssert.assertNotLocked(Fqn.ROOT, lm, icc); LockAssert.assertNotLocked(A_B_D, lm, icc); // /a/b, /c, /c/e, /a/b/c and /a/b/c/e should all be locked. LockAssert.assertLocked(A_B, lm, icc); LockAssert.assertLocked(C, lm, icc); LockAssert.assertLocked(C_E, lm, icc); LockAssert.assertLocked(A_B_C, lm, icc); LockAssert.assertLocked(A_B_C_E, lm, icc); } @Override protected void assertNoLocks() { ComponentRegistry cr = TestingUtil.extractComponentRegistry(cache); LockManager lm = cr.getComponent(LockManager.class); InvocationContextContainer icc = cr.getComponent(InvocationContextContainer.class); LockAssert.assertNoLocks(lm, icc); } public void testNonexistentSource() { cache.put(A_B_C, "k", "v"); assert "v".equals(cache.get(A_B_C, "k")); assert 1 == cache.getNode(A_B).getChildren().size(); assert cache.getNode(A_B).getChildrenNames().contains(C.getLastElement()); assert !cache.getNode(A_B).getChildrenNames().contains(D.getLastElement()); cache.move(D, A_B); assert "v".equals(cache.get(A_B_C, "k")); assert 1 == cache.getNode(A_B).getChildren().size(); assert cache.getNode(A_B).getChildrenNames().contains(C.getLastElement()); assert !cache.getNode(A_B).getChildrenNames().contains(D.getLastElement()); } public void testNonexistentTarget() { cache.put(A_B_C, "k", "v"); assert "v".equals(cache.get(A_B_C, "k")); assert 1 == cache.getNode(A_B).getChildren().size(); assert cache.getNode(A_B).getChildrenNames().contains(C.getLastElement()); assert null == cache.getNode(D); cache.move(A_B, D); assert null == cache.getNode(A_B_C); assert null == cache.getNode(A_B); assert null != cache.getNode(D); assert null != cache.getNode(D_B); assert null != cache.getNode(D_B_C); assert "v".equals(cache.get(D_B_C, "k")); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/PersistingTransientStateTest.java0000644000175000017500000000063711111047320032605 0ustar moellermoellerpackage org.jboss.cache.api.mvcc; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.testng.annotations.Test; @Test(groups = "functional", testName = "api.mvcc.PersistingTransientStateTest") public class PersistingTransientStateTest extends org.jboss.cache.statetransfer.PersistingTransientStateTest { public PersistingTransientStateTest() { nls = NodeLockingScheme.MVCC; } }jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/read_committed/0000755000175000017500000000000011675221561027054 5ustar moellermoeller././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/read_committed/NodeMoveWithCLMvccTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/read_committed/NodeMoveWithCLMvccTes0000644000175000017500000000106611142045063033044 0ustar moellermoellerpackage org.jboss.cache.api.mvcc.read_committed; import org.jboss.cache.config.Configuration; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.api.NodeMoveAPIWithCLTest; import org.testng.annotations.Test; /** * @author Mircea.Markus@jboss.com */ @Test(groups = {"functional", "mvcc"}, testName = "api.mvcc.read_committed.NodeMoveWithCLMvccTest") public class NodeMoveWithCLMvccTest extends NodeMoveAPIWithCLTest { @Override protected void configure(Configuration c) { c.setIsolationLevel(IsolationLevel.READ_COMMITTED); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/read_committed/PassivationTest.java0000644000175000017500000000331111131613416033044 0ustar moellermoellerpackage org.jboss.cache.api.mvcc.read_committed; import org.jboss.cache.api.mvcc.LockAssert; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.invocation.InvocationContextContainer; import org.jboss.cache.loader.testloaders.DummySharedInMemoryCacheLoader; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.lock.LockManager; import org.jboss.cache.passivation.PassivationTestsBase; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "api.mvcc.read_committed.PassivationTest") public class PassivationTest extends PassivationTestsBase { @Override protected void configureCache() throws Exception { cache.getConfiguration().setNodeLockingScheme(NodeLockingScheme.MVCC); cache.getConfiguration().setIsolationLevel(IsolationLevel.READ_COMMITTED); String binName = "bin-" + Thread.currentThread().getName(); CacheLoaderConfig clc = UnitTestConfigurationFactory.buildSingleCacheLoaderConfig(false, "", DummySharedInMemoryCacheLoader.class.getName(), "debug=true\nbin=" + binName, false, true, false, false, false); clc.setPassivation(true); cache.getConfiguration().setCacheLoaderConfig(clc); } @AfterMethod public void postTest() { ComponentRegistry cr = TestingUtil.extractComponentRegistry(cache); LockAssert.assertNoLocks(cr.getComponent(LockManager.class), cr.getComponent(InvocationContextContainer.class)); } } ././@LongLink0000000000000000000000000000016100000000000011563 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/read_committed/StateTransferConcurrencyTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/read_committed/StateTransferConcurre0000644000175000017500000000121411111047320033243 0ustar moellermoellerpackage org.jboss.cache.api.mvcc.read_committed; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.lock.IsolationLevel; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "api.mvcc.read_committed.StateTransferConcurrencyTest") public class StateTransferConcurrencyTest extends org.jboss.cache.statetransfer.StateTransferConcurrencyTest { @Override protected void additionalConfiguration(Configuration c) { c.setNodeLockingScheme(NodeLockingScheme.MVCC); c.setIsolationLevel(IsolationLevel.READ_COMMITTED); } } ././@LongLink0000000000000000000000000000015700000000000011570 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/read_committed/NodeReplicatedMoveMvccTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/read_committed/NodeReplicatedMoveMvc0000644000175000017500000000146411120367722033156 0ustar moellermoeller/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.api.mvcc.read_committed; import org.jboss.cache.api.NodeReplicatedMoveTest; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.lock.IsolationLevel; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "api.mvcc.read_committed.NodeReplicatedMoveMvccTest") public class NodeReplicatedMoveMvccTest extends NodeReplicatedMoveTest { public NodeReplicatedMoveMvccTest() { nodeLockingScheme = NodeLockingScheme.MVCC; } @Override protected void configure(Configuration c) { c.setIsolationLevel(IsolationLevel.READ_COMMITTED); } }././@LongLink0000000000000000000000000000016000000000000011562 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/read_committed/ReadCommittedLockParentTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/read_committed/ReadCommittedLockPare0000644000175000017500000000177611111047320033134 0ustar moellermoellerpackage org.jboss.cache.api.mvcc.read_committed; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "api.mvcc.read_committed.ReadCommittedLockParentTest") public class ReadCommittedLockParentTest extends ReadCommittedLockTest { public ReadCommittedLockParentTest() { lockParentForChildInsertRemove = true; } @Override public void testOverwritingOnInsert() { // no op since a locked parent makes this test irrelevant. } @Override public void testOverwritingOnInsert2() { // no op since a locked parent makes this test irrelevant. } @Override public void testOverwritingOnInsert3() { // no op since a locked parent makes this test irrelevant. } @Override public void testConcurrentInsertRemove1() { // no op since a locked parent makes this test irrelevant. } @Override public void testConcurrentInsertRemove2() { // no op since a locked parent makes this test irrelevant. } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/read_committed/SyncReplTxMvccTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/read_committed/SyncReplTxMvccTest.ja0000644000175000017500000000125011111047320033072 0ustar moellermoellerpackage org.jboss.cache.api.mvcc.read_committed; import org.jboss.cache.api.SyncReplTxTest; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.lock.IsolationLevel; import org.testng.annotations.Test; @Test(groups = {"functional", "jgroups", "transaction", "mvcc"}, testName = "api.mvcc.read_committed.SyncReplTxMvccTest") public class SyncReplTxMvccTest extends SyncReplTxTest { public SyncReplTxMvccTest() { nodeLockingScheme = NodeLockingScheme.MVCC; } @Override protected void configure(Configuration c) { c.setIsolationLevel(IsolationLevel.READ_COMMITTED); } }jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/read_committed/NodeMoveMvccTest.java0000644000175000017500000000117611111047320033071 0ustar moellermoeller/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.api.mvcc.read_committed; import org.jboss.cache.api.mvcc.NodeMoveMvccTestBase; import org.jboss.cache.config.Configuration; import org.jboss.cache.lock.IsolationLevel; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "api.mvcc.read_committed.NodeMoveMvccTest") public class NodeMoveMvccTest extends NodeMoveMvccTestBase { @Override protected void configure(Configuration c) { c.setIsolationLevel(IsolationLevel.READ_COMMITTED); } }././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/read_committed/StateTransferTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/read_committed/StateTransferTest.jav0000644000175000017500000000121311111047320033160 0ustar moellermoellerpackage org.jboss.cache.api.mvcc.read_committed; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.statetransfer.StateTransfer200Test; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "api.mvcc.read_committed.StateTransferTest") public class StateTransferTest extends StateTransfer200Test { @Override protected void additionalConfiguration(Configuration c) { c.setNodeLockingScheme(NodeLockingScheme.MVCC); c.setIsolationLevel(IsolationLevel.READ_COMMITTED); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/read_committed/CacheAPIMVCCTest.java0000644000175000017500000000137411111047320032552 0ustar moellermoellerpackage org.jboss.cache.api.mvcc.read_committed; import org.jboss.cache.api.CacheAPITest; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.lock.IsolationLevel; import org.testng.annotations.Test; /** * MVCC version of {@link org.jboss.cache.api.CacheAPITest} */ @Test(groups = {"functional", "mvcc"}, testName = "api.mvcc.read_committed.CacheAPIMVCCTest") public class CacheAPIMVCCTest extends CacheAPITest { @Override protected void configure(Configuration c) { super.configure(c); c.setIsolationLevel(IsolationLevel.READ_COMMITTED); } @Override protected NodeLockingScheme getNodeLockingScheme() { return NodeLockingScheme.MVCC; } }././@LongLink0000000000000000000000000000016500000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/read_committed/DeletedChildResurrectionMvccTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/read_committed/DeletedChildResurrect0000644000175000017500000000132411111047320033170 0ustar moellermoellerpackage org.jboss.cache.api.mvcc.read_committed; import org.jboss.cache.api.DeletedChildResurrectionTest; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.lock.IsolationLevel; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "api.mvcc.read_committed.DeletedChildResurrectionMvccTest") public class DeletedChildResurrectionMvccTest extends DeletedChildResurrectionTest { public DeletedChildResurrectionMvccTest() { nodeLockingScheme = NodeLockingScheme.MVCC; } @Override protected void configure(Configuration c) { c.setIsolationLevel(IsolationLevel.READ_COMMITTED); } }jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/read_committed/NodeAPIMVCCTest.java0000644000175000017500000000425211120367722032445 0ustar moellermoellerpackage org.jboss.cache.api.mvcc.read_committed; import javax.transaction.TransactionManager; import org.jboss.cache.CacheSPI; import org.jboss.cache.Node; import org.jboss.cache.api.NodeAPITest; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.interceptors.MVCCLockingInterceptor; import org.jboss.cache.interceptors.OptimisticNodeInterceptor; import org.jboss.cache.interceptors.PessimisticLockInterceptor; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.lock.IsolationLevel; import org.testng.annotations.Test; import java.util.List; /** * An MVCC version of {@link org.jboss.cache.api.NodeAPITest} */ @Test(groups = {"functional", "mvcc"}, testName = "api.mvcc.read_committed.NodeAPIMVCCTest") public class NodeAPIMVCCTest extends NodeAPITest { protected NodeLockingScheme getNodeLockingScheme() { return NodeLockingScheme.MVCC; } @Override protected void configure(Configuration c) { c.setIsolationLevel(IsolationLevel.READ_COMMITTED); } protected void assertNodeLockingScheme() { assert cache.getConfiguration().getNodeLockingScheme() == NodeLockingScheme.MVCC; boolean interceptorChainOK = false; List chain = cache.getInterceptorChain(); for (CommandInterceptor i : chain) { if (i instanceof PessimisticLockInterceptor) assert false : "Not an MVCC locking chain!!"; if (i instanceof OptimisticNodeInterceptor) assert false : "Not an MVCC locking chain!!"; if (i instanceof MVCCLockingInterceptor) interceptorChainOK = true; } assert interceptorChainOK : "Not an MVCC locking chain!!"; } @Override public void testLocking() { // no op - this is tested separately } @Override protected void childrenUnderTxCheck() throws Exception { assert cache.getNode(A_B) != null; assert cache.getNode(A_C) != null; assert cache.getInvocationContext().getLocks().contains(A); assert cache.getInvocationContext().getLocks().contains(A_B); assert cache.getInvocationContext().getLocks().contains(A_C); } }jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/read_committed/SyncReplMvccTest.java0000644000175000017500000000121711111047320033110 0ustar moellermoellerpackage org.jboss.cache.api.mvcc.read_committed; import org.jboss.cache.api.SyncReplTest; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.lock.IsolationLevel; import org.testng.annotations.Test; @Test(groups = {"functional", "jgroups", "mvcc"}, testName = "api.mvcc.read_committed.SyncReplMvccTest") public class SyncReplMvccTest extends SyncReplTest { public SyncReplMvccTest() { nodeLockingScheme = NodeLockingScheme.MVCC; } @Override protected void configure(Configuration c) { c.setIsolationLevel(IsolationLevel.READ_COMMITTED); } }jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/read_committed/CacheLoaderTest.java0000644000175000017500000000235611121730272032705 0ustar moellermoellerpackage org.jboss.cache.api.mvcc.read_committed; import org.jboss.cache.CacheSPI; import org.jboss.cache.api.mvcc.LockAssert; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.invocation.InvocationContextContainer; import org.jboss.cache.loader.DummyInMemoryCacheLoaderTest; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.lock.LockManager; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "api.mvcc.read_committed.CacheLoaderTest") public class CacheLoaderTest extends DummyInMemoryCacheLoaderTest { @Override protected void configureCache(CacheSPI cache) throws Exception { super.configureCache(cache); cache.getConfiguration().setNodeLockingScheme(NodeLockingScheme.MVCC); cache.getConfiguration().setIsolationLevel(IsolationLevel.READ_COMMITTED); } @AfterMethod public void postTest() { ComponentRegistry cr = TestingUtil.extractComponentRegistry(cache); LockAssert.assertNoLocks(cr.getComponent(LockManager.class), cr.getComponent(InvocationContextContainer.class)); } } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/read_committed/ReadCommittedLockTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/read_committed/ReadCommittedLockTest0000644000175000017500000000055611111047320033157 0ustar moellermoellerpackage org.jboss.cache.api.mvcc.read_committed; import org.jboss.cache.api.mvcc.LockTestBase; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "api.mvcc.read_committed.ReadCommittedLockTest") public class ReadCommittedLockTest extends LockTestBase { public ReadCommittedLockTest() { repeatableRead = false; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/ForceWriteLockTest.java0000644000175000017500000000245411111047320030446 0ustar moellermoellerpackage org.jboss.cache.api.mvcc; import org.jboss.cache.Fqn; import org.jboss.cache.NodeSPI; import org.jboss.cache.config.Configuration.NodeLockingScheme; 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.Test; @Test(groups = {"functional", "mvcc"}, testName = "api.mvcc.ForceWriteLockTest") public class ForceWriteLockTest extends org.jboss.cache.options.ForceWriteLockTest { public ForceWriteLockTest() { nodeLockingScheme = NodeLockingScheme.MVCC; } @Override protected void assertNotLocked(Fqn fqn) { assert !TestingUtil.extractLockManager(cache).isLocked(cache.peek(fqn, true)) : "Node " + fqn + " is locked!!"; } @Override protected void assertLocked(Object owner, Fqn fqn, boolean write_locked) { if (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)); assertTrue("node " + fqn + " is not write-locked by owner " + owner, lm.ownsLock(fqn, LockType.WRITE, owner)); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/LockTestBase.java0000644000175000017500000004452611131613416027264 0ustar moellermoellerpackage org.jboss.cache.api.mvcc; import org.jboss.cache.*; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.invocation.InvocationContextContainer; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.lock.LockManager; import org.jboss.cache.lock.TimeoutException; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.Collections; /** * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ @Test(groups = {"functional", "mvcc"}) public abstract class LockTestBase extends AbstractSingleCacheTest { protected Fqn A = Fqn.fromString("/a"); protected Fqn AB = Fqn.fromString("/a/b"); protected Fqn ABC = Fqn.fromString("/a/b/c"); protected Fqn ABCD = Fqn.fromString("/a/b/c/d"); protected boolean repeatableRead = true; protected boolean lockParentForChildInsertRemove = false; public Cache cache; public TransactionManager tm; public LockManager lockManager; public InvocationContextContainer icc; public CacheSPI createCache() { cache = new UnitTestCacheFactory().createCache(UnitTestConfigurationFactory.createConfiguration(CacheMode.LOCAL), false, getClass()); cache.getConfiguration().setNodeLockingScheme(NodeLockingScheme.MVCC); cache.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cache.getConfiguration().setIsolationLevel(repeatableRead ? IsolationLevel.REPEATABLE_READ : IsolationLevel.READ_COMMITTED); cache.getConfiguration().setLockParentForChildInsertRemove(lockParentForChildInsertRemove); // reduce lock acquisition timeout so this doesn't take forever to run cache.getConfiguration().setLockAcquisitionTimeout(200); // 200 ms cache.start(); lockManager = TestingUtil.extractComponentRegistry(cache).getComponent(LockManager.class); icc = TestingUtil.extractComponentRegistry(cache).getComponent(InvocationContextContainer.class); tm = TestingUtil.extractComponentRegistry(cache).getComponent(TransactionManager.class); return (CacheSPI) cache; } protected void assertLocked(Fqn fqn) { LockAssert.assertLocked(fqn, lockManager, icc); } protected void assertNotLocked(Fqn fqn) { LockAssert.assertNotLocked(fqn, lockManager, icc); } protected void assertNoLocks() { LockAssert.assertNoLocks(lockManager, icc); } public void testLocksOnPutKeyVal() throws Exception { tm.begin(); cache.put(AB, "k", "v"); if (lockParentForChildInsertRemove) assertLocked(Fqn.ROOT); else assertNotLocked(Fqn.ROOT); assertLocked(A); assertLocked(AB); assertNotLocked(ABC); tm.commit(); assertNoLocks(); tm.begin(); assert cache.get(AB, "k").equals("v"); assertNotLocked(Fqn.ROOT); assertNotLocked(A); assertNotLocked(AB); assertNotLocked(ABC); tm.commit(); assertNoLocks(); tm.begin(); cache.put(ABC, "k", "v"); assertNotLocked(Fqn.ROOT); assertNotLocked(A); if (lockParentForChildInsertRemove) assertLocked(AB); else assertNotLocked(AB); assertLocked(ABC); tm.commit(); assertNoLocks(); } public void testLocksOnPutData() throws Exception { tm.begin(); cache.put(AB, Collections.singletonMap("k", "v")); if (lockParentForChildInsertRemove) assertLocked(Fqn.ROOT); else assertNotLocked(Fqn.ROOT); assertLocked(A); assertLocked(AB); assertNotLocked(ABC); assert "v".equals(cache.get(AB, "k")); tm.commit(); assert "v".equals(cache.get(AB, "k")); assertNoLocks(); tm.begin(); assert "v".equals(cache.get(AB, "k")); assertNotLocked(Fqn.ROOT); assertNotLocked(A); assertNotLocked(AB); assertNotLocked(ABC); tm.commit(); assertNoLocks(); tm.begin(); cache.put(ABC, Collections.singletonMap("k", "v")); assertNotLocked(Fqn.ROOT); assertNotLocked(A); if (lockParentForChildInsertRemove) assertLocked(AB); else assertNotLocked(AB); assertLocked(ABC); tm.commit(); assertNoLocks(); } public void testLocksOnRemoveNode() throws Exception { // init some data on a node cache.put(AB, Collections.singletonMap("k", "v")); assert "v".equals(cache.get(AB, "k")); tm.begin(); cache.removeNode(AB); assertLocked(AB); if (lockParentForChildInsertRemove) assertLocked(A); else assertNotLocked(A); assertNotLocked(Fqn.ROOT); tm.commit(); assert cache.getNode(AB) == null : "Should not exist"; assertNoLocks(); } public void testLocksOnEvictNode() throws Exception { // init some data on a node cache.put(AB, Collections.singletonMap("k", "v")); assert "v".equals(cache.get(AB, "k")); tm.begin(); cache.evict(AB); assertLocked(AB); if (lockParentForChildInsertRemove) assertLocked(A); else assertNotLocked(A); assertNotLocked(Fqn.ROOT); tm.commit(); assert cache.getNode(AB) == null : "Should not exist"; assertNoLocks(); } public void testLocksOnEvictRecursiveNode() throws Exception { // init some data on a node cache.put(AB, Collections.singletonMap("k", "v")); cache.put(ABC, Collections.singletonMap("k", "v")); cache.put(ABCD, Collections.singletonMap("k", "v")); assert "v".equals(cache.get(AB, "k")); assert "v".equals(cache.get(ABC, "k")); assert "v".equals(cache.get(ABCD, "k")); tm.begin(); cache.evict(AB, true); assertLocked(AB); assertLocked(ABC); assertLocked(ABCD); if (lockParentForChildInsertRemove) assertLocked(A); else assertNotLocked(A); assertNotLocked(Fqn.ROOT); tm.commit(); assert cache.getNode(AB) == null : "Should not exist"; assertNoLocks(); } public void testLocksOnRemoveNonexistentNode() throws Exception { assert cache.getNode(AB) == null : "Should not exist"; tm.begin(); cache.removeNode(AB); assertLocked(AB); if (lockParentForChildInsertRemove) assertLocked(A); else assertNotLocked(A); assertNotLocked(Fqn.ROOT); tm.commit(); assert cache.getNode(AB) == null : "Should not exist"; assertNoLocks(); } public void testLocksOnEvictNonexistentNode() throws Exception { assert cache.getNode(AB) == null : "Should not exist"; tm.begin(); cache.evict(AB); assertLocked(AB); if (lockParentForChildInsertRemove) assertLocked(A); else assertNotLocked(A); assertNotLocked(Fqn.ROOT); tm.commit(); assert cache.getNode(AB) == null : "Should not exist"; assertNoLocks(); } public void testLocksOnRemoveData() throws Exception { // init some data on a node cache.put(AB, "k", "v"); cache.put(AB, "k2", "v2"); assert "v".equals(cache.get(AB, "k")); assert "v2".equals(cache.get(AB, "k2")); // remove tm.begin(); Object x = cache.remove(AB, "k"); assert x.equals("v"); assertLocked(AB); assertNotLocked(A); assertNotLocked(Fqn.ROOT); tm.commit(); assert cache.get(AB, "k") == null : "Should not exist"; assert "v2".equals(cache.get(AB, "k2")); assertNoLocks(); // clearData tm.begin(); cache.clearData(AB); assertLocked(AB); assertNotLocked(A); assertNotLocked(Fqn.ROOT); tm.commit(); assert cache.get(AB, "k") == null : "Should not exist"; assert cache.get(AB, "k2") == null : "Should not exist"; assertNoLocks(); // nonexistent key assert cache.get(AB, "k3") == null : "Should not exist"; tm.begin(); cache.remove(AB, "k3"); assertLocked(AB); assertNotLocked(A); assertNotLocked(Fqn.ROOT); tm.commit(); assertNoLocks(); } public void testLocksOnRemoveDataNonExistentNode() throws Exception { assert cache.getNode(AB) == null : "Should not exist"; tm.begin(); cache.remove(AB, "k"); assertNotLocked(AB); assertNotLocked(A); assertNotLocked(Fqn.ROOT); tm.commit(); assert cache.getNode(AB) == null : "Should not exist"; } public void testReadMethods() throws Exception { cache.put(AB, "k", "v"); tm.begin(); assert "v".equals(cache.get(AB, "k")); assertNoLocks(); tm.commit(); assertNoLocks(); tm.begin(); assert cache.getData(AB).containsKey("k"); assertNoLocks(); tm.commit(); assertNoLocks(); tm.begin(); assert cache.getKeys(AB).contains("k"); assertNoLocks(); tm.commit(); assertNoLocks(); tm.begin(); assert cache.getNode(AB) != null; assertNoLocks(); tm.commit(); assertNoLocks(); tm.begin(); assert cache.getNode(A) != null; assert !(cache.getNode(A).getChildrenNames().isEmpty()); assert cache.getNode(A).getChildrenNames().contains(AB.getLastElement()); assertNoLocks(); tm.commit(); assertNoLocks(); } public void testWriteDoesntBlockRead() throws Exception { cache.put(AB, "k", "v"); // start a write. tm.begin(); cache.put(AB, "k2", "v2"); assertLocked(AB); Transaction write = tm.suspend(); // now start a read and confirm that the write doesn't block it. tm.begin(); assert "v".equals(cache.get(AB, "k")); assert null == cache.get(AB, "k2") : "Should not see uncommitted changes"; Transaction read = tm.suspend(); // commit the write tm.resume(write); tm.commit(); assertNoLocks(); tm.resume(read); if (repeatableRead) assert null == cache.get(AB, "k2") : "Should have repeatable read"; else assert "v2".equals(cache.get(AB, "k2")) : "Read committed should see committed changes"; tm.commit(); assertNoLocks(); } public void testWriteDoesntBlockReadNonexistent() throws Exception { // start a write. tm.begin(); cache.put(AB, "k", "v"); assertLocked(AB); Transaction write = tm.suspend(); // now start a read and confirm that the write doesn't block it. tm.begin(); assert null == cache.get(AB, "k") : "Should not see uncommitted changes"; assert null == cache.getNode(AB); Transaction read = tm.suspend(); // commit the write tm.resume(write); tm.commit(); assertNoLocks(); tm.resume(read); if (repeatableRead) { assert null == cache.get(AB, "k") : "Should have repeatable read"; assert null == cache.getNode(AB); } else { assert "v".equals(cache.get(AB, "k")) : "Read committed should see committed changes"; assert null != cache.getNode(AB); } tm.commit(); assertNoLocks(); } public void testConcurrentWriters() throws Exception { tm.begin(); cache.put(AB, "k", "v"); Transaction t1 = tm.suspend(); tm.begin(); try { cache.put(AB, "k", "v"); assert false : "Should fail lock acquisition"; } catch (TimeoutException expected) { // expected.printStackTrace(); // for debugging } tm.commit(); tm.resume(t1); tm.commit(); assertNoLocks(); } public void testRollbacks() throws Exception { cache.put(AB, "k", "v"); tm.begin(); assert "v".equals(cache.get(AB, "k")); Transaction reader = tm.suspend(); tm.begin(); cache.put(AB, "k", "v2"); tm.rollback(); tm.resume(reader); assert "v".equals(cache.get(AB, "k")) : "Expecting 'v' but was " + cache.get(AB, "k"); tm.commit(); // even after commit assert "v".equals(cache.get(AB, "k")); assertNoLocks(); } public void testRollbacksOnNullNode() throws Exception { tm.begin(); assert null == cache.get(AB, "k"); assert null == cache.getNode(AB); Transaction reader = tm.suspend(); tm.begin(); cache.put(AB, "k", "v"); assert null != cache.getNode(AB); assert "v".equals(cache.get(AB, "k")); tm.rollback(); tm.resume(reader); assert null == cache.get(AB, "k") : "Expecting null but was " + cache.get(AB, "k"); assert null == cache.getNode(AB); tm.commit(); // even after commit assert null == cache.get(AB, "k"); assert null == cache.getNode(AB); assertNoLocks(); } public void testPhantomChildren() throws Exception { cache.put(AB, "k", "v"); assert cache.getNode(AB).getChildren().size() == 0; assert cache.getNode(A).getChildren().size() == 1; tm.begin(); cache.put(ABC, "k", "v"); assert cache.getRoot().hasChild(ABC); assert cache.getNode(ABC) != null; assert cache.getNode(AB).getChild(ABC.getLastElement()) != null; assert cache.getNode(AB).getChildren().size() == 1; Transaction t = tm.suspend(); assert cache.getNode(ABC) == null; assert cache.getNode(AB).getChild(ABC.getLastElement()) == null; assert cache.getNode(AB).getChildren().size() == 0; tm.resume(t); assert cache.getRoot().hasChild(ABC); assert cache.getNode(ABC) != null; tm.commit(); assert cache.getNode(ABC) != null; assert cache.getNode(AB).getChild(ABC.getLastElement()) != null; assert cache.getNode(AB).getChildren().size() == 1; } public void testChildCount() throws Exception { cache.put(AB, "k", "v"); assert cache.getNode(AB).getChildren().size() == 0; assert cache.getNode(A).getChildren().size() == 1; tm.begin(); assert cache.getNode(AB).getChildren().size() == 0; assert cache.getNode(A).getChildren().size() == 1; cache.removeNode(AB); assert cache.getNode(A).getChildren().size() == 0; assert cache.getNode(A).hasChild(AB.getLastElement()) == false; assert cache.getNode(AB) == null; Transaction t = tm.suspend(); assert cache.getNode(AB) != null; assert cache.getNode(A).getChild(AB.getLastElement()) != null; assert cache.getNode(A).getChildren().size() == 1; tm.resume(t); assert cache.getNode(A).getChildren().size() == 0; assert cache.getNode(A).hasChild(AB.getLastElement()) == false; assert cache.getNode(AB) == null; tm.commit(); assert cache.getNode(A).getChildren().size() == 0; assert cache.getNode(A).hasChild(AB.getLastElement()) == false; assert cache.getNode(AB) == null; } public void testOverwritingOnInsert() throws Exception { cache.put(AB, "k", "v"); tm.begin(); cache.put(ABC, "k", "v"); assert "v".equals(cache.get(ABC, "k")); assert "v".equals(cache.get(AB, "k")); Transaction t1 = tm.suspend(); tm.begin(); cache.put(AB, "k", "v2"); assert "v2".equals(cache.get(AB, "k")); assert null == cache.get(ABC, "k"); Transaction t2 = tm.suspend(); tm.resume(t1); t1.commit(); assert "v".equals(cache.get(AB, "k")); assert "v".equals(cache.get(ABC, "k")); tm.resume(t2); t2.commit(); assert "v2".equals(cache.get(AB, "k")); assert "v".equals(cache.get(ABC, "k")); } public void testOverwritingOnInsert2() throws Exception { cache.put(AB, "k", "v"); tm.begin(); cache.put(AB, "k", "v2"); assert "v2".equals(cache.get(AB, "k")); assert null == cache.get(ABC, "k"); Transaction t1 = tm.suspend(); tm.begin(); cache.put(ABC, "k", "v"); assert "v".equals(cache.get(ABC, "k")); assert "v".equals(cache.get(AB, "k")); Transaction t2 = tm.suspend(); tm.resume(t1); t1.commit(); assert "v2".equals(cache.get(AB, "k")); assert null == cache.get(ABC, "k"); tm.resume(t2); t2.commit(); assert "v2".equals(cache.get(AB, "k")); assert "v".equals(cache.get(ABC, "k")); } public void testOverwritingOnInsert3() throws Exception { cache.put(AB, "k", "v"); tm.begin(); cache.put(AB, "k", "v2"); assert "v2".equals(cache.get(AB, "k")); assert null == cache.get(ABC, "k"); Transaction t1 = tm.suspend(); tm.begin(); cache.put(ABC, "k", "v"); assert "v".equals(cache.get(ABC, "k")); assert "v".equals(cache.get(AB, "k")); tm.commit(); assert "v".equals(cache.get(ABC, "k")); assert "v".equals(cache.get(AB, "k")); tm.resume(t1); t1.commit(); assert "v2".equals(cache.get(AB, "k")); assert "v".equals(cache.get(ABC, "k")); } public void testConcurrentInsertRemove1() throws Exception { cache.put(AB, "k", "v"); tm.begin(); cache.put(ABC, "k", "v"); assert "v".equals(cache.get(AB, "k")); assert "v".equals(cache.get(ABC, "k")); Transaction t1 = tm.suspend(); tm.begin(); cache.removeNode(AB); assert null == cache.get(ABC, "k"); assert null == cache.get(AB, "k"); tm.commit(); assert null == cache.get(ABC, "k"); assert null == cache.get(AB, "k"); tm.resume(t1); t1.commit(); assert null == cache.get(ABC, "k"); assert null == cache.get(AB, "k"); } public void testConcurrentInsertRemove2() throws Exception { cache.put(AB, "k", "v"); tm.begin(); cache.removeNode(AB); assert null == cache.get(ABC, "k"); assert null == cache.get(AB, "k"); Transaction t1 = tm.suspend(); tm.begin(); assert "v".equals(cache.get(AB, "k")); cache.put(ABC, "k", "v"); assert "v".equals(cache.get(ABC, "k")); tm.commit(); assert "v".equals(cache.get(AB, "k")); assert "v".equals(cache.get(ABC, "k")); tm.resume(t1); t1.commit(); assert null == cache.get(ABC, "k"); assert null == cache.get(AB, "k"); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/LockPerFqnTest.java0000644000175000017500000000553511160156274027610 0ustar moellermoellerpackage org.jboss.cache.api.mvcc; import org.jboss.cache.Cache; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.invocation.InvocationContextContainer; import org.jboss.cache.lock.LockManager; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.concurrent.locks.PerElementLockContainer; 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; @Test(groups = "functional", sequential = true, testName = "api.mvcc.LockPerFqnTest") public class LockPerFqnTest { Cache cache; @BeforeMethod public void setUp() { Configuration cfg = new Configuration(); cfg.setUseLockStriping(false); cfg.setNodeLockingScheme(Configuration.NodeLockingScheme.MVCC); cache = new DefaultCacheFactory().createCache(cfg); } @AfterMethod public void tearDown() { TestingUtil.killCaches(cache); } public void testLocksCleanedUp() { cache.put("/a/b/c", "k", "v"); cache.put("/a/b/d", "k", "v"); assertNoLocks(); } public void testLocksConcurrency() throws Exception { final int NUM_THREADS = 10; final CountDownLatch l = new CountDownLatch(1); final int numLoops = 1000; final List exceptions = new LinkedList(); Thread[] t = new Thread[NUM_THREADS]; for (int i=0; i cache; private TransactionManager tm; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { UnitTestCacheFactory factory = new UnitTestCacheFactory(); Configuration conf = UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); conf.setIsolationLevel(IsolationLevel.READ_COMMITTED); conf.setLockParentForChildInsertRemove(true); conf.setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cache = (CacheSPI) factory.createCache(conf, true, getClass()); tm = cache.getTransactionManager(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache); } public void testPutRemove() throws Exception { Fqn fqn = Fqn.fromElements("test", "a", "b"); for (int i = 0; i < 5; i++) { System.out.println("i == " + i + " [1]"); tm.begin(); check(fqn); tm.commit(); System.out.println("i == " + i + " [2]"); tm.begin(); // the exception is in remove() on the SECOND pass! if (remove(fqn)) { check(fqn); } tm.commit(); } } private void check(Fqn fqn) { if (cache.get(fqn.getAncestor(1), "a") == null) { cache.put(fqn.getAncestor(1), "key-a", "value-a"); cache.put(fqn, "test-key-a", "value-a"); } } private boolean remove(Fqn fqn) { return cache.removeNode(fqn.getAncestor(1)); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/CacheSPIMVCCTest.java0000644000175000017500000000061711111047320027613 0ustar moellermoellerpackage org.jboss.cache.api.mvcc; import org.jboss.cache.api.CacheSPITest; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "api.mvcc.CacheSPIMVCCTest") public class CacheSPIMVCCTest extends CacheSPITest { public CacheSPIMVCCTest() { nodeLockingScheme = NodeLockingScheme.MVCC; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/LockAssert.java0000644000175000017500000000277411111047320027003 0ustar moellermoellerpackage org.jboss.cache.api.mvcc; import org.jboss.cache.Fqn; import org.jboss.cache.invocation.InvocationContextContainer; import org.jboss.cache.lock.LockManager; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.concurrent.locks.LockContainer; /** * Helper class to assert lock status in MVCC * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ public class LockAssert { public static void assertLocked(Fqn fqn, LockManager lockManager, InvocationContextContainer icc) { assert lockManager.isLocked(fqn) : fqn + " not locked!"; assert icc.get().getLocks().contains(fqn) : "Lock not recorded for " + fqn; } public static void assertNotLocked(Fqn fqn, LockManager lockManager, InvocationContextContainer icc) { // can't rely on the negative test since other nodes may share the same lock with lock striping. // assert !lockManager.isLocked(fqn) : fqn + " is locked!"; assert !icc.get().getLocks().contains(fqn) : fqn + " lock recorded!"; } public static void assertNoLocks(LockManager lockManager, InvocationContextContainer icc) { LockContainer lc = (LockContainer) TestingUtil.extractField(lockManager, "lockContainer"); assert lc.getNumLocksHeld() == 0 : "Stale locks exist! NumLocksHeld is " + lc.getNumLocksHeld() + " and lock info is " + lockManager.printLockInfo(); assert icc.get().getLocks().isEmpty() : "Stale (?) locks recorded! " + icc.get().getLocks(); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/mvcc/GetChildrenNamesAfterRemoveTest.java0000644000175000017500000000441711147253131033111 0ustar moellermoellerpackage org.jboss.cache.api.mvcc; import org.jboss.cache.AbstractSingleCacheTest; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.UnitTestCacheFactory; import org.testng.annotations.Test; import javax.transaction.TransactionManager; import java.util.Collections; import java.util.Set; @Test(groups = {"functional", "mvcc"}, sequential = true, testName = "api.mvcc.GetChildrenNamesAfterRemoveTest") public class GetChildrenNamesAfterRemoveTest extends AbstractSingleCacheTest { private CacheSPI cache; public CacheSPI createCache() { // start a single cache instance UnitTestCacheFactory cf = new UnitTestCacheFactory(); cache = (CacheSPI) cf.createCache("configs/local-tx.xml", false, getClass()); cache.getConfiguration().setEvictionConfig(null); cache.start(); return cache; } public void testRemove() throws Exception { TransactionManager tm = cache.getTransactionManager(); Fqn testFqn = Fqn.fromElements("test1"); tm.begin(); assertEmpty(testFqn); cache.put(testFqn, "x", "x"); assertNotEmpty(testFqn); cache.removeNode(testFqn); assertEmpty(testFqn); tm.commit(); assertEmpty(testFqn); } private void assertNotEmpty(Fqn testFqn) { Set> children = cache.getNode(testFqn.getParent()).getChildren(); assert !children.isEmpty() : "Node " + testFqn + " should not be a leaf, but getChildren() returns: " + children; Set childrenNames = cache.getNode(testFqn.getParent()).getChildrenNames(); assert childrenNames.equals(Collections.singleton(testFqn.getLastElement())) : "Retrieving children names on " + testFqn + " should return test1 but is: " + childrenNames; } private void assertEmpty(Fqn testFqn) { Set> children = cache.getNode(testFqn.getParent()).getChildren(); assert children.isEmpty() : "Children should be empty but is " + children; Set childrenNames = cache.getNode(testFqn.getParent()).getChildrenNames(); assert childrenNames.isEmpty() : "Children names should be empty but is " + childrenNames; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/NodeReplicatedMoveTest.java0000644000175000017500000001252311131613416030352 0ustar moellermoeller/* * 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.*; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.factories.UnitTestConfigurationFactory; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNull; import org.testng.annotations.Test; import javax.transaction.TransactionManager; @Test(groups = {"functional", "jgroups", "pessimistic"}, testName = "api.NodeReplicatedMoveTest") public class NodeReplicatedMoveTest extends AbstractMultipleCachesTest { 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 static final Object k = "key", vA = "valueA", vB = "valueB", vC = "valueC", vD = "valueD", vE = "valueE"; protected NodeLockingScheme nodeLockingScheme = NodeLockingScheme.PESSIMISTIC; private CacheSPI cache1; private TransactionManager tm; private CacheSPI cache2; protected void createCaches() { // start a single cache instance cache1 = (CacheSPI) new UnitTestCacheFactory().createCache(UnitTestConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), false, getClass()); cache1.getConfiguration().setSyncCommitPhase(true); cache1.getConfiguration().setSyncRollbackPhase(true); cache1.getConfiguration().setNodeLockingScheme(nodeLockingScheme); configure(cache1.getConfiguration()); cache1.start(); tm = cache1.getTransactionManager(); // start second instance cache2 = (CacheSPI) new UnitTestCacheFactory().createCache(UnitTestConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), false, getClass()); cache2.getConfiguration().setSyncCommitPhase(true); cache2.getConfiguration().setSyncRollbackPhase(true); cache2.getConfiguration().setNodeLockingScheme(nodeLockingScheme); configure(cache2.getConfiguration()); cache2.start(); registerCaches(cache1, cache2); } protected void configure(Configuration c) { // to be overridden } public void testReplicatability() { Node rootNode = cache1.getRoot(); Node nodeA = rootNode.addChild(A); Node 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 testReplTxCommit() throws Exception { Node rootNode = cache1.getRoot(); Fqn A_B = Fqn.fromRelativeFqn(A, B); Node nodeA = rootNode.addChild(A); Node 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 { Node rootNode = cache1.getRoot(); Node nodeA = rootNode.addChild(A); Node 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)); } protected boolean isOptimistic() { return nodeLockingScheme == NodeLockingScheme.OPTIMISTIC; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/SyncReplTxTest.java0000644000175000017500000001143611131613416026716 0ustar moellermoeller/* * 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.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.Node; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.factories.UnitTestConfigurationFactory; 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 java.util.ArrayList; import java.util.List; /** * @author Manik Surtani (manik AT jboss DOT org) */ @Test(groups = {"functional", "jgroups", "transaction", "pessimistic"}, sequential = true, testName = "api.SyncReplTxTest") public class SyncReplTxTest { private List> caches; protected NodeLockingScheme nodeLockingScheme = NodeLockingScheme.PESSIMISTIC; @BeforeMethod(alwaysRun = true) public void setUp() throws CloneNotSupportedException { caches = new ArrayList>(); CacheSPI cache1 = (CacheSPI) new UnitTestCacheFactory().createCache(UnitTestConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), false, getClass()); cache1.getConfiguration().setNodeLockingScheme(nodeLockingScheme); cache1.getConfiguration().setSyncCommitPhase(true); cache1.getConfiguration().setSyncRollbackPhase(true); cache1.getConfiguration().setFetchInMemoryState(false); configure(cache1.getConfiguration()); cache1.start(); CacheSPI cache2 = (CacheSPI) new UnitTestCacheFactory().createCache(cache1.getConfiguration().clone(), false, getClass()); cache2.start(); caches.add(cache1); caches.add(cache2); TestingUtil.blockUntilViewsReceived(caches.toArray(new Cache[0]), 10000); } @AfterMethod(alwaysRun = true) public void tearDown() { TestingUtil.killCaches(caches.toArray(new Cache[caches.size()])); } protected void configure(Configuration c) { // to be overridden } 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; control = ctx.copy(); control.reset(); assertEquals("Should be equal", control, ctx); } }jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/nodevalidity/0000755000175000017500000000000011675221567025645 5ustar moellermoeller././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/nodevalidity/LocalMvccNodeValidityTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/nodevalidity/LocalMvccNodeValidityTest.ja0000644000175000017500000000127411111047320033156 0ustar moellermoellerpackage org.jboss.cache.api.nodevalidity; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.lock.IsolationLevel; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "api.nodevalidity.LocalMvccNodeValidityTest") public class LocalMvccNodeValidityTest extends LocalPessNodeValidityTest { public LocalMvccNodeValidityTest() { nodeLockingScheme = NodeLockingScheme.MVCC; } @Override protected void nodeLockingSchemeSpecificSetup(Configuration c) { c.setNodeLockingScheme(nodeLockingScheme); c.setIsolationLevel(IsolationLevel.READ_COMMITTED); } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/nodevalidity/LocalPessNodeValidityTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/nodevalidity/LocalPessNodeValidityTest.ja0000644000175000017500000000234211132625723033211 0ustar moellermoellerpackage org.jboss.cache.api.nodevalidity; import org.jboss.cache.Cache; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.testng.annotations.Test; /** * @author Manik Surtani * @since 2.1.0 */ @Test(groups = {"functional", "pessimistic"}, testName = "api.nodevalidity.LocalPessNodeValidityTest") public class LocalPessNodeValidityTest extends NodeValidityTestBase { public LocalPessNodeValidityTest() { clustered = false; nodeLockingScheme = NodeLockingScheme.PESSIMISTIC; } Cache onlyInstance; protected Cache createObserver() { return returnOnlyInstance(); } protected Cache createModifier() { return returnOnlyInstance(); } private Cache returnOnlyInstance() { if (onlyInstance == null) { UnitTestCacheFactory f = new UnitTestCacheFactory(); onlyInstance = f.createCache(false, getClass()); nodeLockingSchemeSpecificSetup(onlyInstance.getConfiguration()); onlyInstance.start(); } return onlyInstance; } } ././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/nodevalidity/ReplicatedMvccNodeValidityTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/nodevalidity/ReplicatedMvccNodeValidityTe0000644000175000017500000000132011111047320033230 0ustar moellermoellerpackage org.jboss.cache.api.nodevalidity; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.lock.IsolationLevel; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "api.nodevalidity.ReplicatedMvccNodeValidityTest") public class ReplicatedMvccNodeValidityTest extends ReplicatedPessNodeValidityTest { public ReplicatedMvccNodeValidityTest() { nodeLockingScheme = NodeLockingScheme.MVCC; } @Override protected void nodeLockingSchemeSpecificSetup(Configuration c) { c.setNodeLockingScheme(nodeLockingScheme); c.setIsolationLevel(IsolationLevel.READ_COMMITTED); } } ././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/nodevalidity/InvalidatedOptNodeValidityTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/nodevalidity/InvalidatedOptNodeValidityTe0000644000175000017500000000754111121730272033273 0ustar moellermoellerpackage org.jboss.cache.api.nodevalidity; import org.jboss.cache.CacheSPI; import org.jboss.cache.NodeSPI; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.optimistic.DefaultDataVersion; import org.testng.annotations.Test; /** * @author Manik Surtani * @since 2.1.0 */ @Test(groups = {"functional", "optimistic"}, testName = "api.nodevalidity.InvalidatedOptNodeValidityTest") public class InvalidatedOptNodeValidityTest extends InvalidatedPessNodeValidityTest { public InvalidatedOptNodeValidityTest() { nodeLockingScheme = NodeLockingScheme.OPTIMISTIC; } 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"; } }././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/nodevalidity/ReplicatedPessNodeValidityTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/nodevalidity/ReplicatedPessNodeValidityTe0000644000175000017500000000210111132625723033264 0ustar moellermoellerpackage org.jboss.cache.api.nodevalidity; import org.jboss.cache.Cache; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.testng.annotations.Test; /** * @author Manik Surtani * @since 2.1.0 */ @Test(groups = {"functional", "pessimistic"} , testName = "api.nodevalidity.ReplicatedPessNodeValidityTest") public class ReplicatedPessNodeValidityTest extends NodeValidityTestBase { protected Cache createObserver() { return newCache(); } protected Cache createModifier() { return newCache(); } protected Cache newCache() { UnitTestCacheFactory f = new UnitTestCacheFactory(); Configuration c = new Configuration(); c.setCacheMode(Configuration.CacheMode.REPL_SYNC); Cache cache = f.createCache(c, false, getClass()); nodeLockingSchemeSpecificSetup(cache.getConfiguration()); cache.start(); return cache; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/nodevalidity/NodeValidityTestBase.java0000644000175000017500000002755211120367722032536 0ustar moellermoellerpackage org.jboss.cache.api.nodevalidity; import org.jboss.cache.*; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.optimistic.DefaultDataVersion; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.jboss.cache.util.TestingUtil; 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 extends AbstractMultipleCachesTest { protected NodeLockingScheme nodeLockingScheme = NodeLockingScheme.PESSIMISTIC; // needed to attach a blockUntilViewsReceived in setup protected boolean clustered = true; // needed to test tombstones protected boolean invalidation = false; protected static final Fqn parent = Fqn.fromString("/parent"); protected static final Fqn child = Fqn.fromString("/parent/child"); protected static final String K = "k", V = "v"; protected Cache observer; protected Cache modifier; protected abstract Cache createObserver(); protected abstract Cache createModifier(); protected void nodeLockingSchemeSpecificSetup(Configuration c) { c.setNodeLockingScheme(nodeLockingScheme); if (isOptimistic()) { c.setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); c.setSyncCommitPhase(true); c.setSyncRollbackPhase(true); } } protected boolean isOptimistic() { return nodeLockingScheme == NodeLockingScheme.OPTIMISTIC; } protected void createCaches() { observer = createObserver(); modifier = createModifier(); registerCaches(observer, modifier); if (clustered) TestingUtil.blockUntilViewsReceived(60000, observer, modifier); } public void testRemoval() { 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.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 (isOptimistic()) 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 (isOptimistic()) { 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 (isOptimistic() && 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 (isOptimistic() && 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"; } } } ././@LongLink0000000000000000000000000000015500000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/nodevalidity/InvalidatedMvccNodeValidityTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/nodevalidity/InvalidatedMvccNodeValidityT0000644000175000017500000000132411111047320033237 0ustar moellermoellerpackage org.jboss.cache.api.nodevalidity; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.lock.IsolationLevel; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "api.nodevalidity.InvalidatedMvccNodeValidityTest") public class InvalidatedMvccNodeValidityTest extends InvalidatedPessNodeValidityTest { public InvalidatedMvccNodeValidityTest() { nodeLockingScheme = NodeLockingScheme.MVCC; } @Override protected void nodeLockingSchemeSpecificSetup(Configuration c) { c.setNodeLockingScheme(nodeLockingScheme); c.setIsolationLevel(IsolationLevel.READ_COMMITTED); } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/nodevalidity/LocalOptNodeValidityTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/nodevalidity/LocalOptNodeValidityTest.jav0000644000175000017500000000100311111047320033204 0ustar moellermoellerpackage org.jboss.cache.api.nodevalidity; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.testng.annotations.Test; /** * @author Manik Surtani * @since 2.1.0 */ @Test(groups = {"functional", "optimistic"}, testName = "api.nodevalidity.LocalOptNodeValidityTest") public class LocalOptNodeValidityTest extends LocalPessNodeValidityTest { public LocalOptNodeValidityTest() { nodeLockingScheme = NodeLockingScheme.OPTIMISTIC; } }././@LongLink0000000000000000000000000000015500000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/nodevalidity/InvalidatedPessNodeValidityTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/nodevalidity/InvalidatedPessNodeValidityT0000644000175000017500000000465511121730272033301 0ustar moellermoellerpackage org.jboss.cache.api.nodevalidity; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.loader.testloaders.DummySharedInMemoryCacheLoader; import org.testng.annotations.Test; import org.testng.annotations.AfterClass; import java.util.Properties; /** * @author Manik Surtani * @since 2.1.0 */ @Test(groups = {"functional", "pessimistic"}, testName = "api.nodevalidity.InvalidatedPessNodeValidityTest") public class InvalidatedPessNodeValidityTest extends NodeValidityTestBase { protected DummySharedInMemoryCacheLoader loader; public InvalidatedPessNodeValidityTest() { invalidation = true; nodeLockingScheme = NodeLockingScheme.PESSIMISTIC; } protected Cache createObserver() { return newCache(); } protected Cache createModifier() { return newCache(); } @AfterClass public void emptyCacheLoader() { loader.wipeBin(); } protected Cache newCache() { UnitTestCacheFactory f = new UnitTestCacheFactory(); Configuration c = new Configuration(); c.setCacheMode(Configuration.CacheMode.INVALIDATION_SYNC); Cache cache = f.createCache(c, false, getClass()); nodeLockingSchemeSpecificSetup(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()); Properties props = new Properties(); props.setProperty("bin", "bin-" + Thread.currentThread().getName()); iclc.setProperties(props); CacheLoaderConfig clc = new CacheLoaderConfig(); clc.addIndividualCacheLoaderConfig(iclc); cache.getConfiguration().setCacheLoaderConfig(clc); // disable state transfer!! cache.getConfiguration().setFetchInMemoryState(false); cache.start(); CacheSPI spi = (CacheSPI) cache; loader = (DummySharedInMemoryCacheLoader) spi.getCacheLoaderManager().getCacheLoader(); return cache; } } ././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/nodevalidity/ReplicatedOptNodeValidityTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/nodevalidity/ReplicatedOptNodeValidityTes0000644000175000017500000000102711111047320033271 0ustar moellermoellerpackage org.jboss.cache.api.nodevalidity; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.testng.annotations.Test; /** * @author Manik Surtani * @since 2.1.0 */ @Test(groups = {"functional", "optimistic"}, testName = "api.nodevalidity.ReplicatedOptNodeValidityTest") public class ReplicatedOptNodeValidityTest extends ReplicatedPessNodeValidityTest { public ReplicatedOptNodeValidityTest() { nodeLockingScheme = NodeLockingScheme.OPTIMISTIC; } }jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/pfer/0000755000175000017500000000000011675221573024103 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/pfer/PFERMVCCTestBase.java0000644000175000017500000000164211111047320027566 0ustar moellermoellerpackage org.jboss.cache.api.pfer; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.api.mvcc.LockAssert; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.invocation.InvocationContextContainer; import org.jboss.cache.lock.LockManager; import org.jboss.cache.util.TestingUtil; public abstract class PFERMVCCTestBase extends PutForExternalReadTestBase { protected PFERMVCCTestBase() { nodeLockingScheme = NodeLockingScheme.MVCC; } @Override protected void assertLocked(Fqn fqn, CacheSPI cache, boolean writeLocked) { if (!writeLocked) return; // MVCC only does write locks. ComponentRegistry cr = TestingUtil.extractComponentRegistry(cache); LockAssert.assertLocked(fqn, cr.getComponent(LockManager.class), cr.getComponent(InvocationContextContainer.class)); } }././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/pfer/PFEROptimisticInvalidationSyncTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/pfer/PFEROptimisticInvalidationSyncTest.j0000644000175000017500000000126211111047320033074 0ustar moellermoeller/* * 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: 7168 $ */ @Test(groups = {"functional", "optimistic"}, testName = "api.pfer.PFEROptimisticInvalidationSyncTest") public class PFEROptimisticInvalidationSyncTest extends PFEROptimisticTestBase { public PFEROptimisticInvalidationSyncTest() { cacheMode = Configuration.CacheMode.INVALIDATION_SYNC; } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/pfer/PFEROptimisticInvalidationAsyncTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/pfer/PFEROptimisticInvalidationAsyncTest.0000644000175000017500000000126711111047320033070 0ustar moellermoeller/* * 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: 7168 $ */ @Test(groups = {"functional", "optimistic"}, testName = "api.pfer.PFEROptimisticInvalidationAsyncTest") public class PFEROptimisticInvalidationAsyncTest extends PFEROptimisticTestBase { public PFEROptimisticInvalidationAsyncTest() { cacheMode = Configuration.CacheMode.INVALIDATION_ASYNC; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/pfer/PFERMvccInvalidationSyncTest.java0000644000175000017500000000060711111047320032332 0ustar moellermoellerpackage org.jboss.cache.api.pfer; import org.jboss.cache.config.Configuration; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "api.pfer.PFERMvccInvalidationSyncTest") public class PFERMvccInvalidationSyncTest extends PFERMVCCTestBase { public PFERMvccInvalidationSyncTest() { cacheMode = Configuration.CacheMode.INVALIDATION_SYNC; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/pfer/PFERPessimisticTestBase.java0000644000175000017500000000626011120367722031346 0ustar moellermoellerpackage org.jboss.cache.api.pfer; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.lock.NodeLock; import static org.testng.AssertJUnit.*; import org.testng.annotations.Test; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import javax.transaction.Transaction; public abstract class PFERPessimisticTestBase extends PutForExternalReadTestBase { protected PFERPessimisticTestBase() { nodeLockingScheme = NodeLockingScheme.PESSIMISTIC; } @Override protected void assertLocked(Fqn fqn, CacheSPI cache, boolean writeLocked) { NodeLock lock = cache.peek(fqn, true, true).getLock(); assertTrue("node " + fqn + " is not locked", lock.isLocked()); if (writeLocked) { 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()); } } private static Log log = LogFactory.getLog(PFERPessimisticTestBase.class); /** * 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 { log.warn("******** Here is where it is not going to happen"); // create the parent node first ... replListener2.expect(PutKeyValueCommand.class); cache1.put(parentFqn, key, value); try { replListener2.waitForReplicationToOccur(10000); } finally { log.warn("******** Here is where it did not happen!!!"); } replListener2.expectWithTx(PutKeyValueCommand.class); tm1.begin(); cache1.put(parentFqn, key, value2); Transaction t = tm1.suspend(); assertLocked(parentFqn, cache1, 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 acquire lock with 0ms!", waited < cache1.getConfiguration().getLockAcquisitionTimeout()); // should not block. tm1.resume(t); tm1.commit(); replListener2.waitForReplicationToOccur(1000); 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)); assertNull("PFER should have been a no-op", cache1.get(fqn, key)); assertNull("PFER should have been a no-op", cache2.get(fqn, key)); } }jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/pfer/PFERMvccInvalidationAsyncTest.java0000644000175000017500000000061311111047320032470 0ustar moellermoellerpackage org.jboss.cache.api.pfer; import org.jboss.cache.config.Configuration; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "api.pfer.PFERMvccInvalidationAsyncTest") public class PFERMvccInvalidationAsyncTest extends PFERMVCCTestBase { public PFERMvccInvalidationAsyncTest() { cacheMode = Configuration.CacheMode.INVALIDATION_ASYNC; } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/pfer/PFERPessimisticInvalidationSyncTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/pfer/PFERPessimisticInvalidationSyncTest.0000644000175000017500000000127111111047320033072 0ustar moellermoeller/* * 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_ASYNC. * * @author Brian Stansberry * @version $Revision: 7168 $ */ @Test(groups = {"functional", "pessimistic"}, testName = "api.pfer.PFERPessimisticInvalidationSyncTest") public class PFERPessimisticInvalidationSyncTest extends PFERPessimisticTestBase { public PFERPessimisticInvalidationSyncTest() { cacheMode = Configuration.CacheMode.INVALIDATION_SYNC; } } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/pfer/PFERPessimisticInvalidationAsyncTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/pfer/PFERPessimisticInvalidationAsyncTest0000644000175000017500000000127411111047320033160 0ustar moellermoeller/* * 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: 7168 $ */ @Test(groups = {"functional", "pessimistic"}, testName = "api.pfer.PFERPessimisticInvalidationAsyncTest") public class PFERPessimisticInvalidationAsyncTest extends PFERPessimisticTestBase { public PFERPessimisticInvalidationAsyncTest() { cacheMode = Configuration.CacheMode.INVALIDATION_ASYNC; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/pfer/PutForExternalReadTestBase.java0000644000175000017500000004034211142423305032104 0ustar moellermoellerpackage org.jboss.cache.api.pfer; import org.easymock.EasyMock; import static org.easymock.EasyMock.*; import org.jboss.cache.*; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.write.PutForExternalReadCommand; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.commands.write.RemoveNodeCommand; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.optimistic.TransactionWorkspace; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionContext; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.replicationlisteners.ReplicationListener; import org.jgroups.Address; import static org.testng.AssertJUnit.*; 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; public abstract class PutForExternalReadTestBase extends AbstractMultipleCachesTest { protected Configuration.CacheMode cacheMode; protected NodeLockingScheme nodeLockingScheme; protected final Fqn fqn = Fqn.fromString("/one/two"); protected final Fqn parentFqn = fqn.getParent(); protected final String key = "k", value = "v", value2 = "v2"; protected CacheSPI cache1, cache2; ReplicationListener replListener1; ReplicationListener replListener2; protected TransactionManager tm1, tm2; protected boolean useTx; private RPCManager rpcManager1; protected void createCaches() { UnitTestCacheFactory cf = new UnitTestCacheFactory(); cache1 = (CacheSPI) cf.createCache(UnitTestConfigurationFactory.createConfiguration(cacheMode), false, getClass()); cache1.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache1.getConfiguration().setSerializationExecutorPoolSize(0);//this is very important for async tests! cache1.getConfiguration().setNodeLockingScheme(nodeLockingScheme); cache1.start(); tm1 = cache1.getConfiguration().getRuntimeConfig().getTransactionManager(); cache2 = (CacheSPI) cf.createCache(UnitTestConfigurationFactory.createConfiguration(cacheMode), false, getClass()); cache2.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache2.getConfiguration().setSerializationExecutorPoolSize(0); //this is very important for async tests! cache2.getConfiguration().setNodeLockingScheme(nodeLockingScheme); cache2.start(); tm2 = cache2.getConfiguration().getRuntimeConfig().getTransactionManager(); replListener1 = ReplicationListener.getReplicationListener(cache1); replListener2 = ReplicationListener.getReplicationListener(cache2); rpcManager1 = cache1.getRPCManager(); TestingUtil.blockUntilViewsReceived(10000, cache1, cache2); registerCaches(cache1, cache2); } public void testNoOpWhenNodePresent() { replListener2.expect(PutForExternalReadCommand.class); cache1.putForExternalRead(fqn, key, value); replListener2.waitForReplicationToOccur(); 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 replListener2.expect(RemoveNodeCommand.class); cache1.removeNode(fqn); replListener2.waitForReplicationToOccur(); assertFalse("Should have reset", cache1.getRoot().hasChild(fqn)); assertFalse("Should have reset", cache2.getRoot().hasChild(fqn)); replListener2.expect(PutKeyValueCommand.class); cache1.put(fqn, key, value); replListener2.waitForReplicationToOccur(); // 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); List
memberList = rpcManager1.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 calls for PFER if (!isUsingInvalidation()) { // specify what we expectWithTx called on the mock Rpc Manager. For params we don't care about, just use ANYTHING. // setting the mock object to expectWithTx 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(rpcManager1, RPCManager.class); TestingUtil.extractComponentRegistry(cache1).rewire(); replListener2.expect(RemoveNodeCommand.class); cache1.removeNode(fqn); replListener2.waitForReplicationToOccur(); } public void testTxSuspension() throws Exception { // create parent node first replListener2.expect(PutKeyValueCommand.class); cache1.put(parentFqn, key, value); replListener2.waitForReplicationToOccur(); // start a tx and do some stuff. replListener2.expect(PutForExternalReadCommand.class); tm1.begin(); cache1.get(parentFqn, key); cache1.putForExternalRead(fqn, key, value); // should have happened in a separate tx and have committed already. Transaction t = tm1.suspend(); replListener2.waitForReplicationToOccur(); assertLocked(parentFqn, cache1, 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(); 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 (!isOptimistic()) fail("Should have barfed"); } catch (RuntimeException re) { } if (isOptimistic() && !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(rpcManager1, RPCManager.class); TestingUtil.extractComponentRegistry(cache1).rewire(); } } public void testBasicPropagation() throws Exception { assert !cache1.exists(fqn); assert !cache2.exists(fqn); replListener2.expect(PutForExternalReadCommand.class); cache1.putForExternalRead(fqn, key, value); replListener2.waitForReplicationToOccur(); 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)); // replication to cache 1 should NOT happen. cache2.putForExternalRead(fqn, key, value); 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"); replListener2.expect(PutForExternalReadCommand.class); tm1.begin(); cache1.putForExternalRead(fqn, key, value); tm1.commit(); replListener2.waitForReplicationToOccur(); 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"; //do not expectWithTx a PFER replication, as the node already exists so this is a no-op replListener2.expectWithTx(PutKeyValueCommand.class); tm1.begin(); cache1.putForExternalRead(fqn, key, value); cache1.put(fqn2, key, value); tm1.commit(); replListener2.waitForReplicationToOccur(); 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"; replListener2.expectWithTx(PutKeyValueCommand.class); tm1.begin(); cache1.put(fqn2, key, value); cache1.putForExternalRead(fqn, key, value); tm1.commit(); replListener2.waitForReplicationToOccur(10000); 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"; //do not expectWithTx a PFER replication, as the node already exists so this is a no-op replListener2.expectWithTx(PutKeyValueCommand.class); tm1.begin(); cache1.put(fqn2, key, value); cache1.putForExternalRead(fqn, key, value); cache1.put(fqn2, key, value); tm1.commit(); replListener2.waitForReplicationToOccur(10000); 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 expectWithTx 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); replListener2.expect(RemoveNodeCommand.class); cache1.removeNode(fqn); replListener2.waitForReplicationToOccur(); } protected abstract void assertLocked(Fqn fqn, CacheSPI cache, boolean writeLocked); protected TransactionWorkspace extractTransactionWorkspace(Cache c) { CacheSPI cs = (CacheSPI) c; try { GlobalTransaction gtx = cs.getTransactionTable().get(cs.getTransactionManager().getTransaction()); OptimisticTransactionContext entry = (OptimisticTransactionContext) cs.getTransactionTable().get(gtx); return entry.getTransactionWorkSpace(); } catch (SystemException e) { e.printStackTrace(); fail("Unable to extract transaction workspace from cache"); } return null; } protected boolean isUsingInvalidation() { return cacheMode.isInvalidation(); } protected boolean isAsync() { return !cacheMode.isSynchronous(); } protected boolean isOptimistic() { return nodeLockingScheme == NodeLockingScheme.OPTIMISTIC; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/pfer/PFEROptimisticReplSyncTest.java0000644000175000017500000000060511111047320032045 0ustar moellermoellerpackage org.jboss.cache.api.pfer; import org.jboss.cache.config.Configuration; import org.testng.annotations.Test; @Test(groups = {"functional", "optimistic"}, testName = "api.pfer.PFEROptimisticReplSyncTest") public class PFEROptimisticReplSyncTest extends PFEROptimisticTestBase { public PFEROptimisticReplSyncTest() { cacheMode = Configuration.CacheMode.REPL_SYNC; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/pfer/PFEROptimisticReplAsyncTest.java0000644000175000017500000000061111111047320032203 0ustar moellermoellerpackage org.jboss.cache.api.pfer; import org.jboss.cache.config.Configuration; import org.testng.annotations.Test; @Test(groups = {"functional", "optimistic"}, testName = "api.pfer.PFEROptimisticReplAsyncTest") public class PFEROptimisticReplAsyncTest extends PFEROptimisticTestBase { public PFEROptimisticReplAsyncTest() { cacheMode = Configuration.CacheMode.REPL_ASYNC; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/pfer/PFERMvccReplSyncTest.java0000644000175000017500000000054711111047320030616 0ustar moellermoellerpackage org.jboss.cache.api.pfer; import org.jboss.cache.config.Configuration; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "api.pfer.PFERMvccReplSyncTest") public class PFERMvccReplSyncTest extends PFERMVCCTestBase { public PFERMvccReplSyncTest() { cacheMode = Configuration.CacheMode.REPL_SYNC; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/pfer/PFERMvccReplAsyncTest.java0000644000175000017500000000055311111047320030754 0ustar moellermoellerpackage org.jboss.cache.api.pfer; import org.jboss.cache.config.Configuration; import org.testng.annotations.Test; @Test(groups = {"functional", "mvcc"}, testName = "api.pfer.PFERMvccReplAsyncTest") public class PFERMvccReplAsyncTest extends PFERMVCCTestBase { public PFERMvccReplAsyncTest() { cacheMode = Configuration.CacheMode.REPL_ASYNC; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/pfer/PFERPessimisticReplSyncTest.java0000644000175000017500000000061211111047320032213 0ustar moellermoellerpackage org.jboss.cache.api.pfer; import org.jboss.cache.config.Configuration; import org.testng.annotations.Test; @Test(groups = {"functional", "pessimistic"}, testName = "api.pfer.PFERPessimisticReplSyncTest") public class PFERPessimisticReplSyncTest extends PFERPessimisticTestBase { public PFERPessimisticReplSyncTest() { cacheMode = Configuration.CacheMode.REPL_SYNC; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/pfer/PFERPessimisticReplAsyncTest.java0000644000175000017500000000061611111047320032360 0ustar moellermoellerpackage org.jboss.cache.api.pfer; import org.jboss.cache.config.Configuration; import org.testng.annotations.Test; @Test(groups = {"functional", "pessimistic"}, testName = "api.pfer.PFERPessimisticReplAsyncTest") public class PFERPessimisticReplAsyncTest extends PFERPessimisticTestBase { public PFERPessimisticReplAsyncTest() { cacheMode = Configuration.CacheMode.REPL_ASYNC; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/pfer/PFEROptimisticTestBase.java0000644000175000017500000000361511111047320031164 0ustar moellermoellerpackage org.jboss.cache.api.pfer; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.optimistic.TransactionWorkspace; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionContext; import org.jboss.cache.transaction.TransactionContext; import org.jboss.cache.transaction.TransactionTable; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.assertNotNull; import java.util.Map; public abstract class PFEROptimisticTestBase extends PutForExternalReadTestBase { protected PFEROptimisticTestBase() { nodeLockingScheme = NodeLockingScheme.OPTIMISTIC; } @Override @SuppressWarnings("unchecked") protected void assertLocked(Fqn fqn, CacheSPI cache, boolean writeLocked) { TransactionTable tt = cache.getTransactionTable(); GlobalTransaction gtx = tt.getCurrentTransaction(); OptimisticTransactionContext otc = (OptimisticTransactionContext) cache.getTransactionTable().get(gtx); if (otc == null && gtx == null) { // perhaps the tx has been suspended? Map gtx2ContextMap = (Map) TestingUtil.extractField(tt, "gtx2ContextMap"); assert gtx2ContextMap.size() == 1 : "Can only attempt to access a suspended tx if there is only one such suspended tx!"; gtx = gtx2ContextMap.keySet().iterator().next(); otc = (OptimisticTransactionContext) gtx2ContextMap.get(gtx); } if (otc == null) throw new NullPointerException("No transaction context available!"); TransactionWorkspace workspace = otc.getTransactionWorkSpace(); // scan workspaces for this node assertNotNull("node " + fqn + " should be in transaction workspace", workspace.getNode(fqn)); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/ReAddDeletedNodeTest.java0000644000175000017500000000353411147260547027731 0ustar moellermoellerpackage org.jboss.cache.api; import org.jboss.cache.AbstractSingleCacheTest; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.testng.annotations.Test; import javax.transaction.TransactionManager; @Test(groups = {"functional"}, sequential = true, testName = "api.ReAddDeletedNodeTest") public class ReAddDeletedNodeTest extends AbstractSingleCacheTest { private CacheSPI cache; public CacheSPI createCache() { // start a single cache instance UnitTestCacheFactory cf = new UnitTestCacheFactory(); cache = (CacheSPI) cf.createCache("configs/local-tx.xml", false, getClass()); cache.getConfiguration().setEvictionConfig(null); cache.start(); return cache; } public void testReAdd() throws Exception { TransactionManager tm = cache.getTransactionManager(); Fqn testFqn = Fqn.fromElements("a", "a", "a"); tm.begin(); cache.put(testFqn, "x", "x"); cache.removeNode(testFqn.getParent()); cache.put(testFqn, "x", "x"); assert cache.getNode(testFqn) != null : testFqn + " should not be null (before commit)"; assert cache.getNode(testFqn.getParent()) != null : testFqn.getParent() + " should not be null (before commit)"; assert cache.getNode(testFqn.getParent().getParent()) != null : testFqn.getParent().getParent() + " should not be null (before commit)"; tm.commit(); assert cache.getNode(testFqn) != null : testFqn + " should not be null (after commit)"; assert cache.getNode(testFqn.getParent()) != null : testFqn.getParent() + " should not be null (after commit)"; assert cache.getNode(testFqn.getParent().getParent()) != null : testFqn.getParent().getParent() + " should not be null (after commit)"; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/CacheSPITest.java0000644000175000017500000000617311135624637026237 0ustar moellermoellerpackage org.jboss.cache.api; import org.jboss.cache.CacheSPI; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.util.TestingUtil; 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; import org.jboss.cache.UnitTestCacheFactory; @Test(groups = {"functional", "pessimistic"}, testName = "api.CacheSPITest") public class CacheSPITest { private CacheSPI cache1; private CacheSPI cache2; protected NodeLockingScheme nodeLockingScheme = NodeLockingScheme.PESSIMISTIC; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { Configuration conf1 = UnitTestConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC); Configuration conf2 = UnitTestConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC); conf1.setNodeLockingScheme(nodeLockingScheme); conf2.setNodeLockingScheme(nodeLockingScheme); cache1 = (CacheSPI) new UnitTestCacheFactory().createCache(conf1, false, getClass()); cache2 = (CacheSPI) new UnitTestCacheFactory().createCache(conf2, false, getClass()); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache1, cache2); } 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(60000, true, cache1, cache2); List memb2 = cache2.getMembers(); assertEquals("View has two members", 2, memb1.size()); assertEquals("Both caches have same view", memb1, memb2); cache2.stop(); TestingUtil.blockUntilViewsReceived(60000, true, cache1); memb1 = cache1.getMembers(); assertEquals("View has one member", 1, memb1.size()); assertTrue("Coordinator same", coord.equals(memb1.get(0))); } public void testIsCoordinator() throws Exception { 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-3.2.8.GA/src/test/java/org/jboss/cache/api/DeletedChildResurrectionTest.java0000644000175000017500000000604711131613416031564 0ustar moellermoellerpackage org.jboss.cache.api; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.testng.annotations.Test; import javax.transaction.TransactionManager; import org.jboss.cache.*; /** * 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", "pessimistic"}, sequential = true, testName = "api.DeletedChildResurrectionTest") public class DeletedChildResurrectionTest extends AbstractSingleCacheTest { private CacheSPI 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"; protected NodeLockingScheme nodeLockingScheme = NodeLockingScheme.PESSIMISTIC; protected NodeSPI root; protected TransactionManager txManager; public CacheSPI createCache() { cache = (CacheSPI) new UnitTestCacheFactory().createCache(UnitTestConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true), false, getClass()); cache.getConfiguration().setCacheMode(Configuration.CacheMode.LOCAL); cache.getConfiguration().setCacheLoaderConfig(null); cache.getConfiguration().setNodeLockingScheme(nodeLockingScheme); configure(cache.getConfiguration()); cache.start(); root = cache.getRoot(); txManager = cache.getTransactionManager(); return cache; } protected void configure(Configuration c) { // to be overridden } public void testDeletedChildResurrection1() throws Exception { 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 cache.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. */ public void testDeletedChildResurrection2() throws Exception { 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-3.2.8.GA/src/test/java/org/jboss/cache/api/NodeAPITest.java0000644000175000017500000003447511160132061026064 0ustar moellermoellerpackage org.jboss.cache.api; import org.jboss.cache.*; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import static org.jboss.cache.config.Configuration.NodeLockingScheme.OPTIMISTIC; import static org.jboss.cache.config.Configuration.NodeLockingScheme.PESSIMISTIC; import org.jboss.cache.interceptors.MVCCLockingInterceptor; 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.OptimisticTransactionContext; import static org.testng.AssertJUnit.*; import org.testng.annotations.Test; import javax.transaction.TransactionManager; import java.util.*; /** * Tests {@link org.jboss.cache.Node}-centric operations * * @author Manik Surtani * @since 2.0.0 */ @Test(groups = {"functional", "pessimistic"}, testName = "api.NodeAPITest") public class NodeAPITest extends AbstractSingleCacheTest { protected static final Fqn A = Fqn.fromString("/a"), B = Fqn.fromString("/b"), C = Fqn.fromString("/c"), D = Fqn .fromString("/d"); protected Fqn A_B = Fqn.fromRelativeFqn(A, B); protected Fqn A_C = Fqn.fromRelativeFqn(A, C); public CacheSPI createCache() { cache = (CacheSPI) new UnitTestCacheFactory().createCache("configs/local-tx.xml", false, getClass()); cache.getConfiguration().setNodeLockingScheme(getNodeLockingScheme()); configure(cache.getConfiguration()); cache.start(); return cache; } protected void configure(Configuration c) { // to be overridden } protected NodeLockingScheme getNodeLockingScheme() { return PESSIMISTIC; } protected void assertNodeLockingScheme() { assert cache.getConfiguration().getNodeLockingScheme() == PESSIMISTIC; boolean interceptorChainOK = false; List chain = cache.getInterceptorChain(); for (CommandInterceptor i : chain) { if (i instanceof PessimisticLockInterceptor) interceptorChainOK = true; if (i instanceof OptimisticNodeInterceptor) assert false : "Not a pessimistic locking chain!!"; if (i instanceof MVCCLockingInterceptor) assert false : "Not a pessimistic locking chain!!"; } assert interceptorChainOK : "Not a pessimistic locking chain!!"; } public void testAddingData() { assertNodeLockingScheme(); Node rootNode = cache.getRoot(); Node nodeA = rootNode.addChild(A); nodeA.put("key", "value"); assertEquals("value", nodeA.get("key")); } public void testAddingDataTx() throws Exception { TransactionManager tm = cache.getTransactionManager(); Node rootNode = cache.getRoot(); tm.begin(); Node nodeA = rootNode.addChild(A); nodeA.put("key", "value"); assertEquals("value", nodeA.get("key")); tm.commit(); } public void testOverwritingDataTx() throws Exception { TransactionManager tm = cache.getTransactionManager(); Node rootNode = cache.getRoot(); 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 rootNode = cache.getRoot(); 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 { TransactionManager tm = cache.getTransactionManager(); Node rootNode = cache.getRoot(); tm.begin(); Node nodeA = rootNode.addChild(A); Node nodeB = nodeA.addChild(B); Node nodeC = nodeB.addChild(C); if (getNodeLockingScheme() != OPTIMISTIC) { assertEquals(3, cache.getNumberOfNodes()); assertEquals(4, cache.getNumberOfLocksHeld()); } tm.commit(); tm.begin(); assertEquals(0, cache.getNumberOfLocksHeld()); nodeC.put("key", "value"); if (getNodeLockingScheme() != OPTIMISTIC) assertEquals(4, cache.getNumberOfLocksHeld()); tm.commit(); } public void testImmutabilityOfData() { Node rootNode = cache.getRoot(); 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() { Node rootNode = cache.getRoot(); 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() { Node rootNode = cache.getRoot(); 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() { Node rootNode = cache.getRoot(); rootNode.addChild(A); try { rootNode.getChildren().clear(); fail("Collection of child nodes returned in getChildrenDirect() should be immutable"); } catch (Exception e) { // expected } } protected void childrenUnderTxCheck() throws Exception { assertEquals(3, cache.getNumberOfNodes()); assertEquals(4, cache.getNumberOfLocksHeld()); } public void testGetChildrenUnderTx() throws Exception { TransactionManager tm = cache.getTransactionManager(); tm.begin(); cache.put(A_B, "1", "1"); cache.put(A_C, "2", "2"); childrenUnderTxCheck(); assertEquals("Number of child", 2, cache.getRoot().getChild(A).getChildren().size()); tm.commit(); } @SuppressWarnings("unchecked") protected TransactionWorkspace getTransactionWorkspace() throws Exception { TransactionManager tm = cache.getTransactionManager(); return ((OptimisticTransactionContext) cache.getTransactionTable().get(cache.getTransactionTable().get(tm.getTransaction()))).getTransactionWorkSpace(); } public void testGetChildAPI() { Node rootNode = cache.getRoot(); // creates a Node with fqn /a/b/c Node childA = rootNode.addChild(A); childA.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() { Node rootNode = cache.getRoot(); 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 { TransactionManager tm = cache.getTransactionManager(); Node rootNode = cache.getRoot(); 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() { Node rootNode = cache.getRoot(); 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 { TransactionManager tm = cache.getTransactionManager(); Node rootNode = cache.getRoot(); 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 { TransactionManager tm = cache.getTransactionManager(); cache.put("/foo/1/2/3", "item", 1); assert 1 == (Integer) cache.get("/foo/1/2/3", "item"); tm.begin(); assert 1 == (Integer) cache.get("/foo/1/2/3", "item"); 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 { TransactionManager tm = cache.getTransactionManager(); 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")); } public void testIsLeaf() { cache.put("/a/b/c", "k", "v"); cache.put("/a/d", "k", "v"); Node A = cache.getNode("/a"); Node B = cache.getNode("/a/b"); Node C = cache.getNode("/a/b/c"); Node D = cache.getNode("/a/d"); Node root = cache.getRoot(); assert !root.isLeaf(); assert !A.isLeaf(); assert !B.isLeaf(); assert C.isLeaf(); assert D.isLeaf(); cache.removeNode("/a/b"); cache.removeNode("/a/d"); assert A.isLeaf(); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/DestroyedCacheAPITest.java0000644000175000017500000002070511120367722030066 0ustar moellermoellerpackage org.jboss.cache.api; 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; import org.jboss.cache.*; import org.testng.annotations.AfterMethod; /** * 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", "pessimistic"}, sequential = true, testName = "api.DestroyedCacheAPITest") public class DestroyedCacheAPITest extends AbstractSingleCacheTest { 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; public CacheSPI createCache() { // start a single cache instance UnitTestCacheFactory cf = new UnitTestCacheFactory(); cache = cf.createCache("configs/local-tx.xml", false, getClass()); 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(); return (CacheSPI) cache; } /** * 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() { assertNotNull(cache.getRegion(parent, false)); // should end up with the root region 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()); } public void testGetVersion() { assertEquals(version, cache.getVersion()); } @CacheListener public class Listener { @NodeCreated public void nodeCreated(Event e) { } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/NodeMoveAPIWithCLTest.java0000644000175000017500000001420411142045063027757 0ustar moellermoellerpackage org.jboss.cache.api; import org.testng.annotations.Test; import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNull; import org.jboss.cache.*; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.parsing.XmlConfigHelper; import org.jboss.cache.config.parsing.element.LoadersElementParser; import org.jboss.cache.loader.testloaders.DummyInMemoryCacheLoader; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.w3c.dom.Element; import javax.transaction.TransactionManager; import java.util.Map; /** * @author Mircea.Markus@jboss.com */ @Test(groups = {"functional", "pessimistic"}, testName = "api.NodeMoveAPIWithCLTest") public class NodeMoveAPIWithCLTest extends AbstractSingleCacheTest { protected final Log log = LogFactory.getLog(getClass()); 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 static final Object k = "key", vA = "valueA", vB = "valueB", vC = "valueC", vD = "valueD", vE = "valueE"; protected Configuration.NodeLockingScheme nodeLockingScheme = Configuration.NodeLockingScheme.PESSIMISTIC; private TransactionManager tm; protected CacheSPI createCache() { // start a single cache instance CacheSPI cache = (CacheSPI) new UnitTestCacheFactory().createCache("configs/local-tx.xml", false, getClass()); cache.getConfiguration().setNodeLockingScheme(nodeLockingScheme); cache.getConfiguration().setFetchInMemoryState(false); cache.getConfiguration().setEvictionConfig(null); configure(cache.getConfiguration()); cache.start(); tm = cache.getTransactionManager(); return cache; } protected void configure(Configuration c) { // to be overridden } public void testWithCacheloaders() throws Exception { doCacheLoaderTest(false, false); } public void testWithPassivation() throws Exception { doCacheLoaderTest(true, false); } public void testWithCacheloadersTx() throws Exception { doCacheLoaderTest(false, true); } public void testWithPassivationTx() throws Exception { doCacheLoaderTest(true, true); } protected void doCacheLoaderTest(boolean pasv, boolean useTx) throws Exception { Node rootNode = cache.getRoot(); cache.destroy(); cache.getConfiguration().setCacheLoaderConfig(getSingleCacheLoaderConfig(pasv, "/", DummyInMemoryCacheLoader.class.getName(), "debug=true", 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")); } Node nodeA = rootNode.addChild(A); nodeA.put(k, vA); Node nodeB = rootNode.addChild(B); nodeB.put(k, vB); Node nodeC = nodeA.addChild(C); nodeC.put(k, vC); Node nodeD = nodeC.addChild(D); nodeD.put(k, vD); Node 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; 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)); } protected CacheLoaderConfig getSingleCacheLoaderConfig(boolean passivation, String preload, String cacheloaderClass, String properties, boolean async, boolean fetchPersistentState, boolean shared, boolean purgeOnStartup) throws Exception { String xml = " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + properties + " \n" + " \n" + " "; Element element = XmlConfigHelper.stringToElementInCoreNS(xml); LoadersElementParser elementParser = new LoadersElementParser(); return elementParser.parseLoadersElement(element); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/api/CacheAPITest.java0000644000175000017500000003150411160141267026200 0ustar moellermoellerpackage org.jboss.cache.api; import org.jboss.cache.AbstractSingleCacheTest; 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 org.jboss.cache.Region; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.NodeNotExistsException; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; 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 static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; 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", "pessimistic"}, sequential = true, testName = "api.CacheAPITest") public class CacheAPITest extends AbstractSingleCacheTest { protected CacheSPI cache; private List events; public CacheSPI createCache() { // start a single cache instance UnitTestCacheFactory cf = new UnitTestCacheFactory(); cache = (CacheSPI) cf.createCache("configs/local-tx.xml", false, getClass()); cache.getConfiguration().setEvictionConfig(null); configure(cache.getConfiguration()); cache.start(); events = new ArrayList(); return cache; } protected void configure(Configuration c) { c.setNodeLockingScheme(getNodeLockingScheme()); } @AfterMethod(alwaysRun = true) public void tearDown() { events.clear(); cache.getRegionManager().reset(); } protected NodeLockingScheme getNodeLockingScheme() { return NodeLockingScheme.PESSIMISTIC; } /** * 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)); // remove should REALLY remove though and not just mark as deleted/invalid. NodeSPI n = cache.peek(fqn, true, true); assert n == null; assertEquals(false, cache.removeNode(fqn)); // remove should REALLY remove though and not just mark as deleted/invalid. n = cache.peek(fqn, true, true); assert n == null; // 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 null since no eviction is in use. assert otherRegion == null; } 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() { assert cache.peek(Fqn.fromString("/a/b/c"), true, true) == null; assert !cache.removeNode("/a/b/c"); assert cache.peek(Fqn.fromString("/a/b/c"), true, true) == null; assert cache.peek(Fqn.fromString("/a/b"), true, true) == null; assert cache.peek(Fqn.fromString("/a"), true, true) == null; } public void testPhantomStructuralNodesOnRemoveTransactional() throws Exception { TransactionManager tm = cache.getTransactionManager(); assert cache.peek(Fqn.fromString("/a/b/c"), true, true) == null; tm.begin(); assert !cache.removeNode("/a/b/c"); tm.commit(); assert cache.peek(Fqn.fromString("/a/b/c"), true, true) == null; assert cache.peek(Fqn.fromString("/a/b"), true, true) == null; assert cache.peek(Fqn.fromString("/a"), true, true) == null; } public void testIsLeaf() { cache.put("/a/b/c", "k", "v"); cache.put("/a/d", "k", "v"); assert !cache.isLeaf(Fqn.ROOT); assert !cache.isLeaf("/a"); assert !cache.isLeaf("/a/b"); assert cache.isLeaf("/a/d"); assert cache.isLeaf("/a/b/c"); cache.removeNode("/a/b"); cache.removeNode("/a/d"); assert cache.isLeaf("/a"); try { assert cache.isLeaf("/a/b"); assert false; } catch (NodeNotExistsException expected) { assert true; } } 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-3.2.8.GA/src/test/java/org/jboss/cache/manager/0000755000175000017500000000000011675221576024013 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/manager/CacheManagerTest.java0000644000175000017500000001172011111047320027767 0ustar moellermoeller/* * 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; import org.jboss.cache.util.TestingUtil; /** * Tests CacheRegistryImpl. * * @author Brian Stansberry */ @Test(groups = {"functional"}, sequential = true, testName = "manager.CacheManagerTest") public class CacheManagerTest { /** * A file that includes every configuration element I could think of */ public static final String DEFAULT_CONFIGURATION_FILE = "jbc3-registry-configs.xml"; private Set> caches = new HashSet>(); @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { for (Cache cache : caches) { TestingUtil.killCaches(cache); } caches.clear(); } /** * 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-3.2.8.GA/src/test/java/org/jboss/cache/CacheFactoryTest.java0000644000175000017500000001455311120367722026434 0ustar moellermoeller/* * 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.parsing.XmlConfigurationParser; import org.jboss.cache.lock.IsolationLevel; 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 java.io.InputStream; /** * @author Manik Surtani (manik AT jboss DOT org) */ @Test(groups = {"functional"}, sequential = true, testName = "CacheFactoryTest") public class CacheFactoryTest { Configuration expected; String configFile = "configs/replSync.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) { TestingUtil.killCaches(cache); } cache = null; expected = null; } public void testLoadOldConfig() { cache = (CacheSPI) new UnitTestCacheFactory().createCache("configs/conf2x/buddy-replication-cache.xml", getClass()); assert cache.getCacheStatus() == CacheStatus.STARTED : "Should have started"; } public void testFromConfigFileStarted() { cache = (CacheSPI) new UnitTestCacheFactory().createCache(configFile, getClass()); // 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 UnitTestCacheFactory().createCache(configFile, false, getClass()); // 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 UnitTestCacheFactory().createCache(expected, getClass()); assert cache.getCacheStatus() == CacheStatus.STARTED : "Should have started"; doSimpleConfTests(cache.getConfiguration()); } public void testFromConfigObjUnstarted() { cache = (CacheSPI) new UnitTestCacheFactory().createCache(expected, false, getClass()); 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 UnitTestCacheFactory().createCache(expected, false, getClass()); 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); UnitTestCacheFactory cf = new UnitTestCacheFactory(); cache = (CacheSPI) cf.createCache(is, getClass()); assert cache.getCacheStatus() == CacheStatus.STARTED : "Should have started"; doSimpleConfTests(cache.getConfiguration()); } public void testCreationFromStream() throws Exception { InputStream is = getClass().getClassLoader().getResourceAsStream(configFile); UnitTestCacheFactory cf = new UnitTestCacheFactory(); cache = (CacheSPI) cf.createCache(is, false, getClass()); assert cache.getCacheStatus() != CacheStatus.STARTED : "Should not have started"; doSimpleConfTests(cache.getConfiguration()); } public void testComponentsInjected() throws Exception { UnitTestCacheFactory cf = new UnitTestCacheFactory(); Configuration c = new Configuration(); c.setCacheMode(Configuration.CacheMode.REPL_SYNC); c.setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cache = (CacheSPI) cf.createCache(c, getClass()); 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-3.2.8.GA/src/test/java/org/jboss/cache/VersionConversionTest.java0000644000175000017500000000657611111047320027567 0ustar moellermoellerpackage org.jboss.cache; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.fail; import org.testng.annotations.Test; @Test(groups = {"functional"}, testName = "VersionConversionTest") 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-3.2.8.GA/src/test/java/org/jboss/cache/CallbackTest.java0000644000175000017500000001301011120422620025545 0ustar moellermoellerpackage 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 org.jboss.cache.util.TestingUtil; 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 7305 2008-12-12 08:49:20Z mircea.markus $ */ @Test(groups = "functional", testName = "CallbackTest") public class CallbackTest { CacheSPI cache = null; 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 { TestingUtil.killCaches(cache); } 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 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)); 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)); 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.setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); c.setIsolationLevel(level); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); return (CacheSPI) new UnitTestCacheFactory().createCache(c, getClass()); } 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)) { c.put(FQN_B, KEY, VALUE); } } catch (CacheException ex) { fail("listener was unable to update cache during callback: " + ex); } } } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/replicated/0000755000175000017500000000000011675221602024503 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/replicated/SyncReplTest.java0000644000175000017500000001224311131613416027742 0ustar moellermoeller/* * 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.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.Node; import org.jboss.cache.NodeSPI; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.UnitTestConfigurationFactory; 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 AT jboss DOT org) */ @Test(groups = {"functional", "jgroups"}, testName = "replicated.SyncReplTest") public class SyncReplTest { private ThreadLocal[]> cachesTL = new ThreadLocal[]>(); @BeforeMethod(alwaysRun = true) public void setUp() { Cache[] caches = new Cache[2]; caches[0] = new UnitTestCacheFactory().createCache(UnitTestConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), getClass()); caches[1] = new UnitTestCacheFactory().createCache(UnitTestConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), getClass()); cachesTL.set(caches); TestingUtil.blockUntilViewsReceived(caches, 5000); } @AfterMethod(alwaysRun = true) public void tearDown() { Cache[] caches = cachesTL.get(); if (caches != null) TestingUtil.killCaches(caches); cachesTL.set(null); } public void testBasicOperation() { Cache[] caches = cachesTL.get(); 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() { Cache[] caches = cachesTL.get(); 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() { Cache[] caches = cachesTL.get(); // 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) { Cache[] caches = cachesTL.get(); 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() { Cache[] caches = cachesTL.get(); for (Cache c : caches) { assertInvocationContextInitState(c); } } private void assertInvocationContextInitState(Cache c) { InvocationContext ctx = c.getInvocationContext(); InvocationContext control; control = ctx.copy(); control.reset(); assertEquals("Should be equal", control, ctx); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/replicated/AsyncReplTest.java0000755000175000017500000002011411131613416030102 0ustar moellermoeller/* * * 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.Fqn; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.replicationlisteners.ReplicationListener; 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 org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.config.Configuration; /** * Unit test for replicated async CacheSPI. Use locking and multiple threads to test * concurrent access to the tree. * * @version $Revision: 7422 $ */ @Test(groups = {"functional", "jgroups"}, testName = "replicated.AsyncReplTest") public class AsyncReplTest { private class AsyncReplTestTL { private CacheSPI cache1, cache2; private ReplicationListener replListener1, replListener2; } private ThreadLocal threadLocal = new ThreadLocal(); @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { AsyncReplTestTL tl = new AsyncReplTestTL(); threadLocal.set(tl); tl.cache1 = createCache("CacheGroup"); tl.replListener1 = ReplicationListener.getReplicationListener(tl.cache1); tl.cache2 = createCache("CacheGroup"); tl.replListener2 = ReplicationListener.getReplicationListener(tl.cache2); } private CacheSPI createCache(String name) throws Exception { Configuration c = UnitTestConfigurationFactory.createConfiguration(CacheMode.REPL_ASYNC); c.setClusterName(name); CacheSPI cache = (CacheSPI) new UnitTestCacheFactory().createCache(c, false, getClass()); // 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 { AsyncReplTestTL tl = threadLocal.get(); TestingUtil.killCaches(tl.cache1, tl.cache2); threadLocal.set(null); } public void testTxCompletion() throws Exception { AsyncReplTestTL tl = threadLocal.get(); CacheSPI cache1 = tl.cache1; CacheSPI cache2 = tl.cache2; ReplicationListener replListener1 = tl.replListener1; ReplicationListener replListener2 = tl.replListener2; // test a very simple replication. Fqn fqn = Fqn.fromString("/a"); String key = "key"; replListener2.expect(PutKeyValueCommand.class); cache1.put(fqn, key, "value1"); // allow for replication replListener2.waitForReplicationToOccur(); assertEquals("value1", cache1.get(fqn, key)); assertEquals("value1", cache2.get(fqn, key)); TransactionManager mgr = cache1.getTransactionManager(); mgr.begin(); replListener2.expect(PutKeyValueCommand.class); cache1.put(fqn, key, "value2"); assertEquals("value2", cache1.get(fqn, key)); assertEquals("value1", cache2.get(fqn, key)); mgr.commit(); replListener2.waitForReplicationToOccur(); 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() { AsyncReplTestTL tl = threadLocal.get(); CacheSPI cache1 = tl.cache1; CacheSPI cache2 = tl.cache2; ReplicationListener replListener1 = tl.replListener1; ReplicationListener replListener2 = tl.replListener2; CacheSPI cache3 = null, cache4 = null; try { cache3 = createCache("DifferentGroup"); cache4 = createCache("DifferentGroup"); replListener2.expect(PutKeyValueCommand.class); 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() { AsyncReplTestTL tl = threadLocal.get(); CacheSPI cache1 = tl.cache1; CacheSPI cache2 = tl.cache2; ReplicationListener replListener1 = tl.replListener1; ReplicationListener replListener2 = tl.replListener2; CacheSPI cache4 = null; try { cache1.put("a/b/c", "age", 38); cache4 = createCache("CacheGroup"); 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) { cache4.stop(); } } } public void testAsyncReplDelay() { Integer age; AsyncReplTestTL tl = threadLocal.get(); CacheSPI cache1 = tl.cache1; CacheSPI cache2 = tl.cache2; ReplicationListener replListener1 = tl.replListener1; ReplicationListener replListener2 = tl.replListener2; 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"); assertTrue("should be either null or 38", age == null || age == 38); } catch (Exception e) { fail(e.toString()); } } public void testAsyncReplTxDelay() { Integer age; AsyncReplTestTL tl = threadLocal.get(); CacheSPI cache1 = tl.cache1; CacheSPI cache2 = tl.cache2; ReplicationListener replListener1 = tl.replListener1; ReplicationListener replListener2 = tl.replListener2; 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"); assertTrue("should be either null or 38", age == null || age == 38); } catch (Exception e) { fail(e.toString()); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/replicated/SyncCacheListenerTest.java0000644000175000017500000002017011120422127031542 0ustar moellermoeller/* * * 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.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; 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; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.util.TestingUtil; /** * Test out the TreeCacheListener * * @version $Revision: 7301 $ */ @Test(groups = {"functional"}, sequential = true, testName = "replicated.SyncCacheListenerTest") 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 { //old_factory = System.getProperty(Context.INITIAL_CONTEXT_FACTORY); System.setProperty(Context.INITIAL_CONTEXT_FACTORY, FACTORY); initCaches(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { // We just can't kill DummyTransactionManager. We are sharing single instance in more tests. TestingUtil.killTransaction(DummyTransactionManager.getInstance()); destroyCaches(); /* if (old_factory != null) { System.setProperty(Context.INITIAL_CONTEXT_FACTORY, old_factory); old_factory = null; } */ } private void initCaches() { Configuration conf1 = new Configuration(); Configuration conf2 = new Configuration(); conf1.setCacheMode(Configuration.CacheMode.REPL_SYNC); conf2.setCacheMode(Configuration.CacheMode.REPL_SYNC); conf1.setIsolationLevel(IsolationLevel.SERIALIZABLE); conf2.setIsolationLevel(IsolationLevel.SERIALIZABLE); conf1.setNodeLockingScheme(NodeLockingScheme.PESSIMISTIC); conf2.setNodeLockingScheme(NodeLockingScheme.PESSIMISTIC); conf1.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); conf2.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); /* cache1.setTransactionManagerLookupClass("org.jboss.cache.transaction.GenericTransactionManagerLookup"); cache2.setTransactionManagerLookupClass("org.jboss.cache.transaction.GenericTransactionManagerLookup"); */ conf1.setLockAcquisitionTimeout(5000); conf2.setLockAcquisitionTimeout(5000); cache1 = (CacheSPI) new UnitTestCacheFactory().createCache(conf1, false, getClass()); cache2 = (CacheSPI) new UnitTestCacheFactory().createCache(conf2, false, getClass()); cache1.start(); cache2.start(); } private void destroyCaches() { TestingUtil.killCaches(cache1, cache2); cache1 = null; cache2 = null; } 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) { log_.debug("Callback got event " + e); assertFalse("node was removed on remote cache so isLocal should be false", e.isOriginLocal()); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/replicated/PessimisticSyncReplTxTest.java0000644000175000017500000010412111120422127032463 0ustar moellermoeller/* * * 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.UnitTestCacheFactory; 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: 7301 $ */ @Test(groups = {"functional", "jgroups", "transaction"}, sequential = true, testName = "replicated.PessimisticSyncReplTxTest") public class PessimisticSyncReplTxTest { private static Log log = LogFactory.getLog(PessimisticSyncReplTxTest.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, true); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killTransaction(TransactionSetup.getManager()); 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 { Configuration c1 = new Configuration(); Configuration c2 = new Configuration(); c1.setCacheMode(caching_mode); c2.setCacheMode(caching_mode); c1.setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); c2.setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); c1.setIsolationLevel(IsolationLevel.SERIALIZABLE); c2.setIsolationLevel(IsolationLevel.SERIALIZABLE); c1.setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); c2.setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); c1.setLockAcquisitionTimeout(5000); c2.setLockAcquisitionTimeout(5000); cache1 = (CacheSPI) new UnitTestCacheFactory().createCache(c1, false, getClass()); cache2 = (CacheSPI) new UnitTestCacheFactory().createCache(c2, false, getClass()); 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() { TestingUtil.killCaches(cache1, cache2); 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); 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"); 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(); */ } 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); cache2.put(NODE2, "age", 39); try { tm.commit(); fail("Should not succeed with SERIALIZABLE semantics"); } catch (Exception e) { //should be a classic deadlock here. } /* 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()); } 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); cache2.put(NODE, "age", 39); try { tm.commit(); fail("commit should throw a RollbackException, we should not get here"); } catch (RollbackException rollback) { } 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); // 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) { } assertEquals(0, cache1.getNumberOfLocksHeld()); assertEquals(0, cache2.getNumberOfLocksHeld()); assertEquals(0, 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); // 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(); tm.resume(tx); try { tm.commit(); fail("commit should throw a RollbackException, we should not get here"); } catch (RollbackException rollback) { } finally { tm.resume(tx2); tm.rollback(); } // Sleep, as the commit call to cache2 is async TestingUtil.sleepThread(1000); //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(); } 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(); cache1.put("/bela/ban", "name", "Michelle Ban"); tm.commit(); } 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(); Configuration conf1 = new Configuration(); Configuration conf2 = new Configuration(); conf1.setClusterName("TempCluster"); conf2.setClusterName("TempCluster"); conf1.setCacheMode(Configuration.CacheMode.REPL_SYNC); conf2.setCacheMode(Configuration.CacheMode.REPL_SYNC); conf1.setSyncCommitPhase(true); conf2.setSyncCommitPhase(true); conf1.setSyncRollbackPhase(true); conf2.setSyncRollbackPhase(true); conf1.setIsolationLevel(IsolationLevel.REPEATABLE_READ); conf2.setIsolationLevel(IsolationLevel.REPEATABLE_READ); conf1.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); conf2.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); conf1.setLockAcquisitionTimeout(5000); conf2.setLockAcquisitionTimeout(5000); final CacheSPI c1 = (CacheSPI) new UnitTestCacheFactory().createCache(conf1, false, getClass()); final CacheSPI c2 = (CacheSPI) new UnitTestCacheFactory().createCache(conf2, false, getClass()); 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); synchronized (mutex) { mutex.wait(); } tm.commit(); } 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]; thread.start(); } TestingUtil.sleepThread(6000); synchronized (myMutex) { myMutex.notifyAll(); } for (MyThread thread : threads) { try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } } 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) { } 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(); c1.put("/a/b/c", "age", 38); lock.release(); TestingUtil.sleepThread(300); Thread.yield(); lock.acquire(); c1.put("/a/b/c", "age", 39); 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(); // Should replicate the value right away. Integer val = (Integer) cache2.get("/a/b/c", "age"); assertEquals(new Integer(38), val); lock.release(); TestingUtil.sleepThread(300); Thread.yield(); TestingUtil.sleepThread(500); lock.acquire(); val = (Integer) cache2.get("/a/b/c", "age"); 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; final Semaphore threadOneFirstPart = new Semaphore(0); final Semaphore threadTwoFirstPart = new Semaphore(0); final Semaphore threadOneSecondPart = new Semaphore(0); Thread t1 = new Thread() { public void run() { TransactionManager tm; try { tm = beginTransaction(); c1.put("/a/b/c", "age", 38); c1.put("/a/b/c", "age", 39); threadOneFirstPart.release(); threadTwoFirstPart.acquire(); try { tm.commit(); } catch (RollbackException ex) { } finally { threadOneSecondPart.release(); } } catch (Throwable ex) { ex.printStackTrace(); t1_ex = ex; } } }; Thread t2 = new Thread() { public void run() { TransactionManager tm; try { threadOneFirstPart.acquire(); tm = beginTransaction(); assertNull(cache2.get("/a/b/c", "age"));// must be null as not yet committed cache2.put("/a/b/c", "age", 40); threadTwoFirstPart.release(); threadOneSecondPart.acquire(); assertEquals(40, cache2.get("/a/b/c", "age"));// must not be null tm.commit(); 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-3.2.8.GA/src/test/java/org/jboss/cache/replicated/ReplicationExceptionTest.java0000644000175000017500000001232511132625723032341 0ustar moellermoeller/* * * 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.CacheSPI; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.transaction.DummyTransactionManager; 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 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: 7451 $ */ @Test(groups = {"functional"}, sequential = true, testName = "replicated.ReplicationExceptionTest") 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 { // We just can't kill DummyTransactionManager. We are sharing single instance in more tests. TestingUtil.killTransaction(DummyTransactionManager.getInstance()); 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) { UnitTestCacheFactory instance = new UnitTestCacheFactory(); Configuration conf1 = new Configuration(); Configuration conf2 = new Configuration(); conf1.setCacheMode(caching_mode); conf2.setCacheMode(caching_mode); conf1.setIsolationLevel(IsolationLevel.SERIALIZABLE); conf2.setIsolationLevel(IsolationLevel.SERIALIZABLE); conf1.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); conf2.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); /* cache1.setTransactionManagerLookupClass("org.jboss.cache.transaction.GenericTransactionManagerLookup"); cache2.setTransactionManagerLookupClass("org.jboss.cache.transaction.GenericTransactionManagerLookup"); */ conf1.setLockAcquisitionTimeout(5000); conf2.setLockAcquisitionTimeout(5000); cache1 = (CacheSPI) instance.createCache(conf1, false, getClass()); cache2 = (CacheSPI) instance.createCache(conf2, false, getClass()); cache1.start(); cache2.start(); } void destroyCaches() throws Exception { TestingUtil.killCaches(cache1, cache2); 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)) { 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) { } 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-3.2.8.GA/src/test/java/org/jboss/cache/replicated/ExceptionTest.java0000644000175000017500000000617511131613416030150 0ustar moellermoellerpackage org.jboss.cache.replicated; import org.jboss.cache.Cache; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestConfigurationFactory; import org.jboss.cache.lock.TimeoutException; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.jboss.cache.util.TestingUtil; 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"}, testName = "replicated.ExceptionTest") public class ExceptionTest { private Cache cache1; private Cache cache2; private Fqn fqn = Fqn.fromString("/a"); @BeforeMethod public void setUp() { Configuration c = new Configuration(); c.setSyncCommitPhase(true); c.setSyncRollbackPhase(true); c.setCacheMode(Configuration.CacheMode.REPL_SYNC); c.setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cache1 = new UnitTestCacheFactory().createCache(c, false, getClass()); c = new Configuration(); c.setSyncCommitPhase(true); c.setSyncRollbackPhase(true); c.setCacheMode(Configuration.CacheMode.REPL_SYNC); c.setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cache2 = new UnitTestCacheFactory().createCache(c, false, getClass()); } @AfterMethod public void tearDown() { TestingUtil.killCaches(cache1, cache2); cache1 = null; cache2 = null; } @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 = UnitTestConfigurationFactory.getEmptyConfiguration().getClusterConfig(); String newCfg = UnitTestConfigurationFactory.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-3.2.8.GA/src/test/java/org/jboss/cache/util/0000755000175000017500000000000011675221612023345 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/util/MinMapUtilTest.java0000644000175000017500000000367711111047320027067 0ustar moellermoellerpackage 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.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @Test(groups = {"functional", "transaction"}, sequential = true, testName = "util.MinMapUtilTest") 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; } @AfterMethod public void tearDown() { map = null; } 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-3.2.8.GA/src/test/java/org/jboss/cache/util/UnitTestDatabaseManager.java0000644000175000017500000001042211130373015030675 0ustar moellermoellerpackage org.jboss.cache.util; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; import java.util.StringTokenizer; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * @author Mircea.Markus@jboss.com */ public class UnitTestDatabaseManager { private static final Properties realProps = new Properties(); private static AtomicInteger userIndex = new AtomicInteger(0); static { //so that all individual databases are created here try { InputStream stream = new FileLookup().getAsInputStreamFromClassLoader("cache-jdbc.properties"); realProps.load(stream); } catch (IOException e) { e.printStackTrace(); throw new Error("Missing property file: cache-jdbc.properties", e); } } public static Properties getTestDbProperties() { synchronized (realProps) { return returnBasedOnDifferentInstance(); } } public static void shutdownInMemoryDatabase(Properties props) { Connection conn = null; Statement st = null; try { String shutDownConnection = getShutdownUrl(props); String url = props.getProperty("cache.jdbc.url"); assert url != null; conn = DriverManager.getConnection(shutDownConnection); st = conn.createStatement(); st.execute("SHUTDOWN"); } catch (Throwable e) { throw new IllegalStateException(e); } finally { try { conn.close(); st.close(); } catch (SQLException e) { e.printStackTrace(); } } } public static void clearDatabaseFiles(Properties props) { //now delete the disk folder String dbName = getDatabaseName(props); String toDel = TestingUtil.TEST_FILES + File.separator + dbName; TestingUtil.recursiveFileRemove(toDel); } public static String getDatabaseName(Properties prop) { //jdbc:hsqldb:mem:jbosscache StringTokenizer tokenizer = new StringTokenizer(prop.getProperty("cache.jdbc.url"), ":"); tokenizer.nextToken(); tokenizer.nextToken(); tokenizer.nextToken(); return tokenizer.nextToken(); } private static String getShutdownUrl(Properties props) { String url = props.getProperty("cache.jdbc.url"); assert url != null; //jdbc:derby:jbossdb;create=true StringTokenizer tokenizer = new StringTokenizer(url, ";"); String result = tokenizer.nextToken() + ";" + "shutdown=true"; return result; } private static Properties returnBasedOnDifferentInstance() { //jdbc:hsqldb:mem:jbosscache Properties toReturn = (Properties) realProps.clone(); String jdbcUrl = toReturn.getProperty("cache.jdbc.url"); Pattern pattern = Pattern.compile("jbosscache"); Matcher matcher = pattern.matcher(jdbcUrl); boolean found = matcher.find(); assert found; String newJdbcUrl = matcher.replaceFirst(extractTestName() + userIndex.incrementAndGet()); toReturn.put("cache.jdbc.url", newJdbcUrl); return toReturn; } private static String extractTestName() { StackTraceElement[] stack = Thread.currentThread().getStackTrace(); if (stack.length == 0) return null; for (int i = stack.length - 1; i > 0; i--) { StackTraceElement e = stack[i]; String className = e.getClassName(); if (className.indexOf("org.jboss.cache") != -1) return className.replace('.', '_') + "_" + e.getMethodName(); } return null; } private static Properties returnBasedOnDifferentTables() { Properties toReturn = (Properties) realProps.clone(); String tableName = toReturn.getProperty("cache.jdbc.table.name"); int currentIndex = userIndex.incrementAndGet(); toReturn.setProperty("cache.jdbc.table.name", tableName + currentIndex); String pk = toReturn.getProperty("cache.jdbc.table.primarykey"); toReturn.setProperty("cache.jdbc.table.primarykey", pk + currentIndex); return toReturn; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/util/internals/0000755000175000017500000000000011675221605025346 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/util/internals/replicationlisteners/0000755000175000017500000000000011675221604031607 5ustar moellermoeller././@LongLink0000000000000000000000000000016400000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/util/internals/replicationlisteners/ReplicationListener.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/util/internals/replicationlisteners/Replicati0000644000175000017500000002400111142423305033432 0ustar moellermoellerpackage org.jboss.cache.util.internals.replicationlisteners; import org.jboss.cache.Cache; import org.jboss.cache.RPCManager; import org.jboss.cache.CacheSPI; import org.jboss.cache.CacheStatus; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.remote.*; import org.jboss.cache.commands.tx.PrepareCommand; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.marshall.CommandAwareRpcDispatcher; import org.jboss.cache.marshall.ReplicationObserver; import org.jboss.cache.util.TestingUtil; import org.jgroups.Address; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; 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: *

 *   no tx:
 *       Cache c1, c2; //these being two async caches
 *       ReplicationListener listener2 = ReplicationListener.getReplicationListener(c2);
 *       listener2.expect(PutKeyValueCommand.class);
 *       c1.put(fqn, key, value);
 *       listener2.waitForReplicationToOccur(1000); // -this will block here untill c2 recieves the PutKeyValueCommand command
 *   with tx: (difference is that expectWithTx is used insted of expect
 *       Cache c1, c2; //these being two async caches
 *       ReplicationListener listener2 = ReplicationListener.getReplicationListener(c2);
 *       listener2.expectWithTx(PutKeyValueCommand.class);
 *       txManager.begin();
 *       c1.put(fqn, key, value);
 *       txManager.commit();
 *       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 */ abstract public class ReplicationListener implements ReplicationObserver { public static final long DEFAULT_TIMEOUT = 10000; private CountDownLatch latch = new CountDownLatch(1); protected List> expectedCommands; protected Configuration config; protected final Address localAddress; private Cache cache; /** * Builds a listener that will observe the given cache for recieving replication commands. */ protected ReplicationListener(Cache cache) { ComponentRegistry componentRegistry = TestingUtil.extractComponentRegistry(cache); RPCManager rpcManager = componentRegistry.getComponent(RPCManager.class); CommandAwareRpcDispatcher realDispatcher = (CommandAwareRpcDispatcher) TestingUtil.extractField(rpcManager, "rpcDispatcher"); if (realDispatcher.setReplicationObserver(this) != null) { throw new RuntimeException("Replication listener already present"); } this.localAddress = cache.getLocalAddress(); this.config = cache.getConfiguration(); this.cache = cache; } protected ReplicationListener() { localAddress = null; } abstract public void expect(Class... expectedCommands); /** * Based on cache's configuration, will know for what specific commands to expect to be replicated. * E.g. async replication with a tx, would expect only a PrepareCommand (async is 1PC). sync repl with tx would expect * a prepare and a commit (sync is 2pc). */ abstract public void expectWithTx(Class... writeCommands); /** * Factory method, to be used in order to obtain a replication listener based on a cache config. */ public static ReplicationListener getReplicationListener(Cache cache) { if (cache.getConfiguration().getCacheMode().isInvalidation()) { return new InvalidationReplicationListener(cache); } if (cache.getConfiguration().getNodeLockingScheme().equals(Configuration.NodeLockingScheme.OPTIMISTIC)) { return new OptimisticReplicationListener(cache); } else if (cache.getConfiguration().getNodeLockingScheme().equals(Configuration.NodeLockingScheme.PESSIMISTIC)) { return new PessReplicationListener(cache); } else { return new MvccReplicationListener(cache); } } public void afterExecutingCommand(ReplicableCommand realOne) { if (expectedCommands == null) { log("skipping command " + realOne); return; } log("Processed command: " + realOne); if (realOne instanceof ReplicateCommand) { postReplicateExecution((ReplicateCommand) realOne); } else { postNonVisitableExecution(realOne); } if (expectedCommands.isEmpty()) { latch.countDown(); } } private void log(String s) { System.out.println("[" + localAddress + "] " + s); } protected void postNonVisitableExecution(ReplicableCommand realOne) { if (!expectedCommands.remove(realOne.getClass())) { log("not expecting command " + realOne + " "); } } protected void postReplicateExecution(ReplicateCommand realOne) { if (!realOne.removeCommands(expectedCommands)) { if (realOne.getSingleModification() instanceof PrepareCommand) { Iterator> it = expectedCommands.iterator(); while (it.hasNext()) { Class replicableCommandClass = it.next(); PrepareCommand prepareCommand = (PrepareCommand) realOne.getSingleModification(); if (prepareCommand.containsModificationType(replicableCommandClass)) { it.remove(); break;//only remove once } } } } } /** * 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) { CacheStatus state = ((CacheSPI) cache).getComponentRegistry().getState(); if (!state.equals(CacheStatus.STARTED)) { throw new IllegalStateException("Cannot invoke on an cache that is not started: current cache status is " + state); } // log("enter... ReplicationListener.waitForReplicationToOccur"); waitForReplicationToOccur(timeoutMillis, TimeUnit.MILLISECONDS); // log("exit... ReplicationListener.waitForReplicationToOccur"); } /** * same as {@link #waitForReplicationToOccur(long)}, just that it uses the {@link #DEFAULT_TIMEOUT} for timeout. */ public void waitForReplicationToOccur() { waitForReplicationToOccur(DEFAULT_TIMEOUT); } /** * 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.expectWithTx(...) before calling this method"; try { if (!expectedCommands.isEmpty() && !latch.await(timeout, timeUnit)) { assert false : "[" + localAddress + "] 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. */ protected void internalExpect(Class... expectedCommands) { if (this.expectedCommands == null) { this.expectedCommands = new ArrayList>(); } this.expectedCommands.addAll(Arrays.asList(expectedCommands)); } public void reset() { if (expectedCommands != null) expectedCommands.clear(); } protected boolean isRemoteCommand(Class clazz) { return clazz.equals(AnnounceBuddyPoolNameCommand.class) || clazz.equals(AssignToBuddyGroupCommand.class) || clazz.equals(ClusteredGetCommand.class) || clazz.equals(DataGravitationCleanupCommand.class) || clazz.equals(RemoveFromBuddyGroupCommand.class) || clazz.equals(ReplicateCommand.class); } public Cache getCache() { return cache; } } //[127.0.0.1:7900] Processed command: ReplicateCommand{cmds=PrepareCommand{globalTransaction=GlobalTransaction:<127.0.0.1:7903>:481, modifications=[PutDataMapCommand{fqn=/_BUDDY_BACKUP_/127.0.0.1_7903/JSESSION/BuddyReplicationFailoverTest54_localhost/160, dataVersion=null, data={2=org.jboss.cache.integration.websession.util.SessionMetadata@93a0d8, 1=1233588409000, 3={key=2}, 0=3}, globalTransaction=null, erase=false}], localAddress=127.0.0.1:7903, onePhaseCommit=true}} //[127.0.0.1:7900] skipping command ReplicateCommand{cmds=PrepareCommand{globalTransaction=GlobalTransaction:<127.0.0.1:7903>:482, modifications=[PutDataMapCommand{fqn=/_BUDDY_BACKUP_/127.0.0.1_7903/JSESSION/BuddyReplicationFailoverTest54_localhost/160, dataVersion=null, data={1=1233588409031, 3={key=3}, 0=4}, globalTransaction=null, erase=false}], localAddress=127.0.0.1:7903, onePhaseCommit=true}} ././@LongLink0000000000000000000000000000020000000000000011555 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/util/internals/replicationlisteners/InvalidationReplicationListener.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/util/internals/replicationlisteners/Invalidat0000644000175000017500000000311511111047320033427 0ustar moellermoellerpackage org.jboss.cache.util.internals.replicationlisteners; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.legacy.write.VersionedInvalidateCommand; import org.jboss.cache.commands.write.InvalidateCommand; import org.jboss.cache.commands.write.PutForExternalReadCommand; import org.jboss.cache.Cache; import org.jboss.cache.config.Configuration; /** * Specialization of ReplicationListener for caches that use invalidation. * * @author Mircea.Markus@jboss.com */ public class InvalidationReplicationListener extends ReplicationListener { Class base; public InvalidationReplicationListener(Cache cache) { super(cache); if (cache.getConfiguration().getNodeLockingScheme().equals(Configuration.NodeLockingScheme.OPTIMISTIC)) { base = VersionedInvalidateCommand.class; } else { base = InvalidateCommand.class; } } public void expect(Class... expectedCommands) { expectInvalidations(expectedCommands); } public void expectWithTx(Class... writeCommands) { expectInvalidations(writeCommands); } private void expectInvalidations(Class... commands) { for (Class command : commands) { if (command.equals(PutForExternalReadCommand.class)) { internalExpect();//so that the map won't be empty } else { internalExpect(base); } } } } ././@LongLink0000000000000000000000000000017600000000000011571 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/util/internals/replicationlisteners/OptimisticReplicationListener.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/util/internals/replicationlisteners/Optimisti0000644000175000017500000000500511135117673033514 0ustar moellermoellerpackage org.jboss.cache.util.internals.replicationlisteners; import org.jboss.cache.Cache; 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.OptimisticPrepareCommand; import java.util.List; import java.util.ArrayList; import java.util.Iterator; /** * Specialization of ReplicationListener for optimistic caches. * * @author Mircea.Markus@jboss.com */ public class OptimisticReplicationListener extends ReplicationListener { public OptimisticReplicationListener(Cache cache) { super(cache); } /** * For each command, expect an OptimisticPrepare and an commit. */ public void expect(Class... expectedCommands) { //in the case of optimistic replication, an prepare and an commit is expected for each node for (Class command : expectedCommands) { if (isRemoteCommand(command)) { internalExpect(command); } else { internalExpect(OptimisticPrepareCommand.class, CommitCommand.class); } } } /** * For all given commands expect a single prepare, and a single commit. */ public void expectWithTx(Class... writeCommands) { internalExpect(OptimisticPrepareCommand.class, CommitCommand.class); } protected void postReplicateExecution(ReplicateCommand realOne) { List mods = getAllModifications(realOne); for (Iterator> typeIt = expectedCommands.iterator(); typeIt.hasNext();) { Class commadType = typeIt.next(); Iterator instanceIt = mods.iterator(); while (instanceIt.hasNext()) { ReplicableCommand replicableCommand = instanceIt.next(); if (replicableCommand.getClass().equals(commadType)) { instanceIt.remove(); typeIt.remove(); } } } } private List getAllModifications(ReplicateCommand realOne) { List result = new ArrayList(); if (realOne.isSingleCommand()) { result.add(realOne.getSingleModification()); } else { result.addAll(realOne.getModifications()); } return result; } } ././@LongLink0000000000000000000000000000017000000000000011563 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/util/internals/replicationlisteners/MvccReplicationListener.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/util/internals/replicationlisteners/MvccRepli0000644000175000017500000000174111111047320033403 0ustar moellermoellerpackage org.jboss.cache.util.internals.replicationlisteners; import org.jboss.cache.Cache; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.tx.PrepareCommand; import org.jboss.cache.commands.tx.CommitCommand; /** * Specialization of ReplicationListener for mvcc caches. * * @author Mircea.Markus@jboss.com */ public class MvccReplicationListener extends ReplicationListener { public MvccReplicationListener(Cache cache) { super(cache); } /** * all the commands should be received at the other end. */ public void expect(Class... expectedCommands) { internalExpect(expectedCommands); } public void expectWithTx(Class... writeCommands) { internalExpect(PrepareCommand.class); //this is because for async replication we have an 1pc transaction if (config.getCacheMode().isSynchronous()) internalExpect(CommitCommand.class); } } ././@LongLink0000000000000000000000000000017000000000000011563 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/util/internals/replicationlisteners/PessReplicationListener.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/util/internals/replicationlisteners/PessRepli0000644000175000017500000000566011135332460033441 0ustar moellermoellerpackage org.jboss.cache.util.internals.replicationlisteners; import org.jboss.cache.Cache; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.read.GravitateDataCommand; import org.jboss.cache.commands.remote.DataGravitationCleanupCommand; import org.jboss.cache.commands.tx.PrepareCommand; import org.jboss.cache.commands.tx.CommitCommand; import org.jboss.cache.commands.legacy.write.*; import org.jboss.cache.commands.legacy.read.LegacyGravitateDataCommand; import org.jboss.cache.commands.write.*; import java.util.Map; import java.util.HashMap; /** * Specialization of ReplicationListener for optimistic caches. * * @author Mircea.Markus@jboss.com */ public class PessReplicationListener extends ReplicationListener { private static Map, Class> mvcc2PessMap = new HashMap, Class>(); static { mvcc2PessMap.put(ClearDataCommand.class, PessClearDataCommand.class); mvcc2PessMap.put(MoveCommand.class, PessMoveCommand.class); mvcc2PessMap.put(PutDataMapCommand.class, PessPutDataMapCommand.class); mvcc2PessMap.put(PutForExternalReadCommand.class, PessPutForExternalReadCommand.class); mvcc2PessMap.put(PutKeyValueCommand.class, PessPutKeyValueCommand.class); mvcc2PessMap.put(RemoveKeyCommand.class, PessRemoveKeyCommand.class); mvcc2PessMap.put(RemoveNodeCommand.class, PessRemoveNodeCommand.class); mvcc2PessMap.put(DataGravitationCleanupCommand.class, DataGravitationCleanupCommand.class); mvcc2PessMap.put(GravitateDataCommand.class, LegacyGravitateDataCommand.class); } public PessReplicationListener(Cache cache) { super(cache); } /** * In this scenario, all the commands shold be replaced wiht their pessimistic cunterparts. */ public void expect(Class... expectedCommands) { for (Class command: expectedCommands) { super.internalExpect(getPessCommand(command)); } } public void expectWithTx(Class... commands) { for (Class command : commands) { if (command.equals(PessPutForExternalReadCommand.class)) { throw new IllegalArgumentException("PFER are not part of tx, use no-tx .expect()"); } } internalExpect(PrepareCommand.class); if (config.getCacheMode().isSynchronous()) { internalExpect(CommitCommand.class); } } private Class getPessCommand(Class command) { Class result = mvcc2PessMap.get((Class) command); if (result == null) throw new IllegalStateException("Unknown command: " + command); return result; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/util/internals/EvictionController.java0000644000175000017500000001257511111047320032030 0ustar moellermoellerpackage 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.RegionRegistry; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.eviction.EvictionTimerTask; import org.jboss.cache.eviction.EvictionTimerTask.Task; import org.jboss.cache.eviction.LRUAlgorithmConfig; import org.jboss.cache.util.TestingUtil; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; /** * 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; RegionManager regionManager; EvictionTimerTask timerTask; long originalWakeupInterval; RegionRegistry rr; public EvictionController(Cache cache) { this.cache = (CacheSPI) cache; 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!!!"); } rr = this.cache.getComponentRegistry().getComponent(RegionRegistry.class); stopEvictionThread(); originalWakeupInterval = cache.getConfiguration().getEvictionConfig().getWakeupInterval(); } public void startEviction() { startEviction(false); } /** * Kick starts the eviction process * * @param restartEvictionTimerTask if true, restarts the eviction timer scheduled executor after manually kicking off an eviction. */ public void startEviction(boolean restartEvictionTimerTask) { try { Method method = EvictionTimerTask.class.getDeclaredMethod("processRegions", new Class[]{}); method.setAccessible(true); method.invoke(timerTask); } catch (Exception e) { e.printStackTrace(); throw new IllegalStateException(e); } if (restartEvictionTimerTask) { timerTask.init(originalWakeupInterval, null, rr); } } /** * 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!"); } long ttl = 0; if (erConfig.getEvictionAlgorithmConfig() instanceof LRUAlgorithmConfig) { LRUAlgorithmConfig configuration = (LRUAlgorithmConfig) erConfig.getEvictionAlgorithmConfig(); ttl = configuration.getTimeToLive(); } else { throw new IllegalArgumentException("Only LRU being handled for now; please add other implementations here"); } TestingUtil.sleepThread(ttl + 500); evictRegion(region); } /** * Only evicts the given region. */ public void evictRegion(String regionStr) throws Exception { for (Region region : rr.values()) { if (region.getEvictionRegionConfig() != null && region.getFqn().equals(Fqn.fromString(regionStr))) { Method method = EvictionTimerTask.class.getDeclaredMethod("handleRegion", Region.class); method.setAccessible(true); method.invoke(timerTask, region); } } } public Signaller getEvictionThreadSignaller() { final Signaller s = new Signaller(); Task signallingTask = timerTask.new Task() { public void run() { s.getToken(); try { super.run(); } finally { s.releaseToken(); } } }; try { Class ettClass = EvictionTimerTask.class; Field f = ettClass.getDeclaredField("task"); f.setAccessible(true); f.set(timerTask, signallingTask); } catch (Exception e) { throw new RuntimeException(e); } timerTask.init(originalWakeupInterval, null, rr); return s; } public void stopEvictionThread() { timerTask.stop(); } public static class Signaller { Semaphore s = new Semaphore(1); public boolean waitForEvictionThreadCompletion(long time, TimeUnit unit) throws InterruptedException { try { return s.tryAcquire(time, unit); } finally { s.release(); } } void getToken() { try { s.acquire(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } void releaseToken() { s.release(); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/util/internals/ViewChangeListener.java0000644000175000017500000000307211111047320031722 0ustar moellermoellerpackage 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-3.2.8.GA/src/test/java/org/jboss/cache/util/internals/EvictionWatcher.java0000644000175000017500000000412311120422476031301 0ustar moellermoellerpackage org.jboss.cache.util.internals; import org.jboss.cache.Cache; import org.jboss.cache.Fqn; import org.jboss.cache.notifications.annotation.CacheListener; import org.jboss.cache.notifications.annotation.NodeEvicted; import org.jboss.cache.notifications.event.NodeEvictedEvent; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * Watches and waits for eviction events * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ @CacheListener public class EvictionWatcher { Cache cache; List fqnsToWaitFor; CountDownLatch latch; EvictionController.Signaller signaller; public EvictionWatcher(Cache cache, Fqn... fqnsToWaitFor) { this(cache, Arrays.asList(fqnsToWaitFor)); } public EvictionWatcher(Cache cache, List fqnsToWaitFor) { this.cache = cache; this.fqnsToWaitFor = new ArrayList(fqnsToWaitFor); latch = new CountDownLatch(fqnsToWaitFor.size()); EvictionController ec = new EvictionController(cache); signaller = ec.getEvictionThreadSignaller(); cache.addCacheListener(this); } @NodeEvicted public void receive(NodeEvictedEvent ee) { boolean xpect = false; if (ee.isPre() && fqnsToWaitFor.contains(ee.getFqn())) { xpect = true; fqnsToWaitFor.remove(ee.getFqn()); latch.countDown(); } } /** * Blocks for an eviction event to happen on all the configured Fqns to wait for. * * @return true if the eviction events occured, false if we timed out. */ public boolean waitForEviction(long timeout, TimeUnit unit) throws InterruptedException { try { boolean evicted = latch.await(timeout, unit); // now make sure the eviction thread has completed. signaller.waitForEvictionThreadCompletion(timeout, unit); return evicted; } finally { cache.removeCacheListener(this); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/util/internals/ReplicationQueueNotifier.java0000644000175000017500000000512311120422476033162 0ustar moellermoellerpackage 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); } } public void waitUntillAllReplicated(long timeout) { synchronized (replicated) { try { replicated.wait(timeout); } catch (InterruptedException e) { throw new RuntimeException(e); } } } private class ReplicationQueueDelegate extends ReplicationQueue { ReplicationQueue original; private ReplicationQueueDelegate(ReplicationQueue original) { this.original = original; } @Override public void flush() { original.flush(); synchronized (replicated) { replicated.notifyAll(); } } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/util/log4j/0000755000175000017500000000000011675221606024367 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/util/log4j/MultipleFilesQuietWriter.java0000644000175000017500000000764511150767153032231 0ustar moellermoellerpackage org.jboss.cache.util.log4j; import org.apache.log4j.helpers.QuietWriter; import org.apache.log4j.spi.ErrorHandler; import java.io.*; import java.util.HashMap; import java.util.Map; /** * @author Mircea.Markus@jboss.com */ public class MultipleFilesQuietWriter extends QuietWriter { public static final String DEFAULT_LOG_FILE = "logs/default.log"; private static Map testName2Qw = new HashMap(); public MultipleFilesQuietWriter(ErrorHandler errorHandler) { super(new Writer() { public void write(char cbuf[], int off, int len) throws IOException { throw new UnsupportedOperationException("Not implemented");//sholdn't be called } public void flush() throws IOException { throw new UnsupportedOperationException("Not implemented");//sholdn't be called } public void close() throws IOException { throw new UnsupportedOperationException("Not implemented");//sholdn't be called } }, errorHandler); File file = new File("logs"); if (!file.isDirectory()) file.mkdir(); } public void write(String string) { try { QuietWriter qw = getQuietWriter(); qw.write(string); } catch (IOException e) { throw new RuntimeException(e); } } private QuietWriter getQuietWriter() throws IOException { String logFile = getTestClass(); if (logFile == null) { logFile = DEFAULT_LOG_FILE; } else { logFile = "logs/" + logFile; } QuietWriter qw = testName2Qw.get(logFile); if (qw == null) { File file = new File(logFile); if (file.exists()) { file.delete(); file.createNewFile(); } FileOutputStream ostream = new FileOutputStream(file); Writer writer = new OutputStreamWriter(ostream); qw = new QuietWriter(writer, errorHandler); testName2Qw.put(logFile, qw); } return qw; } public void flush() { for (QuietWriter qw : testName2Qw.values()) { qw.flush(); } } public void close() throws IOException { for (QuietWriter qw : testName2Qw.values()) { qw.close(); } } public void write(int c) throws IOException { getQuietWriter().write(c); } public void write(char cbuf[], int off, int len) throws IOException { getQuietWriter().write(cbuf, off, len); } public void write(String str, int off, int len) throws IOException { getQuietWriter().write(str, off, len); } public void write(char cbuf[]) throws IOException { getQuietWriter().write(cbuf); } public Writer append(CharSequence csq) throws IOException { return getQuietWriter().append(csq); } public Writer append(CharSequence csq, int start, int end) throws IOException { return getQuietWriter().append(csq, start, end); } public Writer append(char c) throws IOException { return getQuietWriter().append(c); } public String getTestClass() { StackTraceElement[] stack = Thread.currentThread().getStackTrace(); if (stack.length == 0) return null; for (int i = stack.length - 1; i > 0; i--) { StackTraceElement e = stack[i]; String className = e.getClassName(); if (className.indexOf("org.jboss.cache") != -1) return getFileName(className); //+ "." + e.getMethodName(); } return null; } private String getFileName(String className) { String noPackageStr = className.substring("org.jboss.cache.".length()); return noPackageStr.replace('.', '_') + ".log"; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/util/log4j/PerTestFileAppender.java0000644000175000017500000000060011150767153031073 0ustar moellermoellerpackage org.jboss.cache.util.log4j; import org.apache.log4j.WriterAppender; /** * @author Mircea.Markus@jboss.com */ public class PerTestFileAppender extends WriterAppender { public PerTestFileAppender() { } public void activateOptions() { super.activateOptions(); this.qw = new MultipleFilesQuietWriter(getErrorHandler()); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/util/SingleBuddyGravitationHelper.java0000644000175000017500000001566511142045537032005 0ustar moellermoellerpackage org.jboss.cache.util; import org.jboss.cache.Cache; import org.jboss.cache.commands.read.GravitateDataCommand; import org.jboss.cache.commands.remote.DataGravitationCleanupCommand; import org.jboss.cache.commands.write.PutDataMapCommand; import org.jboss.cache.util.internals.replicationlisteners.ReplicationListener; import java.util.List; /** * Helper class for monitoring replication between caches during data gravitation. * Usage: *
 *   import static org.jboss.cache.util.SingleBuddyGravitationHelper
 *    ....
 *
 *    inCaches(cache1, cache2, cache3).dataWillGravitateFrom(0).to(1);
      assertEquals("value", caches.get(1).get(fqn, key)); //this call will cause data gravitation
      expectGravitation();   //here is where the failure will be if gravitation fails
 *
 *  
* * * @author Mircea.Markus@jboss.com */ public class SingleBuddyGravitationHelper { private Cache[] caches; private ReplicationListener[] replicationListeners; private int fromIndex = -1; private int toIndex = -1; static ThreadLocal perThread = new ThreadLocal(); boolean strict = true; /** * Creates an SingleBuddyGravitationHelper based on the list of caches passed in. * The caches sequence is important, caches[i+1] must be a for caches[i]. * Important: it is assumed that caches do not have replication listeners already built. If so use * {@link SingleBuddyGravitationHelper#inReplicationListeners(java.util.List)} methods, as a cache cannot have 2 * replication listener instances. */ public static SingleBuddyGravitationHelper inCaches(Cache... caches) { SingleBuddyGravitationHelper gravitationHelper = new SingleBuddyGravitationHelper(caches); perThread.set(gravitationHelper); return gravitationHelper; } /** * Transforms the List in an array and calls {@link SingleBuddyGravitationHelper#inCaches(org.jboss.cache.Cache[])} */ public static SingleBuddyGravitationHelper inCaches(List caches) { Cache[] cachesArray = (Cache[]) caches.toArray(new Cache[caches.size()]); return inCaches(cachesArray); } /** * Insted of creating a replication listener for each cache, use the already build ReplicationListeners. */ public static SingleBuddyGravitationHelper inReplicationListeners(ReplicationListener... caches) { SingleBuddyGravitationHelper gravitationHelper = new SingleBuddyGravitationHelper(caches); perThread.set(gravitationHelper); return gravitationHelper; } /** * Transforms the suplied list in an array and calls * {@link SingleBuddyGravitationHelper#inReplicationListeners(org.jboss.cache.util.internals.replicationlisteners.ReplicationListener[])} */ public static SingleBuddyGravitationHelper inReplicationListeners(List replListeners) { ReplicationListener[] listeners = (ReplicationListener[]) replListeners.toArray(new ReplicationListener[replListeners.size()]); SingleBuddyGravitationHelper gravitationHelper = new SingleBuddyGravitationHelper(listeners); perThread.set(gravitationHelper); return gravitationHelper; } /** * After the {@link SingleBuddyGravitationHelper#dataWillGravitateFrom(int)} and {@link SingleBuddyGravitationHelper#to(int)} * are called, and after the gravitation call to the cache is done, here we wait for the gravitation commands to replicate. */ public static void expectGravitation() { SingleBuddyGravitationHelper gravitationHelper = perThread.get(); assert gravitationHelper != null : "replication helper should be created. Use inReplicationListeners before calling this"; gravitationHelper.waitForReplication(); } /** * You specify here the original data owner. */ public SingleBuddyGravitationHelper dataWillGravitateFrom(int index) { assertValidIndex(index); this.fromIndex = index; return this; } /** * Here you specify where the data will migrate to. */ public SingleBuddyGravitationHelper to(int index) { toIndex = index; assert fromIndex >= 0 : "Must call dataWillGravitateFrom before this one"; //all other caches must receive an GravitateDataCommand expectGravitateData(); //all other caches will receive an DataGravitationCleanup command expectGravitationCleanup(); //buddy of the new data owner should also receive the data int cacheCount = caches.length; int newDataOwnerIndex = getIndex(caches[toIndex]); int newBuddyIndex = (newDataOwnerIndex == cacheCount - 1) ? 0 : newDataOwnerIndex + 1; replicationListeners[newBuddyIndex].expect(PutDataMapCommand.class); return this; } private SingleBuddyGravitationHelper(Cache[] caches) { this.caches = caches; replicationListeners = new ReplicationListener[caches.length]; for (int i = 0; i < caches.length; i++) { replicationListeners[i] = ReplicationListener.getReplicationListener(caches[i]); } } private SingleBuddyGravitationHelper(ReplicationListener[] replicationListeners) { this.replicationListeners = replicationListeners; caches = new Cache[replicationListeners.length]; for (int i = 0; i < replicationListeners.length; i++) { caches[i] = replicationListeners[i].getCache(); } } private void assertValidIndex(int index) { assert index >= 0 && index < caches.length; } //disable for now private void strict() { this.strict = true; } private int getIndex(Cache cache) { for (int i = 0; i < caches.length; i++) { if (caches[i] == cache) return i; } throw new RuntimeException("cache not found withis cache instances"); } private void expectGravitationCleanup() { if (!strict) { replicationListeners[fromIndex].expect(DataGravitationCleanupCommand.class); return; } for (int i = 0; i < caches.length; i++) { if (i != toIndex) replicationListeners[i].expect(DataGravitationCleanupCommand.class); } } private void expectGravitateData() { if (!strict) return; //this means that we are a buddy of the node we gravitate from, so local gravitation will take place and // no DataGravitation commands will be send accross if (newOwnerIsBuddyOfOldOwner()) { return; } for (int i = 0; i < caches.length; i++) { if (caches[i] != caches[toIndex]) replicationListeners[i].expect(GravitateDataCommand.class); } } private boolean newOwnerIsBuddyOfOldOwner() { return (toIndex - 1 == fromIndex) || (toIndex == 0 && fromIndex == caches.length - 1); } private void waitForReplication() { for (ReplicationListener listener : replicationListeners) { if (listener.getCache() != caches[toIndex]) listener.waitForReplicationToOccur(); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/util/concurrent/0000755000175000017500000000000011675221607025533 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/util/concurrent/locks/0000755000175000017500000000000011675221607026646 5ustar moellermoeller././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/util/concurrent/locks/OwnableReentrantLockTest.javajbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/util/concurrent/locks/OwnableReentrantLockTes0000644000175000017500000002635011120422476033326 0ustar moellermoellerpackage org.jboss.cache.util.concurrent.locks; import org.jboss.cache.factories.context.MVCCContextFactory; import org.jboss.cache.invocation.InvocationContextContainer; import org.jboss.cache.transaction.GlobalTransaction; 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.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @Test(groups = {"unit", "mvcc"}, testName = "util.concurrent.locks.OwnableReentrantLockTest") public class OwnableReentrantLockTest { private InvocationContextContainer getInvocationContextContainer() { InvocationContextContainer icc = new InvocationContextContainer(); icc.injectContextFactory(new MVCCContextFactory()); return icc; } public void testReentrancyThread() { InvocationContextContainer icc = getInvocationContextContainer(); OwnableReentrantLock lock = new OwnableReentrantLock(icc); lock.lock(); // locked by current thread assert lock.getOwner().equals(Thread.currentThread()); assert lock.getHoldCount() == 1; assert lock.tryLock(); assert lock.getOwner().equals(Thread.currentThread()); assert lock.getHoldCount() == 2; lock.unlock(); assert lock.getOwner().equals(Thread.currentThread()); assert lock.getHoldCount() == 1; lock.unlock(); assert lock.getOwner() == null; assert lock.getHoldCount() == 0; } public void testReentrancyGtx() { InvocationContextContainer icc = getInvocationContextContainer(); OwnableReentrantLock lock = new OwnableReentrantLock(icc); // create and set a gtx GlobalTransaction gtx = new GlobalTransaction(); gtx.setId(10); icc.get().setGlobalTransaction(gtx); lock.lock(); // locked by current thread assert lock.getOwner().equals(gtx); assert lock.getHoldCount() == 1; lock.lock(); assert lock.getOwner().equals(gtx); assert lock.getHoldCount() == 2; lock.unlock(); assert lock.getOwner().equals(gtx); assert lock.getHoldCount() == 1; lock.unlock(); assert lock.getOwner() == null; assert lock.getHoldCount() == 0; } public void testReentrancyNotSameGtx() { InvocationContextContainer icc = getInvocationContextContainer(); OwnableReentrantLock lock = new OwnableReentrantLock(icc); // create and set a gtx GlobalTransaction gtx = new GlobalTransaction(); gtx.setId(10); icc.get().setGlobalTransaction(gtx); GlobalTransaction gtx2 = new GlobalTransaction(); gtx2.setId(10); assert gtx != gtx2; lock.lock(); // locked by current thread assert lock.getOwner().equals(gtx); assert lock.getHoldCount() == 1; icc.get().setGlobalTransaction(gtx2); lock.lock(); assert lock.getOwner().equals(gtx2); assert lock.getHoldCount() == 2; lock.unlock(); assert lock.getOwner().equals(gtx2); assert lock.getHoldCount() == 1; icc.get().setGlobalTransaction(gtx); lock.unlock(); assert lock.getOwner() == null; assert lock.getHoldCount() == 0; } public void testThreadLockedByThread() throws InterruptedException { InvocationContextContainer icc = getInvocationContextContainer(); final OwnableReentrantLock lock = new OwnableReentrantLock(icc); final AtomicBoolean acquired = new AtomicBoolean(false); final AtomicBoolean threwExceptionOnRelease = new AtomicBoolean(false); lock.lock(); assert lock.getOwner().equals(Thread.currentThread()); assert lock.getHoldCount() == 1; Thread t = new Thread() { public void run() { try { acquired.set(lock.tryLock(10, TimeUnit.MILLISECONDS)); } catch (InterruptedException e) { // do nothing } try { lock.unlock(); } catch (IllegalMonitorStateException e) { // expected threwExceptionOnRelease.set(true); } } }; t.start(); t.join(); assert !acquired.get() : "Second thread should not have acquired lock"; assert threwExceptionOnRelease.get() : "Second thread should have thrown an exception trying to release lock"; lock.unlock(); assert !lock.isLocked(); } public void testThreadLockedByGtx() throws InterruptedException { InvocationContextContainer icc = getInvocationContextContainer(); final OwnableReentrantLock lock = new OwnableReentrantLock(icc); GlobalTransaction gtx = new GlobalTransaction(); gtx.setId(10); icc.get().setGlobalTransaction(gtx); final AtomicBoolean acquired = new AtomicBoolean(false); final AtomicBoolean threwExceptionOnRelease = new AtomicBoolean(false); lock.lock(); assert lock.getOwner().equals(gtx); assert lock.getHoldCount() == 1; Thread t = new Thread() { public void run() { try { acquired.set(lock.tryLock(10, TimeUnit.MILLISECONDS)); } catch (InterruptedException e) { // do nothing } try { lock.unlock(); } catch (IllegalMonitorStateException e) { // expected threwExceptionOnRelease.set(true); } } }; t.start(); t.join(); assert !acquired.get() : "Second thread should not have acquired lock"; assert threwExceptionOnRelease.get() : "Second thread should have thrown an exception trying to release lock"; lock.unlock(); assert !lock.isLocked(); } public void testGtxLockedByThread() throws InterruptedException { final InvocationContextContainer icc = getInvocationContextContainer(); final OwnableReentrantLock lock = new OwnableReentrantLock(icc); final AtomicBoolean acquired = new AtomicBoolean(false); final AtomicBoolean threwExceptionOnRelease = new AtomicBoolean(false); lock.lock(); assert lock.getOwner().equals(Thread.currentThread()); assert lock.getHoldCount() == 1; Thread t = new Thread() { public void run() { try { GlobalTransaction gtx = new GlobalTransaction(); gtx.setId(10); icc.get().setGlobalTransaction(gtx); acquired.set(lock.tryLock(10, TimeUnit.MILLISECONDS)); } catch (InterruptedException e) { // do nothing } try { lock.unlock(); } catch (IllegalMonitorStateException e) { // expected threwExceptionOnRelease.set(true); } } }; t.start(); t.join(); assert !acquired.get() : "Second thread should not have acquired lock"; assert threwExceptionOnRelease.get() : "Second thread should have thrown an exception trying to release lock"; lock.unlock(); assert !lock.isLocked(); } public void testGtxLockedByGtxFail() throws InterruptedException { final InvocationContextContainer icc = getInvocationContextContainer(); final OwnableReentrantLock lock = new OwnableReentrantLock(icc); final AtomicBoolean acquired = new AtomicBoolean(false); final AtomicBoolean threwExceptionOnRelease = new AtomicBoolean(false); GlobalTransaction gtx = new GlobalTransaction(); gtx.setId(10); icc.get().setGlobalTransaction(gtx); lock.lock(); assert lock.getOwner().equals(gtx); assert lock.getHoldCount() == 1; Thread t = new Thread() { public void run() { try { GlobalTransaction gtx = new GlobalTransaction(); gtx.setId(20); icc.get().setGlobalTransaction(gtx); acquired.set(lock.tryLock(10, TimeUnit.MILLISECONDS)); } catch (InterruptedException e) { // do nothing } try { lock.unlock(); } catch (IllegalMonitorStateException e) { // expected threwExceptionOnRelease.set(true); } } }; t.start(); t.join(); assert !acquired.get() : "Second thread should not have acquired lock"; assert threwExceptionOnRelease.get() : "Second thread should have thrown an exception trying to release lock"; lock.unlock(); assert !lock.isLocked(); } public void testGtxLockedByGtxSuccess() throws InterruptedException { final InvocationContextContainer icc = getInvocationContextContainer(); final OwnableReentrantLock lock = new OwnableReentrantLock(icc); final AtomicBoolean acquired = new AtomicBoolean(false); GlobalTransaction gtx = new GlobalTransaction(); gtx.setId(10); icc.get().setGlobalTransaction(gtx); lock.lock(); assert lock.getOwner().equals(gtx); assert lock.getHoldCount() == 1; Thread t = new Thread() { public void run() { try { GlobalTransaction gtx = new GlobalTransaction(); gtx.setId(10); icc.get().setGlobalTransaction(gtx); acquired.set(lock.tryLock(10, TimeUnit.MILLISECONDS)); } catch (InterruptedException e) { // do nothing } } }; t.start(); t.join(); assert acquired.get() : "Second thread should have acquired lock"; assert lock.getHoldCount() == 2; lock.unlock(); lock.unlock(); assert !lock.isLocked(); } public void satisfyCodeCoverage() throws InterruptedException { final InvocationContextContainer icc = getInvocationContextContainer(); final OwnableReentrantLock lock = new OwnableReentrantLock(icc); lock.lockInterruptibly(); try { lock.newCondition(); assert false : "Should throw exception"; } catch (UnsupportedOperationException uoe) { // good } assert lock.isHeldExclusively(); lock.unlock(); assert !lock.isHeldExclusively(); } public void testSerialization() throws IOException, ClassNotFoundException { final InvocationContextContainer icc = getInvocationContextContainer(); final OwnableReentrantLock lock = new OwnableReentrantLock(icc); lock.lock(); assert lock.isLocked(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(lock); oos.close(); baos.close(); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray())); OwnableReentrantLock l2 = (OwnableReentrantLock) ois.readObject(); assert !l2.isLocked(); assert l2.getOwner() == null; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/util/license/0000755000175000017500000000000011675221610024765 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/util/license/ValidateLicenseHeaders.java0000644000175000017500000004163411111047320032155 0ustar moellermoeller/* * 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.util.license; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.RandomAccessFile; import java.io.SyncFailedException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.logging.Level; import java.util.logging.Logger; /** * A utility which scans all java source files in the cvs tree and validates * the license header prior to the package statement for headers that match * those declared in thirdparty/licenses/license-info.xml * * @author Scott.Stark@jboss.org * @version $Revision: 7168 $ */ public class ValidateLicenseHeaders { /** * Used to strip out diffs due to copyright date ranges */ static final String COPYRIGHT_REGEX = "copyright\\s(\\(c\\))*\\s*[\\d]+(\\s*,\\s*[\\d]+|\\s*-\\s*[\\d]+)*"; static final String DEFAULT_HEADER = "/*\n" + " * JBoss, Home of Professional Open Source.\n" + " * Copyright 2000 - 2008, Red Hat Middleware LLC, and individual contributors\n" + " * as indicated by the @author tags. See the copyright.txt file in the\n" + " * distribution for a full listing of individual contributors.\n" + " *\n" + " * This is free software; you can redistribute it and/or modify it\n" + " * under the terms of the GNU Lesser General Public License as\n" + " * published by the Free Software Foundation; either version 2.1 of\n" + " * the License, or (at your option) any later version.\n" + " *\n" + " * This software is distributed in the hope that it will be useful,\n" + " * but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" + " * Lesser General Public License for more details.\n" + " *\n" + " * You should have received a copy of the GNU Lesser General Public\n" + " * License along with this software; if not, write to the Free\n" + " * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n" + " * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n" + " */\n"; static Logger log = Logger.getLogger("ValidateCopyrightHeaders"); static boolean addDefaultHeader = false; static FileFilter dotJavaFilter = new DotJavaFilter(); /** * The term-headers from the thirdparty/license/license-info.xml */ static TreeMap licenseHeaders = new TreeMap(); /** * Java source files with no license header */ static ArrayList noheaders = new ArrayList(); /** * Java source files with a header that does not match one from licenseHeaders */ static ArrayList invalidheaders = new ArrayList(); /** * Total java source files seen */ static int totalCount; /** * Total out of date jboss headers seen */ static int jbossCount; /** * ValidateLicenseHeaders jboss-src-root * * @param args */ public static void main(String[] args) throws Exception { if (args.length == 0 || args[0].startsWith("-h")) { log.info("Usage: ValidateLicenseHeaders [-addheader] src-root"); System.exit(1); } int rootArg = 0; if (args.length == 2) { if (args[0].startsWith("-add")) addDefaultHeader = true; else { log.severe("Uknown argument: " + args[0]); log.info("Usage: ValidateLicenseHeaders [-addheader] src-root"); System.exit(1); } rootArg = 1; } File jbossSrcRoot = new File(args[rootArg]); if (jbossSrcRoot.exists() == false) { log.info("Src root does not exist, check " + jbossSrcRoot.getAbsolutePath()); System.exit(1); } // URL u = Thread.currentThread().getContextClassLoader().getResource("META-INF/services/javax.xml.parsers.DocumentBuilderFactory"); // System.err.println(u); // // // Load the valid copyright statements for the licenses // File licenseInfo = new File(jbossSrcRoot, "thirdparty/licenses/license-info.xml"); // if( licenseInfo.exists() == false ) // { // log.severe("Failed to find the thirdparty/licenses/license-info.xml under the src root"); // System.exit(1); // } // DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); // DocumentBuilder db = factory.newDocumentBuilder(); // Document doc = db.parse(licenseInfo); // NodeList licenses = doc.getElementsByTagName("license"); // for(int i = 0; i < licenses.getLength(); i ++) // { // Element license = (Element) licenses.item(i); // String key = license.getAttribute("id"); // ArrayList headers = new ArrayList(); // licenseHeaders.put(key, headers); // NodeList copyrights = license.getElementsByTagName("terms-header"); // for(int j = 0; j < copyrights.getLength(); j ++) // { // Element copyright = (Element) copyrights.item(j); // copyright.normalize(); // String id = copyright.getAttribute("id"); // // The id will be blank if there is no id attribute // if( id.length() == 0 ) // continue; // String text = getElementContent(copyright); // if( text == null ) // continue; // // Replace all duplicate whitespace and '*' with a single space // text = text.replaceAll("[\\s*]+", " "); // if( text.length() == 1) // continue; // // text = text.toLowerCase().trim(); // // Replace any copyright date0-date1,date2 with copyright ... // text = text.replaceAll(COPYRIGHT_REGEX, "..."); // LicenseHeader lh = new LicenseHeader(id, text); // headers.add(lh); // } // } log.fine(licenseHeaders.toString()); File[] files = jbossSrcRoot.listFiles(dotJavaFilter); log.info("Root files count: " + files.length); processSourceFiles(files, 0); log.info("Processed " + totalCount); log.info("Updated jboss headers: " + jbossCount); // Files with no headers details log.info("Files with no headers: " + noheaders.size()); FileWriter fw = new FileWriter("NoHeaders.txt"); for (Iterator iter = noheaders.iterator(); iter.hasNext();) { File f = (File) iter.next(); fw.write(f.getAbsolutePath()); fw.write('\n'); } fw.close(); // Files with unknown headers details log.info("Files with invalid headers: " + invalidheaders.size()); fw = new FileWriter("InvalidHeaders.txt"); for (Iterator iter = invalidheaders.iterator(); iter.hasNext();) { File f = (File) iter.next(); fw.write(f.getAbsolutePath()); fw.write('\n'); } fw.close(); // License usage summary log.info("Creating HeadersSummary.txt"); fw = new FileWriter("HeadersSummary.txt"); for (Iterator iter = licenseHeaders.entrySet().iterator(); iter.hasNext();) { Map.Entry entry = (Map.Entry) iter.next(); String key = (String) entry.getKey(); fw.write("+++ License type=" + key); fw.write('\n'); List list = (List) entry.getValue(); Iterator jiter = list.iterator(); while (jiter.hasNext()) { LicenseHeader lh = (LicenseHeader) jiter.next(); fw.write('\t'); fw.write(lh.id); fw.write(", count="); fw.write("" + lh.count); fw.write('\n'); } } fw.close(); } /** * Get all non-comment content from the element. * * @param element * @return the concatenated text/cdata content */ public static String getElementContent(Element element) { if (element == null) return null; NodeList children = element.getChildNodes(); StringBuffer result = new StringBuffer(); for (int i = 0; i < children.getLength(); i++) { Node child = children.item(i); if (child.getNodeType() == Node.TEXT_NODE || child.getNodeType() == Node.CDATA_SECTION_NODE) { result.append(child.getNodeValue()); } else if (child.getNodeType() == Node.COMMENT_NODE) { // Ignore comment nodes } else { result.append(child.getFirstChild()); } } return result.toString().trim(); } /** * Validate the headers of all java source files * * @param files * @param level * @throws IOException */ static void processSourceFiles(File[] files, int level) throws IOException { for (File f : files) { if (level == 0) log.info("processing " + f); if (f.isDirectory()) { File[] children = f.listFiles(dotJavaFilter); processSourceFiles(children, level + 1); } else { parseHeader(f); } } } /** * Read the first comment upto the package ...; statement * * @param javaFile */ static void parseHeader(File javaFile) throws IOException { totalCount++; RandomAccessFile raf = new RandomAccessFile(javaFile, "rw"); String line = raf.readLine(); StringBuffer tmp = new StringBuffer(); long endOfHeader = 0; boolean packageOrImport = false; while (line != null) { long nextEOH = raf.getFilePointer(); line = line.trim(); // Ignore any single line comments if (line.startsWith("//")) { line = raf.readLine(); continue; } // If this is a package/import/class/interface statement break if (line.startsWith("package") || line.startsWith("import") || line.indexOf("class") >= 0 || line.indexOf("interface") >= 0) { packageOrImport = true; break; } // Update the current end of header marker endOfHeader = nextEOH; if (line.startsWith("/**")) tmp.append(line.substring(3)); else if (line.startsWith("/*")) tmp.append(line.substring(2)); else if (line.startsWith("*")) tmp.append(line.substring(1)); else tmp.append(line); tmp.append(' '); line = raf.readLine(); } raf.close(); if (tmp.length() == 0 || !packageOrImport) { addDefaultHeader(javaFile); return; } String text = tmp.toString(); // Replace all duplicate whitespace with a single space text = text.replaceAll("[\\s*]+", " "); text = text.toLowerCase().trim(); // Replace any copyright date0-date1,date2 with copyright ... text = text.replaceAll(COPYRIGHT_REGEX, "..."); if (tmp.length() == 0) { System.out.println("adding default header for " + javaFile); addDefaultHeader(javaFile); return; } else { if (javaFile.toString().contains("BaseEvictionAlgorithm")) { System.out.println("Replacing header for " + javaFile); System.out.println("Existing header: " + tmp); System.out.println("Existing header: " + text); System.out.println("EOH: " + endOfHeader); } replaceHeader(javaFile, endOfHeader); return; } // Search for a matching header // boolean matches = false; // String matchID = null; // Iterator iter = licenseHeaders.entrySet().iterator(); // escape: // while( iter.hasNext() ) // { // Map.Entry entry = (Map.Entry) iter.next(); // String key = (String) entry.getKey(); // System.out.println("Considering key " + key); // List list = (List) entry.getValue(); // Iterator jiter = list.iterator(); // while( jiter.hasNext() ) // { // LicenseHeader lh = (LicenseHeader) jiter.next(); // System.out.println("Considering hdr " + lh); // if( text.startsWith(lh.text) ) // { // matches = true; // matchID = lh.id; // lh.count ++; // lh.usage.add(javaFile); // if( log.isLoggable(Level.FINE) ) // log.fine(javaFile+" matches copyright key="+key+", id="+lh.id); // break escape; // } // } // } // text = null; // tmp.setLength(0); // if(!matches) // invalidheaders.add(javaFile); // else if( matchID.startsWith("jboss") && !matchID.endsWith("#0")) // { // // This is a legacy jboss head that needs to be updated to the default // replaceHeader(javaFile, endOfHeader); // jbossCount ++; // } } /** * Replace a legacy jboss header with the current default header * * @param javaFile - the java source file * @param endOfHeader - the offset to the end of the legacy header * @throws IOException - thrown on failure to replace the header */ static void replaceHeader(File javaFile, long endOfHeader) throws IOException { if (log.isLoggable(Level.FINE)) log.fine("Replacing legacy jboss header in: " + javaFile); RandomAccessFile raf = new RandomAccessFile(javaFile, "rw"); File bakFile = new File(javaFile.getAbsolutePath() + ".bak"); FileOutputStream fos = new FileOutputStream(bakFile); fos.write(DEFAULT_HEADER.getBytes()); FileChannel fc = raf.getChannel(); long count = raf.length() - endOfHeader; fc.transferTo(endOfHeader, count, fos.getChannel()); fc.close(); fos.close(); raf.close(); if (!javaFile.delete()) log.severe("Failed to delete java file: " + javaFile); if (!bakFile.renameTo(javaFile)) throw new SyncFailedException("Failed to replace: " + javaFile); } /** * Add the default jboss lgpl header */ static void addDefaultHeader(File javaFile) throws IOException { if (addDefaultHeader) { FileInputStream fis = new FileInputStream(javaFile); FileChannel fc = fis.getChannel(); int size = (int) fc.size(); ByteBuffer contents = ByteBuffer.allocate(size); fc.read(contents); fis.close(); ByteBuffer hdr = ByteBuffer.wrap(DEFAULT_HEADER.getBytes()); FileOutputStream fos = new FileOutputStream(javaFile); fos.write(hdr.array()); fos.write(contents.array()); fos.close(); } noheaders.add(javaFile); } /** * A class that encapsulates the license id and valid terms header */ static class LicenseHeader { String id; String text; int count; ArrayList usage = new ArrayList(); LicenseHeader(String id, String text) { this.id = id; this.text = text; } } /** * A filter which accepts files ending in .java (but not _Stub.java), or * directories other than gen-src and gen-parsers */ static class DotJavaFilter implements FileFilter { public boolean accept(File pathname) { boolean accept = false; String name = pathname.getName(); if (pathname.isDirectory()) { // Ignore the gen-src directories for generated output accept = !name.equals("gen-src") && !name.equals("gen-parsers") && !name.equals(".svn"); } else { accept = !name.endsWith("_Stub.java") && name.endsWith(".java"); } return accept; } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/util/CachesTest.java0000644000175000017500000001354111120422476026236 0ustar moellermoellerpackage org.jboss.cache.util; import org.jboss.cache.Cache; import org.jboss.cache.DataContainer; import org.jboss.cache.DataContainerImpl; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.invocation.CacheInvocationDelegate; import org.jboss.cache.util.Caches.ChildSelector; import java.util.Collection; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.jboss.cache.UnitTestCacheFactory; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import static org.testng.AssertJUnit.*; /** * Tests {@link Caches}. */ @Test(groups = "functional", sequential = true, testName = "util.CachesTest") public class CachesTest { String a = "a"; String b = "b"; String c = "c"; Cache cache; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache = new UnitTestCacheFactory().createCache(getClass()); } @AfterMethod(alwaysRun = true) public void tearDown() { TestingUtil.killCaches(cache); cache = null; } public void testSegment() { Map m = Caches.asPartitionedMap(cache); // m.put(a, b); testMap(m); m.clear(); int c = 100; for (int i = 0; i < c; i++) { m.put(Integer.toHexString(i), "foo " + i); } for (int i = 0; i < c; i++) { assertEquals("foo " + i, m.get(Integer.toHexString(i))); } } public void testAsMap() { Map m = Caches.asMap(cache); testMap(m); for (Node n : (Set) cache.getRoot().getChildren()) { assertEquals("/a", n.getFqn().toString()); assertEquals(c, n.get("K")); } m.clear(); m.put(a, a); testCollectionRemove(m.keySet()); m.put(a, a); testCollectionRemove(m.values()); m.put(a, a); testCollectionRemove(m.entrySet()); } private void testCollectionRemove(Collection c) { Iterator i; i = c.iterator(); assertEquals(true, i.hasNext()); try { i.remove(); fail("no next"); } catch (IllegalStateException e) { } i.next(); i.remove(); assertEquals(false, i.hasNext()); assertEquals("C " + c, 0, c.size()); } public void testAsSimpleSet() { Set s = Caches.asSimpleSet(cache.getRoot()); testSet(s); } private void testSet(Set s) { assertEquals(0, s.size()); assertEquals(true, s.add(a)); assertEquals(false, s.add(a)); assertEquals(1, s.size()); assertEquals("[a]", s.toString()); assertEquals(true, s.contains(a)); assertEquals(true, s.remove(a)); assertEquals(false, s.remove(a)); assertEquals(false, s.contains(a)); s.add(b); s.clear(); assertEquals(false, s.contains(b)); s.add(c); Iterator i = s.iterator(); s.add(a); s.remove(a); // stable iterator assertTrue(i.hasNext()); assertEquals(c, i.next()); i.remove(); assertEquals(false, i.hasNext()); assertEquals(0, s.size()); assertEquals(true, s.isEmpty()); } private void testMap(Map m) { assertEquals(null, m.put(a, b)); assertEquals(b, m.put(a, c)); assertEquals("{a=c}", m.toString()); assertEquals(1, m.size()); assertEquals("a", m.keySet().iterator().next()); assertEquals(true, m.containsKey(a)); assertEquals(true, m.containsValue(c)); assertEquals(false, m.containsValue(b)); assertEquals(c, m.remove(a)); assertEquals(null, m.remove(a)); assertEquals(0, m.size()); assertEquals(false, m.keySet().iterator().hasNext()); m.put(c, a); assertEquals(1, m.keySet().size()); assertEquals(1, m.entrySet().size()); assertEquals(1, m.values().size()); Iterator i = m.keySet().iterator(); m.put(b, b); m.remove(b); // stable iterator assertEquals(true, i.hasNext()); assertEquals(c, i.next()); assertEquals(false, i.hasNext()); assertEquals(true, m.keySet().contains(c)); assertEquals(true, m.entrySet().contains(new SimpleImmutableEntry(c, a))); assertEquals(true, m.values().contains(a)); assertEquals(false, m.keySet().contains(a)); assertEquals(false, m.entrySet().contains(new SimpleImmutableEntry(a, c))); assertEquals(false, m.values().contains(c)); assertEquals(false, m.isEmpty()); m.clear(); assertEquals(0, m.size()); m.put(a, a); m.clear(); assertEquals(0, m.size()); assertEquals(true, m.isEmpty()); } public void testAsSimpleMap() { Map m = Caches.asSimpleMap(cache.getRoot()); testMap(m); } public void testSelector() { Map m = Caches.asPartitionedMap(cache.getRoot(), new DepartmentSelector()); Person f = new Person("Fred", a); Person g = new Person("George", b); Person h = new Person("Harry", b); // associate person with a number m.put(f, 42); m.put(g, 69); m.put(h, 21); assertEquals(42, m.get(f)); assertEquals(69, m.get(g)); } private String printDetails(Cache c) { DataContainer dc = ((CacheInvocationDelegate) c).getDataContainer(); return ((DataContainerImpl) dc).printDetails(); } public static class Person { String name; String department; public Person(String name, String department) { super(); this.name = name; this.department = department; } public String getDepartment() { return department; } public String getName() { return name; } } public static class DepartmentSelector implements ChildSelector { public Fqn childName(Person key) { return Fqn.fromElements(key.getDepartment()); } } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/util/ImmutableListCopyTest.java0000644000175000017500000000665611252203514030463 0ustar moellermoellerpackage org.jboss.cache.util; 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.Collections; import java.util.List; import java.util.ListIterator; @Test(groups = "unit", testName = "util.ImmutableListCopyTest") public class ImmutableListCopyTest { public void testImmutability() { String o = "one"; List l = Immutables.immutableListCopy(Collections.singletonList(o)); 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(o)); assert false; } catch (UnsupportedOperationException good) { } try { l.retainAll(Collections.singletonList("l")); assert false; } catch (UnsupportedOperationException good) { } try { l.iterator().remove(); assert false; } catch (Exception good) { } try { l.listIterator().set("w"); assert false; } catch (Exception good) { } } public void testListIterator() { List list = Immutables.immutableListWrap(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 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; } public void testSubLists() { List ints = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); List list = Immutables.immutableListCopy(ints); assert ints.subList(2, 5).equals(list.subList(2, 5)); assert ints.subList(1, 9).equals(list.subList(1, 9)); assert ints.subList(0, 1).equals(list.subList(0, 1)); } static Object copy(Object o) throws Exception { ByteArrayOutputStream bo = new ByteArrayOutputStream(); new ObjectOutputStream(bo).writeObject(o); ByteArrayInputStream is = new ByteArrayInputStream(bo.toByteArray()); return new ObjectInputStream(is).readObject(); } public void testSerialization() throws Exception { List ints = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); copy( Immutables.immutableListCopy(ints) ); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/util/DeltaMapTest.java0000644000175000017500000001012611120422476026533 0ustar moellermoellerpackage 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.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @Test(groups = {"functional", "transaction"}, sequential = true, testName = "util.DeltaMapTest") 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); } @AfterMethod public void tearDown() { hm = null; backup = null; dm = null; } 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); 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-3.2.8.GA/src/test/java/org/jboss/cache/util/BitEncodedIntegerSetTest.java0000644000175000017500000000750711111047320031036 0ustar moellermoellerpackage org.jboss.cache.util; import org.testng.annotations.Test; import java.util.HashSet; /** * @author Manik Surtani (manik AT jboss DOT org) * @since 2.1.0 */ @Test(groups = "functional", testName = "util.BitEncodedIntegerSetTest") 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-3.2.8.GA/src/test/java/org/jboss/cache/util/FastCopyHashMapTest.java0000644000175000017500000000311511146273534030045 0ustar moellermoellerpackage org.jboss.cache.util; import org.testng.annotations.Test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Map; @Test(groups = "unit", testName = "util.FastCopyHashMapTest") public class FastCopyHashMapTest { public void testSerialization() throws Exception { Map map = new FastCopyHashMap(); map.put("k1", "v1"); map.put("k2", "v2"); Map map2 = serializeAndDeserialize(map); assert map2 instanceof FastCopyHashMap; assert map2.size() == map.size(); for (Object key : map.keySet()) assert map2.containsKey(key); } public void testNonexistentKey() throws Exception { Map map = new FastCopyHashMap(); map.put("k1", "v1"); map.put("k2", "v2"); assert map.get("dont exist") == null; } public void testNonexistentKeyDeserialized() throws Exception { Map map = new FastCopyHashMap(); map.put("k1", "v1"); map.put("k2", "v2"); Map map2 = serializeAndDeserialize(map); assert map2.get("dont exist") == null; } private T serializeAndDeserialize(T object) throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(object); oos.close(); baos.close(); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray())); Object retval = ois.readObject(); ois.close(); return (T) retval; } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/util/UnitTestTestNGListener.java0000644000175000017500000000475011133360374030566 0ustar moellermoeller/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package org.jboss.cache.util; import java.io.FileOutputStream; import java.io.PrintStream; import org.jboss.cache.UnitTestCacheFactory; import org.testng.IClass; import org.testng.ITestContext; import org.testng.ITestListener; import org.testng.ITestResult; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * * @author dpospisi */ public class UnitTestTestNGListener implements ITestListener { /** * Holds test classes actually running in all threads. */ private ThreadLocal threadTestClass = new ThreadLocal(); Log log = LogFactory.getLog(UnitTestTestNGListener.class); private int failed = 0; private int succeded = 0; private int skipped = 0; public void onTestStart(ITestResult res) { log.info("Starting test " + getTestDesc(res)); threadTestClass.set(res.getTestClass()); } synchronized public void onTestSuccess(ITestResult arg0) { System.out.println(getThreadId() + " Test " + getTestDesc(arg0) + " succeded."); log.info("Test succeded " + getTestDesc(arg0) + "."); succeded++; printStatus(); } synchronized public void onTestFailure(ITestResult arg0) { System.out.println(getThreadId() + " Test " + getTestDesc(arg0) + " failed."); if (arg0.getThrowable() != null) log.error("Test failed " + getTestDesc(arg0), arg0.getThrowable()); failed++; printStatus(); } synchronized public void onTestSkipped(ITestResult arg0) { System.out.println(getThreadId() + " Test " + getTestDesc(arg0) + " skipped."); log.info(" Test " + getTestDesc(arg0) + " skipped."); if (arg0.getThrowable() != null) log.error("Test skipped : " + arg0.getThrowable(), arg0.getThrowable()); skipped++; printStatus(); } public void onTestFailedButWithinSuccessPercentage(ITestResult arg0) { } public void onStart(ITestContext arg0) { } public void onFinish(ITestContext arg0) { } private String getThreadId() { return "["+ Thread.currentThread().getName() + "]"; } private String getTestDesc(ITestResult res) { return res.getMethod().getMethodName() + "(" + res.getTestClass().getName() + ")"; } private void printStatus() { System.out.println("Testsuite execution progress: tests succeded " + succeded + ", failed " + failed + ", skipped " + skipped + "."); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/util/TestingUtil.java0000644000175000017500000006373011325553351026474 0ustar moellermoeller/* * 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.UnitTestCacheFactory; import org.jboss.cache.commands.CommandsFactory; import org.jboss.cache.commands.VisitableCommand; 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.Channel; import org.jgroups.JChannel; import javax.transaction.TransactionManager; import java.io.File; import java.lang.reflect.Field; import java.util.Arrays; import java.util.List; import java.util.Random; import java.util.concurrent.atomic.AtomicInteger; /** * Utilities for unit testing JBossCache. * * @author Brian Stansberry */ public class TestingUtil { private static Random random = new Random(); public static final String TEST_FILES = "testFiles"; /** * Holds unique mcast_port for each thread used for JGroups channel construction. */ private static final ThreadLocal threadID = new ThreadLocal() { private final AtomicInteger uniquePort = new AtomicInteger(0); @Override protected Integer initialValue() { return uniquePort.getAndIncrement(); } }; private static UnitTestCacheFactory utf = new UnitTestCacheFactory(); /** * 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" + views(caches)); } private static String views(Cache... caches) { StringBuilder builder = new StringBuilder("[\n"); for (Cache c:caches) { builder.append(" ").append(c.getLocalAddress()).append("->").append(c.getMembers()).append("\n"); } builder.append("]"); return builder.toString(); } /** * 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" + views(caches)); } /** * 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" + views(caches)); } /** * 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" + views(cache)); } /** * 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; } 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) { try { if (c!= null) utf.removeCache(c); if (c != null) // && ( (c.getCacheStatus() == CacheStatus.STARTED) || c.getCacheStatus() == CacheStatus.FAILED) ) { CacheSPI spi = (CacheSPI) c; Channel channel = null; if (spi.getRPCManager() != null) { channel = spi.getRPCManager().getChannel(); } if (spi.getTransactionManager() != null) { try { spi.getTransactionManager().rollback(); } catch (Throwable t) { // don't care } } CacheLoaderManager clm = spi.getCacheLoaderManager(); CacheLoader cl = clm == null ? null : clm.getCacheLoader(); if (cl != null) { try { cl.remove(Fqn.ROOT); } catch (Throwable t) { // don't care } } try { spi.stop(); } catch (Throwable t) { System.err.println(Thread.currentThread().getName() + " !!!!!!!!!!!!!!!!!!!!! WARNING - Cache instance refused to stop."); t.printStackTrace(); } try { spi.destroy(); } catch (Throwable t) { System.err.println(Thread.currentThread().getName() + " !!!!!!!!!!!!!!!!!!!!! WARNING - Cache instance refused to destroy."); t.printStackTrace(); } if (channel != null) { if (channel.isOpen()) { System.err.println(Thread.currentThread().getName() + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Channel still opened."); Thread.dumpStack(); channel.close(); } if (channel.isOpen()) { System.err.println(Thread.currentThread().getName() + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Channel close failed."); System.exit(-1); } if (channel.isConnected()) { System.err.println(Thread.currentThread().getName() + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Channel still connected."); Thread.dumpStack(); System.exit(-1); } } } } catch (Throwable t) { t.printStackTrace(); System.exit(-1); } } } /** * Kills a cache - stops it, clears any data in any cache loaders, and rolls back any associated txs */ public static void killCaches(boolean clearCacheLoader, Cache... caches) { for (Cache c : caches) { try { if (c!= null) utf.removeCache(c); if (c != null) // && ( (c.getCacheStatus() == CacheStatus.STARTED) || c.getCacheStatus() == CacheStatus.FAILED) ) { CacheSPI spi = (CacheSPI) c; Channel channel = null; if (spi.getRPCManager() != null) { channel = spi.getRPCManager().getChannel(); } if (spi.getTransactionManager() != null) { try { spi.getTransactionManager().rollback(); } catch (Throwable t) { // don't care } } if (clearCacheLoader) { CacheLoaderManager clm = spi.getCacheLoaderManager(); CacheLoader cl = clm == null ? null : clm.getCacheLoader(); if (cl != null) { try { cl.remove(Fqn.ROOT); } catch (Throwable t) { // don't care } } } try { spi.stop(); } catch (Throwable t) { System.err.println(Thread.currentThread().getName() + " !!!!!!!!!!!!!!!!!!!!! WARNING - Cache instance refused to stop."); t.printStackTrace(); } try { spi.destroy(); } catch (Throwable t) { System.err.println(Thread.currentThread().getName() + " !!!!!!!!!!!!!!!!!!!!! WARNING - Cache instance refused to destroy."); t.printStackTrace(); } if (channel != null) { if (channel.isOpen()) { System.err.println(Thread.currentThread().getName() + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Channel still opened."); Thread.dumpStack(); channel.close(); } if (channel.isOpen()) { System.err.println(Thread.currentThread().getName() + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Channel close failed."); System.exit(-1); } if (channel.isConnected()) { System.err.println(Thread.currentThread().getName() + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Channel still connected."); Thread.dumpStack(); System.exit(-1); } } } } catch (Throwable t) { t.printStackTrace(); System.exit(-1); } } } /** * 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 } } } } } /** * Clears transaction with the current thread in the given transaction manager. * @param txManager a TransactionManager to be cleared */ public static void killTransaction(TransactionManager txManager) { if (txManager != null) { try { txManager.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.printCacheDetails(c)); } count++; } System.out.println("**** END: Cache Contents ****"); } public static void dumpCacheContents(Cache... caches) { dumpCacheContents(Arrays.asList(caches)); } public static int getThreadId() { return threadID.get(); } public static CacheLoader getCacheLoader(Cache c) { CacheLoaderManager clm = extractComponentRegistry(c).getComponent(CacheLoaderManager.class); return clm == null ? null : clm.getCacheLoader(); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/invocationcontext/0000755000175000017500000000000011675221613026147 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/invocationcontext/TwoPcTransactionTest.java0000644000175000017500000000572611120575347033127 0ustar moellermoellerpackage org.jboss.cache.invocationcontext; import org.jboss.cache.CacheSPI; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.AbstractSingleCacheTest; import org.jboss.cache.transaction.TransactionTable; import org.jboss.cache.transaction.TransactionContext; import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNull; import javax.transaction.TransactionManager; import javax.transaction.Transaction; /** * @author Mircea.Markus@jboss.com */ public class TwoPcTransactionTest extends AbstractSingleCacheTest { private TransactionManager tm; protected CacheSPI createCache() { cache = (CacheSPI) new UnitTestCacheFactory().createCache("configs/local-tx.xml", getClass()); tm = cache.getTransactionManager(); return cache; } @SuppressWarnings("deprecation") static void doScrubbingTest(CacheSPI cache, TransactionManager tm, 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(); TransactionContext transactionContext = 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 transactionContext hasn't leaked stuff. assert transactionContext.getModifications().isEmpty() : "Should have scrubbed modifications in transaction transactionContext"; assert transactionContext.getLocks().isEmpty() : "Should have scrubbed modifications in transaction transactionContext"; assert transactionContext.getOrderedSynchronizationHandler() == null : "Should have removed the ordered sync handler"; } public void testScrubbingAfterCommit() throws Exception { doScrubbingTest(cache, tm, true); } public void testScrubbingAfterRollback() throws Exception { doScrubbingTest(cache, tm, false); } } jbosscache-core-3.2.8.GA/src/test/java/org/jboss/cache/invocationcontext/OnePcTransactionTest.java0000644000175000017500000000744211120575347033074 0ustar moellermoellerpackage org.jboss.cache.invocationcontext; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.UnitTestCacheFactory; import org.jboss.cache.AbstractSingleCacheTest; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.config.Configuration; import org.jboss.cache.transaction.TransactionContext; import org.jboss.cache.transaction.TransactionTable; import org.jboss.cache.transaction.GenericTransactionManagerLookup; 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.Map; /** * A test to ensure the transactional context is properly set up in the IC * * @author Manik Surtani */ @Test(groups = {"functional", "transaction"}, testName = "invocationcontext.OnePcTransactionTest") public class OnePcTransactionTest extends AbstractSingleCacheTest { private TransactionManager tm; protected CacheSPI createCache() { Configuration config = new Configuration(); config.setTransactionManagerLookupClass(GenericTransactionManagerLookup.class.getName()); config.setCacheMode(CacheMode.REPL_ASYNC); cache = (CacheSPI) new UnitTestCacheFactory().createCache(config, true, getClass()); tm = cache.getTransactionManager(); return cache; } 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 testScrubbingAfterOnePhaseCommit() throws Exception { TwoPcTransactionTest.doScrubbingTest(cache, tm, true); } public void testScrubbingAfterOnePhaseRollback() throws Exception { TwoPcTransactionTest.doScrubbingTest(cache, tm, false); } } jbosscache-core-3.2.8.GA/src/test/resources/0000755000175000017500000000000011675221627020515 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/resources/configs/0000755000175000017500000000000011675221626022144 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/resources/configs/clonable-config.xml0000644000175000017500000001514311236775757025730 0ustar moellermoeller numBuddies = 11 ignoreColocatedBuddies = true location=/tmp/FileCacheLoader pushStateWhenCoordinator=true pushStateWhenCoordinatorTimeout=5000 location=/tmp/BdbjeCacheLoader pushStateWhenCoordinator=true pushStateWhenCoordinatorTimeout=5000 location=/tmp/JdbmCacheLoader pushStateWhenCoordinator=true pushStateWhenCoordinatorTimeout=5000 cache.jdbc.driver=com.foo.jdbc.Driver cache.jdbc.url=foo://driver cache.jdbc.user=sa cache.jdbc.password=secret pushStateWhenCoordinator=true pushStateWhenCoordinatorTimeout=5000 host=127.0.0.1\nport=12121 pushStateWhenCoordinator=true pushStateWhenCoordinatorTimeout=5000 timeout=500 pushStateWhenCoordinator=true pushStateWhenCoordinatorTimeout=5000 jbosscache-core-3.2.8.GA/src/test/resources/configs/local-tx.xml0000644000175000017500000000175611236775757024436 0ustar moellermoeller jbosscache-core-3.2.8.GA/src/test/resources/configs/integration/0000755000175000017500000000000011675221616024466 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/resources/configs/integration/sfsb-cache-configs.xml0000644000175000017500000002565211112450520030626 0ustar moellermoeller PESSIMISTIC REPEATABLE_READ REPL_ASYNC ${jboss.partition.name:JBCTest}-SFSBCache ${jboss.default.jgroups.stack:udp} true 60000 17500 15000 true true 0 0 true false org.jboss.cache.buddyreplication.NextMemberBuddyLocator numBuddies = 1 ignoreColocatedBuddies = true default 17500 false true true true false true org.jboss.cache.loader.FileCacheLoader location=${java.io.tmpdir}${/}sfsb false true false false 5 org.jboss.cache.eviction.NullEvictionPolicy PESSIMISTIC REPEATABLE_READ REPL_ASYNC ${jboss.partition.name:JBCTest}-SFSBCache ${jboss.default.jgroups.stack:udp} true 60000 17500 15000 true true 0 0 true true org.jboss.cache.buddyreplication.NextMemberBuddyLocator numBuddies = 1 ignoreColocatedBuddies = true default 17500 false true true true false true org.jboss.cache.loader.FileCacheLoader location=${java.io.tmpdir}${/}sfsb false true false false 5 org.jboss.cache.eviction.NullEvictionPolicy jbosscache-core-3.2.8.GA/src/test/resources/configs/integration/web-session-cache-configs.xml0000644000175000017500000003733011142423305032127 0ustar moellermoeller org.jboss.cache.transaction.BatchModeTransactionManagerLookup PESSIMISTIC REPEATABLE_READ REPL_ASYNC ${jboss.partition.name:JBCTest}-SessionCache ${jboss.default.jgroups.stack:udp} true 60000 17500 15000 false false 0 0 true false org.jboss.cache.buddyreplication.NextMemberBuddyLocator numBuddies = 1 ignoreColocatedBuddies = true default 17500 false true true true false true org.jboss.cache.loader.FileCacheLoader location=${java.io.tmpdir}${/}session false true false false org.jboss.cache.transaction.BatchModeTransactionManagerLookup PESSIMISTIC REPEATABLE_READ REPL_ASYNC ${jboss.partition.name:JBCTest}-FieldSessionCache ${jboss.default.jgroups.stack:udp} true 60000 17500 15000 true true 0 0 true false org.jboss.cache.buddyreplication.NextMemberBuddyLocator numBuddies = 1 ignoreColocatedBuddies = true default 17500 false true true true false true org.jboss.cache.loader.FileCacheLoader location=${java.io.tmpdir}${/}field-session false true false false 5 org.jboss.cache.eviction.NullEvictionPolicy org.jboss.cache.transaction.BatchModeTransactionManagerLookup PESSIMISTIC REPEATABLE_READ REPL_ASYNC ${jboss.partition.name:JBCTest}-SessionCache ${jboss.default.jgroups.stack:udp} true 60000 17500 15000 false false 0 0 true true org.jboss.cache.buddyreplication.NextMemberBuddyLocator numBuddies = 1 ignoreColocatedBuddies = true default 17500 false true true true false true org.jboss.cache.loader.FileCacheLoader location=${java.io.tmpdir}${/}session false true false false jbosscache-core-3.2.8.GA/src/test/resources/configs/integration/hibernate-cache-configs.xml0000644000175000017500000010107711112450520031626 0ustar moellermoeller OPTIMISTIC INVALIDATION_SYNC ${jboss.partition.name:JBCTest}-optimistic-entity ${jboss.default.jgroups.stack:udp} false 20000 20000 15000 true true 0 0 5 org.jboss.cache.eviction.LRUPolicy 10000 1000 120 PESSIMISTIC READ_COMMITTED INVALIDATION_SYNC ${jboss.partition.name:JBCTest}-pessimistic-entity ${jboss.default.jgroups.stack:udp} false 20000 20000 15000 true true 0 0 5 org.jboss.cache.eviction.LRUPolicy 10000 1000 120 PESSIMISTIC REPEATABLE_READ INVALIDATION_SYNC ${jboss.partition.name:JBCTest}-pessimistic-entity-rr ${jboss.default.jgroups.stack:udp} false 20000 20000 15000 true true 0 0 5 org.jboss.cache.eviction.LRUPolicy 10000 1000 120 OPTIMISTIC LOCAL 15000 5 org.jboss.cache.eviction.LRUPolicy 10000 1000 120 OPTIMISTIC REPL_ASYNC ${jboss.partition.name:JBCTest}-replicated-query ${jboss.default.jgroups.stack:udp} false 20000 20000 15000 true true 0 0 5 org.jboss.cache.eviction.LRUPolicy 10000 1000 120 PESSIMISTIC REPEATABLE_READ REPL_ASYNC ${jboss.partition.name:JBCTest}-timestamps-cache ${jboss.default.jgroups.stack:udp} true 20000 20000 15000 true true 0 0 5 org.jboss.cache.eviction.LRUPolicy 10000 1000 120 OPTIMISTIC REPL_SYNC true true ${jboss.partition.name:JBCTest}-optimistic-shared ${jboss.default.jgroups.stack:udp} true 20000 20000 15000 true true 0 0 5 org.jboss.cache.eviction.LRUPolicy 10000 1000 120 PESSIMISTIC REPEATABLE_READ REPL_SYNC ${jboss.partition.name:JBCTest}-pessimistic-shared ${jboss.default.jgroups.stack:udp} true 20000 20000 15000 true true 0 0 5 org.jboss.cache.eviction.LRUPolicy 10000 1000 120 PESSIMISTIC REPEATABLE_READ REPL_SYNC ${jboss.partition.name:JBCTest}-pessimistic-shared-rr ${jboss.default.jgroups.stack:udp} true 20000 20000 15000 true true 0 0 5 org.jboss.cache.eviction.LRUPolicy 10000 1000 120 jbosscache-core-3.2.8.GA/src/test/resources/configs/integration/jgroups-channelfactory-stacks.xml0000644000175000017500000004105611112450520033153 0ustar moellermoeller '> ]> &shared-udp; jbosscache-core-3.2.8.GA/src/test/resources/configs/parser-test-async.xml0000644000175000017500000001250311236775757026267 0ustar moellermoeller numBuddies = 1 ignoreColocatedBuddies = true cache.jdbc.table.name=jbosscache cache.jdbc.table.create=true cache.jdbc.table.drop=true pushStateWhenCoordinator=true pushStateWhenCoordinatorTimeout=20000 jbosscache-core-3.2.8.GA/src/test/resources/configs/incorrect-3_0-config-file.xml0000644000175000017500000000047711150773520027416 0ustar moellermoeller jbosscache-core-3.2.8.GA/src/test/resources/configs/local-passivation.xml0000644000175000017500000000213411236775757026332 0ustar moellermoeller jbosscache-core-3.2.8.GA/src/test/resources/configs/policyPerRegion-eviction.xml0000644000175000017500000000252311236775757027634 0ustar moellermoeller jbosscache-core-3.2.8.GA/src/test/resources/configs/simple-3_0-config-file.xml0000644000175000017500000000032711150773520026711 0ustar moellermoeller jbosscache-core-3.2.8.GA/src/test/resources/configs/replSync.xml0000644000175000017500000000077411236775757024511 0ustar moellermoeller jbosscache-core-3.2.8.GA/src/test/resources/configs/conf2x/0000755000175000017500000000000011675221624023341 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/test/resources/configs/conf2x/clonable-config.xml0000644000175000017500000002012611111047320027066 0ustar moellermoeller org.jboss.cache.transaction.GenericTransactionManagerLookup OPTIMISTIC SERIALIZABLE REPL_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-3.2.8.GA/src/test/resources/configs/conf2x/pess-local.xml0000644000175000017500000000504011111047320026104 0ustar moellermoeller jboss:service=Naming jboss:service=TransactionManager org.jboss.cache.transaction.GenericTransactionManagerLookup REPEATABLE_READ LOCAL JBossCache-Cluster 20000 15000 10000 jbosscache-core-3.2.8.GA/src/test/resources/configs/conf2x/cacheloader-enabled-cache.xml0000644000175000017500000001240511131164620030735 0ustar moellermoeller 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= false true false false jbosscache-core-3.2.8.GA/src/test/resources/configs/conf2x/multiplexer-enabled-cache.xml0000644000175000017500000001157311111047320031055 0ustar moellermoeller 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-3.2.8.GA/src/test/resources/configs/conf2x/zeroTTL.xml0000644000175000017500000000342211111047320025407 0ustar moellermoeller jboss:service=Naming jboss:service=TransactionManager org.jboss.cache.transaction.GenericTransactionManagerLookup REPEATABLE_READ LOCAL 15000 10 200000 org.jboss.cache.eviction.LRUPolicy 5000 0 1000 0 1000 -1 1000 -10 jbosscache-core-3.2.8.GA/src/test/resources/configs/conf2x/optimistically-locked-cache.xml0000644000175000017500000000634311111047320031417 0ustar moellermoeller jboss:service=Naming jboss:service=TransactionManager org.jboss.cache.transaction.GenericTransactionManagerLookup false true Optimistic READ_COMMITTED REPL_ASYNC true 10000 1 org.jboss.cache.eviction.LRUPolicy 10 8 5 10 1 2 10 1 1 jbosscache-core-3.2.8.GA/src/test/resources/configs/conf2x/policyPerRegion-eviction.xml0000644000175000017500000001475011111047320031002 0ustar moellermoeller jboss:service=Naming jboss:service=TransactionManager org.jboss.cache.transaction.GenericTransactionManagerLookup REPEATABLE_READ REPL_SYNC JBossCache-Cluster123 15000 5 200000 5000 1000 5000 1000 5 10000 10000 8 10 jbosscache-core-3.2.8.GA/src/test/resources/configs/conf2x/eviction-enabled-cache.xml0000644000175000017500000002210411111047320030313 0ustar moellermoeller jboss:service=Naming jboss:service=TransactionManager org.jboss.cache.transaction.GenericTransactionManagerLookup REPEATABLE_READ LOCAL 15000 5 200000 org.jboss.cache.eviction.LRUPolicy 5000 1000 1000 5000 jbosscache-core-3.2.8.GA/src/test/resources/configs/conf2x/local-cache.xml0000644000175000017500000000345311111047320026203 0ustar moellermoeller jboss:service=Naming jboss:service=TransactionManager org.jboss.cache.transaction.GenericTransactionManagerLookup REPEATABLE_READ LOCAL 15000 jbosscache-core-3.2.8.GA/src/test/resources/configs/conf2x/total-replication-cache.xml0000644000175000017500000001347411111047320030547 0ustar moellermoeller jboss:service=Naming jboss:service=TransactionManager org.jboss.cache.transaction.GenericTransactionManagerLookup REPEATABLE_READ REPL_SYNC JBossCache-Cluster true 20000 15000 10000 jbosscache-core-3.2.8.GA/src/test/resources/configs/conf2x/default-test-config2x.xml0000644000175000017500000001213011111047320030156 0ustar moellermoeller jboss:service=Naming jboss:service=TransactionManager org.jboss.cache.transaction.GenericTransactionManagerLookup REPEATABLE_READ REPL_SYNC false 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-3.2.8.GA/src/test/resources/configs/conf2x/buddy-replication-cache.xml0000644000175000017500000001746711122603675030556 0ustar moellermoeller 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-3.2.8.GA/src/test/resources/configs/mvcc-repl-sync-br.xml0000644000175000017500000000506511236775757026153 0ustar moellermoeller numBuddies = 1 ignoreColocatedBuddies = true jbosscache-core-3.2.8.GA/src/test/resources/configs/mixedPolicy-eviction.xml0000644000175000017500000000237011236775757027010 0ustar moellermoeller jbosscache-core-3.2.8.GA/src/test/resources/configs/string-property-replaced.xml0000644000175000017500000000377311236775757027661 0ustar moellermoeller ignoreColocatedBuddies = true numBuddies = ${test.property.BuddyReplicationConfig.numBuddies:1} location=${test.property.CacheLoaderConfiguration.location,java.io.tmpdir:/tmp} jbosscache-core-3.2.8.GA/src/test/resources/configs/local-lru-eviction.xml0000644000175000017500000000260511236775757026415 0ustar moellermoeller jbosscache-core-3.2.8.GA/src/test/resources/configs/mux.xml0000644000175000017500000000061311236775757023513 0ustar moellermoeller jbosscache-core-3.2.8.GA/src/test/resources/configs/parser-test.xml0000644000175000017500000001120111236775757025146 0ustar moellermoeller numBuddies = 1 ignoreColocatedBuddies = true cache.jdbc.table.name=jbosscache cache.jdbc.table.create=true cache.jdbc.table.drop=true pushStateWhenCoordinator=true pushStateWhenCoordinatorTimeout=20000 jbosscache-core-3.2.8.GA/src/test/resources/configs/buddy-replication-cache.xml0000644000175000017500000000537211236775757027370 0ustar moellermoeller numBuddies = 1 ignoreColocatedBuddies = true jbosscache-core-3.2.8.GA/src/test/resources/unit-test-cache-service.xml0000644000175000017500000003363711236775757025721 0ustar moellermoeller jbosscache-core-3.2.8.GA/src/test/resources/jbc3-registry-configs.xml0000644000175000017500000002075111236775757025374 0ustar moellermoeller jbosscache-core-3.2.8.GA/src/test/resources/cache-jdbc.properties0000755000175000017500000000373411131164620024572 0ustar moellermoeller# Standard JBoss Cache 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=BINARY cache.jdbc.parent.column=parent # JBoss Cache Table properties for Hypersonic, just overrides #cache.jdbc.node.type=OBJECT ## # DataSource #cache.jdbc.datasource=DefaultDS ## # JDBC driver specific properties ## MySql #cache.jdbc.driver=com.mysql.jdbc.Driver #cache.jdbc.url=jdbc:mysql://localhost:3306/jbossdb #cache.jdbc.user=root #cache.jdbc.password=admin #cache.jdbc.node.type=BLOB ## 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 #cache.jdbc.node.type=BLOB ## 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 #cache.jdbc.node.type=BLOB ## 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 #cache.jdbc.node.type=BLOB ## HSQL cache.jdbc.driver = org.hsqldb.jdbcDriver cache.jdbc.url=jdbc:hsqldb:mem:jbosscache cache.jdbc.user=sa cache.jdbc.password= jbosscache-core-3.2.8.GA/src/test/resources/log4j.xml0000644000175000017500000000572211145342403022250 0ustar moellermoeller jbosscache-core-3.2.8.GA/src/main/0000755000175000017500000000000011675222134016442 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/docbook/0000755000175000017500000000000011675221643020066 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/docbook/tutorial/0000755000175000017500000000000011675221630021725 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/docbook/tutorial/en/0000755000175000017500000000000011675221630022327 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/docbook/tutorial/en/master.xml0000644000175000017500000002304711236523520024346 0ustar moellermoeller
JBoss Cache Core Edition Tutorial Release 3.2.0 Malagueta August 2009 Manik Surtani manik AT jboss DOT org Galder Zamarreño galder DOT zamarreno AT jboss DOT com 2005 2006 2007 2008 2009 JBoss, a division of Red Hat Inc., and all authors as named.
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 POJO Cache, 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 3.x distribution from the download page. You will need the ALL distribution ( jbosscache-core-3.X.Y.GA-all.zip). Unzip it, and you will get a directory containing the distribution, such as jbosscache-core-3.X.Y . For the sake of this tutorial, I will refer to this as ${JBOSSCACHE_HOME} . The configuration files are located in ${JBOSSCACHE_HOME}/etc. 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. config-samples/total-replication.xml - Cache configuration file used for this tutorial.
Script The only script needed for this tutorial is the ${JBOSSCACHE_HOME}/tutorial/build.xml ant script. You also need to have Apache Ant installed for running the tutorial GUI.
Running The Tutorial GUI The GUI is run by: Changing to the ${JBOSSCACHE_HOME}/tutorial directory (e.g.,cd ${JBOSSCACHE_HOME}/tutorial) And then running the ant script (e.g.,ant run) 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 org.jboss.cache.Cache interface, used by the GUI instance. root - a reference to the root org.jboss.cache.Node instance for the above cache. transactionManager - a reference to the registered javax.transaction.TransactionManager instance. 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 GUI as a replicated tutorial, 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 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 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 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 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-3.2.8.GA/src/main/docbook/images/0000755000175000017500000000000011675221641021331 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/docbook/images/OnlyOneCacheLoader.png0000644000175000017500000004723510660354341025505 0ustar moellermoeller‰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-3.2.8.GA/src/main/docbook/images/Interceptor.png0000644000175000017500000003115410660354341024336 0ustar moellermoeller‰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Ùþÿ -/þÿÿÿ,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿì¥Á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-3.2.8.GA/src/main/docbook/images/BuddyReplication.png0000644000175000017500000001145210660354341025300 0ustar moellermoeller‰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Û…•™ž‘œŸ£¤€}¢¥©ª‹'«®¯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.þÿ (*þÿÿÿ'ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿì¥Á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-3.2.8.GA/src/main/docbook/images/DataVersions.png0000644000175000017500000001576710660354341024456 0ustar moellermoeller‰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*ÕÐÏ(08JqH@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-3.2.8.GA/src/main/docbook/images/LocalCacheLoader.png0000644000175000017500000004754510660354341025160 0ustar moellermoeller‰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-3.2.8.GA/src/main/docbook/images/CacheMgmtInterceptor.png0000644000175000017500000116226610660354341026121 0ustar moellermoeller‰PNG  IHDRÂT“Ï pHYsÃÃÇo¨d IDATxœì˜É#Y^Ç º ›à28‚@#ŽpàO@ƒLOUÚ±:¼;·êê‚aP³iHÐ3h-f†êÌØlçf;+ghæÀ ‰ªÌ´#ÂK¤íØ·«ý8¼¬¨”Y}ìCeè^üÞ‹ïïóßüÚÊ?ùÓ?óàÁ½õögô­ÏüÂÏÿò¯}þ׿øT¢*$^¢†&*4Q¡ÈW°B¥¸QÆ‹UšnÑt£HT60† ª%²ZÆ+ô;d¯í0;ÍÒV‰lÉú£R«PÞúò£ V¨“x«Dm—¨mšÜ"°&V¨ãŪ [oÑäElUJÔV½ú¤Z~L“›$Þ¤ˆV‰ÚÚxXÆ 5o–¨­µE“›ÖÀ +—èZ‰®8ƒi’(ÓT•"+… ²D×êµ­j¥ESU’(Sd¥D7(¢A` ¤S.íÐä&^¬7ª ½æx±ÎÐÛÚ{f/Öp½ú57VH¼…ÚSŠØ$°&:W¹´K“[x±ñR§‰«x±ÊЛڻf/Vi²I`µzõ1Õ6–H¼Ž«Ú»Ѻçy-Ï­:µ ó˜Ä[¹µzù§\ÚÍ·áÅMn¡ ^l ס= ½ƒ¾Ì´Z-ï ¦x±Z.m•K[$^G“•&›¨r‡N¹Õø³jù]ô!àÅjJâ­{ž×ò4Ë¥[u°B/V)¢ÁЛf›¡7:Eq£†ê…"6ó<Á‹Ü0X¡†ëÑbèí ³ËÐÛÖØxX.—vH¼Yܨb…MnæV¿[çúP ½Sa3ô:Ë=ÏkyÊe¦EåbŠ4MUËLƒ¦ª8VBY]¢kY!p†À™]«U· ¬F“› ½]¢¶H¼I` Šh•K;f—Ä›…G•âFµDmU˜]šÜÄ 5¬P!ñz‰jQDƒÀjV+Q­Ze—Àj4ÙdèÍÕ"ñ:Õ(¢Q.mU˜m¯1År‰jU˜mšlb…ÊÝ:7ˆ‡iUÊM’` d±@½äÙD߼ѢˆWp¬L‘•zmsgû=‚h’Äf‰Þ)3)r +6p¬É”v™Ò.·°bƒÀ[Li­‹µzc»ÞØ*Wš8Á‹ IÖhºEQMoÐôV™y\fSäŽmâØ&IlV+O(jÃê8Þ`˜­Je§TÚ$ˆz¹¼Y­n—+M’ªd™¢«]%±ZmãIõá{å†zZ¢Ÿä {Jc_-Ó],|õKž¾S|B–ž”ËÛe²‚mPú YþÊõôÝbk{ƒªáE¢‚?lß©bƒýý_¼ÿ}¾½üï@àAè/Ý™ØB¡çyh†!¼¿ÞìëÁƒ·>ó#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šõƒ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-3.2.8.GA/src/main/docbook/images/PublicAPI.png0000644000175000017500000005230610660354341023612 0ustar moellermoeller‰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-3.2.8.GA/src/main/docbook/images/Listeners.png0000644000175000017500000001246510660354341024014 0ustar moellermoeller‰PNG  IHDR,/K×{üIDATxÚíÝO«%ÅÆñy¾„¼WŠEƒÃ¸ ™¥’‘1â:DÌÐ…«pá* „º! ñu„Yf5P2d˜ÜsºêWÕÕÝÕÝŸ‡ïBÏ=·OŸêso§«o=·’ˆˆˆˆ¬ž[†@DDD„„‰ˆˆˆ0!a""""‡•°EDDD¤5³$ìHŽy°·#"""‡&"""BÂH˜ˆˆˆ0&"""²¥„Ýúo.,>²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-3.2.8.GA/src/main/docbook/images/MultipleCacheLoaders.doc0000644000175000017500000007600010660354341026051 0ustar moellermoellerÐÏࡱá>þÿ 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-3.2.8.GA/src/main/docbook/images/DelegatingCacheLoader.png0000644000175000017500000010463010660354341026156 0ustar moellermoeller‰PNG  IHDR(57¢gAMAÙܲÚ cHRMz&€„ú€èu0ê`:˜pœºQ< pHYs  šœ†IDATxí œÅÙÿA¼Eñ_o$YÑà• 1‡ä`Ô ‚"1B 1U\EpÝ%&bŒ èbâæ}u=65$JT¢$“ ®º¨¨«<ÿúõLmõÎöÌÎÌöQÝýãóYz¦»«êéªçøÎSÕÝÂ?öu XÙÒƒÑöuM»û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-3.2.8.GA/src/main/docbook/images/OnlyOneCacheLoader.doc0000644000175000017500000005400010660354341025452 0ustar moellermoellerÐÏࡱá>þÿ ')þÿÿÿ&ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿì¥Á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-3.2.8.GA/src/main/docbook/images/TransactionLookup.png0000644000175000017500000001267110660354341025522 0ustar moellermoeller‰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Œë|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-3.2.8.GA/src/main/docbook/images/SharedCacheLoader.png0000644000175000017500000005224110660354341025321 0ustar moellermoeller‰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®´''Æ´jUPs*%¿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-3.2.8.GA/src/main/docbook/images/writeskew.png0000644000175000017500000013071511077747365024106 0ustar moellermoeller‰PNG  IHDRÝ`ÙH€IDATxÚìXSg†EQت,•%îÑÖV«þ­£C­£®º÷ž­÷Bˆ¨ˆlp/@†€È'.A@EVØ# [ÿ7 !aKžûz/®ÃÉ¢©ï“ûœóåû}¨4Â[x ¼ðx à%ðÀKà%øº½d$µ:³rà_@¯ßK>?=û¥L>?cÊ´°ž›Õ ó/eñÙŸ_–Ÿ_2eÅ«WTÖE`ó¥Î}dê<¯‚˜ºÀ«×T‹*øÒ—ºüù SWxBuµ°B¯ÕÛë_ê¯Â¨l +œÊ®°"ì¿ÔÍÏïøåðù=SŽ…õÊ©°"¿Ô­Ïùåò9Š)׊vûR·?Ç0åΫX¦îð*ŽÊ£°Xžåõ9ž)o^%0åëDª»…•ä+P÷>'3uŸW)T +•êaa¥=*ªôÇåWXOŠŠýôK=ûÌá×óÏ\¦^ð*“Ê¿¨²^~©WŸ³™ àUS¼Ê¥ *ª¼×_*øs>¯sUñ4, ª—†îe¹×ªÏIx HÌù\:`k¤µøŸß)bNì^b{F+øö©b1÷N4æ*2†®Ì˜ {`E¯ke°™b.!èºÅñM‘Ï.;ö*9æ„ÇÐå±¼Nê®û¦u+z¶k–ÚÕï%¥¡ã–;†îUEÇÐ}΃˜«A/AÃV{Ãúû˜Óóho[bpð_Úp¾| €„¼äkÛo¸{ ¥Ãk·“Ì9aß«:ê]”ÖÎ+×¶µï5K'ªvR”oûÍ”1C2‚®>w>¡Þ¥ý‘í‹è§’|›ã(æ²Âì—ÎÝ©ƒ|WÕŽZ«þabnã²)ª•äÛ}3å¯ÿe„ØQÌYlRëÒ^Q¾Í¼©p#Gþï;z]¹o/méqV]¥ƒÇµ#sîWü< WkÙ–#†~|ÏŠ2îùÞ­Gö® ŸJ mmNoŒ¹ˆg—;¶—ß½y^RÿÆö#æ$æ%hغѰ¡~¬NmÏcy§„ß’’’Ú¿m1€jð’ú<¶_(æÜ¬÷Ò¯ý{ªîZ;ý‘Ý‘njÏßpbÏRÚyÓtçC»£´1d@ÏÛçµûtW¡h£˜³ÒßÐ\¦ÙCûc6›~ÿåûˆ/ouSW>rˉý+x´Ø›b'Ó¬é¬I#.ŸÙÞ²…Ì­ÆzÿÑM‹gŽysÏ‚Z8ô5ÝJgrtCW5e ÕŽYÝÞâÝ:ä§Þ·¯éÓC­SGÁkÕܨÛéï]_úX÷’¯dl?b®Z¼ [{ Ëôúßò©7ö÷±DÃPu/ùªÆö—sWNnf®U‡ûœ5ؽtÌð´ÓèÀJ&æÎðŽºÖ.OÉEþ®§è¸GQ®Í‚þx`Œ9'~ßÒ`ßò1#~ä=ðК‹§·ÒÆ»G6Ÿ£Ý’ƒm…N û¹œfbÎ@{ïñµüçarl#mßw:ùðïÖsFÛ?Ç{­]2™÷¢"1WŽ—ÔÛ±ýˆ9Ix ¶Î5ì>­E¼«9Û— a¨u/©scû ÷”s^PÌÝ>·OºI“9‡[]G;Ï,Œ9'ËÝ”n[WýÓTZš¹P}Ëz߬‰Ãä¾¥[,öܾxPZºÉœÉ#­Žó¢êŒÎZc½¿ÄÜíOÎs# cîD±˜;ºg9mD>¿L1wáÌÞ_b§ÏÄœÓй­ÿÎjÚTZtl¼¤^íGÌÕ¶— a%߰Ǭ¥;lýoö×÷e4,¨u/©ÇcûK޹{&æ|¯êPÌíXCAÖ„õôÜÅ›h§áÞeLÌݲÞ+sG×ýØOóýCËw-›4i|R{厵3è&–ÿ¥‹§xG]†Ú+Y//7n,µfá„GN†íÚ´^»hbäÓ tÓ®õsb_^áÇÜc—Ót$·`ú¨ûކ?è¥(ß&=܉‰¹[—tKˆ¹Är½¤~íGÌIÔKаµß°þ>ô¢ª]ÚŸ>¼þôá ÷n¡a¨1/©Ócûù4•s÷®é|~wó‡QW•"#÷m¯ÐvÍüq%Æ\JàÕßÿ÷]s™f²­ZL5˜j÷ÆÛ´«jGÞ‡ôç=pá„ÏÑ®§­é¦Þ‰2òß%]Ïtí®Ñ™ží𮥅1wîu™Û¨ ׆~UíÜÞÇÞà3ËS æ¼kÛKjzl?b®½ [ »~å4Á¯:ÿ·ü4,’ð’†´ÐFÊËKâ,´‘ùÖ>+ü¦àB)¯¯‹.´Á~ëÀŸ )ï£[âë%NhøÆ m æ$æ%hX4,4 /ÁBXh£ÚÆö#æ$ï%hX4,|]^‚…6°ÐFÍíGÌUÙKаhX4,€—`¡ ,´!¡±ýŸ?!æjÑKаhX4,¨O^ò5Œí/åœpµ/´QJÌUfeœ2c®ÞíGÌIÎKаhX4,hÐ^R?Æö—sÕ¹ÐFùתÅ[h£"cèêëØ~Ä\My  ‹†õ×KÒØþ’ÆÐ‰sQB1W…1t {l?bN^‚†EâaAòŒíÇØþêÛ˜“°— aѰhXЀ¼cû1¶_ÒcûsÕè%hX4,4l/ÁØ~Œí¯ðØ~Ä\íy  ‹†õÉK0¶cûkbl?bNB^‚†EâaAƒöŒíÇØ~ÉŒíGÌÕˆ— aѰhXP½cû1¶¿Çö#æªê%hX4,4 /ÁØ~Œí¯æ±ýŸ?… æ$æ%hX4,4 /ÁØ~Œí¯†±ýˆ¹êò4, ¶—`l?ÆöWfl?b®–¼ ‹†EÂúä%Û±ý54¶1' /AâaѰ A{ Æöcl¿ÄÆö#æªßKаhX4,øz¼cû1¶¿zÇö#æ$ê%hX4,|Å^‚±ýÛ_ýcûsó4, —`l?ÆöWÏØ~Ä\µx  ‹† ÛK0¶cû+9¶1W^‚†EâaAýñjÄ\¥½4,øš½„¨ ü^•ýYi𠋆ð€˜CÌÁKаhXjÄK@e®ôаx b€†^s aðÄ ¼”ÆèÑ£³³³ñ>€†ÀKjŸY³fÅÆÆâ} €— æhXà%1/AÌаÀKb4,^‚˜ a€—ÄhX¼1@Ã/ˆ9аx b€†^s aðÄ ¼ æ@Ãà%ˆ9/©NæÎ…÷4,^Rûüûï¿x@Ãà%µÏúõëýýýñ>€†ÀKs4,ð€˜ €— æhXà%1/AÌаÀKb4,^‚˜ a€—ÄhX¼1@Ã/ˆ9аx b€†^s aðÄ €—T'ZZZ~~~x@Ãà%µžžž««+Þаx b€†^s aðÄ ¼ æ@Ãà%ˆ9x @Ì€†ÀKs4,ð€˜ €— æhXà%1/AÌаÀKb4,^‚˜ aðÄ ¼¤!püøq¼ að’ÚǪ¼ að’zs#AEÀ¿7€†EÃx ¨Þ˜‹e'ó+®°R¨XTœÂŠç¤ V¯Ò¸…•XXéTILeò*™WüJ),vJ;U Ò²8‚•žMÅe*£¨2©Ø9EÅáUS\¦r©²©2*+7§°òx•Í«\¦rø•ŸG•+Py¼Êg*ÿSa!æ à% ®ÆœpÆ¥e\QÌeT8æ²K9¶pÌe‹¹Ü2c®(ãJ޹\á˜+̸¼Oˆ9€†EÃx ¨˜Ì8‰Ç\QÆeË8‰ÆœpÆU.æòJ޹Oô^9€u ‹†ðPã1Çá—è9á2c.³Ì˜ËŠ9Ñs‘Œã ;ærJ‹9ŒCÌ4,ÀK@‹9÷ûÞ¾ÐLFF³›æUGÛÒcNøZµ»¯=ðâ«å^«¶8ocët³Ì˜“üµêó—.:¹8SÆÝôþN»›ö½V˜hX4,€—€šŽ¹EË—ž»vù¬µyWMÍŽÊÅ?'òñÝIc£W¡¯Ë¾Vý<(€^åŒÙÙš¼Vý:ä ½¨™…9Å\t\¬‰™Yø»ˆŠ^«FÌ4,ÀK@MÇÜÑ“q씘ô¤ÑãþRÓPgbn÷} ŠŠí;´ß¶g'sFæ&=zö¤Z»aŠš*+#ÙÇ漢ªý-G¯‡¾´±Oçý¤G17<öúuø0zyyy3kKWOwU5µå«V¶k׎¶×®û·‹ŠŠœ¼Üß“&Æ&Åû>~H·Ð=D?M-Ì)Ý’2R.^¤¬¬¬¡¡±aó&&æþ[¿N…÷@ù‰“'%¤$Qº™š›©ªÒ«+Ìž;'%=møˆô¢ 6çÏ=}ñ\M]ÍÍý6œ‹›ë AƒZ·n=|øðÀ @ ¸'OŸª««ë>L?•””¬¬­ù‡˜hX4,€—€šŽ¹¶íÚuêÒ™~JIIݸu“2îéëW´ãÖ-‡ŽêÑÆûëÁïß6nÜøŸ™ÓM¬-(\hg\z’»¯7sZØÍǃ6þ<ÈÖéfÏÞ½:*+ Æœþ©tëÜóŸ¼zAw í>ýúnÙ¾Õã®7ð™YYÖ?B;/߸æy—÷„ƒÿ|ó–c¯Þ½)Ú(æÎš›6oÞÜÓ×ÇÌÒbÄo#ƒBß<~öDSSÓÂÚòèq}ºÿ5Ûñɉ222ÓgÎ8wáBË–-÷îßwÊè4Ý´pÑ€ @þiá×ÁÁt+Ü™³Æ]»ªkh°¹œ{÷ïÓ­ƒ‡ qusëݧO§Ns ‹†ðPk1GG]Z»v¬X»šŽ½¨Ââ"÷é¤PÓÖÓÑÕ?ÒJ¶Õ’•Ë y©ñ"$(›6oÑ‚cÎØÂ”ÒmÙª”8‚1÷üuÑia&æ¬ÎÛ0ç„_ê;üǨ?i§¾á &æL-Í3²¹+V­¢ç¡˜{øô1å/WÍ™7×ó®sN8($øˆþÑ?G¢ûŸ8eh}Ά6Þ„…fææÄÆ³ŠŸÎåÇÜ1}^,¾ ÈÉÏ3>kLÛw}}™˜³¶±ÉËÏ_³f ½(b aѰ^jù´0‹byéýJ?W¯ûWZZš~®Ý°ŽÊÄÊ\çèaºéiÐËDnÚœó b.Y(æ.Û^§t[·iCÓ¦MËŽ9çÛ®”qöÎŽô*ÓgÍ063¡ÇO21wÝÞ6#;sÃæMô<Ì…j[ûi3fÈ+ðû®ÙÝp¼åLœ9{¦‰¹í1<}ò¤Ñ)&æ(àÞ†‡§d¤•sz‡yÿïßQÌÙœçýÇÞñô`bÎÁÑ‘bNk«½(b aѰ^j:æâ¾ÄÜÜE Ll,N™ùe(¯óø\s´£ #ó³Þïò³‹ãýç~´gâÔÉ'ΜjÓ¦ swŠÇÜUûü˜œ!0Œ—8[¶o ùÎÄœ«§;Åܦ­[èžáQï-l¬hç‘ãǘ˜»á`O1·ñKÌ1=ûÀ‚ÃB_¿ iÒ¤É1ƒãZÛ¶ÒM¢£¬ ŽºhOdt/®\µÒ÷þ½víÚ­Z³:ì]Ý´}çŽÈ¨ü˜»ÿðÉÍ›?ŸŽºý@lð¯ aѰ^sˆ9€†Eâa¼¤aÄ ¼ æ@Ãà%ˆ9x ,žžžÚÚÚx@Ãà%µ¿¿ÿúõëñ>€†ÀKs4,ð€˜ €— æhXà%1/AÌаÀKb4,^‚˜ a€—ÄhX¼1@Ã/ˆ9аx b®€‘ Rà@âa¼TKÌŲ“ùWT)L±¨8üJ/^ LqÓ˜J,VéIüÊ,¬äÌ ÁJ)*vJ;µx¥eqŠU6'W\~e«L6UNQqx•Å/.S¹TÙLe¯¬ÜœÂÊãUvaå2•#Pˆ9€†EÃx ¨É˜+1ãRâK޹´’b.]4æ’K9vI1Çйôâ1'”qÅ3®ä˜Ë-йLá*–qeÅ\>b aѰ^ª?æªxì• Ñc¯Ôª{±%zìU<æòèMs(ÿü à% AAAkÖ¬‘hÌUýØ+­ÊÇ^ì:{ì…˜hX4,€—€R‰5kVõÇ\ÉÇ^ž}}¡EË]»i™-vNX¼c¯{vÑ3$d¤ð½N"Ç^ˆ9€†EÃx ¨˜ëÛ†BS ÆÜ›áíÚµëбãû¸èŽ7iÿ S_¿?|ÓÕy£Öæ¦M›R¨™Y[ÐMzÇŽ…óNÛ.[¹ü¶=ª æR…bîšýôlsÈED±b½ïÝ¥0=oîo¯ƒ*(*Ä%Ň¼ £[—¯Zéy×§cÁSQÌ9Þr¦ s+K¿çO† êâ~»ìtb{!æ à% ¶b®œ)¼b.‘›nZaKV,‹KOš5oý4kÖì¯ñcYéÉ~/Ÿ«k¨7iÒä—a¿**)-]¹<%‹½G{_Ûvm¿móíÔéÿÐKŽ9n 7£Oß>LÌedsO1’W§_UTTÜ<Ü™QýûhË.ùœp½s ‹†ðPÝ1W™c¯r籎MMˆJd ~Ûð}\”Ð|‘q) ‰ìÔJÌcý!&Zhj¦Ä´Ê2¡)RØi ©Éå~ÛPüc/Ä@âa¼ÔJÌaѯ޽s ‹†ðP­1÷u,ú•Uåy¬Å:öBÌ4,ÀK@ÍÇ\}:öb×à±b aѰ^ª=æ*tìEw~öúíçØ+øÝÛ˜äøZ_!=•ÎÉΤ¨Ø˜„ä$ñýʸ<Ä@âa¼ÔlÌ•sìåêã!¯ À{={ %%%''›š(zìõ:"´õ7­C>„‹³Bº×½»5ºbw]âÇ^×ílé¼xùmŸ<}jÔèÑb®Ž˜hX4,€—€šŽ¹Š^¨Þ¯s`Üß㙘۴M«I“&¼‰ÎY‰{Í_¼ðÑ£Ä\!‰¹«Åc®ŠÇ^ì,®þ ƒo¾ù†7—6/ærbãY222ž>^•;öÊËÏGÌ4,ÀK@É$%%M:U¢1—Zî· ÿ?îÀaʸxvj§Î¦NÿGMC}ØÈBÇ^‰œ´æ-Z˜XšQÆMœ!æ à% ÚcNüÅ5*0õ“WþŠŠŠqiIUÇ:G‹~{9ÞrúñÇ+}ì…˜hX4,€—€j¹’Ðq%°è×MWçw1ë΢_ÏüŸVòØ 1аhX/ÕsX!]Üc¯|Ä@âa¼TkÌÕÖ éb£ÒSjc…ôœJ{!æ à% ZcŽ—k®Þw5jtáÆ•r½îøzó†Ê߸*xìµ}Ï.ÚÉJOÿØkò?Syø••ùç}Ÿ÷5Åkv7ªu…ô —.:»ÜÿØëÒåË.®®üŒCÌ4,ÀK@5Æ\|É1Wê±—PÌ%—se{%sÒ¥¥¥—,_“ÈâÇÜ»ègLŒƒÃC«¸BzÇ^Á!¼¹±Í,ÌÅ<ö ¥û[XZ"æ à% ¦cnÑò¥]TU”Ú·7±¶`2îºÓÍþ$ÛZöÃ~yøâiRñ˜;cnÒ£WÏî={Œ?މ9þ±—‹‡»ªšÚòU+Úµkçêé¾ï ¶¢¢bûvíÝC17dèPº›¶m6nÙÄ?!üà©=ÄÉíÖ½Çiã€®Žššš¢’¢©¥¹Ð±—‰¹™ªªª‚¢Âì9³“ÒS(àœ\]ÔºuëaÇù¾¢Œ{ôä1=\GO·àI”,¬,éØkøˆôº 6çÏQÌÒÑQRRêбã>íýsÚ´ÕÔÕïúú>yú”6éñåþç/\ÈGÌ4,ÀK@MÆÜÀÁ?ßp¾Ù·¿Î]:SÆ=~õ¼EËpÇOªi¨«ª«Å¤$ðc.$2\JJjêôi¦ÖrrrÅcŽ}Ãñ&íéÓ¯ï–í[¿àÍÞH‡ fœ¼á`o}ámLš2ÙÓ×›süÓžwy/1hðÏ·œzõî­¬¬,xìÅJJ‘‘™6c†Í…s-[¶Ü³ï«à Ú €;ml¤¡¡¡®®žÊN÷¹Ç{¶Ÿvv½Õ›ž¤“2ÅÜ)£Ó´sᢅAAoÃÂh{ÇÎÇOІÓ-çÔô4u >}ûôÿ®¿¦¦fzF†Ñ™3¼ô_´((ø5b aѰ^j4æN›'pR—¯Y%Ó¼9ÅÜÁ#º´óÁs?Þ*§ iÛÅÓs'ÏÑÆ«Ð×É™3fÏ¢íxÁ˜sâÅœåy›Ô,öA=ÆëÑ;j ßJVvùª•¬Ô$ºuö>ÁQýB1gfiAѶbõ* 5Ác/ËsÖtëë·!Üœ¬˜øX:ö:¢Ï[°ôEÀK:ꢤ£m¯»Þw bÎÒÊ*+/gÕšÕô$Ù¼ÓÂ!üÓ‡¡¿êè±c'NÊÊÊ®^³&7?ÏçîÝÆ!æ à% TF-‘˜»xãJ'í¿Më›6mJ1·_çí|úšb9m;¸:ócîè }ÚùN1·péb˜cócÎé¶ ÅÜ¿ÖIKKÿ·aݺM¨,l¬Dc.£XÌùÐÆu{[ж[6Ñ#x¡Úðô)º5¸ æBÂß&¥§êÖ£=o#Â)æ¬lx!èvlj9;{йÍZ¼¥=c.'/wã¦ôWÑÏÍ[¶P¿xs¼ÅQs ‹†ðPafÍš+±˜ãÅÜí»žRRR3çξåéþãÀŸä>°bø1Æ’¶tår7o;‹¹,¶mA̹zº3+‘Ò¶‰¥ùƒ'âäæ"NÌÙ:ع¡sï£>R­XµÒç¾oÛvíV®^u÷Á=ú;çΛGG] RPTHHNbbƒ@Ì冿‹ ÛwúxËÕ…¶­l¬ž½x1ôCÝ=î0§…ü8€ª}ûöq,Ö»÷ïé>;w팎‰f21аhX/5sWù1—øe‘t9yyº©‹JÇÛ.¼at÷ cŽŽºvkïkÛ¶í·ß~;eÚ?_bŽ],æ¼îPÌ%d¤Ì™7—YÁkìøq‰©¬´b1—Q^Ì ê70<¡©©Iû‡ÃÍÍ6:{†R˜¥¢¢âîé‘•›SbÌq²2»uïFûutu9™Üù æ3Õ„ ¸Y™Ë–/§»½(€6¦L™’•ݽ{wº¿žžb aѰ^j(æÊœÇ:4ê}iS3Å$ÇÇg¤”=ómÃø´äؤxqæ±f‹75SBj’з £ãbÊ/’›ËŠãÛ0ƒË¡£®2æ‹ÌÉ͉Oˆçgb aѰ^j&æÒª<5»ÊóXgVyÑ/±ç‹{kþ…jªOˆ9€†EÃx ¨Ö˜KÜ¢_e¬.æ¢_l‰Îc-”q]!](ãs ‹†ðP31W޽$·Bznu{!æ à% úc®n{eÔc¯b1÷ 1аhX/5sX!½BÇ^ˆ9€†EÃx ¨î˜«öÒkþØK"+¤#æ à% Æc®”sµ{ìÅ®KÇ^ˆ9€†EÃx ¨™˜«¦c/v•½2ëà±b aѰ^ª1æ@%@Ì4,ÀK€äcލ8ø·аhX/ˆ9Ä@Ã4,€—|Õ1@Ã/`Á‚>|Àû/©}Ö¯_ïïï÷4,^‚˜ a€—ÄhX¼1@Ã/ˆ9аx b€†^s aðÄ ¼ æ@Ãà%ˆ9x @Ì€†ÀKs4,ð€˜ €— æhX¼1@Ã/ihiiùùùá} €—Ô>zzz®®®x@Ãà%õ;æF‚ÚÿhѰx€— æJö’Xv²`ÅU S,*¿RãE*Wi ÜÂJ,VéTIüÊäU2¯2+¥¨Ø)YìT‘JËâ«lN:¯¸üÊ(V™Tìáâäd —*—©l¦2‹WVnNaåVvaå2•#XùTy¹Å+WùEõ)?ÿS>¼ /^‚˜«¨—”(%%xIBy^’$/áTÝK8åyIfy^"$%"^’/ðà%@’^RÞÉ’”øÒ¼„+†—dò½$£/a‹é%éåy »|x@` sÇÜÒ‚^þø¹s禳3ø^bkg§®®~òÔ©|x €—/Ảï%—oÞà]v±½ºiûV))©Ç/ÈKÂY›ÉȬ˜X[´hÙbÛža±‘M›6ýsÌh#ó³ŠJJôèÔ‡Û·xGœn’—,_½R¶µ,y ÿ:Îu{ÚèÓ¯ïæmZŸ?¥mÚÐ=v„6®ß´å{ÉË Ú³eûV=ý£¼›ìb’X$Id9¤A]»Æ%'D'²dddþ™1Íò¼M‹–-I\ø^âz‡§.Τ#«Ö®‘mÝš6Κ›6oÞÜË÷®™¥ÅˆßF…¾)¼Žco{ÒèmО±ÑÒÒÒ:zºoÂBi϶Ûõ ŽóžÊÉQÔK‚‚ƒ[¶lIFrÆØX£«†º†z—”šBÕÌ™3/\ºH·î×Þ»ÀKŽéë·jÕŠL%7?^à%ÀKsð’¿§LjÙª¥Û]O³óVô™úßæ ä%Æ–f´ýôõK'54ú}<'•Ùãõè^'uÉÊåexÉâ^b~Þ*93ý€Þ¡Æ<¢{øø±V²­–­ZÁ÷’z:t“ν#útÓòU+R³8n^wàáë–Å1·±¤§ NÏæFÆÅ^Ç)ÑK>}LŽ¥ ¨0gÞ\Ï»>EãKìmã’âÉ!vïÛ{Üð™Vdt”Þ‘ÃôBGŽ50>FðÂãbÓ·œV!/a¾!™ÀI-}š×¢oǦ&F%²Jü†0+-)&‰Uî7„ã’JœQD„¶…¾!œ”žšœ‘&Þôó9i쌤Ԕr§Ÿeʼn~C85=­´i^á%^¼¤ApüøñJÈ1^Rééç…¼¤BÓ¼V}úùŒº1ý¼˜Ó¼ÂK@Õ/©XP^Rê4¯!#|Ÿ=Š/˜Z­¦ÖæTu áœZXC˜/%ðP•†ÀK”—H kKr ax €—/Ảå%UYC˜îùìõ«øÂ1%å¬!üîmLr|ÝXC8KqÊó’8+%5^à%ÀKs5à%¼Ó$®>ò Ìù)))99¹˜ÔDQ/yÚú›Ö!ÂÉH,Î[ßp´¯ÜÂVÎÙ;;ˆ3¸Äæâyg§J .¹÷÷ÕÛ›vä%.]tvq&¹ÿè!ó}ñ—œ62=f´”ÀK¼x b®*^RÖÂûtŒû{w}à%^¼1WQ/I¹êh7`ಲ²CýåÞ ?’’;|TÔT÷Ò¦Ÿ ŠŠ§ÍŒ…Öþkü¸‡uHJâÙ):wš2ý5 õa#G,Iä¤6oÑ⬥yɯÇч½¼¼¼™µI‰³ãʶ–ýeد~/Ÿ ^ĉOKž¿haGeeu u›6— >œ÷Xy +Ò‘×ý×EEEN^îïIã’ܽ<ÔÔÔV¬^ÕN®ÝwßÏÜÓòœ5ßKFýßúuä%û {’yÄ%Æ«««[XYzx{ÒžU«W·““337WSWsuw>b=‰‚‚‚ÍùsO_<§nî·ýž>¡ ]==5uu%%%Kk+ÆH¬l¬{õîEµyófzÎìœR“ñÆOýg*¼ÀK€— æ*æ%^>mѲɱ“äªêj“ãnyñ&ûéçA×ízôêÙQ¹£—Чøû>ä%×ìèž—íolÙ±MJJêYÐ+A/yàÏ›dì¡/y‰þÉ´=wÁ<²§/[´lIFrÂ褺†:}Þ³Ò’ø^rÆÌ¤yóæwîzµ01rD@HÁ©“ôØy < ðøÔ¯«¦¦™•Åá‚Eþ®Ü¸vÓÙ‘6úöë·uǶ#úÇh{þÂþ¯ø'K6mÙ¬ÜI™››=pÐ º•”âòÕ+´ñ&,Ôñ–3môë×oûŽw¼<˜ë8§ŒNÓÆÂE ‚‚ø×q|ïó¦euæ4©I§ÎvîÞÅ÷’ÛwÜù^bW¦—Ø;:—lÑÒ¢÷„¼ÄàÏäB߆’—,Z´ˆï%æææ7æfrá%^¼1W/qñ¾#%%5cÎ,'·”WPg}t)ð’Kvׄ½¤à"ÎôÙ3·íÞI^¢«„îfjcéóø>Õν»yç®^âCØë!o 2ÚxLÛ›·i…¼¿ãëM/:kî7oŸTPPˆJˆã{ÉiãïüúºI“&G ô_‡‡Òcµ¶o ‹|·y+Ï "¢>XØXÓΣÇ1^âîåA^ñ–·†ßŽí‘¿‰³`ÑBÚ?ú¯1!ao…züÔï%žÞ^‚^þ.‚6¶ïÜõQÈKœ½$ ·dà´iÓLÍÌÚ¶mË÷===Á‹8ð/^‚˜+ßKâx—oNÈÉóÎLtVébïæÌ⤔â%…s©©¨©Þts&/á­ù§®ÆŸæõ}\tó-þ3Šï%ÑI,iiésW/ÑvBFŠf7MzÚ}‡¤d²Oœ9%_ð¢]TTnÝqü&Nd\Ìð‘#š7oÞJVvÜ„ñ¬”ÄdNºf·ntç:Ÿø«kh¯ü:l˜’’ÒòU+/¹ãíI^’Æe3÷<¨sHÐK®ÛÙÒNÝ#‡ÉEÔÕÕI˜oâ0^âåã•Wä%œ,n·î¼'ÑÑÕõ’\/¡Ú»o/ý‡|÷ý÷3fÌhܸqíû”?{Îì OÀKjÑKÔ¤á^ЗÔõ˜s5þé¢a¼¤¶¼tà%ð/ðxIòþ0؆©&ðÄ /©[^ÒÏšÀKs4,¼¤ÎyIƒk/AÌаð’ºè% s^x b€†…—ÔQ/i€cMà%ˆ9^Rw½¤¡]З æhXxIm®',~}·—ÄhX/©'Wp¾ æ@Ãx ¼^ò5àéé©­­÷4,€—HÄKÂÜð’jÄßßýúõx@Ãx ¼^‚˜ aë—<¿{¢qc©vm[ûM+•Ίû¶Í¦NWöLý{¨8ŸÙîöÆ(A ÈŽ·×Þ1‡ÿÌâÿ%ðx b€†ý¼¤»f'f;1âRWõn­E/‰ 9§®Úž6²Xvô÷ÀKà%ˆ9¶zɧǺ=t/òãã«ì1dPOSõÌ}hOÏîü^“6½ÄÚx=„d"éݥ߆õ×ÔèØ·—ª·³®à¼§ã¡ÕKÇ1ÛVOt¹¾Ï×õðÒù£èþÊä¦Mú%'Á~æÔa22Mé罂›à%ðÄ Û°¼D¶Uó¿ÿú™ô¢W.Kæâ_Çq½±¿G·NÑolâÃ.üôƒæSoR–®êh;æzï]âBÏ3^rÍzÝŒ„{BwÙº•ÓÆ]½-ÿMü€”Œ9ÓG\·Ùæf«Ý¸±Tà#£ÜÄ›ß÷Ó _ùçKp^‚˜ a¢—tê(ÛN›,Äâô:Ç˻شvò¶ ÿ0wÓ?´dó¿Sï_¸fÙ8¡ë8•å›6•öt/AÁK@öŽ„½$El/IÛK8%{‰°”yIž —ˆJI‰^"*%ð/—ÀKà% ½„UÌKRKð’’¤$QXJÒK>Y"â%i%x ·D/a—î%\±½$[l/É+ÓKB÷x ¼^/µà%Œ”|LaG†—í% ¥{IR)^’ÈI{˜ÌMç{Ijé^ò!6:1#¥$))ÑK²Äóq/âˆç%Ÿà%^/©ù*m ¼^òzÉ«ð7£ÇýÕ´iÓF©¨©^u°•àE÷»^ Ìùÿà@)))99¹øôdQ/™üÏTú”••Kõ’J .!/¹÷ð=³íM;ò’ —.:»8“‘ÜôvÚÝ´‡—xICð’¿ÿú™Y´RˆùVŽÞ¾…5£BKôUââÃ.¨tVŒyc/—Ôi‚‚‚Ö¬YSE/‰ÉH0ðÇvrr[wïªÝîz z}øòi‹–-ÈHŽ:¡¦¡®ª®̲wu¢;÷êÓûŒ…iŸ~}»÷ìA^¢wü(í|ýAZZz¿îAÁA¯rrr^î’—Üp¼Iw»vÓvëÎíRRRþÁ‚^b}áÝ:iÊdO_ŸA¯Z´lIFrÒè´º†éBbzêMgž?õí×oëŽm¬”Dæ4ÉÆ-›•••¹9Y ¤[-¬­.]½BoÂBo9ÓF¿~ý¶ïØqÇÓƒ¹ŽsÊè4m,\´0 (ˆÇ÷þ=Ú½;uêD^Ó¸qãÙ³g_¸x‘<†î“ /ð’¯ÌKn]ÛûMë–ô©rî¾Û‘1üHžAnñ}? ºIt±=ѵúD%ºæŸÐ”ó¢Ëо¨4ÀKà% ÅKt xVáìé.ä%ëÐþ{ÏÇsRIMhû–‡›½+ïÃþ¤É™nÚ)“3´ý$àEhÔûfÍší×9xêìú, Ã÷’ÇþÏd[Ë&²SÉK&OÒ²U+¯ûw­/ž§nÔÚ,è%¬Ô$Ú¹G{_z6WïØÚ~úòyF6÷dIÜñöb¼ÄæâyÁÁ%žw}h§·ï]ò!MMÍ…‹Sõëß?37›ñ’ó/f Œ/ ¸Ž#ä%V6Ö¤#«×¬‘‘‘¡ 3s3Úñî]^~þÒeK¿xÉ'x €—|e^2â—~ü_éƒÿ²… G—N ´!ºØ^‰kõ =JtÍ?!/]&Pô…à%ð’†ë%¶.ô¡{èØaÆK Μúë/~/öêhó΂„‘—œ±4¥m{W'A/1?oMÛ¾O&rÓÇNÿÓ £ÇþõËða‚qNñûo$%1‘ôyßH€öíÛ'²ÓJô’ƒz<% %/1·¶ä)‘»ã%.în‚^’‘ÅUTRùÛÈÖ­[Ÿ:sšÔ¤SçN;wïâ{Éí;îbzÉMGÒ‘-ZZM›6¥ ƒ´3ôí[ò’E‹ zÉ'x €—|E^Â_`Ï×õ0‰ÅŽMÓ]oì7zà%ó-%N/´VŸè£D×üòÑeK|!x ¼¤zIlFÒ÷?þðÍ7ßlغùØ©í;´ïÿýw´ÓÕ玔”ÔŒ9³œ=Ü üI^Aá+Šñ’AC{<¸ûÛ¿«¨ª0ƒ^Ï]¹Hw&ó04>-è%3fÏÚ±gyÉa}ÞYósÖ÷üRíÚ·‡~½xíJ‰^âuï.=Ûìysïx{’î(((Ä&Æ3^âîå!4èuÁ¢…´ô_cÞ„½eŒçñS?¾—xz{ñ¼äQ¡—„¿‹ í;wDF}|PÜK½$ 0vN›6ÍÔ̬mÛ¶ð/ù꽄܂9A2A@r ª ¢kõ‰>JtÍ?¡%úD— „—ÀKsE^ÇN~4òÏß›4iÒLFfä¿y>òef.Ñ?m('/OÉUºÜt»•ÀIe¼dÔ_cèΚ]Ï]½Ę̀—–Ô®];™æÍßÅE .QUSuºíB^Ò¯5u5þŒjãc›·h1jÌ轄êä#yÞKwQQq½ãÎÎÎd¼„LEÈK®ÛÝ ýºGô¸¹Ùêêê***Ì×p/ñòñæ{‰ÝM;N·[÷n´­£«[†—0ßÄÙ»oŸ¼¼üwß?cæŒÆçæåÂK¼ä+ö’×Ïôï£6nôÀ?F|O;µwÌ)q`¡µúD%ºæŸÐ}¢ËÂKà%ˆ¹B/‰˜éõCbÜ;V´èÌ%!#ø3ª1^bçâ™ÏNáC8øÝÛo¿ývâ”ÉB3½>À .)iúyv¹Ëâ|ˆ‰k撊̨ÆÍΊaÅ•;£Ú«€€mÛ¶=~ü8ïSþâ%‹•••ùƒKà%^R½¤Ü"ó(÷>¢kõ‰>JtÍ?ÑEëÔ¢}ðP§¼DÜåúø^"8sÉní}Íš5“iÞÜë‘o5,×W¥Õ*½\_fV–¦¦fÓ¦MÛ·o/%%¥§§Ç—x €—|Å^‚yèá% >yIdbœï³Gb½$0ìÍûAá!UYFX"Ëõ‰±Œ°ˆ”䕺\‡Ë½{÷®‹‹KXxÿÂð/—ÀKà% &¼D"Ëõ%Ui¹>NE—ëãÔìr}ð/—ÀKà% º¼$Nl/IÄ2©b/#\'—냗x ¼^/5á%â^Ä©Ð2Âå]Äa×Á%â/×/ð’¯ÕKœ®ì™ú÷К\ÃOð;8üó*½4 ¼|­^"ÙA¯’\"î ×Ò–ëû/ðx‰„Öðƒ—ÀKsåyI…—¤‰7¸¤Jƒ^%2¸$K¢ƒKà%^R½Dp‰>úõäá´­¦¢´gë,Á~¡ý¢Ëò‰.Ý'ú¨ïà ?fîù߇§ÜAnÎôÌZ}BÏ#ê%B þ‰®È÷kã ôþdùðxI=ó’ê\’RÓƒKÄôš™“uÖÄdìØ±£G>yêT:;^à%_«—.Ñv¤õö<3OZ°Ÿ1óÁ/º_tY>Ñ¥ûD%zŸÏ—|ûM«°¦Ü8Û^=º¾ˆ>—ˆ.ø'ºF ã%׬·ÑÄ™‹^$IRRÒÔ©S«æ%b^ÄIý*—”Œ€ÔDœÁ%ðPó $â%ü%úL ×ÔóÀιT¤wÍc>øE÷‹.Ë'ºtŸè£J\ÞOÔKFþÚŸ?Ç«ñ†_]ÐKDü]#¼¤³²|Ó¦ÒžŽ‡p^R ÐdxI\"q/;v¬ —Œ=º /ù/µ×°@"^Ÿr^gÏ|Ú&É`êÁí£Ì¿è~ÑeùD—î}”è}Ê_²|ákã %> —ˆ.ø'ºF y‰†ZRœïûiMJ /—Ôu/U^à%õ×KBŸ™ôí¥ÊÌ¿aõDÛó;˜~Ñý¢Ëò‰.Ý'ú(ÑûÎXϬá'ê%¢Ï#ä%¢ þ‰®È_2qìÏ‚g\à%ð’zs ---AÕ˜ÑG•x~1kø‰z‰èóˆŽ{ZðOt@¾—D[+È+8H^/—ÔuìííIMFE:²qãÆ7nØÚÚ;vlÕªUãÇŸ7oÞ¾}ûΟ?/ð’¯ÀKD+?Ù‘{Cœý¢HE—î}”è}J[ÃOœ¿ªŒÿD×Ä÷„á%ˆ¹¯ìììgÏž>}zúôé´7 a¿>/AÁKb4,€—ÀKà%ˆ9^‚‚—ÀKs_7>>>zzz>är¹x7^/— æsµITTÔ¹sçV®\9vìØ½{÷º»»gddàmhXx ¼^‚˜µIllì•+Wþý÷ßÑ£GkiiQc'%%ámhغà%ÕQ[×O®¦g®Ý‚—ÄÜ×鈳³óöíÛÿøã’¼! [Gø,_ñ ð€˜û Áˆ“:HdØ]4,¼ÀKbÔ¾Ê3Ϫ&hX2ðÄ(gÏžYYYEDDÔ¼—4ða}T¾n§Ñ°ÀKb‘””týúõ5kÖÌ;רØ8 ^R3^òÀU»Õ ¼ä+gôèј%½¾ Š ʤI“Ž?îççW­ÿCá%ôóñCµ¥&hXà%_9³fÍ—>¾âããIP´´´¦N/©>/¡bΚÔüX4,ðx ¨g¤¦¦æååÁKªÕKjë¬ ¶ž‚¯×ÁKbÀKª×KøjòÀà ÊÀÎÎN[[ï¼ToÌbPÿ¿yóÆÕÕµÒ“ÉÂK„ö|Q“:Þ° ¶ˆŸ0aþ¯ÁK@MxI,;™_q…•Â‹Ã¯Ôøâ•@ÅMc*±¨Ò©’˜ÊäU2¯2ø•RXì”,vjñJËâU6'W\¦2Š*“ŠST^eñ‹K•K•M•Y¼²rsx•Ç«ìÂÊe*‡_ùyT¹•—ŸW7½$**ÊØØxîܹëׯ§Ã¸ŠŽD—ˆî¬É :ð’zÇöíÛÏ;‡÷^jÔKâŠ{ KÀKJNZ‰^’$ì%ây GÐKÒK÷¶°——’R½$GL/É­'^Â' ÀÐÐpúôé+W®¤Ü|óæ ¼¤r^Â[j/©_\¹r…ªo¼€—ÀKÊö’ xIf ^R$%"^’V‚—ˆJ Wèd »ä“%%x‰ ”d•*%¹¢'Kòòóé-ªS2“‘˜ššÎ;—¥\;—”vSÍœ5—Ô#‚‚‚&L˜P}_Žðx‰X^Â*ý"NBéq’*y‡Sé‹8YÜ Ð7ììÌjºˆS¼DPPÊýÖ¼¤Œ[k`¬ ¼¤¾——G®ïêꊷ^jÚK*}ÇÝ׫Q£F†Æ§/Y¿eýYîEœ{vÑ=2RJ¼ˆcu᜽³C¹q¼ïù*(*0Ròúí)))9yùTN†„.âä×G/xIÙw¨îyMà%õ++«íÛ·ã}€—€Zôñ.âœ,ùâ%FB^’\úEœÔâ^"z²äep ÝdlfRîE=Ý¿'Nd¼dÛŽíMš4¡ž»xARq¾>/‰÷ññ9©; ^"ŽšTÓxI½Àßß„ Ô2x+à% –½¤Bqø^’TÜKnûx¨ª©îÜ·G¹S'U•ŽöŒ—[˜öèÕ³GÏc'Œ/ð’TÒ‘µëþí¢¢"''7aÒÄØ$Ö°áÃé&yy +2íC;tè°{ß^¡o⌟0Aïè’v·s—ÎÓgÎÐÐÐùÛÈJœ,i ^òöí[--­u«&ÂKÊ­ê» /©Ðÿ#???¼ðP ^R•¯¥y‰ƒÛ-ÚèÝ·ÏYK³¾ýúuïÙƒ¼$4ò””Ô?3¦™Y[’…Ð3R#“Sõ‹8ð¼x ¨Q/©â´%±i‰ò í;t02={ÒØèÛo¿í׿?¹ã%?ìýÐ÷·?ÿPQUIÉd…‡ÐÎe+—ßöñèб#ã%›¶niÚ´iØÇ÷6–´çÈñc¯ÃC™k7a‘ïnÞr¤m3+‹GÏürë¶+ÿ"ά9³wïÝC^¢â8ÝÇæÂùÇÏžPíÓÞO¿^½q=«/©ØÉ’¼Oðxɉÿ€—/ùÊY°`Á‡$ã%Ÿ{ÞÖÙ¡‹JæDE×nš÷¼ù^2zì˜&MštÕìzáêeæëÁ{´÷µm×öÛ6ßNþã%O_ù«khÐÝ~ö«’’Ò²•+’9éšÝºÑ­t&³ÓçÌŸ'--ݬY³qãǧpÒùsÏ«©©¹Þq'/éÿÝwêêêüiKXI -Z´óטª_Äi ^rîSŠ#¤Æ¼¤Ò €—ÔÖ¯_ïïï_u/©Êtjïã¢ßÅEñ§-a¼ä¦«st"+‰“&8Z\JB";UhÚ’¬ÁéÔR¹b¢øÓ–$e¤²R…æx Î(\"ñ¹çù^’ß0¼dÈ žÌÆËû§7–ºf½Mè³ÙÝþÀøÑ%þ‘/ÎÓΙ>âÆ¹íÚÚõ’J7,^Ò@¼DòsÏ3^âàê\ËsÏWí"NCð’ GFKæb¶×­ü{íòñ¿ ë/ôٜŲKŒ¸$q/çi+á%’ýká%ÀK@íyIÅ/â”6÷|tëÁ3¿èD–Ä瞯—èZœ ÉI°Wî —ôî’†Z‡·ÏM?›ï¹^:ÝDÊ¢©Ñ±o/Uog]¡Ïoãã«{vïüã÷š´Áß3x`!ƒzš®¥_}]“dЯt·mþá?-mœ<¼B]µ½šŠÒž­…¾Ø9—^hà€nÿû¹— —x:Z½t³½aõD—ëûDÿ*æi} ~ÒMô5mÒ/ô_Ç‚ÙòÇÄŸá%€Çã\»v —^$ÈKHJÅGÉËËG$ň3â•™æÕýì©&^@øÊ Á4ng<Î5T'ŽÇyO))©ÿN$/9h{PGGç“ò’K¦›§Q†w¬s'ùe‹ô˜ÔC³ë §5¢#IS¢Üö™/¤û=k¬y/×gò…)§_äz›¬N†Ál9dµ”Ì€DaܘÌkó…‹ ~cŠÝ¹eΤñƒÿs6fÒK»…bêï¿Tñ:˜Éÿ®÷y‰è§b½„L‹9rþì1G¯¢ßÔtÃL¶Xx `0«×^$ÎKfý5wä˜QbvâÔà%¢R­׈W‘NÖKjñZK'Ž£³“®žã%%e|kë:&¿¯ô’¬ìl™ÐÐPVJ>z/Ye8麿%eöì˜?wúÈ77ã}†Ã~îWÅK\l—“ÐËüg'Û·“eFl0)ñ‘Ów_k•gŸ§ü7ý4£îØ=¾q`ЀÞtLEŽ—ÎÈïî^L¢ /×–±–Ñ#¾%Q`нoßï+u¦yfåÒ‰çŽm 4øÇÞT/㜲RGa/¹ê·‹Ê§LQÚõ/TN»­ýT5yɽËû|§]ðüTjôE…ð@Ï;—ËåâRÀK€dyIzaŽl[Y[g{ò½ÉuÆe¤dÊŒi£tÆŽ,ù÷ŸÏÕ¾PPPШ—œ™põ2ã%W/«k¨_ ¾”[R¸nóÆ>ýú2^b²ÃTYY¹K×®M6 K‰ñ†u;udS×®]Y/™ôûä߯c¤dƬ™cÆŽ%Y¶â_555E… “&rr³Y/¹z㚆†FPhIÉ&“-ýúõc¤dÇN3e•®ÝºšlÛ*Ú‰³uÛ¶nªªL'Nl|\·nÝ6nÚÄx 3²DWWW___ÈK^~Ü^òó ¯È(Ó÷K5º³7cºÇ·k+}×NØK˜ñä+½{v߽ݠÊý{ûÆY$"”¶­Uø–µÓÉèà9Ó~%É Qè¡Ùµ—v÷>½¿ Qx™çÍ6Ømžóã÷=Òw²îFw–.Oo¤ƒ©@a/¡r†þÔ·u—ßFÿ@^"ú©jò¦wIS½Ë4ý¡t®“.Æð’OœœœœéÓ§ß¿—^$ËK8Ey7Ý£ÛsÀõËœâ|KÁô©w#¢Ÿ%HKK›˜™†Þ¹¡¥Ý㳃Ùn Úuìô ÖK¼/ùRæœÏyr‘E†Kääå(IW¯3¶Ø³[ ^¬—xøx¯Ý¸žM›¶na½„Y®/<:òiês:ïó·îßé¡­íäæbi%(çÔ¹3Œ—œõ8w)(2ü|ÉK ÿ1”——')‰ŽL»~ë}‚_ÁËÇ›m,±³·=fLí222cÆŒÑÕÓ+(âææç=xøPØKÖ¬YÓ«W¯OÇKR£¼ës³…)§™vÑDÛ…G¥0[xç˜<# ¤AÜÔ3¢ï}‘ë]œ~VxKQÚ™šND.Re"¸Z>›žÜ>zñÕcDä1÷C­á%Ÿ8FFFîîî¸ð ‰^âì.XÂ&òilfq~BÆó¶íڮݼÁÜʲuëÖQOã³x÷"í°49z”`ªxk«Ú½d»…YË–-Í,Í-÷Zµ—kÿ÷’E¬—\ð÷51ÝÆ¦íæfl'NZVF»ví6™lÞc½—Λ”ò¬¨¬$<æÉ®=–£uÆVÜoóÆK‚«ñ Ë]t^Ë=»÷ÙXËÉÉ-1\Ê6–°µ6l˜º†z›6m(CjÂ<€ÃxÉI—†•XÛØ•”–|"^Ò”I¸£YRÁóS½´».?qÜ ßFÿ Ü/ùñóó344¬¨¨À¥€—Éó’â<[gÁšy œ¦ûfêÌ齿úòÇÁƒÆé')9ííIwë?¦M9`/Xx·Í^ÖK.ú1-"¤#sçÿÅxɲÿÒñËV,_n´’’ÓWÖKV®1’¢c§ŽÂƒKfÌžõUŸ¯ ¬;A¤Äëâ*gêôiöNtëûY/  ªl¹@:2ÁÆKV¬ZIǯ\µÊhÍjJG«2âõU?ŽÐtjU¼ÄÑɉ̆WÂc¤^Ò€©0åtÒc§æT¾"ÇëfÀî¨;vâÏ»/ùX111ILLÄu€—‰ófÚ’€ëϸþñŸ z)%%å~ö$yÉ c£Ö­[G'ÇÛ»:Ñö{,_{ɽˆ0¦ËæaT„¦–&ã%ç.œ§‡¯Ý½E’áíQÌiK/3ç=ãy޼dÍ:c¦áÄ娛À‡öZ±^^ÙU´nÃú¨¸---ÆK.ø &™uqu½ûðþŸ~ºx©Š—Øìß?xð`áiKªx‰¹……ššÛX/A‚—/Íà%O³Ò¤¥¥Ýþ;Î>†Ó³W¯.]»dæ’—Ü »§¡¥ÙªU«Ÿ‡þŸ²ŠÊüE Y/É*ÎÿMw<å»vë6aò$ò’¼nfaîŒÙ³˜Åöho7Oü'qzõîݵkׂ’"ò’‡^-é7l¨J•EK¿ò²>Ow‚廩v›¬ÿ;ã%\^ñì9s˜óêêé•×9÷ük/9ÅxÉŒ™3ô&èÁKêŸ2ãWk/—/—ˆá%¯çx2cšÁ¢…µL§–þ¬¦éÔ+öñ «L[’Q“–Ãy÷éÔªN[’–™QÓtjYzYeÚ’‚bnNAÞ{Ì=_^QÑ­[·ÀÀ@VJ^~ì^BêÀLéѰƒ?F ÿöË^Ÿ_d/©O'Néûuâ4öˆ×rñK>)/¡Ä.­'ÎrzïzXí%ˆS¾èr€b~ªÚ®e]@xÉGCEE…‘‘ÑÉ“'q)¼äƒñP'˜ïóÐÃK>PÈHÖ¯_Åù¼äƒñ/PðxIC±wïÞì?J2‰ŽŽž4iF¼x ¼^/—Ô†k%¨¸•ŠŠŠ¹sçb¾/ùP½|RÀKà%=vvv¦¦¦¸^/’—xn×Õù±–»r#-ìWû¹ü`xɧFxxø¤I“ÒÓÓq)¼^>$/)åxd'ž¨å®Ü ûÕy®?^ò ‚a%^/ž—\«\*¯Úóê³°_µëíQf𽇠üÒÁæŸjWécÏ%Zò!«¥s§¤LÄ-[*¡0å´ðÁð¼^>/aúqDÌ«¶½Dü…ýD×Ûó;»­wÏî´…Þ5à;í{—÷‰ž”=—hÉd-ßôÓ.€—ÀK¼^/iNx<Þ¬Y³p)¼^à%ðxI3cjjjff†ëà%ð/ùTÒ*ÃItçóôô„—Húúú\.—ÀKà%àƒ÷$1“µÙ4CCéS§Þ¼y^"9äääLš4éÎ;¸^/^@<>lllldd/‘LMMíììp¼^à%Ÿ(t¹nÞ¼9kÖ,wwwxIóºxñb>ŸKà%›—ŒÍþc?\è^˜’’"ù^‚(Chxɇê%éE¹Â)ãMÊc‡R1›ò3ER– dñ^¥ì·R!¥6•R® q…SÞ›T”WZ”/’ J‹ßJüâBAⱉûV*¡TTV5—• '¥r&ñ™Tòv*-/{•*^%þ«TΤ2áô‚REùÛ©B^¼I/_¼xù•h/Ah#´¼äcò’jk®j*¯¬º*¯œ¨¼Šë_y×Uy•ÔUyU©¹D*¯ T^àCð„6BÀK>/©ëU^fM•OŒÊ«„­¼¸5T^EbV^…uU^EuU^¼7•¿†Ê«ŽoT|‘oTâW^ >€— ´Ú^"A¼÷2` TyÔPyŠSyåÕUyÔUyUóŠ/FKoCW^åµW^•5*¯›èèhq¦Ó655ý¼¡Ð†—€¦ýúÅV^×/·xMÛvmµ´µ÷;ØUÛí%˜Ž9væ? é¾|ý*}žÓgÅì¾rãÖãÜ;õ@_¿uƒÞåqÞCÌ–^ÿ€Kt|HH*¯O úz0iÒ$ŸŠŠŠZ [±bESzɈ‡RRRžçrê?¸$ઠºp?{ ¡Ð†—€Æõ’¹óÿrrw;äâØÿÛoZµj“’$Ú-ì%ïÕÒ[XÏ–Þ:+¯Ú[zŸ¥¥Ú9ØÇ&Æ×Ú]VCåå‰Ê ÔÙd²lÙ2ƒððpÉñ’•kWSDÓÿ¤ýçw\RChÇ¢:kÓv%eå.]»¬Û²±Š—lÞ¾µrW×õ[61•×’ÿùBí ݉’3ÓÓò²fÏ›ÛMUUCKóßU+˜–^“¦Ì»6˜l®ÒÒK»”+wm4ÙL5צ­[Ô54.]ºzëe¶n7¥ÊËÎÑ^M]]IIiÚÌœü¶ò¢ L•ׯ-›ûöëGÕV7žÁ_ªªªZZZ+WQåuûþ] ‹þ~“õ§;ž©¼fΞ5ö·±TmíØi¦¬¢Üµ[W“m[…[z…+/_¿ÊËË6,<"‚iéurq¦³Ó‡Ÿ5kVa—­¼Îyxhjjî?pà*¯O‰àààéÓ§›ššæää4»—¤f«~Þ}òŸúšš¿ Æz‰½íêjŠJJNŸú,;ƒ"Úùø‘~_÷ï¡­½b‘š†zZAö¥«!”ñ ò'Y»iCŸ¾}èûFèŠGuÏ‹Þ>þ”Y°äïÎ;_ º„ÐFhÃK@{Iÿo¿?Qo¤ÎhÙ¶²ô“*¯{OÑvú²µc·eþó<ÃzÉý')³jíš{vQæäù³T[ii÷°sv0«<øØéÿÚÛÉÈÊú_:äd?tÄð‡QáaÑ‘´kõ:có=–”9sþ[y=ŠìZ³~­…ÕnÁ./´Õ¤TR]©Õ£GFnVj6GFF橺;Ò¶];ªÝØÊË/PP_xùúPµäC9yyÊvr•• ¹zÅÑÅyø¯#"c£_5özžÛo{€2´åyzª´´ôN óèøXÚ²nÃz«}{E]ð­¼"£¢ÚµkGÕÖ!;;­ZšZš\^QN~}ªiÓ¦?áN{·™n»TYyí±²jß¾=Ugô5 •×§ŸÏgºu\]]y<^3zÉçÏ º]Î2Z¿VJJêvøC íÎó622$+önÎmÛµ¥oñéÏZ·n=z¬Ž­ÓaezKj~–×¥‹‚x¹pž¼ä屢åäåÈKØ~œ3^ž”éÛ¿EôÍ÷Úmx h`/ùnÀ÷ß|ÿe.]Ì©üRµuçö–-[šZ˜í´²l/×~þâ¿Y/Ù¶síÚn±Ó|ïnfÕ\÷#í°49zcimzû:Õƒô…lÚ¬~—sK ·[˜Ñ»è˜]{÷л.YÄV^TíÚiia¹ÏŠvý½dQ~i±H`ËJ‚®^¦oTNG\¨äˆØ¨B>ïYFšpcoµ•×Í{·é()+Ñצà+¡o:¡=ÏeädRE³y«É^kªŽŸ¥¦XXî¢YîÙ½ÏÆZNNnÉÒ¥ì°8¶ò¢úˆ2ÃÃË*Ê©þ¢|èÕ«G£L|B5233MLL¦OŸžŸŸß\^2á÷IíÚ·£Èu<æJÿ–ÿ®^I¡mçâ(h}òˆSœ›ú4³8ŸÙrëZVq>…s-^ø¶—8sEh#´á% Q¼¤²'oôo:ðÿyžáç-]¾Œ"~þ³r9¥Ã®N¬—Vî2\ñï²UË)Ñ·®3Þž´åiSØ”f³7‡WxÒó¬þ”?Ù™Sÿ¬ø—Ž¡Ÿÿ®ZA‰*#¶¥wÙÊå´‹~.7ZIÉéˆ+[y ÚcΡÊkß¶òz#ÜØë@Ï ^Tg,˜ÏT^”<¼<ÿœ:UQI‘9ìêëÊ‹WΟ5{öW}ú 2Doâ„’rþŠU+é¬XµÊhõjJGŽ­¼,v ‡“©ò:rô傃²e+¯„¤ÄÂ".Sy}ÿà óçÏGåõ‰“˜˜Ø\í%ÑÏÚÈÈ´B¥‹JjA–¥ã%d$¢#žegØ:¦-w#ÂÈK,YÄx‰w€/eNy#/™c0¯ª—x ¼ÄËß'·„‹ÐFhÃK@cyIDRl§Î»vëŸñì”÷9Ú~ÐéðåÛ×tÎ×›õ’ÓÞ”±u²½scàÁ¾VÑ·“èäx{WgÚe¾Çr¿í7ß}ù0:¢U«VV»Ï]8O»ìœ®Þ¹9hÈ`/?¶ò:çãE»»8^¿{‹vyû_LÏÉÔÐÔüöûï(©¨¨$¦>KHyJuÙÂÅ‹‚¯†vêÜiÑ’ÅlåõèI8eÖ®_GU›¦–Syr8üÝ÷ßGÅÇ>‰‹¡°gß^áÊ+øŠà·& ;wÞ“*¯ ¾‚¯†Î®®wÜòÓOþ—D+¯k7oÐñ³ç̽zeàÀÊÊÊ9ùy©éiô©èKØõ7:wî¼ÔЩ¼ƒ‚èK7ìQØëÊë%*/Д^b¶GЩzØÍ)èæUJÌÐ1·“Ç#ŸÆÒ?­Á¢~—;uê4ñß±©Oéæ½p颋AþªÝU/¹þ2FëÖÜòXCK³Z/ñ ò'/Ah#´á% ±¼„’«½¤:+%?sÚ¬T[µiÓFgüoTO±^’–Ÿ5mÖLfרñãÒ ²o…Ý£š‹bõç¡¿(«¨Ì_´01ýùÐÃedeÛ˵§;>5‡Ã)Ì1ûÕ»~Ó—Y˜Ë>F˜ÅÍ›1{Öë]ãéå_ æ“èܸ{›e&LžTÈ/Þc½¯‡¶6½:lXrZ [y”×Ó¥|·nÝ&ý>™©¼R33†ÿ:BVVVNNNWO/+?W¸ò¢Ô»wo2°¢RU^…¼¢Ùsæ0@WO—[R,Zyñ+ÊíVRR¢—jêjA!!Ìpýýök÷ìIŸjøðáœLÎ¥€fp¯¤DSSs䨑¨¼@³xI¿¯û«kh°Ó–Ä¥?“m+;Jg4§8Ï|ïn-íôOûC‰yž˜Yœ¿ÉÔ¤cÇŽ:tøéÿ~f¼„S”GÁHù®ÝºM˜<±Z/ñ ¾D^‚ÐFhÃK@CzIís.=Ïå<ÍL«ö1ÂÔ¼Ìä¬táÇÒŸUyB8-/“$Fø1Âôüì”lNµr rÒr8u>F˜‘›Uí´KT[Q¾Êc„9…ù¹Üñæ¨.+(âÒ÷¤:ç¨Nçdˆ>F˜_XPÓ\¨¼@³xIÓÏW íçÙôc}e³ yIÖë ¦xçá„6B^ÀKzŽê¦™£š+sT‹9$*/P­—dffÞ¼y³‘¼ä½C»Š— ´Úð Q^’_ÿ9ªn.Èâú.4ZÖ ²5*/PÅKâââ ŒŒŒ(Óä^RchÇB^$ÍK$h¡Ñ«¡JJJLå%%%¥ ¨[T 9 ²5*/ð¿Zǽ&&&2ËëÐ1Íä%‚ˆö RTRbB›ES iùÙ¢¡ý$1V¾ƒ|Lrŵó1·³ÞžïÚ®Çzúx‰3¸äˆû1/Ÿ ïÚ×n ½9wÞƒBûø w_Šèë·n2ÏãˆÚmmuÆê ´á% ¹¼äÝmš5„«T^;,Ìõ&N`ꬵÖ1ë“QåU{Ko½{ ß§±•øŸÏã0ËëP\7¦—ÔÚ[wn?A— j£uk˜˜r<ê*Ús æÒCA}?R°0…­ƒÝ{„ö£¨ÁÜ'Žöuvâ„W.daïäø¡šžfï蟔ø$&Zðë8;QP§f¤Û;:2Ó©‰ÚYÙÙ222¡WBÚðP[-fjjZ¿Ê«ÆÇ¢çõ¼è=àÇåäåþoè/w=¤š‹YÙk»¹³z–½³c•Êk¼ž®ùnKª³ K‹?ÿüó?§NÑÔÒþë ìFåˆÈÈHCCÃÚáóùUVÕyïÐ>åíñý?ÈÉÉýôËÿ]{x‡â:ðF¨š†º‰™)ýTRV>èhW%´Ó¿}×NŠèÌ¢¼îŸwÿ}ÊZšCG ¯ÚÙÅù²mÛvq¤ þeØPºÙ+**:º9S\{øxÿð:ïÞ‚vZ/Ïõ›6JIIEÄDIÔc„”^¢òÿû_zzúôéÓõlhßxt¯m»¶d${öï#·P×Ôxž›q1D09Ø€AO{{ôþêËnªÝª„6Ýů‡RPŸöò¨\°óìš ë(¦îG>í»áa‚IÆn^¥ ¶ÚoMùYsg“…Ü ÈÖ¶û5µ4é~Ï)Èa½ä£½¬¬,ÅõagÇá#†‡ÇDî;°ŸÞ;{ÞÜûáa7ïÝé¡­íè꼫r‘¿“gO3qݯÿµÖYZí¡üœysÃ"³¡m´fµjwU ꤽ¤ÿ:I™èøXï‹>‚Iû÷_¿aC`HÓsÀö eæý5/<2’íǹz]0-Ûà!ƒ/úûõéÛ§{÷îÑÌ|¯3fÌ8î~œ™r­”ϧÐ^c¼¦W¯^mx /i¸Á%o7–˜ï,Qq'ìU^6•‹‚^ºÄx‰ƒ‹éÈßKËÈÈKÉýÇaròò¼"ò’ßÿÐoß¾ý•›×ýç.XÚt­±D=FˆÊ 4½—˜îÚI±põþm í=öQþB?ã%¶N‡3+—èk###Ú7Þ•“—ËàæRPOÒŸÜ®}»Àk¡ÎÇKƬXc$Únÿ§1Ï«ôãì´òí°ûÔ¤&”÷¿Ävâ\¿sK°äž’ÒŒÙ3Cƒ…ûq˜ Ž´Øc9zÌÚ¸ÏÆšñ’#îǪíÇ¡¸¹J/_»"--­­­ý—¥þ_MqÍxÉ1wwÁø’[¯Æ—D õãTñ×#®ÚK ©ž¡ vtrdÍ¡ˆ^°`ë%666t®’Ò„6¼4’—ä‰ÙÝØn77£ÈC•—£«`}QŸK~Œ—œöäâ ¸ûy3^rüìI êe«VPL ‡ö^[›á#GPDÇ¥>Yó¯ §0— íÕK?ËL¯â%¦•MAíP¹ZÖ…K¾ÂOâœ9ïñÇÔ?™%÷Nž;#ì%ç/zÓ-êôivŽ‚O»ï€ ã%¾þ5yIŸ§¬¢<â×òòò$5éþy÷›7±^r)0€õZ½ÄÓÛ‹B{±1] ê}Ö“‹‹¥ˆþ믿X/qrrjÙ²%¯„‡Ð†—€Æõ’fB8èêeú"5cÖÌK—ƒüø#}£JÍâUzÉY/Ïj½dúÌ›L¶P…µgŸ`}T·ãGoÞ»}ëÞm“m[™Fàw{Œ°1—¼DåÈK|||j}†¶ïå@Š©©3§_òÿþÇ•”8Ï}+½ä„Çéª^RÔSfL[·y#E´¹•¥ ©òˆKèíë”6šl´=œ:Á†vÈMÁ6d$”‹4R®3ŽyšXÈÓgÍôÈ)Y¬—´·ûöûï"b£"bŸ´jÕj÷>«' ±ô^ãõëâŸ%­^+p‚Ä”dç#n‚E»öîa¼$ $ˆÂ<&1N°†ß†õ‰Ï’…C{î_óh»Îoccâã…º}ïë%Á—C„½$!)‘2ë7nx–ò¼Š—xù\ö’ðÁ’þù§ƒ£c§NX/±°°PSSChÃK€„xIã>!lsè ¢¢à[Ôjjý©«ÝKÔ54ü.Q…õõ7_khj²sAfdg¶mÛ–ê)ÉyB•h(/9zô¨¾¾¾‡‡‡8¡½ç€µBeL}®ö…§¿…v ^ò*¨Õ4ÔÏûûPP ÖüÓÔ`CûiFªlÛ¶£ÇŽaC;5‡#--}ôÔ Êgqó´{jS±[ͶSh[: ÈÂOâ<ËH6b¸¬`9O¹ñzºœ¼ìÜâBíž=éàí;w<ÓÔÒ"_ùeèP•¿—,f¼$ðr0…v¯ˆ9rÇN3áÐ>ã!XùÜÜr…³¦¦&IÚŒ—„„^¦ f½¤¸”׳— ææ¢^R.ä%”L¶šÐ/òÍ·ßN:µeË–TPPϘ9Co‚B^šØKšó ᤴçbÎý¤ ¤¨î9ªË%bp */ð¿êÇ¡BLLL ª]^G4´£Ÿ'ˆÚ÷"q*—ˆÚÓfÎX°øo&´³‹òR“…C;1íYMOs r² sÙ¸Îãq“ÓRØÐNádÔ4ý|aIñóô´z†6_šÆÉ¨3´‡?^·nÝíÛ·)¨éR«ªªRhS°wëÖ-00¡ /ä%’ò„ðÇ=G5*/а^ÂN÷Kccã*Ëë4Yhß}üPIY9=?û£ m^i‰¶¶vëÖ­»té"%%enaA¡íëë;`À„6¼4µ—HæÂ…’±ÐhÙ{=!ŒÊ 4¸—ü¯ry???}}}›üüü¦mOß iÏ>ÖÐ.æ_¹r墯o\|<ÚažD=AhÃK€„xIC>!\Ïéç_}£âKÈB£b .Aåþ×8Ïã‘¡Ðð’ÁK>¸5„~¡ÑÆB•hl/Ah#´¼^ÒD/©‰ÌÌL333ª7***ðð/^ÒÌ„‡‡/^¼899/ðà%x ¼/^à%x ðx ^€—ÀK¬—$''ÛÙÙåççãïà%^¼¤™áóù'Ož¤ºåÌ™3x–ÀK¼xIó“““³{÷nƒ;wî888Œ7NGGçàÁƒ<J/ðà%MMttôŠ+fΜ)¼ © þ”^à%ÀKša/¡—øSx €—/©»|Pð/ /ðà% Y~zQ.›2^¥<&q(¿J™ÅùÂ)K ²x¯Rö›T˜Ã+Üè ðÍÛÊz_n —Iy¯RQ^iQþ[©¸@8ñ‹ ‰Ç$î›TB©¨ì­T\VÊ&¥r&ñ)•¥Òò²W©Bø¯R9“ÊØô¢¢üíT!H/^¥—/^¼J/á%ð/^Ò„^Rüž^’ÉÍ#5ùmÜocttHJÒr³Þ–’ê‘Æ³1ˆIDATº½¤Pl/)®ÞKªJÉ/©öQ)©ÖKD¥^/ ANN޾¾>® :::|>¿Y¼„ó–—äWã%ÕI ã%‚T"H¹‚Ä­¦±DÄK ªñ^µ^RT³—ðÄö¾Ø^RQ«—xU‚Tx ܯ_€dúôééééMã%Œ”<ÏãD=K¨ÝK²jö’œ¼$»¸àaTD.¯õ’üš½$9=5››W”Të%¥ây‰¸8âyÉKx ¼ÀK€—4¢—¾>d$×oݤç=á%^/HŠ—¤qs¾ÿñ‡Î k7o<ìæ¤¥­Ý±S§˜Ô¤†òSóºô/Y½nm«V­È\Ž©2¸$·¸PZZzþ¢…iÙœz.ôššžfï蟔ø$&šÎîèìD^’š‘nï蘘”(Π×ðx €—/i/9ççM·êÝû÷1ƒK‚n\9ìê—žLRrú‚ç÷?““ûù—ÿ»þðy‰oH€š†ú“ͪÝUÕÔÕN{y”Lü}òÈÑ£)™¤ÿû8=]a/§;ÞÌÒœ¤$§¸ ûçŸëOýSSKs؈áUK†üô}ŒŽ:®ZcDFrÞ÷€?ÊÉËÿ2tèýÇIJ‚ƒ444-]ÒY¡s@Hã%cÆê,[±œ2Û¶›ÒÞ¸¤ÄŒìLMMMgW— ËÁ´e‰àxG'' M ¿ÿaÇÓY”””Ž;zïáÚèpéν»”1ße¡¡©©¢¢ââæÊH‰Û‘#_õéCiõšÕTf© ¼^à%ÀKÕKvY[Ñ­ÚÿJP•A¯7ÝkÛ®-ÉžÖZšêš©¹O¿ tðW}ûrvèÛ¿_¯/{“—XìÝMFEħ&KKKo3ß!<èUAA!äÆò’³Þçé°Óçϭݸ^JJ*,*\ØKÜŽ¥½“~Ÿ|5ôaäã¶íÚ‘‘ì·=¨©¥Eº]˜ÞGàOýú÷_»a'/›i&Yµfµªª*¯¬ôÇ?Ò^g7×§NR&:>Öû¢eú÷ï¿~ÆÀà ¦ç€íAÊÌûk^xd$Ûsõú5Ê 2Ø×߯Oß>Ý»w'/IKOkÙ²åŒ3Ž»»“ÇÐü2>¼^à%ÀK×KÌ÷ ¬Â'8 Š—lßµ“¶_{p;³8ŸÔ„òƒü=ý7ûýö‡²xìQþnøÃØ”§mÚ´Ù¶sÇÇè^‘ÍzÉí°ûròrÙEùä%“õo×¾}Èõ+nîÇ諌W { '?‡6n1ÝZÈçY챤ü½G¸|ÞþJ“¼ÂxÉ÷cƒK‚¯„ÒÆËW¯ikkÏ30 Ôÿë¯KÊùŒ—sw/_%ÔSÅK\¸‘Ž,54”‘‘¡Œ£“#mLLJªxñbÁ¯½ä%¼^à%ÀKÑKÎùzÑM×lÏ.ÆKö:ðÓ/ÿw'â¡ÉNSA+HL$yÉ!Ê{ú]ö§cn”¿zïf6¯pœžî€?êŒûíÿ† îı¶=0|ä¯$%‰iÏè~ßBˆ.]ºdTë%;,J•K^âäæ"P¢ÆK|ü…½„[ÊSVQñëyyù‡’štÿ¼ûÆÍ›X/¹ ¦—œ÷ö"YclܺukÊì³ÞGcãâÈKþ2øKØK^ÂKà%^¼¤‘¼$›óíßuèÐaåÚÕ{XwéÚåëo¿¡~¡RRRSgN÷ òÿþÇŠJJIœÆKtãʯ£Fª©«1ƒ^žt§ƒÉŽ1žÛ÷î°^|9Dà%·^yIBR"eÖoÜð,åù·½ÄÛç‚°—„GDÐÆ?ÿüÓÁѱS§Nðx €—/i /É(Ê}9bôÈV­Zµ‘‘1ê×à[W™™K¬Ú((*Ò-ùsµ/Îû_Ì*Îg¼dÌocé`-íGO¹33ªeätîÜYFV6)#Exp‰º†ú…K¾ä%ý¿þZCSƒQíyfºlÛ¶cÆêTë%”ö²UTœú 55¿À€"~ ã%d*U¼äŒÇYÚnniÁ+çkjjª©©1á0^z™õóÅ¥¼ž½zR~§¹y-^Â<‰c²u«¢¢â7ß~;uÚÔ–-[–W”ÃKà%^¼¤½$Ch¦×äìŒ$NªèÌ%1ÏÙÕ/ñð½ð,+=³(}B8*)î³Ï>›øûä*3½>xÎ .©núù¢:—ÅINKkæ’w™QÇ/MãdÔ9£ÚãððuëÖݾ}»âå ƒùªªªìàx ¼Ô«êÁb˜ÀKêòq—ëc½Dxæ’ͦ[Û´i##+rëjÎÛÓÏ7Är}õšQí½—ë+)-ÕÖÖnݺu—.]¤¤¤,,,X)—ÀKÀûãàà0räHa5jÔöíÛÝÝÝqq€—¼«—<ËθzÿVrVº°—DÄGŸô<™SeFµwò’Y®OŒe„E¤¤¢ÆåúŠy¼+W®øúúÆ'ijOÃKà% ^TTTL˜0AØKLMMgÍšÕ¨ë>D/iåúrêµ\_ñ».×WÜ´ËõÁKà%  5j#%zzz«V­ºví. ð’ ±½$«!–Î{a‰\®^/ Ǽyó/±²²Z±bEEE® ð’wíÄy§e„ëêÄ)’œÁ%â/×/—€†!11q̘1³gÏž3gN\\.’Ïܹs“““?@/iØA¯ ;¸DÜA¯5-×÷^/ ÅÆ'L˜°wï^\ >V¬XÖ¤^ò΃K Ä\R¯A¯ 2¸¤´A—ÀKà%<#Q@²½¤±—ä5õà’Fô /—| ^òj¾gѧòªk5­¡x«½´¦ø¯ñ‰ÐבZÛHë øºž»«&Îß rx ’ä%bvâäcp ¼^òñ{ §6/©¹ ‰ÿš‚¿¦fÒš¾‹Wñ’Z¢]$ÔkŠsÑžZx  —HÄà’¦™Q­&/y /—|J^"Ö<5Å¿Ø^R]ßmÙ»yI©^R}¿|ã%ˆg$ÁK@}@=/ù˜¼¤¶Nœ¼¤ðݽD¬¯#u}©ÓKÊá%|ˆ^ÂâêþQá%¶—\ºÜâmô§þYK'ã%ë6o¤#3 r„—8s;ëí)ä%U§-*h„Á%ÇÜÝ/\ô^œó]—ÀK€—ÀK¼D²¼dîg÷£.”Nõ¿\çà’·¼¤²±ä~ä#ÚrÐÁ®¾8ï2¸$2&ŠNêèäTZQ–š‘fïè”(nc‰Ð4ð$ÊKŸº—X´\x㊚†º‰™)ýTRV>èhÇHÉAÇý¿ü²×—½Ó_ÅK~6”¶(**:¸9û]R×P_¸dQçÎý‚ —/ûBMMAAAoÒÄ´lÎÕ[7Ô54¶››ÑOeee{gG2’ì¼¹óTUU5µ´V®^ÅxɲËÕèŠ 'MÊÌÍ&/±wrTW§¤4cÖÌÜÂüaÇ Nª¤ävìè݇÷545ü.‘”øúû 8P^^~ذaᑤ#wïÝÓÐÔ´Øe¡©©©¢¢âêæ&<| ^¼x‰yI»öí:vêÄ$—G}Chã€AO{{ôþêËnªÝHJ¢’㥤¤~Ÿò‡½«IF/±ÚoM[fÎ}çу³Þž”ïÛ¿ßêõkƒ®„ôÐîaïâd¾Ç’6ž8s*ðJetþ¢÷W}¾"!/±s´—•• ¾êàâ4ü×±Q·îßÑÖÖvvs±´ÚCÇŸ>w†“›%##3eÚԣǵk×ÎdÛÖý¶=óæ=ŽŒxÝãE{ÉHÙÙiõÐ"Ñ)â_»~ö2ØÏß¿O߾ݻw‡—/^"Y^’ñÚKô&OÚf¾ƒI×Ãî0^bët8³8þâ¿ÛÈÈ—ØØ¤a1‘Ù¼‚)3¦½ò’×Oß|Ìôãä•p/q>v„é¾ ‹ 7ß½kԘт†›}Œ—òy/YLªA^rãîm’%%¥™³g]¹Ì´—DÄDíÚ³{´Î:Þú€ëQ7ÊDÇÇ–”óÓ33*ûq¢Ù~œë¯½d•e‡‡—U”:lGù+W¯2^âvÄ­âÅ CCC:)¼x ð õ«ƒ6ÂS0^rüìIò’W­hݺ5yÉ.kÁý>êiyÉÜùo{ WÔK¼/ù’”xøxIKKO™6õ£=mܻߚñ’ÓžçÈKV®6¢Â™Á%g½<ÿœ:EQI‘öžò8ã}ñ½qêôiöNŽ´Åæà~¦u„ñ’Ø„øÜÂü'Œ—8¿å%»vQ&1)‘¼äȱ£” b¼ÄËÛ‹¼ÄØØ˜Nú^¼x‰dzÉlƒyöGœ™tú‚'ã%'&2.ºU«V{öY­Y·–v=MyîRÙLB[’SSZ¶l¹xÉâ+ׯvêÜyÉÒ%ñ‰‰´kýÆ É)ÏX/¹v󆔔Ôì9³C¯^8h ²²rn~ã%|.ˆxÉKx ðà%Íï%¯¼$¤ÊsÂßýð=ë%YB^BiÓ6“N:}öÙg¿ÿ©_ÅK2¹y=zjÓÆ­fÛ/ñ $/¹ûø¡¦–&©Æÿ ýEEEeáâEÕzI '}øˆ²²²íåätõt3ósÂ"kiiÑ6T¥‹Ê¢%‹yåü}û­µµµé-ÆKÍH/*áõìÙ“J33ßÉz ¿¢Üîða%%%z©¦®Rþ¢âµ—øÀK€—/‘\/y×éçSr22 s«~>«(?!5¹Ú'„Ÿe¤‰3ý|va^7_xæ’´Ì áy™Çƒ³ósÙI‹ù%iœôj§yMÏ䔉7—3¼x ðÉô’O`úùo-ȉu%ccã;wîà:/‘(/©ßôó¥4ý<»ð¼q°°°ðóóÃu^Òx^RÛ²85¬$þÖr5­!ž/Ʋ8õ™~¾º•9ßyúyx ðà%ç%â.©®§ðÝ;qŠë9ýü/©µ§LŒNx ðà%ë%ŸÀà)—/^" ^R['N£ .áKâàx ðà%’â%âtâ¼Ëà’¢*ƒK yp ¿ÞƒKà%ÀK€—|H^Ò ƒKxõ\ÒPOÃK(/Äÿ‡ð’OÇK$dpISwâÀK/aÖ9g’ð\Â=ÎU*«Zê«Z¾JÕRkÕÒÐ[W[¯X5X-_®j¼ÏTeðxÉ'à%ï2¸$çƒ\òŽ8ð$ÐK2ÞöNí•U5ÎuTYµzIqMOÖÒÖ[MõUûã„bxI9¼|R^„— ‘^’×^RRëW©Z[yknè­úª†¯Uu|³ª¹Ñ·úv_TVð’/ þé@/áÔ܉“Ug§ó;wâ¿w'·”]Ä/i¤Nx €—ÀKÍà%ï݉P¹@ºÝAÆKV¬1¢— éÏêìÄÙ°e™ÅÍ«¶ÇõøQO¯:;q._»ª¤¬ÄHÉ“¸h)))EÅübnuâ¼€—€¸Ð|^"^'ŽPcÉk/±­â%¹5wâä¿í%¢%¢"h—£}8;-Ì'LœÈxɺ ë[µjEo<ê~¼¡:qà%^Íì%ïÔ‰ÃzIÎÛ^r)4H]C}ãÖ-ªÝ»«©«õöd¼ÄÎÙ¡÷W_öþ²÷8=ÝJ/É'ùgù²/ÔÔô&MLÏá 6Œv)*):q%#15Û®¬¬Üµk×Í[Mª<‰£«§g±Û’¤¤¨”÷ùŸO™6UKKkį#Þ£±^à% ^RŸ¯5y‰—ÿEÊôé×÷°‹c¿þý{}Ù›¼$öY’””ÔSÿtts! ¡²¹ù7îÞî¡­íàâl±Ç’¶üwöô¾û)3{ÞÜáa‘±Q”7^¿n÷Þ=”ñð>/<âUAQáÚ­ä%^/Ð^Oo¯ ›7Ñ)žÄF7H'¼ÀKà {÷îmÔ;âûxÉÛ#^/ߺFB°Ïöã%ËVÒ˧)Œ—t°Ë-áÚ:ØQþ~Äã‡Q&".*¿´hêŒéŒ—” :nÌwï5f4m±²±îÇ1ÛeÞ²eKóÝ–VÖ{ÛËÉ-Zº„m,yþHN^¾°¤˜¼DÿÏ?Ú·oOŽâþß zïšµÆï2âµ¶™!á%ð¯p­¤)½ä]ŸÄyû„<`ËömŒ—Ì[` %%Å)Ìö—ãG(ãÞ=6û(û,‘¼ä¯ó/ñôñ–––ž2m*‰mÙ»ßFØKþ]¹‚ö._¹båêU”\a½ä Ý¡_G$)IÍH“‘‘i!D—®]¸%ÅõïÄ—x 4©—ÔsÚ’ô‚lE%¥.]»Ú:ÞogûÙgŸõÿúkrÆK |ùæÕ_GRSWË+)ŠLˆ¡ ÿ})4¨k·nŒ—­]ÓºuëøçO¸Ð˽{ž$Ä2}7ñÏ’Î_ô¦¼£«ó­ûwÿ4äâ%?¶gúÌ›M¶—XYï¥cŽ?vûþ]J[M·ÑËSgÏ”Vã%ïÖXRñ^/ÐŒ^òîsÏŸóñúBí ¦¡¢GOí k—Y/Ñ7¶U«V=´{?õóxðÓ­:wú¬ãgúSþ`¼äÞã0M--:ì—¡¿¨¨¨,\¼(·¸P»gOÚ»}çŽÜ¢Â™sfKKK·iÓf¼®n^q!;÷¼†††_`yÉ×ß|£©©ÉN[ÂÉÉjÛ¶íØßÆÖ¿^à%Ðl^RŸéÔžf¤&e¤°Ó–0^rÞÏ'5›“S\ õnDXô³iii3ÓÐ;7´´{rv0ÛmA»Ž>Áz‰÷%_Êœó9O.²Èp‰œ¼e¢#iãêuÆ{v 4Â˃õïµ׳iÓÖ-¬—0Ëõ…GG>M}NçÝa¾óÖý;=´µÜ\,­åœ:w†ñ’³ç.R悟/y‰á?†òòò$%Ññ‚‰b×oØ`µOð+xùx³%vöö£ÇŒé¡ÝCFFf̘1ºzzEÜÜü¼ {Éš5kzõê/ðhN/qv,aù46³8?!ãyÛvm×nÞ`neÙºu먧ñY¼‚{‘vXš=J0U¼µUí^²Ý¬eË–f–æ–{­Ú˵ÿ{É"ÖK.øûš˜ncÓvs3¶'-+£]»v›L6ï±ÞKçMJyVTVód×ËÑ:c«î·yã%ÁÕx‰…å.:¯åžÝûl¬åää–.eKØÚ6L]C½M›6”!5aÀa¼ä¤ÀKÃJ¬mlH‰JJKà%^Íä%Åy¶Î‚5ó8)L÷ÍÔ™Ó{õåƒÓORrÚÛ“îÖL›rÀ^° ðn›½¬—\ôcZDHGæÎÿ‹ñ’e+þ¥ã—­X¾Üh%%§#®¬—¬\c$+DÇN…—̘=ë«>_ 2Xw‚I‰×Å TÎÔéÓìè,Öö³^TÙ"rtdþ‚Œ—¬Xµ’Ž_¹j•ÑšÕ”Ž?VeÄë«~¡éÔªx‰£“™ ¯„ÇH ¼ÀK I½„™¶$àºÀ3n‡?d¼Ä'ÈŸ^JII¹Ÿ=I^²ÂبuëÖÑÉñö®N´}çËÀ×^r/"Œé²y¡©¥Éxɹ çiãagÇkwo‘dxû_sÚ’ÀËÁÌyÏxž#/Y³Î˜i8q9ê&ð¡½V¬—„Wv­Û°>*.FKK‹ñ’ ¾‚If]\]ï>¼?ä§Ÿ.^ªâ%6û÷Þ„ëj,—x 4§—¼w'Ncx-iدïÚ‰/ðhj/iNœ¼±^à% ^RŸNœÒ÷ëÄiì¯åâ7–ÀK¼šÇK@ÀKà%šÂKX¼>|œ'Ož¼iÓ&OOÏÆ(ÿð€—ÀKÞ'N̘1ÃÀÀàäÉ“ð/€ÕK>ø|¾™™ÙÂ… srrp5¼à%Í««ë”)SÂÂÂp)¼à%ÍI‰¾¾~pp0.€—@Ã@·USSS\‡÷#..núôéh5ðh°/ý+V¬Àuxo¸\nEE®€—¼x ÀKð€—/x ^ðà%/õ!22rýúõùùù¸^ð’æÇÁÁÁÀÀ %%—ÀK^Òüøøø`â5/x‰¤9eÊ???\ /xIó“’’²xñb >Ÿ«à%/if***vïÞmddÄårq5¼à%ÍÙ ®€—¼D"À"^ðà% ñDFFâ:/€æ'==}úôé¸ÀK^¼¼DÂàñx¸ð€—ÀK$‚… †††â:ÀK^/‘”?ÄÑ£Gq)à%/ÍONNŽ‘‘‘±±1æ„…—¼4?|>ïÞ½ .ÌÏÏÇÕ€—¼4?'Ož400HNNnâó._¾¼¼¼×^ððwîÜÑ××oÊ‘°[¶liÑ¢…¥¥%.>¼à% *ÉÉÉFFF|>¿ Ε””$''G^"++‡‹/x h6~þùgiiiS3Sú9~üxôæÀK^š‡C‡µhÑb¥ÑÊBwêô©”?}ú4. ¼à% þ:v쨩¥™•—Í-)ÊÈâ¨vWUWWdzÊð€—€¦fêTA‰·Ï…¢ÒââÒbúyÌýmY¾|9.¼à% é8zô()ȢŋŠKy<>¥úIù_†þ"++…K/x ¨ƒœœœú¯ó———§¨¨¨¢¢—WÂ/))+--+¥Ÿ”¿uç–´´ôÏ?ÿŒ°ðhR233§L™‚ëðaÁL¼FNYŸBæÏŸß¢E‹óÞçIDÈHøå|A*ã—VªÉò•ËiïÞ½{qµá%ФŒ1áƒÃÃÄ222òýÞ"--=uÚ´J)!#)+£T!øIyÚ’•“¥Ò¥‹¢¢bVV®6¼à% ‚ƒƒßoýa.—Û½{÷Ï:~]JR^V^Q.œÊjRzòô)r—¹sçâRÃK^‹åË}4‡—ðÝ7Œ‹T/?˜—´½„_2köl:2$$ ^ðÐðܽ{WZZZWW—Çç±%#Jl“I\bœœœ\ß¾}1^ðÐÀ”””üðÃíåÚGÆ<)á—0%LKɋװ­&ü2>_²ÇjÀÂK^žµk×’d˜ï2/.-®ô’W%yù:Uª Û•SPTðÝ÷ßÉÉÉÕóñ/x xCxx¸´´ô !ƒó óŠKßêÄa¼äåË—Â^B{KÊJéÈàËÁôF}}}ôæÀK^Rо}û’^ܼ{“[RôÊK*Ê^uâTJ å™®fˆ IÇϘ9£E‹...¸’ð€—€úbjjJb±~Ó†‚âò ^¥—°#^/¡ÃÞò’Š7^’ÊISUUUTTÄz~ðh© \œððpYYÙÞ_~™ž•!ê%l'N-^’_\pÀîÖóƒ—@£ààà0räHa5jÔöíÛÝÝÝqq>> $--}1À7‡›+ì%Uúqj÷zïÐaC©œ‡â’ÂK !¡»Î„ „½ÄÔÔtÖ¬Y|>ç#ãСC-Z´X°h'?“Ü"¿ÒKØq¯5/a§0x ËxɽÇ÷Û˵ÿùçŸqUá%ÐÀ„††Ž5Š‘==½U«V]»v —å##))©cÇŽZÚ=bŸÅ½ñWðœ°Èó8Už~ý}ñ<*&-N¸+'ëu“I¯[´šìäu*¦-´ö2%•Oâ:q¨*‡J£2©d*?ôÑ5E%E¬ç/€ê½¤ zpHJà%’‰¡¡!ÛƒÃ&fˆI|FbåÓÂo5™TöæZM¸%EE‚ÄI‘ ¥¤RJØÆz—à áÌd*'*56üY$[þN :é¢E‹pýá%P—4vI ^"yDEEIKK÷û¶?ÓƒÃ&ᮜä¬ç)9iéyœÌ‚¬n.£&Å……<.%nåÏÂÊîFJè˜Ê‘%LcÉs¦‡J£2…O1dèO¤&wïÞÅ_^U¼¤Ñ{pø‚T/‘4äääHf/˜cb¹M8mÛ½}»•Ùν»¬wí¶Ùcµßjï}Ö¶6ûm÷°;pÐÎÖö0“½N‚—´öÒ1t$OïÚm³ÛÂz•C¥mÝm*|Š%«–¶¨¤¤¤x ¼ñ’&èÁ!)—H...-$€-[¶ào//i̾`ñx‰dáááá"œ8q x ¼å%݃S/^âxIãõàð_÷à¼äE¼x Ôå%߃Sö¢¼^¼Äñ’ÆîÁ){Q/^u{É)i´œrx ðÛK·‡R¼x ˆã%݃Sé%/à%ÀK /i‚’x ðÏK¹Gà%/á%ÀK N/iü’’ðà% ž—4nÎ —¼„—|4¼(//ÉÎ~×w•ææâÒÁK /i‚x‰¤q²W/ÏÁƒ_å{÷>Ü¢ÅÓóç)Ÿpò$åŸØÛÓO&9µkGÇ9ÂÌMN¾¨£ã(#C»Ü””­­ÿ÷ò%mÏyôˆ¶üñsX¤­-½äçå $†Ï¿žt!bÿþ9sÎ~÷]”ƒÙIIV–£¬,iã%d*úú¾ãÆ9µmK?é]áûöÑö´Ë—Ùr§N¥-e\.ã%·Ö¬¡Ÿä1¬—0ôÌ×Wà.'œ:E)ùÂæí$1aøsÀK^ò©s´K—à™3uï~ÓȨ !ìញ ý$i`¼ÄcàÀsPæÆòåÌ[íÙ#è©ñ÷g ñŸ0á°”Tyq1ã%ô^*Ó©];FPÈK⎧Lü‰t0³‘ôÕÝ/¿Ä¸¹áo/ð© ¯ïÒ¡ƒ 1ãâEzy\MÍMQѾeK’ á~?]]2ç~~”§#iûõþaJxÁç;Ëɹ««ÿïu?yIqz:ë(+ûª§²a&dölÁñeed0>£F±^ryÞ¼Ô  ü-à%>u˜Nié2.W ýE/O÷ïOya/áq8n ÇTUù/_¼8ûí·Ž22Wþþ;öèQAƒïØÙ { [2;î5hÆ ‡Ö­ƒgÎ|zþüÝM›œÚ·g†¤÷LLØ>/ðé’u暑ç!Ì˸cÇØªUƽƟ8!ØehHù¢”?]]ò Ás ­¬„Ÿf$ãeEùÍ›ç„ËÊ®.^|¬{wÚâÒ¡ƒŸž^Þ“'LÉ‚q¯¯;‰¼xH5J23ßõ]%YYŒÄx à%ðÀKà%šŠ´Ë—Ó…ý÷_3~†„“'…§8{¿ÃÄ,ä½¹ojJêEYY³Rg9b^ ¶f\ß^@²¼„™¯,ÆÕµ>‡‰Yȧà%â_ x Éõ’¬Nhj>²´¤ŸGTTba ;œ»†ÆeåÙ³Ëy<Ú’à1h³¼¼÷ðáyQQ´%ýêUz×õþqSPˆLs­;/P›—Ä=*¸AþóÙeèîEéöFùÒÜÜÿ½žš™ç”Ô„òׯ3÷û„“'Ù{?“´g}Ë–÷î8pÀYNŽŠ-°Ú~‡ì°0º} ÚTæÎ͸qCô°‚„ú$ÇŽ¥‘¶¶Â{EOZÅKjúl¢ÅF;;S†›œL»BfÏm/©r|µ×°öBjñæ×aÖ1.ˆ‹«©œwº5Ò\ëÂKÔæ%Ìüë·×®uhÝš2Ì—oF#èfVÎã=Ú½[pc{öL`-•KìÒM”¹ßS†½÷3ù[«W;HKÓÏÛÆÆ”âOœ-°¦ñÏ.^¤¯õnJJ‚f//áÞ_ºDÅÏœÉܳ#Þ+zÒ*^RÓg-–nó”áedÐñW-ª¢¢ÇW{ k/$ýÊÁÊÆ•« ^ùûoQ/I8uŠò9ááÕ–ó®—¢¦Ó\ëÂKÔæ%Ì’¼ì=•ÇáÐîk†ÿßÞý¼ÂÆqOJ²²¡Ä?`aaC6bcáðø”%Y0↸ws‘›üÈ”DÜÒ%S”B‰¹òc2wŽqï™Áwî£ñ8ÏyÆŒ éýZsžó=_Ïæùœg¦NçùÆÆ—’yà>ßÜÈËKmc¬­MÕÕ––þ‹FÕz^]M¯ýOŸÕþÁÕÎÎL}ýi0h”ˆ£¾kpÂátWòXÿ½¶öÏñ±$˜ÁüüP_Ÿ>ìgW—´'¥ÔîËno¯~Ö¼©'—Øz3ËÆNNÔût~¯¯——{"…9Þw3¹98PG'«ªô\"]]lmÍ57OTVÚêä:¶fÞ뽃ä@¹$õ¸ßßÿµºZþœmlŒ_^Ê‘½@@mcLTTœ­¬<¯÷ÿ¼©–5OÄå‘=PP°ÐÚª–@OÁ¤ë~«©‘K¶{zÒ]ýD~45 -´µ¹Ž£‹ìïË.ye¶¡a¬¬LVYý¬ïM_äKofYö«»[òÓHqñrG‡'R˜ãms˜¡È}2©~*)a©½]Ï%ó--R\æêhzÚVç SáÛÌ{½w\x 7óQ%‰»;órýÈ}"á[-˵¶aê¿¶³¾7ͦ7OÙT«··²ØÛ*˜ãý'ðÕ"Ú‹U.9 ¥7 .¯ÖÉu*27C.f.ùôÿ)¹€ÎuœëP(›ýr ¹KÈ%€\@.är —KÈ%€\@.×#Ì]m]­iŸZIEND®B`‚jbosscache-core-3.2.8.GA/src/main/docbook/images/LocalCacheLoader.doc0000644000175000017500000005500010660354341025122 0ustar moellermoellerÐÏࡱá>þÿ (*þÿÿÿ'ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿì¥Á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-3.2.8.GA/src/main/docbook/images/ClusteredCacheLoader.doc0000644000175000017500000005400010750374443026026 0ustar moellermoellerÐÏࡱá>þÿ ')þÿÿÿ&ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿì¥Á‘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-3.2.8.GA/src/main/docbook/images/TreeCacheArchitecture.png0000644000175000017500000006247210660354341026235 0ustar moellermoeller‰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-3.2.8.GA/src/main/docbook/images/MultipleCacheLoaders.png0000644000175000017500000015407310660354341026077 0ustar moellermoeller‰PNG  IHDR(ÚW¨—gAMAÙܲÚ cHRMz&€„ú€èu0ê`:˜pœºQ< pHYs  šœ†IDATxí œÔÔù÷Áûý~ÁÖ+â­ˆ/U‘R/Û²­´€R‹^ÚRP± „¢¸Šà º V¨B«ÐBµ®VuÕºV·Öµ•j©¢µXW]/«®ò¼çI晓™M²ÉÌ$s’ù Ÿ%™ääœ'Ïy.ß99Iªˆ6vÃ_W: |ŒÑ@W}…ýðgØ@á6`Œ£CäænU…r%øŠ9¨$»Ã¹">Å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-3.2.8.GA/src/main/docbook/images/Marshaller.png0000644000175000017500000002035510660354341024133 0ustar moellermoeller‰PNG  IHDR*¥ñ¢ ´IDATxÚíÝ‹eåyÀñù3ò'ôïLÖX5“ŽNrã-kÕ5º0qÈȦASSÓ$«ÓŒnªØ¨T\;aZÁj³uÉ“ [·YA2XX:a`aAXX2éí­Ó “{ïyÏ{~Þóãóåa™½sϯwÞç|ïûžsž;7 væ4€~ôý:«ŸÛÈK!ýt^¹}8FhøÙ•~ôC?@?0KýÌ}Êä‹©¯dظeß–c[‡GA?Ðý¤:iª¢¥ŸÃ¥èº©Ÿ¹?2¶†À{&ßXÏØ.¥n‹~ Sú™úoÒ`eªE’ì•:è ÿ46¢è¸~¦nëL’SÒz&GQ1órô]ÖOà‡Håäø!¼·ôÒOnIÞ@?@?Qc—© 9:'69Õ¸³žL}GßyýúõÑ1Þ¼ySG€Vê§ŒäôéÓï¾û®^íÐO 4:„Ï|æ3«««ûûûz ´@?ÝàúõëƒÁ`ooOú©Ón@?usáÂÓnÐhýtÓn`ôS7î´úÐýè@ؾtA#Ðý ›É€†³öì3:*ýк˜ 7Þ}þŒ¾J?ôúµêçìs+ D?ôSØßÛùà­Wž}êë,-~áÎ;>;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-3.2.8.GA/src/main/docbook/images/CacheLoader.png0000644000175000017500000001755510660354341024203 0ustar moellermoeller‰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-3.2.8.GA/src/main/docbook/images/SPI.png0000644000175000017500000010725510660354341022501 0ustar moellermoeller‰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-3.2.8.GA/src/main/docbook/faq/0000755000175000017500000000000011675221641020633 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/docbook/faq/en/0000755000175000017500000000000011675221642021236 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/docbook/faq/en/master.xml0000644000175000017500000021200411236523520023243 0ustar moellermoeller Frequently Asked Questions about JBoss Cache Release 3.2.0 Malagueta August 2009 Manik Surtani manik AT jboss DOT org Ben Wang ben DOT wang AT jboss DOT com Bela Ban bela AT jboss DOT com Scott Marlow smarlow AT novell DOT com Galder Zamarreño galder DOT zamarreno AT jboss DOT 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 Forum. This FAQ is divided into specific sections, all pertaining to the core JBoss Cache library. POJO Cache has a separate FAQ document pertaining to POJO Cache specifics. 2005 2006 2007 2008 2009 JBoss, a division of Red Hat Inc., and all authors as named. 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, and caches would participate in ongoing JTA transactions. 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 edition of JBoss Cache (often referred to as POJO Cache) comes with a separate set of documentation (Users' Guide, FAQ, etc.) available on the JBoss Cache documentation website. JBoss Cache is made available in one of four different packages: jbosscache-core contains the core Cache library for users who do not wish to use the additional functionality offered by POJO Cache. jbosscache-pojo contains the core Cache library as well as POJO Cache extensions and dependencies. 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 POJO Cache 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 about licensing? JBoss Cache is licensed under LGPL, anOSI-approved open source license. 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.org subversion repository. See the JBoss Cache development wiki page for details. How do I build JBoss Cache from sources? Read the README-Maven.txt file in the source root. Note that you will need a JDK >= 5.0, and Apache Maven >= 2.0.6. Which versions of the JDK are supported by JBoss Cache? JBoss Cache is baselined on Java 5 and is tested on Java 5 and 6 VMs. 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. . How do I know the version of JBoss Cache that I am using? java -jar jbosscache-core.jar will spit out version details. Can I run JBoss Cache outside of JBoss Application Server? Absolutely! Even though JBoss Cache comes integrated with JBoss Application Server, it can also be used in any other 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 Users' 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. What about from 2.x to 3.x? JBoss Cache 3.x is API compatible with 2.x, although as far as possible you should refactor your code not to use deprecated methods as these may disappear in future releases of JBoss Cache. JBoss Cache 3.x comes with an all new configuration format. Old 2.x configuration files will still work, although you will get a warning in the logs about this. Again, as far as possible, we recommend migrating your configuration file to the new format. Scripts are provided with the JBoss Cache 3.x distribution to migrate configuration files (see config2to3.sh andconfig2to3.bat). Note that to take advantage of some of the new features in JBoss Cache 3.x, you need to be using the new configuration format. Where can I report bugs or problems? Please report any bugs or problems to JBoss Cache User Forum. JBoss Cache - Core Using JBoss Cache 2 or 3 on JBoss AS 4.x JBoss AS 4.x ships with JBoss Cache 1.4.x. To make use of new features, performance improvements and bug fixes in newer releases, you can follow some of the steps outlined onthis wiki page. 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 this wiki page. JBoss Cache 3.x with MVCC in particular works very well as a Hibernate second level cache. What about using POJO Cache as a Hibernate cache? It is not necessary to use POJO Cache for second level cache inside Hibernate because Hibernate manages fine-grained fields in Java objects. Using POJO Cache won't provide any advantage, and will be an unnecessary performance drawback. 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. Can I use a schema or DTD to validate my JBoss Cache configuration file? As of JBoss Cache 3.x, yes. An XSD schema is provided in your jbosscache-core.jar file, and is also available online, on http://www.jboss.org/jbosscache/jbosscache-config-3.0.xsd. You can configure your IDE, text editor or XML authoring tool to use this schema to validate your file. 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 for network communications. A JGroups configuration section is present in your JBoss Cache configuration. 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 Users' 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 Users' Guide for more information on Buddy Replication, and how it can be used to achieve very high scalability. I'm using Buddy Replication. Do I need to have some form of session affinity? Session affinity relates to returning to the same cache instance for the same data being used. While this is strictly not a requirement for Buddy Replication, it is greatly recommended to minimize moving state around a cluster. 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 Users' Guide for details on how to configure the JGroups Multiplexer. A faster and more efficient approach is to use a shared transport in JGroups. Please see the JGroups documentation for more details on how to do this. 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 asJBoss Transactions. While JBoss Cache does ships with a dummy transaction manager (org.jboss.cache.transaction.DummyTransactionManager), we do not recommend using this for production. It is not thread safe, and is intended for internal testing only. 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. The org.jboss.cache.transaction.GenericTransactionManagerLookup class that ships with JBoss Cache is able to detect and bind to most popular transaction managers. See the GenericTransactionManagerLookup javadocs for more information. 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. As of JBoss Cache 3.x, when using the MVCC locking scheme, only READ_COMMITTED and REPEATABLE_READ are supported. Any other isolation level provided will either be upgraded or downgraded accordingly. How does JBoss Cache lock data for concurrent access? In JBoss Cache 2.x, by default pessimistic locking is used 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. In JBoss Cache 3.x, optimistic and pessimistic locking are deprecated in favour of MVCC (multi-version concurrency control), which is far more efficient than either optimistic or pessimistic locking. For a detailed discussion on our MVCC implementation, see this blog entry andthis wiki page. How do I enable Optimistic Locking or MVCC in JBoss Cache? Please see the configuration section of the Users' Guide for details. 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. Does JBoss Cache support SELECT FOR UPDATE semantics? Yes, but this is is only possible if you are running within a JTA transaction and are using either MVCC or PESSIMISTIC as your node locking scheme. To achieve SELECT FOR UPDATE semantics, simply do: 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 implications if network latency is a problem. How can I do a mass removal? If you do acache.removeNode("/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 Users' Guide for more details. JBoss Cache uses a : character in its object name. This causes problems with my MBean server. What can I do about it? This is something we have seen with some MBean servers. By default, JBoss Cache uses jboss.cache:service=JBossCache as a prefix to all objects it binds in JMX. To work around this, use the -Djbosscache.jmx.prefix JVM parameter to pass in an alternate prefix. Can I disable JBoss Cache management attributes? Yes, you can. See the section on configuration in the JBoss Cache Users' Guide. 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. This is on the roadmap though, so do keep an eye on JBCACHE-60 if you are interested. Does JBoss Cache handle the concept of application classloading inside, say, a Java EE 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 Users' Guide for more details. To solve the second kind of issue, you can use the the UseLazyDeserialization configuration option in JBoss Cache, which wraps your objects in a Marshalledvalue wrapper. The MarshalledValue serializes and deserializes your object on demand, ensuring the proper thread local context class loader is used each time. 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.notifications.annotations.CacheListener annotation for details. How do I implement a custom listener to listen to cache events? See the Users' Guide on this subject. 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 Users' 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 guide 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. 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 wakeUpInterval milliseconds (or wakeUpIntervalSeconds seconds, prior to 3.x) 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 wakeUpInterval so the timer thread processes the queue more frequently. 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.jdbm.JdbmCacheLoader : this implementation is based on JDBM, an open source file-based transactional persistence engine. org.jboss.cache.loader.bdbje.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 Users' Guide for more details. Is the FileCacheLoader recommended for production use? No, it is not. The FileCacheLoader has some severe limitations which restrict its 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 its use is restricted to testing. Can writing to cache loaders be asynchronous? Yes. Set the async attrobute to true. See the JBoss Cache Users' 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 Users' 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 Users' 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 Users' 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-3.2.8.GA/src/main/docbook/css/0000755000175000017500000000000011675221643020656 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/docbook/css/dummy.css0000644000175000017500000000003310764261746022525 0ustar moellermoeller/* This is a placeholder */jbosscache-core-3.2.8.GA/src/main/docbook/userguide/0000755000175000017500000000000011675221643022062 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/docbook/userguide/en/0000755000175000017500000000000011675221650022462 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/docbook/userguide/en/modules/0000755000175000017500000000000011675221647024140 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/docbook/userguide/en/modules/deployment.xml0000644000175000017500000004324511237000670027033 0ustar moellermoeller 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. After creation, you could share your cache instance among different application components either by using an IOC container such as Spring, JBoss Microcontainer, etc., or by binding it to JNDI, or simply holding a static reference to the cache. If, after deploying your cache you wish to expose a management interface to it in JMX, see the section on Programatic Registration in JMX.
Via JBoss Microcontainer (JBoss AS 5.x) For detailed information on how to deploy JBoss Cache instances this way, please check section "11.2. Deploying Your Own JBoss Cache Instance" and more specifically, section "11.2.3. Deployment Via a -jboss-beans.xml File" in the JBoss Application Server 5 Clustering Guide.
Automatic binding to JNDI in JBoss AS Although access to cache instances bound to JNDI is not possible, JBoss Application Server 5 binds a CacheManager to JNDI which can be looked up and from where cache instances can be retrieved. Further detailed information can be found in section "11.2.1. Deployment Via the CacheManager Service" in the JBoss Application Server 5 Clustering Guide.
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 its 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. If a CacheJmxWrapper is registered, JBoss Cache also provides MBeans for several other internal components and subsystems. These MBeans are used to capture and expose statistics related to the subsystems they represent. 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 JmxRegistrationManager constructor.
JMX-Based Deployment in JBoss AS (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 various other components, and exposes these statistics through a set of MBeans. Gathering of statistics is enabled by default; this can be disabled for a specific cache instance through the Configuration.setExposeManagementStatistics() setter. 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 Configuration.setExposeManagementStatistics(false) and 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 and component 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.
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 using the <literal>jconsole</literal> Utility 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 using your JDK's jconsole tool. When running a standalone cache outside of an application server, 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 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.
jbosscache-core-3.2.8.GA/src/main/docbook/userguide/en/modules/introduction.xml0000644000175000017500000002377311111047320027372 0ustar moellermoeller Overview
What is JBoss Cache? JBoss Cache is a tree-structured, clustered, transactional cache. It can be used in a standalone, non-clustered environment, to cache frequently accessed data in memory thereby removing data retrieval or calculation bottlenecks while providing "enterprise" features such as JTA compatibility, eviction and persistence. JBoss Cache is also a clustered cache, and can be used in a cluster to replicate state providing a high degree of failover. A variety of replication modes are supported, including invalidation and buddy replication, and network communications can either be synchronous or asynchronous. When used in a clustered mode, the cache is an effective mechanism of building high availability, fault tolerance and even load balancing into custom applications and frameworks. For example, the JBoss Application Server and Red Hat's Enterprise Application Platform make extensive use of JBoss Cache to cluster services such as HTTP and EJB sessions, as well as providing a distributed entity cache for JPA. JBoss Cache can - and often is - used outside of JBoss AS, in other Java EE environments such as Spring, Tomcat, Glassfish, BEA WebLogic, IBM WebSphere, and even in standalone Java programs thanks to its minimal dependency set.
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 Users' Guide, FAQ and tutorial all available on the JBoss Cache documentation website. As such, POJO Cache will not be discussed further in this book.
Summary of Features
Caching objects JBoss Cache offers a simple and straightforward API, where data - simple Java objects - can be placed in the cache. Based on configuration options selected, this data may be one or all of: cached in-memory for efficient, thread-safe retrieval. replicated to some or all cache instances in a cluster. persisted to disk and/or a remote, in-memory cache 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 most Java EE compliant transaction managers). attach to JMX consoles and provide runtime statistics on the state of the cache. allow client code to attach listeners and receive notifications on cache events. allow grouping of cache operations into batches, for efficient replication
Local and clustered modes The cache is organized 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. JBoss Cache can be either local or replicated. Local caches exist only within the scope of the JVM in which they are created, whereas replicated caches propagate any changes to some or all other caches in the same cluster. A cluster may span different hosts on a network or just different JVMs on a single host.
Clustered caches and transactions 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 completes successfully. All modifications are kept in a list associated with the transaction of the caller. When the transaction commits, changes are replicated. Otherwise, on a rollback, we simply undo the changes locally and discard the modification list, resulting in zero network traffic and overhead. For example, if a caller makes 100 modifications and then rolls back the transaction, nothing is replicated, resulting in no network traffic. If a caller has no transaction or batch associated with it, modifications are replicated immediately. E.g. in the example used earlier, 100 messages would be broadcast for each modification. In this sense, running without a batch or transaction can be thought of as analogous as running with auto-commit switched on in JDBC terminology, where each operation is committed automatically and immediately. 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. All of the above holds true for batches as well, which has similar behavior.
Thread safety The cache is completely thread-safe. It employs multi-versioned concurrency control (MVCC) to ensure thread safety between readers and writers, while maintaining a high degree of concurrency. The specific MVCC implementation used in JBoss Cache allows for reader threads to be completely free of locks and synchronized blocks, ensuring a very high degree of performance for read-heavy applications. It also uses custom, highly performant lock implementations that employ modern compare-and-swap techniques for writer threads, which are tuned to multi-core CPU architectures. Multi-versioned concurrency control (MVCC) is the default locking scheme since JBoss Cache 3.x. Optimistic and pessimistic locking schemes from older versions of JBoss Cache are still available but are deprecated in favor of MVCC, and will be removed in future releases. Use of these deprecated locking schemes are strongly discouraged. The JBoss Cache MVCC implementation only supports READ_COMMITTED and REPEATABLE_READ isolation levels, corresponding to their database equivalents. See the section on transactions and concurrency for details on MVCC.
Requirements JBoss Cache requires a Java 5.0 (or newer) compatible virtual machine and set of libraries, and is developed and tested on Sun's JDK 5.0 and JDK 6. 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, as well as several optional jars for optional features.
License JBoss Cache is an open source project, 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.
jbosscache-core-3.2.8.GA/src/main/docbook/userguide/en/modules/architecture.xml0000644000175000017500000004262411111047320027327 0ustar moellermoeller 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 qualified name, Fqn.ROOT.
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.
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 also 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 its 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. To put it simply, don't use such methods unless you really know what you're doing!
Interceptors JBoss Cache essentially is a core data structure - an implementation of DataContainer - and aspects and features are implemented using interceptors in front of this data structure. A CommandInterceptor is an abstract class, interceptor implementations extend this. CommandInterceptor implements the Visitor interface so it is able to alter commands in a strongly typed manner as the command makes its way to the data structure. More on visitors and commands in the next section. Interceptor implementations are chained together in the InterceptorChain class, which dispatches a command across the chain of interceptors. A special interceptor, the CallInterceptor, always sits at the end of this chain to invoke the command being passed up the chain by calling the command's process() method. JBoss Cache ships with several interceptors, representing different behavioral aspects, 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 the RpcManager class 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 in the order in which they would be encountered by a command.
Writing Custom Interceptors Custom interceptors to add specific aspects or features can be written by extending CommandInterceptor and overriding the relevant visitXXX() methods based on the commands you are interested in intercepting. There are other abstract interceptors you could extend instead, such as the PrePostProcessingCommandInterceptor and the SkipCheckChainedInterceptor. Please see their respective javadocs for details on the extra features provided. The custom interceptor will need to be added to the interceptor chain by using the Cache.addInterceptor() methods. See the javadocs on these methods for details. Adding custom interceptors via XML is also supported, please see the XML configuration reference for details.
Commands and Visitors Internally, JBoss Cache uses a command/visitor pattern to execute API calls. Whenever a method is called on the cache interface, the CacheInvocationDelegate, which implements the Cache interface, creates an instance of VisitableCommand and dispatches this command up a chain of interceptors. Interceptors, which implement the Visitor interface, are able to handle VisitableCommands they are interested in, and add behavior to the command. Each command contains all knowledge of the command being executed such as parameters used and processing behavior, encapsulated in a process() method. For example, the RemoveNodeCommand is created and passed up the interceptor chain when Cache.removeNode() is called, and RemoveNodeCommand.process() has the necessary knowledge of how to remove a node from the data structure. In addition to being visitable, commands are also replicable. The JBoss Cache marshallers know how to efficiently marshall commands and invoke them on remote cache instances using an internal RPC mechanism based on JGroups.
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 interceptor 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 , and information around which nodes have been locked, etc. 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 organization remote calls to organize 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 favor 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 CacheMarshaller300 . 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 or extending the AbstractMarshaller class, and adding it to their configuration by using the Configuration.setMarshallerClass() setter.
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 3.0.x ships with CacheMarshaller300 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.
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-3.2.8.GA/src/main/docbook/userguide/en/modules/configuration_reference.xml0000644000175000017500000040513111236775757031564 0ustar moellermoeller 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. cache.jdbc.table.name=jbosscache cache.jdbc.table.create=true cache.jdbc.table.drop=true pushStateWhenCoordinator=true pushStateWhenCoordinatorTimeout=20000 ]]>
XML validation Configuration XML files are validated using an XSD schema. This schema is included in jbosscache-core.jar and is also available online: http://www.jboss.org/jbosscache/jbosscache-config-3.0.xsd. Most IDEs and XML authoring tools will be able to use this schema to validate your configuration file as you write it. JBoss Cache also validates your configuration file when you start up, and will throw an exception if it encounters an invalid file. You can suppress this behavior by passing in -Djbosscache.config.validate=false to your JVM when you start up. Alternatively, you can point the validator to a different schema by passing in -Djbosscache.config.schemaLocation=url.
Configuration File Quick Reference A list of definitions of each of the XML elements attributes used above, and their bean counterparts for programmatic configuration. If the description of an attribute states that it is dynamic, that means it can be changed after the cache is created and started.
The <literal><![CDATA[<jbosscache />]]></literal> Element The ]]> Element Description This is the root element for the JBoss Cache configuration file. This is the only mandatory element in a valid JBoss Cache configuration file. Parent none (is root element) Children ]]>, ]]>, ]]>, ]]>, ]]>, ]]>, ]]>, ]]>, ]]>, ]]>, ]]>, ]]> Bean Equivalent Configuration
<literal><![CDATA[<jbosscache />]]></literal> Attributes ]]> Attributes Attribute Bean Field Allowed Default Description xmlns - urn:jboss:jbosscache-core:config:3.2 urn:jboss:jbosscache-core:config:3.2 Defines the XML namespace for all configuration entries. xmlns:xsi - http://www.w3.org/2001/XMLSchema-instance http://www.w3.org/2001/XMLSchema-instance Defines the XML schema instance for the configuration.
The <literal><![CDATA[<locking />]]></literal> Element The ]]> Element Description This element specifies locking behavior on the cache. Parent ]]> Children Bean equivalent Configuration
<literal><![CDATA[<locking />]]></literal> Attributes ]]> Attributes Attribute Bean Field Allowed Default Description isolationLevel isolationLevel READ_COMMITTED, REPEATABLE_READ REPEATABLE_READ The isolation level used for transactions. lockParentForChildInsertRemove lockParentForChildInsertRemove true, false false Specifies whether parent nodes are locked when inserting or removing children. This can also be configured on a per-node basis (see Node.setLockForChildInsertRemove() lockAcquisitionTimeout lockAcquisitionTimeout (dynamic) Any positive long value 10000 Length of time, in milliseconds, that a thread will try and acquire a lock. A TimeoutException is usually thrown if a lock cannot be acquired in this given timeframe. Can be overridden on a per-invocation basis using Option.setLockAcquisitionTimeout() nodeLockingScheme (deprecated) nodeLockingScheme mvcc, pessimistic, optimistic mvcc Specifies the node locking scheme to be used. writeSkewCheck writeSkewCheck true, false false Specifies whether to check for write skews. Only used if nodeLockingScheme is mvcc and isolationLevel is REPEATABLE_READ. See the section on write skews for a more detailed discussion. useLockStriping useLockStriping true, false true Specifies whether lock striping is used. Only used if nodeLockingScheme is mvcc. Lock striping usually offers greater performance and better memory usage, although in certain cases deadlocks may occur where several Fqns map to the same shared lock. This can be mitigated by increasing your concurrency level, though the only concrete solution is to disable lock striping altogether. concurrencyLevel concurrencyLevel Any positive integer; 0 not allowed. 500 Specifies the number of shared locks to use for write locks acquired. Only used if nodeLockingScheme is mvcc. See the section on JBoss Cache's MVCC implementation for a more detailed discussion.
The <literal><![CDATA[<transaction />]]></literal> Element The ]]> Element Description This element specifies transactional behavior on the cache. Parent ]]> Children Bean equivalent Configuration
<literal><![CDATA[<transaction />]]></literal> Attributes ]]> Attributes Attribute Bean Field Allowed Default Description transactionManagerLookupClass transactionManagerLookupClass A valid class that is available on the classpath none Specifies the TransactionManagerLookupClass implementation to use to obtain a transaction manager. If not specified (and a TransactionManager is not injected using RuntimeConfig.setTransactionManager(), the cache will not be able to participate in any transactions. syncCommitPhase syncCommitPhase (dynamic) true, false false If enabled, commit messages that are broadcast around a cluster are done so synchronously. This is usually of little value since detecting a failure in broadcasting a commit means little else can be done except log a message, since some nodes in a cluster may have already committed and cannot rollback. syncRollbackPhase syncRollbackPhase (dynamic) true, false false If enabled, rollback messages that are broadcast around a cluster are done so synchronously. This is usually of little value since detecting a failure in broadcasting a rollback means little else can be done except log a message, since some nodes in a cluster may have already committed and cannot rollback.
The <literal><![CDATA[<jmxStatistics />]]></literal> Element The ]]> Element Description This element specifies whether cache statistics are gathered and reported via JMX. Parent ]]> Children Bean equivalent Configuration
<literal><![CDATA[<jmxStatistics />]]></literal> Attributes ]]> Attributes Attribute Bean Field Allowed Default Description enabled exposeManagementStatistics true, false true Controls whether cache statistics are gathered and exposed via JMX.
The <literal><![CDATA[<startup />]]></literal> Element The ]]> Element Description This element specifies behavior when the cache starts up. Parent ]]> Children Bean equivalent Configuration
<literal><![CDATA[<startup />]]></literal> Attributes ]]> Attributes Attribute Bean Field Allowed Default Description regionsInactiveOnStartup inactiveOnStartup true, false false If region-based marshalling is enabled, this attribute controls whether new regions created are inactive on startup.
The <literal><![CDATA[<shutdown />]]></literal> Element The ]]> Element Description This element specifies behavior when the cache shuts down. Parent ]]> Children Bean equivalent Configuration
<literal><![CDATA[<shutdown />]]></literal> Attributes ]]> Attributes Attribute Bean Field Allowed Default Description hookBehavior shutdownHookBehavior DEFAULT, DONT_REGISTER, REGISTER DEFAULT This attribute determines whether the cache registers a JVM shutdown hook so that it can clean up resources if the JVM is receives a shutdown signal. By default a shutdown hook is registered if no MBean server (apart from the JDK default) is detected. REGSTER forces the cache to register a shutdown hook even if an MBean server is detected, and DONT_REGISTER forces the cache NOT to register a shutdown hook, even if no MBean server is detected.
The <literal><![CDATA[<listeners />]]></literal> Element The ]]> Element Description This element specifies behavior of registered cache listeners. Parent ]]> Children Bean equivalent Configuration
<literal><![CDATA[<listeners />]]></literal> Attributes ]]> Attributes Attribute Bean Field Allowed Default Description asyncPoolSize listenerAsyncPoolSize integer 1 The size of the threadpool used to dispatch events to cache listeners that have registered as asynchronous listeners. If this number is less than 1, all asynchronous listeners will be treated as synchronous listeners and notified synchronously. asyncQueueSize listenerAsyncQueueSize positive integer 50000 The size of the bounded queue used by the async listener threadpool. Only considered if asyncPoolSize is greater than 0. Increase this if you see a lot of threads blocking trying to add events to this queue.
The <literal><![CDATA[<invocationBatching />]]></literal> Element The ]]> Element Description This element specifies behavior of invocation batching. Parent ]]> Children Bean equivalent Configuration
<literal><![CDATA[<invocationBatching />]]></literal> Attributes ]]> Attributes Attribute Bean Field Allowed Default Description enabled invocationBatchingEnabled true, false false Whether invocation batching is enabled or not. See the chapter on invocation batching for details.
The <literal><![CDATA[<serialization />]]></literal> Element The ]]> Element Description This element specifies behavior of object serialization in JBoss Cache. Parent ]]> Children Bean equivalent Configuration
<literal><![CDATA[<serialization />]]></literal> Attributes ]]> Attributes Attribute Bean Field Allowed Default Description marshallerClass marshallerClass A valid class that is available on the classpath VersionAwareMarshaller Specifies the marshaller to use when serializing and deserializing objects, either for replication or persistence. useLazyDeserialization useLazyDeserialization true, false false A mechanism by which serialization and deserialization of objects is deferred till the point in time in which they are used and needed. This typically means that any deserialization happens using the thread context class loader of the invocation that requires deserialization, and is an effective mechanism to provide classloader isolation. useRegionBasedMarshalling (deprecated) useRegionBasedMarshalling true, false false An older mechanism by which classloader isolation was achieved, by registering classloaders on specific regions. version replicationVersion Valid JBoss Cache version string Current version Used by the VersionAwareMarshaller in determining which version stream parser to use by default when initiating communications in a cluster. Useful when you need to run a newer version of JBoss Cache in a cluster containing older versions, and can be used to perform rolling upgrades. objectInputStreamPoolSize objectInputStreamPoolSize Positive integer 50 Not used at the moment. objectOutputStreamPoolSize objectOutputStreamPoolSize Positive integer 50 Not used at the moment.
The <literal><![CDATA[<eviction />]]></literal> Element The ]]> Element Description This element controls how eviction works in the cache. Parent ]]> Children ]]>, ]]> Bean equivalent EvictionConfig
<literal><![CDATA[<eviction />]]></literal> Attributes ]]> Attributes Attribute Bean Field Allowed Default Description wakeUpInterval wakeupInterval integer 5000 The frequency with which the eviction thread runs, in milliseconds. If set to less than 1, the eviction thread never runs and is effectively disabled.
The <literal><![CDATA[<default />]]></literal> Element The ]]> Element Description This element defines the default eviction region. Parent ]]> Children ]]> Bean equivalent EvictionRegionConfig
<literal><![CDATA[<default />]]></literal> Attributes ]]> Attributes Attribute Bean Field Allowed Default Description algorithmClass evictionAlgorithmConfig A valid class that is available on the classpath none This attribute needs to be specified if this tag is being used. Note that if being configured programmatically, the eviction algorithm's corresponding EvictionAlgorithmConfig file should be used instead. E.g., where you would use LRUAlgorithm in XML, you would use an instance of LRUAlgorithmConfig programmatically. actionPolicyClass evictionActionPolicyClassName A valid class that is available on the classpath DefaultEvictionActionPolicy The eviction action policy class, defining what happens when a node needs to be evicted. eventQueueSize eventQueueSize (dynamic integer 200000 The size of the bounded eviction event queue.
The <literal><![CDATA[<region />]]></literal> Element The ]]> Element Description This element defines an eviction region. Multiple instances of this tag can exist provided they have unique name attributes. Parent ]]> Children ]]> Bean equivalent EvictionRegionConfig
<literal><![CDATA[<region />]]></literal> Attributes ]]> Attributes Attribute Bean Field Allowed Default Description name regionFqn A String that could be parsed using Fqn.fromString() none This should be a unique name that defines this region. See the chapter on eviction for details of eviction regions. algorithmClass evictionAlgorithmConfig A valid class that is available on the classpath none This attribute needs to be specified if this tag is being used. Note that if being configured programmatically, the eviction algorithm's corresponding EvictionAlgorithmConfig file should be used instead. E.g., where you would use LRUAlgorithm in XML, you would use an instance of LRUAlgorithmConfig programmatically. actionPolicyClass evictionActionPolicyClassName A valid class that is available on the classpath DefaultEvictionActionPolicy The eviction action policy class, defining what happens when a node needs to be evicted. eventQueueSize eventQueueSize (dynamic integer 200000 The size of the bounded eviction event queue.
The <literal><![CDATA[<property />]]></literal> Element The ]]> Element Description A mechanism of passing in name-value properties to the enclosing configuration element. Parent ]]>, ]]>, ]]> Children Bean equivalent Either direct setters or setProperties() enclosing bean
<literal><![CDATA[<property />]]></literal> Attributes ]]> Attributes Attribute Bean Field Allowed Default Description name Either direct setters or setProperties() enclosing bean String none Property name value Either direct setters or setProperties() enclosing bean String none Property value
The <literal><![CDATA[<loaders />]]></literal> Element The ]]> Element Description Defines any cache loaders. Parent ]]> Children ]]>, ]]> Bean equivalent CacheLoaderConfig
<literal><![CDATA[<loaders />]]></literal> Attributes ]]> Attributes Attribute Bean Field Allowed Default Description passivation passivation true, false false If true, cache loaders are used in passivation mode. See the chapter on cache loaders for a detailed discussion on this. shared shared true, false false If true, cache loaders are used in shared mode. See the chapter on cache loaders for a detailed discussion on this.
The <literal><![CDATA[<preload />]]></literal> Element The ]]> Element Description Defines preloading of Fqn subtrees when a cache starts up. This element has no attributes. Parent ]]> Children ]]> Bean equivalent CacheLoaderConfig
The <literal><![CDATA[<node />]]></literal> Element The ]]> Element Description This element defines a subtree under which all content will be preloaded from the cache loaders when the cache starts. Multiple subtrees can be preloaded, although it only makes sense to define more than one subtree if they do not overlap. Parent ]]> Children Bean equivalent CacheLoaderConfig
<literal><![CDATA[<node />]]></literal> Attributes ]]> Attributes Attribute Bean Field Allowed Default Description fqn preload String none An Fqn to preload. This should be a String that can be parsed with Fqn.fromString(). When doing this programmatically, you should create a single String containing all of the Fqns you wish to preload, separated by spaces, and pass that into CacheLoaderConfig.setPreload().
The <literal><![CDATA[<loader />]]></literal> Element The ]]> Element Description This element defines a cache loader. Multiple elements may be used to create cache loader chains. Parent ]]> Children ]]>, ]]> Bean equivalent IndividualCacheLoaderConfig
<literal><![CDATA[<loader />]]></literal> Attributes ]]> Attributes Attribute Bean Field Allowed Default Description class className A valid class that is available on the classpath none A cache loader implementation to use. async async true, false false All modifications to this cache loader happen asynchronously, on a separate thread. fetchPersistentState fetchPersistentState true, false false When a cache starts up, retrieve persistent state from the cache loaders in other caches in the cluster. Only one loader element may set this to true. Also, only makes sense if the ]]> tag is present. purgeOnStartup purgeOnStartup true, false false Purges this cache loader when it starts up.
The <literal><![CDATA[<properties />]]></literal> Element The ]]> Element Description This element contains a set of properties that can be read by a java.util.Properties instance. This tag has no attributes, and the contents of this tag will be parsed by Properties.load(). Parent ]]>, ]]>, ]]> Children Bean equivalent IndividualCacheLoaderConfig.setProperties()
The <literal><![CDATA[<singletonStore />]]></literal> Element The ]]> Element Description This element configures the enclosing cache loader as a singleton store cache loader. Parent ]]> Children ]]> Bean equivalent SingletonStoreConfig
<literal><![CDATA[<singletonStore />]]></literal> Attributes ]]> Attributes Attribute Bean Field Allowed Default Description class className A valid class that is available on the classpath SingletonStoreCacheLoader A singleton store wrapper implementation to use. enabled enabled true, false false If true, the singleton store cache loader is enabled.
The <literal><![CDATA[<customInterceptors />]]></literal> Element The ]]> Element Description This element allows you to define custom interceptors for the cache. This tag has no attributes. Parent ]]> Children ]]> Bean equivalent None. At runtime, instantiate your own interceptor and pass it in to the cache using Cache.addInterceptor().
The <literal><![CDATA[<interceptor />]]></literal> Element The ]]> Element Description This element allows you configure a custom interceptor. This tag may appear multiple times. Parent ]]> Children ]]> Bean equivalent None. At runtime, instantiate your own interceptor and pass it in to the cache using Cache.addInterceptor().
<literal><![CDATA[<interceptor />]]></literal> Attributes ]]> Attributes Attribute Bean Field Allowed Default Description class - A valid class that is available on the classpath none An implementation of CommandInterceptor. position - first, last A position at which to place this interceptor in the chain. First is the first interceptor encountered when an invocation is made on the cache, last is the last interceptor before the call is passed on to the data structure. Note that this attribute is mutually exclusive with before, after and index. before - Fully qualified class name of an interceptor Will place the new interceptor directly before the instance of the named interceptor. Note that this attribute is mutually exclusive with position, after and index. after - Fully qualified class name of an interceptor Will place the new interceptor directly after the instance of the named interceptor. Note that this attribute is mutually exclusive with position, before and index. index - Positive integers A position at which to place this interceptor in the chain, with 0 being the first position. Note that this attribute is mutually exclusive with position, before and after.
The <literal><![CDATA[<clustering />]]></literal> Element The ]]> Element Description If this element is present, the cache is started in clustered mode. Attributes and child elements define clustering characteristics. Parent ]]> Children ]]>, ]]>, ]]>, ]]>, ]]> Bean equivalent Configuration
<literal><![CDATA[<clustering />]]></literal> Attributes ]]> Attributes Attribute Bean Field Allowed Default Description mode cacheMode replication, invalidation, r, i replication See the chapter on clustering for the differences between replication and invalidation. When using the bean, synchronous and asynchronous communication is combined with clustering mode to give you the enumberation Configuration.CacheMode. clusterName clusterName String JBossCache-cluster A cluster name which is used to identify the cluster to join.
The <literal><![CDATA[<sync />]]></literal> Element The ]]> Element Description If this element is present, all communications are synchronous, in that whenever a thread sends a message sent over the wire, it blocks until it receives an acknowledgement from the recipient. This element is mutually exclusive with the ]]> element. Parent ]]> Children Bean equivalent Configuration.setCacheMode()
<literal><![CDATA[<sync />]]></literal> Attributes ]]> Attributes Attribute Bean Field Allowed Default Description replTimeout syncReplTimeout (dynamic) positive integer 15000 This is the timeout used to wait for an acknowledgement when making a remote call, after which an exception is thrown.
The <literal><![CDATA[<async />]]></literal> Element The ]]> Element Description If this element is present, all communications are asynchronous, in that whenever a thread sends a message sent over the wire, it does not wait for an acknowledgement before returning. This element is mutually exclusive with the ]]> element. Parent ]]> Children Bean equivalent Configuration.setCacheMode()
<literal><![CDATA[<async />]]></literal> Attributes ]]> Attributes Attribute Bean Field Allowed Default Description serializationExecutorPoolSize serializationExecutorPoolSize positive integer 25 In addition to replication happening asynchronously, even serialization of contents for replication happens in a separate thread to allow the caller to return as quickly as possible. This setting controls the size of the serializer thread pool. Setting this to any value less than 1 means serialization does not happen asynchronously. serializationExecutorQueueSize serializationExecutorQueueSize positive integer 50000 This is used to define the size of the bounded queue that holds tasks for the serialization executor. This is ignored if a serialization executor is not used, such as when serializationExecutorPoolSize is less than 1. useReplQueue useReplQueue true, false false If true, this forces all async communications to be queued up and sent out periodically as a batch. replQueueInterval replQueueInterval positive integer 5000 If useReplQueue is set to true, this attribute controls how often the asynchronous thread used to flush the replication queue runs. This should be a positive integer which represents thread wakeup time in milliseconds. replQueueMaxElements replQueueMaxElements positive integer 1000 If useReplQueue is set to true, this attribute can be used to trigger flushing of the queue when it reaches a specific threshold.
The <literal><![CDATA[<stateRetrieval />]]></literal> Element The ]]> Element Description This tag controls ho state is retrieved from neighboring caches when this cache instance starts. Parent ]]> Children Bean equivalent Configuration
<literal><![CDATA[<stateRetrieval />]]></literal> Attributes ]]> Attributes Attribute Bean Field Allowed Default Description fetchInMemoryState fetchInMemoryState true, false true If true, this will cause the cache to ask neighboring caches for state when it starts up, so the cache starts "warm". timeout stateRetrievalTimeout positive integer 10000 This is the maximum amount of time - in milliseconds - to wait for state from neighboring caches, before throwing an exception and aborting startup. nonBlocking useNonBlockingStateTransfer true, false false This configuration switch enables the Non-Blocking State Transfer mechanism, new in 3.1.0. Note that this requires MVCC as a node locking scheme, and that STREAMING_STATE_TRANSFER is present in the JGroups stack used.
The <literal><![CDATA[<buddy />]]></literal> Element The ]]> Element Description If this tag is present, then state is not replicated across the entire cluster. Instead, buddy replication is used to select cache instances to maintain backups on. See this section on buddy replication for details. Note that this is only used if the clustering mode is replication, and not if it is invalidation. Parent ]]> Children ]]>, ]]>, Bean equivalent BuddyReplicationConfig
<literal><![CDATA[<buddy />]]></literal> Attributes ]]> Attributes Attribute Bean Field Allowed Default Description enabled enabled true, false false If true, buddy replication is enabled. communicationTimeout buddyCommunicationTimeout positive integer 10000 This is the maximum amount of time - in milliseconds - to wait for buddy group organization communications from buddy caches. poolName buddyPoolName String This is used as a means to identify cache instances and provide hints to the buddy selection algorithms. More information on the section on buddy replication.
The <literal><![CDATA[<dataGravitation />]]></literal> Element The ]]> Element Description This tag configures how data gravitation is conducted. See this section on buddy replication for details. Parent ]]> Children Bean equivalent BuddyReplicationConfig
<literal><![CDATA[<dataGravitation />]]></literal> Attributes ]]> Attributes Attribute Bean Field Allowed Default Description auto autoDataGravitation true, false true If true, when a get() is performed on a cache and nothing is found, a gravitation from neighboring caches is attempted. If this is false, then gravitations can only occur if the Option.setForceDataGravitation() option is provided. removeOnFind dataGravitationRemoveOnFind true, false true If true, when gravitation occurs, the instance that requests the gravitation takes ownership of the state and requests that all other instances remove the gravitated state from memory. searchBackupTrees dataGravitationSearchBackupTrees true, false true If true, incoming gravitation requests will cause the cache to search not just its primary data structure but its backup structure as well.
The <literal><![CDATA[<locator />]]></literal> Element The ]]> Element Description This tag provides a pluggable mechanism for providing buddy location algorithms. Parent ]]> Children ]]> Bean equivalent BuddyLocatorConfig
<literal><![CDATA[<locator />]]></literal> Attributes ]]> Attributes Attribute Bean Field Allowed Default Description class className A valid class that is available on the classpath NextMemberBuddyLocator A BuddyLocator implementation to use when selecting buddies from the cluster. Please refer to BuddyLocator javadocs for details.
The <literal><![CDATA[<jgroupsConfig />]]></literal> Element The ]]> Element Description This tag provides a configuration which is used with JGroups to create a network communication channel. Parent ]]> Children A series of elements representing JGroups protocols (see JGroups documentation). Note that there are no child elements if any of the element attributes are used instead. See section on attributes. Bean equivalent Configuration
<literal><![CDATA[<jgroupsConfig />]]></literal> Attributes ]]> Attributes Attribute Bean Field Allowed Default Description configFile clusterConfig A JGroups configuration file on the classpath udp.xml If this attribute is used, then any JGroups elements representing protocols within this tag are ignored. Instead, JGroups settings are read from the file specified. Note that this cannot be used with the multiplexerStack attribute. multiplexerStack muxStackName A valid multiplexer stack name that exists in the channel factory passed in to the RuntimeConfig This can only be used with the RuntimeConfig, where you pass in a JGroups ChannelFactory instance using RuntimeConfig.setMuxChannelFactory(). If this attribute is used, then any JGroups elements representing protocols within this tag are ignored. Instead, the JGroups channel is created using the factory passed in. Note that this cannot be used with the configFile attribute.
jbosscache-core-3.2.8.GA/src/main/docbook/userguide/en/modules/preface.xml0000644000175000017500000000623211111047320026245 0ustar moellermoeller Preface This is the official JBoss Cache Users' Guide. Along with its accompanying documents (an FAQ, a tutorial and a whole set of documents on POJO Cache), this is freely available on the JBoss Cache documentation website. 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 developers wishing to use JBoss Cache as either a standalone in-memory cache, a distributed or replicated cache, a clustering library, or an in-memory database. It is targeted at application developers who wish to use JBoss Cache in their code base, as well as "OEM" developers who wish to build on and extend JBoss Cache 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 programming is necessary. No prior knowledge of JBoss Application Server is expected or required. For further discussion, use the user forum available 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 JBoss Cache 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. I always appreciate feedback, suggestions and corrections, and these should be directed to the developer mailing list rather than direct emails to any of the authors. We hope you find this book useful, and wish you happy reading! Manik Surtani, October 2008 jbosscache-core-3.2.8.GA/src/main/docbook/userguide/en/modules/configuration.xml0000644000175000017500000003277211236775757027555 0ustar moellermoeller 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. The simplest example of a configuration XML file, a cache configured to run in LOCAL mode, looks like this: ]]> This file uses sensible defaults for isolation levels, lock acquisition timeouts, locking modes, etc. 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.
Validating Configuration Files By default JBoss Cache will validate your XML configuration file against an XML schema and throw an exception if the configuration is invalid. This can be overridden with the -Djbosscache.config.validate=false JVM parameter. Alternately, you could specify your own schema to validate against, using the -Djbosscache.config.schemaLocation=url parameter. By default though, configuration files are validated against the JBoss Cache configuration schema, which is included in the jbosscache-core.jar or on http://www.jboss.org/jbosscache/jbosscache-config-3.0.xsd. Most XML editing tools can be used with this schema to ensure the configuration file you create is correct and valid.
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. 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 such as Spring, Google Guice, JBoss Microcontainer, etc. 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: EvictionAlgorithmConfig : implementation-specific configuration object for the EvictionAlgorithm implementation being used. What configuration elements are exposed depends on the needs of the EvictionAlgorithm 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 force a write lock when reading data (when used in a transaction, this provides semantics similar to SELECT FOR UPDATE in a database) 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-3.2.8.GA/src/main/docbook/userguide/en/modules/eviction_policies.xml0000644000175000017500000005637711236260260030376 0ustar moellermoeller Eviction Eviction controls JBoss Cache's memory management by restricting how many nodes are allowed to be stored in memory, and for how long. Memory constraints on servers mean caches cannot grow indefinitely, so eviction needs to occur to prevent out of memory errors. Eviction is most often used alongside cache loaders.
Design Eviction in JBoss Cache is designed around four concepts: 1. Collecting statistics 2. Determining which nodes to evict 3. How nodes are evicted 4. Eviction threads. In addition, Regions play a key role in eviction, as eviction is always configured on a per-region basis so that different subtrees in the cache can have different eviction characteristics.
Collecting Statistics This is done on the caller's thread whenever anyone interacts with the cache. If eviction is enabled, an EvictionInterceptor is added to the interceptor chain and events are recorded in an event queue. Events are denoted by the EvictionEvent class. Event queues are held on specific Regions so each region has its own event queue. This aspect of eviction is not configurable, except that the EvictionInterceptor is either added to the interceptor chain or not, depending on whether eviction is enabled.
Determining Which Nodes to Evict An EvictionAlgorithm implementation processes the eviction queue to decide which nodes to evict. JBoss Cache ships with a number of implementations, including FIFOAlgorithm, LRUAlgorithm, LFUAlgorithm, etc. Each implementation has a corresponding EvictionAlgorithmConfig implementation with configuration details for the algorithm. Custom EvictionAlgorithm implementations can be provided by implementing the interface or extending one of the provided implementations. Algorithms are executed by calling its process() method and passing in the event queue to process. This is typically done by calling Region.processEvictionQueues(), which will locate the Algorithm assigned to the region.
How Nodes are Evicted Once the EvictionAlgorithm decides which nodes to evict, it uses an implementation of EvictionActionPolicy to determine how to evict nodes. This is configurable on a per-region basis, and defaults to DefaultEvictionActionPolicy, which invokes Cache.evict() for each node that needs to be evicted. JBoss Cache also ships with RemoveOnEvictActionPolicy, which calls Cache.removeNode() for each node that needs to be evicted, instead of Cache.evict(). Custom EvictionActionPolicy implementations can be used as well.
Eviction threads By default, a single cache-wide eviction thread is used to periodically iterate through registered regions and call Region.processEvictionQueues() on each region. The frequency with which this thread runs can be configured using the wakeUpInterval attribute in the eviction configuration element, and defaults to 5000 milliseconds if not specified. The eviction thread can be disabled by setting wakeUpInterval to 0. This can be useful if you have your own periodic maintenance thread running and would like to iterate through regions and call Region.processEvictionQueues() yourself.
Eviction Regions The concept of regions and the Region class were visited earlier when talking about marshalling. Regions are also used to define the eviction behavior for nodes within that region. In addition to using a region-specific configuration, you can also configure default, cache-wide eviction behavior 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 String objects. For each region, you can define eviction parameters. 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 node /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..
Configuring Eviction
Basic Configuration The basic eviction configuration element looks like: ... ]]> wakeUpInterval - this required parameter defines how often the eviction thread runs, in milliseconds. eventQueueSize - this optional parameter defines the size of the bounded queue which holds eviction events. If your eviction thread does not run often enough, you may find that the event queue fills up. It may then be necessary to get your eviction thread to run more frequently, or increase the size of your event queue. This configuration is just the default event queue size, and can be overridden in specific eviction regions. If not specified, this defaults to 200000. algorithmClass - this is required, unless you set individual algorithmClass attributes on each and every region. This defines the default eviction algorithm to use if one is not defined for a region. Algorithm configuration attributes - these are specific to the algorithm specified in algorithmClass. See the section specific to the algorithm you are interested in for details.
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 LRUAlgorithm but a different number of maxNodes:
Shipped Eviction Policies This section details the different algorithms shipped with JBoss Cache, and the various configuration parameters used for each algorithm.
LRUAlgorithm - Least Recently Used org.jboss.cache.eviction.LRUAlgorithm 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 immediate expiry, -1 denotes no limit. timeToLive - The amount of time a node is not written to or read (in milliseconds) before the node is swept away. 0 denotes immediate expiry, -1 denotes no limit. maxAge - Lifespan of a node (in milliseconds) regardless of idle time before the node is swept away. 0 denotes immediate expiry, -1 denotes no limit. minTimeToLive - 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.
FIFOAlgorithm - First In, First Out org.jboss.cache.eviction.FIFOAlgorithm 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 immediate expiry, -1 denotes no limit. minTimeToLive - 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.
MRUAlgorithm - Most Recently Used org.jboss.cache.eviction.MRUAlgorithm 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 immediate expiry, -1 denotes no limit. minTimeToLive - 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.
LFUAlgorithm - Least Frequently Used org.jboss.cache.eviction.LFUAlgorithm 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 visited, 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 immediate expiry, -1 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. minTimeToLive - 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.
ExpirationAlgorithm org.jboss.cache.eviction.ExpirationAlgorithm 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 immediate expiry, -1 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.
ElementSizeAlgorithm - Eviction based on number of key/value pairs in a node org.jboss.cache.eviction.ElementSizeAlgorithm 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 immediate expiry, -1 denotes no limit. maxElementsPerNode - This is the trigger number of attributes per node for the node to be selected for eviction. 0 denotes immediate expiry, -1 denotes no limit. minTimeToLive - 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.
jbosscache-core-3.2.8.GA/src/main/docbook/userguide/en/modules/transactions.xml0000644000175000017500000003325111111047320027351 0ustar moellermoeller 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 an innovative implementation of multi-versioned concurrency control (MVCC) as the default locking scheme. Versions of JBoss Cache prior to 3.x offered Optimistic and Pessimistic Locking schemes, both of which are now deprecated in favor of MVCC.
Multi-Version Concurrency Control (MVCC) MVCC is a locking scheme commonly used by modern database implementations to control fast, safe concurrent access to shared data.
MVCC Concepts MVCC is designed to provide the following features for concurrent access: Readers that don't block writers Writers that fail fast and achieves this by using data versioning and copying for concurrent writers. The theory is that readers continue reading shared state, while writers copy the shared state, increment a version id, and write that shared state back after verifying that the version is still valid (i.e., another concurrent writer has not changed this state first). This allows readers to continue reading while not preventing writers from writing, and repeatable read semantics are maintained by allowing readers to read off the old version of the state.
MVCC Implementation JBoss Cache's implementation of MVCC is based on a few features: Readers don't acquire any locks Only one additional version is maintained for shared state, for a single writer All writes happen sequentially, to provide fail-fast semantics The extremely high performance of JBoss Cache's MVCC implementation for reading threads is achieved by not requiring any synchronization or locking for readers. For each reader thread, the MVCCLockingInterceptor wraps state in a lightweight container object, which is placed in the thread's InvocationContext (or TransactionContext if running in a transaction). All subsequent operations on the state happens via the container object. This use of Java references allows for repeatable read semantics even if the actual state changes simultaneously. Writer threads, on the other hand, need to acquire a lock before any writing can commence. Currently, we use lock striping to improve the memory performance of the cache, and the size of the shared lock pool can be tuned using the concurrencyLevel attribute of the locking element. See the configuration reference for details. After acquiring an exclusive lock on an Fqn, the writer thread then wraps the state to be modified in a container as well, just like with reader threads, and then copies this state for writing. When copying, a reference to the original version is still maintained in the container (for rollbacks). Changes are then made to the copy and the copy is finally written to the data structure when the write completes. This way, subsequent readers see the new version while existing readers still hold a reference to the original version in their context. If a writer is unable to acquire the write lock after some time, a TimeoutException is thrown. This lock acquisition timeout defaults to 10000 millis and can be configured using the lockAcquisitionTimeout attribute of the locking element. See the configuration reference for details.
Isolation Levels JBoss Cache 3.x supports two isolation levels: REPEATABLE_READ and READ_COMMITTED, which correspond in semantic to database-style isolation levels. Previous versions of JBoss Cache supported all 5 database isolation levels, and if an unsupported isolation level is configured, it is either upgraded or downgraded to the closest supported level. REPEATABLE_READ is the default isolation level, to maintain compatibility with previous versions of JBoss Cache. READ_COMMITTED, while providing a slightly weaker isolation, has a significant performance benefit over REPEATABLE_READ.
Concurrent Writers and Write-Skews Although MVCC forces writers to obtain a write lock, a phenomenon known as write skews may occur when using REPEATABLE_READ: This happens when concurrent transactions performing a read and then a write, based on the value that was read. Since reads involve holding on to the reference to the state in the transaction context, a subsequent write would work off that original state read, which may now be stale. The default behavior with dealing with a write skew is to throw a DataVersioningException, when it is detected when copying state for writing. However, in most applications, a write skew may not be an issue (for example, if the state written has no relationship to the state originally read) and should be allowed. If your application does not care about write skews, you can allow them to happen by setting the writeSkewCheck configuration attribute to false. See the configuration reference for details. Note that write skews cannot happen when using READ_COMMITTED since threads always work off committed state.
Configuring Locking Configuring MVCC involves using the ]]> configuration tag, as follows: ]]> nodeLockingScheme - the node locking scheme used. Defaults to MVCC if not provided, deprecated schemes such as pessimistic or optimistic may be used but is not encouraged. isolationLevel - transaction isolation level. Defaults to REPEATABLE_READ if not provided. writeSkewCheck - defaults to true if not provided. concurrencyLevel - defaults to 500 if not provided. lockAcquisitionTimeout - only applies to writers when using MVCC. Defaults to 10000 if not provided.
Pessimistic and Optimistic Locking Schemes From JBoss Cache 3.x onwards, pessimistic and optimistic locking schemes are deprecated in favor of MVCC. It is recommended that existing applications move off these legacy locking schemes as support for them will eventually be dropped altogether in future releases. Documentation for legacy locking schemes are not included in this user guide, and if necessary, can be referenced in previous versions of this document, which can be found on the JBoss Cache website.
JTA Support JBoss Cache can be configured to use and participate in JTA compliant transactions. Alternatively, if transaction support is disabled, it is equivalent to using autocommit in JDBC calls, where modifications are potentially 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 for unit tests. Being a dummy, this is not recommended for production use a it has some severe limitations to do with concurrent transactions and recovery. 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 TransactionManager. When the transaction commits, we initiate either a one- two-phase commit protocol. See replicated caches and transactions for details.
jbosscache-core-3.2.8.GA/src/main/docbook/userguide/en/modules/compatibility.xml0000644000175000017500000000426311111047320027513 0ustar moellermoeller Version Compatibility and Interoperability
API compatibility 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 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. On the other hand, JBoss Cache 2.1.x will be API and binary compatible with 2.0.x. We have made best efforts, however, to keep JBoss Cache 3.x both binary and API compatible with 2.x. Still, it is recommended that client code is updated not to use deprecated methods, classes and configuration files.
Wire-level interoperability A configuration parameter, Configuration.setReplicationVersion(), 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 Application Server.
jbosscache-core-3.2.8.GA/src/main/docbook/userguide/en/modules/replication.xml0000644000175000017500000007746111160736605027204 0ustar moellermoeller Cache Modes and 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. 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 batches), or at the end of a transaction or batch. 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. A replication queue can therefore offer much higher performance as the actual replication is performed by a background thread. 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 minimizing 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 optimize how it chooses buddies, where it stores data, and minimizes 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 co-located 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 See the configuration reference section for details on configuring buddy replication.
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 minimized 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 batches), or at the end of a transaction or batch, upon successful commit. This is usually more efficient as invalidation messages can be optimized 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 its 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 ]]> configuration element 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.
Non-Blocking State Transfer New in JBoss Cache 3.1.0, Non-Blocking State Transfer (NBST) allows senders to generate and stream state while not stopping handling their "work as usual" transactions. This is particularly important if there is a large volume of state, where generation and streaming of the state can take some time and can cause ongoing transactions on the sender to time out and fail. To achieve this, NBST should be enabled (see configuration reference), and you need to be using MVCC as a node locking scheme. In addition, you need to use JGroups' STREAMING_STATE_TRANSFER protocol in your cluster properties.
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-3.2.8.GA/src/main/docbook/userguide/en/modules/basic_api.xml0000644000175000017500000006711511111047320026561 0ustar moellermoeller 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. The cache organizes data into a tree structure, made up of nodes. 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. Note that the diagram above only depicts some of the more popular API methods. 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. The CacheFactory provides a number of overloaded methods for creating a Cache, but they all fundamentally 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, 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. Here is an example of the simplest mechanism for creating and starting a cache, using the default configuration values: In this example, we tell the CacheFactory to find and parse a configuration file on the classpath: In this example, 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, lets 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, for convenience:
Organizing Your Data and Using the Node Structure A Node should be viewed as a named logical grouping of data. A node should be used to contain data for a single data record, for example information about a particular person or account. It should be kept in mind that all aspects of the cache - locking, cache loading, replication and eviction - happen on a per-node basis. As such, anything grouped together by being stored in a single node will be treated as a single atomic unit.
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 Strings 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 factory methods; see the javadoc for all the possibilities. The following illustrates the most commonly used approaches to creating an Fqn: Note that is the same as
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 network sockets and maintenance threads 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. 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 cache mode affects behavior. See the chapter on Configuration for info on how to configure things like cache 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 . @NodeInvalidated - methods annotated such receive a notification when a node is evicted from memory due to a remote invalidation event. Methods need to accept a parameter type which is assignable from NodeInvalidatedEvent . @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 . @BuddyGroupChanged - methods annotated such receive a notification when a node changes its buddy group, perhaps due to a buddy falling out of the cluster or a newer, closer buddy joining. Methods need to accept a parameter type which is assignable from BuddyGroupChangedEvent. 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:
Synchronous and Asynchronous Notifications By default, all notifications are synchronous, in that they happen on the thread of the caller which generated the event. As such, it is good practise to ensure cache listener implementations don't hold up the thread in long-running tasks. Alternatively, you could set the CacheListener.sync() attribute to false, in which case you will not be notified in the caller's thread. See the configuration reference on tuning this thread pool and size of blocking queue.
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 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. org.jboss.cache.loader.ClusteredCacheLoader - used as a "read-only" cache loader, where other nodes in the cluster are queried for state. Useful when full state transfer is too expensive and it is preferred that state is lazily loaded. These cache loaders, along with advanced aspects and tuning issues, are discussed in the chapter dedicated to cache loaders.
Using Eviction Policies Eviction policies are the counterpart to cache loaders. They are necessary to make sure the cache does not run out of memory and when the cache starts to fill, an eviction algorithm running in a separate thread evicts in-memory state and frees up memory. If configured with a cache loader, the state can then be retrieved from the cache loader if needed. 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-3.2.8.GA/src/main/docbook/userguide/en/modules/cache_loaders.xml0000644000175000017500000021155411236523520027432 0ustar moellermoeller 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 fulfill.
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. cache.jdbc.driver=com.mysql.jdbc.Driver cache.jdbc.url=jdbc:mysql://localhost:3306/jbossdb cache.jdbc.user=root cache.jdbc.password= ]]> 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 cache.jdbc.datasource=java:/DefaultDS pushStateWhenCoordinator=true pushStateWhenCoordinatorTimeout=20000 ]]> 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 loader 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 behavior 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 utilize 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 ). 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 its 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 its 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 Fqns 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 this wiki page 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. 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 ]]> As an alternative to configuring the entire JDBC connection, the name of an existing data source can be given: cache.jdbc.datasource=java:/DefaultDS ]]> Cconfiguration example for a cache loader using c3p0 JDBC connection pooling: 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.connection.factory=org.jboss.cache.loader.C3p0ConnectionFactory c3p0.maxPoolSize=20 c3p0.checkoutTimeout=5000 ]]>
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 isjboss-cache. cache.s3.callingFormat - One ofPATH,SUBDOMAIN, or VANITY. Read the S3 documentation on the use of calling domains. The default value isSUBDOMAIN. cache.s3.optimize - The default isfalse. 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 istrue. 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 retrieval latency. Set to EU to store data in Europe. The default isnull, 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: 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 across the network. Such change is trivial for replication purposes 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) Memory: /A Disk: /A 2) Memory: /A, /B Disk: /A, /B 3) Memory: /B Disk: /A, /B 4) Memory: /A, /B Disk: /A, /B 5) Memory: /A Disk: /A, /B 6) Memory: /A Disk: /A When passivation is enabled: 1) Memory: /A Disk: 2) Memory: /A, /B Disk: 3) Memory: /B Disk: /A 4) Memory: /A, /B Disk: 5) Memory: /A Disk: /B 6) Memory: /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, co-located 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-3.2.8.GA/src/main/docbook/userguide/en/modules/jmx_reference.xml0000644000175000017500000003602111111047320027453 0ustar moellermoeller JMX References
JBoss Cache Statistics There is a whole wealth of information being gathered and exposed on to JMX for monitoring the cache. Some of these are detailed below: JBoss Cache JMX MBeans MBean Attribute/Operation Name Description DataContainerImpl getNumberOfAttributes() Returns the number of attributes in all nodes in the data container getNumberOfNodes() Returns the number of nodes in the data container printDetails() Prints details of the data container RPCManagerImpl localAddressString String representation of the local address membersString String representation of the cluster view statisticsEnabled Whether RPC statistics are being gathered replicationCount Number of successful replications replicationFailures Number of failed replications successRatio RPC call success ratio RegionManagerImpl dumpRegions() Dumps a String representation of all registered regions, including eviction regions depicting their event queue sizes numRegions Number of registered regions BuddyManager buddyGroup A String representation of the cache's buddy group buddyGroupsIParticipateIn String representations of all buddy groups the cache participates in TransactionTable numberOfRegisteredTransactions The number of registered, ongoing transactions transactionMap A String representation of all currently registered transactions mapped to internal GlobalTransaction instances MVCCLockManager concurrencyLevel The configured concurrency level numberOfLocksAvailable Number of locks in the shared lock pool that are not used numberOfLocksHeld Number of locks in the shared lock pool that are in use testHashing(String fqn) Tests the spreading of locks across Fqns. For a given (String based) Fqn, this method returns the index in the lock array that it maps to. ActivationInterceptor Activations Number of passivated nodes that have been activated. CacheLoaderInterceptor CacheLoaderLoads Number of nodes loaded through a cache loader. CacheLoaderMisses Number of unsuccessful attempts to load a node through a cache loader. CacheMgmtInterceptor Hits Number of successful attribute retrievals. Misses Number of unsuccessful attribute retrievals. Stores Number of attribute store operations. Evictions Number of node evictions. NumberOfAttributes Number of attributes currently cached. NumberOfNodes Number of nodes currently cached. ElapsedTime Number of seconds that the cache has been running. TimeSinceReset Number of seconds since the cache statistics have been reset. AverageReadTime Average time in milliseconds to retrieve a cache attribute, including unsuccessful attribute retrievals. AverageWriteTime Average time in milliseconds to write a cache attribute. HitMissRatio 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. ReadWriteRatio Ratio of read operations to write operations. This is the ratio of cache hits and misses to cache stores. CacheStoreInterceptor CacheLoaderStores Number of nodes written to the cache loader. InvalidationInterceptor Invalidations Number of cached nodes that have been invalidated. PassivationInterceptor Passivations Number of cached nodes that have been passivated. TxInterceptor Prepares Number of transaction prepare operations performed by this interceptor. Commits Number of transaction commit operations performed by this interceptor. Rollbacks Number of transaction rollbacks operations performed by this interceptor. numberOfSyncsRegistered Number of synchronizations registered with the transaction manager pending completion and removal.
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, boolean: isPre, boolean: isOriginLocal @NodeCreated org.jboss.cache.NodeEvicted String: fqn, boolean: isPre, boolean: isOriginLocal @NodeEvicted org.jboss.cache.NodeLoaded String: fqn, boolean: isPre @NodeLoaded org.jboss.cache.NodeModifed String: fqn, boolean: isPre, boolean: isOriginLocal @NodeModifed org.jboss.cache.NodeRemoved String: fqn, boolean: isPre, boolean: isOriginLocal @NodeRemoved org.jboss.cache.NodeVisited String: fqn, boolean: isPre @NodeVisited org.jboss.cache.ViewChanged String: view @ViewChanged org.jboss.cache.NodeActivated String: fqn @NodeActivated org.jboss.cache.NodeMoved String: fromFqn, String: toFqn, boolean: isPre @NodeMoved org.jboss.cache.NodePassivated String: fqn @NodePassivated
jbosscache-core-3.2.8.GA/src/main/docbook/userguide/en/modules/batching.xml0000644000175000017500000000424511111047320026421 0ustar moellermoeller Batching API
Introduction The batching API, introduced in JBoss Cache 3.x, is intended as a mechanism to batch the way calls are replicated independent of JTA transactions. This is useful when you want to batch up replication calls within a scope finer than that of any ongoing JTA transactions.
Configuring batching To use batching, you need to enable invocation batching in your cache configuration, either on the Configuration object: or in your XML file: ]]> By default, invocation batching is disabled. Note that you do not have to have a transaction manager defined to use batching.
Batching API Once you have configured your cache to use batching, you use it by calling startBatch() and endBatch() on Cache. E.g.,
jbosscache-core-3.2.8.GA/src/main/docbook/userguide/en/master.xml0000644000175000017500000000727511236523520024504 0ustar moellermoeller ]> JBoss Cache Users' Guide A clustered, transactional cache Release 3.2.0 Malagueta August 2009 Manik Surtani manik AT jboss DOT org Brian Stansberry brian DOT stansberry AT jboss DOT com Galder Zamarreño galder DOT zamarreno AT jboss DOT com Mircea Markus mircea DOT markus AT jboss DOT com 2004 2005 2006 2007 2008 2009 JBoss, a division of Red Hat Inc., and all authors as named. &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; &batching; &deployment; &compatibility; JBoss Cache Architecture This section digs deeper into the JBoss Cache architecture, and is meant for developers wishing to use the more advanced cache features,extend or enhance the 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-3.2.8.GA/src/main/release/0000755000175000017500000000000011675221653020067 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/release/README-libs.txt0000644000175000017500000000343611111047320022500 0ustar moellermoellerJARs included in the distribution 1) REQUIRED JARs ---------------- The following JARs are REQUIRED for the proper operation of JBoss Cache, in addition to the jbosscache-core.jar file: * commons-logging.jar - Used for logging. * jcip-annotations.jar - (http://jcip.net) - Annotations used to assert concurrency behaviour of internal classes. * jgroups.jar (http://jgroups.com) - Group communications library that is the backbone of JBoss Cache's replication. Necessary even when the cache is run in LOCAL mode. * jboss-common-core.jar - JBoss utilities used by JBoss Cache. Version 2.0.5.GA or above needed if run with JDK 6. * jboss-logging-spi.jar - Required by jboss-common-core. * jta.jar - JTA interfaces. Not needed if these are provided elsewhere, e.g., an application server. 2) OPTIONAL JARs ---------------- The following JARs are OPTIONAL, based on the use of certain cache features: * je.jar - (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 Oracle. * c3p0.jar - (http://sourceforge.net/projects/c3p0) - 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. * jdbm.jar - (http://jdbm.sourceforge.net/) - A filesystem-based database similar to BerkeleyDB. Used by the JdbmCacheLoader. * amazon-s3.jar - LGPL licensed utilities for communicating with the Amazon S3 service. Used by the S3CacheLoader. * commons-httpclientjar - Required by amazon-s3. * commons-codec.jar - Required by amazon-s3. jbosscache-core-3.2.8.GA/src/main/release/licenses/0000755000175000017500000000000011675221652021673 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/release/licenses/README-bdbje-lib.txt0000644000175000017500000000201711151232130025157 0ustar moellermoeller $Id: README-bdbje-lib.txt 7784 2009-02-25 12:06:48Z manik.surtani@jboss.com $ NAME: Sleepycat Berkeley DB Java Edition 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-bdbje-lib.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-3.2.8.GA/src/main/release/licenses/LICENSE-bdbje-lib.txt0000644000175000017500000000402011111047320025301 0ustar moellermoeller/* * 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-3.2.8.GA/src/main/release/licenses/cpl-1.0.txt0000755000175000017500000002677611111047320023512 0ustar moellermoellerCommon 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-3.2.8.GA/src/main/release/licenses/README.txt0000644000175000017500000000063011111047320023347 0ustar moellermoellerThis 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-3.2.8.GA/src/main/release/licenses/apache-1.1.txt0000755000175000017500000000541311111047320024137 0ustar moellermoeller/* ==================================================================== * 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-3.2.8.GA/src/main/release/licenses/apache-2.0.txt0000755000175000017500000002613611111047320024144 0ustar moellermoeller 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-3.2.8.GA/src/main/release/Copyright.txt0000644000175000017500000000367511111047320022571 0ustar moellermoellerJBoss (R)* Cache is Copyright 2000-2008, Red Hat Middleware LLC and individual contributors, and is licensed under the GNU LGPL. A summary of the individual contributors is given below. Any omission should be sent to msurtani@redhat.com. CVS/SVN Login(s) Name ------------------------------------------------------------------------------- bdueck Brian Dueck bela, bela@jboss.com, belaban Bela Ban bstansberry, bstansberry@jboss.com Brian Stansberry bwang, bwang00 Ben Wang csuconic Clebert Suconic dhuang Daniel Huang dpospisi@redhat.com Dominik Pospisil gzamarreno, galder.zamarreno@jboss.com Galder Zamarreno genman Elias Ross hmesha Hany Mesha jgreene, jason.greene@jboss.com Jason T. Greene jiwils, jawilson Jimmy Wilson jerrygauth Jerry Gauthier msurtani, manik.surtani@jboss.com Manik Surtani mmarkus, mircea.markus Mircea Markus navssurtani Navin Surtani ovidiu Ovidiu Feodorov pgier Paul Gier rloehr Ruel Loehr rrajasekaran Rajesh Rajasekeran smarlow Scott Marlow starksm Scott Stark tom.benninger@jboss.com Tom Benninger twundke Tom Wundke vblagojevic, vblagojevic@jboss.com Vladimir Blagojevic ------------------------------------------------------------------------------- * JBoss is a registered trademark of Red Hat Middleware LLC. jbosscache-core-3.2.8.GA/src/main/release/LICENSE-lgpl-2.1.txt0000644000175000017500000006350411111047320023132 0ustar moellermoeller 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-3.2.8.GA/src/main/release/README.txt0000644000175000017500000000243011111047320021542 0ustar moellermoellerJBossCache -- a replicated, transactional, and fine-grained cache system ======================================================================== Note: - We offer different packaging for download: + jbosscache-core-3.x.y-bin, just the core jar file and dependencies. + jbosscache-core-3.x.y-all, everything including docs, demos and tutorials, no sources though. + jbosscache-core-3.x.y-doc, just the documentation. + jbosscache-core-3.x.y-src, a zipped up snapshot of the project root directory in subversion, which includes all sources, tests and build scripts. - If you are looking for JBoss Cache POJO edition, this is a separate distribution: + jbosscache-pojo-3.x.y-bin, just the core jar file and dependencies. Note that this will include jbosscache-core.jar as a dependency. + jbosscache-pojo-3.x.y-all, everything including docs, sources and tests. + jbosscache-pojo-3.x.y-doc, just the documentation. Requirements: - JDK 5.0 and up Running the tutorial: - Please read the tutorial documentation (in the jbosscache-core-3.x.y-all distribution) 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 (https://lists.jboss.org/mailman/listinfo/jbosscache-dev) jbosscache-core-3.2.8.GA/src/main/release/README-Demo.txt0000644000175000017500000000042411111047320022425 0ustar moellermoellerJBoss Cache GUI Demo -------------------- JBoss Cache comes with a GUI demo, to visually demonstrate state being moved around a cluster. The demo is available for download on http://sourceforge.net/project/showfiles.php?group_id=22866&package_id=102339 - Manik Surtani jbosscache-core-3.2.8.GA/src/main/java/0000755000175000017500000000000011675221653017370 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/0000755000175000017500000000000011675221653020157 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/0000755000175000017500000000000011675221654021300 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/0000755000175000017500000000000011675222134022335 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/factories/0000755000175000017500000000000011675221666024325 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/factories/CommandsMetaFactory.java0000644000175000017500000000431411111047320031044 0ustar moellermoeller/* * 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.factories; import org.jboss.cache.commands.CommandsFactory; import org.jboss.cache.commands.CommandsFactoryImpl; import org.jboss.cache.commands.OptimisticCommandsFactoryImpl; import org.jboss.cache.commands.PessimisticCommandsFactoryImpl; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.factories.annotations.DefaultFactoryFor; /** * COnstructs commands factory * * @author Manik Surtani (
manik AT jboss DOT org) * @since 3.0 */ @DefaultFactoryFor(classes = CommandsFactory.class) public class CommandsMetaFactory extends ComponentFactory { @SuppressWarnings("deprecation") protected T construct(Class componentType) { switch (configuration.getNodeLockingScheme()) { case MVCC: return componentType.cast(new CommandsFactoryImpl()); case OPTIMISTIC: return componentType.cast(new OptimisticCommandsFactoryImpl()); case PESSIMISTIC: return componentType.cast(new PessimisticCommandsFactoryImpl()); } throw new ConfigurationException("Unknown locking scheme " + configuration.getNodeLockingScheme()); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/factories/context/0000755000175000017500000000000011675221660026003 5ustar moellermoeller././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/factories/context/OptimisticContextFactory.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/factories/context/OptimisticContextFactory.ja0000644000175000017500000000351411111047320033323 0ustar moellermoeller/* * 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.factories.context; import org.jboss.cache.transaction.OptimisticTransactionContext; import org.jboss.cache.transaction.TransactionContext; import javax.transaction.RollbackException; import javax.transaction.SystemException; import javax.transaction.Transaction; /** * Constructs contexts for optimistic locking * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 * @deprecated will be removed when optimistic locking is removed. */ @Deprecated @SuppressWarnings("deprecation") public class OptimisticContextFactory extends PessimisticContextFactory { @Override public TransactionContext createTransactionContext(Transaction tx) throws SystemException, RollbackException { return new OptimisticTransactionContext(tx); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/factories/context/MVCCContextFactory.java0000644000175000017500000000357511111047320032265 0ustar moellermoeller/* * 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.factories.context; import org.jboss.cache.InvocationContext; import org.jboss.cache.invocation.MVCCInvocationContext; import org.jboss.cache.transaction.MVCCTransactionContext; import org.jboss.cache.transaction.TransactionContext; import javax.transaction.RollbackException; import javax.transaction.SystemException; import javax.transaction.Transaction; /** * Constructs contexts for MVCC locking * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ public class MVCCContextFactory implements ContextFactory { public InvocationContext createInvocationContext() { return new MVCCInvocationContext(); } public TransactionContext createTransactionContext(Transaction tx) throws SystemException, RollbackException { return new MVCCTransactionContext(tx); } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/factories/context/PessimisticContextFactory.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/factories/context/PessimisticContextFactory.j0000644000175000017500000000436611111047320033340 0ustar moellermoeller/* * 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.factories.context; import org.jboss.cache.DataContainer; import org.jboss.cache.InvocationContext; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.invocation.LegacyInvocationContext; import org.jboss.cache.transaction.PessimisticTransactionContext; import org.jboss.cache.transaction.TransactionContext; import javax.transaction.RollbackException; import javax.transaction.SystemException; import javax.transaction.Transaction; /** * Constructs contexts for pessimistic locking * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 * @deprecated will be removed when pessimistic locking is removed. */ @Deprecated @SuppressWarnings("deprecation") public class PessimisticContextFactory implements ContextFactory { DataContainer container; @Inject public void inject(DataContainer container) { this.container = container; } public InvocationContext createInvocationContext() { return new LegacyInvocationContext(container); } public TransactionContext createTransactionContext(Transaction tx) throws SystemException, RollbackException { return new PessimisticTransactionContext(tx); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/factories/context/ContextFactory.java0000644000175000017500000000422511111047320031605 0ustar moellermoeller/* * 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.factories.context; import org.jboss.cache.InvocationContext; import org.jboss.cache.transaction.TransactionContext; import javax.transaction.RollbackException; import javax.transaction.SystemException; import javax.transaction.Transaction; /** * This is the factory responsible for creating {@link org.jboss.cache.InvocationContext}s and {@link org.jboss.cache.transaction.TransactionContext}s * for requests, based on the configuration used. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ public interface ContextFactory { /** * @return a new invocation context */ InvocationContext createInvocationContext(); /** * @param tx JTA transaction to associate the new context with * @return a new transaction context * @throws javax.transaction.RollbackException * in the event of an invalid transaaction * @throws javax.transaction.SystemException * in the event of an invalid transaction */ TransactionContext createTransactionContext(Transaction tx) throws SystemException, RollbackException; } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/factories/BootstrapFactory.java0000644000175000017500000000436211111047320030454 0ustar moellermoeller/* * 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.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; /** * Factory for setting up bootstrap components * * @author Manik Surtani (manik AT jboss DOT 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-3.2.8.GA/src/main/java/org/jboss/cache/factories/RegionManagerFactory.java0000644000175000017500000000351111111047320031210 0ustar moellermoeller/* * 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.factories; import org.jboss.cache.LegacyRegionManagerImpl; import org.jboss.cache.RegionManager; import org.jboss.cache.RegionManagerImpl; import org.jboss.cache.factories.annotations.DefaultFactoryFor; /** * Creates region managers * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ @DefaultFactoryFor(classes = RegionManager.class) public class RegionManagerFactory extends ComponentFactory { @SuppressWarnings("deprecation") protected T construct(Class componentType) { switch (configuration.getNodeLockingScheme()) { case MVCC: return componentType.cast(new RegionManagerImpl()); default: return componentType.cast(new LegacyRegionManagerImpl()); } } }jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/factories/ReplicationQueueFactory.java0000644000175000017500000000354411111047320031756 0ustar moellermoeller/* * 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.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 AT jboss DOT 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-3.2.8.GA/src/main/java/org/jboss/cache/factories/TransactionManagerFactory.java0000644000175000017500000000643311111047320032260 0ustar moellermoeller/* * 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.factories; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.factories.annotations.DefaultFactoryFor; import org.jboss.cache.transaction.BatchModeTransactionManager; import org.jboss.cache.transaction.TransactionManagerLookup; import javax.transaction.TransactionManager; /** * Uses a number of mechanisms to retrieve a transaction manager. * * @author Manik Surtani (manik AT jboss DOT org) * @since 2.1.0 */ @DefaultFactoryFor(classes = {TransactionManager.class}) public class TransactionManagerFactory extends ComponentFactory { 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); } } if (transactionManager == null && configuration.isInvocationBatchingEnabled()) { log.info("Using a batchMode transaction manager"); transactionManager = BatchModeTransactionManager.getInstance(); } return componentType.cast(transactionManager); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/factories/StateTransferManagerFactory.java0000644000175000017500000000373411111047320032561 0ustar moellermoeller/* * 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.factories; import org.jboss.cache.factories.annotations.DefaultFactoryFor; import org.jboss.cache.statetransfer.DefaultStateTransferManager; import org.jboss.cache.statetransfer.LegacyStateTransferManager; import org.jboss.cache.statetransfer.StateTransferManager; /** * Constructs {@link org.jboss.cache.statetransfer.StateTransferManager} instances. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ @DefaultFactoryFor(classes = StateTransferManager.class) public class StateTransferManagerFactory extends ComponentFactory { @SuppressWarnings("deprecation") protected T construct(Class componentType) { switch (configuration.getNodeLockingScheme()) { case MVCC: return componentType.cast(new DefaultStateTransferManager()); default: return componentType.cast(new LegacyStateTransferManager()); } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/factories/jgroups/0000755000175000017500000000000011675221661026011 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/factories/CacheConfigsXmlParser.java0000644000175000017500000000272411111047320031321 0ustar moellermoeller/* * 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.factories; import org.jboss.cache.annotations.Compat; /** * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 * @deprecated here for compatibility reasons. Use the class with the same name in the org.jboss.cache.config.parsing package. */ @Compat @Deprecated public class CacheConfigsXmlParser extends org.jboss.cache.config.parsing.CacheConfigsXmlParser { } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/factories/LockManagerFactory.java0000644000175000017500000000427511111047320030665 0ustar moellermoeller/* * 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.factories; import org.jboss.cache.factories.annotations.DefaultFactoryFor; import org.jboss.cache.lock.LockManager; import org.jboss.cache.lock.MVCCLockManager; import org.jboss.cache.lock.NodeBasedLockManager; import org.jboss.cache.lock.PessimisticNodeBasedLockManager; /** * Creates lock managers * * @author Manik Surtani (manik AT jboss DOT org) * @since 2.2.0 */ @DefaultFactoryFor(classes = LockManager.class) public class LockManagerFactory extends EmptyConstructorFactory { @Override @SuppressWarnings("deprecation") protected T construct(Class componentType) { if (log.isTraceEnabled()) log.trace("Cache configuration is " + configuration.getNodeLockingScheme()); switch (configuration.getNodeLockingScheme()) { case MVCC: return componentType.cast(super.construct(MVCCLockManager.class)); case OPTIMISTIC: return componentType.cast(super.construct(NodeBasedLockManager.class)); case PESSIMISTIC: default: return componentType.cast(super.construct(PessimisticNodeBasedLockManager.class)); } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/factories/annotations/0000755000175000017500000000000011675221663026657 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/factories/annotations/DefaultFactoryFor.java0000644000175000017500000000352711111047320033071 0ustar moellermoeller/* * 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.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 AT jboss DOT 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-3.2.8.GA/src/main/java/org/jboss/cache/factories/annotations/Stop.java0000644000175000017500000000353611111047320030433 0ustar moellermoeller/* * 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.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 AT jboss DOT 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-3.2.8.GA/src/main/java/org/jboss/cache/factories/annotations/Destroy.java0000644000175000017500000000355011111047320031133 0ustar moellermoeller/* * 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.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 AT jboss DOT 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-3.2.8.GA/src/main/java/org/jboss/cache/factories/annotations/NonVolatile.java0000644000175000017500000000415411111047320031735 0ustar moellermoeller/* * 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.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. Components such as the {@link org.jboss.cache.lock.LockManager}, though, should * never be marked as @NonVolatile since based on the configuration, different lock manager implementations * may be selected. LockManager is, hence, not resilient to changes in the configuration. * * @author Manik Surtani (manik AT jboss DOT 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-3.2.8.GA/src/main/java/org/jboss/cache/factories/annotations/Inject.java0000644000175000017500000000515311111047320030717 0ustar moellermoeller/* * 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.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.ComponentRegistry#wireDependencies(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.
 * 
* * @author Manik Surtani * @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-3.2.8.GA/src/main/java/org/jboss/cache/factories/annotations/Start.java0000644000175000017500000000354111111047320030577 0ustar moellermoeller/* * 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.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 AT jboss DOT 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-3.2.8.GA/src/main/java/org/jboss/cache/factories/StateTransferFactory.java0000644000175000017500000000541611111047320031265 0ustar moellermoeller/* * 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.factories; import org.jboss.cache.factories.annotations.DefaultFactoryFor; import org.jboss.cache.statetransfer.DefaultStateTransferGenerator; import org.jboss.cache.statetransfer.DefaultStateTransferIntegrator; import org.jboss.cache.statetransfer.LegacyStateTransferGenerator; import org.jboss.cache.statetransfer.LegacyStateTransferIntegrator; import org.jboss.cache.statetransfer.StateTransferGenerator; import org.jboss.cache.statetransfer.StateTransferIntegrator; /** * Factory class able to create {@link org.jboss.cache.statetransfer.StateTransferGenerator} and * {@link org.jboss.cache.statetransfer.StateTransferIntegrator} instances. *

* Updated in 3.0.0 to extend ComponentFactory, etc. *

* * @author Brian Stansberry * @author Manik Surtani * @version $Revision: 7168 $ */ @DefaultFactoryFor(classes = {StateTransferGenerator.class, StateTransferIntegrator.class}) public class StateTransferFactory extends ComponentFactory { @SuppressWarnings("deprecation") protected T construct(Class componentType) { if (componentType.equals(StateTransferIntegrator.class)) { switch (configuration.getNodeLockingScheme()) { case MVCC: return componentType.cast(new DefaultStateTransferIntegrator()); default: return componentType.cast(new LegacyStateTransferIntegrator()); } } else { switch (configuration.getNodeLockingScheme()) { case MVCC: return componentType.cast(new DefaultStateTransferGenerator()); default: return componentType.cast(new LegacyStateTransferGenerator()); } } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/factories/TransactionLogFactory.java0000644000175000017500000000302011142425313031422 0ustar moellermoeller/* * 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.factories; import org.jboss.cache.factories.annotations.DefaultFactoryFor; import org.jboss.cache.transaction.TransactionLog; /** * Constructs {@link org.jboss.cache.transaction.TransactionLog} instances. * * @author Jason T. Greene * @since 3.0 */ @DefaultFactoryFor(classes = TransactionLog.class) public class TransactionLogFactory extends ComponentFactory { protected T construct(Class componentType) { return componentType.cast(new TransactionLog()); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/factories/RuntimeConfigAwareFactory.java0000644000175000017500000000436111111047320032227 0ustar moellermoeller/* * 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.factories; 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 AT jboss DOT org) * @since 2.1.0 */ @DefaultFactoryFor(classes = RPCManager.class) public class RuntimeConfigAwareFactory extends EmptyConstructorFactory { @Override 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-3.2.8.GA/src/main/java/org/jboss/cache/factories/ContextMetaFactory.java0000644000175000017500000000523511111047320030732 0ustar moellermoeller/* * 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.factories; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.factories.annotations.DefaultFactoryFor; import org.jboss.cache.factories.context.ContextFactory; import org.jboss.cache.factories.context.MVCCContextFactory; import org.jboss.cache.factories.context.OptimisticContextFactory; import org.jboss.cache.factories.context.PessimisticContextFactory; /** * Responsible for creating the appropriate {@link org.jboss.cache.factories.context.ContextFactory} based on configuration used. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ @DefaultFactoryFor(classes = ContextFactory.class) public class ContextMetaFactory extends ComponentFactory { @SuppressWarnings("deprecation") protected T construct(Class componentType) { if (log.isTraceEnabled()) log.trace("Cache configuration is " + configuration.getNodeLockingScheme()); switch (configuration.getNodeLockingScheme()) { case MVCC: if (log.isTraceEnabled()) log.trace("Creating an MVCC context factory"); return componentType.cast(new MVCCContextFactory()); case OPTIMISTIC: if (log.isTraceEnabled()) log.trace("Creating an optimistic context factory"); return componentType.cast(new OptimisticContextFactory()); case PESSIMISTIC: if (log.isTraceEnabled()) log.trace("Creating a pessimistic context factory"); return componentType.cast(new PessimisticContextFactory()); } throw new ConfigurationException("Unknown configuration node locking scheme"); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/factories/NodeMetaFactory.java0000644000175000017500000000423011111047320030165 0ustar moellermoeller/* * 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.factories; import org.jboss.cache.NodeFactory; import org.jboss.cache.PessimisticNodeFactory; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.factories.annotations.DefaultFactoryFor; import org.jboss.cache.mvcc.MVCCNodeFactory; import org.jboss.cache.optimistic.OptimisticNodeFactory; /** * Creates node factories. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ @DefaultFactoryFor(classes = NodeFactory.class) public class NodeMetaFactory extends ComponentFactory { @SuppressWarnings("deprecation") protected T construct(Class componentType) { switch (configuration.getNodeLockingScheme()) { case MVCC: return componentType.cast(new MVCCNodeFactory()); case OPTIMISTIC: return componentType.cast(new OptimisticNodeFactory()); case PESSIMISTIC: return componentType.cast(new PessimisticNodeFactory()); default: throw new ConfigurationException("Unknown locking scheme " + configuration.getNodeLockingScheme()); } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/factories/InterceptorChainFactory.java0000644000175000017500000002472411111047320031744 0ustar moellermoeller/* * 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.factories; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.config.CustomInterceptorConfig; import org.jboss.cache.factories.annotations.DefaultFactoryFor; import org.jboss.cache.interceptors.*; import org.jboss.cache.interceptors.base.CommandInterceptor; import java.util.List; /** * Factory class that builds an interceptor chain based on cache configuration. * * @author Manik Surtani (manik AT jboss DOT 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); } return chainedInterceptor; } public InterceptorChain buildInterceptorChain() throws IllegalAccessException, InstantiationException, ClassNotFoundException { boolean optimistic = configuration.getNodeLockingScheme() == NodeLockingScheme.OPTIMISTIC; boolean invocationBatching = configuration.isInvocationBatchingEnabled(); // load the icInterceptor first CommandInterceptor first = invocationBatching ? createInterceptor(BatchingInterceptor.class) : 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); // NOW add the ICI if we are using batching! if (invocationBatching) interceptorChain.appendIntereceptor(createInterceptor(InvocationContextInterceptor.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 (configuration.getNodeLockingScheme() == NodeLockingScheme.MVCC) { // if MVCC, then the CLI or AI must come before the MVCCLI. if (configuration.isUsingCacheLoaders()) { if (configuration.getCacheLoaderConfig().isPassivation()) { interceptorChain.appendIntereceptor(createInterceptor(ActivationInterceptor.class)); } else { interceptorChain.appendIntereceptor(createInterceptor(CacheLoaderInterceptor.class)); } } interceptorChain.appendIntereceptor(createInterceptor(MVCCLockingInterceptor.class)); } else if (configuration.getNodeLockingScheme() == NodeLockingScheme.PESSIMISTIC) { interceptorChain.appendIntereceptor(createInterceptor(PessimisticLockInterceptor.class)); } if (configuration.isUsingCacheLoaders()) { if (configuration.getCacheLoaderConfig().isPassivation()) { if (configuration.getNodeLockingScheme() != NodeLockingScheme.MVCC) { interceptorChain.appendIntereceptor(createInterceptor(LegacyActivationInterceptor.class)); interceptorChain.appendIntereceptor(createInterceptor(LegacyPassivationInterceptor.class)); } else { interceptorChain.appendIntereceptor(createInterceptor(PassivationInterceptor.class)); } } else { if (configuration.getNodeLockingScheme() != NodeLockingScheme.MVCC) { interceptorChain.appendIntereceptor(createInterceptor(LegacyCacheLoaderInterceptor.class)); interceptorChain.appendIntereceptor(createInterceptor(LegacyCacheStoreInterceptor.class)); } else { interceptorChain.appendIntereceptor(createInterceptor(CacheStoreInterceptor.class)); } } } if (configuration.isUsingBuddyReplication()) { if (configuration.getNodeLockingScheme() == NodeLockingScheme.MVCC) interceptorChain.appendIntereceptor(createInterceptor(DataGravitatorInterceptor.class)); else interceptorChain.appendIntereceptor(createInterceptor(LegacyDataGravitatorInterceptor.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 default interceptor chain."); buildCustomInterceptors(interceptorChain, configuration.getCustomInterceptors()); return interceptorChain; } private void buildCustomInterceptors(InterceptorChain interceptorChain, List customInterceptors) { for (CustomInterceptorConfig config : customInterceptors) { if (interceptorChain.containsInstance(config.getInterceptor())) continue; if (config.isFirst()) { interceptorChain.addInterceptor(config.getInterceptor(), 0); } if (config.isLast()) interceptorChain.appendIntereceptor(config.getInterceptor()); if (config.getIndex() >= 0) interceptorChain.addInterceptor(config.getInterceptor(), config.getIndex()); if (config.getAfterClass() != null) { List withClassName = interceptorChain.getInterceptorsWithClassName(config.getAfterClass()); if (withClassName.isEmpty()) { throw new ConfigurationException("Cannot add after class: " + config.getAfterClass() + " as no such iterceptor exists in the default chain"); } interceptorChain.addAfterInterceptor(config.getInterceptor(), withClassName.get(0).getClass()); } if (config.getBeforeClass() != null) { List withClassName = interceptorChain.getInterceptorsWithClassName(config.getBeforeClass()); if (withClassName.isEmpty()) { throw new ConfigurationException("Cannot add before class: " + config.getAfterClass() + " as no such iterceptor exists in the default chain"); } interceptorChain.addBeforeInterceptor(config.getInterceptor(), withClassName.get(0).getClass()); } } } @Override protected T construct(Class componentType) { try { return componentType.cast(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-3.2.8.GA/src/main/java/org/jboss/cache/factories/ComponentFactory.java0000644000175000017500000000624311111047320030441 0ustar moellermoeller/* * 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.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 AT jboss DOT 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 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-3.2.8.GA/src/main/java/org/jboss/cache/factories/BuddyManagerFactory.java0000644000175000017500000000352211111047320031036 0ustar moellermoeller/* * 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.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 AT jboss DOT 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-3.2.8.GA/src/main/java/org/jboss/cache/factories/ComponentRegistry.java0000644000175000017500000010471411236536237030666 0ustar moellermoeller/* * 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.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.Lifecycle; 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 AT jboss DOT org) * @since 2.1.0 */ @NonVolatile public class ComponentRegistry implements Lifecycle { /** * 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(); volatile 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; private volatile boolean statusCheckNecessary = true; /** * 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); s.add(ContextMetaFactory.class); s.add(NodeMetaFactory.class); s.add(StateTransferManagerFactory.class); s.add(StateTransferFactory.class); s.add(RegionManagerFactory.class); s.add(NodeMetaFactory.class); s.add(CommandsMetaFactory.class); s.add(TransactionLogFactory.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 (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. Registry now contains " + componentLookup.keySet()); } // ------------------------------ 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 SkipBlockUntilStart true if the call should not wait for the cache to start (typical local use) and false if we should try and wait (typical remote lookup use) * @return true if invocations are allowed, false otherwise. */ public boolean invocationsAllowed(boolean SkipBlockUntilStart) { if (trace) 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 (SkipBlockUntilStart) return false; if (trace) log.trace("Is remotely originating."); // else if this is a remote call and the status is STARTING, wait until the cache starts. if (statusCheckNecessary) { if (state == CacheStatus.STARTING) { if (trace) 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 its {@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); } public Object getInstance() { return instance; } public String getName() { return name; } } /** * 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 + '}'; } } /** * Returns an immutable set contating all the components that exists in the reporsitory at this moment. */ public Set getRegisteredComponents() { HashSet defensiveCopy = new HashSet(componentLookup.values()); return Collections.unmodifiableSet(defensiveCopy); } public void setStatusCheckNecessary(boolean statusCheckNecessary) { this.statusCheckNecessary = statusCheckNecessary; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/factories/EmptyConstructorFactory.java0000644000175000017500000000652111111047320032042 0ustar moellermoeller/* * 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.factories; import org.jboss.cache.DataContainer; import org.jboss.cache.RegionRegistry; import org.jboss.cache.batch.BatchContainer; 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.mvcc.MVCCNodeHelper; import org.jboss.cache.notifications.Notifier; import org.jboss.cache.remoting.jgroups.ChannelMessageListener; import org.jboss.cache.transaction.TransactionTable; /** * Simple factory that just uses reflection and an empty constructor of the component type. * * @author Manik Surtani (manik AT jboss DOT org) * @since 2.1.0 */ @DefaultFactoryFor(classes = {Notifier.class, MVCCNodeHelper.class, RegionRegistry.class, ChannelMessageListener.class, CacheLoaderManager.class, Marshaller.class, InvocationContextContainer.class, CacheInvocationDelegate.class, TransactionTable.class, DataContainer.class, LockStrategyFactory.class, BuddyFqnTransformer.class, BatchContainer.class}) public class EmptyConstructorFactory extends ComponentFactory { @Override 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 componentType.cast(componentImpl.newInstance()); } else { return componentType.newInstance(); } } catch (Exception e) { throw new ConfigurationException("Unable to create component " + componentType, e); } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/0000755000175000017500000000000011675221705024160 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/EvictionException.java0000755000175000017500000000274011111047320030450 0ustar moellermoeller/* * 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.eviction; import org.jboss.cache.CacheException; /** * @author Ben Wang, Feb 11, 2004 */ public class EvictionException extends CacheException { 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-3.2.8.GA/src/main/java/org/jboss/cache/eviction/LFUConfiguration.java0000644000175000017500000000632311111047320030165 0ustar moellermoeller/* * 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.eviction; import org.jboss.cache.config.Dynamic; import org.jboss.cache.config.EvictionAlgorithmConfig; import java.util.concurrent.TimeUnit; /** * Configuration implementation for {@link LFUPolicy}. *

* If configured via XML, expects the following: *

*

 * 
 *    10
 *    20
 * 
 * 
* * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 7168 $ * @deprecated see {@link org.jboss.cache.eviction.LFUAlgorithmConfig} */ @Deprecated public class LFUConfiguration extends EvictionPolicyConfigBase implements ModernizableConfig { /** * 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; } public EvictionAlgorithmConfig modernizeConfig() { LFUAlgorithmConfig modernCfg = new LFUAlgorithmConfig(); modernCfg.setMaxNodes(getMaxNodes()); modernCfg.setMinTimeToLive(getMinTimeToLiveSeconds(), TimeUnit.SECONDS); modernCfg.setMinNodes(getMinNodes()); return modernCfg; } @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 (obj instanceof LFUConfiguration && 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-3.2.8.GA/src/main/java/org/jboss/cache/eviction/FIFOPolicy.java0000755000175000017500000000347211111047320026717 0ustar moellermoeller/* * 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.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: 7168 $ * @deprecated see FIFOAlgorithm */ @Deprecated public class FIFOPolicy extends BaseEvictionPolicy implements ModernizablePolicy { private FIFOAlgorithm algorithm; public FIFOPolicy() { super(); algorithm = new FIFOAlgorithm(); } public EvictionAlgorithm getEvictionAlgorithm() { return algorithm; } public Class getEvictionConfigurationClass() { return FIFOConfiguration.class; } public Class modernizePolicy() { return FIFOAlgorithm.class; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/EvictionPolicyConfigBase.java0000644000175000017500000001027511240253605031701 0ustar moellermoeller/* * 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.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; /** * Base implementation of {@link EvictionPolicyConfig}. Adds properties * for the most commonly used config elements. * * @author Manik Surtani * @deprecated See {@link org.jboss.cache.eviction.EvictionAlgorithmConfigBase}. */ @Deprecated 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 } public boolean equals(Object o) { if (this == o) return true; if (!(o.getClass().equals(getClass()))) return false; EvictionPolicyConfigBase that = (EvictionPolicyConfigBase) o; if (maxNodes != that.maxNodes) return false; if (minTimeToLiveSeconds != that.minTimeToLiveSeconds) return false; if (evictionPolicyClass != null ? !evictionPolicyClass.equals(that.evictionPolicyClass) : that.evictionPolicyClass != null) return false; return true; } public int hashCode() { int result; result = (evictionPolicyClass != null ? evictionPolicyClass.hashCode() : 0); result = 31 * result + maxNodes; result = 31 * result + minTimeToLiveSeconds; result = 31 * result + (minTimeToLiveSeconds ^ (minTimeToLiveSeconds >>> 3)); return result; } public void reset() { setEvictionPolicyClass(null); setMaxNodes(0); setMinTimeToLiveSeconds(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-3.2.8.GA/src/main/java/org/jboss/cache/eviction/BaseSortedEvictionAlgorithm.java0000644000175000017500000001051711111047320032412 0ustar moellermoeller/* * 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.eviction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.util.concurrent.BlockingQueue; /** * 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(BlockingQueue queue) throws EvictionException { boolean evictionNodesModified = false; EvictionEvent node; int count = 0; while ((node = getNextInQueue(queue)) != 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-3.2.8.GA/src/main/java/org/jboss/cache/eviction/NullEvictionQueue.java0000755000175000017500000000637011111047320030434 0ustar moellermoeller/* * 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.eviction; import org.jboss.cache.Fqn; import java.util.Iterator; import java.util.NoSuchElementException; /** * 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 iterator() { 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 NodeEntry next() { throw new NoSuchElementException("No more elements"); } public void remove() { throw new IllegalStateException("Must call next() before remove()"); } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/SortedEvictionQueue.java0000644000175000017500000000251411111047320030753 0ustar moellermoeller/* * 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.eviction; /** * Sorted Eviction Queue implementation. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 7168 $ */ public interface SortedEvictionQueue extends EvictionQueue { /** * Provide contract to resort a sorted queue. */ void resortEvictionQueue(); } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/NullEvictionPolicyConfig.java0000755000175000017500000000410011111047320031722 0ustar moellermoeller/* * 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.eviction; import org.jboss.cache.config.ConfigurationComponent; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.config.EvictionAlgorithmConfig; import org.jboss.cache.config.EvictionPolicyConfig; /** * Configuration class for {@link NullEvictionPolicy}. * * @author Brian Stansberry * @deprecated see {@link NullEvictionAlgorithmConfig} */ @Deprecated public class NullEvictionPolicyConfig extends ConfigurationComponent implements EvictionPolicyConfig, ModernizableConfig { private static final long serialVersionUID = -6591180473728241737L; /** * Returns {@link NullEvictionPolicy}. */ public String getEvictionPolicyClass() { return NullEvictionPolicy.class.getName(); } public EvictionAlgorithmConfig modernizeConfig() { return new NullEvictionAlgorithmConfig(); } /** * No-op */ public void reset() { // no-op } /** * No-op */ public void validate() throws ConfigurationException { // no-op } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/EvictionAlgorithmConfigBase.java0000644000175000017500000001017311240253605032365 0ustar moellermoeller/* * 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.eviction; import org.jboss.cache.annotations.Compat; import org.jboss.cache.config.ConfigurationComponent; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.config.Dynamic; import org.jboss.cache.config.EvictionAlgorithmConfig; import java.util.concurrent.TimeUnit; /** * A base class used for configuring eviction algorithms. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ public abstract class EvictionAlgorithmConfigBase extends ConfigurationComponent implements EvictionAlgorithmConfig { private static final long serialVersionUID = 4591691674370188932L; protected String evictionAlgorithmClassName; @Dynamic protected int maxNodes = -1; @Dynamic protected long minTimeToLive = -1; /** * Can only be instantiated by a subclass. */ protected EvictionAlgorithmConfigBase() { } public String getEvictionAlgorithmClassName() { return evictionAlgorithmClassName; } public int getMaxNodes() { return maxNodes; } public void setMaxNodes(int maxNodes) { testImmutability("maxNodes"); this.maxNodes = maxNodes; } /** * @return The minimum time to live, in milliseconds. */ public long getMinTimeToLive() { return minTimeToLive; } /** * @param minTimeToLive time to live, in milliseconds */ public void setMinTimeToLive(long minTimeToLive) { testImmutability("minTimeToLive"); this.minTimeToLive = minTimeToLive; } public void setMinTimeToLive(long time, TimeUnit timeUnit) { testImmutability("minTimeToLive"); minTimeToLive = timeUnit.toMillis(time); } @Deprecated @Compat public void setMinTimeToLiveSeconds(long time) { setMinTimeToLive(time, TimeUnit.SECONDS); } public void validate() throws ConfigurationException { if (evictionAlgorithmClassName == null) throw new ConfigurationException("Eviction algorithm class name cannot be null!"); } public boolean equals(Object o) { if (this == o) return true; if (!(o.getClass().equals(getClass()))) return false; EvictionAlgorithmConfigBase that = (EvictionAlgorithmConfigBase) o; if (maxNodes != that.maxNodes) return false; if (minTimeToLive != that.minTimeToLive) return false; if (evictionAlgorithmClassName != null ? !evictionAlgorithmClassName.equals(that.evictionAlgorithmClassName) : that.evictionAlgorithmClassName != null) return false; return true; } public int hashCode() { int result; result = (evictionAlgorithmClassName != null ? evictionAlgorithmClassName.hashCode() : 0); result = 31 * result + maxNodes; result = (int) (31 * result + minTimeToLive); result = 31 * result + (int) (minTimeToLive ^ (minTimeToLive >>> 32)); return result; } public void reset() { maxNodes = -1; minTimeToLive = -1; } public EvictionAlgorithmConfig clone() throws CloneNotSupportedException { return (EvictionAlgorithmConfig) super.clone(); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/EvictionEvent.java0000755000175000017500000000745011433521426027611 0ustar moellermoeller/* * 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.eviction; import org.jboss.cache.Fqn; import org.jboss.cache.commands.DataCommand; import javax.transaction.Transaction; /** * An eviction event records activity on nodes in the cache. These are recorded on a {@link org.jboss.cache.Region} for processing * later by calls to {@link org.jboss.cache.Region#processEvictionQueues()}. *

* * @see org.jboss.cache.Region */ public class EvictionEvent { private Fqn fqn; private Type type; private int elementDifference; private long inUseTimeout; private long creationTimestamp; private DataCommand command; private Transaction transaction; public EvictionEvent() { } public static enum Type { ADD_NODE_EVENT, REMOVE_NODE_EVENT, VISIT_NODE_EVENT, ADD_ELEMENT_EVENT, REMOVE_ELEMENT_EVENT, MARK_IN_USE_EVENT, UNMARK_USE_EVENT } public EvictionEvent(Fqn fqn, Type type, int elementDifference, DataCommand command, Transaction tx) { this.fqn = fqn; this.type = type; this.elementDifference = elementDifference; this.creationTimestamp = System.currentTimeMillis(); this.command = command; this.transaction = tx; } 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(Type event) { type = event; } public Type getEventType() { return type; } public DataCommand getCommand() { return command; } public void setCommand(DataCommand command) { this.command = command; } public Transaction getTransaction() { return transaction; } public void setTransaction(Transaction transaction) { this.transaction = transaction; } @Override public String toString() { return "EvictedEventNode[fqn=" + fqn + " event=" + type + " diff=" + elementDifference + "]"; } /** * Copies this evicted event node to create a new one with the same values, except with a new Fqn root. * * @param newRoot new Fqn root to use * @return a new EvictedEventNode instance * @see org.jboss.cache.Region#copy(org.jboss.cache.Fqn) */ public EvictionEvent copy(Fqn newRoot) { return new EvictionEvent(Fqn.fromRelativeFqn(newRoot, fqn), type, elementDifference, command, transaction); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/LRUAlgorithm.java0000755000175000017500000001420111357175221027333 0ustar moellermoeller/* * 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.eviction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.config.EvictionAlgorithmConfig; 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() throws EvictionException { return new LRUQueue(); } @Override protected boolean shouldEvictNode(NodeEntry entry) { LRUAlgorithmConfig config = (LRUAlgorithmConfig) evictionAlgorithmConfig; // 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)) { if (trace) log.trace("Do not evict - is younger than minimum TTL"); return false; } // no idle or max time limit if (config.getTimeToLive() < 0 && config.getMaxAge() < 0) { log.trace("No idle or max time limit!"); return false; } long currentTime = System.currentTimeMillis(); if (config.getTimeToLive() > -1) { long idleTime = currentTime - entry.getModifiedTimeStamp(); if (trace) { log.trace("Node " + entry.getFqn() + " has been idle for " + idleTime + "ms"); } if ((idleTime >= (config.getTimeToLive()))) { if (trace) { log.trace("Node " + entry.getFqn() + " should be evicted because of idle time"); log.trace("Time to live in millies is: " + (config.getTimeToLive())); log.trace("Config instance is: " + System.identityHashCode(config)); } return true; } } if (config.getMaxAge() > -1) { long objectLifeTime = currentTime - entry.getCreationTimeStamp(); if (trace) { log.trace("Node " + entry.getFqn() + " has been alive for " + objectLifeTime + "ms"); } if ((objectLifeTime >= config.getMaxAge())) { 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 (trace) { if (ne == null) log.trace("Got a NULL node entry!"); else log.trace("About to evict " + ne.getFqn()); } if (ne != null && !this.evictCacheNode(ne.getFqn())) { try { if (trace) log.trace("Could not evict " + ne.getFqn() + " so adding to recycle queue"); 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 = ((LRUAlgorithmConfig) evictionAlgorithmConfig).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); } } } public Class getConfigurationClass() { return LRUAlgorithmConfig.class; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/LRUPolicy.java0000755000175000017500000000420711111047320026633 0ustar moellermoeller/* * 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.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: 7168 $ * @deprecated see LRUAlgorithm */ @Deprecated public class LRUPolicy extends BaseEvictionPolicy implements ModernizablePolicy { 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(); } public Class modernizePolicy() { return LRUAlgorithm.class; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/MRUAlgorithm.java0000644000175000017500000000511711357175221027337 0ustar moellermoeller/* * 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.eviction; import org.jboss.cache.config.EvictionAlgorithmConfig; /** * 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: 8363 $ */ public class MRUAlgorithm extends BaseEvictionAlgorithm { @Override protected EvictionQueue setupEvictionQueue() 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; MRUAlgorithmConfig config = (MRUAlgorithmConfig) evictionAlgorithmConfig; return config.getMaxNodes() > -1 && evictionQueue.getNumberOfNodes() > config.getMaxNodes(); } @Override protected void processVisitedNodes(EvictionEvent evictedEventNode) throws EvictionException { super.processVisitedNodes(evictedEventNode); ((MRUQueue) evictionQueue).moveToTopOfStack(evictedEventNode.getFqn()); } public Class getConfigurationClass() { return MRUAlgorithmConfig.class; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/EvictionAlgorithm.java0000755000175000017500000000765011111047320030445 0ustar moellermoeller/* * 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.eviction; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionAlgorithmConfig; import org.jboss.cache.eviction.EvictionEvent.Type; import java.util.concurrent.BlockingQueue; /** * 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: 7168 $ */ public interface EvictionAlgorithm { /** * Entry point for eviction algorithm. Invoking this will cause the algorithm to process the queue of {@link org.jboss.cache.eviction.EvictionEvent} * passed in. * * @param queue to process */ void process(BlockingQueue queue) throws EvictionException; /** * Reset the whole eviction queue. The queue may need to be reset due to corrupted state, for example. */ void resetEvictionQueue(); /** * Get the EvictionQueue implementation used by this algorithm. * * @return the EvictionQueue implementation. */ EvictionQueue getEvictionQueue(); /** * Sets the eviction action policy, so the algorithm knows what to do when a node is to be evicted. * * @param evictionActionPolicy to set */ void setEvictionActionPolicy(EvictionActionPolicy evictionActionPolicy); /** * Assigns the algorithm instance to a specific region. * * @param fqn of the region to be assigned to * @param cache cache reference * @param evictionAlgorithmConfig configuration for the current algorithm instance. * @param configuration for the entire cache. */ void assignToRegion(Fqn fqn, CacheSPI cache, EvictionAlgorithmConfig evictionAlgorithmConfig, Configuration configuration); /** * Tests whether the algorithm would ignore certain event types on certain Fqns. * * @param eventType event type to test for * @return true if the event representing the parameters would be ignored by this algorithm or not. */ boolean canIgnoreEvent(Type eventType); /** * Invoked by the region manager when the enclosing region is initialized. */ void initialize(); /** * This is a helper so that the XML parser will be able to select and use the correct {@link org.jboss.cache.config.EvictionAlgorithmConfig} implementation * class corresponding to this EvictionAlgorithm. E.g., the {@link FIFOAlgorithm} would return {@link org.jboss.cache.eviction.FIFOAlgorithmConfig}.class. * * @return a class that is used to configure this EvictionAlgorithm. */ Class getConfigurationClass(); } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/ExpirationAlgorithmConfig.java0000755000175000017500000001142011111047320032123 0ustar moellermoeller/* * 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.eviction; import org.jboss.cache.annotations.Compat; import org.jboss.cache.config.Dynamic; import java.util.concurrent.TimeUnit; /** * Configuration for indicating the Node key for setting a specific eviction time. */ public class ExpirationAlgorithmConfig extends EvictionAlgorithmConfigBase { 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 long timeToLive = -1; public ExpirationAlgorithmConfig() { evictionAlgorithmClassName = ExpirationAlgorithm.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 isWarnNoExpirationKey() { 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; } /** * @return time to live, in milliseconds */ public long getTimeToLive() { return timeToLive; } /** * Sets the time to live * * @param timeToLive value in milliseconds */ public void setTimeToLive(long timeToLive) { this.timeToLive = timeToLive; } public void setTimeToLive(long timeToLive, TimeUnit timeUnit) { this.timeToLive = timeUnit.toMillis(timeToLive); } @Deprecated @Compat public void setTimeToLiveSeconds(long time) { setTimeToLive(time, TimeUnit.SECONDS); } @Override public ExpirationAlgorithmConfig clone() throws CloneNotSupportedException { return (ExpirationAlgorithmConfig) super.clone(); } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; ExpirationAlgorithmConfig that = (ExpirationAlgorithmConfig) o; if (timeToLive != that.timeToLive) return false; if (warnNoExpirationKey != that.warnNoExpirationKey) return false; if (expirationKeyName != null ? !expirationKeyName.equals(that.expirationKeyName) : that.expirationKeyName != null) return false; return true; } public int hashCode() { int result = super.hashCode(); result = 31 * result + (expirationKeyName != null ? expirationKeyName.hashCode() : 0); result = 31 * result + (warnNoExpirationKey ? 1 : 0); result = 31 * result + (int) (timeToLive ^ (timeToLive >>> 32)); return result; } @Override public void reset() { super.reset(); evictionAlgorithmClassName = ExpirationAlgorithm.class.getName(); warnNoExpirationKey = true; timeToLive = -1; } public String toString() { return "ExpirationAlgorithmConfig{" + "expirationKeyName='" + expirationKeyName + '\'' + ", warnNoExpirationKey=" + warnNoExpirationKey + ", timeToLive=" + timeToLive + ", maxNodes=" + maxNodes + ", minTTL=" + minTimeToLive + '}'; } }jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/LFUAlgorithm.java0000644000175000017500000000756311357175221027331 0ustar moellermoeller/* * 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.eviction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.config.EvictionAlgorithmConfig; /** * 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 = -1 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: 8363 $ */ 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; LFUAlgorithmConfig config = (LFUAlgorithmConfig) evictionAlgorithmConfig; int size = this.getEvictionQueue().getNumberOfNodes(); if (config.getMaxNodes() > -1 && 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. * * @return The created LFUQueue. * @throws EvictionException */ @Override protected EvictionQueue setupEvictionQueue() throws EvictionException { return new LFUQueue(); } @Override protected void prune() throws EvictionException { super.prune(); // clean up the Queue's eviction removals ((LFUQueue) this.evictionQueue).prune(); } public Class getConfigurationClass() { return LFUAlgorithmConfig.class; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/FIFOAlgorithmConfig.java0000644000175000017500000000506111240253605030535 0ustar moellermoeller/* * 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.eviction; import org.jboss.cache.config.ConfigurationException; /** * Configuration for {@link FIFOAlgorithm}. *

* Requires a "maxNodes" attribute otherwise a ConfigurationException is thrown. * * @author Manik Surtani * @since 3.0 */ public class FIFOAlgorithmConfig extends EvictionAlgorithmConfigBase { /** * The serialVersionUID */ private static final long serialVersionUID = -7229715009546277313L; public FIFOAlgorithmConfig() { evictionAlgorithmClassName = FIFOAlgorithm.class.getName(); // We require that maxNodes is set setMaxNodes(-1); } public FIFOAlgorithmConfig(int maxNodes) { evictionAlgorithmClassName = FIFOAlgorithm.class.getName(); // We require that maxNodes is set setMaxNodes(maxNodes); } /** * Requires a positive maxNodes value or ConfigurationException * is thrown. */ @Override public void validate() throws ConfigurationException { super.validate(); if (maxNodes < -1) maxNodes = -1; } @Override public String toString() { StringBuilder ret = new StringBuilder(); ret.append("FIFOAlgorithmConfig: maxNodes = ").append(getMaxNodes()); return ret.toString(); } @Override public void reset() { super.reset(); setMaxNodes(-1); evictionAlgorithmClassName = FIFOAlgorithm.class.getName(); } @Override public FIFOAlgorithmConfig clone() throws CloneNotSupportedException { return (FIFOAlgorithmConfig) super.clone(); } }jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/EvictionActionPolicy.java0000644000175000017500000000315011111047320031100 0ustar moellermoeller/* * 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.eviction; import org.jboss.cache.Cache; import org.jboss.cache.Fqn; /** * Performs an eviction on a given Fqn. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ public interface EvictionActionPolicy { /** * Sets a reference to the cache. * * @param cache cache */ void setCache(Cache cache); /** * Performs an eviction on a given node. * * @param fqn fqn to evict * @return true if the eviction was successful, false if not. */ boolean evict(Fqn fqn); } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/EvictionQueueList.java0000644000175000017500000002026711111047320030433 0ustar moellermoeller/* * 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.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: 7168 $ */ 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 NodeEntry next() { this.doConcurrentModCheck(); this.forwardCursor(); return cursor.node; } public boolean hasPrevious() { this.doConcurrentModCheck(); return previous != null; } public NodeEntry 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(NodeEntry o) { this.doConcurrentModCheck(); cursor.node = (NodeEntry) o; } public void add(NodeEntry 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-3.2.8.GA/src/main/java/org/jboss/cache/eviction/ElementSizeAlgorithmConfig.java0000644000175000017500000000703411111047320032230 0ustar moellermoeller/* * 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.eviction; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.config.Dynamic; /** * Configuration for {@link ElementSizeAlgorithm}. *

* Requires a positive "maxElementsPerNode" value otherwise a ConfigurationException is thrown. * * @author Manik Surtani * @since 3.0 */ public class ElementSizeAlgorithmConfig extends EvictionAlgorithmConfigBase { /** * The serialVersionUID */ private static final long serialVersionUID = 2510593544656833758L; @Dynamic private int maxElementsPerNode = -1; public ElementSizeAlgorithmConfig() { evictionAlgorithmClassName = ElementSizeAlgorithm.class.getName(); // Force configuration of maxElementsPerNode setMaxElementsPerNode(-1); } public ElementSizeAlgorithmConfig(int maxNodes, int maxElementsPerNode) { this(); setMaxNodes(maxNodes); setMaxElementsPerNode(maxElementsPerNode); } 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 { super.validate(); if (maxElementsPerNode < -1) maxElementsPerNode = -1; } @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 (obj instanceof ElementSizeAlgorithmConfig && super.equals(obj)) { return this.maxElementsPerNode == ((ElementSizeAlgorithmConfig) 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); evictionAlgorithmClassName = ElementSizeAlgorithm.class.getName(); } @Override public ElementSizeAlgorithmConfig clone() throws CloneNotSupportedException { return (ElementSizeAlgorithmConfig) super.clone(); } }jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/BaseEvictionAlgorithm.java0000644000175000017500000004631611433521426031252 0ustar moellermoeller/* * 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.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.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.config.EvictionAlgorithmConfig; import org.jboss.cache.eviction.EvictionEvent.Type; 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: 8443 $ */ public abstract class BaseEvictionAlgorithm implements EvictionAlgorithm { private static final Log log = LogFactory.getLog(BaseEvictionAlgorithm.class); private static final boolean trace = log.isTraceEnabled(); protected EvictionActionPolicy evictionActionPolicy; protected EvictionAlgorithmConfig evictionAlgorithmConfig; /** * Contains Fqn instances. */ protected BlockingQueue recycleQueue; /** * Contains NodeEntry instances. */ protected EvictionQueue evictionQueue; protected boolean allowTombstones = false; protected Configuration configuration; protected Fqn regionFqn; protected CacheSPI cache; /** * This method will create an EvictionQueue implementation and prepare it for use. * * @return The created EvictionQueue to be used as the eviction queue for this algorithm. * @throws EvictionException if there are problems * @see EvictionQueue */ protected abstract EvictionQueue setupEvictionQueue() 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); } public synchronized void initialize() { if (evictionQueue == null) { evictionQueue = setupEvictionQueue(); if (log.isDebugEnabled()) log.debug("Initialized: " + this); Configuration.CacheMode cm = configuration != null ? configuration.getCacheMode() : Configuration.CacheMode.LOCAL; allowTombstones = configuration != null && configuration.getNodeLockingScheme() == NodeLockingScheme.OPTIMISTIC && (cm == Configuration.CacheMode.INVALIDATION_ASYNC || cm == Configuration.CacheMode.INVALIDATION_SYNC); } } public EvictionActionPolicy getEvictionActionPolicy() { return evictionActionPolicy; } public void setEvictionActionPolicy(EvictionActionPolicy evictionActionPolicy) { this.evictionActionPolicy = evictionActionPolicy; } public EvictionAlgorithmConfig getEvictionAlgorithmConfig() { return evictionAlgorithmConfig; } public void assignToRegion(Fqn fqn, CacheSPI cache, EvictionAlgorithmConfig evictionAlgorithmConfig, Configuration configuration) { if (log.isTraceEnabled()) log.trace(getClass().getSimpleName() + " instantiated and assigned to region " + fqn + " with cfg " + evictionAlgorithmConfig); this.regionFqn = fqn; this.cache = cache; this.evictionAlgorithmConfig = evictionAlgorithmConfig; this.configuration = configuration; } public boolean canIgnoreEvent(Type eventType) { return false; // don't ignore anything! } /** * Process the given eviction event queue. 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 eventQueue queue containing eviction events * @throws EvictionException */ public void process(BlockingQueue eventQueue) throws EvictionException { if (trace) log.trace("process(): region: " + regionFqn); initialize(); this.processQueues(eventQueue); this.emptyRecycleQueue(); this.prune(); } public void resetEvictionQueue() { // a no-op } /** * Get the underlying EvictionQueue implementation. * * @return the EvictionQueue used by this algorithm * @see EvictionQueue */ public EvictionQueue getEvictionQueue() { return this.evictionQueue; } protected EvictionEvent getNextInQueue(BlockingQueue queue) { try { return queue.poll(0, TimeUnit.SECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } return null; } /** * 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 queue queue to inspect * @throws EvictionException in the event of problems */ protected void processQueues(BlockingQueue queue) throws EvictionException { EvictionEvent node; int count = 0; while ((node = getNextInQueue(queue)) != null) { 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"); } 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); boolean evictionResult; try { evictionResult = evictionActionPolicy.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 (evictionResult) { if (trace) log.trace("Eviction of cache node with fqn of " + fqn + " successful"); } return evictionResult; } 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(EvictionEvent, int)} 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(EvictionEvent evictedEventNode) throws EvictionException { processAddedNodes(evictedEventNode, evictedEventNode.getElementDifference()); } protected void processAddedNodes(EvictionEvent 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(EvictionEvent 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(EvictionEvent 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(EvictionEvent 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(EvictionEvent 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() + " 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 (evictionAlgorithmConfig instanceof EvictionAlgorithmConfigBase) { EvictionAlgorithmConfigBase cfg = (EvictionAlgorithmConfigBase) evictionAlgorithmConfig; long minTTL = cfg.getMinTimeToLive(); return minTTL >= 1 && (entry.getModifiedTimeStamp() + minTTL > System.currentTimeMillis()); } else { log.trace("Eviction policy implementation does not support minimum TTL!"); return false; } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/NullEvictionAlgorithm.java0000755000175000017500000000526511111047320031300 0ustar moellermoeller/* * 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.eviction; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionAlgorithmConfig; import org.jboss.cache.eviction.EvictionEvent.Type; import java.util.concurrent.BlockingQueue; /** * An eviction algorithm that does nothing - a no-op for everything. * * @author Brian Stansberry */ public class NullEvictionAlgorithm implements EvictionAlgorithm { /** * Singleton instance of this class. */ private static final NullEvictionAlgorithm INSTANCE = new NullEvictionAlgorithm(); /** * Constructs a new NullEvictionAlgorithm. */ private NullEvictionAlgorithm() { } public static NullEvictionAlgorithm getInstance() { return INSTANCE; } /** * Returns {@link NullEvictionQueue#INSTANCE}. */ public EvictionQueue getEvictionQueue() { return NullEvictionQueue.INSTANCE; } public void setEvictionActionPolicy(EvictionActionPolicy evictionActionPolicy) { // no-op } public void assignToRegion(Fqn fqn, CacheSPI cache, EvictionAlgorithmConfig evictionAlgorithmConfig, Configuration configuration) { // no-op } public void process(BlockingQueue queue) throws EvictionException { // no-op } public void resetEvictionQueue() { // no-op } public boolean canIgnoreEvent(Type eventType) { return true; // always ignore everything! } public void initialize() { // no-op } public Class getConfigurationClass() { return NullEvictionAlgorithmConfig.class; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/LFUAlgorithmConfig.java0000644000175000017500000000536311111047320030435 0ustar moellermoeller/* * 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.eviction; import org.jboss.cache.config.Dynamic; /** * Configuration implementation for {@link LFUAlgorithm}. * * @author Manik Surtani * @since 3.0 */ public class LFUAlgorithmConfig extends EvictionAlgorithmConfigBase { /** * The serialVersionUID */ private static final long serialVersionUID = 1865801530398969179L; @Dynamic private int minNodes = -1; public LFUAlgorithmConfig() { evictionAlgorithmClassName = LFUAlgorithm.class.getName(); } public LFUAlgorithmConfig(int maxNodes, int minNodes) { this(); setMaxNodes(maxNodes); setMinNodes(minNodes); } 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("LFUAlgorithmConfig: maxNodes = ").append(getMaxNodes()).append(" minNodes = ").append(getMinNodes()); return ret.toString(); } @Override public boolean equals(Object obj) { if (obj instanceof LFUAlgorithmConfig && super.equals(obj)) { return (this.minNodes == ((LFUAlgorithmConfig) obj).minNodes); } return false; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + minNodes; return result; } @Override public LFUAlgorithmConfig clone() throws CloneNotSupportedException { return (LFUAlgorithmConfig) super.clone(); } @Override public void reset() { super.reset(); minNodes = -1; evictionAlgorithmClassName = LFUAlgorithm.class.getName(); } }jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/MRUPolicy.java0000644000175000017500000000344711111047320026636 0ustar moellermoeller/* * 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.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: 7168 $ * @deprecated see MRUAlgorithm */ @Deprecated public class MRUPolicy extends BaseEvictionPolicy implements ModernizablePolicy { private MRUAlgorithm algorithm; public MRUPolicy() { super(); algorithm = new MRUAlgorithm(); } public EvictionAlgorithm getEvictionAlgorithm() { return algorithm; } public Class getEvictionConfigurationClass() { return MRUConfiguration.class; } public Class modernizePolicy() { return MRUAlgorithm.class; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/EvictionEventType.java0000644000175000017500000000270111111047320030427 0ustar moellermoeller/* * 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.eviction; /** * @deprecated left here for old interfaces. Use {@link org.jboss.cache.eviction.EvictionEvent.Type} instead. */ @Deprecated public enum EvictionEventType { @Deprecated ADD_NODE_EVENT, @Deprecated REMOVE_NODE_EVENT, @Deprecated VISIT_NODE_EVENT, @Deprecated ADD_ELEMENT_EVENT, @Deprecated REMOVE_ELEMENT_EVENT, @Deprecated MARK_IN_USE_EVENT, @Deprecated UNMARK_USE_EVENT }jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/RegionNameConflictException.java0000755000175000017500000000302011111047320032366 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/eviction/EvictionQueue.java0000644000175000017500000000625011111047320027573 0ustar moellermoeller/* * 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.eviction; import org.jboss.cache.Fqn; /** * 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: 7168 $ */ public interface EvictionQueue extends Iterable { /** * 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); /** * Clear the queue. */ void clear(); } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/FIFOQueue.java0000644000175000017500000000634411111047320026542 0ustar moellermoeller/* * 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.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: 7168 $ */ 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 iterator() { return nodeMap.values().iterator(); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/ElementSizeQueue.java0000644000175000017500000001366511111047320030247 0ustar moellermoeller/* * 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.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: 7168 $ */ 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 iterator() { 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-3.2.8.GA/src/main/java/org/jboss/cache/eviction/NullEvictionPolicy.java0000755000175000017500000000536611111047320030613 0ustar moellermoeller/* * 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.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 * @deprecated see NullEvictionAlgorithm */ @Deprecated public class NullEvictionPolicy implements EvictionPolicy, ModernizablePolicy { private static final Log log = LogFactory.getLog(NullEvictionPolicy.class); private CacheSPI cache; /** * Returns true */ public boolean canIgnoreEvent(Fqn fqn, EvictionEventType 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.getInstance(); } /** * Returns {@link NullEvictionPolicyConfig}. */ public Class getEvictionConfigurationClass() { return NullEvictionPolicyConfig.class; } public CacheSPI getCache() { return cache; } public void setCache(CacheSPI cache) { this.cache = cache; } public Class modernizePolicy() { return NullEvictionAlgorithm.class; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/ModernizablePolicy.java0000644000175000017500000000273311111047320030603 0ustar moellermoeller/* * 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.eviction; /** * Attached to deprecated eviction policies that know how to map to modern counterparts. This interface * is itself deprecated and is only used to bridge deprecated configurations. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 * @deprecated */ @Deprecated public interface ModernizablePolicy { Class modernizePolicy(); }jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/LFUQueue.java0000644000175000017500000001421311111047320026437 0ustar moellermoeller/* * 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.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: 7168 $ */ 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.iterator(); 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 iterator() { 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-3.2.8.GA/src/main/java/org/jboss/cache/eviction/EvictedEventNode.java0000644000175000017500000000325511433521426030216 0ustar moellermoeller/* * 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.eviction; import org.jboss.cache.Fqn; import org.jboss.cache.annotations.Compat; /** * This is here for backward compat. * * @deprecated please use {@link org.jboss.cache.eviction.EvictionEvent} instead. */ @Deprecated @Compat public class EvictedEventNode extends EvictionEvent { public EvictedEventNode(Fqn fqn, Type type, int elementDifference) { super(fqn, type, elementDifference, null, null); } public EvictedEventNode(EvictionEvent ee) { setElementDifference(ee.getElementDifference()); setEventType(ee.getEventType()); setFqn(ee.getFqn()); setInUseTimeout(ee.getInUseTimeout()); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/BaseEvictionPolicy.java0000755000175000017500000000415011111047320030541 0ustar moellermoeller/* * 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.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: 7168 $ * @deprecated see {@link org.jboss.cache.eviction.EvictionActionPolicy} */ @Deprecated 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, EvictionEventType eventType) { return false; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/LRUAlgorithmConfig.java0000644000175000017500000001135611111047320030450 0ustar moellermoeller/* * 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.eviction; import org.jboss.cache.annotations.Compat; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.config.Dynamic; import java.util.concurrent.TimeUnit; /** * Configuration implementation for {@link LRUAlgorithm}. *

* * @author Manik Surtani * @since 3.0 */ public class LRUAlgorithmConfig extends EvictionAlgorithmConfigBase { /** * The serialVersionUID */ private static final long serialVersionUID = -3426716488271559729L; /** * value expressed in millis */ @Dynamic private long timeToLive = -1; /** * value expressed in millis */ @Dynamic private long maxAge = -1; public LRUAlgorithmConfig() { evictionAlgorithmClassName = LRUAlgorithm.class.getName(); // Force config of ttls setTimeToLive(-1); setMaxAge(-1); } public LRUAlgorithmConfig(long timeToLive, long maxAge) { this(); this.timeToLive = timeToLive; this.maxAge = maxAge; } public LRUAlgorithmConfig(long timeToLive, long maxAge, int maxNodes) { this(timeToLive, maxAge); this.maxNodes = maxNodes; } /** * @return the time to live, in milliseconds */ public long getTimeToLive() { return timeToLive; } /** * Sets the time to live * * @param timeToLive the time to live, in milliseconds */ public void setTimeToLive(long timeToLive) { testImmutability("timeToLive"); this.timeToLive = timeToLive; } public void setTimeToLive(long timeToLive, TimeUnit timeUnit) { testImmutability("timeToLive"); this.timeToLive = timeUnit.toMillis(timeToLive); } @Deprecated @Compat public void setTimeToLiveSeconds(long time) { setTimeToLive(time, TimeUnit.SECONDS); } /** * @return the max age per element, in milliseconds */ public long getMaxAge() { return maxAge; } /** * Sets the max age per element * * @param maxAge value in milliseconds */ public void setMaxAge(long maxAge) { testImmutability("maxAge"); this.maxAge = maxAge; } public void setMaxAge(long maxAge, TimeUnit timeUnit) { testImmutability("maxAge"); this.maxAge = timeUnit.toMillis(maxAge); } /** * Requires a positive timeToLive value or ConfigurationException * is thrown. */ @Override public void validate() throws ConfigurationException { super.validate(); if (timeToLive < -1) timeToLive = -1; } public String toString() { return getClass().getSimpleName() + "{" + "algorithmClassName=" + evictionAlgorithmClassName + ", timeToLive=" + timeToLive + ", maxAge=" + maxAge + ", minTTL=" + minTimeToLive + ", maxNodes=" + maxNodes + '}'; } public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof LRUAlgorithmConfig)) return false; if (!super.equals(o)) return false; LRUAlgorithmConfig that = (LRUAlgorithmConfig) o; if (maxAge != that.maxAge) return false; if (timeToLive != that.timeToLive) return false; return true; } public int hashCode() { int result = super.hashCode(); result = 31 * result + (int) (timeToLive ^ (timeToLive >>> 32)); result = 31 * result + (int) (maxAge ^ (maxAge >>> 32)); return result; } @Override public void reset() { super.reset(); setTimeToLive(-1); setMaxAge(-1); evictionAlgorithmClassName = LRUAlgorithm.class.getName(); } @Override public LRUAlgorithmConfig clone() throws CloneNotSupportedException { return (LRUAlgorithmConfig) super.clone(); } }jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/MRUAlgorithmConfig.java0000644000175000017500000000503411240253605030455 0ustar moellermoeller/* * 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.eviction; import org.jboss.cache.config.ConfigurationException; /** * Configuration for {@link MRUAlgorithm}. *

* Requires a "maxNodes" attribute otherwise a ConfigurationException is thrown. * * @author Manik Surtani * @since 3.0 */ public class MRUAlgorithmConfig extends EvictionAlgorithmConfigBase { /** * The serialVersionUID */ private static final long serialVersionUID = -8734577898966155218L; public MRUAlgorithmConfig() { evictionAlgorithmClassName = MRUAlgorithm.class.getName(); // We require that maxNodes is set setMaxNodes(-1); } public MRUAlgorithmConfig(int maxNodes) { evictionAlgorithmClassName = MRUAlgorithm.class.getName(); setMaxNodes(maxNodes); } /** * Requires a positive maxNodes value or ConfigurationException * is thrown. */ @Override public void validate() throws ConfigurationException { super.validate(); if (getMaxNodes() < -1) maxNodes = -1; } @Override public String toString() { StringBuilder str = new StringBuilder(); str.append("MRUAlgorithmConfig: "). append(" maxNodes =").append(getMaxNodes()); return str.toString(); } @Override public void reset() { super.reset(); setMaxNodes(-1); evictionAlgorithmClassName = MRUAlgorithm.class.getName(); } @Override public MRUAlgorithmConfig clone() throws CloneNotSupportedException { return (MRUAlgorithmConfig) super.clone(); } }jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/EvictionPolicy.java0000755000175000017500000000757111111047320027760 0ustar moellermoeller/* * 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.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 * @deprecated please use {@link EvictionActionPolicy} instead. */ @Deprecated 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 EvictionEventType 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, EvictionEventType eventType); } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/ModernizableConfig.java0000644000175000017500000000302711111047320030546 0ustar moellermoeller/* * 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.eviction; import org.jboss.cache.config.EvictionAlgorithmConfig; /** * Attached to deprecated eviction configuration elements that know how to map to modern counterparts. This interface * is itself deprecated and is only used to bridge deprecated configurations. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 * @deprecated */ @Deprecated public interface ModernizableConfig { EvictionAlgorithmConfig modernizeConfig(); } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/ExpirationPolicy.java0000755000175000017500000000374111111047320030315 0ustar moellermoeller/* * 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.eviction; import org.jboss.cache.Fqn; /** * Returns the {@link ExpirationAlgorithm} as the policy's algorithm. * * @author rosse * @deprecated see ExpirationAlgorithm */ @Deprecated public class ExpirationPolicy extends BaseEvictionPolicy implements ModernizablePolicy { private EvictionAlgorithm algorithm; public ExpirationPolicy() { algorithm = new ExpirationAlgorithm(); } 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, EvictionEventType eventType) { return (eventType == EvictionEventType.VISIT_NODE_EVENT); } public Class modernizePolicy() { return ExpirationAlgorithm.class; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/RemoveOnEvictActionPolicy.java0000644000175000017500000000447411111047320032057 0ustar moellermoeller/* * 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.eviction; 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.buddyreplication.BuddyFqnTransformer; /** * An eviction action policy that calls {@link org.jboss.cache.Cache#removeNode(org.jboss.cache.Fqn)} to evict a node. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ public class RemoveOnEvictActionPolicy implements EvictionActionPolicy { Cache cache; private static final Log log = LogFactory.getLog(RemoveOnEvictActionPolicy.class); public void setCache(Cache cache) { this.cache = cache; } public boolean evict(Fqn fqn) { // eviction code may also try and evict the fqn of a buddy. If the Fqn is that of a buddy we need to ensure that // this call is ignored since a proper remove will happen on the data owner when the data is evicted, and that // remove will propagate here. if (BuddyFqnTransformer.isBackupFqn(fqn)) return true; try { return cache.removeNode(fqn); } catch (Exception e) { if (log.isDebugEnabled()) log.debug("Unable to evict " + fqn, e); return false; } } }jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/DefaultEvictionActionPolicy.java0000644000175000017500000000400311111047320032403 0ustar moellermoeller/* * 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.eviction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Cache; import org.jboss.cache.Fqn; /** * Default eviction action policy that calls {@link org.jboss.cache.Cache#evict(org.jboss.cache.Fqn)} to evict a node. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ public class DefaultEvictionActionPolicy implements EvictionActionPolicy { Cache cache; private static final Log log = LogFactory.getLog(DefaultEvictionActionPolicy.class); public void setCache(Cache cache) { this.cache = cache; } public boolean evict(Fqn fqn) { try { if (log.isTraceEnabled()) log.trace("Evicting Fqn " + fqn); cache.evict(fqn); return true; } catch (Exception e) { if (log.isDebugEnabled()) log.debug("Unable to evict " + fqn, e); return false; } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/EvictionTimerTask.java0000755000175000017500000001072011364010163030417 0ustar moellermoeller/* * 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.eviction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Region; import org.jboss.cache.RegionRegistry; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; 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: 8393 $ */ public class EvictionTimerTask { private Log log = LogFactory.getLog(EvictionTimerTask.class); private RegionRegistry regionsRegistry; private static AtomicInteger tcount = new AtomicInteger(); private long wakeupInterval; ScheduledExecutorService scheduledExecutor; private Task task; public EvictionTimerTask() { task = new Task(); } public void init(long wakeupInterval, ThreadFactory evictionThreadFactory, RegionRegistry rr) { if (log.isTraceEnabled()) log.trace("Creating a new eviction listener with wakeupInterval millis set at " + wakeupInterval); this.regionsRegistry = rr; this.wakeupInterval = wakeupInterval; start(evictionThreadFactory); } public void stop() { if (log.isDebugEnabled()) log.debug("Stopping eviction timer"); if (scheduledExecutor != null) { scheduledExecutor.shutdownNow(); } scheduledExecutor = null; } private void start(ThreadFactory tf) { if (wakeupInterval < 1) { if (log.isInfoEnabled()) log.info("Wakeup Interval set to " + wakeupInterval + ". Not starting an eviction thread!"); return; } if (tf == null) tf = new ThreadFactory() { public Thread newThread(Runnable r) { Thread t = new Thread(r, "EvictionTimer-" + tcount.getAndIncrement()); t.setDaemon(true); return t; } }; scheduledExecutor = Executors.newSingleThreadScheduledExecutor(tf); scheduledExecutor.scheduleWithFixedDelay(task, wakeupInterval / 2, wakeupInterval, TimeUnit.MILLISECONDS); } private void processRegions() { if (log.isTraceEnabled()) log.trace("Processing eviction regions " + regionsRegistry.keySet()); for (Region region : regionsRegistry.values()) { if (region.getEvictionRegionConfig() != null) handleRegion(region); } } private void handleRegion(Region region) { try { region.processEvictionQueues(); } 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); } } public class Task implements Runnable { public void run() { try { // 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. processRegions(); } catch (Throwable t) { log.warn("Eviction task encountered an unexpected error", t); } } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/ElementSizePolicy.java0000644000175000017500000000333311111047320030411 0ustar moellermoeller/* * 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.eviction; /** * @author Daniel Huang * @version $Revison: $ * @deprecated see ElementSizeAlgorithm */ @Deprecated public class ElementSizePolicy extends BaseEvictionPolicy implements ModernizablePolicy { private ElementSizeAlgorithm algorithm; public ElementSizePolicy() { super(); algorithm = new ElementSizeAlgorithm(); } public EvictionAlgorithm getEvictionAlgorithm() { return this.algorithm; } public Class getEvictionConfigurationClass() { return ElementSizeConfiguration.class; } public Class modernizePolicy() { return ElementSizeAlgorithm.class; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/LFUPolicy.java0000644000175000017500000000334611111047320026617 0ustar moellermoeller/* * 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.eviction; /** * Least Frequently Used Eviction Policy. * * @author Daniel Huang - dhuang@jboss.org - 10/2005 * @version $Revision: 7168 $ * @deprecated see LFUAlgorithm */ @Deprecated public class LFUPolicy extends BaseEvictionPolicy implements ModernizablePolicy { private LFUAlgorithm algorithm; public LFUPolicy() { super(); algorithm = new LFUAlgorithm(); } public EvictionAlgorithm getEvictionAlgorithm() { return algorithm; } public Class getEvictionConfigurationClass() { return LFUConfiguration.class; } public Class modernizePolicy() { return LFUAlgorithm.class; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/LRUQueue.java0000644000175000017500000001276211111047320026462 0ustar moellermoeller/* * 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.eviction; import org.jboss.cache.Fqn; import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.Log; 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: 7168 $ */ public class LRUQueue implements EvictionQueue { private static Log log = LogFactory.getLog(LRUQueue.class); 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() { if (log.isTraceEnabled()) log.trace("LRUQUeue.size() = " + maxAgeQueue.size()); 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 iterator() { return lruQueue.values().iterator(); } protected final Iterator iterateMaxAgeQueue() { return maxAgeQueue.values().iterator(); } protected final Iterator iterateLRUQueue() { return lruQueue.values().iterator(); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/NodeEntry.java0000755000175000017500000001134311111047320026717 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/eviction/MRUQueue.java0000644000175000017500000001060211111047320026452 0ustar moellermoeller/* * 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.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: 7168 $ */ 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 iterator() { return list.iterator(); } @Override public String toString() { return list.toString(); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/NullEvictionAlgorithmConfig.java0000755000175000017500000000366311111047320032426 0ustar moellermoeller/* * 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.eviction; import org.jboss.cache.config.ConfigurationComponent; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.config.EvictionAlgorithmConfig; /** * Configuration class for {@link NullEvictionAlgorithm}. * * @author Manik Surtani * @since 3.0 */ public class NullEvictionAlgorithmConfig extends ConfigurationComponent implements EvictionAlgorithmConfig { private static final long serialVersionUID = -6591180473728241737L; /** * No-op */ public void reset() { // no-op } public String getEvictionAlgorithmClassName() { return NullEvictionAlgorithm.class.getName(); } /** * No-op */ public void validate() throws ConfigurationException { // no-op } public NullEvictionAlgorithmConfig clone() throws CloneNotSupportedException { return (NullEvictionAlgorithmConfig) super.clone(); } }jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/ExpirationConfiguration.java0000755000175000017500000000721711111047320031667 0ustar moellermoeller/* * 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.eviction; import org.jboss.cache.config.Dynamic; import org.jboss.cache.config.EvictionAlgorithmConfig; import java.util.concurrent.TimeUnit; /** * Configuration for indicating the Node key for setting a specific eviction time. * * @deprecated see {@link org.jboss.cache.eviction.ExpirationAlgorithmConfig} */ @Deprecated public class ExpirationConfiguration extends EvictionPolicyConfigBase implements ModernizableConfig { 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()); } public EvictionAlgorithmConfig modernizeConfig() { ExpirationAlgorithmConfig modernCfg = new ExpirationAlgorithmConfig(); modernCfg.setExpirationKeyName(getExpirationKeyName()); modernCfg.setTimeToLive(getTimeToLiveSeconds(), TimeUnit.SECONDS); modernCfg.setWarnNoExpirationKey(getWarnNoExpirationKey()); modernCfg.setMaxNodes(getMaxNodes()); modernCfg.setMinTimeToLive(getMinTimeToLiveSeconds(), TimeUnit.SECONDS); return modernCfg; } /** * 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-3.2.8.GA/src/main/java/org/jboss/cache/eviction/FIFOConfiguration.java0000644000175000017500000000620211240253605030266 0ustar moellermoeller/* * 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.eviction; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.config.EvictionAlgorithmConfig; import java.util.concurrent.TimeUnit; /** * 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: 8181 $ * @deprecated see {@link org.jboss.cache.eviction.FIFOAlgorithmConfig} */ @Deprecated public class FIFOConfiguration extends EvictionPolicyConfigBase implements ModernizableConfig { /** * The serialVersionUID */ private static final long serialVersionUID = -7229715009546277313L; public FIFOConfiguration() { super(); // We require that maxNodes is set setMaxNodes(-1); } public EvictionAlgorithmConfig modernizeConfig() { FIFOAlgorithmConfig modernCfg = new FIFOAlgorithmConfig(); modernCfg.setMaxNodes(getMaxNodes()); modernCfg.setMinTimeToLive(getMinTimeToLiveSeconds(), TimeUnit.SECONDS); return modernCfg; } /** * 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-3.2.8.GA/src/main/java/org/jboss/cache/eviction/ElementSizeConfiguration.java0000644000175000017500000001043411111047320031761 0ustar moellermoeller/* * 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.eviction; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.config.Dynamic; import org.jboss.cache.config.EvictionAlgorithmConfig; import java.util.concurrent.TimeUnit; /** * 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: 7168 $ * @deprecated see {@link org.jboss.cache.eviction.ElementSizeAlgorithmConfig} */ @Deprecated public class ElementSizeConfiguration extends EvictionPolicyConfigBase implements ModernizableConfig { /** * 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 EvictionAlgorithmConfig modernizeConfig() { ElementSizeAlgorithmConfig modernCfg = new ElementSizeAlgorithmConfig(); modernCfg.setMaxElementsPerNode(getMaxElementsPerNode()); modernCfg.setMaxNodes(getMaxNodes()); modernCfg.setMinTimeToLive(getMinTimeToLiveSeconds(), TimeUnit.SECONDS); return modernCfg; } 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 (obj instanceof ElementSizeConfiguration && 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-3.2.8.GA/src/main/java/org/jboss/cache/eviction/ExpirationAlgorithm.java0000755000175000017500000003106411433521426031016 0ustar moellermoeller/* * 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.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.commands.DataCommand; import org.jboss.cache.commands.write.PutDataMapCommand; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.config.EvictionAlgorithmConfig; import org.jboss.cache.eviction.EvictionEvent.Type; import javax.transaction.Status; import javax.transaction.SystemException; import javax.transaction.Transaction; import java.util.Iterator; import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.BlockingQueue; /** * 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 * ExpirationAlgorithmConfig#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 ExpirationAlgorithmConfig#getTimeToLive()} (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 ExpirationAlgorithmConfig#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 ExpirationAlgorithmConfig config; private SortedSet set; /** * Constructs a new algorithm with a policy. */ public ExpirationAlgorithm() { this.set = new TreeSet(); } private void addEvictionEntry(EvictionEvent node) { Fqn fqn = node.getFqn(); addEvictionEntry(fqn, node.getCommand(), node.getTransaction()); } private void addEvictionEntry(Fqn fqn, DataCommand command, Transaction tx) { Long l = getExpiration(fqn); if (l == null) { if (command != null) l = getExpirationFromCommand(command); if (l == null) return; } if (l == -1) { if (config.isWarnNoExpirationKey() && log.isWarnEnabled()) 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, tx); } } private Long getExpirationFromCommand(DataCommand command) { if (command instanceof PutKeyValueCommand) { PutKeyValueCommand putKeyCommand = (PutKeyValueCommand) command; if (putKeyCommand.getKey().equals(config.getExpirationKeyName())) { return (Long) putKeyCommand.getValue(); } } else if (command instanceof PutDataMapCommand) { PutDataMapCommand putDataCommand = (PutDataMapCommand) command; Object expiration = putDataCommand.getData().get(config.getExpirationKeyName()); if (expiration != null) { return (Long) expiration; } } return null; } private void setExpiration(Fqn fqn, Long l, Transaction tx) { ExpirationEntry ee = new ExpirationEntry(fqn, l, tx); if (trace) log.trace("adding eviction entry: " + ee); set.add(ee); } @SuppressWarnings("unchecked") private Long getExpiration(Fqn fqn) { NodeSPI n = cache.peek(fqn, false); if (n == null) return null; Long expiration = (Long) n.getDirect(config.getExpirationKeyName()); if ( expiration == null ) return -1L; return expiration; } @Override protected void processQueues(BlockingQueue queue) throws EvictionException { EvictionEvent node; int count = 0; while ((node = getNextInQueue(queue)) != 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: " + regionFqn); } private void markInUse(EvictionEvent node) { long expiration = node.getInUseTimeout() + System.currentTimeMillis(); setExpiration(node.getFqn(), expiration, node.getTransaction()); } @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(); if (trace) log.trace("attempt to prune: " + ee); Long ce = getExpiration(ee.getFqn()); if (ce == null) { if (ee.getTransaction() != null && isTransactionActive(ee.getTransaction())) { if (trace) log.trace("transaction active, keep eviction entry: " + ee); continue; } else { // Expiration now older i.remove(); continue; } } if (ce == -1 || ce > ee.getExpiration()) { // Expiration now older i.remove(); continue; } if (ee.getExpiration() < now || (max != -1 && set.size() > max)) { i.remove(); evictCacheNode(ee.getFqn()); } else { break; } } if (max != -1 && set.size() > max ) log.warn("Unable to remove nodes to reduce region size below " + config.getMaxNodes() + ". " + "Set expiration for nodes in this region"); } private boolean isTransactionActive(Transaction transaction) { try { switch (transaction.getStatus()) { case Status.STATUS_UNKNOWN: case Status.STATUS_ROLLEDBACK: case Status.STATUS_COMMITTED: case Status.STATUS_NO_TRANSACTION: return false; default: return true; } } catch (SystemException e) { return false; } } @Override public void resetEvictionQueue() { for (ExpirationEntry ee : set) { addEvictionEntry(ee.getFqn(), null, null); } } @Override protected EvictionQueue setupEvictionQueue() throws EvictionException { this.config = (ExpirationAlgorithmConfig) evictionAlgorithmConfig; return new DummyEvictionQueue(); } @Override protected boolean shouldEvictNode(NodeEntry ne) { throw new UnsupportedOperationException(); } @Override public boolean canIgnoreEvent(Type eventType) { return (eventType == EvictionEvent.Type.VISIT_NODE_EVENT); } public Class getConfigurationClass() { return ExpirationAlgorithmConfig.class; } /** * 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; private Transaction transaction; public ExpirationEntry(Fqn fqn) { this.fqn = fqn; } public ExpirationEntry(Fqn fqn, long expiration, Transaction transaction) { this.fqn = fqn; this.expiration = expiration; this.transaction = transaction; } /** * 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; } public Transaction getTransaction() { return transaction; } @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 iterator() { return null; } public void modifyElementCount(int difference) { } public void removeNodeEntry(NodeEntry entry) { throw new UnsupportedOperationException(); } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/FIFOAlgorithm.java0000755000175000017500000000507211357175221027422 0ustar moellermoeller/* * 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.eviction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.config.EvictionAlgorithmConfig; /** * First-in-first-out algorithm used to evict nodes. * * @author Daniel Huang - dhuang@jboss.org * @author Morten Kvistgaard * @version $Revision: 8363 $ */ 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() 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; FIFOAlgorithmConfig config = (FIFOAlgorithmConfig) evictionAlgorithmConfig; if (trace) { log.trace("Deciding whether node in queue " + ne.getFqn() + " requires eviction."); } int size = this.getEvictionQueue().getNumberOfNodes(); return config.getMaxNodes() != -1 && size > config.getMaxNodes(); } public Class getConfigurationClass() { return FIFOAlgorithmConfig.class; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/LRUConfiguration.java0000644000175000017500000001147611111047320030206 0ustar moellermoeller/* * 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.eviction; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.config.Dynamic; import org.jboss.cache.config.EvictionAlgorithmConfig; import java.util.concurrent.TimeUnit; /** * Configuration implementation for {@link LRUPolicy}. *

* If configured via XML, expects the following: *

*

 * 
 *    10000
 *    8
 *    10
 * 
 * 
* * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 7168 $ * @deprecated see {@link org.jboss.cache.eviction.LRUAlgorithmConfig} */ @Deprecated public class LRUConfiguration extends EvictionPolicyConfigBase implements ModernizableConfig { /** * 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); } public EvictionAlgorithmConfig modernizeConfig() { LRUAlgorithmConfig modernCfg = new LRUAlgorithmConfig(); modernCfg.setMaxNodes(getMaxNodes()); modernCfg.setMinTimeToLive(getMinTimeToLiveSeconds(), TimeUnit.SECONDS); modernCfg.setTimeToLive(getTimeToLiveSeconds(), TimeUnit.SECONDS); modernCfg.setMaxAge(getMaxAgeSeconds(), TimeUnit.SECONDS); return modernCfg; } @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 timeToLive value or ConfigurationException * is thrown. */ @Override public void validate() throws ConfigurationException { if (timeToLiveSeconds < 0) { throw new ConfigurationException("timeToLive must be " + "configured to a value greater than or equal to 0 for " + getEvictionPolicyClass()); } } public String toString() { return "LRUConfiguration{" + "timeToLiveSeconds=" + timeToLiveSeconds + ", timeToLiveSeconds=" + timeToLiveSeconds + ", maxAgeSeconds=" + maxAgeSeconds + '}'; } public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof LRUConfiguration)) return false; if (!super.equals(o)) return false; LRUConfiguration that = (LRUConfiguration) o; if (maxAgeSeconds != that.maxAgeSeconds) return false; if (maxAgeSeconds != that.maxAgeSeconds) return false; if (timeToLiveSeconds != that.timeToLiveSeconds) return false; if (timeToLiveSeconds != that.timeToLiveSeconds) return false; return true; } public int hashCode() { int result = super.hashCode(); result = 31 * result + timeToLiveSeconds; result = 31 * result + maxAgeSeconds; result = 31 * result + (timeToLiveSeconds ^ (timeToLiveSeconds >>> 7)); result = 31 * result + (maxAgeSeconds ^ (maxAgeSeconds >>> 7)); return result; } @Override public void reset() { super.reset(); setTimeToLiveSeconds(-1); } @Override public LRUConfiguration clone() throws CloneNotSupportedException { return (LRUConfiguration) super.clone(); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/ElementSizeAlgorithm.java0000644000175000017500000000445711111047320031110 0ustar moellermoeller/* * 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.eviction; import org.jboss.cache.config.EvictionAlgorithmConfig; /** * @author Daniel Huang * @version $Revision: 7168 $ */ public class ElementSizeAlgorithm extends BaseSortedEvictionAlgorithm { @Override protected EvictionQueue setupEvictionQueue() 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; int size = this.getEvictionQueue().getNumberOfNodes(); ElementSizeAlgorithmConfig config = (ElementSizeAlgorithmConfig) evictionAlgorithmConfig; return config.getMaxNodes() > -1 && 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(); } public Class getConfigurationClass() { return ElementSizeAlgorithmConfig.class; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/eviction/MRUConfiguration.java0000644000175000017500000000607211240253605030213 0ustar moellermoeller/* * 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.eviction; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.config.EvictionAlgorithmConfig; import java.util.concurrent.TimeUnit; /** * 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: 8181 $ * @deprecated see {@link org.jboss.cache.eviction.MRUAlgorithmConfig} */ @Deprecated public class MRUConfiguration extends EvictionPolicyConfigBase implements ModernizableConfig { /** * 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()); } public EvictionAlgorithmConfig modernizeConfig() { MRUAlgorithmConfig modernCfg = new MRUAlgorithmConfig(); modernCfg.setMaxNodes(getMaxNodes()); modernCfg.setMinTimeToLive(getMinTimeToLiveSeconds(), TimeUnit.SECONDS); return modernCfg; } /** * 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-3.2.8.GA/src/main/java/org/jboss/cache/cluster/0000755000175000017500000000000011675221706024022 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/cluster/ReplicationQueue.java0000644000175000017500000001313611111047320030126 0ustar moellermoeller/* * 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.cluster; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.RPCManager; import org.jboss.cache.commands.CommandsFactory; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.remote.ReplicateCommand; import org.jboss.cache.config.Configuration; 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.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** * Periodically (or when certain size is exceeded) takes elements and replicates them. * * @author Bela Ban May 24, 2003 * @version $Revision: 7168 $ */ public class ReplicationQueue { private static final Log log = LogFactory.getLog(ReplicationQueue.class); /** * Max elements before we flush */ private long max_elements = 500; /** * Holds the replication jobs: LinkedList */ final List elements = new LinkedList(); /** * For periodical replication */ private ScheduledExecutorService scheduledExecutor = null; private RPCManager rpcManager; private Configuration configuration; private boolean enabled; private CommandsFactory commandsFactory; private static final AtomicInteger counter = new AtomicInteger(0); public boolean isEnabled() { return enabled; } @Inject 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() { long interval = configuration.getReplQueueInterval(); this.max_elements = configuration.getReplQueueMaxElements(); // check again enabled = configuration.isUseReplQueue() && (configuration.getBuddyReplicationConfig() == null || !configuration.getBuddyReplicationConfig().isEnabled()); if (enabled && interval > 0 && scheduledExecutor == null) { scheduledExecutor = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() { public Thread newThread(Runnable r) { return new Thread(r, "ReplicationQueue-periodicProcessor-" + counter.getAndIncrement()); } }); scheduledExecutor.scheduleWithFixedDelay(new Runnable() { public void run() { flush(); } }, 500l, interval, TimeUnit.MILLISECONDS); } } /** * Stops the asynchronous flush queue. */ @Stop public synchronized void stop() { if (scheduledExecutor != null) { scheduledExecutor.shutdownNow(); } scheduledExecutor = 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); } } } }jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/marshall/0000755000175000017500000000000011675222111024133 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/marshall/NodeDataExceptionMarker.java0000644000175000017500000000434211111047320031471 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/marshall/CacheMarshaller200.java0000644000175000017500000011011611155055621030241 0ustar moellermoeller/* * 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.marshall; import org.jboss.cache.CacheException; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.Region.Status; import org.jboss.cache.buddyreplication.GravitateResult; import org.jboss.cache.commands.CommandsFactory; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.WriteCommand; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.optimistic.DefaultDataVersion; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.TransactionLog.LogEntry; import org.jboss.cache.util.FastCopyHashMap; import org.jboss.cache.util.Immutables; 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.*; /** * An enhanced marshaller for RPC calls between CacheImpl instances. * * @author Manik Surtani (manik AT jboss DOT 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_IMMUTABLE_MAPCOPY = 21; protected static final int MAGICNUMBER_MARSHALLEDVALUE = 22; protected static final int MAGICNUMBER_FASTCOPY_HASHMAP = 23; protected static final int MAGICNUMBER_ARRAY = 24; protected static final int MAGICNUMBER_BYTE = 25; protected static final int MAGICNUMBER_CHAR = 26; protected static final int MAGICNUMBER_FLOAT = 27; protected static final int MAGICNUMBER_DOUBLE = 28; protected static final int MAGICNUMBER_OBJECT = 29; protected static final int MAGICNUMBER_TXLOG_ENTRY = 50; protected static final int MAGICNUMBER_REQUEST_IGNORED_RESPONSE = 51; protected static final int MAGICNUMBER_EXTENDED_RESPONSE = 52; 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); } catch (Exception th) { if(log.isErrorEnabled()) log.error("Error while marshalling object: " + o, th); throw th; } finally { if (log.isTraceEnabled()) log.trace("Done serializing object: " + o); 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 @SuppressWarnings("deprecation") 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 LogEntry) { out.writeByte(MAGICNUMBER_TXLOG_ENTRY); marshallLogEntry((LogEntry)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(FastCopyHashMap.class)) { out.writeByte(MAGICNUMBER_FASTCOPY_HASHMAP); marshallMap((Map) o, out, refMap); } else if (o instanceof Map && Immutables.isImmutable(o)) { out.writeByte(MAGICNUMBER_IMMUTABLE_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 RequestIgnoredResponse) { out.writeByte(MAGICNUMBER_REQUEST_IGNORED_RESPONSE); } else if (o instanceof ExtendedResponse) { out.writeByte(MAGICNUMBER_EXTENDED_RESPONSE); marshallExtendedResponse((ExtendedResponse)o, out, refMap); } else if (o instanceof Serializable) { if (trace) { log.trace("Not optimum: 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 marshallExtendedResponse(ExtendedResponse response, ObjectOutputStream out, Map refMap) throws Exception { out.writeBoolean(response.isReplayIgnoredRequests()); marshallObject(response.getResponse(), out, refMap); } private void marshallLogEntry(LogEntry log, ObjectOutputStream out, Map refMap) throws Exception { marshallObject(log.getTransaction(), out, refMap); List mods = log.getModifications(); boolean isList = mods.size() > 1; out.writeBoolean(isList); if (isList) marshallObject(log.getModifications(), out, refMap); else marshallObject(mods.get(0), out, refMap); } 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); } @SuppressWarnings("unchecked") private void marshallCollection(Collection c, ObjectOutputStream out, Map refMap) throws Exception { writeUnsignedInt(out, c.size()); for (Object o : c) { marshallObject(o, out, refMap); } } @SuppressWarnings("unchecked") 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_TXLOG_ENTRY: return unmarshallLogEntry(in, refMap); case MAGICNUMBER_IPADDRESS: retVal = unmarshallIpAddress(in); return retVal; case MAGICNUMBER_DEFAULT_DATA_VERSION: retVal = unmarshallDefaultDataVersion(in); return retVal; case MAGICNUMBER_ARRAY: return unmarshallArray(in, refMap); 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_IMMUTABLE_MAPCOPY: return unmarshallMapCopy(in, refMap); case MAGICNUMBER_FASTCOPY_HASHMAP: return unmarshallFastCopyHashMap(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); case MAGICNUMBER_REQUEST_IGNORED_RESPONSE: return new RequestIgnoredResponse(); case MAGICNUMBER_EXTENDED_RESPONSE: return unmarshallExtendedResponse(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 ExtendedResponse unmarshallExtendedResponse(ObjectInputStream in, UnmarshalledReferences refMap) throws Exception { boolean replayIgnoredRequests = in.readBoolean(); ExtendedResponse response = new ExtendedResponse(unmarshallObject(in, refMap)); response.setReplayIgnoredRequests(replayIgnoredRequests); return response; } @SuppressWarnings("unchecked") private LogEntry unmarshallLogEntry(ObjectInputStream in, UnmarshalledReferences refMap) throws Exception { GlobalTransaction gtx = (GlobalTransaction)unmarshallObject(in, refMap); boolean isList = in.readBoolean(); List mods; if (isList) mods = (List)unmarshallObject(in, refMap); else mods = Collections.singletonList(unmarshallObject(in, refMap)); return new LogEntry(gtx, mods); } private FastCopyHashMap unmarshallFastCopyHashMap(ObjectInputStream in, UnmarshalledReferences refMap) throws Exception { FastCopyHashMap map = new FastCopyHashMap(); populateFromStream(in, refMap, map); return map; } @SuppressWarnings("unchecked") 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; } @SuppressWarnings("unchecked") private Map unmarshallMapCopy(ObjectInputStream in, UnmarshalledReferences refMap) throws Exception { // read in as a HashMap first Map m = unmarshallHashMap(in, refMap); return Immutables.immutableMapWrap(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; } @SuppressWarnings("unchecked") 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)); } @SuppressWarnings("unchecked") 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)); } @SuppressWarnings("unchecked") 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)); } @SuppressWarnings("deprecation") protected void marshallDefaultDataVersion(DefaultDataVersion ddv, ObjectOutputStream out) throws Exception { writeUnsignedLong(out, ddv.getRawVersion()); } @SuppressWarnings("deprecation") 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); } protected Object unmarshallArray(ObjectInputStream in, UnmarshalledReferences refs) throws Exception { int sz = readUnsignedInt(in); byte type = in.readByte(); switch (type) { case MAGICNUMBER_BOOLEAN: { boolean isPrim = in.readBoolean(); if (isPrim) { boolean[] a = new boolean[sz]; for (int i = 0; i < sz; i++) a[i] = in.readBoolean(); return a; } else { Boolean[] a = new Boolean[sz]; for (int i = 0; i < sz; i++) a[i] = in.readBoolean(); return a; } } case MAGICNUMBER_INTEGER: { boolean isPrim = in.readBoolean(); if (isPrim) { int[] a = new int[sz]; for (int i = 0; i < sz; i++) a[i] = in.readInt(); return a; } else { Integer[] a = new Integer[sz]; for (int i = 0; i < sz; i++) a[i] = in.readInt(); return a; } } case MAGICNUMBER_LONG: { boolean isPrim = in.readBoolean(); if (isPrim) { long[] a = new long[sz]; for (int i = 0; i < sz; i++) a[i] = in.readLong(); return a; } else { Long[] a = new Long[sz]; for (int i = 0; i < sz; i++) a[i] = in.readLong(); return a; } } case MAGICNUMBER_CHAR: { boolean isPrim = in.readBoolean(); if (isPrim) { char[] a = new char[sz]; for (int i = 0; i < sz; i++) a[i] = in.readChar(); return a; } else { Character[] a = new Character[sz]; for (int i = 0; i < sz; i++) a[i] = in.readChar(); return a; } } case MAGICNUMBER_BYTE: { boolean isPrim = in.readBoolean(); if (isPrim) { byte[] a = new byte[sz]; int bsize = 10240; int offset = 0; int bytesLeft = sz; while (bytesLeft > 0) { int read = in.read(a, offset, Math.min(bsize, bytesLeft)); offset += read; bytesLeft -= read; } return a; } else { Byte[] a = new Byte[sz]; for (int i = 0; i < sz; i++) a[i] = in.readByte(); return a; } } case MAGICNUMBER_SHORT: { boolean isPrim = in.readBoolean(); if (isPrim) { short[] a = new short[sz]; for (int i = 0; i < sz; i++) a[i] = in.readShort(); return a; } else { Short[] a = new Short[sz]; for (int i = 0; i < sz; i++) a[i] = in.readShort(); return a; } } case MAGICNUMBER_FLOAT: { boolean isPrim = in.readBoolean(); if (isPrim) { float[] a = new float[sz]; for (int i = 0; i < sz; i++) a[i] = in.readFloat(); return a; } else { Float[] a = new Float[sz]; for (int i = 0; i < sz; i++) a[i] = in.readFloat(); return a; } } case MAGICNUMBER_DOUBLE: { boolean isPrim = in.readBoolean(); if (isPrim) { double[] a = new double[sz]; for (int i = 0; i < sz; i++) a[i] = in.readDouble(); return a; } else { Double[] a = new Double[sz]; for (int i = 0; i < sz; i++) a[i] = in.readDouble(); return a; } } case MAGICNUMBER_OBJECT: { Object[] a = new Object[sz]; for (int i = 0; i < sz; i++) a[i] = unmarshallObject(in, refs); return a; } default: throw new CacheException("Unknown array type"); } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/marshall/CacheMarshaller210.java0000644000175000017500000001155011111047320030231 0ustar moellermoeller/* * 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.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 its 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-3.2.8.GA/src/main/java/org/jboss/cache/marshall/CacheMarshaller300.java0000644000175000017500000001322011111047320030225 0ustar moellermoeller/* * 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.marshall; import org.jboss.cache.CacheException; import java.io.ObjectOutputStream; import java.lang.reflect.Array; import java.util.Map; /** * Adds special treatment of arrays over and above the superclass. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ public class CacheMarshaller300 extends CacheMarshaller210 { @Override protected void marshallObject(Object o, ObjectOutputStream out, Map refMap) throws Exception { if (o != null && o.getClass().isArray() && isKnownType(o.getClass().getComponentType())) { marshallArray(o, out, refMap); } else { super.marshallObject(o, out, refMap); } } protected boolean isKnownType(Class c) { return (c.equals(Object.class) || c.isPrimitive() || c.equals(Character.class) || c.equals(Integer.class) || c.equals(Long.class) || c.equals(Byte.class) || c.equals(Boolean.class) || c.equals(Short.class) || c.equals(Float.class) || c.equals(Double.class)); } protected void marshallArray(Object o, ObjectOutputStream out, Map refMap) throws Exception { out.writeByte(MAGICNUMBER_ARRAY); Class arrayTypeClass = o.getClass().getComponentType(); int sz = Array.getLength(o); writeUnsignedInt(out, sz); boolean isPrim = arrayTypeClass.isPrimitive(); if (!isPrim && arrayTypeClass.equals(Object.class)) { out.writeByte(MAGICNUMBER_OBJECT); for (int i = 0; i < sz; i++) marshallObject(Array.get(o, i), out, refMap); } else if (arrayTypeClass.equals(byte.class) || arrayTypeClass.equals(Byte.class)) { out.writeByte(MAGICNUMBER_BYTE); out.writeBoolean(isPrim); if (isPrim) out.write((byte[]) o); else for (int i = 0; i < sz; i++) out.writeByte((Byte) Array.get(o, i)); } else if (arrayTypeClass.equals(int.class) || arrayTypeClass.equals(Integer.class)) { out.writeByte(MAGICNUMBER_INTEGER); out.writeBoolean(isPrim); if (isPrim) for (int i = 0; i < sz; i++) out.writeInt(Array.getInt(o, i)); else for (int i = 0; i < sz; i++) out.writeInt((Integer) Array.get(o, i)); } else if (arrayTypeClass.equals(long.class) || arrayTypeClass.equals(Long.class)) { out.writeByte(MAGICNUMBER_LONG); out.writeBoolean(isPrim); if (isPrim) for (int i = 0; i < sz; i++) out.writeLong(Array.getLong(o, i)); else for (int i = 0; i < sz; i++) out.writeLong((Long) Array.get(o, i)); } else if (arrayTypeClass.equals(boolean.class) || arrayTypeClass.equals(Boolean.class)) { out.writeByte(MAGICNUMBER_BOOLEAN); out.writeBoolean(isPrim); if (isPrim) for (int i = 0; i < sz; i++) out.writeBoolean(Array.getBoolean(o, i)); else for (int i = 0; i < sz; i++) out.writeBoolean((Boolean) Array.get(o, i)); } else if (arrayTypeClass.equals(char.class) || arrayTypeClass.equals(Character.class)) { out.writeByte(MAGICNUMBER_CHAR); out.writeBoolean(isPrim); if (isPrim) for (int i = 0; i < sz; i++) out.writeChar(Array.getChar(o, i)); else for (int i = 0; i < sz; i++) out.writeChar((Character) Array.get(o, i)); } else if (arrayTypeClass.equals(short.class) || arrayTypeClass.equals(Short.class)) { out.writeByte(MAGICNUMBER_SHORT); out.writeBoolean(isPrim); if (isPrim) for (int i = 0; i < sz; i++) out.writeShort(Array.getShort(o, i)); else for (int i = 0; i < sz; i++) out.writeShort((Short) Array.get(o, i)); } else if (arrayTypeClass.equals(float.class) || arrayTypeClass.equals(Float.class)) { out.writeByte(MAGICNUMBER_FLOAT); out.writeBoolean(isPrim); if (isPrim) for (int i = 0; i < sz; i++) out.writeFloat(Array.getFloat(o, i)); else for (int i = 0; i < sz; i++) out.writeFloat((Float) Array.get(o, i)); } else if (arrayTypeClass.equals(double.class) || arrayTypeClass.equals(Double.class)) { out.writeByte(MAGICNUMBER_DOUBLE); out.writeBoolean(isPrim); if (isPrim) for (int i = 0; i < sz; i++) out.writeDouble(Array.getDouble(o, i)); else for (int i = 0; i < sz; i++) out.writeDouble((Double) Array.get(o, i)); } else throw new CacheException("Unknown array type!"); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/marshall/MarshallUtil.java0000644000175000017500000000634511111047320027377 0ustar moellermoeller/* * 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.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: 7168 $ */ 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(); } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/marshall/InactiveRegionAwareRpcDispatcher.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/marshall/InactiveRegionAwareRpcDispatcher.jav0000644000175000017500000000746111240253605033207 0ustar moellermoeller/* * 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.marshall; import org.jboss.cache.RPCManager; 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, RPCManager manager) { super(channel, l, l2, serverObj, container, interceptorChain, componentRegistry, manager); } @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-3.2.8.GA/src/main/java/org/jboss/cache/marshall/MarshalledValueMap.java0000644000175000017500000001106311111047320030476 0ustar moellermoeller/* * 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.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 AT jboss DOT 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-3.2.8.GA/src/main/java/org/jboss/cache/marshall/RegionalizedReturnValue.java0000644000175000017500000000352111111047320031600 0ustar moellermoeller/* * 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.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 AT jboss DOT 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-3.2.8.GA/src/main/java/org/jboss/cache/marshall/RegionalizedMethodCall.java0000644000175000017500000000350511111047320031342 0ustar moellermoeller/* * 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.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 AT jboss DOT org) * @since 2.1.1 */ public class RegionalizedMethodCall { public ReplicableCommand command; public Fqn region; } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/marshall/MarshalledValue.java0000644000175000017500000001737511111047320030054 0ustar moellermoeller/* * 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.marshall; import org.jboss.cache.CacheException; import org.jboss.util.stream.MarshalledValueInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.Externalizable; import java.io.IOException; import java.io.NotSerializableException; import java.io.ObjectInput; import java.io.ObjectInputStream; import java.io.ObjectOutput; import java.io.ObjectOutputStream; import java.io.Serializable; 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 AT jboss DOT 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-3.2.8.GA/src/main/java/org/jboss/cache/marshall/MarshalledValueHelper.java0000644000175000017500000000503411111047320031201 0ustar moellermoeller/* * 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.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 AT jboss DOT 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-3.2.8.GA/src/main/java/org/jboss/cache/marshall/CommandAwareRpcDispatcher.java0000644000175000017500000004031611450761557032031 0ustar moellermoeller/* * 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.marshall; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.InvocationContext; import org.jboss.cache.RPCManager; import org.jboss.cache.RPCManagerImpl.FlushTracker; 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.StateTransferControlCommand; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.interceptors.InterceptorChain; import org.jboss.cache.invocation.InvocationContextContainer; import org.jboss.cache.invocation.CacheNotReadyException; import org.jboss.cache.util.concurrent.BoundedExecutors; import org.jboss.cache.util.concurrent.WithinThreadExecutor; 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.Buffer; import org.jgroups.util.Rsp; import org.jgroups.util.RspList; import java.io.NotSerializableException; import java.util.Map; import java.util.Vector; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** * A JGroups RPC dispatcher that knows how to deal with {@link org.jboss.cache.commands.ReplicableCommand}s. * * @author Manik Surtani (manik AT jboss DOT org) * @since 2.2.0 */ public class CommandAwareRpcDispatcher extends RpcDispatcher { protected InvocationContextContainer invocationContextContainer; protected InterceptorChain interceptorChain; protected ComponentRegistry componentRegistry; protected boolean trace; private ExecutorService replicationProcessor; private AtomicInteger replicationProcessorCount; private boolean asyncSerial; private Configuration configuration; private RPCManager rpcManager; private ReplicationObserver replicationObserver; protected Log log = LogFactory.getLog(CommandAwareRpcDispatcher.class); public CommandAwareRpcDispatcher() {} public CommandAwareRpcDispatcher(Channel channel, MessageListener l, MembershipListener l2, Object serverObj, InvocationContextContainer container, InterceptorChain interceptorChain, ComponentRegistry componentRegistry, RPCManager manager) { super(channel, l, l2, serverObj); this.invocationContextContainer = container; this.componentRegistry = componentRegistry; this.interceptorChain = interceptorChain; this.rpcManager = manager; trace = log.isTraceEnabled(); // what sort of a repl processor do we need? Configuration c = componentRegistry.getComponent(Configuration.class); this.configuration = c; replicationProcessor = c.getRuntimeConfig().getAsyncSerializationExecutor(); if (c.getCacheMode().isSynchronous() || (replicationProcessor == null && c.getSerializationExecutorPoolSize() < 1) || requireSyncMarshalling(c)) // if an executor has not been injected and the pool size is set { // in-process thread. Not async. replicationProcessor = new WithinThreadExecutor(); asyncSerial = false; } else { asyncSerial = true; if (replicationProcessor == null) { replicationProcessorCount = new AtomicInteger(0); replicationProcessor = BoundedExecutors.newFixedThreadPool(c.isUseReplQueue() ? 1 : c.getSerializationExecutorPoolSize(), new ThreadFactory() { public Thread newThread(Runnable r) { return new Thread(r, "AsyncReplicationProcessor-" + replicationProcessorCount.incrementAndGet()); } }, c.getSerializationExecutorQueueSize() ); } } } public ReplicationObserver setReplicationObserver(ReplicationObserver replicationObserver) { ReplicationObserver result = this.replicationObserver; this.replicationObserver = replicationObserver; return result; } /** * Serial(sync) marshalling should be enabled for async optimistic caches. That is because optimistic async is a 2PC, * which might cause the Commit command to be send before the Prepare command, so replication will fail. This is not * the same for async pessimistic/mvcc replication, as this uses a 1PC. */ private boolean requireSyncMarshalling(Configuration c) { boolean enforceSerialMarshalling = c.getNodeLockingScheme().equals(Configuration.NodeLockingScheme.OPTIMISTIC) && !c.getCacheMode().isInvalidation(); if (enforceSerialMarshalling) { if (c.getSerializationExecutorPoolSize() > 1 && log.isWarnEnabled()) { log.warn("Async optimistic caches do not support serialization pools."); } if (trace) log.trace("Disbaling serial marshalling for opt async cache"); } return enforceSerialMarshalling; } /* JBCACHE-1590 - method can't be named "stop" because that's already * used by the parent class for its own purposes. */ public void stopDispatcher() { replicationProcessor.shutdownNow(); try { replicationProcessor.awaitTermination(60, TimeUnit.SECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } 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 anycasting, boolean oob, RspFilter filter) throws NotSerializableException, ExecutionException, InterruptedException { 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)); boolean supportReplay = configuration.isNonBlockingStateTransfer(); ReplicationTask replicationTask = new ReplicationTask(command, oob, dests, mode, timeout, anycasting, supportReplay, filter); Future response = replicationProcessor.submit(replicationTask); if (asyncSerial) { // don't care about the response. return. return null; } else { RspList retval = response.get(); if (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 { ReplicableCommand command = (ReplicableCommand) req_marshaller.objectFromByteBuffer(req.getBuffer(), req.getOffset(), req.getLength()); Object execResult = executeCommand(command, req); if (log.isTraceEnabled()) log.trace("Command : " + command + " executed, result is: " + execResult); return execResult; } 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 { boolean unlock = false; FlushTracker flushTracker = rpcManager.getFlushTracker(); try { if (cmd == null) throw new NullPointerException("Unable to execute a null command! Message was " + req); if (trace) log.trace("Executing command: " + cmd + " [sender=" + req.getSrc() + "]"); boolean replayIgnored = false; if (configuration.isNonBlockingStateTransfer() && !(cmd instanceof StateTransferControlCommand)) { int flushCount = flushTracker.getFlushCompletionCount(); flushTracker.lockProcessingLock(); unlock = true; flushTracker.waitForFlushCompletion(configuration.getStateRetrievalTimeout()); // If this thread blocked during a NBST flush, then inform the sender // it needs to replay ignored messages replayIgnored = flushTracker.getFlushCompletionCount() != flushCount; } Object ret; if (cmd instanceof VisitableCommand) { InvocationContext ctx = invocationContextContainer.get(); ctx.setOriginLocal(false); if (!componentRegistry.invocationsAllowed(false)) { return new RequestIgnoredResponse(); } try { ret = interceptorChain.invoke(ctx, (VisitableCommand) cmd); } catch (CacheNotReadyException cnre) { // this could happen, even though we check the state earlier on. There is still a window // for the cache to be shut down in the meanwhile. if (log.isDebugEnabled()) log.debug("Cache not in a state to respond!", cnre); return new RequestIgnoredResponse(); } } 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 (requiresRunningCache(cmd) && !componentRegistry.invocationsAllowed(false)) { return new RequestIgnoredResponse(); } ret = cmd.perform(null); } if (replayIgnored) { ExtendedResponse extended = new ExtendedResponse(ret); extended.setReplayIgnoredRequests(true); ret = extended; } return ret; } finally { if (replicationObserver != null) replicationObserver.afterExecutingCommand(cmd); if (unlock) flushTracker.unlockProcessingLock(); } } private boolean requiresRunningCache(ReplicableCommand cmd) { return !(cmd instanceof AnnounceBuddyPoolNameCommand || cmd instanceof AssignToBuddyGroupCommand || cmd instanceof RemoveFromBuddyGroupCommand || cmd instanceof StateTransferControlCommand); } @Override public String toString() { return getClass().getSimpleName() + "[Outgoing marshaller: " + req_marshaller + "; incoming marshaller: " + rsp_marshaller + "]"; } private class ReplicationTask implements Callable { private ReplicableCommand command; private boolean oob; private Vector
dests; private int mode; private long timeout; private boolean anycasting; private boolean supportReplay; private RspFilter filter; private ReplicationTask(ReplicableCommand command, boolean oob, Vector
dests, int mode, long timeout, boolean anycasting, boolean supportReplay, RspFilter filter) { this.command = command; this.oob = oob; this.dests = dests; this.mode = mode; this.timeout = timeout; this.anycasting = anycasting; this.supportReplay = supportReplay; this.filter = filter; } public RspList call() throws Exception { Buffer buf; try { buf = req_marshaller.objectToBuffer(command); } catch (Exception e) { if (log.isErrorEnabled()) log.error(e); throw new RuntimeException("Failure to marshal argument(s)", e); } Message msg = new Message(); msg.setBuffer(buf); if (oob) msg.setFlag(Message.OOB); // Replay capability requires responses from all members! int mode = supportReplay ? GroupRequest.GET_ALL : this.mode; RspList retval = castMessage(dests, msg, mode, timeout, anycasting, filter); if (trace) 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 (supportReplay) { boolean replay = false; Vector
ignorers = new Vector
(); for (Map.Entry entry : retval.entrySet()) { Object value = entry.getValue().getValue(); if (value instanceof RequestIgnoredResponse) { ignorers.add(entry.getKey()); } else if (value instanceof ExtendedResponse) { ExtendedResponse extended = (ExtendedResponse) value; replay |= extended.isReplayIgnoredRequests(); entry.getValue().setValue(extended.getResponse()); } } if (replay && ignorers.size() > 0) { if (trace) log.trace("Replaying message to ignoring senders: " + ignorers); RspList responses = castMessage(ignorers, msg, GroupRequest.GET_ALL, timeout, anycasting, filter); if (responses != null) retval.putAll(responses); } } return retval; } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/marshall/MethodCall.java0000644000175000017500000001040311111047320027000 0ustar moellermoeller/* * 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.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 AT jboss DOT 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-3.2.8.GA/src/main/java/org/jboss/cache/marshall/AbstractMarshaller.java0000644000175000017500000002046311157534206030567 0ustar moellermoeller/* * 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.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.WriteCommand; 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.remote.StateTransferControlCommand; 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 AT jboss DOT 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 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; } 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 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!"); } 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: case StateTransferControlCommand.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-3.2.8.GA/src/main/java/org/jboss/cache/marshall/ExtendedResponse.java0000644000175000017500000000310011144464602030253 0ustar moellermoeller/* * 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.marshall; /** * A response with extended information * * @author Jason T. Greene */ public class ExtendedResponse { private boolean replayIgnoredRequests; private final Object response; public ExtendedResponse(Object response) { this.response = response; } public boolean isReplayIgnoredRequests() { return replayIgnoredRequests; } public void setReplayIgnoredRequests(boolean replayIgnoredRequests) { this.replayIgnoredRequests = replayIgnoredRequests; } public Object getResponse() { return response; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/marshall/NodeData.java0000644000175000017500000000763611111047320026461 0ustar moellermoeller/* * 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.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.HashMap; import java.util.Map; /** * Serializable representation of the data of a node (FQN and attributes) * * @author Bela Ban * @version $Id: NodeData.java 7168 2008-11-19 17:37:20Z jason.greene@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, boolean mapSafe) { this.fqn = fqn; if (mapSafe || attrs == null) this.attrs = attrs; else this.attrs = new HashMap(attrs); } public NodeData(String fqn, Map attrs, boolean mapSafe) { this(Fqn.fromString(fqn), attrs, mapSafe); } 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-3.2.8.GA/src/main/java/org/jboss/cache/marshall/RegionNotFoundException.java0000644000175000017500000000274511111047320031555 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/marshall/RequestIgnoredResponse.java0000644000175000017500000000217711144464602031470 0ustar moellermoeller/* * 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.marshall; /** * Indicates that the request was ignored, * * @author Jason T. Greene */ public class RequestIgnoredResponse { } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/marshall/MarshallingException.java0000644000175000017500000000276011111047320031113 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/marshall/VersionAwareMarshaller.java0000644000175000017500000002742411236525737031444 0ustar moellermoeller/* * 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.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 AT jboss DOT 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 VERSION_300 = 30; private static final int VERSION_310 = 31; private static final int VERSION_320 = 32; 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: marshaller = marshallers.get(VERSION_210); if (marshaller == null) { am = new CacheMarshaller210(); marshaller = am; componentRegistry.wireDependencies(am); am.init(); marshallers.put(VERSION_210, marshaller); } break; case VERSION_320: case VERSION_310: case VERSION_300: knownVersion = true; default: if (!knownVersion && log.isWarnEnabled()) { log.warn("Unknown replication version [" + versionId + "]. Falling back to the default marshaller installed."); } marshaller = marshallers.get(VERSION_300); if (marshaller == null) { am = new CacheMarshaller300(); marshaller = am; componentRegistry.wireDependencies(am); am.init(); marshallers.put(VERSION_300, 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-3.2.8.GA/src/main/java/org/jboss/cache/marshall/NodeDataMarker.java0000644000175000017500000000250511111047320027611 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/marshall/ReplicationObserver.java0000644000175000017500000000053411135332460030761 0ustar moellermoellerpackage org.jboss.cache.marshall; import org.jboss.cache.commands.ReplicableCommand; /** * This is a hook for observing remotely replicated commands on this instance. Mainly used for unit testing. * * @author Mircea.Markus@jboss.com */ public interface ReplicationObserver { public void afterExecutingCommand(ReplicableCommand command); } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/marshall/UnmarshalledReferences.java0000644000175000017500000000512611111047320031413 0ustar moellermoeller/* * 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.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 AT jboss DOT 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-3.2.8.GA/src/main/java/org/jboss/cache/marshall/InactiveRegionException.java0000644000175000017500000000327111111047320031556 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/marshall/RegionNameConflictException.java0000644000175000017500000000313111111047320032351 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/marshall/Marshaller.java0000644000175000017500000001256511111047320027071 0ustar moellermoeller/* * 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.marshall; import org.jboss.cache.Fqn; import org.jboss.cache.io.ByteBuffer; 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; /** * A specialized form of {@link org.jgroups.blocks.RpcDispatcher.Marshaller2#objectToBuffer(Object)} that returns an instance * of {@link ByteBuffer} instead of {@link org.jgroups.util.Buffer}. * * @param o object to marshall * @return a ByteBuffer * @throws Exception */ ByteBuffer objectToBuffer(Object o) throws Exception; } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/Cache.java0000644000175000017500000006023611171345716024215 0ustar moellermoeller/* * 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; import net.jcip.annotations.ThreadSafe; import org.jboss.cache.config.Configuration; import org.jboss.cache.interceptors.base.CommandInterceptor; 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 = new DefaultCacheFactory().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 AT jboss DOT org) * @see Node * @see CacheFactory * @since 2.0.0 */ @ThreadSafe public interface Cache extends Lifecycle { /** * 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); /** * 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); /** * Tests if a node is a leaf, i.e., doesn't have any children * @param fqn fqn to test * @return true if it is a leaf, false otherwise */ boolean isLeaf(Fqn fqn); /** * Tests if a node is a leaf, i.e., doesn't have any children * @param fqn fqn to test * @return true if it is a leaf, false otherwise */ boolean isLeaf(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. * @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. *

    * Note: As of 3.0.0 and when using MVCC locking, more specific behaviour is defined as follows: *

      *
    • A no-op if the node is moved unto itself. E.g., move(fqn, fqn.getParent()) will not do anything.
    • *
    • If a target node does not exist it will be created silently, to be more consistent with other APIs such as put() on a nonexistent node.
    • *
    • If the source node does not exist this is a no-op, to be more consistent with other APIs such as get() on a nonexistent 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); /** * Starts a batch. This is a lightweight batching mechanism that groups cache writes together and finally performs the * write, persistence and/or replication when {@link #endBatch(boolean)} is called rather than for each invocation on the * cache. *

    * Note that if there is an existing transaction in scope and the cache has been configured to use a JTA compliant * transaction manager, calls to {@link #startBatch()} and {@link #endBatch(boolean)} are ignored and treated as no-ops. *

    * * @see #endBatch(boolean) * @since 3.0 */ void startBatch(); /** * Ends an existing ongoing batch. A no-op if a batch has not been started yet. *

    * Note that if there is an existing transaction in scope and the cache has been configured to use a JTA compliant * transaction manager, calls to {@link #startBatch()} and {@link #endBatch(boolean)} are ignored and treated as no-ops. *

    * * @param successful if true, changes made in the batch are committed. If false, they are discarded. * @see #startBatch() * @since 3.0 */ void endBatch(boolean successful); /** * 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 * @since 3.0 */ 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 * @since 3.0 */ 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 * @since 3.0 */ void removeInterceptor(int position); /** * Removes the interceptor of specified type. * * @param interceptorType type of interceptor to remove * @since 3.0 */ void removeInterceptor(Class interceptorType); } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/remoting/0000755000175000017500000000000011675221707024166 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/remoting/jgroups/0000755000175000017500000000000011675221710025651 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/remoting/jgroups/ChannelMessageListener.java0000644000175000017500000003004311254437247033106 0ustar moellermoeller/* * 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.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.DefaultStateTransferManager; 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 AT jboss DOT 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 static final Log log = LogFactory.getLog(ChannelMessageListener.class); private static final boolean trace = log.isTraceEnabled(); private StateTransferManager stateTransferManager; private Configuration configuration; /** * True if state was initialized during start-up. */ private volatile boolean isStateSet = false; @Inject 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("Caught exception integrating state!", 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("Caught exception generating state!", 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() { if (log.isTraceEnabled()) log.trace("State retrieval request received"); 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; } if (trace) log.trace("setState() called with byte array of size " + new_state.length); 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) { if (trace) log.trace("Getting state for state id " + state_id); MarshalledValueOutputStream out = null; String sourceRoot = state_id; byte[] result; boolean hasDifferentSourceAndIntegrationRoots = state_id.indexOf(DefaultStateTransferManager.PARTIAL_STATE_DELIMITER) > 0; if (hasDifferentSourceAndIntegrationRoots) { sourceRoot = state_id.split(DefaultStateTransferManager.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) { if (trace) log.trace("Getting state for state id " + state_id); String sourceRoot = state_id; MarshalledValueOutputStream out = null; boolean hasDifferentSourceAndIntegrationRoots = state_id.indexOf(DefaultStateTransferManager.PARTIAL_STATE_DELIMITER) > 0; if (hasDifferentSourceAndIntegrationRoots) { sourceRoot = state_id.split(DefaultStateTransferManager.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; } if (trace) log.trace("setState() called with input stream"); 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 for id "+state_id+ " is null"); return; } if (trace) log.trace("Receiving state byte array of length " +state.length+" for " + state_id); MarshalledValueInputStream in = null; String targetRoot = state_id; boolean hasDifferentSourceAndIntegrationRoots = state_id.indexOf(DefaultStateTransferManager.PARTIAL_STATE_DELIMITER) > 0; if (hasDifferentSourceAndIntegrationRoots) { targetRoot = state_id.split(DefaultStateTransferManager.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 (trace) log.trace("Receiving state on stream for " + stateId); String targetRoot = stateId; MarshalledValueInputStream in = null; boolean hasDifferentSourceAndIntegrationRoots = stateId.indexOf(DefaultStateTransferManager.PARTIAL_STATE_DELIMITER) > 0; if (hasDifferentSourceAndIntegrationRoots) { targetRoot = stateId.split(DefaultStateTransferManager.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) { if (log.isTraceEnabled()) log.trace("Unknown error while integrating state", t); stateReceivingFailed(t); } finally { Util.close(in); synchronized (stateLock) { // Notify wait that state has been set. stateLock.notifyAll(); } } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/io/0000755000175000017500000000000011675222133022743 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/io/ExposedByteArrayOutputStream.java0000644000175000017500000001213511111047320031423 0ustar moellermoeller/* * 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: ExposedByteArrayOutputStream.java 7168 2008-11-19 17:37:20Z jason.greene@jboss.com $ */ @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-3.2.8.GA/src/main/java/org/jboss/cache/io/ByteBuffer.java0000644000175000017500000000301511111047320025626 0ustar moellermoeller/* * 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-3.2.8.GA/src/main/java/org/jboss/cache/NodeNotValidException.java0000644000175000017500000000317011111047320027371 0ustar moellermoeller/* * 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; /** * 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-3.2.8.GA/src/main/java/org/jboss/cache/LegacyRegionManagerImpl.java0000644000175000017500000001333511111047320027655 0ustar moellermoeller/* * 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; import net.jcip.annotations.ThreadSafe; import org.jboss.cache.buddyreplication.BuddyManager; import static org.jboss.cache.lock.LockType.WRITE; import java.util.ArrayList; import java.util.Set; /** * For optimistic and pessimistically locked caches * * @author Manik Surtani * @since 2.0.0 * @deprecated will be removed along with optimistic and pessimistic locking. */ @ThreadSafe @Deprecated public class LegacyRegionManagerImpl extends RegionManagerImpl { /** * 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 CacheException if there is a problem evicting nodes * @throws IllegalStateException if {@link org.jboss.cache.config.Configuration#isUseRegionBasedMarshalling()} is false */ @Override protected void inactivateRegion(Fqn fqn) throws CacheException { 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 lock(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); if (subtreeRoot != null) { // Acquire locks subtreeLocked = lockManager.lockAll(subtreeRoot, WRITE, getOwnerForLock(), stateFetchTimeout); // Lock the parent, as we're about to write to it parent = subtreeRoot.getParentDirect(); if (parent != null) parentLocked = lockManager.lockAll(parent, WRITE, getOwnerForLock(), stateFetchTimeout); // Remove the subtree cache.evict(subtree, true); // 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; } } } catch (InterruptedException e) { throw new CacheException(e); } 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); } } unlock(fqn); } } private Object getOwnerForLock() { Object owner = cache.getCurrentTransaction(); return owner == null ? Thread.currentThread() : owner; } }jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/StringFqn.java0000644000175000017500000001025011260155447025113 0ustar moellermoeller/* * 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; 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 AT jboss DOT org) * @since 2.2.0 */ // TODO: 3.0.0: Implement proper String escaping. @Experimental public final class StringFqn extends Fqn { private static final long serialVersionUID = -1886519334118453717L; protected StringFqn() { super(); stringRepresentation = SEPARATOR; } @SuppressWarnings("unchecked") protected StringFqn(StringFqn base, List elements) { super(base, (List) elements); String elementStringRep = getStringRepresentation(elements); stringRepresentation = base.isRoot() ? elementStringRep : base.stringRepresentation + elementStringRep; } protected StringFqn(StringFqn base, StringFqn relative) { super(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; } } @SuppressWarnings("unchecked") protected StringFqn(List stringElements) { super((List) 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 public int hashCode() { return super.hashCode(); } @Override protected int calculateHashCode() { return stringRepresentation.hashCode(); } @Override @SuppressWarnings(value = "unchecked") 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-3.2.8.GA/src/main/java/org/jboss/cache/Fqn.java0000644000175000017500000005200011252520115023710 0ustar moellermoeller/* * 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; import net.jcip.annotations.Immutable; import org.jboss.cache.annotations.Compat; import org.jboss.cache.util.Util; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.Arrays; 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 a List or varargs: *
     * Fqn abc = Fqn.fromElements("people", "Smith", "Joe");
     * 
    * This is a bit more efficient to construct. *

    * Note that
    *

    * Fqn f = Fqn.fromElements("/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 * * @version $Revision: 8221 $ */ @Immutable @Compat(notes = "The generics, while originally intended to be removed in 3.0, have been retained for backward compat.") public class Fqn implements Comparable>, Externalizable { private static final long serialVersionUID = -6901735117605327068L; /** * Separator between FQN elements. */ public static final String SEPARATOR = "/"; protected Object[] elements; private transient int hash_code = 0; /** * Immutable root Fqn. */ public static final Fqn ROOT = new Fqn(); /** * 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. ---------------------- /** * Public to satisfy Externalization. // TODO: Remove this ctor as well as Externalization!! */ public Fqn() { elements = new Object[]{}; } // --- deprecated compat stuff /** * 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 @Compat public Fqn(List names) { // the list is unsafe - may be referenced externally this(names.toArray(), true); } /** * 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 @Compat public Fqn(E... names) { // safe - the list is created here. this(names, 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 @Compat @SuppressWarnings("unchecked") public Fqn(Fqn base, Fqn relative) { this(base, (E[]) relative.elements); } /** * 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 @Compat public Fqn(Fqn base, E... childNames) { elements = new Object[base.elements.length + childNames.length]; System.arraycopy(base.elements, 0, elements, 0, base.elements.length); System.arraycopy(childNames, 0, elements, base.elements.length, childNames.length); } // --- end deprecated stuff /** * 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 @Compat(notes = "Not truly deprecated, this constructor should really be protected and not public. Marked as deprecated for anyone using it as a public API.") @SuppressWarnings("unchecked") protected Fqn(Object[] names, boolean safe) { if (names != null) { // if not safe make a defensive copy if (safe) elements = names; else { elements = new Object[names.length]; System.arraycopy(names, 0, elements, 0, names.length); } } else { elements = new Object[]{}; } } // ----------------- END: Private constructors for use by factory methods only. ---------------------- /** * 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 */ @SuppressWarnings("unchecked") public static Fqn fromList(List names) { return new Fqn(names.toArray(), true); } /** * 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 */ @SuppressWarnings("unchecked") @Compat public static Fqn fromList(List names, boolean safe) { return new Fqn(names.toArray(), true); // this will always be safe!! } /** * 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(T... elements) { return new Fqn(elements, true); } /** * 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 */ @SuppressWarnings("unchecked") public static Fqn fromRelativeFqn(Fqn base, Fqn relative) { return new Fqn(base, (T[]) relative.elements); } /** * 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 */ @SuppressWarnings("unchecked") public static Fqn fromRelativeList(Fqn base, List relativeElements) { return new Fqn(base, (T[]) relativeElements.toArray()); } /** * 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, T... relativeElements) { return new Fqn(base, 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:
    *
        * Fqn.fromElements("a", "b", "c");
        * 
    * * @param stringRepresentation String representation of the Fqn * @return an Fqn constructed from the string representation passed in */ @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(SEPARATOR); return new Fqn(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(); 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) * * @param startIndex starting index * @param endIndex end index * @return a subFqn */ public Fqn getSubFqn(int startIndex, int endIndex) { if (endIndex < startIndex) throw new IllegalArgumentException("End index cannot be less than start index!"); int len = endIndex - startIndex; Object[] subElements = new Object[len]; System.arraycopy(elements, startIndex, subElements, 0, len); return new Fqn(subElements, true); } /** * @return the number of elements in the Fqn. The root node contains zero. */ public int size() { return elements.length; } /** * @param n index of the element to return * @return Returns the nth element in the Fqn. */ public Object get(int n) { return elements[n]; } /** * @return the last element in the Fqn. * @see #getLastElementAsString */ public Object getLastElement() { if (isRoot()) return null; return elements[elements.length - 1]; } /** * @param element element to find * @return true if the Fqn contains this element, false otherwise. */ public boolean hasElement(Object element) { return indexOf(element) != -1; } /** * 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; if (elements.length != other.elements.length) return false; // compare elements in *reverse*! for (int i=elements.length - 1; i>=0; i--) { if (!Util.safeEquals(elements[i], other.elements[i])) return false; } return true; } /** * 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(elements.length); for (Object element : elements) { out.writeObject(element); } } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { int size = in.readShort(); this.elements = new Object[size]; for (int i = 0; i < size; i++) elements[i] = in.readObject(); } /** * 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() != elements.length && 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 elements.length == 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) { Object[] parentElems = parentFqn.elements; if (parentElems.length > elements.length) { return false; } for (int i = parentElems.length - 1; i >= 0; i--) { if (!parentElems[i].equals(elements[i])) { return false; } } return true; } /** * Calculates a hash code by summing the hash code of all elements. * * @return a calculated hashcode */ protected int calculateHashCode() { int hashCode = 19; for (Object o : elements) hashCode = 31 * hashCode + (o == null ? 0 : o.hashCode()); if (hashCode == 0) hashCode = 0xDEADBEEF; // degenerate case return hashCode; } protected String getStringRepresentation(List elements) { return getStringRepresentation(elements.toArray()); } protected String getStringRepresentation(Object[] 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 (elements.length) { case 0: case 1: return root(); default: return getSubFqn(0, elements.length - 1); } } public static final Fqn root() // declared final so compilers can optimise and in-line. { return ROOT; } /** * Returns true if this is a root Fqn. * * @return true if the Fqn is Fqn.ROOT. */ public boolean isRoot() { return elements.length == 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 */ @SuppressWarnings("unchecked") public List peekElements() { return (List) Arrays.asList(elements); } private int indexOf(Object o) { if (o == null) { for (int i = 0; i < elements.length; i++) if (elements[i] == null) return i; } else { for (int i = 0; i < elements.length; i++) if (o.equals(elements[i])) return i; } return -1; } /** * Compares this Fqn to another using {@link FqnComparator}. */ public int compareTo(Fqn fqn) { return FqnComparator.INSTANCE.compare(this, fqn); } /** * Creates a new Fqn whose ancestor has been replaced with the new ancestor passed in. * * @param oldAncestor old ancestor to replace * @param newAncestor nw ancestor to replace with * @return a new Fqn with ancestors replaced. */ public Fqn replaceAncestor(Fqn oldAncestor, Fqn newAncestor) { if (!isChildOf(oldAncestor)) throw new IllegalArgumentException("Old ancestor must be an ancestor of the current Fqn!"); Fqn subFqn = this.getSubFqn(oldAncestor.size(), size()); return Fqn.fromRelativeFqn(newAncestor, subFqn); } }jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/NodeFactory.java0000644000175000017500000001037211160141267025414 0ustar moellermoeller/* * 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; import org.jboss.cache.mvcc.ReadCommittedNode; import org.jboss.cache.optimistic.TransactionWorkspace; import org.jboss.cache.optimistic.WorkspaceNode; import java.util.Map; /** * An interface for a factory that creates nodes. This used to be a concrete class prior to 3.0.0. Made into an * interface to simplify logic of different locking schemes and node types. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ public interface NodeFactory { ReadCommittedNode createWrappedNode(InternalNode node, InternalNode parent); ReadCommittedNode createWrappedNodeForRemoval(Fqn fqn, InternalNode node, InternalNode parent); WorkspaceNode createWrappedNode(NodeSPI dataNode, TransactionWorkspace workspace); /** * Creates a new node and populates its attributes. *

    * Note that the data map passed in must not be null, and must not be referenced anywhere else as a defensive copy * is NOT made when injecting it into the node. * * @param fqn * @param parent * @param data * @return a new node */ NodeSPI createNode(Fqn fqn, NodeSPI parent, Map data); /** * Creates a new node and populates its attributes. *

    * Note that the data map passed in must not be null, and must not be referenced anywhere else as a defensive copy * is NOT made when injecting it into the node. * * @param childName * @param parent * @param data * @return a new node */ NodeSPI createNode(Object childName, NodeSPI parent, Map data); /** * Creates a new, empty node. * * @param fqn * @param parent * @return a new node */ NodeSPI createNode(Fqn fqn, NodeSPI parent); /** * Creates a new, empty node. * * @param childName * @param parent * @return a new node */ NodeSPI createNode(Object childName, NodeSPI parent); /** * Creates a new node, and optionally attaches the node to its parent. *

    * The assumption here is that any locks are acquired to prevent concurrent creation of the same node. Implementations * of the NodeFactory should not attempt to synchronize or guard against concurrent creation. *

    * * @param fqn fqn of node to create. Must not be null or root. * @param parent parent to attach to. Must not be null, even if attachToParent is false. * @param ctx invocation context to register with. Must not be null. * @param attachToParent if true, the node is registered in the parent's child map. If false, it is not. * @return a new node, or the existing node if one existed. */ InternalNode createChildNode(Fqn fqn, InternalNode parent, InvocationContext ctx, boolean attachToParent); NodeSPI createRootNode(); /** * Creates an internal node. Similar to {@link #createNode(Fqn, NodeSPI)} except that the resultant node is not wrapped * in a {@link org.jboss.cache.invocation.NodeInvocationDelegate}. * * @param childFqn * @return a new node */ InternalNode createInternalNode(Fqn childFqn); } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/CacheException.java0000644000175000017500000000362411111047320026052 0ustar moellermoeller/* * 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; /** * 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-3.2.8.GA/src/main/java/org/jboss/cache/loader/0000755000175000017500000000000011675222132023601 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/loader/ManagedConnectionFactory.java0000644000175000017500000001264111111047320031341 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/loader/s3/0000755000175000017500000000000011675222113024125 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/loader/s3/S3CacheLoader.java0000644000175000017500000003064111111047320027322 0ustar moellermoeller/* * 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.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: S3CacheLoader.java 7168 2008-11-19 17:37:20Z jason.greene@jboss.com $ */ @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 = get(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); } /** * 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 { if (values == null) values = Collections.emptyMap(); put0(name, values); } /** * 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-3.2.8.GA/src/main/java/org/jboss/cache/loader/s3/S3LoaderConfig.java0000644000175000017500000001734011111047320027525 0ustar moellermoeller/* * 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.loader.s3; 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; import java.lang.reflect.Field; import java.util.Properties; /** * 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-3.2.8.GA/src/main/java/org/jboss/cache/loader/s3/S3Exception.java0000644000175000017500000000330111111047320027117 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/loader/ClusteredCacheLoader.java0000644000175000017500000002734511176306660030471 0ustar moellermoeller/* * 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.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.CommandsFactory; 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.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 AT jboss DOT 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 its 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 its 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 null; // 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-3.2.8.GA/src/main/java/org/jboss/cache/loader/JDBCCacheLoaderOld.java0000644000175000017500000002472111111047320027653 0ustar moellermoeller/* * 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.loader; 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 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: 7168 $ * @deprecated please use the {@link org.jboss.cache.loader.JDBCCacheLoader}. */ @Deprecated @SuppressWarnings("deprecation") public class JDBCCacheLoaderOld extends AdjListJDBCCacheLoader { private static final Log log = LogFactory.getLog(JDBCCacheLoaderOld.class); private JDBCCacheLoaderOldConfig config; @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); } } @Override protected Log getLogger() { return log; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/loader/JDBCCacheLoader.java0000755000175000017500000003376211336261030027231 0ustar moellermoeller/* * 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.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; 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.sql.Statement; 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.batch.enable: whether or not to use batching on repetitive operations (e.g. inserts during state transfer). * Enabling batching should give an important performance boost. It might be required to disable this if the JDBC driver * does not support batching. Default set to 'true' *
    • *
    • * cache.jdbc.batch.size: number of operations afer which the batching buffer will be flushed. If 'cache.jdbc.batch.enable' * is false, this will be ignored. Default value is 1000. *
    • *
    * * @author Mircea.Markus@iquestint.com * @author Galder Zamarreno * @version 1.0 */ @ThreadSafe public class JDBCCacheLoader extends AdjListJDBCCacheLoader { private static final Log log = LogFactory.getLog(JDBCCacheLoader.class); 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 { if (getLogger().isTraceEnabled()) getLogger().trace("put name=" + name + " key=" + key + " value=" + value); lock.acquireLock(name, true); try { Map existing = loadNode(name); if (existing == null) { // do not use a singleton map here since this is serialized and stored in the DB. Map m = new HashMap(1); m.put(key, value); addNewSubtree(name, m); return null; } if (existing == NULL_NODE_IN_ROW) { // do not use a singleton map here since this is serialized and stored in the DB. Map m = new HashMap(1); m.put(key, value); updateNode(name, m); return null; } //creation sequence important - we need to overwrite old values Object oldVal = existing.put(key, value); updateNode(name, existing); return oldVal; } finally { lock.releaseLock(name); } } /** * As per interface's contract. * Performance Note: Optimised O(nodeDepth) db calls. */ public void put(Fqn name, Map attributes) throws Exception { if (getLogger().isTraceEnabled()) getLogger().trace("put name=" + name + " attr=" + attributes); lock.acquireLock(name, true); Map toStore = attributes; if (toStore != null && !(toStore instanceof HashMap)) toStore = new HashMap(attributes); try { if (!exists(name)) { addNewSubtree(name, toStore); } else { updateNode(name, toStore); } } finally { lock.releaseLock(name); } } @Override protected void storeStateHelper(Fqn subtree, List nodeData, boolean moveToBuddy) throws Exception { lock.acquireLock(subtree, true); Connection con = null; PreparedStatement ps = null; boolean autocommitPrev = true;//this initialization is here only for making code compilable, ignore it int batchCaount = 0; try { con = cf.getConnection(); autocommitPrev = con.getAutoCommit(); if (config.isBatchEnabled()) con.setAutoCommit(false); ps = prepareAndLogStatement(con, config.getInsertNodeSql()); 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(); } Map attributes = nd.getAttributes() == null ? null : new HashMap(nd.getAttributes()); populatePreparedStatementForInsert(fqn, attributes, ps); if (!config.isBatchEnabled()) { if (ps.executeUpdate() != 1) { throw new IllegalStateException("One and only one row must have been updated!"); } } else { ps.addBatch(); batchCaount++; if (batchCaount >= config.getBatchSize()) { int result[] = ps.executeBatch(); for (int aResult : result) { if (aResult != 1 /* one and only one row must have been updated */ && aResult != Statement.SUCCESS_NO_INFO) { throw new IllegalStateException("Failure executing batch insert during state transfer!"); } } batchCaount = 0; } } } if (batchCaount > 0) { if (batchCaount > config.getBatchSize()) { throw new IllegalStateException("batchCaount > config.getBatchSize() should never happen!"); } ps.executeBatch();//flush the batch here } } finally { lock.releaseLock(subtree); if (con != null) { con.setAutoCommit(autocommitPrev); safeClose(ps); cf.close(con); } } } /** * 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 { lock.acquireLock(fqn, true); conn = cf.getConnection(); String fqnString = fqn.toString(); //apend / at the end avoids this issue: 'a/b/cd' is not a child of 'a/b/c' String fqnWildcardString = getFqnWildcardString(fqnString, fqn); ps = prepareAndLogStatement(conn, config.getDeleteNodeSql(), fqnString, fqnWildcardString); ps.executeUpdate(); } 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(); String fqnString = fqn.toString(); String fqnWildcardString = getFqnWildcardString(fqnString, fqn); ps = prepareAndLogStatement(connection, config.getRecursiveChildrenSql(), fqnString, fqnWildcardString); 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, true); 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 String getFqnWildcardString(String fqnString, Fqn fqn) { return escapeSqlLikeParameter(fqnString + (fqn.isRoot() ? "" : Fqn.SEPARATOR)) + '%'; } /** * Escape all characters occurring in the FQN string that have significance to SQL within a LIKE clause. Escape character '^' * must match value in JDBCCacheLoaderConfig.constructRecursiveChildrenSql. This method * doubles any occurrence of the escape character '^' and replaces any occurrence of wildcard characters '_' '%' with escaped version e.g. "^_" * * @param res the fqn as a String * @return the escaped result */ private String escapeSqlLikeParameter(String res) { return res.replaceAll("([_%^])", "^$1"); } 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 void addNewSubtree(Fqn name, Map attributes) throws Exception { if (getLogger().isTraceEnabled()) getLogger().trace("addNewSubtree name=" + name + " attr=" + attributes); 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)); } @Override protected Log getLogger() { return log; } /** * 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 { 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 { conn = cf.getConnection(); ps = prepareAndLogStatement(conn, 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-3.2.8.GA/src/main/java/org/jboss/cache/loader/AbstractCacheLoader.java0000644000175000017500000003103111131164620030252 0ustar moellermoeller/* * 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.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.Modification.ModificationType; 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.Immutables; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.ArrayList; 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 AT jboss DOT 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 : Immutables.immutableMapCopy(attributes)); put(fqn, attrs); } public void storeEntireState(ObjectInputStream is) throws Exception { storeState(Fqn.ROOT, is); } 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? if (trace) log.trace("Empty persistent stream?"); 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 { List mod = new ArrayList(nodeData.size()); for (Object aNodeData : nodeData) { NodeData nd = (NodeData) aNodeData; if (nd.isMarker()) { if (trace) log.trace("Reached delimiter; exiting loop"); break; } Fqn fqn; if (moveToBuddy) { fqn = buddyFqnTransformer.getBackupFqn(subtree, nd.getFqn()); } else { fqn = nd.getFqn(); } if (trace) log.trace("Storing state in Fqn " + fqn); mod.add(new Modification(ModificationType.PUT_DATA_ERASE, fqn, nd.getAttributes())); } prepare(null, mod, true); } public void loadEntireState(ObjectOutputStream os) throws Exception { loadState(Fqn.ROOT, os); } public void loadState(Fqn subtree, ObjectOutputStream os) throws Exception { loadStateHelper(subtree, os); } 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; Object 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, true); } //out.writeObject(nd); list.add(nd); // then visit the children childrenNames = getChildrenNames(fqn); if (childrenNames == null) { return; } for (Object childrenName : childrenNames) { childName = 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 modification " + m.getType()); } } } protected 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-3.2.8.GA/src/main/java/org/jboss/cache/loader/LocalDelegatingCacheLoaderConfig.java0000644000175000017500000000504711111047320032656 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/loader/FileCacheLoaderConfig.java0000644000175000017500000000632311111047320030515 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/loader/TcpDelegatingCacheLoader.java0000644000175000017500000003375511133377451031252 0ustar moellermoeller/* * 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.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 org.jboss.cache.util.concurrent.SynchronizedRestarter; 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.ConnectException; import java.net.InetSocketAddress; 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 7465 2009-01-14 15:05:13Z manik.surtani@jboss.com $ */ public class TcpDelegatingCacheLoader extends AbstractCacheLoader { volatile private Socket sock; private TcpDelegatingCacheLoaderConfig config; volatile ObjectInputStream in; volatile ObjectOutputStream out; private static final Log log = LogFactory.getLog(TcpDelegatingCacheLoader.class); private static final boolean trace = log.isTraceEnabled(); private final SynchronizedRestarter restarter = new SynchronizedRestarter(); 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 { if (trace) log.trace("About to invoke operation " + m); Object rv = m.invoke(this, params); if (trace) log.trace("Completed invocation of " + m); return rv; } 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()); restarter.restartComponent(this); } catch (InterruptedException e1) { Thread.currentThread().interrupt(); } catch (Exception e1) { if (trace) log.trace("Unable to reconnect", e1); } } 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); } @SuppressWarnings("unchecked") 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 (this) { 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; } @SuppressWarnings("unchecked") protected Map _get(Fqn name) throws Exception { synchronized (this) { 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 (this) { 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 (this) { 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 (this) { 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 (this) { 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 (this) { 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 (this) { 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 (this) { 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 { try { InetSocketAddress address = new InetSocketAddress(config.getHost(), config.getPort()); sock = new Socket(); sock.setSoTimeout(config.getReadTimeout()); sock.connect(address, config.getReadTimeout()); out = new ObjectOutputStream(new BufferedOutputStream(sock.getOutputStream())); out.flush(); in = new ObjectInputStream(new BufferedInputStream(sock.getInputStream())); } catch (ConnectException ce) { log.info("Unable to connect to TCP socket on interface " + config.getHost() + " and port " + config.getPort()); throw ce; } } @Override public void stop() { try { if (in != null) in.close(); } catch (IOException e) { if (trace) log.trace("Unable to close resource", e); } try { if (out != null) out.close(); } catch (IOException e) { if (trace) log.trace("Unable to close resource", e); } try { if (sock != null) sock.close(); } catch (IOException e) { if (trace) log.trace("Unable to close resource", e); } } @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-3.2.8.GA/src/main/java/org/jboss/cache/loader/CacheLoaderManager.java0000644000175000017500000004051111435716664030106 0ustar moellermoeller/* * 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.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 AT jboss DOT 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. if (loader instanceof AbstractDelegatingCacheLoader) registry.wireDependencies(((AbstractDelegatingCacheLoader) loader).getCacheLoader()); else 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); } fetchPersistentState = fetchPersistentState || (loader.getConfig() != null && loader.getConfig().isFetchPersistentState()); fetchPersistentState = fetchPersistentState || (config != null && config.isFetchPersistentState()); } } 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-3.2.8.GA/src/main/java/org/jboss/cache/loader/AdjListJDBCCacheLoader.java0000644000175000017500000005707511336261030030504 0ustar moellermoeller/* * 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.loader; import net.jcip.annotations.ThreadSafe; import org.apache.commons.logging.Log; import org.jboss.cache.Fqn; import org.jboss.cache.Modification; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.io.ByteBuffer; import org.jboss.cache.lock.StripedLock; import org.jboss.cache.util.Util; 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(128); // dummy, used for serializing empty maps. One should NOT use Collections.emptyMap() here since if an emptyMap is // serialized, upon deserialization it cannot be added to. private static final Map EMPTY_HASHMAP = new HashMap(0, 1); /** * Creates a prepared statement using the given connection and SQL string, logs the statement that is about to be * executed to the logger, and optionally sets String parameters provided on the prepared statement before * returning the prepared statement. * * @param conn Connection to use to create the prepared statement * @param sql SQL to use with the prepared statement * @param params optional parameters to add to the statement. * @return a prepared statement * @throws Exception if there are problems */ protected PreparedStatement prepareAndLogStatement(Connection conn, String sql, String... params) throws Exception { PreparedStatement ps = conn.prepareStatement(sql); for (int i = 0; i < params.length; i++) ps.setString(i + 1, params[i]); // Logging the SQL we plan to run if (getLogger().isTraceEnabled()) { StringBuilder sb = new StringBuilder("Executing SQL statement ["); sb.append(sql).append("]"); if (params.length != 0) { sb.append(" with params "); boolean first = true; for (String param : params) { if (first) { first = false; } else { sb.append(", "); } sb.append("[").append(param).append("]"); } } getLogger().trace(sb.toString()); } return ps; } 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 */ getLogger().debug("Initialising with a connection factory since data source is not provided."); if (getLogger().isDebugEnabled()) { getLogger().debug("Using connection factory " + config.getConnectionFactoryClass()); } cf = (ConnectionFactory) Util.loadClass(config.getConnectionFactoryClass()).newInstance(); } catch (Exception e) { getLogger().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 { lock.acquireLock(fqn, false); con = cf.getConnection(); ps = prepareAndLogStatement(con, config.getSelectChildNamesSql(), fqn.toString()); 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 (getLogger().isDebugEnabled()) { getLogger().debug("executing ddl: " + config.getCreateTableDDL()); } st = con.createStatement(); st.executeUpdate(config.getCreateTableDDL()); } } finally { safeClose(st); cf.close(con); } if (config.getCreateTable()) { createDummyTableIfNeeded(); } } private void createDummyTableIfNeeded() throws Exception { Connection conn = null; PreparedStatement ps = null; if (config.getDropTable()) { try { conn = cf.getConnection(); ps = prepareAndLogStatement(conn, config.getDummyTableRemovalDDL()); ps.execute(); } catch (Exception e) { if (getLogger().isTraceEnabled()) getLogger().trace("No need to drop tables!"); } finally { safeClose(ps); cf.close(conn); } } try { conn = cf.getConnection(); if (!tableExists(config.getDummyTable(), conn)) { ps = prepareAndLogStatement(conn, config.getDummyTableCreationDDL()); ps.execute(); safeClose(ps); ps = prepareAndLogStatement(conn, 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 (getLogger().isDebugEnabled()) { getLogger().debug("executing ddl: " + config.getDropTableDDL()); } con = cf.getConnection(); st = con.createStatement(); st.executeUpdate(config.getDropTableDDL()); safeClose(st); } catch (SQLException e) { getLogger().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 { if (getLogger().isTraceEnabled()) getLogger().trace("exists name=" + name); lock.acquireLock(name, false); Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = cf.getConnection(); ps = prepareAndLogStatement(conn, config.getExistsSql(), name.toString()); rs = ps.executeQuery(); boolean res = rs.next(); if (getLogger().isTraceEnabled()) getLogger().trace("exists name=" + name + " is " + res); return res; } 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 { if (getLogger().isTraceEnabled()) getLogger().trace("remove name=" + name); 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) { if (getLogger().isTraceEnabled()) getLogger().trace("loadNode name=" + name); boolean rowExists = false; Connection con = null; PreparedStatement ps = null; ResultSet rs = null; try { con = cf.getConnection(); ps = prepareAndLogStatement(con, config.getSelectNodeSql(), 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 dataMap 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 dataMap, boolean rowMayExist) { if (getLogger().isTraceEnabled()) getLogger().trace("insertNode name=" + name + " dataMap=" + dataMap); Connection con = null; PreparedStatement ps = null; try { con = cf.getConnection(); ps = prepareAndLogStatement(con, config.getInsertNodeSql()); populatePreparedStatementForInsert(name, dataMap, ps); 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) { getLogger().error("Failed to insert node :" + e.getMessage()); throw new IllegalStateException("Failed to insert node: " + e.getMessage(), e); } finally { safeClose(ps); cf.close(con); } } /** * Expects a PreparedStatement binded to {@link org.jboss.cache.loader.JDBCCacheLoaderConfig#getInsertNodeSql()} */ protected void populatePreparedStatementForInsert(Fqn name, Map dataMap, PreparedStatement ps) throws Exception { String fqnString = name.toString(); ps.setString(1, fqnString); if (dataMap != null) { ByteBuffer byteBuffer = marshall(dataMap); ps.setBinaryStream(2, byteBuffer.getStream(), byteBuffer.getLength()); } 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()); } // and a repeat - the 4th param is the same as the 1st one. ps.setString(4, fqnString); } /** * Updates a node in the database. * * @param name the fqn * @param node new node value */ protected void updateNode(Fqn name, Map node) { if (getLogger().isTraceEnabled()) getLogger().trace("updateNode name=" + name); Connection con = null; PreparedStatement ps = null; try { con = cf.getConnection(); ps = prepareAndLogStatement(con, config.getUpdateNodeSql()); if (node == null) node = EMPTY_HASHMAP; ByteBuffer byteBuffer = marshall(node); ps.setBinaryStream(1, byteBuffer.getStream(), byteBuffer.getLength()); ps.setString(2, name.toString()); /*int rows = */ ps.executeUpdate(); } 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 Log getLogger(); protected abstract AdjListJDBCCacheLoaderConfig processConfig(CacheLoaderConfig.IndividualCacheLoaderConfig base); protected void reportAndRethrowError(String message, Exception cause) throws IllegalStateException { getLogger().error(message, cause); throw new IllegalStateException(message, cause); } protected void safeClose(InputStream is) { if (is != null) { try { is.close(); } catch (IOException e) { getLogger().warn("Failed to close input stream: " + e.getMessage()); } } } protected void safeClose(Statement st) { if (st != null) { try { st.close(); } catch (SQLException e) { getLogger().warn("Failed to close statement: " + e.getMessage()); } } } protected void safeClose(ResultSet rs) { if (rs != null) { try { rs.close(); } catch (SQLException e) { getLogger().warn("Failed to close result set: " + e.getMessage()); } } } protected Object unmarshall(InputStream from) throws Exception { return getMarshaller().objectFromStream(from); } protected ByteBuffer marshall(Object obj) throws Exception { return getMarshaller().objectToBuffer(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-3.2.8.GA/src/main/java/org/jboss/cache/loader/AsyncCacheLoader.java0000644000175000017500000003205511111047320027566 0ustar moellermoeller/* * 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.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.Immutables; 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.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.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. *
    *
    cache.async.threadPoolSize
    *
    The size of the async processor thread pool. Defaults to 1. This * property is new in JBoss Cache 3.0.
    *
    * 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 ExecutorService executor; private AtomicBoolean stopped = new AtomicBoolean(true); private BlockingQueue queue = new ArrayBlockingQueue(DEFAULT_QUEUE_SIZE); private List processorFutures; 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; } /** * TODO this is the same as the AbstractCacheLoader. */ @Override public void prepare(Object tx, List modifications, boolean one_phase) throws Exception { if (one_phase) { put(modifications); } else { transactions.put(tx, modifications); } } /** * TODO this is the same as the AbstractCacheLoader. */ @Override 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); } /** * TODO this is the same as the AbstractCacheLoader. */ @Override public void rollback(Object tx) { transactions.remove(tx); } @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 : Immutables.immutableMapCopy(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(); executor = Executors.newFixedThreadPool(config.getThreadPoolSize(), new ThreadFactory() { public Thread newThread(Runnable r) { Thread t = new Thread(r, "AsyncCacheLoader-" + threadId.getAndIncrement()); t.setDaemon(true); return t; } }); processorFutures = new ArrayList(config.getThreadPoolSize()); for (int i = 0; i < config.getThreadPoolSize(); i++) processorFutures.add(executor.submit(new AsyncProcessor())); } @Override public void stop() { stopped.set(true); if (executor != null) { for (Future f : processorFutures) f.cancel(true); executor.shutdown(); try { boolean terminated = executor.isTerminated(); while (!terminated) { terminated = executor.awaitTermination(60, TimeUnit.SECONDS); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } executor = null; super.stop(); } private void enqueue(final 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 { // Modifications to invoke as a single put private final List mods = new ArrayList(config.getBatchSize()); 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); if (log.isDebugEnabled()) log.debug("Exception: ", e); } } } @Override public String toString() { return super.toString() + " delegate=[" + super.getCacheLoader() + "]" + " stopped=" + stopped + " batchSize=" + config.getBatchSize() + " returnOld=" + config.getReturnOld() + " asyncPut=" + config.getUseAsyncPut() + " threadPoolSize=" + config.getThreadPoolSize() + " queue.remainingCapacity()=" + queue.remainingCapacity() + " queue.peek()=" + queue.peek(); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/loader/SingletonStoreCacheLoader.java0000644000175000017500000004477411325553351031520 0ustar moellermoeller/* * 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.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.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 config 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 { /* Navigates to the children */ Collection children = node.getChildren(); if (trace) log.trace("Children for " + node.getFqn() + " are " + children); if (!children.isEmpty()) { for (NodeSPI aChildren : children) { pushState(aChildren); } } Map data = node.getData(); Fqn fqn = node.getFqn(); if (!data.isEmpty()) { put(fqn, data); } } /** * 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(); return mbrs != null && mbrs.size() > 0 && localAddress.equals(mbrs.firstElement()); } /* 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-3.2.8.GA/src/main/java/org/jboss/cache/loader/CacheLoaderAop.java0000755000175000017500000000404311111047320027227 0ustar moellermoeller/* * 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.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 7168 2008-11-19 17:37:20Z jason.greene@jboss.com $ */ 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-3.2.8.GA/src/main/java/org/jboss/cache/loader/ChainingCacheLoader.java0000644000175000017500000003534111111047320030232 0ustar moellermoeller/* * 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.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 AT jboss DOT 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-3.2.8.GA/src/main/java/org/jboss/cache/loader/ClusteredCacheLoaderConfig.java0000644000175000017500000000560411111047320031571 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/loader/jdbm/0000755000175000017500000000000011675222123024515 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/loader/jdbm/JdbmCacheLoader2Config.java0000644000175000017500000000334111116343407031517 0ustar moellermoeller/* * 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.loader.jdbm; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; /** * Configuration for {@link JdbmCacheLoader2}. * * @author Elias Ross */ public class JdbmCacheLoader2Config extends JdbmCacheLoaderConfig { private static final long serialVersionUID = 8905490360516820352L; /** * Constructs a new JdbmCacheLoader2Config. */ public JdbmCacheLoader2Config() { super(); } /** * Constructs a new JdbmCacheLoader2Config. */ public JdbmCacheLoader2Config(IndividualCacheLoaderConfig base) { super(base); } @Override void setClassName() { setClassName(JdbmCacheLoader2.class.getName()); } }jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/loader/jdbm/JdbmCacheLoader.java0000644000175000017500000004522411117015662030315 0ustar moellermoeller/* * 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.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.CacheException; 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.ArrayList; 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 Cache.

    *

    * 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 7261 2008-12-07 18:53:38Z genman $ */ @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; RecordManager recman; 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 " + getClass().getSimpleName() + " instance."); checkNotOpen(); checkNonNull(cache, "CacheSPI object is required"); String locationStr = config.getLocation(); if (locationStr == null) { locationStr = System.getProperty("java.io.tmpdir"); config.setLocation(locationStr); } // JBCACHE-1448 db name parsing fix courtesy of Ciro Cavani /* Parse config string. */ int offset = locationStr.indexOf('#'); String cacheDbName; if (offset >= 0 && offset < locationStr.length() - 1) { cacheDbName = locationStr.substring(offset + 1); locationStr = locationStr.substring(0, offset); } else { cacheDbName = cache.getClusterName(); if (cacheDbName == null) cacheDbName = "CacheInstance-" + System.identityHashCode(cache); } // 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!"); } try { openDatabase(new File(location, 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 = createConfig(base); } if (trace) log.trace("Configuring cache loader with location = " + config.getLocation()); } JdbmCacheLoaderConfig createConfig(IndividualCacheLoaderConfig base) { return new JdbmCacheLoaderConfig(base); } 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 IOException { if (trace) { log.trace("getChildrenNames " + name); } synchronized (tree) { return getChildrenNames0(name); } } 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(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; } void commit() throws IOException { 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(); } } 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(); } void put0(Fqn name, Map values) throws Exception { if (trace) { log.trace("put " + name + " values=" + values); } makeNode(name); if (values == null) { return; } Fqn keys = keys(name); Tuple t = new Tuple(); List removeList = new ArrayList(); 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(); if (!values.containsKey(nullUnmask(k))) removeList.add(fqn); } } for (Map.Entry me : values.entrySet()) { Fqn rec = key(name, me.getKey()); insert(rec, nullMask(me.getValue())); } for (Object o : removeList) { tree.remove(o); } } /** * 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); } void erase0(Fqn name, boolean self) throws IOException { if (trace) { log.trace("erase " + name + " self=" + self); } List removeList = new ArrayList(); synchronized (tree) { TupleBrowser browser = tree.browse(name); Tuple t = new Tuple(); boolean first = true; while (browser.getNext(t)) { if (first && !self) { first = false; continue; } Fqn fqn = (Fqn) t.getKey(); if (!fqn.isChildOrEquals(name)) { break; } removeList.add(fqn); } } for (Object o : removeList) tree.remove(o); } /** * Erase a FQN's key. * Does not commit. */ 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"); for (Modification m : modifications) { switch (m.getType()) { case PUT_DATA: put0(m.getFqn(), m.getData()); break; case PUT_DATA_ERASE: erase0(m.getFqn(), false); put0(m.getFqn(), m.getData()); break; case PUT_KEY_VALUE: put0(m.getFqn(), m.getKey(), m.getValue()); break; case REMOVE_DATA: erase0(m.getFqn(), false); break; case REMOVE_KEY_VALUE: eraseKey0(m.getFqn(), m.getKey()); break; case REMOVE_NODE: erase0(m.getFqn()); break; case MOVE: move(m.getFqn(), m.getFqn2()); break; default: throw new CacheException("Unknown modification " + m.getType()); } } 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 IOException { 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. */ void checkOpen() { if (tree == null) { throw new IllegalStateException( "Operation not allowed before calling create()"); } } /** * Throws an exception if the environment is not open. */ protected void checkNotOpen() { if (tree != null) { throw new IllegalStateException( "Operation not allowed after calling create()"); } } /** * Throws an exception if the parameter is null. */ protected 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(""); } /** * Returns the number of database record nodes. */ public int size() { return tree.size(); } @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-3.2.8.GA/src/main/java/org/jboss/cache/loader/jdbm/JdbmCacheLoader2.java0000644000175000017500000001473211240253605030375 0ustar moellermoeller/* * 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.loader.jdbm; 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.Fqn; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * Optimized version of the {@link JdbmCacheLoader} implementation which is better * for storing data under a single node. Speed is improved. *

    * Data is stored using an FQN to Map association, where the depth is prefixed to the FQN: *

     * 0/ = NodeData
     * 1/node1 = Node Data
     * 1/node2 = Node Data
     * 2/node2/node3 = Node Data
     * 2/node2/node4 = Node Data
     * 
    *

    * Browse operations lock the entire tree; eventually the JDBM team plans to fix this. * * @author Elias Ross * @version $Id: JdbmCacheLoader.java 7086 2008-11-05 18:39:35Z genman $ */ @ThreadSafe public class JdbmCacheLoader2 extends JdbmCacheLoader { private static final Log log = LogFactory.getLog(JdbmCacheLoader2.class); private static final boolean trace = log.isTraceEnabled(); /** * Max number of dummy parent nodes to cache. */ private static final int PARENT_CACHE_SIZE = 100; /** * 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()); @Override JdbmCacheLoader2Config createConfig(IndividualCacheLoaderConfig base) { return new JdbmCacheLoader2Config(base); } /** * Adds a depth number to the start of the FQN. */ private Fqn withDepth(Fqn name) { return withDepth(name, name.size()); } /** * Adds a depth number to the start of the FQN. */ private Fqn withDepth(Fqn name, int depth) { Fqn n = Fqn.fromElements(depth); return Fqn.fromRelativeList(n, name.peekElements()); } @Override Set getChildrenNames0(Fqn name) throws IOException { Fqn name2 = withDepth(name, name.size() + 1); TupleBrowser browser = tree.browse(name2); Tuple t = new Tuple(); Set set = new HashSet(); while (browser.getNext(t)) { Fqn fqn = (Fqn) t.getKey(); if (!fqn.isChildOf(name2)) { break; } set.add(fqn.getLastElement()); } if (set.isEmpty()) { return null; } return Collections.unmodifiableSet(set); } @Override public Map get(Fqn name) throws Exception { checkOpen(); checkNonNull(name, "name"); return (Map) tree.find(withDepth(name)); } @Override public boolean exists(Fqn name) throws IOException { return tree.find(withDepth(name)) != null; } @Override Object put0(Fqn name, Object key, Object value) throws Exception { checkNonNull(name, "name"); ensureParent(name); Fqn dname = withDepth(name); Map map = (Map) tree.find(dname); Object oldValue = null; if (map != null) { oldValue = map.put(key, value); tree.insert(dname, map, true); } else { map = new HashMap(); map.put(key, value); tree.insert(dname, map, false); } return oldValue; } @Override void put0(Fqn name, Map values) throws IOException { if (trace) { log.trace("put " + name + " values=" + values); } ensureParent(name); Fqn dname = withDepth(name); if (values == null) values = emptyMap(); else values = new HashMap(values); tree.insert(dname, values, true); } private Map emptyMap() { return new HashMap(0); } /** * Ensures a parent node exists. * Calls recursively to initialize parents as necessary. */ private void ensureParent(Fqn name) throws IOException { if (name.size() <= 1) return; Fqn parent = name.getParent(); if (parents.contains(parent)) return; if (!exists(parent)) put0(parent, emptyMap()); parents.add(parent); if (parents.size() > PARENT_CACHE_SIZE) { parents.clear(); } } @Override void erase0(Fqn name, boolean prune) throws IOException { if (trace) { log.trace("erase " + name + " prune=" + prune); } if (!prune) { put0(name, emptyMap()); return; } Set children = getChildrenNames(name); if (children != null) { log.trace("remove children: " + children); for (Object child : children) { erase0(Fqn.fromRelativeElements(name, child), true); } } parents.remove(name); try { tree.remove(withDepth(name)); } catch (IllegalArgumentException e) { log.trace("remove non-existant key? " + e); } } @Override Object eraseKey0(Fqn name, Object key) throws IOException { Fqn dname = withDepth(name); Map map = (Map) tree.find(dname); Object oldValue = null; if (map != null) { oldValue = map.remove(key); tree.insert(dname, map, true); } return oldValue; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/loader/jdbm/Null.java0000644000175000017500000000264311111047320026264 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/loader/jdbm/JdbmCacheLoaderConfig.java0000644000175000017500000000525211116343407031440 0ustar moellermoeller/* * 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.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(); } void setClassName() { setClassName(JdbmCacheLoader.class.getName()); } /** * For use by {@link JdbmCacheLoader}. * * @param base generic config object created by XML parsing. */ JdbmCacheLoaderConfig(IndividualCacheLoaderConfig base) { setClassName(); 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-3.2.8.GA/src/main/java/org/jboss/cache/loader/AbstractDelegatingCacheLoader.java0000644000175000017500000001165411111047320032242 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/loader/JDBCCacheLoaderOldConfig.java0000644000175000017500000000344711111047320031003 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/loader/JDBCCacheLoaderConfig.java0000644000175000017500000001105311242502761030346 0ustar moellermoeller/* * 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.loader; 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 String recursiveChildrenSql; private String nodeCountSql; private boolean batchEnabled = true;//by default enable batching during state transfer private long batchSize = 1000; //default state transfer batch size 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); deleteNodeSql = constructDeleteNodeSql(); recursiveChildrenSql = constructRecursiveChildrenSql(); nodeCountSql = constructNodeCountSql(); batchEnabled = Boolean.valueOf(props.getProperty("cache.jdbc.batch.enable")); if (props.containsKey("cache.jdbc.batch.size")) { batchSize = Long.parseLong(props.getProperty("cache.jdbc.batch.size")); } } /** * Returns the sql string for removing a node and all its children. */ @Override public String getDeleteNodeSql() { if (deleteNodeSql == null) deleteNodeSql = constructDeleteNodeSql(); return deleteNodeSql; } /** * Returns an sql that will return a node and all its children. */ public String getRecursiveChildrenSql() { if (recursiveChildrenSql == null) recursiveChildrenSql = 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 (nodeCountSql == null) nodeCountSql = constructNodeCountSql(); return nodeCountSql; } public void setNodeCountSql(String nodeCountSql) { this.nodeCountSql = nodeCountSql; } @Deprecated public String getSqlConcat() { return ""; } @Deprecated public void setSqlConcat(String sqlConcat) { } /** * If batch is enabled certain operations (e.g. state transfer) will use {@link java.sql.PreparedStatement#addBatch(String)} * approach for insertig data into the database. This normally brings significant performance improvements. * * @return */ public boolean isBatchEnabled() { return batchEnabled; } /** * The statement will be flushed after batching batchSize operations. * * @see #isBatchEnabled() */ public long getBatchSize() { return batchSize; } private String constructNodeCountSql() { return "SELECT COUNT(*) FROM " + table; } private String constructRecursiveChildrenSql() { return "SELECT " + fqnColumn + "," + nodeColumn + " FROM " + table + " WHERE " + fqnColumn + " = ? OR " + fqnColumn + " LIKE ? ESCAPE '^'"; } @Override protected String constructDeleteNodeSql() { return "DELETE FROM " + table + " WHERE " + fqnColumn + " = ? OR " + fqnColumn + " LIKE ? ESCAPE '^'"; } }jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/loader/tcp/0000755000175000017500000000000011675222125024371 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/loader/tcp/TcpCacheServer.java0000644000175000017500000004231111133375606030100 0ustar moellermoeller/* * 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.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 org.jboss.cache.util.concurrent.ConcurrentHashSet; import org.jboss.cache.util.concurrent.SynchronizedRestarter; 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.HashSet; 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 7463 2009-01-14 14:49:42Z 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 Set conns = new ConcurrentHashSet(); private final SynchronizedRestarter restarter = new SynchronizedRestarter(); /** * 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; private static final Log log = LogFactory.getLog(TcpCacheServer.class); private final static boolean trace = log.isTraceEnabled(); 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() { boolean attemptRestart = false; 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! Attempting a server restart", se); attemptRestart = true; } } catch (IOException e) { log.error("Caught exception! Attempting a server restart", e); attemptRestart = true; } if (attemptRestart) { try { restarter.restartComponent(TcpCacheServer.this); } catch (Exception e) { throw new CacheException("Unable to restart TcpCacheServer", 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(); } catch (IOException e) { // nada } srv_sock = null; } } 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(" + sock.getPort() + ")"); t.setDaemon(true); t.start(); } public void close() { t = null; try { if (output != null) output.close(); } catch (Throwable th) { if (trace) log.trace("Unable to close resource", th); } try { if (input != null) input.close(); } catch (Throwable th) { if (trace) log.trace("Unable to close resource", th); } try { if (sock != null) sock.close(); } catch (Throwable th) { if (trace) log.trace("Unable to close resource", 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()) && t.isAlive()) { try { if (trace) log.trace("Reading next byte"); op = input.readByte(); } catch (IOException e) { log.debug("Client closed socket"); close(); break; } try { if (trace) log.trace("Resetting output"); 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() : new HashSet(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 = c.getNode(fqn); if (n == null) { // node doesn't exist - return null output.writeObject(null); break; } Map map = n.getData(); if (map == null) { map = Collections.emptyMap(); } 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; } if (trace) log.trace("Flushing stream"); 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-3.2.8.GA/src/main/java/org/jboss/cache/loader/tcp/TcpCacheServerMBean.java0000644000175000017500000000342311133375606031004 0ustar moellermoeller/* * 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.loader.tcp; import org.jboss.cache.Cache; import org.jboss.cache.Lifecycle; 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 7463 2009-01-14 14:49:42Z manik.surtani@jboss.com $ */ public interface TcpCacheServerMBean extends Lifecycle { String getBindAddress(); void setBindAddress(String bind_addr) throws UnknownHostException; int getPort(); void setPort(int port); String getConfig(); void setConfig(String config); Cache getCache(); void setCacheJmxWrapper(CacheJmxWrapperMBean wrapper); String getConnections(); } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/loader/tcp/TcpCacheOperations.java0000644000175000017500000000302311111047320030733 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/loader/SingletonStoreDefaultConfig.java0000644000175000017500000001374711111047320032057 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/loader/TcpDelegatingCacheLoaderConfig.java0000644000175000017500000001216711122167554032371 0ustar moellermoeller/* * 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.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; private int readTimeout = 0; 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; } public int getReadTimeout() { return readTimeout; } public void setReadTimeout(int readTimeout) { testImmutability("readTimeout"); this.readTimeout = readTimeout; } @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); } s = props.getProperty("readTimeout"); if (s != null && s.length() > 0) { this.readTimeout = 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) && (readTimeout == other.readTimeout); } 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; result = 31 * result + readTimeout; return result; } @Override public TcpDelegatingCacheLoaderConfig clone() throws CloneNotSupportedException { return (TcpDelegatingCacheLoaderConfig) super.clone(); } }jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/loader/ReadOnlyDelegatingCacheLoader.java0000644000175000017500000000716611111047320032217 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/loader/C3p0ConnectionFactory.java0000644000175000017500000001014311133721130030507 0ustar moellermoeller/* * 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.loader; import com.mchange.v2.c3p0.DataSources; import com.mchange.v2.c3p0.PooledDataSource; 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 PooledDataSource ds; private Properties config = new Properties(); @Override public void setConfig(AdjListJDBCCacheLoaderConfig config) { super.setConfig(config); writeProps(config.getProperties()); writeProps(System.getProperties()); if (trace) log.trace("Using props: " + this.config); } private void writeProps(Properties properties) { Enumeration e = properties.propertyNames(); while (e.hasMoreElements()) { String property = (String) e.nextElement(); if (property.startsWith("c3p0.")) { String newName = property.substring("c3p0.".length()); this.config.put(newName, properties.get(property)); } } } @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 = (PooledDataSource) DataSources.pooledDataSource(unpooled, config); if (log.isDebugEnabled()) { log.debug("Pooled datasource(url=" + getUrl() + ",usr=" + getUsr() + ",pwd=" + getPwd() + ") started."); } } @Override public Connection checkoutConnection() throws SQLException { if (trace) { log.trace("DataSource before checkout (NumBusyConnectionsAllUsers) : " + ds.getNumBusyConnectionsAllUsers()); log.trace("DataSource before checkout (NumConnectionsAllUsers) : " + ds.getNumConnectionsAllUsers()); } Connection connection = ds.getConnection(); if (trace) { log.trace("DataSource after checkout (NumBusyConnectionsAllUsers) : " + ds.getNumBusyConnectionsAllUsers()); log.trace("DataSource after checkout (NumConnectionsAllUsers) : " + ds.getNumConnectionsAllUsers()); 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-3.2.8.GA/src/main/java/org/jboss/cache/loader/AsyncCacheLoaderConfig.java0000644000175000017500000001124111111047320030706 0ustar moellermoeller/* * 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.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; private int threadPoolSize = 1; /** * 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 getThreadPoolSize() { return threadPoolSize; } public void setThreadPoolSize(int threadPoolSize) { testImmutability("threadPoolSize"); this.threadPoolSize = threadPoolSize; } 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 batch size: " + batchSize); s = props.getProperty("cache.async.threadPoolSize"); if (s != null) threadPoolSize = Integer.parseInt(s); if (threadPoolSize <= 0) throw new IllegalArgumentException("Invalid thread pool size: " + threadPoolSize); 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-3.2.8.GA/src/main/java/org/jboss/cache/loader/AdjListJDBCCacheLoaderConfig.java0000644000175000017500000004560711242502714031633 0ustar moellermoeller/* * 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.loader; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.util.Util; import java.util.Properties; /** * AdjListJDBCCacheLoaderConfig * * @author Manik Surtani (manik AT jboss DOT 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"; protected boolean createTable = CREATE_TABLE_DEFAULT; protected String createTableDDL; protected String datasourceName; protected String deleteAllSql; protected String deleteNodeSql; protected boolean dropTable = DROP_TABLE_DEFAULT; protected String dropTableDDL; protected String driverClass; protected String insertNodeSql; protected String jdbcURL; protected String jdbcUser; protected String jdbcPassword; protected String selectChildFqnsSql; protected String selectChildNamesSql; protected String selectNodeSql; protected String updateNodeSql; protected String updateTableSql; protected String existsSql; protected String connectionFactoryClass; protected String primaryKey = PRIMARY_KEY_DEFAULT; protected String fqnType = FQN_TYPE_DEFAULT; protected String nodeType = NODE_TYPE_DEFAULT; protected 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 getDummyTable() { return table + "_D"; } 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"); } protected String constructDropTableDDL() { return "DROP TABLE " + table; } protected String constructCreateTableDDL() { // removed CONSTRAINT clause as this causes problems with some databases, like Informix. return "CREATE TABLE " + table + "(" + fqnColumn + " " + fqnType + " NOT NULL, " + nodeColumn + " " + nodeType + ", " + parentColumn + " " + fqnType + ", 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(); } protected String constructSelectNodeSql() { return "SELECT " + nodeColumn + " FROM " + table + " WHERE " + fqnColumn + " = ?"; } protected String constructUpdateNodeSql() { return "UPDATE " + table + " SET " + nodeColumn + " = ? WHERE " + fqnColumn + " = ?"; } protected String constructDeleteAllSql() { return "DELETE FROM " + table; } protected String constructDeleteNodeSql() { return "DELETE FROM " + table + " WHERE " + fqnColumn + " = ?"; } protected String constructSelectChildNamesSql() { return "SELECT " + fqnColumn + " FROM " + table + " WHERE " + parentColumn + " = ?"; } protected String constructExistsSql() { return "SELECT '1' FROM " + table + " WHERE " + fqnColumn + " = ?"; } protected 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-3.2.8.GA/src/main/java/org/jboss/cache/loader/NonManagedConnectionFactory.java0000644000175000017500000001362611111047320032020 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/loader/bdbje/0000755000175000017500000000000011675222130024645 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/loader/bdbje/BdbjeCacheLoader.java0000644000175000017500000010246711113556312030602 0ustar moellermoeller/* * 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.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.marshall.NodeData; 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 Cache.

    *

    *

    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 7218 2008-11-27 17:45:46Z 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(); } protected void storeStateHelper(Fqn subtree, List nodeData, boolean moveToBuddy) throws Exception { for (Object aNodeData : nodeData) { NodeData nd = (NodeData) aNodeData; if (nd.isMarker()) { if (log.isTraceEnabled()) log.trace("Reached delimiter; exiting loop"); break; } Fqn fqn; if (moveToBuddy) { fqn = buddyFqnTransformer.getBackupFqn(subtree, nd.getFqn()); } else { fqn = nd.getFqn(); } if (log.isTraceEnabled()) log.trace("Storing state in Fqn " + fqn); 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 } } } /** * 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); } // JBCACHE-1448 db name parsing fix courtesy of Ciro Cavani /* Parse config string. */ int offset = configStr.indexOf('#'); if (offset >= 0 && offset < configStr.length() - 1) { cacheDbName = configStr.substring(offset + 1); configStr = configStr.substring(0, offset); } else { cacheDbName = cache.getClusterName(); if (cacheDbName == null) cacheDbName = "CacheInstance-" + System.identityHashCode(cache); } // 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!"); } 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 " + location); env = new Environment(location, 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 if (values != null && !(values instanceof HashMap)) { values = 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.put(keyEntry, dataEntry); if (status == OperationStatus.SUCCESS) { createParentNodes(cursor, name); } } 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 & !onePhase) { throw new UnsupportedOperationException( "prepare() not allowed with a non-transactional cache loader"); } else if (onePhase) { for (Modification modification : modifications) { } } 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-3.2.8.GA/src/main/java/org/jboss/cache/loader/bdbje/BdbjeCacheLoaderConfig.java0000644000175000017500000000522711111047320031714 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/loader/ConnectionFactory.java0000644000175000017500000000323311111047320030061 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/loader/LocalDelegatingCacheLoader.java0000755000175000017500000001271411111047320031532 0ustar moellermoeller/* * 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.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 7168 2008-11-19 17:37:20Z jason.greene@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-3.2.8.GA/src/main/java/org/jboss/cache/loader/CacheLoader.java0000755000175000017500000003746411133375606026623 0ustar moellermoeller/* * 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.loader; import net.jcip.annotations.ThreadSafe; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Lifecycle; 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 AT jboss DOT org) * @see CacheSPI * @see org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig * @see org.jboss.cache.loader.AbstractCacheLoader * @since 2.0.0 */ @ThreadSafe public interface CacheLoader extends Lifecycle { /** * 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 *

    * Note since 3.0, as an optimization, this method will require a definitive attribute map and * not just a subset. This will allow cache loader implementations to overwrite rather than merge, if that is * deemed more efficient. This will not break backward compatibility since performing a merge will not cause * any loss of data even though it is an unnecessary step. * * @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 its 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); } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/loader/FileCacheLoader.java0000755000175000017500000004107011135645240027403 0ustar moellermoeller/* * 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.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 its 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 its 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 7564 2009-01-21 16:17:36Z mircea.markus $ */ @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 = -1; try { osVersion = Float.parseFloat(System.getProperty("os.version").trim()); } catch (Exception e) { if (log.isTraceEnabled()) log.trace("Unable to determine operating system version!"); } // 4.x is windows NT/2000 and 5.x is XP. isOldWindows = System.getProperty("os.name").toLowerCase().startsWith("windows") && osVersion < 4; } public void setConfig(IndividualCacheLoaderConfig base) { if (base instanceof FileCacheLoaderConfig) { this.config = (FileCacheLoaderConfig) base; } else if (base != null) { this.config = new FileCacheLoaderConfig(base); } } public IndividualCacheLoaderConfig getConfig() { return config; } @Override public void create() throws Exception { lock.acquireLock(Fqn.ROOT, true); String location = this.config != null ? this.config.getLocation() : null; if (location != null && location.length() > 0) { root = new File(location); rootPath = root.getAbsolutePath() + File.separator; } 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, true); } @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(); if (subdirs == null) { if (log.isWarnEnabled()) log.warn("Null list of files for dir: " + dir.getAbsolutePath()); return false; } 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 { if (attrs != null && !(attrs instanceof HashMap)) throw new RuntimeException("Unsupporte dmap type " + attrs.getClass()); regionAwareMarshall(fqn, attrs); } @Override protected void doMarshall(Fqn fqn, Object toMarshall) throws Exception { Map attrs = (Map) toMarshall; if (attrs != null && !(attrs instanceof HashMap)) throw new RuntimeException("Map is " + attrs.getClass()); 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-3.2.8.GA/src/main/java/org/jboss/cache/PessimisticUnversionedNode.java0000644000175000017500000003176411253465613030542 0ustar moellermoeller/* * 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; import static org.jboss.cache.AbstractNode.NodeFlags.VALID; import org.jboss.cache.commands.CommandsFactory; import org.jboss.cache.commands.legacy.write.CreateNodeCommand; import org.jboss.cache.lock.IdentityLock; import org.jboss.cache.lock.LockStrategyFactory; import org.jboss.cache.lock.PessimisticNodeBasedLockManager; import org.jboss.cache.marshall.MarshalledValue; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.util.FastCopyHashMap; import org.jboss.cache.util.Immutables; import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentMap; /** * UnversionedNode specific to pessimistic locking, with legacy code. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 * @deprecated will be removed along with optimistic and pessimistic locking. */ @SuppressWarnings("deprecation") @Deprecated public class PessimisticUnversionedNode extends UnversionedNode { /** * 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 volatile IdentityLock lock = null; protected LockStrategyFactory lockStrategyFactory; CommandsFactory commandsFactory; protected NodeFactory nodeFactory; public PessimisticUnversionedNode(Object name, Fqn fqn, Map data, CacheSPI cache) { super(fqn, cache, false); if (!fqn.isRoot() && !name.equals(fqn.getLastElement())) throw new IllegalArgumentException("Child " + name + " must be last part of " + fqn); if (data != null && !data.isEmpty()) setInternalState(data); else this.data = new FastCopyHashMap(); setLockForChildInsertRemove(cache != null && cache.getConfiguration() != null && cache.getConfiguration().isLockParentForChildInsertRemove()); } /** * @return a genericized version of the child map. */ @SuppressWarnings("unchecked") private ConcurrentMap> children() { return children; } // ------ lock-per-node paradigm public void injectLockStrategyFactory(LockStrategyFactory lockStrategyFactory) { this.lockStrategyFactory = lockStrategyFactory; } public void injectDependencies(CacheSPI spi, CommandsFactory commandsFactory, NodeFactory nodeFactory) { this.cache = spi; this.commandsFactory = commandsFactory; this.nodeFactory = nodeFactory; } protected synchronized void initLock() { if (lock == null) { lock = new IdentityLock(lockStrategyFactory, delegate); } } @Override public IdentityLock getLock() { // this.lock is volatile, so double-checked locking is ok if (lock == null) { initLock(); } return lock; } @Override public String toString() { StringBuilder sb = new StringBuilder(super.toString()); if (lock != null) { if (lock.isReadLocked()) { sb.append(" RL"); } if (lock.isWriteLocked()) { sb.append(" WL"); } } return sb.toString(); } @SuppressWarnings("unchecked") @Override public InternalNode copy() { PessimisticUnversionedNode n = new PessimisticUnversionedNode(fqn.getLastElement(), fqn, data, cache); copyInternals(n); n.children = children; n.lockStrategyFactory = lockStrategyFactory; n.commandsFactory = commandsFactory; n.nodeFactory = nodeFactory; return n; } // ------ legacy addChild methods that used a lot of implicit locks. @Override public void addChildDirect(NodeSPI child) { Fqn childFqn = child.getFqn(); if (childFqn.isDirectChildOf(fqn)) { synchronized (this) { children().put(child.getFqn().getLastElement(), child); } } else throw new CacheException("Attempting to add a child [" + child.getFqn() + "] to [" + getFqn() + "]. Can only add direct children."); } private NodeSPI getOrCreateChild(Object childName, GlobalTransaction gtx, boolean createIfNotExists, boolean notify, PessimisticNodeBasedLockManager.LockAcquirer la) { NodeSPI child; if (childName == null) { throw new IllegalArgumentException("null child name"); } child = (NodeSPI) children().get(childName); 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 childFqn = Fqn.fromRelativeElements(fqn, childName); NodeSPI newChild = nodeFactory.createNode(childFqn, delegate); if (newChild == null) { throw new IllegalStateException(); } synchronized (this) { // check again to see if the child exists // after acquiring exclusive lock child = (NodeSPI) children().get(childName); if (child == null) { if (notify) cache.getNotifier().notifyNodeCreated(childFqn, true, ctx); child = newChild; if (la != null) la.acquire(child); children().put(childName, child); } } // notify if we actually created a new child if (newChild == child) { if (trace) log.trace("created child: fqn=" + childFqn); if (gtx != null) { CreateNodeCommand createNodeCommand = commandsFactory.buildCreateNodeCommand(childFqn); ctx.getTransactionContext().addLocalModification(createNodeCommand); } if (notify) cache.getNotifier().notifyNodeCreated(childFqn, false, ctx); } } return child; } @Override public NodeSPI addChildDirect(Fqn f, boolean notify) { if (f.size() == 1) { GlobalTransaction gtx = cache.getInvocationContext().getGlobalTransaction(); return getOrCreateChild(f.getLastElement(), gtx, true, notify, null); } else { throw new UnsupportedOperationException("Cannot directly create children which aren't directly under the current node."); } } @Override public NodeSPI addChildDirect(Object o, boolean notify) { GlobalTransaction gtx = cache.getInvocationContext().getGlobalTransaction(); return getOrCreateChild(o, gtx, true, notify, null); } public NodeSPI addChildAndAcquireLock(Object o, boolean notify, PessimisticNodeBasedLockManager.LockAcquirer la) { GlobalTransaction gtx = cache.getInvocationContext().getGlobalTransaction(); return getOrCreateChild(o, gtx, true, notify, la); } @Override 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; } } @Override public NodeSPI getChildDirect(Object childName) { if (childName == null) return null; return (NodeSPI) children().get(childName); } @Override 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); } exclDeleted = Collections.unmodifiableSet(exclDeleted); return exclDeleted; } @Override public Map> getChildrenMapDirect() { return children(); } @Override public void setChildrenMapDirect(Map> children) { if (children == null) this.children = null; else { this.children.clear(); this.children().putAll(children); } } @Override public void addChildDirect(Object nodeName, Node nodeToAdd) { if (nodeName != null) { children().put(nodeName, nodeToAdd); } } @SuppressWarnings("unchecked") @Override public Set> getChildrenDirect(boolean includeMarkedForRemoval) { if (includeMarkedForRemoval) { if (children != null && !children.isEmpty()) { return Immutables.immutableSetConvert(children.values()); } else { return Collections.emptySet(); } } else { return getChildrenDirect(); } } @Override public NodeSPI addChildDirect(Fqn f) { return addChildDirect(f, true); } @Override public NodeSPI getOrCreateChild(Object childName, GlobalTransaction gtx) { return getOrCreateChild(childName, gtx, true, true, null); } @Override public void markAsRemoved(boolean marker, boolean recursive) { setFlag(NodeFlags.REMOVED, marker); if (recursive && children != null) { synchronized (this) { for (Node child : children().values()) { ((NodeSPI) child).markAsDeleted(marker, true); } } } } @Override 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); } } } /** * Adds details of the node into a map as strings. */ @Override protected void printDetailsInMap(StringBuilder sb, int indent) { printIndent(sb, indent); indent += 2;// increse it sb.append(Fqn.SEPARATOR); if (!fqn.isRoot()) sb.append(fqn.getLastElement()); sb.append(" "); sb.append(data); for (Node n : children().values()) { sb.append("\n"); ((NodeSPI) n).printDetails(sb, indent); } } @Override 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); } } @Override public void releaseObjectReferences(boolean recursive) { if (recursive && children != null) { for (Node child : children().values()) { child.releaseObjectReferences(recursive); } } if (data != null) { for (K 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-3.2.8.GA/src/main/java/org/jboss/cache/Version.java0000644000175000017500000001330111622063211024612 0ustar moellermoeller/* * 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; import net.jcip.annotations.Immutable; /** * Contains version information about this release of JBoss Cache. * * @author Bela Ban * @version $Id: Version.java 8464 2011-08-15 00:21:29Z dereed $ */ @Immutable public class Version { public static final String version = "3.2.8.GA"; public static final String codename = "Malagueta"; //public static final String cvs = "$Id: Version.java 8464 2011-08-15 00:21:29Z dereed $"; static final byte[] version_id = {'0','3','2','8','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-3.2.8.GA/src/main/java/org/jboss/cache/DefaultCacheFactory.java0000644000175000017500000001503411237013623027036 0ustar moellermoeller/* * 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; import org.jboss.cache.annotations.Compat; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.config.parsing.XmlConfigurationParser; import org.jboss.cache.config.parsing.XmlConfigurationParser2x; import org.jboss.cache.factories.ComponentFactory; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.invocation.CacheInvocationDelegate; import org.jboss.cache.jmx.PlatformMBeanServerRegistration; 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}. *

    * * @author Manik Surtani (manik AT jboss DOT org) * @see org.jboss.cache.factories.ComponentFactory */ public class DefaultCacheFactory extends ComponentFactory implements CacheFactory { private ClassLoader defaultClassLoader; /** * This is here for backward compatibility only. Use new DefaultCacheFactory() instead. * * @deprecated */ @SuppressWarnings("unchecked") @Deprecated @Compat 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; try { c = parser.parseFile(configFileName); } catch (ConfigurationException e) { XmlConfigurationParser2x oldParser = new XmlConfigurationParser2x(); c = oldParser.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); componentRegistry.registerComponent(new PlatformMBeanServerRegistration(), PlatformMBeanServerRegistration.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 = null; try { c = parser.parseStream(is); } catch (ConfigurationException e) { XmlConfigurationParser2x oldParser = new XmlConfigurationParser2x(); c = oldParser.parseStream(is); if (c != null && log.isInfoEnabled()) log.info("Detected legacy configuration file format when parsing configuration file. Migrating to the new (3.x) file format is recommended. See FAQs for details."); } 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-3.2.8.GA/src/main/java/org/jboss/cache/RegionManagerImpl.java0000644000175000017500000006251611245232602026544 0ustar moellermoeller/* * 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; import net.jcip.annotations.ThreadSafe; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import static org.jboss.cache.Region.Type.ANY; import static org.jboss.cache.Region.Type.EVICTION; import static org.jboss.cache.Region.Type.MARSHALLING; 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.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.Start; import org.jboss.cache.factories.annotations.Stop; import org.jboss.cache.jmx.annotations.MBean; import org.jboss.cache.jmx.annotations.ManagedAttribute; import org.jboss.cache.jmx.annotations.ManagedOperation; import org.jboss.cache.lock.LockManager; import org.jboss.cache.util.concurrent.locks.LockContainer; import org.jboss.cache.util.concurrent.locks.ReentrantSharedLockContainer; import org.jgroups.Address; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; /** * The default region manager, used with MVCC locking. * * @author Manik Surtani * @since 3.0.0 */ @ThreadSafe @MBean(objectName = "RegionManager", description = "Manages eviction and marshalling regions") public class RegionManagerImpl implements 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_"); private RegionRegistry regionsRegistry; private boolean defaultInactive; protected static final Log log = LogFactory.getLog(RegionManagerImpl.class); protected static final boolean trace = log.isTraceEnabled(); CacheSPI cache; private boolean usingEvictions; private EvictionConfig evictionConfig; private final EvictionTimerTask evictionTimerTask = new EvictionTimerTask(); private final LockContainer regionLocks = new ReentrantSharedLockContainer(4); protected Configuration configuration; protected RPCManager rpcManager; protected LockManager lockManager; protected BuddyFqnTransformer buddyFqnTransformer; private boolean isUsingBR; // -------- region lock helpers protected final boolean isRegionLocked(Fqn fqn) { return regionLocks.isLocked(fqn); } protected final void lock(Fqn fqn) { regionLocks.acquireLock(fqn); } protected final void unlock(Fqn fqn) { regionLocks.releaseLock(fqn); } @Inject public void injectDependencies(CacheSPI cache, Configuration configuration, RPCManager rpcManager, LockManager lockManager, BuddyFqnTransformer transformer, RegionRegistry regionsRegistry) { this.cache = cache; this.rpcManager = rpcManager; this.configuration = configuration; this.lockManager = lockManager; this.buddyFqnTransformer = transformer; this.regionsRegistry = regionsRegistry; } @Start public void start() { if (trace) log.trace("Starting region manager"); isUsingBR = configuration.getBuddyReplicationConfig() != null && configuration.getBuddyReplicationConfig().isEnabled(); evictionConfig = configuration.getEvictionConfig(); if (evictionConfig != null && evictionConfig.isValidConfig()) { this.evictionConfig = configuration.getEvictionConfig(); // start with the default region EvictionRegionConfig defaultRegion = configuration.getEvictionConfig().getDefaultEvictionRegionConfig(); if (defaultRegion.getEvictionAlgorithmConfig() != null) defaultRegion.getEvictionAlgorithmConfig().validate(); // validate individual region configs now for (EvictionRegionConfig erc : configuration.getEvictionConfig().getEvictionRegionConfigs()) { evictionConfig.applyDefaults(erc); erc.validate(); } setEvictionConfig(configuration.getEvictionConfig()); setUsingEvictions(true); } else { setUsingEvictions(false); log.debug("Not using an EvictionPolicy"); } setDefaultInactive(configuration.isInactiveOnStartup()); if (isUsingEvictions()) { evictionTimerTask.init(evictionConfig.getWakeupInterval(), configuration.getRuntimeConfig().getEvictionTimerThreadFactory(), regionsRegistry); } } @Stop protected void stop() { if (isUsingEvictions()) evictionTimerTask.stop(); } @Destroy protected void destroy() { regionsRegistry.clear(); regionLocks.reset(); } public boolean isUsingEvictions() { return usingEvictions; } public boolean isDefaultInactive() { return defaultInactive; } public void setDefaultInactive(boolean defaultInactive) { this.defaultInactive = defaultInactive; Region defaultRegion = regionsRegistry.get(Fqn.ROOT); if (defaultRegion != null) defaultRegion.setActive(!defaultInactive); } 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); } } public Region getRegion(Fqn fqn, boolean createIfAbsent) { return getRegion(fqn, ANY, createIfAbsent); } public Region getValidMarshallingRegion(Fqn fqn) { if (fqn == null) return null; return getRegion(fqn, Region.Type.MARSHALLING, false); } public Region getRegion(Fqn fqn, Region.Type type, boolean createIfAbsent) { if (isUsingBR && fqn != null && buddyFqnTransformer.isBackupFqn(fqn)) { fqn = buddyFqnTransformer.getActualFqn(fqn); } if (trace) log.trace("Contents of RegionsRegistry: " + regionsRegistry); if (DEFAULT_REGION.equals(fqn)) fqn = Fqn.ROOT; // first see if a region for this specific Fqn exists Region r = regionsRegistry.get(fqn); if (r != null) { // 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.getEvictionRegionConfig() != null)) { return r; } } // if not, attempt to create one ... if (createIfAbsent) { r = new RegionImpl(fqn, this); // could be created concurrently; so make sure we use appropriate methods on regionsRegistry for this. Region previous = regionsRegistry.putIfAbsent(fqn, r); if (previous != null) r = previous; 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; if (fqn == null || fqn.isRoot()) return null; Fqn nextFqn = fqn; // Truncate Fqn as an optimization int largestFqnLength = regionsRegistry.getLargestFqnLength(); if (nextFqn.size() > largestFqnLength) nextFqn = nextFqn.getSubFqn(0, largestFqnLength); while (nextBestThing == null) { r = regionsRegistry.get(nextFqn); if (r != null) { if (trace) 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.getEvictionRegionConfig() != null)) { nextBestThing = r; } } if (nextFqn.isRoot()) break; nextFqn = nextFqn.getParent(); } // 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; } public Region getRegion(String fqn, boolean createIfAbsent) { return getRegion(Fqn.fromString(fqn), createIfAbsent); } public boolean removeRegion(Fqn fqn) { Region r = regionsRegistry.remove(fqn); if (r == null) return false; return true; } public EvictionTimerTask getEvictionTimerTask() { return evictionTimerTask; } public Configuration getConfiguration() { return configuration; } public void activate(Fqn fqn) throws RegionNotEmptyException { activate(fqn, false); } public void activateIfEmpty(Fqn fqn) { activate(fqn, true); } private void activate(Fqn fqn, boolean suppressRegionNotEmptyException) { try { if (trace) 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.getNode(fqn); // NOTE this used to be a peek! 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 lock(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. Node root = cache.getRoot(); cache.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); subtreeRoot = root.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! NodeSPI root = cache.getRoot(); cache.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); subtreeRoot = root.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 { unlock(fqn); } } 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 CacheException if there is a problem evicting nodes * @throws IllegalStateException if {@link org.jboss.cache.config.Configuration#isUseRegionBasedMarshalling()} is false */ protected void inactivateRegion(Fqn fqn) throws CacheException { NodeSPI subtreeRoot = null; InvocationContext ctx = cache.getInvocationContext(); ctx.getOptionOverrides().setLockAcquisitionTimeout((int) (cache.getConfiguration().getLockAcquisitionTimeout() + 5000)); try { // Record that this fqn is in status change, so can't provide state lock(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)); } } } // Remove the subtree from the main cache and any buddy backup trees for (Fqn subtree : list) { subtreeRoot = cache.peek(subtree, false); if (subtreeRoot != null) { // Remove the subtree cache.evict(subtree, true); } } } finally { unlock(fqn); } } 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.getEvictionRegionConfig() != null; case MARSHALLING: return r.isActive() && r.getClassLoader() != null; } // should never reach here? return false; } 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); } } public void reset() { regionsRegistry.clear(); } 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.getEvictionRegionConfig() != null) || (type == MARSHALLING && r.isActive() && r.getClassLoader() != null)) { regions.add(r); } } } else { // put all regions regions = new ArrayList(regionsRegistry.values()); } Collections.sort(regions); return regions; } public void setUsingEvictions(boolean usingEvictions) { this.usingEvictions = usingEvictions; } public void setEvictionConfig(EvictionConfig evictionConfig) { // JBAS-1288 // Try to establish a default region if there isn't one already // boolean needDefault; List ercs = evictionConfig.getEvictionRegionConfigs(); // 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 (evictionConfig.getDefaultEvictionRegionConfig().getEvictionAlgorithmConfig() != null && !ercs.contains(evictionConfig.getDefaultEvictionRegionConfig())) // then the default is a real region too; not just a template for others { ercs.add(0, evictionConfig.getDefaultEvictionRegionConfig()); } // 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 (fqn == null) throw new ConfigurationException("Regions cannot be configured with a null region fqn. If you configured this region programmatically, ensure that you set the region fqn in EvictionRegionConfig"); if (trace) log.trace("Creating eviction region " + fqn); if (fqn.equals(DEFAULT_REGION) || fqn.isRoot()) { if (setDefault) { throw new ConfigurationException("A default region for evictions has already been set for this cache"); } if (trace) log.trace("Applying settings for default region to Fqn.ROOT"); fqn = Fqn.ROOT; setDefault = true; } Region r = getRegion(fqn, true); evictionConfig.applyDefaults(erc); r.setEvictionRegionConfig(erc); } } @ManagedOperation(description = "A String representation of all registered regions") public String dumpRegions() { StringBuilder sb = new StringBuilder(); if (regionsRegistry != null) { 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; } // --------- for backward compat -------------- /** * Starts the eviction processing thread. */ public void startEvictionThread() { evictionTimerTask.init(evictionConfig.getWakeupInterval(), configuration.getRuntimeConfig().getEvictionTimerThreadFactory(), regionsRegistry); } /** * Stops the eviction processing thread */ public void stopEvictionThread() { evictionTimerTask.stop(); } @ManagedAttribute(name = "numRegions", description = "A count of all regions") public int getNumRegions() { return regionsRegistry.size(); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/RPCManagerImpl.java0000644000175000017500000012132011450761557025750 0ustar moellermoeller/* * 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; 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.Configuration.NodeLockingScheme; import org.jboss.cache.config.ConfigurationException; 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.jmx.annotations.MBean; import org.jboss.cache.jmx.annotations.ManagedAttribute; import org.jboss.cache.jmx.annotations.ManagedOperation; 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.DefaultStateTransferManager; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.TransactionTable; import org.jboss.cache.util.CachePrinter; import org.jboss.cache.util.concurrent.ReclosableLatch; import org.jboss.cache.util.reflect.ReflectionUtil; import org.jgroups.Address; import org.jgroups.Channel; import org.jgroups.ChannelClosedException; import org.jgroups.ChannelException; import org.jgroups.ChannelFactory; import org.jgroups.ChannelNotConnectedException; import org.jgroups.ExtendedMembershipListener; import org.jgroups.JChannel; import org.jgroups.View; import org.jgroups.blocks.GroupRequest; import org.jgroups.blocks.RspFilter; import org.jgroups.protocols.TP; import org.jgroups.protocols.pbcast.STREAMING_STATE_TRANSFER; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Rsp; import org.jgroups.util.RspList; import javax.transaction.TransactionManager; import java.net.URL; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Random; import java.util.Set; import java.util.Vector; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * Manager that handles all RPC calls between JBoss Cache instances * * @author Manik Surtani (manik AT jboss DOT org) */ @MBean(objectName = "RPCManager", description = "Manages RPC connections to remote caches") public class RPCManagerImpl implements RPCManager { private Channel channel; private final Log log = LogFactory.getLog(RPCManagerImpl.class); private final boolean trace = log.isTraceEnabled(); private volatile List

    members; private long replicationCount; private long replicationFailures; private boolean statisticsEnabled = false; private final Object coordinatorLock = new Object(); /** * True if this Cache is the coordinator. */ private volatile boolean coordinator = false; /** * The most recent state transfer source */ volatile Address lastStateTransferSource; /** * JGroups RpcDispatcher in use. */ private CommandAwareRpcDispatcher rpcDispatcher = null; /** * JGroups message listener. */ private ChannelMessageListener messageListener; Configuration configuration; private Notifier notifier; private CacheSPI spi; private InvocationContextContainer invocationContextContainer; private Marshaller marshaller; private TransactionManager txManager; private TransactionTable txTable; private InterceptorChain interceptorChain; private boolean isUsingBuddyReplication; private volatile boolean isInLocalMode; private ComponentRegistry componentRegistry; private LockManager lockManager; private FlushTracker flushTracker; @Inject public 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; } public abstract class FlushTracker { // closed whenever a FLUSH is in progress. Open by default. final ReclosableLatch flushBlockGate = new ReclosableLatch(true); private final AtomicInteger flushCompletionCount = new AtomicInteger(); // closed whenever a FLUSH is NOT in progress. Closed by default. final ReclosableLatch flushWaitGate = new ReclosableLatch(false); public void block() { flushBlockGate.close(); flushWaitGate.open(); } public void unblock() { flushWaitGate.close(); flushCompletionCount.incrementAndGet(); flushBlockGate.open(); } public int getFlushCompletionCount() { return flushCompletionCount.get(); } public abstract void lockProcessingLock() throws InterruptedException; public abstract void unlockProcessingLock(); public abstract void lockSuspendProcessingLock() throws InterruptedException; public abstract void unlockSuspendProcessingLock(); public void waitForFlushCompletion(long timeout) throws InterruptedException { if (channel.flushSupported() && !flushBlockGate.await(timeout, TimeUnit.MILLISECONDS)) { throw new TimeoutException("Timed out waiting for flush to unblock. (timeout = " + CachePrinter.prettyPrint(timeout) + ")"); } } public void waitForFlushStart(long timeout) throws InterruptedException { if (channel.flushSupported() && !flushWaitGate.await(timeout, TimeUnit.MILLISECONDS)) { throw new TimeoutException("Timed out waiting for flush to block. (timeout = " + CachePrinter.prettyPrint(timeout) + " )"); } } } private final class StandardFlushTracker extends FlushTracker { // All locking methods are no-ops public void lockProcessingLock() { } public void lockSuspendProcessingLock() { } public void unlockProcessingLock() { } public void unlockSuspendProcessingLock() { } } private final class NonBlockingFlushTracker extends FlushTracker { private final ReentrantReadWriteLock coordinationLock = new ReentrantReadWriteLock(); public void lockProcessingLock() throws InterruptedException { if (!coordinationLock.readLock().tryLock(configuration.getStateRetrievalTimeout(), TimeUnit.MILLISECONDS)) throw new TimeoutException("Could not obtain processing lock"); } public void unlockProcessingLock() { coordinationLock.readLock().unlock(); } public void lockSuspendProcessingLock() throws InterruptedException { while (true) { try { if (!coordinationLock.writeLock().tryLock(configuration.getStateRetrievalTimeout(), TimeUnit.MILLISECONDS)) throw new TimeoutException("Could not obtain processing lock"); return; } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } } } public void unlockSuspendProcessingLock() { if (coordinationLock.isWriteLockedByCurrentThread()) { coordinationLock.writeLock().unlock(); } } @Override public void waitForFlushCompletion(long timeout) throws InterruptedException { while (true) { try { if (!flushBlockGate.await(timeout, TimeUnit.MILLISECONDS)) throw new TimeoutException("Timed out waiting for flush to unblock. (timeout = " + CachePrinter.prettyPrint(timeout) + ")"); return; } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } } } @Override public void waitForFlushStart(long timeout) throws InterruptedException { while (true) { try { if (!flushWaitGate.await(timeout, TimeUnit.MILLISECONDS)) throw new TimeoutException("Timed out waiting for flush to block. (timeout = " + CachePrinter.prettyPrint(timeout) + ")"); return; } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } } } } // ------------ START: Lifecycle methods ------------ @Start(priority = 15) public void start() { switch (configuration.getCacheMode()) { case LOCAL: if (log.isDebugEnabled()) 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(); boolean nonBlocking = configuration.isNonBlockingStateTransfer(); sanityCheckConfiguration(nonBlocking, fetchState); this.flushTracker = nonBlocking ? new NonBlockingFlushTracker() : new StandardFlushTracker(); initialiseChannelAndRpcDispatcher(fetchState && !nonBlocking, nonBlocking); if (!fetchState || nonBlocking) { try { // Allow commands to be ACKed during state transfer if (nonBlocking) { componentRegistry.setStatusCheckNecessary(false); } channel.connect(configuration.getClusterName()); if (log.isInfoEnabled()) log.info("Cache local address is " + getLocalAddress()); } catch (ChannelException e) { throw new CacheException("Unable to connect to JGroups channel", e); } if (!fetchState) { return; } } long start = System.currentTimeMillis(); if (nonBlocking) { startNonBlockStateTransfer(getMembers()); } else { try { channel.connect(configuration.getClusterName(), null, null, configuration.getStateRetrievalTimeout()); if (log.isInfoEnabled()) log.info("Cache local address is " + getLocalAddress()); if (getMembers().size() > 1 && !isCoordinator()) messageListener.waitForState(); } catch (ChannelException e) { throw new CacheException("Unable to connect to JGroups channel", e); } catch (Exception ex) { // make sure we disconnect from the channel before we throw this exception! // JBCACHE-761 disconnect(); throw new CacheException("Unable to fetch state on startup", ex); } } if (log.isInfoEnabled()) { log.info("state was retrieved successfully (in " + CachePrinter.prettyPrint((System.currentTimeMillis() - start)) + ")"); } } } private void sanityCheckJGroupsStack(JChannel channel) { if (channel.getProtocolStack().findProtocol(STREAMING_STATE_TRANSFER.class) == null) { throw new ConfigurationException("JGroups channel does not use STREAMING_STATE_TRANSFER! This is a requirement for non-blocking state transfer. Either make sure your JGroups configuration uses STREAMING_STATE_TRANSFER or disable non-blocking state transfer."); } } private void sanityCheckConfiguration(boolean nonBlockingStateTransfer, boolean fetchStateOnStart) { if (isInLocalMode || !nonBlockingStateTransfer || !fetchStateOnStart) return; // don't care about these cases! if (configuration.getNodeLockingScheme() != NodeLockingScheme.MVCC) { throw new ConfigurationException("Non-blocking state transfer is only supported with the MVCC node locking scheme. Please change your node locking scheme to MVCC or disable non-blocking state transfer."); } if (isUsingBuddyReplication) { throw new ConfigurationException("Non-blocking state transfer cannot be used with buddy replication at this time. Please disable either buddy replication or non-blocking state transfer."); } } private void startNonBlockStateTransfer(List
    members) { if (members.size() < 2) { if (log.isInfoEnabled()) log.info("Not retrieving state since cluster size is " + members.size()); return; } boolean success = false; int numRetries = 5; int initwait = (1 + new Random().nextInt(10)) * 100; int waitIncreaseFactor = 2; outer: for (int i = 0, wait = initwait; i < numRetries; i++) { for (Address member : members) { if (member.equals(getLocalAddress())) { continue; } try { if (log.isInfoEnabled()) log.info("Trying to fetch state from: " + member); if (getState(null, member)) { messageListener.waitForState(); success = true; break outer; } } catch (Exception e) { if (log.isDebugEnabled()) log.debug("Error while fetching state", e); } } if (!success) { wait *= waitIncreaseFactor; if (log.isWarnEnabled()) { log.warn("Could not find available peer for state, backing off and retrying after " + wait + " millis. Retries left: " + (numRetries - 1 - i)); } try { Thread.sleep(wait); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } if (!success) { disconnect(); throw new CacheException("Unable to fetch state on startup"); } componentRegistry.setStatusCheckNecessary(true); } public void disconnect() { if (channel != null && channel.isOpen()) { if (log.isInfoEnabled()) 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) { if (log.isInfoEnabled()) log.info("Stopping the RpcDispatcher"); rpcDispatcher.stopDispatcher(); } 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 fetchStateWithoutNBST, boolean nbst) 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.getJGroupsConfigFile() != null) { URL u = configuration.getJGroupsConfigFile(); if (trace) log.trace("Grabbing cluster properties from " + u); channel = new JChannel(u); } else if (configuration.getClusterConfig() == null) { if (log.isDebugEnabled()) 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); } if (nbst) sanityCheckJGroupsStack((JChannel) 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, fetchStateWithoutNBST); channel.setOpt(Channel.BLOCK, true); if (configuration.isUseRegionBasedMarshalling()) { rpcDispatcher = new InactiveRegionAwareRpcDispatcher(channel, messageListener, new MembershipListenerAdaptor(), spi, invocationContextContainer, interceptorChain, componentRegistry, this); } else { rpcDispatcher = new CommandAwareRpcDispatcher(channel, messageListener, new MembershipListenerAdaptor(), invocationContextContainer, invocationContextContainer, interceptorChain, componentRegistry, this); } checkAppropriateConfig(); 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); } } if (trace) { if (muxchannel == null) { log.trace("Null mux channel!"); } else { log.trace("Using multiplex channel: " + muxchannel.printProtocolSpec(true)); } } return muxchannel; } @Deprecated 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.getFqn(), 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); } } /** * Only used with MVCC. */ private void removeLocksForDeadMembers(InternalNode node, List deadMembers) { Set deadOwners = new HashSet(); Object owner = lockManager.getWriteOwner(node.getFqn()); if (isLockOwnerDead(owner, deadMembers)) deadOwners.add((GlobalTransaction) owner); // MVCC won't have any read locks. for (GlobalTransaction deadOwner : deadOwners) { boolean localTx = deadOwner.getAddress().equals(getLocalAddress()); boolean broken = LockUtil.breakTransactionLock(node.getFqn(), lockManager, deadOwner, localTx, txTable, txManager); if (broken && trace) log.trace("Broke lock for node " + node.getFqn() + " held by " + deadOwner); } // Recursively unlock children for (InternalNode child : node.getChildren()) removeLocksForDeadMembers(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 { boolean success = true; boolean unlock = false; try { // 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 + " modeToUse: " + modeToUse); } flushTracker.lockProcessingLock(); unlock = true; flushTracker.waitForFlushCompletion(configuration.getStateRetrievalTimeout()); 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)); success = false; } 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); success = true; } } return retval; } catch (Exception e) { success = false; throw e; } finally { computeStats(success); if (unlock) { flushTracker.unlockProcessingLock(); } } } // ------------ START: Partial state transfer methods ------------ public void fetchPartialState(List
    sources, Fqn sourceTarget, Fqn integrationTarget) throws Exception { String encodedStateId = sourceTarget + DefaultStateTransferManager.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) { try { if (log.isDebugEnabled()) { log.debug("Node " + getLocalAddress() + " fetching partial state " + stateId + " from member " + target); } messageListener.setStateSet(false); successfulTransfer = getState(stateId, target); if (successfulTransfer) { try { messageListener.waitForState(); } catch (Exception transferFailed) { if (trace) log.trace("Error while fetching state", transferFailed); successfulTransfer = false; } } if (log.isDebugEnabled()) { log.debug("Node " + getLocalAddress() + " fetching partial state " + stateId + " from member " + target + (successfulTransfer ? " successful" : " failed")); } if (successfulTransfer) break; } catch (IllegalStateException ise) { // thrown by the JGroups channel if state retrieval fails. if (log.isInfoEnabled()) { log.info("Channel problems fetching state. Continuing on to next provider. ", ise); } } } if (!successfulTransfer && log.isDebugEnabled()) { log.debug("Node " + getLocalAddress() + " could not fetch partial state " + stateId + " from any member " + targets); } } private boolean getState(String stateId, Address target) throws ChannelNotConnectedException, ChannelClosedException { lastStateTransferSource = target; return ((JChannel) channel).getState(target, stateId, configuration.getStateRetrievalTimeout(), !configuration.isNonBlockingStateTransfer()); } // ------------ END: Partial state transfer methods ------------ // ------------ START: Informational methods ------------ @ManagedAttribute(description = "Local address") public String getLocalAddressString() { Address address = getLocalAddress(); return address == null ? "null" : address.toString(); } public Address getLastStateTransferSource() { return lastStateTransferSource; } public Address getLocalAddress() { return channel != null ? channel.getLocalAddress() : null; } @ManagedAttribute(description = "Cluster view") public String getMembersString() { List l = getMembers(); return l == null ? "null" : l.toString(); } 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()) { if (log.isDebugEnabled()) 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) { try { 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); spi.getInvocationContext().getOptionOverrides().setSkipCacheStatusCheck(true); NodeSPI root = spi.getRoot(); if (root != null) { // UGH!!! What a shameless hack! if (configuration.getNodeLockingScheme() == NodeLockingScheme.MVCC) { removeLocksForDeadMembers(root.getDelegationTarget(), removed); } else { removeLocksForDeadMembers(root, 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(); } } catch (Throwable e) { //do not rethrow! jgroups might behave funny, resulting even in deadlock log.error("Error found while processing view accepted!!!", e); } } /** * 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() { if (!configuration.isNonBlockingStateTransfer()) { try { if (log.isDebugEnabled()) log.debug("Block received at " + getLocalAddress()); flushTracker.block(); notifier.notifyCacheBlocked(true); notifier.notifyCacheBlocked(false); if (log.isDebugEnabled()) log.debug("Block processed at " + getLocalAddress()); } catch (Throwable e) { //do not rethrow! jgroups might behave funny, resulting even in deadlock log.error("Error found while processing block()", e); } } } /** * Indicates that a channel has received a UNBLOCK event from FLUSH protocol. */ public void unblock() { if (!configuration.isNonBlockingStateTransfer()) { try { if (log.isDebugEnabled()) log.debug("UnBlock received at " + getLocalAddress()); notifier.notifyCacheUnblocked(true); notifier.notifyCacheUnblocked(false); flushTracker.unblock(); if (log.isDebugEnabled()) log.debug("UnBlock processed at " + getLocalAddress()); } catch (Throwable e) { //do not rethrow! jgroups might behave funny, resulting even in deadlock log.error("Error found while processing unblock", e); } } } } //jmx operations private void computeStats(boolean success) { if (statisticsEnabled && rpcDispatcher != null) { if (success) { replicationCount++; } else { replicationFailures++; } } } @ManagedOperation public void resetStatistics() { this.replicationCount = 0; this.replicationFailures = 0; } @ManagedAttribute(description = "number of successful replications") public long getReplicationCount() { return replicationCount; } @ManagedAttribute(description = "number of failed replications") public long getReplicationFailures() { return replicationFailures; } @ManagedAttribute(description = "whether or not jmx statistics are enabled") public boolean isStatisticsEnabled() { return statisticsEnabled; } @ManagedAttribute(description = "whether or not the RPCManager is used in this cache instance") public boolean isEnabled() { return !isInLocalMode; } @ManagedAttribute public void setStatisticsEnabled(boolean statisticsEnabled) { this.statisticsEnabled = statisticsEnabled; } @ManagedAttribute(description = "RPC call success ratio") public String getSuccessRatio() { if (replicationCount == 0 || !statisticsEnabled) { return "N/A"; } double totalCount = replicationCount + replicationFailures; double ration = (double) replicationCount / totalCount * 100d; return NumberFormat.getInstance().format(ration) + "%"; } /** * Checks to see whether the cache is using an appropriate JGroups config. */ private void checkAppropriateConfig() { //if we use a shared transport do not log any warn message if (configuration.getMultiplexerStack() != null) { return; } //bundling is not good for sync caches Configuration.CacheMode cacheMode = configuration.getCacheMode(); if (!cacheMode.equals(Configuration.CacheMode.LOCAL) && configuration.getCacheMode().isSynchronous()) { ProtocolStack stack = ((JChannel) channel).getProtocolStack(); TP transport = stack.getTransport(); if (transport.isEnableBundling() && log.isWarnEnabled()) { log.warn("You have enabled jgroups's message bundling, which is not recommended for sync replication. If there is no particular " + "reason for this we strongly recommend to disable message bundling in JGroups config (enable_bundling=\"false\")."); } } //bundling is good for async caches if (!cacheMode.isSynchronous()) { ProtocolStack stack = ((JChannel) channel).getProtocolStack(); TP transport = stack.getTransport(); if (!transport.isEnableBundling() && log.isWarnEnabled()) { log.warn("You have disabled jgroups's message bundling, which is not recommended for async replication. If there is no particular " + "reason for this we strongly recommend to enable message bundling in JGroups config (enable_bundling=\"true\")."); } } } public FlushTracker getFlushTracker() { return flushTracker; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/0000755000175000017500000000000011675221734025063 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/EvictionInterceptor.java0000644000175000017500000002250711433521426031724 0ustar moellermoeller/* * 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.interceptors; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.Region; import org.jboss.cache.RegionManager; import org.jboss.cache.commands.DataCommand; 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.EvictionEvent; import static org.jboss.cache.eviction.EvictionEvent.Type.*; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.interceptors.base.CommandInterceptor; import javax.transaction.Transaction; /** * Eviction Interceptor. *

    * This interceptor is used to handle eviction events. * * @author Daniel Huang * @author Mircea.Markus@jboss.com * @version $Revision: 8443 $ */ 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 nodeIsNowAbsent = (retVal != null && (Boolean) retVal); if (!nodeIsNowAbsent) { Region r; if (fqn != null && (r = getRegion(fqn)) != null) { registerEvictionEventToRegionManager(fqn, ADD_NODE_EVENT, 0, r, null, null); } } 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())) != null) { boolean isTransactional = ctx.getTransactionContext() != null; if (isTransactional) { registerEvictionEventToRegionManager(command.getFqn(), ADD_ELEMENT_EVENT, 1, r, command, ctx.getTransactionContext().getTransaction()); } else { registerEvictionEventToRegionManager(command.getFqn(), ADD_ELEMENT_EVENT, 1, r, null, null); } } 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)) != 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(); } boolean isTransactional = ctx.getTransactionContext() != null; if (isTransactional) { registerEvictionEventToRegionManager(fqn, ADD_NODE_EVENT, size, r, command, ctx.getTransactionContext().getTransaction()); } else { registerEvictionEventToRegionManager(fqn, ADD_NODE_EVENT, size, r, null, null); } } } 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)) != null) { registerEvictionEventToRegionManager(fqn, REMOVE_ELEMENT_EVENT, 1, r, null, null); } } 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)) != null) { registerEvictionEventToRegionManager(fqn, VISIT_NODE_EVENT, 0, r, null, null); } } 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)) != null) { registerEvictionEventToRegionManager(fqn, VISIT_NODE_EVENT, 0, r, null, null); } 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())) != null) { registerEvictionEventToRegionManager(command.getFqn(), REMOVE_NODE_EVENT, 0, r, null, null); } 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())) != null) { registerEvictionEventToRegionManager(command.getFqn(), REMOVE_NODE_EVENT, 0, r, null, null); } return retVal; } private void registerEvictionEventToRegionManager(Fqn fqn, EvictionEvent.Type type, int elementDifference, Region region, DataCommand command, Transaction tx) { //we do not trigger eviction events for resident nodes if (dataContainer.isResident(fqn)) { if (trace) log.trace("Ignoring Fqn " + fqn + " as it is marked as resident"); return; } region.registerEvictionEvent(fqn, type, elementDifference, command, tx); if (trace) log.trace("Registering event " + type + " on node " + fqn); } protected Region getRegion(Fqn fqn) { return regionManager.getRegion(fqn, Region.Type.EVICTION, false); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/InvalidationInterceptor.java0000644000175000017500000003770011111047320032553 0ustar moellermoeller/* * 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.interceptors; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.AbstractVisitor; import org.jboss.cache.commands.CommandsFactory; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.commands.WriteCommand; import org.jboss.cache.commands.legacy.write.VersionedInvalidateCommand; 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.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 org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.config.Option; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.Start; import org.jboss.cache.jmx.annotations.ManagedAttribute; import org.jboss.cache.jmx.annotations.ManagedOperation; 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.OptimisticTransactionContext; import org.jboss.cache.transaction.TransactionContext; 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 AT jboss DOT org) */ public class InvalidationInterceptor extends BaseRpcInterceptor { private long invalidations = 0; protected Map> txMods; protected boolean optimistic; private CommandsFactory commandsFactory; private boolean statsEnabled; @Inject public void injectDependencies(CommandsFactory commandsFactory) { this.commandsFactory = commandsFactory; } @Start void initTxMap() { optimistic = configuration.getNodeLockingScheme() == NodeLockingScheme.OPTIMISTIC; if (optimistic) txMods = new ConcurrentHashMap>(); this.setStatisticsEnabled(configuration.getExposeManagementStatistics()); } @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.getTransactionContext().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(); TransactionContext transactionContext = ctx.getTransactionContext(); if (transactionContext == null) throw new IllegalStateException("cannot find transaction transactionContext for " + gtx); if (transactionContext.hasModifications()) { List mods; if (transactionContext.hasLocalModifications()) { mods = new ArrayList(command.getModifications()); mods.removeAll(transactionContext.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(); TransactionContext transactionContext = ctx.getTransactionContext(); if (transactionContext == null) throw new IllegalStateException("cannot find transaction transactionContext for " + gtx); if (transactionContext.hasModifications()) { List mods = new ArrayList(transactionContext.getModifications()); if (transactionContext.hasLocalModifications()) mods.removeAll(transactionContext.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.getTransactionContext().addLocalModification((WriteCommand) 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 = optimistic ? 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; } } 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) ((VersionedInvalidateCommand) 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) { OptimisticTransactionContext entry = (OptimisticTransactionContext) ctx.getTransactionContext(); return entry.getTransactionWorkSpace(); } @ManagedOperation public void resetStatistics() { invalidations = 0; } @ManagedOperation public Map dumpStatistics() { Map retval = new HashMap(); retval.put("Invalidations", invalidations); return retval; } @ManagedAttribute public boolean getStatisticsEnabled() { return this.statsEnabled; } @ManagedAttribute public void setStatisticsEnabled(boolean enabled) { this.statsEnabled = enabled; } @ManagedAttribute(description = "number of invalidations") public long getInvalidations() { return invalidations; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/OptimisticInterceptor.java0000644000175000017500000001356111111047320032255 0ustar moellermoeller/* * 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.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.OptimisticTransactionContext; 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 AT jboss DOT org) * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated public abstract class OptimisticInterceptor extends CommandInterceptor { protected TransactionManager txManager; protected TransactionTable txTable; protected LockManager lockManager; @Inject void injectDependencies(TransactionManager txManager, TransactionTable txTable, LockManager lockManager) { this.txManager = txManager; this.txTable = txTable; this.lockManager = lockManager; } protected TransactionWorkspace getTransactionWorkspace(InvocationContext ctx) throws CacheException { OptimisticTransactionContext transactionContext = (OptimisticTransactionContext) ctx.getTransactionContext(); if (transactionContext == 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 transactionContext.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.setRemoved(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; try { locked = lockManager.lock(node, READ, gtx, timeout); } catch (InterruptedException e) { // test if we acquired the lock locked = lockManager.getReadOwners(node).contains(gtx); } if (!locked) throw new TimeoutException("Unable to lock node " + node.getFqn() + " after timeout " + timeout + " for copying into workspace"); WorkspaceNode wn = nodeFactory.createWrappedNode(node, workspace); lockManager.unlock(node, gtx); return wn; } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/LegacyPassivationInterceptor.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/LegacyPassivationInterceptor.jav0000644000175000017500000000442711111047320033416 0ustar moellermoeller/* * 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.interceptors; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.write.EvictCommand; import org.jboss.cache.factories.annotations.Inject; import java.util.List; /** * Passivation interceptor for optimistic and pessimistic locking * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated public class LegacyPassivationInterceptor extends PassivationInterceptor { private DataContainer dataContainer; @Inject void injectDataContainer(DataContainer dataContainer) { this.dataContainer = dataContainer; } @Override public Object visitEvictFqnCommand(InvocationContext ctx, EvictCommand command) throws Throwable { if (command.isRecursive()) { for (Fqn f : getNodeList(command.getFqn())) passivate(ctx, f); } else { passivate(ctx, command.getFqn()); } return invokeNextInterceptor(ctx, command); } private List getNodeList(Fqn startingPoint) { return dataContainer.getNodesForEviction(startingPoint, true); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/NotificationInterceptor.java0000644000175000017500000000512511111047320032554 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/LegacyActivationInterceptor.java0000644000175000017500000002430211240253605033362 0ustar moellermoeller/* * 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.interceptors; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; 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.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.jmx.annotations.ManagedOperation; import javax.transaction.TransactionManager; import java.util.HashMap; 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: LegacyActivationInterceptor.java 8181 2009-08-11 11:35:33Z manik.surtani@jboss.com $ * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated public class LegacyActivationInterceptor extends LegacyCacheLoaderInterceptor { 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 LegacyActivationInterceptor() { 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(ctx, command.getFqn(), true); return returnValue; } @Override public Object visitGetKeysCommand(InvocationContext ctx, GetKeysCommand command) throws Throwable { Object returnValue = super.visitGetKeysCommand(ctx, command); removeNodeFromCacheLoader(ctx, command.getFqn(), true); return returnValue; } @Override public Object visitGetNodeCommand(InvocationContext ctx, GetNodeCommand command) throws Throwable { Object returnValue = super.visitGetNodeCommand(ctx, command); removeNodeFromCacheLoader(ctx, command.getFqn(), true); return returnValue; } @Override public Object visitGetKeyValueCommand(InvocationContext ctx, GetKeyValueCommand command) throws Throwable { Object returnValue = super.visitGetKeyValueCommand(ctx, command); removeNodeFromCacheLoader(ctx, command.getFqn(), true); 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(ctx, command.getFqn(), true); return returnValue; } @Override public Object visitPutDataMapCommand(InvocationContext ctx, PutDataMapCommand command) throws Throwable { Object returnValue = super.visitPutDataMapCommand(ctx, command); removeNodeFromCacheLoader(ctx, command.getFqn(), true); return returnValue; } @Override public Object visitRemoveKeyCommand(InvocationContext ctx, RemoveKeyCommand command) throws Throwable { Object returnValue = super.visitRemoveKeyCommand(ctx, command); removeNodeFromCacheLoader(ctx, command.getFqn(), true); 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(ctx, command.getFqn().getParent(), true); removeNodeFromCacheLoader(ctx, command.getTo(), true); return returnValue; } private boolean wasLoadedIntoMemory(InvocationContext ctx, Fqn fqn) { Set fqnsLoaded = ctx.getFqnsLoaded(); return fqnsLoaded != null && fqnsLoaded.contains(fqn); } /** * 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). */ private void removeNodeFromCacheLoader(InvocationContext ctx, Fqn fqn, boolean checkIfLoaded) throws Throwable { // only bother with this if the node has been loaded by the cache loader previous to this call. if (fqn != null) { boolean remove = false; if (!checkIfLoaded || wasLoadedIntoMemory(ctx, fqn)) { 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); remove = true; } } else if (loaderNoChildren(fqn)) { if (log.isDebugEnabled()) log.debug("no children " + n); remove(fqn); remove = true; } } } if (!fqn.isRoot() && remove) { // check fqn parent, since the parent may not be needed on disk anymore removeNodeFromCacheLoader(ctx, fqn.getParent(), false); } } } private boolean childrenLoaded(NodeSPI node) throws Exception { if (!node.isChildrenLoaded() && loader.getChildrenNames(node.getFqn()) != null) return false; for (NodeSPI child : node.getChildrenDirect()) { if (!child.isDataLoaded()) { return false; } if (child.getChildrenDirect().size() > 0) { if (!childrenLoaded(child)) { return false; } } else if (!loaderNoChildren(child.getFqn())) { return false; } } return true; } 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; } @ManagedOperation public void resetStatistics() { super.resetStatistics(); activations = 0; } @ManagedOperation public Map dumpStatistics() { Map retval = super.dumpStatistics(); if (retval == null) { retval = new HashMap(); } retval.put("Activations", activations); return retval; } @Override protected void recordNodeLoaded(InvocationContext ctx, Fqn fqn) { ctx.addFqnLoaded(fqn); } }././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/OptimisticValidatorInterceptor.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/OptimisticValidatorInterceptor.j0000755000175000017500000003341411111047320033435 0ustar moellermoeller/* * 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.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 AT jboss DOT org) * @author Steve Woodcock (stevew@jofti.com) * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated public class OptimisticValidatorInterceptor extends OptimisticInterceptor { private boolean useTombstones; private DataContainer dataContainer; @Inject public void initialize(DataContainer dataContainer) { this.dataContainer = dataContainer; } @Inject 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.isRemoved()) { 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.isRemoved()) 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.isRemoved() || 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.isRemoved()) 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); } @SuppressWarnings("unchecked") 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.isRemoved()) { 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.getParentDirect(); 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(); 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.getParentDirect()); } 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-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/OptimisticNodeInterceptor.java0000755000175000017500000005642011240253605033077 0ustar moellermoeller/* * 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.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.WriteCommand; 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 AT jboss DOT org) * @author Steve Woodcock (stevew@jofti.com) * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated 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 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.isRemoved()) { 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); } } /** * Adds a method call to the modification list of a given transaction's transaction entry */ private void addToModificationList(WriteCommand command, InvocationContext ctx) { // Option opt = ctx.getOptionOverrides(); ctx.getTransactionContext().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.setRemoved(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.isRemoved()) { 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; } } ././@LongLink0000000000000000000000000000015500000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/BuddyRegionAwareEvictionInterceptor.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/BuddyRegionAwareEvictionIntercep0000644000175000017500000000517011111047320033355 0ustar moellermoeller/* * 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.interceptors; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.buddyreplication.BuddyFqnTransformer; import org.jboss.cache.factories.annotations.Inject; /** * A subclass of EvictionInterceptor that is aware of and able to deal with buddy regions. * * @author Manik Surtani (manik AT jboss DOT 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) { Region r = super.getRegion(fqn); 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.setEvictionRegionConfig(actualRegion.getEvictionRegionConfig()); return newRegion; } else return null; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/PassivationInterceptor.java0000644000175000017500000001204511622053025032433 0ustar moellermoeller/* * 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.interceptors; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.commands.write.EvictCommand; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.interceptors.base.JmxStatsCommandInterceptor; import org.jboss.cache.jmx.annotations.ManagedAttribute; import org.jboss.cache.jmx.annotations.ManagedOperation; 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.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 8462 2011-08-14 23:11:17Z dereed $ */ public class PassivationInterceptor extends JmxStatsCommandInterceptor { private final AtomicLong passivations = new AtomicLong(0); protected CacheLoader loader; private Notifier notifier; @Inject public void setDependencies(Notifier notifier, CacheLoaderManager loaderManager) { this.notifier = notifier; 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()) { for (Fqn f : command.getNodesToEvict()) passivate(ctx, f); } else { passivate(ctx, command.getFqn()); } return invokeNextInterceptor(ctx, command); } protected 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(ctx, 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"); } } } /** * Returns attributes for a node. */ private Map getNodeAttributes(InvocationContext ctx, Fqn fqn) throws NodeNotLoadedException { if (fqn == null) { throw new NodeNotLoadedException(); } NodeSPI n = ctx.lookUpNode(fqn); if (n == null) throw new NodeNotLoadedException(); // Fix JBCACHE-1601. But NOT for OPTIMISTIC because of JBCACHE-1602 if (configuration.getNodeLockingScheme() != NodeLockingScheme.OPTIMISTIC && !n.isDataLoaded()) throw new NodeNotLoadedException(); return n.getDataDirect(); } private static class NodeNotLoadedException extends Exception { /** * The serialVersionUID */ private static final long serialVersionUID = -4078972305344328905L; } @ManagedOperation public void resetStatistics() { passivations.set(0); } @ManagedOperation public Map dumpStatistics() { Map retval = new HashMap(); retval.put("Passivations", passivations.get()); return retval; } @ManagedAttribute(description = "number of cache node passivations") public long getPassivations() { return passivations.get(); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/OptimisticTxInterceptor.java0000644000175000017500000002014511111047320032565 0ustar moellermoeller/* * 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.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.OptimisticTransactionContext; import org.jboss.cache.transaction.TransactionContext; 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 AT jboss DOT org) * @since 2.2.0 * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated 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())) { TransactionContext transactionContext = ctx.getTransactionContext(); if (transactionContext != null) { if (optionOverride.isForceAsynchronous()) transactionContext.setForceAsyncReplication(true); else transactionContext.setForceSyncReplication(true); } } } @Override protected PrepareCommand buildPrepareCommand(GlobalTransaction gtx, List modifications, boolean onePhaseCommit) { // optimistic locking NEVER does one-phase prepares. return commandsFactory.buildOptimisticPrepareCommand(gtx, modifications, 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); TransactionContext transactionContext = ctx.getTransactionContext(); if (transactionContext != null) { ((OptimisticTransactionContext) transactionContext).getTransactionWorkSpace().clearNodes(); } } 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; } } } ././@LongLink0000000000000000000000000000015500000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/BaseTransactionalContextInterceptor.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/BaseTransactionalContextIntercep0000644000175000017500000000760011111047320033423 0ustar moellermoeller/* * 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.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.TransactionContext; 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 void injectDependencies(TransactionTable txTable, TransactionManager txManager) { this.txManager = txManager; this.txTable = txTable; } protected void copyInvocationScopeOptionsToTxScope(InvocationContext ctx) { // notify the transaction tCtx that this override is in place. TransactionContext tCtx = txTable.get(ctx.getGlobalTransaction()); if (tCtx != null) { Option txScopeOption = new Option(); txScopeOption.setCacheModeLocal(ctx.getOptionOverrides() != null && ctx.getOptionOverrides().isCacheModeLocal()); txScopeOption.setSkipCacheStatusCheck(ctx.getOptionOverrides() != null && ctx.getOptionOverrides().isSkipCacheStatusCheck()); tCtx.setOption(txScopeOption); } } protected void setTransactionalContext(Transaction tx, GlobalTransaction gtx, TransactionContext tCtx, 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 (tCtx == null) { if (gtx != null) { ctx.setTransactionContext(txTable.get(gtx)); } else if (tx == null) { // then nullify the transaction tCtx as well ctx.setTransactionContext(null); } } else { ctx.setTransactionContext(tCtx); } } /** * 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-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/DataGravitatorInterceptor.java0000644000175000017500000000404511111047320033042 0ustar moellermoeller/* * 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.interceptors; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.mvcc.MVCCNodeHelper; import org.jboss.cache.mvcc.ReadCommittedNode; /** * MVCC specific version of the LegacyDataGravitatorInterceptor * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ public class DataGravitatorInterceptor extends LegacyDataGravitatorInterceptor { MVCCNodeHelper helper; @Inject public void injectMvccNodeHelper(MVCCNodeHelper helper) { this.helper = helper; } @Override protected boolean nodeDoesNotExist(InvocationContext ctx, Fqn fqn) { ReadCommittedNode node = (ReadCommittedNode) ctx.lookUpNode(fqn); return node == null || node.isNullNode(); } @Override protected void wrapIfNeeded(InvocationContext ctx, Fqn fqnToWrap) throws InterruptedException { helper.wrapNodeForReading(ctx, fqnToWrap, true); } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/InvocationContextInterceptor.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/InvocationContextInterceptor.jav0000644000175000017500000002312311113556312033451 0ustar moellermoeller/* * 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.interceptors; import org.jboss.cache.InvocationContext; import org.jboss.cache.RPCManager; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.commands.read.ExistsCommand; 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 AT jboss DOT org) */ public class InvocationContextInterceptor extends BaseTransactionalContextInterceptor { 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 visitExistsNodeCommand(InvocationContext ctx, ExistsCommand command) throws Throwable { return handleAll(ctx, command, null, 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 { // TODO: scope upgrading should happen transparently /* * 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(); // make sure we wipe fqns loaded in the context otherwise this will leak. // TODO: This should ideally be done by calling InvocationContext.reset() on exit ctx.setFqnsLoaded(null); // 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); // TODO: Calling ctx.reset() here breaks stuff. Check whether this is just becuse UTs expect stuff in the ctx or whether this really breaks functionality. // ctx.reset(); // instead, for now, just wipe contents of the looked up node map ctx.clearLookedUpNodes(); } } 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())); } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/LegacyCacheLoaderInterceptor.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/LegacyCacheLoaderInterceptor.jav0000755000175000017500000005272711371344767033310 0ustar moellermoeller/* * 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.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.buddyreplication.BuddyFqnTransformer; import org.jboss.cache.commands.WriteCommand; 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.config.Configuration.NodeLockingScheme; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.Start; import org.jboss.cache.interceptors.base.JmxStatsCommandInterceptor; import org.jboss.cache.jmx.annotations.ManagedAttribute; import org.jboss.cache.jmx.annotations.ManagedOperation; 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.TransactionContext; 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: LegacyCacheLoaderInterceptor.java 8400 2010-05-08 20:30:47Z bstansberry@jboss.com $ * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated public class LegacyCacheLoaderInterceptor extends JmxStatsCommandInterceptor { 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 usingVersionedInvalidation = false; protected BuddyFqnTransformer buddyFqnTransformer; /** * 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, BuddyFqnTransformer buddyFqnTransformer) { this.txTable = txTable; this.clm = clm; CacheMode mode = configuration.getCacheMode(); usingVersionedInvalidation = configuration.getNodeLockingScheme().isVersionedScheme() && mode.isInvalidation(); this.dataContainer = dataContainer; this.lockManager = lockManager; this.notifier = notifier; this.buddyFqnTransformer = buddyFqnTransformer; } @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.getTransactionContext(), 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.getTransactionContext(), 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.getTransactionContext(), false, true, false); } loadIfNeeded(ctx, command.getFqn(), null, false, false, true, ctx.getTransactionContext(), 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.getTransactionContext(), 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.getTransactionContext(), false, false, !usingVersionedInvalidation); } 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.getTransactionContext(), 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.getTransactionContext(), 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.getTransactionContext(), 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.getTransactionContext().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.getNodeLockingScheme() == NodeLockingScheme.OPTIMISTIC && command.getFqn() != null) { loadIfNeeded(ctx, command.getFqn(), null, false, false, false, ctx.getTransactionContext(), 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.getTransactionContext(), 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.getTransactionContext(), false, false, false); } return invokeNextInterceptor(ctx, command); } private void loadIfNeeded(InvocationContext ctx, Fqn fqn, Object key, boolean allKeys, boolean initNode, boolean acquireLock, TransactionContext transactionContext, 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(fqn, 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, transactionContext); } // 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, transactionContext); } } else { n = loadNode(ctx, fqn, n, transactionContext); } } } // 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; } // The children of a dead buddy backup root must be Integer. // Ensure that we didn't pull in the wrong type from a cache loader // (e.g. FileCacheLoader) that can only return String if (buddyFqnTransformer != null && buddyFqnTransformer.isDeadBackupRoot(fqn)) { childrenNames = buddyFqnTransformer.getDeadBackupRootChildren(childrenNames); } // 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(loadData(ctxt, 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(Fqn fqn, NodeSPI n, Object key, boolean allKeys) { if (n == null) { if (trace) log.trace("Node [" + fqn + "] is null in memory. Must load? true"); return true; } // check this first!!! if (!n.isValid() && configuration.getNodeLockingScheme().isVersionedScheme()) { // attempt to load again; this only happens if we have tombstones lying around, or we are using invalidation. if (trace) { log.trace("Loading node [" + fqn + "] 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 [" + fqn + "], key requested is null"); return false; } if (n.getKeysDirect().contains(key)) { if (trace) log.trace("Don't load [" + fqn + "], already have necessary key in memory"); return false; } } if (!n.isDataLoaded()) { if (trace) log.trace("Must load node [" + fqn + "], uninitialized"); return true; } if (trace) log.trace("Don't load node [" + fqn + "], by default"); return false; } protected void lock(Fqn fqn, LockType lockType, boolean recursive, InvocationContext ctx) throws Throwable { if (configuration.getNodeLockingScheme() == NodeLockingScheme.OPTIMISTIC) 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) { TransactionContext transactionContext = ctx.getTransactionContext(); if (transactionContext == null) return false; for (WriteCommand txCacheCommand : transactionContext.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, TransactionContext transactionContext) throws Exception { if (trace) log.trace("loadNode " + fqn); Map nodeData = loadData(ctx, 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, transactionContext); // n.clearDataDirect(); n.setInternalState(nodeData); // set this node as valid? if (usingVersionedInvalidation) 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, TransactionContext transactionContext) throws Exception { NodeSPI n = createNodes(fqn, transactionContext); n.setDataLoaded(false); if (trace) { log.trace("createTempNode n " + n); } return n; } @SuppressWarnings("unchecked") private NodeSPI createNodes(Fqn fqn, TransactionContext transactionContext) throws Exception { Object[] results = dataContainer.createNodes(fqn); List> createdNodes = (List>) results[0]; NodeSPI lastCreated = null; for (NodeSPI node : createdNodes) { node.setDataLoaded(false); if (transactionContext != null) { transactionContext.addDummyNodeCreatedByCacheLoader(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(InvocationContext ctx, Fqn fqn) throws Exception { if (trace) log.trace("Attempting to load data for " + fqn); Map nodeData = loader.get(fqn); boolean nodeExists = (nodeData != null); if (trace) log.trace("Node " + fqn + " exists? " + nodeExists); if (nodeExists) recordNodeLoaded(ctx, fqn); if (getStatisticsEnabled()) { if (nodeExists) { cacheLoads++; } else { cacheMisses++; } } return nodeData; } @ManagedAttribute(description = "number of cache loader node loads") public long getCacheLoaderLoads() { return cacheLoads; } @ManagedAttribute(description = "number of cache loader node misses") public long getCacheLoaderMisses() { return cacheMisses; } @ManagedOperation public void resetStatistics() { cacheLoads = 0; cacheMisses = 0; } @ManagedOperation public Map dumpStatistics() { Map retval = new HashMap(); retval.put("CacheLoaderLoads", cacheLoads); retval.put("CacheLoaderMisses", cacheMisses); return retval; } protected void recordNodeLoaded(InvocationContext ctx, Fqn fqn) { // this is a no-op. Only used by subclasses. } }jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/Interceptor.java0000644000175000017500000000715311111047320030210 0ustar moellermoeller/* * 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.interceptors; import org.jboss.cache.CacheSPI; 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; /** * Class representing an interceptor. * * @author Bela Ban * @version $Id: Interceptor.java 7168 2008-11-19 17:37:20Z jason.greene@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 void start() { // for backward compatibility, this must only be done when the cache starts. setCache(cache); } @Inject 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()) + "}"; } @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()); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/CacheStoreInterceptor.java0000755000175000017500000004020211371344767032173 0ustar moellermoeller/* * 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.interceptors; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.Modification; import org.jboss.cache.NodeSPI; import org.jboss.cache.buddyreplication.BuddyFqnTransformer; import org.jboss.cache.commands.AbstractVisitor; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.commands.WriteCommand; 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.jmx.annotations.ManagedAttribute; import org.jboss.cache.jmx.annotations.ManagedOperation; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.loader.CacheLoaderManager; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.TransactionContext; import javax.transaction.SystemException; 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 8400 2010-05-08 20:30:47Z bstansberry@jboss.com $ */ public class CacheStoreInterceptor extends SkipCheckChainedInterceptor { private CacheLoaderConfig loaderConfig = null; protected TransactionManager txMgr = null; private Map txStores = new ConcurrentHashMap(); private long cacheStores = 0; CacheLoader loader; private CacheLoaderManager loaderManager; private boolean statsEnabled; private BuddyFqnTransformer buddyFqnTransformer; public CacheStoreInterceptor() { log = LogFactory.getLog(getClass()); trace = log.isTraceEnabled(); } @Inject protected void init(CacheLoaderManager loaderManager, TransactionManager txManager, CacheLoaderConfig clConfig, BuddyFqnTransformer buddyFqnTransformer) { // 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; } @Start protected void start() { // this should only happen after the CacheLoaderManager has started, since the CacheLoaderManager only creates the CacheLoader instance in its @Start method. loader = loaderManager.getCacheLoader(); this.setStatisticsEnabled(configuration.getExposeManagementStatistics()); } /** * 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()) || ctx.getOptionOverrides().isSuppressPersistence()) { 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.getTransactionContext().hasAnyModifications()) { // this is a commit call. GlobalTransaction gtx = command.getGlobalTransaction(); if (trace) log.trace("Calling loader.commit() for gtx " + gtx); try { loader.commit(gtx); } finally { if (getStatisticsEnabled()) { Integer puts = (Integer) txStores.get(gtx); if (puts != null) { cacheStores = cacheStores + puts; } txStores.remove(gtx); } } return invokeNextInterceptor(ctx, command); } 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.getTransactionContext().hasAnyModifications()) { GlobalTransaction gtx = command.getGlobalTransaction(); // this is a rollback method try { loader.rollback(gtx); } catch (Exception e) { log.info("Problems rolling back transaction " + gtx + " on cache loader. Perhaps the prepare phase hasn't been initiated on this loader?", e); } 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.getTransactionContext(), 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 = ctx.lookUpNode(command.getFqn()); 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; } if (command.isErase()) { loader.removeData(command.getFqn()); } storeStateForPutDataMap(command.getFqn(), ctx); if (getStatisticsEnabled()) cacheStores++; return returnValue; } protected void storeStateForPutDataMap(Fqn f, InvocationContext ctx) throws Exception { loader.put(f, ctx.lookUpNode(f).getDelegationTarget().getData()); } @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); } protected boolean inTransaction() throws SystemException { return txMgr != null && txMgr.getTransaction() != null; } private void recursiveMove(Fqn fqn, Fqn newFqn) throws Exception { loader.put(newFqn, loader.get(fqn)); //recurse Set childrenNames = loader.getChildrenNames(fqn); if (childrenNames != null) { // The children of a dead buddy backup root must be Integer. // Ensure that we didn't pull in the wrong type from a cache loader // (e.g. FileCacheLoader) that can only return String if (buddyFqnTransformer != null && buddyFqnTransformer.isDeadBackupRoot(fqn)) { childrenNames = buddyFqnTransformer.getDeadBackupRootChildren(childrenNames); } for (Object child : childrenNames) { recursiveMove(Fqn.fromRelativeElements(fqn, child), Fqn.fromRelativeElements(newFqn, child)); } } } protected StoreModificationsBuilder prepareCacheLoader(GlobalTransaction gtx, TransactionContext transactionContext, boolean onePhase) throws Throwable { if (transactionContext == null) { throw new Exception("transactionContext for transaction " + gtx + " not found in transaction table"); } List modifications = transactionContext.getModifications(); if (modifications.size() == 0) { if (trace) log.trace("Transaction has not logged any modifications!"); return null; } if (trace) log.trace("Cache loader modification list: " + modifications); StoreModificationsBuilder modsBuilder = new StoreModificationsBuilder(getStatisticsEnabled()); for (WriteCommand 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); if (getStatisticsEnabled() && modsBuilder.putCount > 0 && !onePhase) { txStores.put(gtx, modsBuilder.putCount); } } return modsBuilder; } 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; } } @ManagedOperation public void resetStatistics() { cacheStores = 0; } @ManagedOperation public Map dumpStatistics() { Map retval = new HashMap(); retval.put("CacheLoaderStores", cacheStores); return retval; } @ManagedAttribute public boolean getStatisticsEnabled() { return statsEnabled; } @ManagedAttribute public void setStatisticsEnabled(boolean enabled) { this.statsEnabled = enabled; } @ManagedAttribute(description = "number of cache loader stores") public long getCacheLoaderStores() { return cacheStores; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/InterceptorChain.java0000644000175000017500000003006611157534206031170 0ustar moellermoeller/* * 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.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.jmx.annotations.MBean; import org.jboss.cache.jmx.annotations.ManagedOperation; import org.jboss.cache.util.CachePrinter; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.List; /** * 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 * todo - if you add the same interceptor instance twice, things get really dirty. * -- this should be treated as an missuse and an exception should be thrown */ @MBean(description = "InterceptorChain") 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 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; } @ManagedOperation(description = "Retrieves a list of the interceptors in the chain") public String getInterceptorDetails() { StringBuilder sb = new StringBuilder("Interceptor chain: \n"); int count = 0; for (CommandInterceptor i : asList()) { count++; sb.append(" ").append(count).append(". ").append(i).append("\n"); } return sb.toString(); } @ManagedOperation(description = "Retrieves a list of the interceptors in the chain, formatted as HTML") public String getInterceptorDetailsAsHtml() { return CachePrinter.formatHtml(getInterceptorDetails()); } /** * 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 addAfterInterceptor(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; } /** * 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 addBeforeInterceptor(CommandInterceptor toAdd, Class beforeInterceptor) { if (firstInChain.getClass().equals(beforeInterceptor)) { toAdd.setNext(firstInChain); firstInChain = toAdd; return true; } CommandInterceptor it = firstInChain; while (it.getNext() != null) { if (it.getNext().getClass().equals(beforeInterceptor)) { 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 (InterruptedException ie) { Thread.currentThread().interrupt(); return null; } 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 its 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; } /** * Returns all the interceptors that have the fully qualified name of their class equal with the supplied class name. */ public List getInterceptorsWithClassName(String fqName) { CommandInterceptor iterator = firstInChain; List result = new ArrayList(2); while (iterator != null) { if (iterator.getClass().getName().equals(fqName)) result.add(iterator); iterator = iterator.getNext(); } return result; } public String toString() { return "InterceptorChain{\n" + CachePrinter.printInterceptorChain(firstInChain) + "\n}"; } /** * Checks whether the chain contains the supplied interceptor instance. */ public boolean containsInstance(CommandInterceptor interceptor) { CommandInterceptor it = firstInChain; while (it != null) { if (it == interceptor) return true; it = it.getNext(); } return false; } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/OrderedSynchronizationHandler.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/OrderedSynchronizationHandler.ja0000644000175000017500000000736211111047320033371 0ustar moellermoeller/* * 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.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 7168 2008-11-19 17:37:20Z jason.greene@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); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/CacheMgmtInterceptor.java0000644000175000017500000001642311142561344031774 0ustar moellermoeller/* * 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.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.JmxStatsCommandInterceptor; import org.jboss.cache.jmx.annotations.ManagedAttribute; import org.jboss.cache.jmx.annotations.ManagedOperation; import java.util.HashMap; import java.util.Map; /** * Captures cache management statistics * * @author Jerry Gauthier * @version $Id: CacheMgmtInterceptor.java 7654 2009-02-05 13:00:20Z manik.surtani@jboss.com $ */ public class CacheMgmtInterceptor extends JmxStatsCommandInterceptor { private long hitTimes = 0; private long missTimes = 0; private long storeTimes = 0; private long hits = 0; private long misses = 0; private long stores = 0; private long evictions = 0; private long start = System.currentTimeMillis(); private long reset = 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); 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) { missTimes = missTimes + (t2 - t1); misses++; } else { hitTimes = hitTimes + (t2 - t1); 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) { storeTimes = storeTimes + (t2 - t1); stores = 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(); storeTimes = storeTimes + (t2 - t1); stores++; return retval; } @ManagedAttribute(description = "number of cache attribute hits") public long getHits() { return hits; } @ManagedAttribute(description = "number of cache attribute misses") public long getMisses() { return misses; } @ManagedAttribute(description = "number of cache attribute put operations") public long getStores() { return stores; } @ManagedAttribute(description = "number of cache eviction operations") public long getEvictions() { return evictions; } @ManagedAttribute(description = "hit/miss ratio for the cache") public double getHitMissRatio() { double total = hits + misses; if (total == 0) { return 0; } return (hits / total); } @ManagedAttribute(description = "read/writes ratio for the cache") public double getReadWriteRatio() { if (stores == 0) { return 0; } return (((double) (hits + misses) / (double) stores)); } @ManagedAttribute(description = "average number of milliseconds for a read operation") public long getAverageReadTime() { long total = hits + misses; if (total == 0) { return 0; } return (hitTimes + missTimes) / total; } @ManagedAttribute(description = "average number of milliseconds for a write operation") public long getAverageWriteTime() { if (stores == 0) { return 0; } return (storeTimes) / stores; } @ManagedAttribute(description = "number of cache attributes") public int getNumberOfAttributes() { return dataContainer.getNumberOfAttributes(); } @ManagedAttribute(description = "number of nodes in the cache") public int getNumberOfNodes() { return dataContainer.getNumberOfNodes(); } @ManagedAttribute(description = "seconds since cache started") public long getElapsedTime() { return (System.currentTimeMillis() - start) / 1000; } @ManagedAttribute(description = "number of seconds since the cache statistics were last reset") public long getTimeSinceReset() { return (System.currentTimeMillis() - reset) / 1000; } @ManagedOperation public Map dumpStatistics() { Map retval = new HashMap(); retval.put("Hits", hits); retval.put("Misses", misses); retval.put("Stores", stores); retval.put("Evictions", 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; } @ManagedOperation public void resetStatistics() { hits = 0; misses = 0; stores = 0; evictions = 0; hitTimes = 0; missTimes = 0; storeTimes = 0; reset = System.currentTimeMillis(); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/CacheLoaderInterceptor.java0000755000175000017500000004527411371344767032323 0ustar moellermoeller/* * 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.interceptors; import org.jboss.cache.CacheException; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.InternalNode; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.buddyreplication.BuddyFqnTransformer; 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 org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.Start; import org.jboss.cache.interceptors.base.JmxStatsCommandInterceptor; import org.jboss.cache.jmx.annotations.ManagedAttribute; import org.jboss.cache.jmx.annotations.ManagedOperation; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.loader.CacheLoaderManager; import org.jboss.cache.mvcc.MVCCNodeHelper; import org.jboss.cache.mvcc.NullMarkerNode; import org.jboss.cache.notifications.Notifier; 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 8400 2010-05-08 20:30:47Z bstansberry@jboss.com $ */ public class CacheLoaderInterceptor extends JmxStatsCommandInterceptor { private long cacheLoads = 0; private long cacheMisses = 0; private CacheLoaderManager clm; protected TransactionTable txTable = null; protected CacheLoader loader; protected DataContainer dataContainer; protected Notifier notifier; protected boolean isActivation = false; // protected boolean usingVersionedInvalidation = false; protected MVCCNodeHelper helper; protected BuddyFqnTransformer buddyFqnTransformer; /** * 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, Notifier notifier, MVCCNodeHelper helper, BuddyFqnTransformer buddyFqnTransformer) { this.txTable = txTable; this.clm = clm; // CacheMode mode = configuration.getCacheMode(); // usingVersionedInvalidation = mode.isInvalidation(); this.dataContainer = dataContainer; this.notifier = notifier; this.helper = helper; } @Start protected void startInterceptor() { loader = clm.getCacheLoader(); } @Override public Object visitPutDataMapCommand(InvocationContext ctx, PutDataMapCommand command) throws Throwable { if (command.getFqn() != null) { if (command.isErase()) { replace(ctx, command.getFqn()); } else { loadIfNeeded(ctx, command.getFqn(), null, true, true, false, false, false, false, true); } } 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, false, false, false, true); } 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, false, true, false, true); } loadIfNeeded(ctx, command.getFqn(), null, false, false, true, true, true, false, true); } 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, false, false, false, true); } 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, false, false, true, true); } 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, false, false, true, true); loadChildren(fqn, dataContainer.peekInternalNode(fqn, true), 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, false, false, false, true); } 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, false, false, false, true); } 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"); // this needs to be done in reverse order. List list = ctx.getTransactionContext().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.getNodeLockingScheme() == NodeLockingScheme.OPTIMISTIC && command.getFqn() != null) { loadIfNeeded(ctx, command.getFqn(), null, false, false, false, false, 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, false, false, false, true); } 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, false, false, false, true); } return invokeNextInterceptor(ctx, command); } private void replace(InvocationContext ctx, Fqn fqn) throws InterruptedException { NodeSPI n = helper.wrapNodeForReading(ctx, fqn, true); if (n instanceof NullMarkerNode) { ctx.getLookedUpNodes().remove(fqn); } n = helper.wrapNodeForWriting(ctx, fqn, true, true, true, false, false); n.setDataLoaded(true); } private void loadIfNeeded(InvocationContext ctx, Fqn fqn, Object key, boolean allKeys, boolean initNode, boolean acquireWriteLock, boolean recursive, boolean isMove, boolean bypassLoadingData, boolean shouldLoadIfNodeIsNull) throws Throwable { NodeSPI n = helper.wrapNodeForReading(ctx, fqn, true); if (n instanceof NullMarkerNode) { ctx.getLookedUpNodes().remove(fqn); n = null; } boolean mustLoad = mustLoad(fqn, n, key, allKeys || isMove, shouldLoadIfNodeIsNull); if (trace) log.trace("load element " + fqn + " mustLoad=" + mustLoad); if (mustLoad) { if (acquireWriteLock || initNode) { boolean isNew = n == null; n = helper.wrapNodeForWriting(ctx, fqn, true, false, true, false, true); // won't create any nodes but will acquire locks. if (isNew && n != null) n.setDataLoaded(false); } // This is really convoluted if (n == null || !n.isDeleted()) { boolean exists; Map nodeData = null; if (bypassLoadingData) { exists = loader.exists(fqn); } else { nodeData = loadData(ctx, fqn); exists = nodeData != null; } if (n == null && exists) { // just create a dummy node in memory n = helper.wrapNodeForWriting(ctx, fqn, true, true, true, false, false); for (NodeSPI node = n; node != null && node.isCreated(); node = node.getParentDirect()) { node.setDataLoaded(false); } } if (nodeData != null) { setNodeState(ctx, fqn, n, nodeData); } } } // The complete list of children aren't known without loading them if (recursive && n != null) { loadChildren(fqn, n.getDelegationTarget(), recursive, isMove, ctx); } } /** * Load the children. * * @param node may be null if the node was not found. * @param ctxt */ private void loadChildren(Fqn fqn, InternalNode 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.removeChildren(); } node.setChildrenLoaded(true); } return; } // The children of a dead buddy backup root must be Integer. // Ensure that we didn't pull in the wrong type from a cache loader // (e.g. FileCacheLoader) that can only return String if (buddyFqnTransformer != null && buddyFqnTransformer.isDeadBackupRoot(fqn)) { childrenNames = buddyFqnTransformer.getDeadBackupRootChildren(childrenNames); } // Create if node had not been created already if (node == null) { NodeSPI temp = helper.wrapNodeForWriting(ctxt, fqn, true, true, true, false, false); node = temp.getDelegationTarget(); } // Create one DataNode per child, mark as UNINITIALIZED for (Object name : childrenNames) { Fqn childFqn = Fqn.fromRelativeElements(fqn, name); // create child if it didn't exist NodeSPI child = helper.wrapNodeForWriting(ctxt, childFqn, true, true, true, false, false); if (child.isCreated()) child.setDataLoaded(false); if ((isMove || isActivation) && recursive) { // load data for children as well! child.setInternalState(loadData(ctxt, child.getFqn())); child.setDataLoaded(true); } if (recursive) { loadChildren(child.getFqn(), child.getDelegationTarget(), true, isMove, ctxt); } } node.setChildrenLoaded(true); } private boolean mustLoad(Fqn fqn, NodeSPI n, Object key, boolean allKeys, boolean shouldLoadIfNodeIsNull) { if (n == null) { if (trace) log.trace("Node [" + fqn + "] is null in memory. Must load? " + shouldLoadIfNodeIsNull); return shouldLoadIfNodeIsNull; } // check this first!!! if (!n.isValid()) // && configuration.getNodeLockingScheme().isVersionedScheme()) { // attempt to load again; this only happens if we have tombstones lying around, or we are using invalidation. if (trace) { log.trace("Loading node [" + fqn + "] 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 [" + fqn + "], key requested is null"); return false; } if (n.containsKeyDirect(key)) { if (trace) log.trace("Don't load [" + fqn + "], already have necessary key in memory"); return false; } } if (!n.isDataLoaded()) { if (trace) log.trace("Must load node [" + fqn + "], uninitialized"); return true; } if (trace) log.trace("Don't load node [" + fqn + "], by default"); 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 void setNodeState(InvocationContext ctx, Fqn fqn, NodeSPI n, Map nodeData) throws Exception { if (trace) log.trace("setNodeState node is " + n); if (nodeData != null) { notifier.notifyNodeLoaded(fqn, true, Collections.emptyMap(), ctx); if (isActivation) { notifier.notifyNodeActivated(fqn, true, Collections.emptyMap(), ctx); } Map internalState = n.getInternalState(false); if (internalState != null && !internalState.isEmpty()) { nodeData = new HashMap(nodeData); nodeData.putAll(internalState); } // nodeData.putAll(n.getInternalState(false)); n.setInternalState(nodeData); // set this node as valid? // if (usingVersionedInvalidation) n.setValid(true, false); n.setValid(true, false); notifier.notifyNodeLoaded(fqn, false, nodeData, ctx); if (isActivation) { notifier.notifyNodeActivated(fqn, false, nodeData, ctx); } } if (!n.isDataLoaded()) { if (trace) log.trace("Setting dataLoaded to true"); n.setDataLoaded(true); } } private Map loadData(InvocationContext ctx, Fqn fqn) throws Exception { if (trace) log.trace("Attempting to load data for " + fqn); Map nodeData = loader.get(fqn); boolean nodeExists = (nodeData != null); if (trace) log.trace("Node " + fqn + " exists? " + nodeExists); if (nodeExists) recordNodeLoaded(ctx, fqn); if (getStatisticsEnabled()) { if (nodeExists) { cacheLoads++; } else { cacheMisses++; } } return nodeData; } @ManagedAttribute(description = "number of cache loader node loads") public long getCacheLoaderLoads() { return cacheLoads; } @ManagedAttribute(description = "number of cache loader node misses") public long getCacheLoaderMisses() { return cacheMisses; } @ManagedOperation @Override public void resetStatistics() { cacheLoads = 0; cacheMisses = 0; } @ManagedOperation @Override public Map dumpStatistics() { Map retval = new HashMap(); retval.put("CacheLoaderLoads", cacheLoads); retval.put("CacheLoaderMisses", cacheMisses); return retval; } protected void recordNodeLoaded(InvocationContext ctx, Fqn fqn) { // this is a no-op. Only used by subclasses. } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/TxInterceptor.java0000644000175000017500000012375011144362365030545 0ustar moellermoeller/* * 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.interceptors; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import javax.transaction.InvalidTransactionException; import javax.transaction.Status; import javax.transaction.Synchronization; import javax.transaction.SystemException; import javax.transaction.Transaction; 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.CommandsFactory; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.commands.WriteCommand; 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.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.ComponentRegistry; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.context.ContextFactory; import org.jboss.cache.invocation.InvocationContextContainer; import org.jboss.cache.jmx.annotations.ManagedAttribute; import org.jboss.cache.jmx.annotations.ManagedOperation; import org.jboss.cache.lock.LockManager; import org.jboss.cache.notifications.Notifier; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.TransactionContext; import org.jboss.cache.transaction.TransactionLog; import org.jboss.cache.transaction.TransactionTable; import org.jboss.cache.util.concurrent.ConcurrentHashSet; /** * 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 AT jboss DOT org) * @author Steve Woodcock (stevew@jofti.com) * @author Galder Zamarreno */ public class TxInterceptor extends BaseTransactionalContextInterceptor { protected CommandsFactory commandsFactory; protected RPCManager rpcManager; private Notifier notifier; private InvocationContextContainer invocationContextContainer; private ComponentRegistry componentRegistry; private ContextFactory contextFactory; private TransactionLog transactionLog; /** * 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; private boolean statsEnabled; @Inject public void intialize(RPCManager rpcManager, ContextFactory contextFactory, Notifier notifier, InvocationContextContainer icc, TransactionLog transactionLog, CommandsFactory factory, ComponentRegistry componentRegistry, LockManager lockManager) { this.contextFactory = contextFactory; this.transactionLog = transactionLog; this.commandsFactory = factory; this.rpcManager = rpcManager; this.notifier = notifier; this.invocationContextContainer = icc; this.componentRegistry = componentRegistry; this.lockManager = lockManager; setStatisticsEnabled(configuration.getExposeManagementStatistics()); } @Override public Object visitPrepareCommand(InvocationContext ctx, PrepareCommand command) throws Throwable { Object result = null; // this is a prepare, commit, or rollback. GlobalTransaction gtx = ctx.getGlobalTransaction(); if (trace) log.trace("Got gtx from invocation context " + gtx); try { if (gtx.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 public Object visitCommitCommand(InvocationContext ctx, CommitCommand command) throws Throwable { GlobalTransaction gtx = ctx.getGlobalTransaction(); 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 + "]"); 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 { GlobalTransaction gtx = ctx.getGlobalTransaction(); if (!gtx.isRemote()) { if (trace) log.trace("received my own message (discarding it)"); return null; } try { if (trace) log.trace("(" + rpcManager.getLocalAddress() + ") call on command [" + command + "]"); 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. * * @throws Throwable */ @Override public Object handleDefault(InvocationContext ctx, VisitableCommand command) throws Throwable { try { Object ret = attachGtxAndPassUpChain(ctx, command); if (command instanceof WriteCommand && ctx.getTransaction() == null) transactionLog.logNoTxWrite((WriteCommand)command); return ret; } 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 // ------------------------------------------------------------------------ // -------------------------------------------------------------- /** * 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 // transactionContext for TX in txTable, the modifications // below will need this transactionContext to add their modifications // under the GlobalTx key TransactionContext transactionContext = txTable.get(gtx); if (transactionContext == null) { // create a new transaction transactionContext if (trace) log.trace("creating new tx transactionContext"); transactionContext = contextFactory.createTransactionContext(ltx); txTable.put(gtx, transactionContext); } setTransactionalContext(ltx, gtx, transactionContext, ctx); // register a sync handler for this tx. registerHandler(ltx, new RemoteSynchronizationHandler(gtx, ltx, transactionContext), 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 { transactionLog.logPrepare(command); // 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; } 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 */ protected void replayModifications(InvocationContext ctx, Transaction ltx, PrepareCommand command) throws Throwable { try { // replay modifications for (WriteCommand 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. * * @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) { try { if (trace) log.trace("Running commit for " + gtx); VisitableCommand commitCommand = onePhaseCommit ? buildPrepareCommand(gtx, modifications, true) : commandsFactory.buildCommitCommand(gtx); handleCommitRollback(ctx, commitCommand); if (onePhaseCommit) { transactionLog.logOnePhaseCommit(gtx, modifications); } else { transactionLog.logCommit(gtx); } } 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 { TransactionContext transactionContext = ctx.getTransactionContext(); if (transactionContext != null) lockManager.unlock(ctx); } /** * creates a rollback() */ protected void runRollbackPhase(InvocationContext ctx, GlobalTransaction gtx, Transaction tx) { try { // JBCACHE-457 VisitableCommand rollbackCommand = commandsFactory.buildRollbackCommand(gtx); if (trace) log.trace(" running rollback for " + gtx); transactionLog.rollback(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); } } 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. PrepareCommand prepareCommand = buildPrepareCommand(gtx, modifications, false); transactionLog.logPrepare(prepareCommand); 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); 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. */ private GlobalTransaction registerTransaction(Transaction tx, InvocationContext ctx) throws Exception { // we have ascertained that the current thread *is* associated with a transaction. We need to make sure the // transaction is in a valid state before moving on, and throwing an exception if not. boolean txValid = TransactionTable.isValid(tx); if (!txValid) { throw new IllegalStateException("Transaction " + tx + " is not in a valid state to be invoking cache operations on."); } GlobalTransaction gtx; if (transactions.add(tx)) { gtx = txTable.getCurrentTransaction(tx, true); TransactionContext transactionContext; if (ctx.getGlobalTransaction() == null) { ctx.setGlobalTransaction(gtx); transactionContext = txTable.get(gtx); ctx.setTransactionContext(transactionContext); } else { transactionContext = ctx.getTransactionContext(); } 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, transactionContext, !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. */ private void registerHandler(Transaction tx, Synchronization handler, InvocationContext ctx) throws Exception { OrderedSynchronizationHandler orderedHandler = ctx.getTransactionContext().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 * * @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; TransactionContext transactionContext = null; protected InvocationContext ctx; // the context for this call. RemoteSynchronizationHandler(GlobalTransaction gtx, Transaction tx, TransactionContext entry) { this.gtx = gtx; this.tx = tx; this.transactionContext = entry; } public void beforeCompletion() { if (trace) log.trace("Running beforeCompletion on gtx " + gtx); if (transactionContext == null) { log.error("Transaction has a null transaction entry - beforeCompletion() will fail."); throw new IllegalStateException("cannot find transaction entry for " + gtx); } modifications = transactionContext.getModifications(); ctx = invocationContextContainer.get(); setTransactionalContext(tx, gtx, transactionContext, ctx); if (ctx.isOptionsUninitialised() && transactionContext.getOption() != null) { ctx.setOptionOverrides(transactionContext.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, transactionContext, ctx); if (ctx.isOptionsUninitialised() && transactionContext != null && transactionContext.getOption() != null) { // use the options from the transaction entry instead ctx.setOptionOverrides(transactionContext.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 (transactionContext != null) { // this should ideally be set in beforeCompletion(), after compacting the list. if (modifications == null) modifications = transactionContext.getModifications(); boolean isSuppressEventNotification = ctx.getOptionOverrides().isSuppressEventNotification(); ctx.setOptionOverrides(transactionContext.getOption()); ctx.getOptionOverrides().setSuppressEventNotification(isSuppressEventNotification); } 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); 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 (transactionContext != null) transactionContext.reset(); transactionContext = 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, TransactionContext transactionContext, boolean remoteLocal) { super(gtx, tx, transactionContext); 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, transactionContext, ctx); if (!transactionContext.hasModifications()) { if (trace) log.trace("No modifications in this tx. Skipping beforeCompletion()"); modifications = Collections.emptyList(); return; } else { modifications = transactionContext.getModifications(); } // set any transaction wide options as current for this thread, caching original options that would then be reset originalOptions = ctx.getOptionOverrides(); transactionalOptions = transactionContext.getOption(); transactionalOptions.setSuppressEventNotification(originalOptions.isSuppressEventNotification()); 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. 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, transactionContext, ctx); if (transactionalOptions != null) ctx.setOptionOverrides(transactionalOptions); try { super.afterCompletion(status); } finally { ctx.setOptionOverrides(originalOptions); } } @Override public String toString() { return "TxInterceptor.LocalSynchronizationHandler(gtx=" + gtx + ", tx=" + getTxAsString() + ")"; } } @ManagedOperation public void resetStatistics() { prepares = 0; commits = 0; rollbacks = 0; } @ManagedOperation public Map dumpStatistics() { Map retval = new HashMap(3); retval.put("Prepares", prepares); retval.put("Commits", commits); retval.put("Rollbacks", rollbacks); return retval; } @ManagedAttribute public boolean getStatisticsEnabled() { return this.statsEnabled; } @ManagedAttribute public void setStatisticsEnabled(boolean enabled) { this.statsEnabled = enabled; } @ManagedAttribute(description = "number of transaction prepares") public long getPrepares() { return prepares; } @ManagedAttribute(description = "number of transaction commits") public long getCommits() { return commits; } @ManagedAttribute(description = "number of transaction rollbacks") public long getRollbacks() { return rollbacks; } @ManagedAttribute(name = "numberOfSyncsRegistered", writable = false, description = "number of transaction synchronizations currently registered") public int getNumberOfSyncsRegistered() { return transactions.size(); } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/OptimisticLockingInterceptor.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/OptimisticLockingInterceptor.jav0000755000175000017500000001312311111047320033420 0ustar moellermoeller/* * 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.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.BatchModeTransactionManager; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.TransactionContext; /** * 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 AT jboss DOT org) * @author Steve Woodcock (stevew@jofti.com) * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated public class OptimisticLockingInterceptor extends OptimisticInterceptor { @Start void init() { if (txManager == null || txManager.getClass().equals(BatchModeTransactionManager.class)) 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 { TransactionContext transactionContext = ctx.getTransactionContext(); if (transactionContext != 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-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/MVCCLockingInterceptor.java0000644000175000017500000003433511117531341032201 0ustar moellermoeller/* * 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.interceptors; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.InternalNode; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; 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.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; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.interceptors.base.PrePostProcessingCommandInterceptor; import org.jboss.cache.lock.LockManager; import org.jboss.cache.mvcc.MVCCNodeHelper; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Map; /** * Interceptor to implement MVCC functionality. * * @author Manik Surtani (manik AT jboss DOT org) * @see MVCC designs * @since 3.0 */ public class MVCCLockingInterceptor extends PrePostProcessingCommandInterceptor { LockManager lockManager; DataContainer dataContainer; MVCCNodeHelper helper; @Inject public void setDependencies(LockManager lockManager, DataContainer dataContainer, MVCCNodeHelper helper) { this.lockManager = lockManager; this.dataContainer = dataContainer; this.helper = helper; } @Override protected boolean doBeforeCall(InvocationContext ctx, VisitableCommand command) { if (ctx.getOptionOverrides().isSuppressLocking() && log.isWarnEnabled()) { log.warn("Lock suppression not supported with MVCC!"); } return true; } @Override public Object handlePutDataMapCommand(InvocationContext ctx, PutDataMapCommand command) throws Throwable { helper.wrapNodeForWriting(ctx, command.getFqn(), true, true, false, false, false); // get the node and stick it in the context. return invokeNextInterceptor(ctx, command); } @Override public Object handlePutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable { helper.wrapNodeForWriting(ctx, command.getFqn(), true, true, false, false, false); // get the node and stick it in the context. return invokeNextInterceptor(ctx, command); } @Override public Object handlePutForExternalReadCommand(InvocationContext ctx, PutForExternalReadCommand command) throws Throwable { ctx.getOptionOverrides().setLockAcquisitionTimeout(0); helper.wrapNodeForWriting(ctx, command.getFqn(), true, true, false, false, false); // get the node and stick it in the context. return invokeNextInterceptor(ctx, command); } @Override public Object handleRemoveNodeCommand(InvocationContext ctx, RemoveNodeCommand command) throws Throwable { helper.wrapNodesRecursivelyForRemoval(ctx, command.getFqn()); return invokeNextInterceptor(ctx, command); } @Override public Object handleClearDataCommand(InvocationContext ctx, ClearDataCommand command) throws Throwable { helper.wrapNodeForWriting(ctx, command.getFqn(), true, false, false, false, false); return invokeNextInterceptor(ctx, command); } @Override public Object handleEvictFqnCommand(InvocationContext ctx, EvictCommand command) throws Throwable { // set lock acquisition timeout to 0 - we need to fail fast. ctx.getOptionOverrides().setLockAcquisitionTimeout(0); if (command.isRecursive()) { handleRecursiveEvict(ctx, command); } else { handleNonrecursiveEvict(ctx, command); } return invokeNextInterceptor(ctx, command); } @SuppressWarnings("unchecked") private void handleRecursiveEvict(InvocationContext ctx, EvictCommand command) throws InterruptedException { List fqnsToEvict; if (command.getFqn().isRoot()) { // if this is the root node, do not attempt to lock this for writing but instead just get all direct children of root. Map children = dataContainer.peekInternalNode(Fqn.ROOT, false).getChildrenMap(); if (!children.isEmpty()) { fqnsToEvict = new LinkedList(); // lock recursively. for (InternalNode child : children.values()) { fqnsToEvict.addAll(helper.wrapNodesRecursivelyForRemoval(ctx, child.getFqn())); } } else { fqnsToEvict = Collections.emptyList(); } } else { // lock current node recursively. fqnsToEvict = helper.wrapNodesRecursivelyForRemoval(ctx, command.getFqn()); } // set these in the evict command so that the command is aware of what needs to be evicted. command.setNodesToEvict(fqnsToEvict); } @SuppressWarnings("unchecked") private void handleNonrecursiveEvict(InvocationContext ctx, EvictCommand command) throws InterruptedException { if (command.getFqn().isRoot()) { // if this is the root node, do not attempt to lock this for writing but instead just get all direct children of root. Map children = dataContainer.peekInternalNode(Fqn.ROOT, false).getChildrenMap(); if (!children.isEmpty()) { for (InternalNode child : children.values()) { helper.wrapNodeForWriting(ctx, child.getFqn(), true, false, false, true, true); } } } else { // just wrap the node for writing. Do not create if absent. helper.wrapNodeForWriting(ctx, command.getFqn(), true, false, false, true, true); } } @Override public Object handleInvalidateCommand(InvocationContext ctx, InvalidateCommand command) throws Throwable { // this should be handled the same as a recursive evict command. ctx.getOptionOverrides().setLockAcquisitionTimeout(0); if (!command.getFqn().isRoot()) helper.wrapNodesRecursivelyForRemoval(ctx, command.getFqn()); return invokeNextInterceptor(ctx, command); } @Override public Object handleRemoveKeyCommand(InvocationContext ctx, RemoveKeyCommand command) throws Throwable { helper.wrapNodeForWriting(ctx, command.getFqn(), true, false, false, false, false); return invokeNextInterceptor(ctx, command); } @Override public Object handleGetDataMapCommand(InvocationContext ctx, GetDataMapCommand command) throws Throwable { helper.wrapNodeForReading(ctx, command.getFqn(), true); return invokeNextInterceptor(ctx, command); } @Override public Object handleExistsNodeCommand(InvocationContext ctx, ExistsCommand command) throws Throwable { helper.wrapNodeForReading(ctx, command.getFqn(), true); return invokeNextInterceptor(ctx, command); } @Override public Object handleGetKeyValueCommand(InvocationContext ctx, GetKeyValueCommand command) throws Throwable { helper.wrapNodeForReading(ctx, command.getFqn(), true); return invokeNextInterceptor(ctx, command); } @Override public Object handleGetNodeCommand(InvocationContext ctx, GetNodeCommand command) throws Throwable { helper.wrapNodeForReading(ctx, command.getFqn(), true); return invokeNextInterceptor(ctx, command); } @Override public Object handleGetKeysCommand(InvocationContext ctx, GetKeysCommand command) throws Throwable { helper.wrapNodeForReading(ctx, command.getFqn(), true); return invokeNextInterceptor(ctx, command); } @Override public Object handleGetChildrenNamesCommand(InvocationContext ctx, GetChildrenNamesCommand command) throws Throwable { helper.wrapNodeForReading(ctx, command.getFqn(), true); return invokeNextInterceptor(ctx, command); } @Override public Object handleMoveCommand(InvocationContext ctx, MoveCommand command) throws Throwable { // Nodes we need to get WLs for: // 1) node we are moving FROM, and its parent and ALL children. Same as removeNode. List nodeAndChildren = helper.wrapNodesRecursivelyForRemoval(ctx, command.getFqn()); Fqn newParent = command.getTo(); Fqn oldParent = command.getFqn().getParent(); // 2) The new parent. helper.wrapNodeForWriting(ctx, newParent, true, true, false, false, false); if (!oldParent.equals(newParent)) { // the nodeAndChildren list contains all child nodes, including the node itself. // 3) now obtain locks on the new places these new nodes will occupy. for (Fqn f : nodeAndChildren) { Fqn newChildFqn = f.replaceAncestor(oldParent, newParent); helper.wrapNodeForWriting(ctx, newChildFqn, true, true, true, false, false); } } // now pass up the chain. return invokeNextInterceptor(ctx, command); } @Override public Object handleGravitateDataCommand(InvocationContext ctx, GravitateDataCommand command) throws Throwable { helper.wrapNodeForReading(ctx, command.getFqn(), true); return invokeNextInterceptor(ctx, command); } @Override public Object handleRollbackCommand(InvocationContext ctx, RollbackCommand command) throws Throwable { Object retval = null; try { retval = invokeNextInterceptor(ctx, command); } finally { transactionalCleanup(false, ctx); } return retval; } @Override public Object handleCommitCommand(InvocationContext ctx, CommitCommand command) throws Throwable { Object retval = null; try { retval = invokeNextInterceptor(ctx, command); } finally { transactionalCleanup(true, ctx); } return retval; } @Override public Object handlePrepareCommand(InvocationContext ctx, PrepareCommand command) throws Throwable { Object retval = null; try { retval = invokeNextInterceptor(ctx, command); } finally { if (command.isOnePhaseCommit()) transactionalCleanup(true, ctx); } return retval; } @SuppressWarnings("unchecked") protected void doAfterCall(InvocationContext ctx, VisitableCommand command) { // for non-transactional stuff. if (ctx.getTransactionContext() == null) { List locks; if (!(locks = ctx.getLocks()).isEmpty()) { cleanupLocks(locks, ctx, Thread.currentThread(), true); } else { if (trace) log.trace("Nothing to do since there are no modifications in scope."); } } else { if (trace) log.trace("Nothing to do since there is a transaction in scope."); } } private void cleanupLocks(List locks, InvocationContext ctx, Object owner, boolean commit) { // clean up. // unlocking needs to be done in reverse order. ListIterator it = locks.listIterator(locks.size()); if (commit) { while (it.hasPrevious()) { Fqn f = it.previous(); NodeSPI rcn = ctx.lookUpNode(f); // could be null with read-committed if (rcn != null) rcn.commitUpdate(ctx, dataContainer); // and then unlock if (trace) log.trace("Releasing lock on [" + f + "] for owner " + owner); lockManager.unlock(f, owner); } } else { while (it.hasPrevious()) { Fqn f = it.previous(); NodeSPI rcn = ctx.lookUpNode(f); // could be null with read-committed if (rcn != null) rcn.rollbackUpdate(); // and then unlock if (trace) log.trace("Releasing lock on [" + f + "] for owner " + owner); lockManager.unlock(f, owner); } } ctx.clearLocks(); } @SuppressWarnings("unchecked") private void transactionalCleanup(boolean commit, InvocationContext ctx) { if (ctx.getTransactionContext() != null) { List locks = ctx.getTransactionContext().getLocks(); if (!locks.isEmpty()) cleanupLocks(locks, ctx, ctx.getGlobalTransaction(), commit); } else { throw new IllegalStateException("Attempting to do a commit or rollback but there is no transactional context in scope. " + ctx); } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/ActivationInterceptor.java0000644000175000017500000002466211237013623032246 0ustar moellermoeller/* * 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.interceptors; import org.jboss.cache.Fqn; import org.jboss.cache.InternalNode; import org.jboss.cache.InvocationContext; 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.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.jmx.annotations.ManagedAttribute; import org.jboss.cache.jmx.annotations.ManagedOperation; import org.jboss.cache.mvcc.ReadCommittedNode; import javax.transaction.TransactionManager; import java.util.HashMap; 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 8169 2009-08-07 12:01:23Z manik.surtani@jboss.com $ */ public class ActivationInterceptor extends CacheLoaderInterceptor { 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(ctx, command.getFqn(), true); return returnValue; } @Override public Object visitGetKeysCommand(InvocationContext ctx, GetKeysCommand command) throws Throwable { Object returnValue = super.visitGetKeysCommand(ctx, command); removeNodeFromCacheLoader(ctx, command.getFqn(), true); return returnValue; } @Override public Object visitGetNodeCommand(InvocationContext ctx, GetNodeCommand command) throws Throwable { Object returnValue = super.visitGetNodeCommand(ctx, command); removeNodeFromCacheLoader(ctx, command.getFqn(), true); return returnValue; } @Override public Object visitGetKeyValueCommand(InvocationContext ctx, GetKeyValueCommand command) throws Throwable { Object returnValue = super.visitGetKeyValueCommand(ctx, command); removeNodeFromCacheLoader(ctx, command.getFqn(), true); 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(ctx, command.getFqn(), true); return returnValue; } @Override public Object visitPutDataMapCommand(InvocationContext ctx, PutDataMapCommand command) throws Throwable { Object returnValue = super.visitPutDataMapCommand(ctx, command); removeNodeFromCacheLoader(ctx, command.getFqn(), true); return returnValue; } @Override public Object visitRemoveKeyCommand(InvocationContext ctx, RemoveKeyCommand command) throws Throwable { Object returnValue = super.visitRemoveKeyCommand(ctx, command); removeNodeFromCacheLoader(ctx, command.getFqn(), true); 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(ctx, command.getFqn().getParent(), true); removeNodeFromCacheLoader(ctx, command.getTo(), true); return returnValue; } private boolean wasLoadedIntoMemory(InvocationContext ctx, Fqn fqn) { Set fqnsLoaded = ctx.getFqnsLoaded(); return fqnsLoaded != null && fqnsLoaded.contains(fqn); } /** * 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). */ private void removeNodeFromCacheLoader(InvocationContext ctx, Fqn fqn, boolean checkIfLoaded) throws Throwable { if (fqn != null) { boolean remove = false; if (!checkIfLoaded || wasLoadedIntoMemory(ctx, fqn)) { InternalNode n; if (((n = findNode(ctx, fqn)) != null) && n.isDataLoaded() && loader.exists(fqn)) { // node not null and attributes have been loaded? if (n.hasChildren()) { boolean result = childrenLoaded(n); if (result) { log.debug("children all initialized"); remove(fqn); remove = true; } } else if (loaderNoChildren(fqn)) { if (log.isDebugEnabled()) log.debug("no children " + n); remove(fqn); remove = true; } } } if (!fqn.isRoot() && remove) { // check fqn parent, since the parent may not be needed on disk anymore removeNodeFromCacheLoader(ctx, fqn.getParent(), false); } } } private InternalNode findNode(InvocationContext ctx, Fqn fqn) { ReadCommittedNode n = (ReadCommittedNode) ctx.lookUpNode(fqn); if (n == null || n.isNullNode()) { return dataContainer.peekInternalNode(fqn, true); } else { return n.getDelegationTarget(); } } private boolean childrenLoaded(InternalNode node) throws Exception { if (!node.isChildrenLoaded() && loader.getChildrenNames(node.getFqn()) != null) return false; for (InternalNode child : node.getChildren()) { if (!child.isDataLoaded()) { return false; } if (child.hasChildren()) { if (!childrenLoaded(child)) { return false; } } else if (!loaderNoChildren(child.getFqn())) { return false; } } return true; } 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; } } @ManagedAttribute(description = "number of cache node activations") public long getActivations() { return activations; } @ManagedOperation public void resetStatistics() { super.resetStatistics(); activations = 0; } @ManagedOperation public Map dumpStatistics() { Map retval = super.dumpStatistics(); if (retval == null) { retval = new HashMap(); } retval.put("Activations", activations); return retval; } @Override protected void recordNodeLoaded(InvocationContext ctx, Fqn fqn) { ctx.addFqnLoaded(fqn); } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/LegacyDataGravitatorInterceptor.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/LegacyDataGravitatorInterceptor.0000644000175000017500000005072111241335610033335 0ustar moellermoeller/* * 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.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.CommandsFactory; import org.jboss.cache.commands.DataCommand; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.VisitableCommand; 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.OptimisticPrepareCommand; 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.marshall.NodeData; import org.jboss.cache.marshall.RequestIgnoredResponse; import org.jboss.cache.transaction.GlobalTransaction; import org.jgroups.Address; import org.jgroups.blocks.GroupRequest; import org.jgroups.blocks.RspFilter; import java.util.Comparator; 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 AT jboss DOT org) * @deprecated will be removed with optimistic and pessimistic locking. */ @Deprecated public class LegacyDataGravitatorInterceptor 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 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()); } } /** * Make sure you also run a cleanup if we have an 1pc. */ @Override public Object visitPrepareCommand(InvocationContext ctx, PrepareCommand command) throws Throwable { if (command.isOnePhaseCommit()) { return dataGravitationCleanupOnCommit(ctx, command); } return invokeNextInterceptor(ctx, command); } @Override public Object visitCommitCommand(InvocationContext ctx, CommitCommand command) throws Throwable { return dataGravitationCleanupOnCommit(ctx, command); } @Override public Object visitOptimisticPrepareCommand(InvocationContext ctx, OptimisticPrepareCommand command) throws Throwable { return visitPrepareCommand(ctx, command); } private Object dataGravitationCleanupOnCommit(InvocationContext ctx, VisitableCommand command) throws Throwable { GlobalTransaction gtx = ctx.getGlobalTransaction(); try { doCommit(gtx); return invokeNextInterceptor(ctx, command); } finally { cleanupCommands.remove(gtx); } } /** * @param ctx invocation context * @param fqn fqn to test * @return true if the node does not exist; false otherwise. */ protected boolean nodeDoesNotExist(InvocationContext ctx, Fqn fqn) { return !dataContainer.exists(fqn); } 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 requested fqn " + command.getFqn()); 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 (nodeDoesNotExist(ctx, command.getFqn())) { // 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); wrapIfNeeded(ctx, data.primaryFqn); } } 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); } protected void wrapIfNeeded(InvocationContext ctx, Fqn fqnToWrap) throws InterruptedException { // no op } 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(); 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(rpcManager.getMembers().size()), 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 instanceof GravitateResult) { GravitateResult candidate = (GravitateResult) o; if (isPreferable(candidate, result)) { result = candidate; } } else if (o == null) { // 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 if (!configuration.isUseRegionBasedMarshalling()) { log.error("Unexpected null response to call " + command + "."); } } else if ((o instanceof RequestIgnoredResponse) == false) { log.error("Unexpected response " + o + " to call " + command + "."); } } return result; } /** * JBCACHE-1530. Compares GravitateResults in order to prefer those not * from dead owner trees. * * @param candidate a result that might be preferable to existingnull * @param existing the currently preferred result. Cannot be null * * @return true if candidate is preferable. */ private boolean isPreferable(GravitateResult candidate, GravitateResult existing) { if (existing.isDataFound() == false) { return true; } else if (candidate.isDataFound() == false) { return false; } else { int ownerIndex = BuddyManager.BUDDY_BACKUP_SUBTREE_FQN.size(); Fqn existingFqn = existing.getBuddyBackupFqn(); String existingOwner = (existingFqn.size() > ownerIndex ? (String) existingFqn.get(ownerIndex) : null); if (existingOwner == null) { return true; } boolean existingDead = existingOwner.endsWith(":DEAD"); Fqn candidateFqn = candidate.getBuddyBackupFqn(); String candidateOwner = (candidateFqn.size() > ownerIndex ? (String) candidateFqn.get(ownerIndex) : null); if (candidateOwner == null) { log.warn("Could not find a data owner in backupFqn from " + candidate); return false; } else if (candidateOwner.endsWith(":DEAD")) { if (existingDead) { // Both are dead. See if generational match matters if (candidateOwner.equals(existingOwner)) { int genIndex = ownerIndex + 1; Object existGen = (existingFqn.size() > genIndex ? existingFqn.get(genIndex) : null); if ((existGen instanceof Integer) == false) { return true; } Object candGen = (candidateFqn.size() > genIndex ? candidateFqn.get(genIndex) : null); if ((candGen instanceof Integer) == false) { log.warn("Could not find a dead data owner generation in backupFqn from " + candidate); return false; } return ((Integer) candGen).intValue() > ((Integer) existGen).intValue(); } else { // No poing comparing generations of different dead owners // Just prefer what we already have return false; } } else { // Prefer non-dead existing return false; } } else { // Candidate isn't dead but we prefer what we already have if it's not dead return existingDead; } } } @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); } } if (trace) log.trace("Retrieved data " + data + " found = " + found); 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 String toString() { return "BackupData{" + "primaryFqn=" + primaryFqn + ", backupFqn=" + backupFqn + ", backupData=" + backupData + '}'; } } public static class ResponseValidityFilter implements RspFilter { int memberCount; public ResponseValidityFilter(int memberCount) { this.memberCount = memberCount; } public boolean isAcceptable(Object object, Address address) { if (object instanceof GravitateResult) { memberCount--; } // always return true to make sure a response is logged by the JGroups RpcDispatcher. return true; } public boolean needMoreResponses() { return memberCount > 1; } } private static class GravitationResponseComparator implements Comparator { public int compare(Object o1, Object o2) { if (o1 instanceof GravitateResult) { if (o2 instanceof GravitateResult) { GravitateResult g2 = (GravitateResult) o2; if (g2.isDataFound() == false) { return -1; } else { return comparePositiveResults((GravitateResult) o1, g2); } } else { return -1; // put GR before non-GR } } else { return 1; // put misc stuff at the end } } private int comparePositiveResults(GravitateResult g1, GravitateResult g2) { Fqn f1 = g1.getBuddyBackupFqn(); Fqn f2 = g2.getBuddyBackupFqn(); return -1; } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/PessimisticLockInterceptor.java0000755000175000017500000004034611111047320033242 0ustar moellermoeller/* * 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.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.CommandsFactory; import org.jboss.cache.commands.DataCommand; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.commands.WriteCommand; import org.jboss.cache.commands.legacy.ReversibleCommand; 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.annotations.Inject; import org.jboss.cache.interceptors.base.PrePostProcessingCommandInterceptor; 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.PessimisticTransactionContext; import org.jboss.cache.transaction.TransactionContext; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Map; /** * 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 7168 2008-11-19 17:37:20Z jason.greene@jboss.com $ * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated public class PessimisticLockInterceptor extends PrePostProcessingCommandInterceptor { 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.getTransactionContext(), ctx.getGlobalTransaction()); Object retVal = invokeNextInterceptor(ctx, command); lockManager.unlock(ctx); return retVal; } @Override protected Object handleCommitCommand(InvocationContext ctx, CommitCommand command) throws Throwable { commit(ctx.getTransactionContext(), 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 { TransactionContext transactionContext = ctx.getTransactionContext(); if (trace) { log.trace("called to rollback cache with GlobalTransaction=" + command.getGlobalTransaction()); } if (transactionContext == null) { log.error("transactionContext for transaction " + command.getGlobalTransaction() + " not found (transaction has possibly already been rolled back)"); } else { for (Fqn fqn : transactionContext.getRemovedNodes()) { dataContainer.removeFromDataStructure(fqn, false); } // 1. Revert the modifications by running the undo-op list in reverse. This *cannot* throw any exceptions ! undoOperations((PessimisticTransactionContext) transactionContext); } if (trace) { log.trace("bypassed locking as method rollback() doesn't require locking"); } Object retVal = invokeNextInterceptor(ctx, command); lockManager.unlock(ctx); return retVal; } private void undoOperations(PessimisticTransactionContext transactionContext) { List modificationList = transactionContext.getAllModifications(); if (modificationList.isEmpty()) { if (trace) log.trace("Modification list is null, no modifications in this transaction!"); return; } if (trace) log.trace("undoOperations " + modificationList); ArrayList copy; copy = new ArrayList(modificationList); RuntimeException exc = null; for (ListIterator i = copy.listIterator(copy.size()); i.hasPrevious();) { WriteCommand undoOp = i.previous(); // since we are using pessimistic locking, all pessimistic WriteCommands implement ReversibleCommand. ReversibleCommand txCommand = (ReversibleCommand) undoOp; if (log.isDebugEnabled()) log.debug("Calling rollback() on command " + undoOp); try { txCommand.rollback(); } catch (RuntimeException re) { exc = re; } } if (exc != null) throw exc; } @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 its 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.getTransactionContext().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); TransactionContext transactionContext = null; if (ctx.getGlobalTransaction() != null) { transactionContext = ctx.getTransactionContext(); transactionContext.addRemovedNode(command.getFqn()); for (NodeSPI nodeSPI : createdNodes) { transactionContext.addRemovedNode(nodeSPI.getFqn()); nodeSPI.markAsDeleted(true); } } lockAllForRemoval(dataContainer.peek(command.getFqn(), false, false), ctx, transactionContext); 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. if (ctx.getGlobalTransaction() == null) { for (NodeSPI nodeSPI : createdNodes) dataContainer.removeFromDataStructure(nodeSPI.getFqn(), true); dataContainer.removeFromDataStructure(command.getFqn(), true); 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 transactionContext transaction entry * @throws InterruptedException in the event of interruption */ @SuppressWarnings("unchecked") public void lockAllForRemoval(NodeSPI node, InvocationContext ctx, TransactionContext transactionContext) throws InterruptedException { if (node == null) return; // lock node lockManager.lockAndRecord(node, WRITE, ctx); // add to deleted list if (transactionContext != null) transactionContext.addRemovedNode(node.getFqn()); // now children. Map children = node.getChildrenMapDirect(); for (NodeSPI child : children.values()) { // lock child. lockAllForRemoval(child, ctx, transactionContext); } } @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(TransactionContext transactionContext, GlobalTransaction gtx) { if (trace) log.trace("committing cache with gtx " + gtx); if (transactionContext == null) { log.error("transactionContext for transaction " + gtx + " not found (maybe already committed)"); return; } // first remove nodes that should be deleted. for (Fqn fqn : transactionContext.getRemovedNodes()) { dataContainer.removeFromDataStructure(fqn, false); } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/LegacyCacheStoreInterceptor.java0000644000175000017500000001200411122736406033301 0ustar moellermoeller/* * 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.interceptors; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.annotations.Compat; 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.factories.annotations.Start; import org.jboss.cache.transaction.GlobalTransaction; import javax.transaction.Transaction; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @Compat @Deprecated public class LegacyCacheStoreInterceptor extends CacheStoreInterceptor { /** * Used with optimistic locking to persist version information on Fqns that have changed. */ private Map> preparingTxs; private boolean optimistic; @Start void checkOptimistic() { optimistic = configuration.getNodeLockingScheme() == Configuration.NodeLockingScheme.OPTIMISTIC; if (optimistic) preparingTxs = new ConcurrentHashMap>(); } @Override protected void storeStateForPutDataMap(Fqn f, InvocationContext ctx) throws Exception { loader.put(f, ctx.lookUpNode(f).getDataDirect()); } @Override protected Object handleOptimisticPrepareCommand(InvocationContext ctx, OptimisticPrepareCommand command) throws Throwable { if (inTransaction()) { if (trace) log.trace("transactional so don't put stuff in the cloader yet."); GlobalTransaction gtx = command.getGlobalTransaction(); StoreModificationsBuilder smb = prepareCacheLoader(gtx, ctx.getTransactionContext(), command.isOnePhaseCommit()); if (smb != null && !smb.modifications.isEmpty()) preparingTxs.put(gtx, smb.affectedFqns); } return invokeNextInterceptor(ctx, command); } @Override protected Object handleRollbackCommand(InvocationContext ctx, RollbackCommand command) throws Throwable { try { return super.handleRollbackCommand(ctx, command); } finally { if (optimistic) { GlobalTransaction gtx = ctx.getGlobalTransaction(); preparingTxs.remove(gtx); } } } @Override protected Object handleCommitCommand(InvocationContext ctx, CommitCommand command) throws Throwable { Object returnValue = super.handleCommitCommand(ctx, command); // persist additional internal state, if any, and then clean up internal resources // specifically for optimistic locking to store versioning info AFTER a tx has committed. Hacky. // note - do NOT do this in a finally block. If the commit fails we shouldn't do this. if (optimistic) { // if the commit fails, preparingTxs will be cleaned up in a call to handleRollbackCommand() Set affectedFqns = preparingTxs.remove(ctx.getGlobalTransaction()); if (affectedFqns != null && !affectedFqns.isEmpty()) { storeInternalState(affectedFqns, ctx); } } return returnValue; } @SuppressWarnings("unchecked") private void storeInternalState(Set affectedFqns, InvocationContext ctx) throws Exception { // 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 = ctx.lookUpNode(f); if (n != null && !n.isDeleted()) { Map internalState = n.getInternalState(false); loader.put(f, internalState); } } } finally { txMgr.resume(tx); } } } ././@LongLink0000000000000000000000000000016000000000000011562 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/OptimisticCreateIfNotExistsInterceptor.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/OptimisticCreateIfNotExistsInter0000755000175000017500000002755211237013623033424 0ustar moellermoeller/* * 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.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 AT jboss DOT org) * @author Steve Woodcock (stevew@jofti.com) * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated 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 void injectDependencies(NodeFactory nodeFactory, DataContainer dataContainer, CacheSPI cacheSPI) { this.nodeFactory = nodeFactory; this.dataContainer = dataContainer; this.cache = cacheSPI; } @Start 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.isRemoved()) { peekInWorkspace.setRemoved(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 its 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.isRemoved()) { //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.isRemoved()) { 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); } } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/ReplicationInterceptor.java0000755000175000017500000001766211111047320032413 0ustar moellermoeller/* * 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.interceptors; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.commands.WriteCommand; 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.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.transaction.GlobalTransaction; import org.jboss.cache.transaction.TransactionContext; /** * 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 7168 2008-11-19 17:37:20Z jason.greene@jboss.com $ */ 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); TransactionContext transactionContext = ctx.getTransactionContext(); if (transactionContext.hasLocalModifications()) { PrepareCommand replicablePrepareCommand = command.copy(); // makre sure we remove any "local" transactions replicablePrepareCommand.removeModifications(transactionContext.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.getTransactionContext().setForceAsyncReplication(true); if (local) ctx.getTransactionContext().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.getTransactionContext().addLocalModification((WriteCommand) 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-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/MarshalledValueInterceptor.java0000644000175000017500000002154511176306660033223 0ustar moellermoeller/* * 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.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.Collections; 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 AT jboss DOT 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 ctx.isBypassUnmarshalling() ? retVal : 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 (!ctx.isBypassUnmarshalling() && !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 ctx.isBypassUnmarshalling() ? retVal : compactAndProcessRetVal(marshalledValues, retVal); } @Override public Object visitGetNodeCommand(InvocationContext ctx, GetNodeCommand command) throws Throwable { Object retVal = invokeNextInterceptor(ctx, command); return ctx.isBypassUnmarshalling() ? retVal : processRetVal(retVal); } @Override public Object visitClearDataCommand(InvocationContext ctx, ClearDataCommand command) throws Throwable { Object retVal = invokeNextInterceptor(ctx, command); return ctx.isBypassUnmarshalling() ? retVal : 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 ctx.isBypassUnmarshalling() ? retVal : compactAndProcessRetVal(marshalledValues, retVal); } @Override public Object visitGetChildrenNamesCommand(InvocationContext ctx, GetChildrenNamesCommand command) throws Throwable { Object retVal = invokeNextInterceptor(ctx, command); return ctx.isBypassUnmarshalling() ? retVal : processRetVal(retVal); } @Override public Object visitGetKeysCommand(InvocationContext ctx, GetKeysCommand command) throws Throwable { Object retVal = invokeNextInterceptor(ctx, command); return ctx.isBypassUnmarshalling() ? retVal : 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 ctx.isBypassUnmarshalling() ? retVal : 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 (m == null) { if (trace) log.trace("Map is nul; returning an empty map."); return Collections.emptyMap(); } 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-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/CallInterceptor.java0000644000175000017500000001534311111047320031004 0ustar moellermoeller/* * 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.interceptors; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.commands.WriteCommand; 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.Configuration.NodeLockingScheme; import org.jboss.cache.factories.annotations.Start; 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 7168 2008-11-19 17:37:20Z jason.greene@jboss.com $ */ public class CallInterceptor extends CommandInterceptor { private boolean notOptimisticLocking; @Start protected void start() { notOptimisticLocking = configuration.getNodeLockingScheme() != NodeLockingScheme.OPTIMISTIC; } @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, WriteCommand command) throws Throwable { Object result = invokeCommand(ctx, command); if (notOptimisticLocking && ctx.isValidTransaction()) { 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.getTransactionContext().addModification(command); } } return result; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/base/0000755000175000017500000000000011675221733025774 5ustar moellermoeller././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/base/SkipCheckChainedInterceptor.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/base/SkipCheckChainedInterceptor0000644000175000017500000003273011111047320033242 0ustar moellermoeller/* * 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.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); } ././@LongLink0000000000000000000000000000016200000000000011564 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/base/PrePostProcessingCommandInterceptor.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/base/PrePostProcessingCommandInt0000644000175000017500000003357311111047320033314 0ustar moellermoeller/* * 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.interceptors.base; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.commands.legacy.write.CreateNodeCommand; 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 adds pre and post processing to each visitXXX() method. *

    * For each visitXXX() method invoked, it will first call {@link #doBeforeCall(org.jboss.cache.InvocationContext, org.jboss.cache.commands.VisitableCommand)} * and if this method returns true, it will proceed to invoking a handleXXX() method and lastly, {@link #doAfterCall(org.jboss.cache.InvocationContext, org.jboss.cache.commands.VisitableCommand)} * in a finally block. Note that the doAfterCall() method is still invoked even if doBeforeCall() returns false. *

    * 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 and * optionally {@link #doBeforeCall(org.jboss.cache.InvocationContext, org.jboss.cache.commands.VisitableCommand)}. *

    * * @author Mircea.Markus@jboss.com * @since 2.2 */ public abstract class PrePostProcessingCommandInterceptor extends CommandInterceptor { @Override public final Object visitPutDataMapCommand(InvocationContext ctx, PutDataMapCommand command) throws Throwable { try { return doBeforeCall(ctx, command) ? handlePutDataMapCommand(ctx, command) : null; } 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 doBeforeCall(ctx, command) ? handlePutKeyValueCommand(ctx, command) : null; } finally { doAfterCall(ctx, command); } } @Override public final Object visitPutForExternalReadCommand(InvocationContext ctx, PutForExternalReadCommand command) throws Throwable { try { return doBeforeCall(ctx, command) ? handlePutForExternalReadCommand(ctx, command) : null; } 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 doBeforeCall(ctx, command) ? handleRemoveNodeCommand(ctx, command) : null; } 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 doBeforeCall(ctx, command) ? handleCreateNodeCommand(ctx, command) : null; } finally { doAfterCall(ctx, command); } } /** * @deprecated in 3.0. Will be removed when Optimistic and Pessimistic locking is removed. */ @Deprecated 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 doBeforeCall(ctx, command) ? handleClearDataCommand(ctx, command) : null; } 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 doBeforeCall(ctx, command) ? handleEvictFqnCommand(ctx, command) : null; } 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 doBeforeCall(ctx, command) ? handleInvalidateCommand(ctx, command) : null; } 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 doBeforeCall(ctx, command) ? handleRemoveKeyCommand(ctx, command) : null; } 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 doBeforeCall(ctx, command) ? handleGetDataMapCommand(ctx, command) : null; } 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 doBeforeCall(ctx, command) ? handleExistsNodeCommand(ctx, command) : null; } 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 doBeforeCall(ctx, command) ? handleGetKeyValueCommand(ctx, command) : null; } 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 doBeforeCall(ctx, command) ? handleGetNodeCommand(ctx, command) : null; } 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 doBeforeCall(ctx, command) ? handleGetKeysCommand(ctx, command) : null; } 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 doBeforeCall(ctx, command) ? handleGetChildrenNamesCommand(ctx, command) : null; } 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 doBeforeCall(ctx, command) ? handleMoveCommand(ctx, command) : null; } 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 doBeforeCall(ctx, command) ? handleGravitateDataCommand(ctx, command) : null; } 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 doBeforeCall(ctx, command) ? handlePrepareCommand(ctx, command) : null; } 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 doBeforeCall(ctx, command) ? handleRollbackCommand(ctx, command) : null; } 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 doBeforeCall(ctx, command) ? handleCommitCommand(ctx, command) : null; } 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 doBeforeCall(ctx, command) ? handleOptimisticPrepareCommand(ctx, command) : null; } 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); protected boolean doBeforeCall(InvocationContext ctx, VisitableCommand command) { return true; } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/base/JmxStatsCommandInterceptor.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/base/JmxStatsCommandInterceptor.0000644000175000017500000000505011111047320033247 0ustar moellermoeller/* * 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.interceptors.base; import org.jboss.cache.factories.annotations.Start; import org.jboss.cache.jmx.JmxStatisticsExposer; import org.jboss.cache.jmx.annotations.ManagedAttribute; import java.util.Collections; import java.util.Map; /** * Base class for all the interceptors exposing management statistics. * * @author Mircea.Markus@jboss.com * @since 3.0 */ public class JmxStatsCommandInterceptor extends CommandInterceptor implements JmxStatisticsExposer { private boolean statsEnabled = false; @Start public void checkStatisticsUsed() { setStatisticsEnabled(configuration.getExposeManagementStatistics()); } /** * Returns whether an interceptor's statistics are * being captured. * * @return true if statistics are captured */ @ManagedAttribute public boolean getStatisticsEnabled() { return statsEnabled; } /** * @param enabled whether gathering statistics for JMX are enabled. */ @ManagedAttribute 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() { } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/base/CommandInterceptor.java0000644000175000017500000001245011111047320032415 0ustar moellermoeller/* * 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.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; /** * 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 { private CommandInterceptor next; protected Log log; protected boolean trace; protected Configuration configuration; public CommandInterceptor() { log = LogFactory.getLog(getClass()); trace = log.isTraceEnabled(); } @Inject void injectConfiguration(Configuration configuration) { this.configuration = configuration; } /** * 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-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/OptimisticReplicationInterceptor.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/OptimisticReplicationInterceptor0000755000175000017500000003753311236274723033560 0ustar moellermoeller/* * 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.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.CommandsFactory; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.VersionedDataCommand; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.commands.WriteCommand; import org.jboss.cache.commands.legacy.write.CreateNodeCommand; 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.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.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.OptimisticTransactionContext; import org.jboss.cache.transaction.TransactionContext; 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 its 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 AT jboss DOT org) * @author Steve Woodcock (stevew@jofti.com) * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated 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); TransactionContext transactionContext = ctx.getTransactionContext(); if (transactionContext.hasLocalModifications()) { OptimisticPrepareCommand replicablePrepareCommand = command.copy(); // makre sure we remove any "local" transactions replicablePrepareCommand.removeModifications(transactionContext.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.getTransactionContext().setForceAsyncReplication(true); return handleDefault(ctx, command); } public Object handleDefault(InvocationContext ctx, VisitableCommand command) throws Throwable { if (isLocalModeForced(ctx) && command instanceof WriteCommand) ctx.getTransactionContext().addLocalModification((WriteCommand) 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.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(gtx); 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 { OptimisticTransactionContext transactionContext = (OptimisticTransactionContext) ctx.getTransactionContext(); if (transactionContext == null) { throw new CacheException("unable to map global transaction " + ctx + " to transaction entry"); } // try and get the workspace from the transaction return transactionContext.getTransactionWorkSpace(); } }jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/BaseRpcInterceptor.java0000644000175000017500000002035711111047320031451 0ustar moellermoeller/* * 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.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.CommandsFactory; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.config.Option; 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.TransactionContext; 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 AT jboss DOT 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 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) { TransactionContext transactionContext = ctx.getTransactionContext(); if (transactionContext != null) { if (transactionContext.isForceAsyncReplication()) sync = false; else if (transactionContext.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.getTransactionContext().hasModifications(); } /** * 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-3.2.8.GA/src/main/java/org/jboss/cache/interceptors/BatchingInterceptor.java0000644000175000017500000000556611111047320031656 0ustar moellermoeller/* * 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.interceptors; import org.jboss.cache.InvocationContext; import org.jboss.cache.batch.BatchContainer; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.interceptors.base.CommandInterceptor; import javax.transaction.Transaction; import javax.transaction.TransactionManager; /** * Interceptor that captures batched calls and attaches contexts. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ public class BatchingInterceptor extends CommandInterceptor { BatchContainer batchContainer; TransactionManager transactionManager; @Inject void inject(BatchContainer batchContainer, TransactionManager transactionManager) { this.batchContainer = batchContainer; this.transactionManager = transactionManager; } /** * Simply check if there is an ongoing tx. *
      *
    • If there is one, this is a no-op and just passes the call up the chain.
    • *
    • If there isn't one and there is a batch in progress, resume the batch's tx, pass up, and finally suspend the batch's tx.
    • *
    • If there is no batch in progress, just pass the call up the chain.
    • *
    */ @Override protected Object handleDefault(InvocationContext ctx, VisitableCommand command) throws Throwable { Transaction tx = null; try { // if in a batch, attach tx if (transactionManager.getTransaction() == null && (tx = batchContainer.getBatchTransaction()) != null) { transactionManager.resume(tx); } return super.handleDefault(ctx, command); } finally { if (tx != null && transactionManager.getTransaction() != null) transactionManager.suspend(); } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/RegionNotEmptyException.java0000644000175000017500000000325111111047320027766 0ustar moellermoeller/* * 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; /** * 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-3.2.8.GA/src/main/java/org/jboss/cache/transaction/0000755000175000017500000000000011675221744024670 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/transaction/OptimisticTransactionContext.java0000644000175000017500000000521311111047320033410 0ustar moellermoeller/* * 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.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 PessimisticTransactionContext} 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 AT jboss DOT org) * @author Steve Woodcock (stevew@jofti.com) * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated public class OptimisticTransactionContext extends PessimisticTransactionContext { private TransactionWorkspace transactionWorkSpace = new TransactionWorkspaceImpl(); public OptimisticTransactionContext(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; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/transaction/DummyContext.java0000644000175000017500000006136311240252332030165 0ustar moellermoeller/* * 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.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; import java.io.ObjectInputStream; import java.io.ByteArrayInputStream; import java.io.ObjectOutputStream; import java.io.ByteArrayOutputStream; /** * @author bela * Date: May 15, 2003 * Time: 6:21:37 PM */ public class DummyContext implements Context { HashMap bindings = new HashMap(); boolean serializing; public DummyContext() { this.serializing = false; } public DummyContext(boolean serializing) { this.serializing = serializing; } /** * 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 { try { deserialize(); return bindings.get(name); } finally { serialize(); } } /** * 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 javax.naming.NameAlreadyBoundException if name is already bound * @throws javax.naming.directory.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 javax.naming.directory.DirContext#bind(Name,Object, * javax.naming.directory.Attributes) */ public void bind(Name name, Object obj) throws NamingException { bind("NAME: " + name.toString(), obj); } /** * 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 javax.naming.NameAlreadyBoundException if name is already bound * @throws javax.naming.directory.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 { try { deserialize(); bindings.put(name, obj); } finally { serialize(); } } /** * 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 javax.naming.directory.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 javax.naming.directory.DirContext#rebind(Name,Object, * javax.naming.directory.Attributes) * @see javax.naming.directory.DirContext */ public void rebind(Name name, Object obj) throws NamingException { bind(name, obj); } /** * 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 javax.naming.directory.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 { bind(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 javax.naming.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 { unbind("NAME: " + name.toString()); } /** * Unbinds the named object. * See {@link #unbind(Name)} for details. * * @param name the name to unbind; may not be empty * @throws javax.naming.NameNotFoundException if an intermediate context does not exist * @throws NamingException if a naming exception is encountered */ public void unbind(String name) throws NamingException { try { deserialize(); bindings.remove(name); } finally { serialize(); } } /** * 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 javax.naming.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 javax.naming.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 javax.naming.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 javax.naming.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 javax.naming.NameNotFoundException if an intermediate context does not exist * @throws javax.naming.NotContextException if the name is bound but does not name a * context, or does not name a context of the appropriate type * @throws javax.naming.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 javax.naming.NameNotFoundException if an intermediate context does not exist * @throws javax.naming.NotContextException if the name is bound but does not name a * context, or does not name a context of the appropriate type * @throws javax.naming.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 javax.naming.NameAlreadyBoundException if name is already bound * @throws javax.naming.directory.InvalidAttributesException if creation of the subcontext requires specification of * mandatory attributes * @throws NamingException if a naming exception is encountered * @see #createSubcontext(String) * @see javax.naming.directory.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 javax.naming.NameAlreadyBoundException if name is already bound * @throws javax.naming.directory.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 javax.naming.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 javax.naming.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; } byte[] bytes = null; private void serialize() { if (serializing) { try { ByteArrayOutputStream bstream = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bstream); oos.writeObject(bindings); oos.close(); bstream.close(); bytes = bstream.toByteArray(); bindings = null; } catch (Exception e) { throw new RuntimeException(e); } } } @SuppressWarnings("unchecked") private void deserialize() { if (serializing) { if (bytes == null) bindings = new HashMap(); else { try { ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes)); bindings = (HashMap) ois.readObject(); ois.close(); bytes = null; } catch (Exception e) { throw new RuntimeException(e); } } } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/transaction/DummyUserTransaction.java0000644000175000017500000001336011111047320031653 0ustar moellermoeller/* * 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.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: 7168 $ * Date: May 15, 2003 * Time: 4:20:17 PM */ public class DummyUserTransaction implements UserTransaction, java.io.Serializable { 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(); } /** * 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(); } /** * 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(); } /** * 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-3.2.8.GA/src/main/java/org/jboss/cache/transaction/DummyTransaction.java0000644000175000017500000002702011136345417031031 0ustar moellermoeller/* * 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.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: 7590 $ * 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); protected 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) { } 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.info("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-3.2.8.GA/src/main/java/org/jboss/cache/transaction/MVCCTransactionContext.java0000644000175000017500000000720411225331374032031 0ustar moellermoeller/* * 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.transaction; import org.jboss.cache.Fqn; import org.jboss.cache.NodeSPI; import javax.transaction.RollbackException; import javax.transaction.SystemException; import javax.transaction.Transaction; import java.util.HashMap; import java.util.Map; /** * A transaction context specially geared to dealing with MVCC. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ public class MVCCTransactionContext extends AbstractTransactionContext { private final Map lookedUpNodes = new HashMap(8); public MVCCTransactionContext(Transaction tx) throws SystemException, RollbackException { super(tx); } /** * Retrieves a node from the registry of looked up nodes in the current scope. *

    * This is not normally called directly since {@link org.jboss.cache.InvocationContext#lookUpNode(org.jboss.cache.Fqn)} * would delegate to this method if a transaction is in scope. *

    * * @param fqn fqn to look up * @return a node, or null if it cannot be found. */ public NodeSPI lookUpNode(Fqn fqn) { return lookedUpNodes.get(fqn); } /** * Puts an entry in the registry of looked up nodes in the current scope. *

    * This is not normally called directly since {@link org.jboss.cache.InvocationContext#putLookedUpNode(org.jboss.cache.Fqn, org.jboss.cache.NodeSPI)} * would delegate to this method if a transaction is in scope. *

    * * @param f fqn to add * @param n node to add */ public void putLookedUpNode(Fqn f, NodeSPI n) { lookedUpNodes.put(f, n); } /** * Clears the registry of looked up nodes. *

    * This is not normally called directly since {@link org.jboss.cache.InvocationContext#clearLookedUpNodes()} * would delegate to this method if a transaction is in scope. *

    */ public void clearLookedUpNodes() { lookedUpNodes.clear(); } /** * Retrieves a map of nodes looked up within the current invocation's scope. *

    * This is not normally called directly since {@link org.jboss.cache.InvocationContext#getLookedUpNodes()} * would delegate to this method if a transaction is in scope. *

    * * @return a map of looked up nodes. */ public Map getLookedUpNodes() { return lookedUpNodes; } @Override public void reset() { super.reset(); lookedUpNodes.clear(); } public void putLookedUpNodes(Map lookedUpNodes) { this.lookedUpNodes.putAll(lookedUpNodes); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/transaction/GlobalTransaction.java0000644000175000017500000000777711155706264031161 0ustar moellermoeller/* * 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.transaction; import org.jgroups.Address; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.concurrent.atomic.AtomicLong; /** * 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 AT jboss DOT org) * @version $Revision: 7898 $ */ public class GlobalTransaction implements Externalizable { private static final long serialVersionUID = 8011434781266976149L; private static AtomicLong sid = new AtomicLong(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 = sid.getAndIncrement(); } 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-3.2.8.GA/src/main/java/org/jboss/cache/transaction/PessimisticTransactionContext.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/transaction/PessimisticTransactionContext.jav0000644000175000017500000000565011111047320033424 0ustar moellermoeller/* * 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.transaction; import org.jboss.cache.commands.WriteCommand; import javax.transaction.RollbackException; import javax.transaction.SystemException; import javax.transaction.Transaction; import java.util.Collections; import java.util.LinkedList; import java.util.List; /** * Information associated with a {@link GlobalTransaction} about the transaction state. *

    * A TransactionContext maintains: *

      *
    • Handle to the local Transaction
    • *
    • List of {@link org.jboss.cache.commands.WriteCommand}s that make up this transaction
    • *
    • List of locks acquired
    • *
    • Any transaction-scope options
    • *
    * * @author Bela Ban * @author Manik Surtani * @version $Revision: 7168 $ * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated public class PessimisticTransactionContext extends AbstractTransactionContext { private List allModifications; public PessimisticTransactionContext(Transaction tx) throws SystemException, RollbackException { super(tx); } @Override public void addLocalModification(WriteCommand command) { if (command == null) return; super.addLocalModification(command); if (allModifications == null) allModifications = new LinkedList(); allModifications.add(command); } @Override public void addModification(WriteCommand command) { if (command == null) return; super.addModification(command); if (allModifications == null) allModifications = new LinkedList(); allModifications.add(command); } public List getAllModifications() { if (allModifications == null) return Collections.emptyList(); return allModifications; } @Override public void reset() { super.reset(); allModifications = null; } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/transaction/GenericTransactionManagerLookup.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/transaction/GenericTransactionManagerLookup.j0000644000175000017500000001516011130440033033271 0ustar moellermoeller/* * 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.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 7375 2009-01-05 17:15:07Z 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 JBoss Cache's internal DummyTransactionManager"); } return tm; } /** * Try to figure out which TransactionManager to use */ 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-3.2.8.GA/src/main/java/org/jboss/cache/transaction/TransactionManagerLookup.java0000644000175000017500000000333711111047320032470 0ustar moellermoeller/* * 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.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 7168 2008-11-19 17:37:20Z jason.greene@jboss.com $ */ public interface TransactionManagerLookup { /** * Returns a new TransactionManager. * * @throws Exception if lookup failed */ TransactionManager getTransactionManager() throws Exception; } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/transaction/JBossTransactionManagerLookup.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/transaction/JBossTransactionManagerLookup.jav0000644000175000017500000000317711111047320033272 0ustar moellermoeller/* * 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.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 7168 2008-11-19 17:37:20Z jason.greene@jboss.com $ */ public class JBossTransactionManagerLookup implements TransactionManagerLookup { public TransactionManager getTransactionManager() throws Exception { return (TransactionManager) new InitialContext().lookup("java:/TransactionManager"); } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/transaction/JBossStandaloneJTAManagerLookup.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/transaction/JBossStandaloneJTAManagerLookup.j0000644000175000017500000000373511111047320033105 0ustar moellermoeller/* * 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.transaction; import javax.transaction.TransactionManager; import javax.transaction.UserTransaction; import java.lang.reflect.Method; /** * 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-3.2.8.GA/src/main/java/org/jboss/cache/transaction/AbstractTransactionContext.java0000644000175000017500000002057011111047320033032 0ustar moellermoeller/* * 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.transaction; import org.jboss.cache.Fqn; import org.jboss.cache.commands.WriteCommand; import org.jboss.cache.config.Option; import org.jboss.cache.interceptors.OrderedSynchronizationHandler; import org.jboss.cache.util.Immutables; import javax.transaction.RollbackException; import javax.transaction.SystemException; import javax.transaction.Transaction; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; /** * An abstract transaction context */ public abstract class AbstractTransactionContext implements TransactionContext { /** * 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 org.jboss.cache.commands.WriteCommand}). 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 * but also need guaranteed ordering for use by lock release code (see JBCCACHE-874). *

    * This needs to be unchecked since we support both MVCC (Fqns held here) or legacy Opt/Pess locking (NodeLocks held here). * once we drop support for opt/pess locks we can genericise this to contain Fqns. - Manik Surtani, June 2008 */ private LinkedHashSet transactionLocks; /** * 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 List removedNodes = null; public AbstractTransactionContext(Transaction tx) throws SystemException, RollbackException { ltx = tx; orderedSynchronizationHandler = new OrderedSynchronizationHandler(tx); } public void addModification(WriteCommand command) { if (command == null) return; if (modificationList == null) modificationList = new LinkedList(); modificationList.add(command); } public List getModifications() { if (modificationList == null) return Collections.emptyList(); return modificationList; } public void addLocalModification(WriteCommand command) { if (command == null) throw new NullPointerException("Command is null!"); if (localModifications == null) localModifications = new LinkedList(); localModifications.add(command); } public List getLocalModifications() { if (localModifications == null) return Collections.emptyList(); return localModifications; } public void addRemovedNode(Fqn fqn) { if (fqn == null) throw new NullPointerException("Fqn is null!"); if (removedNodes == null) removedNodes = new LinkedList(); removedNodes.add(fqn); } public List getRemovedNodes() { if (removedNodes == null) return Collections.emptyList(); return new ArrayList(removedNodes); } public void setTransaction(Transaction tx) { ltx = tx; } public Transaction getTransaction() { return ltx; } @SuppressWarnings("unchecked") public void addLock(Object lock) { // no need to worry about concurrency here - a context is only valid for a single thread. if (transactionLocks == null) transactionLocks = new LinkedHashSet(5); transactionLocks.add(lock); } @SuppressWarnings("unchecked") public void removeLock(Object lock) { // no need to worry about concurrency here - a context is only valid for a single thread. if (transactionLocks != null) transactionLocks.remove(lock); } public void clearLocks() { if (transactionLocks != null) transactionLocks.clear(); } public boolean hasLock(Object lock) { return transactionLocks != null && transactionLocks.contains(lock); } @SuppressWarnings("unchecked") public void addAllLocks(List newLocks) { // no need to worry about concurrency here - a context is only valid for a single thread. if (transactionLocks == null) transactionLocks = new LinkedHashSet(5); transactionLocks.addAll(newLocks); } @SuppressWarnings("unchecked") public List getLocks() { return transactionLocks == null || transactionLocks.isEmpty() ? Collections.emptyList() : Immutables.immutableListConvert(transactionLocks); } public boolean isForceAsyncReplication() { return forceAsyncReplication; } public void setForceAsyncReplication(boolean forceAsyncReplication) { this.forceAsyncReplication = forceAsyncReplication; if (forceAsyncReplication) { forceSyncReplication = false; } } public boolean isForceSyncReplication() { return forceSyncReplication; } public void setForceSyncReplication(boolean forceSyncReplication) { this.forceSyncReplication = forceSyncReplication; if (forceSyncReplication) { forceAsyncReplication = false; } } /** * Returns debug information about this transaction. */ @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("TransactionEntry\nmodificationList: ").append(modificationList); return sb.toString(); } public void addDummyNodeCreatedByCacheLoader(Fqn fqn) { if (dummyNodesCreatedByCacheLoader == null) dummyNodesCreatedByCacheLoader = new LinkedList(); dummyNodesCreatedByCacheLoader.add(fqn); } public List getDummyNodesCreatedByCacheLoader() { if (dummyNodesCreatedByCacheLoader == null) return Collections.emptyList(); return dummyNodesCreatedByCacheLoader; } public void setOption(Option o) { this.option = o; } public Option getOption() { return this.option; } public OrderedSynchronizationHandler getOrderedSynchronizationHandler() { return orderedSynchronizationHandler; } public void setOrderedSynchronizationHandler(OrderedSynchronizationHandler orderedSynchronizationHandler) { this.orderedSynchronizationHandler = orderedSynchronizationHandler; } public boolean hasModifications() { return modificationList != null && !modificationList.isEmpty(); } public boolean hasLocalModifications() { return localModifications != null && !localModifications.isEmpty(); } public boolean hasAnyModifications() { return hasModifications() || hasLocalModifications(); } public void reset() { orderedSynchronizationHandler = null; modificationList = null; localModifications = null; option = null; if (transactionLocks != null) transactionLocks.clear(); if (dummyNodesCreatedByCacheLoader != null) dummyNodesCreatedByCacheLoader.clear(); if (removedNodes != null) removedNodes.clear(); } } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/transaction/BatchModeTransactionManagerLookup.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/transaction/BatchModeTransactionManagerLookup0000644000175000017500000000313011240253605033316 0ustar moellermoeller/* * 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.transaction; import javax.transaction.TransactionManager; /** * Returns an instance of {@link BatchModeTransactionManager}. * * @author Bela Ban Sept 5 2003 * @version $Id: BatchModeTransactionManagerLookup.java 8181 2009-08-11 11:35:33Z manik.surtani@jboss.com $ * @deprecated Use batching API on Cache instead. */ @Deprecated public class BatchModeTransactionManagerLookup implements TransactionManagerLookup { public TransactionManager getTransactionManager() throws Exception { return BatchModeTransactionManager.getInstance(); } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/transaction/DummyTransactionManagerLookup.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/transaction/DummyTransactionManagerLookup.jav0000644000175000017500000000336611111047320033345 0ustar moellermoeller/* * 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.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 7168 2008-11-19 17:37:20Z 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-3.2.8.GA/src/main/java/org/jboss/cache/transaction/BatchModeTransactionManager.java0000755000175000017500000000407011111047320033043 0ustar moellermoeller/* * 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.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: 7168 $ * 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-3.2.8.GA/src/main/java/org/jboss/cache/transaction/DummyBaseTransactionManager.java0000755000175000017500000002421111136323566033122 0ustar moellermoeller/* * 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.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: 7579 $ * 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; DummyTransaction tx = getTransaction(); if (tx == null) { throw new IllegalStateException("thread not associated with transaction"); } status = tx.getStatus(); if (status == Status.STATUS_MARKED_ROLLBACK) { tx.setStatus(Status.STATUS_ROLLEDBACK); rollback(); throw new RollbackException("Transaction status is Status.STATUS_MARKED_ROLLBACK"); } else { 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 DummyTransaction 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((DummyTransaction) tx); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/transaction/TransactionLog.java0000644000175000017500000001241511155055621030455 0ustar moellermoeller/* * 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 org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.commands.WriteCommand; import org.jboss.cache.commands.tx.PrepareCommand; import org.jboss.cache.marshall.Marshaller; import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicBoolean; /** * Logs transactions and writes for Non-Blocking State Transfer * * @author Jason T. Greene */ public class TransactionLog { private final Map pendingPrepares = new ConcurrentHashMap(); private final BlockingQueue entries = new LinkedBlockingQueue(); private AtomicBoolean active = new AtomicBoolean(); public static class LogEntry { private final GlobalTransaction transaction; private final List modifications; public LogEntry(GlobalTransaction transaction, List modifications) { this.transaction = transaction; this.modifications = modifications; } public GlobalTransaction getTransaction() { return transaction; } public List getModifications() { return modifications; } } private static Log log = LogFactory.getLog(TransactionLog.class); public void logPrepare(PrepareCommand command) { pendingPrepares.put(command.getGlobalTransaction(), command); } public void logCommit(GlobalTransaction gtx) { PrepareCommand command = pendingPrepares.remove(gtx); // it is perfectly normal for a prepare not to be logged for this gtx, for example if a transaction did not // modify anything, then beforeCompletion() is not invoked and logPrepare() will not be called to register the // prepare. if (command != null && isActive()) addEntry(gtx, command.getModifications()); } private void addEntry(GlobalTransaction gtx, WriteCommand command) { addEntry(gtx, Collections.singletonList(command)); } private void addEntry(GlobalTransaction gtx, List commands) { LogEntry entry = new LogEntry(gtx, commands); for (;;) { try { if (log.isTraceEnabled()) log.trace("Added commit entry to tx log" + entry); entries.put(entry); break; } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } public void logOnePhaseCommit(GlobalTransaction gtx, List modifications) { // Just in case... if (gtx != null) pendingPrepares.remove(gtx); if (isActive() && !modifications.isEmpty()) addEntry(gtx, modifications); } public void logNoTxWrite(WriteCommand write) { if (isActive()) addEntry(null, write); } public void rollback(GlobalTransaction gtx) { pendingPrepares.remove(gtx); } public final boolean isActive() { return active.get(); } public boolean activate() { return active.compareAndSet(false, true); } public void deactivate() { active.set(false); if (entries.size() > 0) log.error("Unprocessed Transaction Log Entries! = " + entries.size()); entries.clear(); } public int size() { return entries.size(); } public void writeCommitLog(Marshaller marshaller, ObjectOutputStream out) throws Exception { List buffer = new ArrayList(10); while (entries.drainTo(buffer, 10) > 0) { for (LogEntry entry : buffer) marshaller.objectToObjectStream(entry, out); buffer.clear(); } } public void writePendingPrepares(Marshaller marshaller, ObjectOutputStream out) throws Exception { for (PrepareCommand entry : pendingPrepares.values()) marshaller.objectToObjectStream(entry, out); } public final boolean hasPendingPrepare(PrepareCommand command) { return pendingPrepares.containsKey(command.getGlobalTransaction()); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/transaction/DummyContextFactory.java0000644000175000017500000000442111111047320031501 0ustar moellermoeller/* * 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.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; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/transaction/TransactionContext.java0000644000175000017500000002711511111047320031350 0ustar moellermoeller/* * 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.transaction; import org.jboss.cache.Fqn; import org.jboss.cache.commands.WriteCommand; import org.jboss.cache.config.Option; import org.jboss.cache.interceptors.OrderedSynchronizationHandler; import javax.transaction.Transaction; import java.util.List; /** * Captures information pertaining to a specific JTA transaction. *

    * This was a concrete class called TransactionEntry prior to 3.0. *

    * * @author Manik Surtani (manik AT jboss DOT org) * @see org.jboss.cache.InvocationContext */ public interface TransactionContext { /** * Adds a modification to the modification list. * * @param command modification */ void addModification(WriteCommand command); /** * Returns all modifications. If there are no modifications in this transaction this method will return an empty list. * * @return list of modifications. */ List getModifications(); /** * Adds a modification to the local modification list. * * @param command command to add to list. Should not be null. * @throws NullPointerException if the command to be added is null. */ void addLocalModification(WriteCommand command); /** * Returns all modifications that have been invoked with the LOCAL cache mode option. These will also be in the standard modification list. * * @return list of LOCAL modifications, or an empty list. */ List getLocalModifications(); /** * Adds the node that has been removed in the scope of the current transaction. * * @param fqn fqn that has been removed. * @throws NullPointerException if the Fqn is null. */ void addRemovedNode(Fqn fqn); /** * Gets the list of removed nodes. * * @return list of nodes removed in the current transaction scope. Note that this method will return an empty list if nothing has been removed. The list returned is defensively copied. */ List getRemovedNodes(); /** * Sets the local transaction to be associated with this transaction context. * * @param tx JTA transaction to associate with. */ void setTransaction(Transaction tx); /** * Returns a local transaction associated with this context. * * @return a JTA transaction */ Transaction getTransaction(); /** * Adds a lock to the currently maintained collection of locks acquired. *

    * Most code could not use this method directly, but use {@link org.jboss.cache.InvocationContext#addLock(Object)} instead, * which would delegate to this method if a transaction is in scope or otherwise use invocation-specific locks. *

    * Note that currently (as of 3.0.0) this lock is weakly typed. This is to allow support for both MVCC (which uses {@link org.jboss.cache.Fqn}s as locks) * as well as legacy Optimistic and Pessimistic Locking schemes (which use {@link org.jboss.cache.lock.NodeLock} as locks). Once support for * legacy node locking schemes are dropped, this method will be more strongly typed to accept {@link org.jboss.cache.Fqn}. * * @param lock lock to add * @see org.jboss.cache.InvocationContext#addLock(Object) */ @SuppressWarnings("unchecked") void addLock(Object lock); /** * Removes a lock from the currently maintained collection of locks acquired. *

    * Most code could not use this method directly, but use {@link org.jboss.cache.InvocationContext#removeLock(Object)} instead, * which would delegate to this method if a transaction is in scope or otherwise use invocation-specific locks. *

    * Note that currently (as of 3.0.0) this lock is weakly typed. This is to allow support for both MVCC (which uses {@link org.jboss.cache.Fqn}s as locks) * as well as legacy Optimistic and Pessimistic Locking schemes (which use {@link org.jboss.cache.lock.NodeLock} as locks). Once support for * legacy node locking schemes are dropped, this method will be more strongly typed to accept {@link org.jboss.cache.Fqn}. * * @param lock lock to remove * @see org.jboss.cache.InvocationContext#removeLock(Object) */ @SuppressWarnings("unchecked") void removeLock(Object lock); /** * Clears all locks from the currently maintained collection of locks acquired. *

    * Most code could not use this method directly, but use {@link org.jboss.cache.InvocationContext#clearLocks()} instead, * which would delegate to this method if a transaction is in scope or otherwise use invocation-specific locks. *

    * Note that currently (as of 3.0.0) this lock is weakly typed. This is to allow support for both MVCC (which uses {@link org.jboss.cache.Fqn}s as locks) * as well as legacy Optimistic and Pessimistic Locking schemes (which use {@link org.jboss.cache.lock.NodeLock} as locks). Once support for * legacy node locking schemes are dropped, this method will be more strongly typed to accept {@link org.jboss.cache.Fqn}. * * @see org.jboss.cache.InvocationContext#clearLocks() */ void clearLocks(); /** * Adds a List of locks to the currently maintained collection of locks acquired. *

    * Most code could not use this method directly, but use {@link org.jboss.cache.InvocationContext#addAllLocks(java.util.List)} instead, * which would delegate to this method if a transaction is in scope or otherwise use invocation-specific locks. *

    * Note that currently (as of 3.0.0) this list is unchecked. This is to allow support for both MVCC (which uses Fqns as locks) * as well as legacy Optimistic and Pessimistic Locking schemes (which use {@link org.jboss.cache.lock.NodeLock} as locks). Once support for * legacy node locking schemes are dropped, this method will be more strongly typed to accept List. * * @param newLocks locks to add * @see org.jboss.cache.InvocationContext#addAllLocks(java.util.List) */ @SuppressWarnings("unchecked") void addAllLocks(List newLocks); /** * Returns an immutable, defensive copy of the List of locks currently maintained for the transaction. *

    * Most code could not use this method directly, but use {@link org.jboss.cache.InvocationContext#getLocks()} instead, * which would delegate to this method if a transaction is in scope or otherwise use invocation-specific locks. *

    * Note that currently (as of 3.0.0) this list is unchecked. This is to allow support for both MVCC (which uses Fqns as locks) * as well as legacy Optimistic and Pessimistic Locking schemes (which use {@link org.jboss.cache.lock.NodeLock} as locks). Once support for * legacy node locking schemes are dropped, this method will be more strongly typed to return List. * * @return locks held in current scope. * @see org.jboss.cache.InvocationContext#getLocks() */ @SuppressWarnings("unchecked") List getLocks(); /** * Most code could not use this method directly, but use {@link org.jboss.cache.InvocationContext#hasLock(Object)} ()} instead, * which would delegate to this method if a transaction is in scope or otherwise use invocation-specific locks. * * @param lock lock to test * @return true if the lock being tested is already held in the current scope, false otherwise. */ boolean hasLock(Object lock); /** * 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. */ boolean isForceAsyncReplication(); /** * 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 */ void setForceAsyncReplication(boolean forceAsyncReplication); /** * 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. */ boolean isForceSyncReplication(); /** * 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 */ void setForceSyncReplication(boolean forceSyncReplication); /** * Adds an Fqn to the list of uninitialized nodes created by the cache loader. * * @param fqn fqn to add. Must not be null. */ void addDummyNodeCreatedByCacheLoader(Fqn fqn); /** * @return a list of uninitialized nodes created by the cache loader, or an empty list. */ List getDummyNodesCreatedByCacheLoader(); /** * Sets a transaction-scope option override * * @param o option to set */ void setOption(Option o); /** * Retrieves a transaction scope option override * * @return option */ Option getOption(); /** * @return the ordered sync handler associated with this transaction */ OrderedSynchronizationHandler getOrderedSynchronizationHandler(); /** * Associates an ordered sync handler with this transaction. * * @param orderedSynchronizationHandler to set */ void setOrderedSynchronizationHandler(OrderedSynchronizationHandler orderedSynchronizationHandler); /** * @return true if modifications were registered. */ boolean hasModifications(); /** * @return true if any modifications have been invoked with cache mode being LOCAL. */ boolean hasLocalModifications(); /** * @return true if either there are modifications or local modifications that are not for replicating. */ boolean hasAnyModifications(); /** * Cleans up internal state, freeing up references. */ void reset(); } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/transaction/DummyTransactionManager.java0000644000175000017500000000651111111047320032307 0ustar moellermoeller/* * 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.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: 7168 $ * Date: May 15, 2003 * Time: 4:11:37 PM */ public class DummyTransactionManager extends DummyBaseTransactionManager { protected static DummyTransactionManager instance = null; protected static DummyUserTransaction utx = null; protected static Log log = LogFactory.getLog(DummyTransactionManager.class); private static final long serialVersionUID = 4396695354693176535L; public static synchronized 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 synchronized void destroy() { if (instance == null) return; try { System.out.println("Destroy called on DummyTransactionManager. !!!!!!!!!!!!!!!!!!!!!!"); Thread.dumpStack(); 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-3.2.8.GA/src/main/java/org/jboss/cache/transaction/TransactionTable.java0000644000175000017500000003300611137711615030764 0ustar moellermoeller/* * 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.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.factories.annotations.Inject; import org.jboss.cache.factories.annotations.NonVolatile; import org.jboss.cache.factories.context.ContextFactory; import org.jboss.cache.jmx.annotations.ManagedAttribute; 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 TransactionContext} instances under a given transaction. * * @author Bela Ban Apr 14, 2003 * @version $Revision: 7604 $ */ @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 gtx2ContextMap = new ConcurrentHashMap(); protected final Map gtx2TxMap = new ConcurrentHashMap(); private TransactionManager transactionManager = null; private RPCManager rpcManager; private ContextFactory contextFactory; @Inject public void initialize(TransactionManager transactionManager, RPCManager rpcManager, ContextFactory contextFactory) { this.transactionManager = transactionManager; this.rpcManager = rpcManager; this.contextFactory = contextFactory; } /** * Returns the number of local transactions. */ public int getNumLocalTransactions() { return tx2gtxMap.size(); } /** * Returns the number of global transactions. */ public int getNumGlobalTransactions() { return gtx2ContextMap.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 TransactionContext get(GlobalTransaction gtx) { return gtx != null ? gtx2ContextMap.get(gtx) : null; } /** * Associates the global transaction with a transaction context. */ public void put(GlobalTransaction tx, TransactionContext transactionContext) { if (tx == null) { log.error("key (GlobalTransaction) is null"); return; } gtx2ContextMap.put(tx, transactionContext); } /** * Removes a global transation, returns the old transaction entry. */ public TransactionContext remove(GlobalTransaction tx) { if (tx == null) return null; gtx2TxMap.remove(tx); return gtx2ContextMap.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); gtx2ContextMap.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(gtx2ContextMap.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(gtx2ContextMap.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 transactionContextEntry : gtx2ContextMap.entrySet()) { sb.append(transactionContextEntry.getKey()).append(": ").append(transactionContextEntry.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) { } // 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; } } /** * Returns true if transaction is STATUS_MARKED_ROLLBACK, false otherwise */ public static boolean isMarkedAsRollback(Transaction tx) { if (tx == null) return false; int status; try { status = tx.getStatus(); return status == Status.STATUS_MARKED_ROLLBACK; } catch (SystemException e) { return false; } } /** * Returns true of tx's status is ACTIVE or PREPARING or MARKED_ROLLBACK * * @param tx * @return true if the tx is active or preparing */ public static boolean isValid(Transaction tx) { return isActive(tx) || isPreparing(tx) || isMarkedAsRollback(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); TransactionContext transactionContext; try { transactionContext = contextFactory.createTransactionContext(tx); } catch (Exception e) { throw new CacheException("Unable to create a transaction entry!", e); } put(gtx, transactionContext); if (trace) { log.trace("created new GTX: " + gtx + ", local TX=" + tx); } } return gtx; } @ManagedAttribute(name = "numberOfRegisteredTransactions", description = "Number of registered transactions") public int getNumberOfRegisteredTransactions() { return tx2gtxMap.size(); } @ManagedAttribute(name = "transactionMap", description = "A String representation of the transaction map") public String getTransactionMap() { return tx2gtxMap.toString(); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/statetransfer/0000755000175000017500000000000011675221747025233 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/statetransfer/StateTransferManager.java0000644000175000017500000000675611111047320032146 0ustar moellermoeller/* * 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.statetransfer; import org.jboss.cache.Fqn; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; /** * This interface handles requests to generate or integrate state from neighbouring caches in a cluster. *

    * This has existed prior to 3.0.0 as a concrete class. An interface was introduced in 3.0.0 to provide more flexibility * in state transfer implementations. *

    * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ public interface StateTransferManager { /** * 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 millis this method should wait to acquire * any locks, if necessary, on the nodes being transferred * @param force if locks are needed and cannot be acquired after * timeout millis, should the lock acquisition * be forced, and any existing transactions holding locks * on the nodes be rolled back? * @param suppressErrors if true, all exceptions are logged but not propagated. * @throws Exception in event of error */ void getState(ObjectOutputStream out, Fqn fqn, long timeout, boolean force, boolean suppressErrors) throws Exception; /** * 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 */ void setState(ObjectInputStream in, Fqn targetRoot) throws Exception; } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/statetransfer/DefaultStateTransferGenerator.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/statetransfer/DefaultStateTransferGenerator.j0000644000175000017500000002131411151761040033330 0ustar moellermoeller/* * 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.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.InternalNode; import org.jboss.cache.Node; import org.jboss.cache.RPCManager; import org.jboss.cache.RPCManagerImpl.FlushTracker; import org.jboss.cache.Version; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.Start; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.marshall.NodeData; import org.jboss.cache.marshall.NodeDataExceptionMarker; import org.jboss.cache.transaction.TransactionLog; 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 static final Log log = LogFactory.getLog(DefaultStateTransferGenerator.class); private static final boolean trace = log.isTraceEnabled(); private CacheSPI cache; private RPCManager rpcManager; private Set internalFqns; private boolean nonBlocking; private long flushTimeout; private int maxNonProgressingLogWrites = 5; private TransactionLog txLog; @Inject public void inject(CacheSPI cache, RPCManager rpcManager, Configuration configuration, TransactionLog txLog) { this.cache = cache; this.nonBlocking = true; this.flushTimeout = configuration.getStateRetrievalTimeout(); this.nonBlocking = configuration.isNonBlockingStateTransfer(); this.txLog = txLog; this.rpcManager = rpcManager; } @Start(priority = 14) void start() { this.internalFqns = cache.getInternalFqns(); } public void generateState(ObjectOutputStream out, Object rootNode, boolean generateTransient, boolean generatePersistent, boolean suppressErrors) throws Exception { Fqn fqn = getFqn(rootNode); boolean activated = false; CacheLoader cacheLoader = cache.getCacheLoaderManager() == null ? null : cache.getCacheLoaderManager().getCacheLoader(); boolean needToGeneratePersistentState = generatePersistent && cacheLoader != null; try { cache.getMarshaller().objectToObjectStream(STATE_TRANSFER_VERSION, out); // activate the tx log only if we need to generate either transient or persistent state if (nonBlocking && (generateTransient || needToGeneratePersistentState)) { activated = txLog.activate(); if (! activated) throw new StateProviderBusyException("Busy performing state transfer for someone else"); if (trace) log.trace("Transaction log activated!"); } if (generateTransient) { //transient + marker if (trace) log.trace("writing transient state for " + fqn); marshallTransientState((InternalNode) rootNode, out); if (trace) log.trace("transient state succesfully written"); //associated + marker if (trace) log.trace("writing associated state"); delimitStream(out); if (trace) log.trace("associated state succesfully written"); } else { //we have to write two markers for transient and associated delimitStream(out); delimitStream(out); } if (needToGeneratePersistentState) writePersistentData(out, fqn, cacheLoader); delimitStream(out); if (nonBlocking && generateTransient) { writeTxLog(out); } } catch (Exception e) { cache.getMarshaller().objectToObjectStream(new NodeDataExceptionMarker(e, cache.getLocalAddress()), out); throw e; } finally { if (activated) txLog.deactivate(); } } private void writePersistentData(ObjectOutputStream out, Fqn fqn, CacheLoader cacheLoader) throws Exception { if (trace) log.trace("writing persistent state for " + fqn + ", using " + cache.getCacheLoaderManager().getCacheLoader().getClass()); if (fqn.isRoot()) { cacheLoader.loadEntireState(out); } else { cacheLoader.loadState(fqn, out); } if (trace) log.trace("persistent state succesfully written"); } private void writeTxLog(ObjectOutputStream out) throws Exception { FlushTracker flushTracker = rpcManager.getFlushTracker(); try { if (trace) log.trace("Transaction log size is " + txLog.size()); for (int nonProgress = 0, size = txLog.size(); size > 0;) { if (trace) log.trace("Tx Log remaining entries = " + size); txLog.writeCommitLog(cache.getMarshaller(), out); int newSize = txLog.size(); // If size did not decrease then we did not make progress, and could be wasting // our time. Limit this to the specified max. if (newSize >= size && ++nonProgress >= maxNonProgressingLogWrites) break; size = newSize; } // Wait on incoming and outgoing threads to line-up in front of // the flush gate. flushTracker.lockSuspendProcessingLock(); // Signal to sender that we need a flush to get a consistent view // of the remaining transactions. delimitStream(out); out.flush(); if (trace) log.trace("Waiting for a FLUSH"); flushTracker.waitForFlushStart(flushTimeout); if (trace) log.trace("FLUSH received, proceeding with writing commit log"); // Write remaining transactions txLog.writeCommitLog(cache.getMarshaller(), out); delimitStream(out); // Write all non-completed prepares txLog.writePendingPrepares(cache.getMarshaller(), out); delimitStream(out); out.flush(); } finally { flushTracker.unlockSuspendProcessingLock(); } } private Fqn getFqn(Object o) { if (o instanceof Node) return ((Node) o).getFqn(); if (o instanceof InternalNode) return ((InternalNode) o).getFqn(); throw new IllegalArgumentException(); } /** * Places a delimiter marker on the stream * * @param out stream * @throws java.io.IOException if there are errs */ protected void delimitStream(ObjectOutputStream out) throws Exception { cache.getMarshaller().objectToObjectStream(DefaultStateTransferManager.STREAMING_DELIMITER_NODE, out); } /** * Do a preorder traversal: visit the node first, then the node's children * * @param out * @throws Exception */ protected void marshallTransientState(InternalNode node, ObjectOutputStream out) throws Exception { List nodeData = new LinkedList(); generateNodeDataList(node, nodeData); cache.getMarshaller().objectToObjectStream(nodeData, out, node.getFqn()); } protected void generateNodeDataList(InternalNode 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, true); } list.add(nd); // then visit the children for (InternalNode child : node.getChildren()) generateNodeDataList(child, list); } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/statetransfer/LegacyStateTransferGenerator.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/statetransfer/LegacyStateTransferGenerator.ja0000644000175000017500000001424211254437247033330 0ustar moellermoeller/* * 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.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.InternalNode; import org.jboss.cache.Node; import org.jboss.cache.NodeSPI; import org.jboss.cache.Version; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.Start; 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; @Deprecated public class LegacyStateTransferGenerator 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; @Inject public void inject(CacheSPI cache) { this.cache = cache; } @Start(priority = 14) void start() { this.internalFqns = cache.getInternalFqns(); log.trace("LegacyStateTransferGenerator.start: internalFqn is " + internalFqns); } public void generateState(ObjectOutputStream out, Object rootNode, boolean generateTransient, boolean generatePersistent, boolean suppressErrors) throws Exception { Fqn fqn = getFqn(rootNode); 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"); } 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 (Exception e) { cache.getMarshaller().objectToObjectStream(new NodeDataExceptionMarker(e, cache.getLocalAddress()), out); throw e; } } private Fqn getFqn(Object o) { if (o instanceof Node) return ((Node) o).getFqn(); if (o instanceof InternalNode) return ((InternalNode) o).getFqn(); throw new IllegalArgumentException(); } /** * Places a delimiter marker on the stream * * @param out stream * @throws java.io.IOException if there are errs */ protected void delimitStream(ObjectOutputStream out) throws Exception { cache.getMarshaller().objectToObjectStream(DefaultStateTransferManager.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 (log.isTraceEnabled()) { log.trace("generateNodeDataList: internalFqns are:" + internalFqns + ", node is: " + node); } 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, true); } list.add(nd); // then visit the children for (NodeSPI child : node.getChildrenDirect()) generateNodeDataList(child, list); } }jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/statetransfer/StateTransferIntegrator.java0000644000175000017500000000260711111047320032701 0ustar moellermoeller/* * 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.statetransfer; import org.jboss.cache.Fqn; import java.io.ObjectInputStream; /** * @since 1.2.4 */ public interface StateTransferIntegrator { // TODO: target is an Object to support both InternalNodes and NodeSPIs. void integrateState(ObjectInputStream ois, Object target, Fqn targetFqn, boolean integratePersistentState) throws Exception; }././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/statetransfer/DefaultStateTransferManager.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/statetransfer/DefaultStateTransferManager.jav0000644000175000017500000002204711256437261033322 0ustar moellermoeller/* * 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.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.InternalNode; 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.Start; import org.jboss.cache.loader.CacheLoaderManager; 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 org.jboss.cache.util.CachePrinter; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; /** * The default state transfer manager to be used when using MVCC locking. */ public class DefaultStateTransferManager implements StateTransferManager { protected final static Log log = LogFactory.getLog(DefaultStateTransferManager.class); protected static final boolean trace = log.isTraceEnabled(); public static final NodeData STREAMING_DELIMITER_NODE = new NodeDataMarker(); public static final String PARTIAL_STATE_DELIMITER = "_PARTIAL_STATE_DELIMITER"; protected CacheSPI cache; protected Marshaller marshaller; protected RegionManager regionManager; protected Configuration configuration; private CacheLoaderManager cacheLoaderManager; boolean fetchTransientState; boolean fetchPersistentState; protected long stateRetrievalTimeout; protected StateTransferIntegrator integrator; protected StateTransferGenerator generator; @Inject public void injectDependencies(CacheSPI cache, Marshaller marshaller, RegionManager regionManager, Configuration configuration, CacheLoaderManager cacheLoaderManager, StateTransferIntegrator integrator, StateTransferGenerator generator) { this.cache = cache; this.regionManager = regionManager; this.marshaller = marshaller; this.configuration = configuration; this.cacheLoaderManager = cacheLoaderManager; this.integrator = integrator; this.generator = generator; } @Start(priority = 14) public void start() { fetchTransientState = configuration.isFetchInMemoryState(); // do not do state transfers if the cache loader config is shared fetchPersistentState = cacheLoaderManager != null && cacheLoaderManager.isFetchPersistentState() && !configuration.getCacheLoaderConfig().isShared(); stateRetrievalTimeout = configuration.getStateRetrievalTimeout(); } public void getState(ObjectOutputStream out, Fqn fqn, long timeout, boolean force, boolean suppressErrors) throws Exception { // can't give state for regions currently being activated/inactivated boolean canProvideState = cache.getCacheStatus().allowInvocations() && !regionManager.isInactive(fqn) && cache.peek(fqn, false) != null; if (trace) log.trace("Can provide state? " + canProvideState); if (canProvideState && (fetchPersistentState || fetchTransientState)) { marshaller.objectToObjectStream(true, out); long startTime = System.currentTimeMillis(); InternalNode subtreeRoot = fqn.isRoot() ? cache.getRoot().getDelegationTarget() : cache.getNode(fqn).getDelegationTarget(); // we don't need READ locks for MVCC based state transfer! if (log.isDebugEnabled()) log.debug("Generating in-memory (transient) state for subtree " + fqn); // this method will throw a StateProviderBusyException if the state provider is busy providing state to someone else. generator.generateState(out, subtreeRoot, fetchTransientState, fetchPersistentState, suppressErrors); if (log.isDebugEnabled()) log.debug("Successfully generated state in " + CachePrinter.prettyPrint(System.currentTimeMillis() - startTime)); } else { marshaller.objectToObjectStream(false, out); Exception e = null; if (!canProvideState) { String exceptionMessage = "Cache instance at " + cache.getLocalAddress() + " cannot provide state for fqn " + fqn + "."; if (!cache.getCacheStatus().allowInvocations()) exceptionMessage = " [Cache is not in the correct status]"; 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; } } public void setState(ObjectInputStream in, Fqn targetRoot) throws Exception { if (trace) log.trace("Setting state on Fqn root " + targetRoot); cache.getInvocationContext().getOptionOverrides().setSkipCacheStatusCheck(true); NodeSPI target = cache.getNode(targetRoot); if (target == null) { if(trace) log.trace("Target node not found, creating it"); // 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); cache.getInvocationContext().getOptionOverrides().setSkipCacheStatusCheck(true); target = cache.getNode(targetRoot); } Object o = marshaller.objectFromObjectStream(in); Boolean hasState = (Boolean) o; if (trace) log.trace("Do we have state to integrate? " + hasState); 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 */ protected void setState(ObjectInputStream state, NodeSPI targetRoot) throws Exception { long startTime = System.currentTimeMillis(); /* * 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. * */ if (log.isDebugEnabled()) log.debug("starting state integration at node " + targetRoot + ". Fetch Persistent State = " + fetchPersistentState); integrator.integrateState(state, targetRoot.getDelegationTarget(), targetRoot.getFqn(), fetchPersistentState); if (log.isDebugEnabled()) log.debug("successfully integrated state in " + CachePrinter.prettyPrint(System.currentTimeMillis() - startTime)); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/statetransfer/LegacyStateTransferManager.java0000644000175000017500000001665311254437247033313 0ustar moellermoeller/* * 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.statetransfer; 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.NodeSPI; import org.jboss.cache.RegionEmptyException; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.Start; 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.util.CachePrinter; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; /** * This is to support legacy locking schemes such as Pessimistic and Optimistic locking. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ @Deprecated public class LegacyStateTransferManager extends DefaultStateTransferManager { private static Log log = LogFactory.getLog(LegacyStateTransferManager.class); protected LockManager lockManager; private boolean usePut; // for JBCACHE-131 @Inject public void injectLockManager(LockManager lockManager) { this.lockManager = lockManager; } @Start(priority = 14) public void checkLoaders() { usePut = configuration.getCacheLoaderConfig() != null && !configuration.getCacheLoaderConfig().isFetchPersistentState() && !configuration.getCacheLoaderConfig().isShared(); } @Override public void getState(ObjectOutputStream out, Fqn fqn, long timeout, boolean force, boolean suppressErrors) throws Exception { // can't give state for regions currently being activated/inactivated boolean canProvideState = (!regionManager.isInactive(fqn) && cache.peek(fqn, false) != null); if (canProvideState && (fetchPersistentState || fetchTransientState)) { marshaller.objectToObjectStream(true, out); 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, timeout, force); generator.generateState(out, rootNode, fetchTransientState, fetchPersistentState, suppressErrors); if (log.isDebugEnabled()) { log.debug("Successfully generated state in " + CachePrinter.prettyPrint(System.currentTimeMillis() - startTime)); } } finally { releaseStateTransferLocks(rootNode); } } 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. * * @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 */ protected void setState(ObjectInputStream state, NodeSPI targetRoot) throws Exception { long startTime = System.currentTimeMillis(); acquireLocksForStateTransfer(targetRoot, stateRetrievalTimeout, 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. * */ try { if (log.isDebugEnabled()) { log.debug("starting state integration at node " + targetRoot); } integrator.integrateState(state, targetRoot, targetRoot.getFqn(), fetchPersistentState); if (log.isDebugEnabled()) { log.debug("successfully integrated state in " + CachePrinter.prettyPrint(System.currentTimeMillis() - startTime)); } } finally { releaseStateTransferLocks(targetRoot); } } protected void acquireLocksForStateTransfer(NodeSPI root, long timeout, boolean force) throws InterruptedException { if (usePut) return; try { lockManager.lockAll(root, READ, getLockOwner(), timeout, true); } catch (TimeoutException te) { log.error("Caught TimeoutException acquiring locks on region " + root.getFqn(), te); if (force) { throw te; } else { throw te; } } } protected void releaseStateTransferLocks(NodeSPI root) { if (usePut) return; try { lockManager.unlockAll(root, getLockOwner()); } catch (Throwable t) { log.error("failed releasing locks", t); } } private Object getLockOwner() { Object owner = cache.getCurrentTransaction(); if (owner == null) owner = Thread.currentThread(); return owner; } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/statetransfer/DefaultStateTransferIntegrator.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/statetransfer/DefaultStateTransferIntegrator.0000644000175000017500000004630011256437261033363 0ustar moellermoeller/* * 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.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.InternalNode; import org.jboss.cache.InvocationContext; import org.jboss.cache.Node; import org.jboss.cache.NodeSPI; import org.jboss.cache.RPCManager; import org.jboss.cache.buddyreplication.BuddyManager; import org.jboss.cache.commands.CommandsFactory; import org.jboss.cache.commands.WriteCommand; import org.jboss.cache.commands.remote.StateTransferControlCommand; import org.jboss.cache.commands.tx.PrepareCommand; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.ComponentRegistry; 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.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 org.jboss.cache.transaction.TransactionLog; import org.jboss.cache.transaction.TransactionLog.LogEntry; import org.jgroups.Address; 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; import java.util.Vector; public class DefaultStateTransferIntegrator implements StateTransferIntegrator { private static final Log log = LogFactory.getLog(DefaultStateTransferIntegrator.class); private static final boolean trace = log.isTraceEnabled(); private CacheSPI cache; private Set internalFqns; private Configuration cfg; private RPCManager rpcManager; private TransactionLog txLog; private boolean needToPersistState; // for JBCACHE-131 private boolean nonBlocking; private InvocationContextContainer container; private InterceptorChain chain; private ComponentRegistry registry; private CommandsFactory commandsFactory; @Inject public void inject(CacheSPI cache, Configuration cfg, RPCManager rpcManager, TransactionLog txLog, InvocationContextContainer container, InterceptorChain chain, ComponentRegistry registry, CommandsFactory commandsFactory) { this.cache = cache; this.cfg = cfg; this.rpcManager = rpcManager; this.nonBlocking = cfg.isNonBlockingStateTransfer(); this.txLog = txLog; this.container = container; this.chain = chain; this.registry = registry; this.commandsFactory = commandsFactory; } @Start(priority = 14) public void start() { this.internalFqns = cache.getInternalFqns(); needToPersistState = cfg.getCacheLoaderConfig() != null && !cfg.getCacheLoaderConfig().isFetchPersistentState() && !cfg.getCacheLoaderConfig().isShared(); } public void integrateState(ObjectInputStream ois, Object target, Fqn targetRoot, boolean integratePersistentState) throws Exception { // pop version from the stream first! short version = (Short) cache.getMarshaller().objectFromObjectStream(ois); log.info("Using version " + version); integrateTransientState(ois, (InternalNode) target); if (trace) log.trace("Reading marker for nonexistent associated state"); cache.getMarshaller().objectFromObjectStream(ois); if (integratePersistentState) { integratePersistentState(ois, targetRoot); } // Delimiter verifyMarker(cache.getMarshaller().objectFromObjectStream(ois)); if (nonBlocking) integrateTxLog(ois); } /** * Mimics a partial flush between the current instance and the address to flush, by opening and closing the necessary * latches on both ends. * @param addressToFlush address to flush in addition to the current address * @param block if true, mimics setting a flush. Otherwise, mimics un-setting a flush. * @throws Exception if there are issues */ private void mimicPartialFlushViaRPC(Address addressToFlush, boolean block) throws Exception { StateTransferControlCommand cmd = commandsFactory.buildStateTransferControlCommand(block); Vector

    recipient = new Vector
    (); recipient.add(addressToFlush); if (!block) rpcManager.getFlushTracker().unblock(); rpcManager.callRemoteMethods(recipient, cmd, true, cfg.getStateRetrievalTimeout(), true); if (block) rpcManager.getFlushTracker().block(); } private void integrateTxLog(ObjectInputStream ois) throws Exception { if (trace) log.trace("Integrating transaction log"); processCommitLog(ois); mimicPartialFlushViaRPC(rpcManager.getLastStateTransferSource(), true); try { if (trace) log.trace("Retrieving/Applying post-flush commits"); processCommitLog(ois); if (trace) log.trace("Retrieving/Applying pending prepares"); Object object = cache.getMarshaller().objectFromObjectStream(ois); while (object instanceof PrepareCommand) { PrepareCommand command = (PrepareCommand)object; if (! txLog.hasPendingPrepare(command)) { InvocationContext ctx = container.get(); ctx.setOriginLocal(false); ctx.getOptionOverrides().setCacheModeLocal(true); ctx.getOptionOverrides().setSkipCacheStatusCheck(true); chain.invoke(ctx, command); } object = cache.getMarshaller().objectFromObjectStream(ois); } verifyMarker(object); // Block all remote commands once transfer is complete, // and before FLUSH completes registry.setStatusCheckNecessary(true); } finally { if (trace) log.trace("Stopping partial flush"); // channel.stopFlush(targets); mimicPartialFlushViaRPC(rpcManager.getLastStateTransferSource(), false); } } private void processCommitLog(ObjectInputStream ois) throws Exception { Object object = cache.getMarshaller().objectFromObjectStream(ois); while (object instanceof LogEntry) { List mods = ((LogEntry)object).getModifications(); if (trace) log.trace("Mods = " + mods); for (WriteCommand mod : mods) { InvocationContext ctx = container.get(); ctx.setOriginLocal(false); ctx.getOptionOverrides().setCacheModeLocal(true); ctx.getOptionOverrides().setSkipCacheStatusCheck(true); chain.invoke(ctx, mod); } object = cache.getMarshaller().objectFromObjectStream(ois); } verifyMarker(object); } private void verifyMarker(Object object) { if (object instanceof NodeDataExceptionMarker) { NodeDataExceptionMarker e = (NodeDataExceptionMarker)object; throw new CacheException("Error in state transfer stream", e.getCause()); } else if (! (object instanceof NodeDataMarker)) { throw new CacheException("Invalid object unmarshalled"); } } protected void integrateTransientState(ObjectInputStream in, InternalNode target) throws Exception { boolean transientSet = false; try { if (trace) { log.trace("integrating transient state for " + target); } integrateTransientState(target.getFqn(), in); transientSet = true; if (trace) { log.trace("transient state successfully integrated"); } notifyAllNodesCreated(cache.getInvocationContext(), target); } catch (CacheException ce) { throw ce; } catch (Exception e) { throw new CacheException(e); } finally { if (!transientSet) { target.clear(); target.removeChildren(); } } } protected void integratePersistentState(ObjectInputStream in, Fqn targetFqn) throws Exception { CacheLoaderManager loaderManager = cache.getCacheLoaderManager(); CacheLoader loader = loaderManager == null ? null : loaderManager.getCacheLoader(); if (loader == null) { if (trace) { log.trace("cache loader is null, will not attempt to integrate persistent state"); } } else { if (trace) { 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 (trace) { log.trace("persistent state integrated successfully"); } } } } } /** * 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, InternalNode 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.getKeys().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.getData(), ctx); } ctx.setOriginLocal(true); Set children = curr.getChildren(); for (InternalNode n : children) notifyAllNodesCreated(ctx, n); } private void prepareContextOptions() { cache.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache.getInvocationContext().getOptionOverrides().setSkipCacheStatusCheck(true); cache.getInvocationContext().getOptionOverrides().setSuppressPersistence(!needToPersistState); } private void integrateTransientState(Fqn target, ObjectInputStream in) throws Exception { prepareContextOptions(); NodeSPI targetNode = cache.getNode(target); if (trace) log.trace("Target node has following children: " + targetNode.getChildrenNames()); for (Object childname : targetNode.getChildrenNames()) { prepareContextOptions(); if (trace) log.trace("Removing child: " + childname); targetNode.removeChild(childname); } // set these flags to false if we have persistent state! targetNode.setDataLoaded(false); targetNode.setChildrenLoaded(false); 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()) { // with MVCC these calls should ALWAYS go up the interceptor chain since no other locking // takes place elsewhere. prepareContextOptions(); cache.clearData(target); prepareContextOptions(); cache.put(target, nd.getAttributes()); // Check whether this is an integration into the buddy backup // subtree Fqn tferFqn = nd.getFqn(); boolean move = target.isChildOrEquals(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN) && !tferFqn.isChildOrEquals(target); // If it is an integration, calculate how many levels of offset int offset = move ? target.size() - tferFqn.size() : 0; integrateStateTransferChildren(target, offset, nodeDataIterator); integrateRetainedNodes(target); } } // read marker off stack // cache.getMarshaller().objectFromObjectStream(in); } if (trace) log.trace("Finished integrating transient state"); } @SuppressWarnings("unchecked") private List readNodesAsList(ObjectInputStream in) throws Exception { Object obj = cache.getMarshaller().objectFromObjectStream(in); if (obj instanceof NodeDataExceptionMarker) { Throwable cause = ((NodeDataExceptionMarker)obj).getCause(); if (cause instanceof Exception) throw (Exception) cause; throw new CacheException(cause); } if (obj instanceof NodeDataMarker) return null; if (trace) log.trace("Data readed from stream is: " + obj); return (List) obj; } private NodeData integrateStateTransferChildren(Fqn parentFqn, int offset, Iterator nodeDataIterator) throws IOException, ClassNotFoundException { int parentLevel = parentFqn.size(); int targetLevel = parentLevel + 1; Fqn fqn; int size; NodeData nd = nodeDataIterator.hasNext() ? nodeDataIterator.next() : null; while (nd != null && !nd.isMarker()) { fqn = nd.getFqn(); if (trace) log.trace("Integrating state for children: " + fqn); // If we need to integrate into the buddy backup subtree, // change the Fqn to fit under it if (offset > 0) { fqn = Fqn.fromRelativeFqn(parentFqn.getAncestor(offset), fqn); } size = fqn.size(); if (size <= parentLevel) { return nd; } else if (size > targetLevel) { throw new IllegalStateException("NodeData " + fqn + " is not a direct child of " + parentFqn); } Map attrs = nd.getAttributes(); prepareContextOptions(); cache.clearData(fqn); prepareContextOptions(); cache.put(fqn, attrs); cache.getNode(fqn).setDataLoaded(false); cache.getNode(fqn).setChildrenLoaded(false); // Recursively call, which will walk down the tree // and return the next NodeData that's a child of our parent nd = integrateStateTransferChildren(fqn, 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(Fqn target) { Set result = new HashSet(); for (Fqn internalFqn : internalFqns) { if (internalFqn.isChildOf(target)) { prepareContextOptions(); Node node = getInternalNode(cache.getNode(target), internalFqn); if (node != null) { result.add(node.getFqn()); } } } return result; } private Node getInternalNode(Node parentNode, Fqn internalFqn) { Fqn parentFqn = parentNode.getFqn(); Object name = internalFqn.get(parentFqn.size()); prepareContextOptions(); Node result = parentNode.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(Fqn target) { Set retainedNodes = retainInternalNodes(target); for (Fqn retained : retainedNodes) { if (retained.isChildOf(target)) { integrateRetainedNode(target, retained); } } } // TODO: What is this rubbish?!?? private void integrateRetainedNode(Fqn ancFqn, Fqn descFqn) { prepareContextOptions(); InternalNode ancestor = cache.getNode(ancFqn).getDelegationTarget(); Object name = descFqn.get(ancFqn.size()); InternalNode child = ancestor.getChild(name); if (ancFqn.size() == descFqn.size() + 1) { if (child == null) { prepareContextOptions(); InternalNode descendant = cache.getNode(descFqn).getDelegationTarget(); prepareContextOptions(); 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.createInternalNode(name, ancestor); // ancestor.addChild(name, child); // since this shouldn't happen, deal with it. - Manik - Jul08 throw new NullPointerException("Child is null"); } // Keep walking down the tree integrateRetainedNode(child.getFqn(), descFqn); } } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/statetransfer/LegacyStateTransferIntegrator.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/statetransfer/LegacyStateTransferIntegrator.j0000644000175000017500000003506711433521426033357 0ustar moellermoeller/* * 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.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.config.Configuration; import org.jboss.cache.eviction.EvictionEvent; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.Start; 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; @Deprecated public class LegacyStateTransferIntegrator implements StateTransferIntegrator { private static final Log log = LogFactory.getLog(LegacyStateTransferIntegrator.class); private static final boolean trace = log.isTraceEnabled(); private CacheSPI cache; private NodeFactory factory; private Set internalFqns; private Configuration cfg; private boolean usePut; // for JBCACHE-131 @Inject public void inject(CacheSPI cache, NodeFactory nodefactory, Configuration cfg) { this.cache = cache; this.factory = nodefactory; this.cfg = cfg; } @Start(priority = 14) public void start() { this.internalFqns = cache.getInternalFqns(); usePut = cfg.getCacheLoaderConfig() != null && !cfg.getCacheLoaderConfig().isFetchPersistentState() && !cfg.getCacheLoaderConfig().isShared(); } public void integrateState(ObjectInputStream ois, Object target, Fqn targetFqn, boolean integratePersistentState) throws Exception { // pop version from the stream first! short version = (Short) cache.getMarshaller().objectFromObjectStream(ois); log.info("Using version " + version); integrateTransientState(ois, (NodeSPI) target); // read another marker for the dummy associated state if (trace) log.trace("Reading marker for nonexistent associated state"); cache.getMarshaller().objectFromObjectStream(ois); if (integratePersistentState) integratePersistentState(ois, targetFqn); } protected void integrateTransientState(ObjectInputStream in, NodeSPI target) throws Exception { boolean transientSet = false; 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(); } } } protected void integratePersistentState(ObjectInputStream in, Fqn targetFqn) throws Exception { if (trace) log.trace("Reading persistent state from stream"); 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"); } } } } } /** * 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 void integrateTransientState(NodeSPI target, ObjectInputStream in) throws Exception { if (trace) log.trace("Reading transient state from stream"); 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(); if (usePut) { cache.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache.getInvocationContext().getOptionOverrides().setSkipCacheStatusCheck(true); target.clearData(); cache.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache.getInvocationContext().getOptionOverrides().setSkipCacheStatusCheck(true); target.putAll(attributes); } else { 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); } } // set these flags to false if we have persistent state! target.setDataLoaded(false); target.setChildrenLoaded(false); // read marker off stack if (trace) log.trace("Reading marker from stream"); 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; 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()); } Map attrs = nd.getAttributes(); // We handle this NodeData. Create a TreeNode and // integrate its data NodeSPI target; if (usePut) { cache.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache.getInvocationContext().getOptionOverrides().setSkipCacheStatusCheck(true); cache.clearData(fqn); cache.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache.getInvocationContext().getOptionOverrides().setSkipCacheStatusCheck(true); cache.put(fqn, attrs); target = cache.getNode(fqn); } else { target = factory.createNode(fqn, parent, attrs); parent.addChild(fqn.getLastElement(), target); // JBCACHE-913 Region region = cache.getRegion(fqn, false); if (region != null && region.getEvictionRegionConfig() != null) { region.registerEvictionEvent(fqn, EvictionEvent.Type.ADD_NODE_EVENT, attrs == null ? 0 : attrs.size(), null, null); } } // 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.createNode(name, ancestor); ancestor.addChild(name, child); } // Keep walking down the tree integrateRetainedNode(child, descendant); } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/statetransfer/StateTransferGenerator.java0000644000175000017500000000261711111047320032512 0ustar moellermoeller/* * 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.statetransfer; import java.io.ObjectOutputStream; /** * @since 1.2.4 */ public interface StateTransferGenerator { // TODO: rootNode is an Object to support both InternalNodes and NodeSPIs. void generateState(ObjectOutputStream stream, Object rootNode, boolean generateTransient, boolean generatePersistent, boolean suppressErrors) throws Exception; }jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/statetransfer/StateProviderBusyException.java0000644000175000017500000000106711157534240033405 0ustar moellermoellerpackage org.jboss.cache.statetransfer; import org.jboss.cache.CacheException; /** * Thrown when a state provider is busy * * @author Manik Surtani * @since 3.1 */ public class StateProviderBusyException extends CacheException { public StateProviderBusyException() { } public StateProviderBusyException(String message) { super(message); } public StateProviderBusyException(String message, Throwable cause) { super(message, cause); } public StateProviderBusyException(Throwable cause) { super(cause); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/UnversionedNode.java0000644000175000017500000004345411245211742026315 0ustar moellermoeller/* * 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; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import static org.jboss.cache.AbstractNode.NodeFlags.*; import org.jboss.cache.marshall.MarshalledValue; import org.jboss.cache.util.FastCopyHashMap; import org.jboss.cache.util.Immutables; import org.jboss.cache.util.concurrent.SelfInitializingConcurrentHashMap; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * 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 AT jboss DOT org) * @since 2.0.0 */ public class UnversionedNode extends AbstractNode implements InternalNode { /** * Debug log. */ protected static Log log = LogFactory.getLog(UnversionedNode.class); protected static final boolean trace = log.isTraceEnabled(); /** * Map of general data keys to values. */ protected Map data; protected NodeSPI delegate; protected CacheSPI cache; /** * Constructs a new node with an FQN of Root. */ public UnversionedNode() { this.fqn = Fqn.ROOT; initFlags(); } /** * Constructs a new node a given Fqn * * @param fqn fqn of the node */ public UnversionedNode(Fqn fqn) { this.fqn = fqn; initFlags(); } public UnversionedNode(Fqn fqn, CacheSPI cache, boolean lockForChildInsertRemove) { initFlags(); this.cache = cache; setLockForChildInsertRemove(lockForChildInsertRemove); this.fqn = fqn; // if this is a root node, create the child map. if (fqn.isRoot()) { children = new ConcurrentHashMap>(64, .5f, 16); } else { // this always needs to be initialized. The actual cost of the ConcurrentHashMap, however, is deferred. children = new SelfInitializingConcurrentHashMap>(); } } public UnversionedNode(Fqn fqn, CacheSPI cache, boolean lockForChildInsertRemove, Map data) { this(fqn, cache, lockForChildInsertRemove); if (data != null) this.data = new FastCopyHashMap(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 org.jboss.cache.AbstractNode.NodeFlags} enum. */ protected void initFlags() { setFlag(DATA_LOADED); setFlag(VALID); } public NodeSPI getDelegate() { return delegate; } public void setDelegate(NodeSPI delegate) { this.delegate = delegate; } /** * Returns a parent by checking the TreeMap by name. */ public NodeSPI getParent() { if (fqn.isRoot()) { return null; } return cache.peek(fqn.getParent(), true); } // does not need to be synchronized since this will only be accessed by a single thread in MVCC thanks to the write lock. // private void initDataMap() // { // if (data == null) data = new FastCopyHashMap(); // } public CacheSPI getCache() { return cache; } public boolean isChildrenLoaded() { return isFlagSet(CHILDREN_LOADED); } public void setChildrenLoaded(boolean childrenLoaded) { setFlag(CHILDREN_LOADED, childrenLoaded); } public V get(K key) { return data == null ? null : data.get(key); } public Map getData() { if (data == null) return Collections.emptyMap(); return data; } public V put(K key, V value) { if (data == null) { // new singleton map! data = Collections.singletonMap(key, value); return null; } if (data.size() == 1 && data.containsKey(key)) { V oldVal = data.get(key); data = Collections.singletonMap(key, value); return oldVal; } upgradeDataMap(); return data.put(key, value); } @Override public InternalNode getChild(Fqn f) { if (fqn.size() == 1) { return getChild(fqn.getLastElement()); } else { InternalNode currentNode = this; for (int i = 0; i < fqn.size(); i++) { Object nextChildName = fqn.get(i); currentNode = currentNode.getChild(nextChildName); if (currentNode == null) return null; } return currentNode; } } @Override public InternalNode getChild(Object childName) { if (childName == null) return null; return children().get(childName); } @Override public Set> getChildren() { // strip out deleted child nodes... if (children.isEmpty()) return Collections.emptySet(); Set> exclDeleted = new HashSet>(); for (InternalNode n : children().values()) { if (!n.isRemoved()) exclDeleted.add(n); } exclDeleted = Collections.unmodifiableSet(exclDeleted); return exclDeleted; } @Override public Set> getChildren(boolean includeMarkedForRemoval) { if (includeMarkedForRemoval) { if (!children.isEmpty()) { return Immutables.immutableSetConvert(children().values()); } else { return Collections.emptySet(); } } else { return getChildren(); } } @Override public ConcurrentMap> getChildrenMap() { return children(); } @Override public void setChildrenMap(ConcurrentMap> children) { this.children = children; } @Override public void addChild(Object nodeName, InternalNode nodeToAdd) { if (nodeName != null) { children().put(nodeName, nodeToAdd); } } @Override public void addChild(InternalNode child) { addChild(child, false); } @Override public void addChild(InternalNode child, boolean safe) { Fqn childFqn = child.getFqn(); if (safe || childFqn.isDirectChildOf(fqn)) { children().put(childFqn.getLastElement(), child); } else { throw new CacheException("Attempting to add a child [" + childFqn + "] to [" + fqn + "]. Can only add direct children."); } } public V remove(K key) { if (data == null) return null; V value; if (data instanceof FastCopyHashMap) { value = data.remove(key); downgradeDataMapIfNeeded(); } else { // singleton maps cannot remove! value = data.get(key); data = null; } return value; } 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 (isRemoved()) { sb.append(" (deleted) [ ").append(fqn); } else { sb.append("[ ").append(fqn); } if (this instanceof VersionedNode) { sb.append(" version=").append(this.getVersion()); } if (data != null) { 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.isEmpty()) { if (trace) { sb.append(" children=").append(getChildrenNames()); } else { sb.append(" children=["); Set names = getChildrenNames(); 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("]"); } } sb.append("]"); return sb.toString(); } public void clear() { data = null; // if (data != null) data.clear(); } @SuppressWarnings("unchecked") public Set getChildrenNames() { return children.isEmpty() ? Collections.emptySet() : Immutables.immutableSetWrap(new HashSet(children.keySet())); } public Set getKeys() { if (data == null) { return Collections.emptySet(); } return Immutables.immutableSetWrap(new HashSet(data.keySet())); } public boolean containsKey(K key) { return data != null && data.containsKey(key); } public boolean removeChild(Object childName) { return children.remove(childName) != null; } public boolean removeChild(Fqn f) { if (f.size() == 1) { return removeChild(f.getLastElement()); } else { NodeSPI child = getChildDirect(f); return child != null && child.getParentDirect().removeChildDirect(f.getLastElement()); } } public void putAll(Map data) { if (data != null) { if (this.data == null) this.data = copyDataMap(data); else if (this.data.size() == 1 && data.size() == 1 && this.data.keySet().iterator().next().equals(data.keySet().iterator().next())) { // replace key! Entry e = data.entrySet().iterator().next(); // These casts are a work-around for an eclipse compiler bug, please don't remove ;) this.data = Collections.singletonMap((K)e.getKey(), (V) e.getValue()); } else { // size. Do we need to update the existing data map to a FCHM? upgradeDataMap(); this.data.putAll(data); } } } protected final void upgradeDataMap() { if (data != null && !(data instanceof FastCopyHashMap)) data = new FastCopyHashMap(data); } protected final void downgradeDataMapIfNeeded() { if (data.size() == 1 && data instanceof FastCopyHashMap) { Entry e = data.entrySet().iterator().next(); data = Collections.singletonMap(e.getKey(), e.getValue()); } } public void removeChildren() { children.clear(); } public void markAsRemoved(boolean marker, boolean recursive) { setFlag(REMOVED, marker); if (recursive) { for (InternalNode child : children().values()) child.markAsRemoved(marker, true); } } protected final void printIndent(StringBuilder sb, int indent) { if (sb != null) { for (int i = 0; i < indent; i++) { sb.append(" "); } } } /** * 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; // invoke children for (Map.Entry> me : children().entrySet()) { InternalNode n = me.getValue(); Fqn cfqn = Fqn.fromRelativeElements(fqn, me.getKey()); n.setFqn(cfqn); } } public boolean hasChildren() { return !children.isEmpty(); } /** * Adds details of the node into a map as strings. */ protected void printDetailsInMap(StringBuilder sb, int indent) { printIndent(sb, indent); indent += 2;// increse it sb.append(Fqn.SEPARATOR); if (!fqn.isRoot()) sb.append(fqn.getLastElement()); sb.append(" "); sb.append(data); for (InternalNode n : children().values()) { sb.append("\n"); 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 (InternalNode child : children().values()) { 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); } @SuppressWarnings("unchecked") public InternalNode copy() { UnversionedNode n = new UnversionedNode(fqn, cache, isFlagSet(LOCK_FOR_CHILD_INSERT_REMOVE)); n.data = copyDataMap(data); copyInternals(n); return n; } protected void copyInternals(UnversionedNode n) { // direct reference to child map n.children = children; n.delegate = delegate; n.flags = flags; } public void setInternalState(Map state) { if (data == null) { data = copyDataMap(state); } else { // don't bother doing anything here putAll(state); } } protected final Map copyDataMap(Map toCopyFrom) { if (toCopyFrom != null && toCopyFrom.size() > 0) { Map map; if (toCopyFrom instanceof FastCopyHashMap) { map = (FastCopyHashMap) ((FastCopyHashMap) toCopyFrom).clone(); } else if (toCopyFrom.size() == 1) { Entry e = toCopyFrom.entrySet().iterator().next(); map = Collections.singletonMap(e.getKey(), e.getValue()); } else { map = new FastCopyHashMap(toCopyFrom); } return map; } return null; } 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) { for (InternalNode child : children().values()) { child.releaseObjectReferences(recursive); } } if (data != null) { for (K 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); } } } } /** * @return genericized version of the child map */ @SuppressWarnings("unchecked") private ConcurrentMap> children() { return children; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/AbstractNode.java0000644000175000017500000002313311237013623025546 0ustar moellermoeller/* * 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; import static org.jboss.cache.AbstractNode.NodeFlags.REMOVED; import static org.jboss.cache.AbstractNode.NodeFlags.RESIDENT; import org.jboss.cache.lock.IdentityLock; import org.jboss.cache.optimistic.DataVersion; import org.jboss.cache.transaction.GlobalTransaction; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentMap; /** * Base class for {@link UnversionedNode}. * * @author manik */ public abstract class AbstractNode { protected ConcurrentMap children; // purposefully NOT genericized yet, since UnversionedNode and PessimisticUnversionedNode will both store different types. 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(1), /** * Data is loaded from the cache loader if this flag is present. */ DATA_LOADED(1<<1), /** * Node is write-locked when children are added or removed if this flag is enabled. */ LOCK_FOR_CHILD_INSERT_REMOVE(1<<2), /** * Node is valid if this flag is present. */ VALID(1<<3), /** * Node has been removed. */ REMOVED(1<<4), /** * NOde is resident and excluded from evictions */ RESIDENT(1<<5), /** * Specific to Optimistic Locking Workspace nodes - set if a node has been modified in a workspace. */ MODIFIED_IN_WORKSPACE(1<<6), /** * Specific to Optimistic Locking Workspace nodes - set if a node has been created in a workspace. */ CREATED_IN_WORKSPACE(1<<7), /** * Specific to Optimistic Locking Workspace nodes - set if a node has added or removed children in a workspace. */ CHILDREN_MODIFIED_IN_WORKSPACE(1<<8), /** * Specific to Optimistic Locking Workspace nodes - set if an implicit version is associated with this node. */ VERSIONING_IMPLICIT(1<<9), /** * Specific to Optimistic Locking Workspace nodes - set if a node has been resurrected in a workspace. */ RESURRECTED_IN_WORKSPACE(1<<10); 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 isRemoved() { return isFlagSet(REMOVED); } public void setResident(boolean resident) { setFlag(RESIDENT, resident); } public void setRemoved(boolean marker) { markAsRemoved(marker, false); } public abstract void markAsRemoved(boolean marker, boolean recursive); public boolean isResident() { return isFlagSet(RESIDENT); } @SuppressWarnings("deprecation") public IdentityLock getLock() { throw new UnsupportedOperationException("Not supported in this implementation!"); } // versioning public void setVersion(DataVersion version) { throw new UnsupportedOperationException("Versioning not supported"); } public DataVersion getVersion() { throw new UnsupportedOperationException("Versioning not supported"); } @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(); } // ----- default no-op impls of child manipulation methods public InternalNode getChild(Fqn f) { throw new UnsupportedOperationException("Not supported in " + getClass().getSimpleName()); } public InternalNode getChild(Object childName) { throw new UnsupportedOperationException("Not supported in " + getClass().getSimpleName()); } public Set> getChildren() { throw new UnsupportedOperationException("Not supported in " + getClass().getSimpleName()); } public Set> getChildren(boolean includeMarkedForRemoval) { throw new UnsupportedOperationException("Not supported in " + getClass().getSimpleName()); } public Map> getChildrenMap() { throw new UnsupportedOperationException("Not supported in " + getClass().getSimpleName()); } public void addChild(Object nodeName, InternalNode nodeToAdd) { throw new UnsupportedOperationException("Not supported in " + getClass().getSimpleName()); } public void addChild(InternalNode child) { throw new UnsupportedOperationException("Not supported in " + getClass().getSimpleName()); } public void addChild(InternalNode child, boolean safe) { throw new UnsupportedOperationException("Not supported in " + getClass().getSimpleName()); } public void setChildrenMap(ConcurrentMap> children) { throw new UnsupportedOperationException("Not supported in " + getClass().getSimpleName()); } public NodeSPI getChildDirect(Fqn fqn) { throw new UnsupportedOperationException("Not supported in " + getClass().getSimpleName()); } public NodeSPI getChildDirect(Object childName) { throw new UnsupportedOperationException("Not supported in " + getClass().getSimpleName()); } public Set> getChildrenDirect() { throw new UnsupportedOperationException("Not supported in " + getClass().getSimpleName()); } public Set> getChildrenDirect(boolean includeMarkedForRemoval) { throw new UnsupportedOperationException("Not supported in " + getClass().getSimpleName()); } public Map> getChildrenMapDirect() { throw new UnsupportedOperationException("Not supported in " + getClass().getSimpleName()); } public void setChildrenMapDirect(Map> children) { throw new UnsupportedOperationException("Not supported in " + getClass().getSimpleName()); } public void addChildDirect(Object nodeName, Node nodeToAdd) { throw new UnsupportedOperationException("Not supported in " + getClass().getSimpleName()); } public void addChildDirect(NodeSPI child) { throw new UnsupportedOperationException("Not supported in " + getClass().getSimpleName()); } public NodeSPI addChildDirect(Fqn f) { throw new UnsupportedOperationException("Not supported in " + getClass().getSimpleName()); } public NodeSPI addChildDirect(Fqn f, boolean notify) { throw new UnsupportedOperationException("Not supported in " + getClass().getSimpleName()); } public NodeSPI addChildDirect(Object o, boolean notify) { throw new UnsupportedOperationException("Not supported in " + getClass().getSimpleName()); } public NodeSPI getOrCreateChild(Object childName, GlobalTransaction gtx) { throw new UnsupportedOperationException("Not supported in " + getClass().getSimpleName()); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/RegionRegistry.java0000644000175000017500000000476711252520550026164 0ustar moellermoeller/* * 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; import org.jboss.cache.factories.annotations.NonVolatile; import java.util.concurrent.ConcurrentHashMap; /** * An extension of the ConcurrentHashMap that acts as a container for regions. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ @NonVolatile public class RegionRegistry extends ConcurrentHashMap { private static final long serialVersionUID = -2480683437308480252L; private volatile int largestFqnLength; public RegionRegistry() { // default CHM constructor. Potentially fine-tune later? super(); } @Override public Region put(Fqn f, Region r) { largestFqnLength = Math.max(largestFqnLength, f.size()); return super.put(f, r); } @Override public Region putIfAbsent(Fqn f, Region r) { Region rV = super.putIfAbsent(f, r); if (rV == null) largestFqnLength = Math.max(largestFqnLength, f.size()); return rV; } @Override public Region replace(Fqn f, Region r) { Region rV = super.replace(f, r); if (rV != null) largestFqnLength = Math.max(largestFqnLength, f.size()); return rV; } @Override public boolean replace(Fqn f, Region oldR, Region newR) { boolean rV = super.replace(f, oldR, newR); if (rV) largestFqnLength = Math.max(largestFqnLength, f.size()); return rV; } public int getLargestFqnLength() { return largestFqnLength; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/RegionManager.java0000644000175000017500000001652711111047320025714 0ustar moellermoeller/* * 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; import org.jboss.cache.annotations.Compat; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.eviction.EvictionTimerTask; import java.util.List; /** * Manages regions. *

    * Prior to 3.0.0, this was a concrete class. An interface was introduced in 3.0.0 for enhanced flexibility. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ public interface RegionManager { /** * @return true if evictions are being processed. */ boolean isUsingEvictions(); /** * @return true if replication is by default inactive for new {@link org.jboss.cache.Region}s. */ boolean isDefaultInactive(); /** * Sets if replication for new {@link org.jboss.cache.Region}s is by default inactive. */ void setDefaultInactive(boolean defaultInactive); /** * Helper utility that checks for a {@link ClassLoader} registered for the * given {@link org.jboss.cache.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. */ void setContextClassLoaderAsCurrent(Fqn fqn); /** * Returns a region by {@link org.jboss.cache.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 org.jboss.cache.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. */ Region getRegion(Fqn fqn, boolean createIfAbsent); /** * Retrieves a valid marshalling {@link org.jboss.cache.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 */ Region getValidMarshallingRegion(Fqn fqn); /** * An overloaded form of {@link #getRegion(org.jboss.cache.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 */ Region getRegion(Fqn fqn, Region.Type type, boolean createIfAbsent); /** * Returns a region using Fqn.fromString(fqn), calling {@link #getRegion(org.jboss.cache.Fqn,boolean)} * * @param fqn * @param createIfAbsent * @see #getRegion(org.jboss.cache.Fqn,boolean) */ Region getRegion(String fqn, boolean createIfAbsent); /** * Removes a {@link Region} identified by the given fqn. * * @param fqn fqn of the region to remove * @return true if such a region existed and was removed. */ boolean removeRegion(Fqn fqn); /** * 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 org.jboss.cache.RegionNotEmptyException * if the node fqn * exists and already has either data or children */ void activate(Fqn fqn) throws RegionNotEmptyException; /** * Attempts to activate a given region rooted at a given Fqn, similar to {@link #activate(org.jboss.cache.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 */ void activateIfEmpty(Fqn fqn); /** * Convenienve method. If the region defined by fqn does not exist, {@link #isDefaultInactive()} is returned, otherwise * !{@link org.jboss.cache.Region#isActive()} is returned. * * @param fqn fqn to test * @return true if inactive */ boolean isInactive(Fqn 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 */ boolean hasRegion(Fqn fqn, Region.Type type); /** * Disables unmarshalling of replication messages for the region * rooted in the given Fqn. * * @param fqn */ void deactivate(Fqn fqn); /** * Resets the region manager's regions registry */ void reset(); /** * 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 Region} extends. * * @param type Type of region to return * @return an ordered list of all regions, based on the type requested. */ List getAllRegions(Region.Type type); /** * Sets if evictions are processed. */ void setUsingEvictions(boolean usingEvictions); /** * Sets the eviction configuration. */ void setEvictionConfig(EvictionConfig evictionConfig); /** * Returns a string containing debug information on every region. * * @return Regions as a string */ String dumpRegions(); CacheSPI getCache(); /** * @return the eviction timer task attached to the region manager */ EvictionTimerTask getEvictionTimerTask(); /** * @return the configuration */ Configuration getConfiguration(); // ---------- compatibility interface ----------------- /** * Starts the eviction processing thread. * * @deprecated */ @Deprecated @Compat void startEvictionThread(); /** * Stops the eviction processing thread * * @deprecated */ @Deprecated @Compat void stopEvictionThread(); } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/InvocationContext.java0000644000175000017500000005536311176306660026675 0ustar moellermoeller/* * 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; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.annotations.Compat; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.config.Option; import org.jboss.cache.marshall.MethodCall; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.TransactionContext; import org.jboss.cache.transaction.TransactionTable; import org.jboss.cache.util.Immutables; import javax.transaction.Transaction; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * A context that holds information regarding the scope of a single invocation. May delegate some calls to a {@link org.jboss.cache.transaction.TransactionContext} * if one is in scope. *

    * Note that prior to 3.0.0, InvocationContext was a concrete class and not an abstract one. *

    * * @author Manik Surtani (manik AT jboss DOT org) * @see org.jboss.cache.transaction.TransactionContext */ @SuppressWarnings("deprecation") @Compat(notes = "This really ought to be an interface, just like TransactionContext, but since this is public API making this an interface will break binary compat with 2.x.") public abstract class InvocationContext { private static final Log log = LogFactory.getLog(InvocationContext.class); private static final boolean trace = log.isTraceEnabled(); private Transaction transaction; private GlobalTransaction globalTransaction; protected TransactionContext transactionContext; private Option optionOverrides; // defaults to true. private boolean originLocal = true; private boolean localRollbackOnly; private boolean bypassUnmarshalling = false; @Deprecated private MethodCall methodCall; @Deprecated private VisitableCommand command; /** * Set of Fqns loaded by the cache loader interceptor. Only recorded if needed, such as by the ActivationInterceptor */ private Set fqnsLoaded; /** * LinkedHashSet of locks acquired by the invocation. We use a LinkedHashSet because we need efficient Set semantics * but also need guaranteed ordering for use by lock release code (see JBCCACHE-874). *

    * This needs to be unchecked since we support both MVCC (Fqns held here) or legacy Opt/Pess locking (NodeLocks held here). * once we drop support for opt/pess locks we can genericise this to contain Fqns. - Manik Surtani, June 2008 */ protected LinkedHashSet invocationLocks; /** * Retrieves a node from the registry of looked up nodes in the current scope. * * @param fqn fqn to look up * @return a node, or null if it cannot be found. * @since 3.0. */ public abstract NodeSPI lookUpNode(Fqn fqn); /** * Puts an entry in the registry of looked up nodes in the current scope. * * @param f fqn to add * @param n node to add * @since 3.0. */ public abstract void putLookedUpNode(Fqn f, NodeSPI n); /** * Adds a map of looked up nodes to the current map of looked up nodes * * @param lookedUpNodes looked up nodes to add */ public abstract void putLookedUpNodes(Map lookedUpNodes); /** * Clears the registry of looked up nodes. * * @since 3.0. */ public abstract void clearLookedUpNodes(); /** * Retrieves a map of nodes looked up within the current invocation's scope. * * @return a map of looked up nodes. * @since 3.0 */ public abstract Map getLookedUpNodes(); /** * Marks teh context as only rolling back. * * @param localRollbackOnly if true, the context is only rolling back. */ 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 a transaction object on the invocation context. * * @param transaction transaction to set */ 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 TransactionContext getTransactionContext() { return transactionContext; } /** * Sets the transaction context to be associated with the current thread. * * @param transactionContext transaction context to set * @since 2.2.0 */ public void setTransactionContext(TransactionContext transactionContext) { this.transactionContext = transactionContext; } /** * 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 global transaction to set */ 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; } /** * @return true of no options have been set on this context, false otherwise. */ public boolean isOptionsUninitialised() { return optionOverrides == null; } /** * Sets the option overrides to be associated with this invocation * * @param optionOverrides options to set */ 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; } /** * Returns an immutable, defensive copy of the List of locks currently maintained for the current scope. *

    * Note that if a transaction is in scope, implementations should retrieve these locks from the {@link org.jboss.cache.transaction.TransactionContext}. * Retrieving locks from this method should always ensure they are retrieved from the appropriate scope. *

    * Note that currently (as of 3.0.0) this list is unchecked. This is to allow support for both MVCC (which uses Fqns as locks) * as well as legacy Optimistic and Pessimistic Locking schemes (which use {@link org.jboss.cache.lock.NodeLock} as locks). Once support for * legacy node locking schemes are dropped, this method will be more strongly typed to return List. * * @return locks held in current scope. */ @SuppressWarnings("unchecked") public List getLocks() { // first check transactional scope if (transactionContext != null) return transactionContext.getLocks(); return invocationLocks == null || invocationLocks.isEmpty() ? Collections.emptyList() : Immutables.immutableListConvert(invocationLocks); } /** * Adds a List of locks to the currently maintained collection of locks acquired. *

    * Note that if a transaction is in scope, implementations should record locks on the {@link org.jboss.cache.transaction.TransactionContext}. * Adding locks using this method should always ensure they are applied to the appropriate scope. *

    * Note that currently (as of 3.0.0) this list is unchecked. This is to allow support for both MVCC (which uses Fqns as locks) * as well as legacy Optimistic and Pessimistic Locking schemes (which use {@link org.jboss.cache.lock.NodeLock} as locks). Once support for * legacy node locking schemes are dropped, this method will be more strongly typed to accept List. * * @param locks locks to add */ @SuppressWarnings("unchecked") public void addAllLocks(List locks) { // first check transactional scope if (transactionContext != null) { transactionContext.addAllLocks(locks); } else { // no need to worry about concurrency here - a context is only valid for a single thread. if (invocationLocks == null) invocationLocks = new LinkedHashSet(4); invocationLocks.addAll(locks); } } /** * Adds a lock to the currently maintained collection of locks acquired. *

    * Note that if a transaction is in scope, implementations should record this lock on the {@link org.jboss.cache.transaction.TransactionContext}. * Using this method should always ensure that the appropriate scope is used. *

    * Note that currently (as of 3.0.0) this lock is weakly typed. This is to allow support for both MVCC (which uses {@link Fqn}s as locks) * as well as legacy Optimistic and Pessimistic Locking schemes (which use {@link org.jboss.cache.lock.NodeLock} as locks). Once support for * legacy node locking schemes are dropped, this method will be more strongly typed to accept {@link Fqn}. * * @param lock lock to add */ @SuppressWarnings("unchecked") public void addLock(Object lock) { // first check transactional scope if (transactionContext != null) { transactionContext.addLock(lock); } else { // no need to worry about concurrency here - a context is only valid for a single thread. if (invocationLocks == null) invocationLocks = new LinkedHashSet(4); invocationLocks.add(lock); } } /** * Removes a lock from the currently maintained collection of locks acquired. *

    * Note that if a transaction is in scope, implementations should remove this lock from the {@link org.jboss.cache.transaction.TransactionContext}. * Using this method should always ensure that the lock is removed from the appropriate scope. *

    * Note that currently (as of 3.0.0) this lock is weakly typed. This is to allow support for both MVCC (which uses {@link Fqn}s as locks) * as well as legacy Optimistic and Pessimistic Locking schemes (which use {@link org.jboss.cache.lock.NodeLock} as locks). Once support for * legacy node locking schemes are dropped, this method will be more strongly typed to accept {@link Fqn}. * * @param lock lock to remove */ @SuppressWarnings("unchecked") public void removeLock(Object lock) { // first check transactional scope if (transactionContext != null) { transactionContext.removeLock(lock); } else { // no need to worry about concurrency here - a context is only valid for a single thread. if (invocationLocks != null) invocationLocks.remove(lock); } } /** * Clears all locks from the currently maintained collection of locks acquired. *

    * Note that if a transaction is in scope, implementations should clear locks from the {@link org.jboss.cache.transaction.TransactionContext}. * Using this method should always ensure locks are cleared in the appropriate scope. *

    * Note that currently (as of 3.0.0) this lock is weakly typed. This is to allow support for both MVCC (which uses {@link Fqn}s as locks) * as well as legacy Optimistic and Pessimistic Locking schemes (which use {@link org.jboss.cache.lock.NodeLock} as locks). Once support for * legacy node locking schemes are dropped, this method will be more strongly typed to accept {@link Fqn}. */ public void clearLocks() { // first check transactional scope if (transactionContext != null) { transactionContext.clearLocks(); } else { // no need to worry about concurrency here - a context is only valid for a single thread. if (invocationLocks != null) invocationLocks.clear(); } } /** * Note that if a transaction is in scope, implementations should test this lock from on {@link org.jboss.cache.transaction.TransactionContext}. * Using this method should always ensure locks checked in the appropriate scope. * * @param lock lock to test * @return true if the lock being tested is already held in the current scope, false otherwise. */ public boolean hasLock(Object lock) { // first check transactional scope if (transactionContext != null) { return transactionContext.hasLock(lock); } else { return invocationLocks != null && invocationLocks.contains(lock); } } /** * @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 flag to set */ public void setOriginLocal(boolean originLocal) { this.originLocal = originLocal; } /** * @return true if the current transaction is set to rollback only. */ public boolean isLocalRollbackOnly() { return localRollbackOnly; } /** * Resets the context, freeing up any references. */ public void reset() { transaction = null; globalTransaction = null; optionOverrides = null; originLocal = true; invocationLocks = null; methodCall = null; command = null; fqnsLoaded = null; bypassUnmarshalling = false; } /** * This is a "copy-factory-method" that should be used whenever a clone of this class is needed. The resulting instance * is equal() to, but not ==, to the InvocationContext invoked on. Note that this is a shallow copy with the exception * of the Option object, which is deep, as well as any collections held on the context such as locks. Note that the reference * to a {@link org.jboss.cache.transaction.TransactionContext}, if any, is maintained. * * @return a new InvocationContext */ @SuppressWarnings("unchecked") public abstract InvocationContext copy(); /** * Sets the state of the InvocationContext based on the template context passed in * * @param template template to copy from */ 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()); } /** * @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; } /** * If the lock acquisition timeout is overridden for current call using an option, then return that one. * If not overridden, return default value. * * @param timeout timeout to fall back to * @return timeout to use */ public long getLockAcquisitionTimeout(long timeout) { // TODO: this stuff really doesn't belong here. Put it somewhere else. 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) * * @param cacheCommand command to set */ @Deprecated @SuppressWarnings("deprecation") public void setCommand(VisitableCommand cacheCommand) { this.command = cacheCommand; } /** * @return command that is in scope * @see #setCommand(org.jboss.cache.commands.VisitableCommand) */ @Deprecated @SuppressWarnings("deprecation") public VisitableCommand getCommand() { return command; } /** * @return true if there is current transaction associated with the invocation, and this transaction is in a valid state. */ public boolean isValidTransaction() { // ought to move to the transaction context return transaction != null && TransactionTable.isValid(transaction); } /** * Throws the given throwable provided no options suppress or prevent this from happening. * * @param e throwable to throw * @throws Throwable if allowed to throw one. */ public void throwIfNeeded(Throwable e) throws Throwable { // TODO: this stuff really doesn't belong here. Put it somewhere else. 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; } @SuppressWarnings("unchecked") protected void doCopy(InvocationContext copy) { copy.command = command; copy.globalTransaction = globalTransaction; copy.invocationLocks = invocationLocks == null ? null : new LinkedHashSet(invocationLocks); copy.localRollbackOnly = localRollbackOnly; copy.optionOverrides = optionOverrides == null ? null : optionOverrides.copy(); copy.originLocal = originLocal; copy.transaction = transaction; copy.transactionContext = transactionContext; copy.fqnsLoaded = fqnsLoaded; copy.bypassUnmarshalling = bypassUnmarshalling; } /** * Adds an Fqn to the set of Fqns loaded by the cache loader interceptor. Instantiates the set lazily if needed. * * @param fqn fqn to add */ public void addFqnLoaded(Fqn fqn) { if (fqnsLoaded == null) fqnsLoaded = new HashSet(); fqnsLoaded.add(fqn); } /** * @return Set of Fqns loaded by the cache loader interceptor. Only recorded if needed, such as by the ActivationInterceptor */ public Set getFqnsLoaded() { return fqnsLoaded; } public void setFqnsLoaded(Set fqnsLoaded) { this.fqnsLoaded = fqnsLoaded; } @Override public String toString() { return "InvocationContext{" + "transaction=" + transaction + ", globalTransaction=" + globalTransaction + ", transactionContext=" + transactionContext + ", optionOverrides=" + optionOverrides + ", originLocal=" + originLocal + ", bypassUnmarshalling=" + bypassUnmarshalling + '}'; } @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 (bypassUnmarshalling != that.bypassUnmarshalling) return false; if (originLocal != that.originLocal) 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 + (localRollbackOnly ? 1 : 0); result = 29 * result + (bypassUnmarshalling ? 1 : 0); return result; } public void setBypassUnmarshalling(boolean b) { this.bypassUnmarshalling = b; } public boolean isBypassUnmarshalling() { return bypassUnmarshalling; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/CacheSPI.java0000644000175000017500000003204411155772366024574 0ustar moellermoeller/* * 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; import net.jcip.annotations.ThreadSafe; import org.jboss.cache.buddyreplication.BuddyManager; import org.jboss.cache.buddyreplication.GravitateResult; import org.jboss.cache.factories.ComponentRegistry; 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 AT jboss DOT org) * @see NodeSPI * @see Cache * @see org.jboss.cache.loader.CacheLoader * @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(); /** * 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 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 the component registry associated with this cache instance. * * @see org.jboss.cache.factories.ComponentRegistry */ ComponentRegistry getComponentRegistry(); } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/RPCManager.java0000644000175000017500000001557511237013623025127 0ustar moellermoeller/* * 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; import org.jboss.cache.RPCManagerImpl.FlushTracker; 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(); /** * Returns the last state transfer source address. * * @return the last state transfer source address */ public Address getLastStateTransferSource(); /** * Returns the flush tracker associated with this manager. * * @return the current flush tracker */ public FlushTracker getFlushTracker(); } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/CacheManagerImpl.java0000755000175000017500000001763711111047320026324 0ustar moellermoeller/* * 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; 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: 7168 $ */ 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; } 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 */ 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-3.2.8.GA/src/main/java/org/jboss/cache/commands/0000755000175000017500000000000011675221771024144 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/Visitor.java0000644000175000017500000002255011111047320026427 0ustar moellermoeller/* * 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.commands; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.legacy.write.CreateNodeCommand; 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 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. * @deprecated in 3.0. Will be removed once optimistic and pessimistic locking is removed. */ @Deprecated Object visitCreateNodeCommand(InvocationContext ctx, CreateNodeCommand command) throws Throwable; } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/ReplicableCommand.java0000644000175000017500000000543611111047320030335 0ustar moellermoeller/* * 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.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 { /** * 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 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-3.2.8.GA/src/main/java/org/jboss/cache/commands/DataCommand.java0000644000175000017500000000273011111047320027136 0ustar moellermoeller/* * 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.commands; import org.jboss.cache.Fqn; /** * Commands of this type manipulate data in the cache. * * @author Mircea.Markus@jboss.com * @author Manik Surtani (manik AT jboss DOT 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(); } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/write/0000755000175000017500000000000011675221757025302 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/write/ClearDataCommand.java0000644000175000017500000001075411151271021031245 0ustar moellermoeller/* * 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.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.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; protected static final Log log = LogFactory.getLog(ClearDataCommand.class); protected static final boolean trace = log.isTraceEnabled(); 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 = peekVersioned(ctx); if (targetNode == null) { if (log.isDebugEnabled()) log.debug("node " + fqn + " not found"); return null; } Map data = targetNode.getDataDirect(); notifier.notifyNodeModified(fqn, true, NodeModifiedEvent.ModificationType.REMOVE_DATA, data, ctx); targetNode.clearDataDirect(); notifier.notifyNodeModified(fqn, false, NodeModifiedEvent.ModificationType.REMOVE_DATA, data, ctx); return null; } 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 + '}'; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/write/InvalidateCommand.java0000644000175000017500000001043311111047320031476 0ustar moellermoeller/* * 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.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 final 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.notifyNodeInvalidated(fqn, true, ctx); try { return dataContainer.evict(fqn); } finally { notifier.notifyNodeInvalidated(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-3.2.8.GA/src/main/java/org/jboss/cache/commands/write/PutDataMapCommand.java0000644000175000017500000001406011116345644031435 0ustar moellermoeller/* * 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.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.NodeNotExistsException; 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(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; protected static final Log log = LogFactory.getLog(PutDataMapCommand.class); protected static final boolean trace = log.isTraceEnabled(); /* parameters*/ protected Map data; /* true to erase existing data */ protected boolean erase; 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); NodeSPI nodeSPI = ctx.lookUpNode(fqn); if (nodeSPI == null) throw new NodeNotExistsException("Node " + fqn + " does not exist!"); Map existingData = nodeSPI.getDataDirect(); if (notifier.shouldNotifyOnNodeModified()) { notifier.notifyNodeModified(fqn, true, NodeModifiedEvent.ModificationType.PUT_MAP, existingData == null ? Collections.emptyMap() : existingData, ctx); } if (erase) { nodeSPI.clearDataDirect(); } nodeSPI.putAllDirect(data); if (notifier.shouldNotifyOnNodeModified()) { notifier.notifyNodeModified(fqn, false, NodeModifiedEvent.ModificationType.PUT_MAP, nodeSPI.getDataDirect(), ctx); } return null; } 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 erase ? ERASE_VERSIONED_METHOD_ID : VERSIONED_METHOD_ID; } else { return erase ? ERASE_METHOD_ID : 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 (erase != that.erase) 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); if (erase) result++; return result; } /** * Sets a flag indicating the node data should be erased. */ public void setErase(boolean erase) { this.erase = erase; } /** * Returns a flag indicating the node data should be erased. */ public boolean isErase() { return erase; } @Override public String toString() { return "PutDataMapCommand{" + "fqn=" + fqn + ", dataVersion=" + dataVersion + ", data=" + data + ", globalTransaction=" + globalTransaction + ", erase=" + erase + '}'; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/write/RemoveNodeCommand.java0000644000175000017500000001413211111047320031461 0ustar moellermoeller/* * 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.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.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; protected static final Log log = LogFactory.getLog(RemoveNodeCommand.class); protected static final boolean trace = log.isTraceEnabled(); /*parameters*/ private boolean skipSendingNodeEvents = false; protected Fqn parentFqn; protected NodeSPI targetNode; 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 = peekVersioned(ctx); if (targetNode == null || targetNode.isDeleted()) { if (trace) log.trace("node " + fqn + " not found"); return false; } notifyBeforeRemove(targetNode, ctx); boolean found = targetNode.isValid() && !targetNode.isDeleted(); recursivelyMarkAsRemoved(targetNode, ctx); // make sure we clear all data on this node! targetNode.clearDataDirect(); notifyAfterRemove(ctx); return found; } /** * Recursively marks a node as removed. * * @param node Node to mark * @param ctx Invocation context */ protected void recursivelyMarkAsRemoved(NodeSPI node, InvocationContext ctx) { node.markAsDeleted(true); Fqn parentFqn = node.getFqn(); // recursion has to happen like this since child nodes are in the ctx. Map nodes = ctx.getLookedUpNodes(); for (Map.Entry entry : nodes.entrySet()) { if (entry.getKey().isChildOf(parentFqn)) entry.getValue().markAsDeleted(true); } } 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 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-3.2.8.GA/src/main/java/org/jboss/cache/commands/write/MoveCommand.java0000644000175000017500000001351611111047320030331 0ustar moellermoeller/* * 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.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.InternalNode; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.Visitor; import org.jboss.cache.commands.WriteCommand; import org.jboss.cache.commands.read.AbstractDataCommand; import org.jboss.cache.notifications.Notifier; import org.jboss.cache.transaction.GlobalTransaction; import java.util.Map; /** * 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 */ public class MoveCommand extends AbstractDataCommand implements WriteCommand { public static final int METHOD_ID = 36; protected static final Log log = LogFactory.getLog(MoveCommand.class); protected static final boolean trace = log.isTraceEnabled(); /* dependencies */ protected Notifier notifier; /* params */ protected Fqn to; protected 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) { if (fqn.isDirectChildOf(to)) { if (log.isDebugEnabled()) log.debug("Attempting to move " + fqn + " onto itself. Nothing to do."); return null; } NodeSPI node = ctx.lookUpNode(fqn); if (node == null || node.isDeleted()) { if (trace) log.trace("Node " + fqn + " does not exist when attempting to move node! Not doing anything."); return null; } if (trace) log.trace("Moving " + fqn + " to sit under " + to); // the actual move algorithm. NodeSPI newNode = ctx.lookUpNode(Fqn.fromRelativeElements(to, fqn.getLastElement())); Fqn newNodeFqn = newNode.getFqn(); // at this stage all child node objects we need have been created and are available in the ctx. // we just need to mark old ones as deleted, new ones as created, and move data across. notifier.notifyNodeMoved(fqn, newNodeFqn, true, ctx); moveRecursively(node, newNode, ctx); notifier.notifyNodeMoved(fqn, newNodeFqn, false, ctx); return null; } @SuppressWarnings("unchecked") private void moveRecursively(NodeSPI oldNode, NodeSPI newNode, InvocationContext ctx) { if (trace) log.trace("Moving " + oldNode.getFqn() + " to " + newNode.getFqn()); // start deep. Map children = oldNode.getDelegationTarget().getChildrenMap(); if (!children.isEmpty()) { for (InternalNode child : children.values()) { Fqn childFqn = child.getFqn(); Fqn newChildFqn = childFqn.replaceAncestor(oldNode.getFqn(), newNode.getFqn()); moveRecursively(ctx.lookUpNode(childFqn), ctx.lookUpNode(newChildFqn), ctx); } } // now swap the data for the current node. newNode.getDelegationTarget().putAll(oldNode.getDelegationTarget().getData()); oldNode.markAsDeleted(true); } public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable { return visitor.visitMoveCommand(ctx, this); } 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 + '}'; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/write/RemoveKeyCommand.java0000644000175000017500000001227111111047320031326 0ustar moellermoeller/* * 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.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 final boolean trace = log.isTraceEnabled(); /* parameters */ protected Object key; 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 = ctx.lookUpNode(fqn); 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); } Object 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 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 + '}'; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/write/EvictCommand.java0000644000175000017500000001454411155772024030515 0ustar moellermoeller/* * 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.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.InternalNode; 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.mvcc.ReadCommittedNode; import org.jboss.cache.notifications.Notifier; import java.util.Collection; import java.util.Collections; 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; protected static final Log log = LogFactory.getLog(EvictCommand.class); protected static final boolean trace = log.isTraceEnabled(); private List nodesToEvict; public EvictCommand(Fqn fqn) { this.fqn = fqn; } public EvictCommand() { } public void initialize(Notifier notifier, DataContainer dataContainer) { super.initialize(dataContainer); this.notifier = notifier; } public List getNodesToEvict() { return nodesToEvict; } public void setNodesToEvict(List nodesToEvict) { this.nodesToEvict = nodesToEvict; } /** * 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 is now absent from the cache. 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 = lookupForEviction(ctx, fqn); if ((node == null || node.isDeleted() || node.isResident()) && !recursiveEvictOnRoot(node)) { return true; } else if (recursive) { Collection nodesToEvict = getRecursiveEvictionNodes(); for (Fqn aFqn : nodesToEvict) { evictNode(aFqn, ctx, lookupForEviction(ctx, aFqn)); } return !nodesToEvict.isEmpty(); } else { return evictNode(fqn, ctx, node); } } private boolean recursiveEvictOnRoot(NodeSPI node) { return node == null && fqn.isRoot() && recursive && !nodesToEvict.isEmpty(); } protected Collection getRecursiveEvictionNodes() { Collections.sort(nodesToEvict); Collections.reverse(nodesToEvict); return nodesToEvict; } protected boolean evictNode(Fqn fqn, InvocationContext ctx, NodeSPI node) { notifier.notifyNodeEvicted(fqn, true, ctx); try { if (node == null) return true; if (node.hasChildrenDirect() || fqn.isRoot()) { if (trace) log.trace("removing DATA as node has children: evict(" + fqn + ")"); node.clearDataDirect(); node.setDataLoaded(false); return false; } else { if (trace) log.trace("removing NODE as it is a leaf: evict(" + fqn + ")"); InternalNode parentNode = lookupInAllScopes(ctx, fqn.getParent()); if (parentNode != null) { parentNode.removeChild(fqn.getLastElement()); parentNode.setChildrenLoaded(false); } node.setValid(false, false); node.markAsDeleted(true); node.setDataLoaded(false); node.getDelegationTarget().clear(); return true; } } finally { notifier.notifyNodeEvicted(fqn, false, ctx); } } private InternalNode lookupInAllScopes(InvocationContext ctx, Fqn fqn) { ReadCommittedNode nodeSPI = (ReadCommittedNode) lookupForEviction(ctx, fqn); if (nodeSPI == null) { return dataContainer.peekInternalNode(fqn, true); } return nodeSPI.getDelegationTarget(); } protected NodeSPI lookupForEviction(InvocationContext ctx, Fqn fqn) { return ctx.lookUpNode(fqn); } 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 getClass().getSimpleName() + "{" + "fqn=" + fqn + ", recursive=" + recursive + "}"; } public void rollback() { // this is a no-op. } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/write/AbstractVersionedDataCommand.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/write/AbstractVersionedDataCommand.j0000644000175000017500000001027111111047320033142 0ustar moellermoeller/* * 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.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.VersionedDataCommand; import org.jboss.cache.commands.read.AbstractDataCommand; import org.jboss.cache.notifications.Notifier; import org.jboss.cache.optimistic.DataVersion; import org.jboss.cache.optimistic.DataVersioningException; import org.jboss.cache.transaction.GlobalTransaction; /** * Base version of {@link org.jboss.cache.commands.DataCommand} which handles common behaviour * * @author Manik Surtani (manik AT jboss DOT 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); } /** * Utility method to peek a node and throw an exception if the version isn't what is expected. * * @param ctx context to use * @return node peeked, null if nonexistent * @throws org.jboss.cache.optimistic.DataVersioningException * if there is a version mismatch */ protected NodeSPI peekVersioned(InvocationContext ctx) { NodeSPI n = ctx.lookUpNode(fqn); if (n != null && isVersioned() && n.getVersion().newerThan(dataVersion)) { String errMsg = new StringBuilder("Node found, but version is not equal to or less than the expected [").append(dataVersion).append("]. Is [").append(n.getVersion()).append("] instead!").toString(); throw new DataVersioningException(errMsg); } return n; } }jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/write/PutForExternalReadCommand.java0000644000175000017500000000444011111047320033135 0ustar moellermoeller/* * 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.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 AT jboss DOT 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-3.2.8.GA/src/main/java/org/jboss/cache/commands/write/PutKeyValueCommand.java0000644000175000017500000001352111111047320031635 0ustar moellermoeller/* * 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.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.NodeNotExistsException; 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 final boolean trace = log.isTraceEnabled(); /* parametres */ protected Object key; protected Object value; 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("Perform('" + globalTransaction + "', '" + fqn + "', k='" + key + "', v='" + value + "')"); NodeSPI n = ctx.lookUpNode(fqn); if (n == null) throw new NodeNotExistsException("Node " + fqn + " does not exist!"); if (notifier.shouldNotifyOnNodeModified()) { notifier.notifyNodeModified(fqn, true, NodeModifiedEvent.ModificationType.PUT_DATA, n.getDataDirect(), ctx); } Object oldValue = n.putDirect(key, value); if (trace) log.trace("Old value is " + oldValue + ", dataLoaded=" + n.isDataLoaded()); if (notifier.shouldNotifyOnNodeModified()) { Map newData = Collections.singletonMap(key, value); notifier.notifyNodeModified(fqn, false, NodeModifiedEvent.ModificationType.PUT_DATA, newData, ctx); } return 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 + '}'; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/CommandsFactoryImpl.java0000644000175000017500000004453011157534206030723 0ustar moellermoeller/* * 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.commands; 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.legacy.write.CreateNodeCommand; 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.remote.StateTransferControlCommand; 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; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.annotations.Inject; 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; /** * This is the implementation to use for most commands and most locking schemes. */ public class CommandsFactoryImpl implements CommandsFactory { protected RPCManager rpcManager; protected DataContainer dataContainer; protected Notifier notifier; protected InterceptorChain invoker; protected BuddyManager buddyManager; protected TransactionTable transactionTable; protected CacheSPI cacheSpi; protected Configuration configuration; protected TransactionManager txManager; protected BuddyFqnTransformer buddyFqnTransformer; @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 StateTransferControlCommand buildStateTransferControlCommand(boolean enabled) { return new StateTransferControlCommand(enabled); } 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 cmd = new PutKeyValueCommand(gtx, fqn, key, value); cmd.initialize(notifier, dataContainer); return cmd; } public PutForExternalReadCommand buildPutForExternalReadCommand(GlobalTransaction gtx, Fqn fqn, Object key, Object value) { PutForExternalReadCommand cmd = new PutForExternalReadCommand(gtx, fqn, key, value); cmd.initialize(notifier, dataContainer); return cmd; } 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, WriteCommand 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 EvictCommand buildEvictFqnCommand(Fqn fqn) { EvictCommand command = new EvictCommand(fqn); command.initialize(notifier, dataContainer); return command; } public InvalidateCommand buildInvalidateCommand(Fqn fqn) { InvalidateCommand command = new InvalidateCommand(fqn); command.initialize(cacheSpi, dataContainer, notifier); 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 RollbackCommand buildRollbackCommand(GlobalTransaction gtx) { return new RollbackCommand(gtx); } public OptimisticPrepareCommand buildOptimisticPrepareCommand(GlobalTransaction gtx, List modifications, Address address, boolean onePhaseCommit) { return new OptimisticPrepareCommand(gtx, modifications, 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 RemoveNodeCommand buildRemoveNodeCommand(GlobalTransaction gtx, Fqn fqn) { RemoveNodeCommand cmd = new RemoveNodeCommand(gtx, fqn); cmd.initialize(notifier, dataContainer); return cmd; } public ClearDataCommand buildClearDataCommand(GlobalTransaction gtx, Fqn fqn) { ClearDataCommand cmd = new ClearDataCommand(gtx, fqn); cmd.initialize(notifier, dataContainer); return cmd; } public RemoveKeyCommand buildRemoveKeyCommand(GlobalTransaction tx, Fqn fqn, Object key) { RemoveKeyCommand cmd = new RemoveKeyCommand(tx, fqn, key); cmd.initialize(notifier, dataContainer); return cmd; } public MoveCommand buildMoveCommand(Fqn from, Fqn to) { MoveCommand cmd = new MoveCommand(from, to); cmd.initialize(notifier, dataContainer); return cmd; } public CreateNodeCommand buildCreateNodeCommand(Fqn fqn) { throw new UnsupportedOperationException("Not supported in MVCC!"); } 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: { throw new UnsupportedOperationException("CreateNodeCommand is not supported in MVCC!"); } // --- 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: { 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; } case StateTransferControlCommand.METHOD_ID: { StateTransferControlCommand cmd = new StateTransferControlCommand(); cmd.init(rpcManager); command = cmd; break; } default: throw new CacheException("Unknown command id " + id + "!"); } command.setParameters(id, parameters); return command; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/remote/0000755000175000017500000000000011675221761025436 5ustar moellermoeller././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/remote/RemoveFromBuddyGroupCommand.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/remote/RemoveFromBuddyGroupCommand.j0000644000175000017500000000634611111047320033165 0ustar moellermoeller/* * 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.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 + '\'' + '}'; } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/remote/StateTransferControlCommand.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/remote/StateTransferControlCommand.j0000644000175000017500000000245611157534206033241 0ustar moellermoellerpackage org.jboss.cache.commands.remote; import org.jboss.cache.InvocationContext; import org.jboss.cache.RPCManager; import org.jboss.cache.commands.ReplicableCommand; /** * A control command for communication between peers for non-blocking state transfer * * @author Manik Surtani */ public class StateTransferControlCommand implements ReplicableCommand { public static final int METHOD_ID = 49; RPCManager rpcManager; boolean enabled; public StateTransferControlCommand() { } public StateTransferControlCommand(boolean enabled) { this.enabled = enabled; } public void init(RPCManager rpcManager) { this.rpcManager = rpcManager; } public Object perform(InvocationContext ctx) throws Throwable { if (enabled) rpcManager.getFlushTracker().block(); else rpcManager.getFlushTracker().unblock(); return null; } public int getCommandId() { return METHOD_ID; } public Object[] getParameters() { return new Object[]{enabled}; } public void setParameters(int commandId, Object[] parameters) { enabled = (Boolean) parameters[0]; } @Override public String toString() { return "StateTransferControlCommand{" + "enabled=" + enabled + '}'; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/remote/ReplicateCommand.java0000644000175000017500000002275011142423305031501 0ustar moellermoeller/* * 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.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.ArrayList; import java.util.List; import java.util.Collections; /** * 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 final 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) + '}'; } /** * Creates a copy of this command, amking a deep copy of any collections but everything else copied shallow. * * @return a copy */ public ReplicateCommand copy() { ReplicateCommand clone; clone = new ReplicateCommand(); clone.invoker = invoker; clone.modifications = modifications == null ? null : new ArrayList(modifications); clone.singleModification = singleModification; return clone; } public boolean containsCommandType(Class aClass) { if (isSingleCommand()) { return isCommandWithType(getSingleModification(), aClass); } else { for (ReplicableCommand command : getModifications()) { if (isCommandWithType(command, aClass)) return true; } } return false; } private boolean isCommandWithType(ReplicableCommand command, Class aClass) { if (command.getClass().equals(aClass)) return true; if (command instanceof ReplicateCommand) { return ((ReplicateCommand) command).containsCommandType(aClass); } else { return false; } } public boolean removeCommands(List> whereFrom) { boolean foundMods = false; List modifications = isSingleCommand() ? Collections.singletonList(this.singleModification) : this.modifications; for (ReplicableCommand command : modifications) { if (command instanceof ReplicateCommand) { foundMods = foundMods | ((ReplicateCommand)command).removeCommands(whereFrom); } else { foundMods = foundMods | whereFrom.remove(command.getClass()); } } return foundMods; } }././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/remote/AnnounceBuddyPoolNameCommand.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/remote/AnnounceBuddyPoolNameCommand.0000644000175000017500000001030311111047320033102 0ustar moellermoeller/* * 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.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 + '\'' + '}'; } } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/remote/DataGravitationCleanupCommand.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/remote/DataGravitationCleanupCommand0000644000175000017500000002164211241632744033252 0ustar moellermoeller/* * 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.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.buddyreplication.BuddyFqnTransformer; import org.jboss.cache.buddyreplication.BuddyManager; import org.jboss.cache.commands.CommandsFactory; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.write.EvictCommand; import org.jboss.cache.commands.write.RemoveNodeCommand; 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 final 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)) { cleanEmptyDeadRegion(gtx, backup); } else { // JBCACHE-1530 -- check for race where node was moved to a // xxx:DEAD region just after we returned it as a GravitateResult List> deadFqns = buddyManager.getNewlyDeadBackupFqns(backup); if (deadFqns != null) { for (Fqn dead : deadFqns) { result = executeRemove(gtx, dead); if (wasNodeRemoved(result)) { cleanEmptyDeadRegion(gtx, dead); break; } } } } } 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; } private void cleanEmptyDeadRegion(GlobalTransaction gtx, Fqn backupFqn) throws Throwable { // if this is a DIRECT child of a DEAD buddy backup region, then remove the empty dead region structural node. Fqn deadBackupRootFqn = null; if (buddyFqnTransformer.isDeadBackupFqn(backupFqn) && buddyFqnTransformer.isDeadBackupRoot(backup.getAncestor(backupFqn.size() - 2)) && !dataContainer.hasChildren((deadBackupRootFqn = backupFqn.getParent()))) { if (trace) log.trace("Removing dead backup region " + deadBackupRootFqn); executeRemove(gtx, deadBackupRootFqn); // now check the grand parent and see if we are free of versions deadBackupRootFqn = deadBackupRootFqn.getParent(); if (!dataContainer.hasChildren(deadBackupRootFqn)) { if (trace) log.trace("Removing dead backup region " + deadBackupRootFqn); executeRemove(gtx, deadBackupRootFqn); } } } /** * 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-3.2.8.GA/src/main/java/org/jboss/cache/commands/remote/ClusteredGetCommand.java0000644000175000017500000001525711240253605032172 0ustar moellermoeller/* * 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.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; import java.util.Set; /** * 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 final 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); ctx.setBypassUnmarshalling(true); callResults = interceptorChain.invoke(ctx, dataCommand); Set mapCallRes; if (dataCommand instanceof GetChildrenNamesCommand && (mapCallRes = (Set) callResults) != null && mapCallRes.isEmpty()) callResults = null; 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 + '}'; } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/remote/AssignToBuddyGroupCommand.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/remote/AssignToBuddyGroupCommand.jav0000644000175000017500000000760011111047320033154 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/commands/tx/0000755000175000017500000000000011675221763024600 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/tx/OptimisticPrepareCommand.java0000644000175000017500000000657611111047320032377 0ustar moellermoeller/* * 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.commands.tx; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.Visitor; import org.jboss.cache.commands.WriteCommand; import org.jboss.cache.transaction.GlobalTransaction; import org.jgroups.Address; import java.util.ArrayList; import java.util.List; /** * 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; public OptimisticPrepareCommand(GlobalTransaction gtx, List modifications, Address address, boolean onePhaseCommit) { super(gtx, modifications, address, onePhaseCommit); } public OptimisticPrepareCommand() { } @Override public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable { return visitor.visitOptimisticPrepareCommand(ctx, this); } @Override public int getCommandId() { return METHOD_ID; } @Override public Object[] getParameters() { // the null is needed for wire-level compat with pre-command versions return new Object[]{globalTransaction, modifications, null, localAddress, onePhaseCommit}; } /** * A shallow copy of all fields except collections. * * @return a copy of this command */ @Override public OptimisticPrepareCommand copy() { OptimisticPrepareCommand copy = new OptimisticPrepareCommand(); copy.globalTransaction = globalTransaction; copy.localAddress = localAddress; copy.modifications = modifications == null ? null : new ArrayList(modifications); copy.onePhaseCommit = onePhaseCommit; return copy; } @Override @SuppressWarnings("unchecked") public void setParameters(int commandId, Object[] args) { globalTransaction = (GlobalTransaction) args[0]; modifications = (List) args[1]; //args[2] is probably null. localAddress = (Address) args[3]; onePhaseCommit = (Boolean) args[4]; } @Override public String toString() { return "OptimisticPrepareCommand{" + "modifications=" + modifications + ", localAddress=" + localAddress + ", onePhaseCommit=" + onePhaseCommit + ", globalTransaction = " + globalTransaction + '}'; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/tx/CommitCommand.java0000644000175000017500000000343711111047320030155 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/commands/tx/AbstractTransactionCommand.java0000644000175000017500000000547211111047320032677 0ustar moellermoeller/* * 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.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 AT jboss DOT 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-3.2.8.GA/src/main/java/org/jboss/cache/commands/tx/RollbackCommand.java0000644000175000017500000000342111111047320030447 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/commands/tx/PrepareCommand.java0000644000175000017500000001231611111047320030317 0ustar moellermoeller/* * 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.commands.tx; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.Visitor; import org.jboss.cache.commands.WriteCommand; import org.jboss.cache.transaction.GlobalTransaction; import org.jgroups.Address; import java.util.ArrayList; 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; } public PrepareCommand copy() { PrepareCommand copy = new PrepareCommand(); copy.globalTransaction = globalTransaction; copy.localAddress = localAddress; copy.modifications = modifications == null ? null : new ArrayList(modifications); copy.onePhaseCommit = onePhaseCommit; return copy; } @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-3.2.8.GA/src/main/java/org/jboss/cache/commands/OptimisticCommandsFactoryImpl.java0000644000175000017500000001016011111047320032742 0ustar moellermoeller/* * 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.commands; import org.jboss.cache.Fqn; import org.jboss.cache.commands.legacy.read.LegacyGravitateDataCommand; import org.jboss.cache.commands.legacy.write.CreateNodeCommand; import org.jboss.cache.commands.legacy.write.LegacyEvictCommand; import org.jboss.cache.commands.legacy.write.VersionedInvalidateCommand; import org.jboss.cache.commands.read.GravitateDataCommand; import org.jboss.cache.commands.write.EvictCommand; import org.jboss.cache.commands.write.InvalidateCommand; /** * Extends the default commands factory impl for optimistic locking. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 * @deprecated will be removed with opt locking */ @Deprecated @SuppressWarnings("deprecation") public class OptimisticCommandsFactoryImpl extends CommandsFactoryImpl { @Override public EvictCommand buildEvictFqnCommand(Fqn fqn) { EvictCommand command = new LegacyEvictCommand(fqn); command.initialize(notifier, dataContainer); return command; } @Override public InvalidateCommand buildInvalidateCommand(Fqn fqn) { VersionedInvalidateCommand command = new VersionedInvalidateCommand(fqn); command.initialize(txManager); command.initialize(cacheSpi, dataContainer, notifier); return command; } @Override public GravitateDataCommand buildGravitateDataCommand(Fqn fqn, Boolean searchSubtrees) { LegacyGravitateDataCommand command = new LegacyGravitateDataCommand(fqn, searchSubtrees, rpcManager.getLocalAddress()); command.initialize(dataContainer, cacheSpi, buddyFqnTransformer); return command; } @Override public CreateNodeCommand buildCreateNodeCommand(Fqn fqn) { CreateNodeCommand command = new CreateNodeCommand(fqn); command.initialize(dataContainer); return command; } @Override public ReplicableCommand fromStream(int id, Object[] parameters) { ReplicableCommand command; boolean skipSetParams = false; switch (id) { case CreateNodeCommand.METHOD_ID: { CreateNodeCommand returnValue = new CreateNodeCommand(null); returnValue.initialize(dataContainer); command = returnValue; break; } case GravitateDataCommand.METHOD_ID: { LegacyGravitateDataCommand returnValue = new LegacyGravitateDataCommand(rpcManager.getLocalAddress()); returnValue.initialize(dataContainer, cacheSpi, buddyFqnTransformer); command = returnValue; break; } case InvalidateCommand.METHOD_ID: { VersionedInvalidateCommand returnValue = new VersionedInvalidateCommand(null); returnValue.initialize(cacheSpi, dataContainer, notifier); command = returnValue; break; } default: // pass up to superclass command = super.fromStream(id, parameters); skipSetParams = true; } if (!skipSetParams) command.setParameters(id, parameters); return command; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/VisitableCommand.java0000644000175000017500000000340111111047320030203 0ustar moellermoeller/* * 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.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 AT jboss DOT 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-3.2.8.GA/src/main/java/org/jboss/cache/commands/AbstractVisitor.java0000644000175000017500000001532411111047320030114 0ustar moellermoeller/* * 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.commands; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.legacy.write.CreateNodeCommand; 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; 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-3.2.8.GA/src/main/java/org/jboss/cache/commands/legacy/0000755000175000017500000000000011675221767025415 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/legacy/ReversibleCommand.java0000644000175000017500000000326311111047320031635 0ustar moellermoeller/* * 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.commands.legacy; import org.jboss.cache.commands.WriteCommand; /** * A write command that can be reversed by calling a rollback. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated public interface ReversibleCommand extends WriteCommand { /** * 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(); } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/legacy/write/0000755000175000017500000000000011675221767026547 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/legacy/write/PessMoveCommand.java0000644000175000017500000001025611111047320032426 0ustar moellermoeller/* * 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.commands.legacy.write; 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.legacy.ReversibleCommand; import org.jboss.cache.commands.write.MoveCommand; /** * A version of {@link org.jboss.cache.commands.write.MoveCommand} which can be rolled back, for use with * pessimistic locking where changes are made directly on the data structures and may need to be reversed. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated public class PessMoveCommand extends MoveCommand implements ReversibleCommand { public PessMoveCommand() { } public PessMoveCommand(Fqn from, Fqn to) { super(from, to); } /** * Moves a node, from fqn to to, and returns null. * * @param ctx invocation context * @return null */ @Override public Object perform(InvocationContext ctx) { move(fqn, to, false, ctx); return null; } private void adjustFqn(NodeSPI node, Fqn newBase) { Fqn newFqn = Fqn.fromRelativeElements(newBase, node.getFqn().getLastElement()); node.setFqn(newFqn); } private void move(Fqn toMoveFqn, Fqn newParentFqn, boolean skipNotifications, InvocationContext ctx) { // the actual move algorithm. // ctx *could* be null if this is a rollback!!! Sucks big time. NodeSPI newParent = ctx == null ? dataContainer.peek(newParentFqn) : ctx.lookUpNode(newParentFqn); if (newParent == null || newParent.isDeleted()) { throw new NodeNotExistsException("New parent node " + newParentFqn + " does not exist when attempting to move node!!"); } // ctx *could* be null if this is a rollback!!! Sucks big time. NodeSPI node = ctx == null ? dataContainer.peek(toMoveFqn) : ctx.lookUpNode(toMoveFqn); if (node == null || node.isDeleted()) { 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.getParentDirect(); 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 void rollback() { move(Fqn.fromRelativeElements(to, fqn.getLastElement()), fqn.getParent(), true, null); } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/legacy/write/PessRemoveKeyCommand.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/legacy/write/PessRemoveKeyCommand.ja0000644000175000017500000000462411111047320033101 0ustar moellermoeller/* * 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.commands.legacy.write; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.legacy.ReversibleCommand; import org.jboss.cache.commands.write.RemoveKeyCommand; import org.jboss.cache.transaction.GlobalTransaction; /** * A version of {@link org.jboss.cache.commands.write.RemoveKeyCommand} which can be rolled back, for use with * pessimistic locking where changes are made directly on the data structures and may need to be reversed. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated public class PessRemoveKeyCommand extends RemoveKeyCommand implements ReversibleCommand { /* internally used for rollback */ private Object oldValue; public PessRemoveKeyCommand(GlobalTransaction gtx, Fqn fqn, Object key) { super(gtx, fqn, key); } public PessRemoveKeyCommand() { } @Override public Object perform(InvocationContext ctx) { oldValue = super.perform(ctx); return oldValue; } public void rollback() { if (oldValue != null) { NodeSPI targetNode = dataContainer.peek(fqn, false, true); if (targetNode != null) targetNode.putDirect(key, oldValue); } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/legacy/write/CreateNodeCommand.java0000644000175000017500000000671711111047320032705 0ustar moellermoeller/* * 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.commands.legacy.write; 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.WriteCommand; import org.jboss.cache.commands.legacy.ReversibleCommand; 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 AT jboss DOT org) * @since 2.2.0 * @deprecated will be removed when pessimistic locking is removed. */ @Deprecated public class CreateNodeCommand extends AbstractDataCommand implements WriteCommand, ReversibleCommand { public static final int METHOD_ID = 48; protected 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 void rollback() { if (newlyCreated != null) { for (Fqn f : newlyCreated) dataContainer.removeFromDataStructure(f, true); } } List getNewlyCreated() { return newlyCreated; } public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable { return visitor.visitCreateNodeCommand(ctx, this); } @Override public String toString() { return "CreateNodeCommand{" + "fqn=" + fqn + ", newlyCreated=" + newlyCreated + '}'; } } ././@LongLink0000000000000000000000000000016000000000000011562 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/legacy/write/PessPutForExternalReadCommand.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/legacy/write/PessPutForExternalReadC0000644000175000017500000000511211111047320033115 0ustar moellermoeller/* * 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.commands.legacy.write; 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.legacy.ReversibleCommand; import org.jboss.cache.commands.write.PutForExternalReadCommand; import org.jboss.cache.transaction.GlobalTransaction; /** * A version of {@link org.jboss.cache.commands.write.PutForExternalReadCommand} which can be rolled back, for use with * pessimistic locking where changes are made directly on the data structures and may need to be reversed. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated public class PessPutForExternalReadCommand extends PutForExternalReadCommand implements ReversibleCommand { protected Object oldValue; public PessPutForExternalReadCommand(GlobalTransaction gtx, Fqn fqn, Object key, Object value) { super(gtx, fqn, key, value); } public PessPutForExternalReadCommand() { } @Override public Object perform(InvocationContext ctx) { oldValue = super.perform(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); } } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/legacy/write/PessPutKeyValueCommand.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/legacy/write/PessPutKeyValueCommand.0000644000175000017500000000504011111047320033067 0ustar moellermoeller/* * 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.commands.legacy.write; 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.legacy.ReversibleCommand; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.transaction.GlobalTransaction; /** * A version of {@link org.jboss.cache.commands.write.PutKeyValueCommand} which can be rolled back, for use with * pessimistic locking where changes are made directly on the data structures and may need to be reversed. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated public class PessPutKeyValueCommand extends PutKeyValueCommand implements ReversibleCommand { protected Object oldValue; public PessPutKeyValueCommand(GlobalTransaction gtx, Fqn fqn, Object key, Object value) { super(gtx, fqn, key, value); } public PessPutKeyValueCommand() { } @Override public Object perform(InvocationContext ctx) { oldValue = super.perform(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); } } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/legacy/write/PessClearDataCommand.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/legacy/write/PessClearDataCommand.ja0000644000175000017500000000563411111047320033015 0ustar moellermoeller/* * 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.commands.legacy.write; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.legacy.ReversibleCommand; import org.jboss.cache.commands.write.ClearDataCommand; import org.jboss.cache.transaction.GlobalTransaction; import java.util.HashMap; /** * A version of {@link org.jboss.cache.commands.write.ClearDataCommand} which can be rolled back, for use with * pessimistic locking where changes are made directly on the data structures and may need to be reversed. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated public class PessClearDataCommand extends ClearDataCommand implements ReversibleCommand { private HashMap originalData; public PessClearDataCommand(GlobalTransaction gtx, Fqn fqn) { super(gtx, fqn); } public PessClearDataCommand() { } @Override public Object perform(InvocationContext ctx) { if (globalTransaction != null) { NodeSPI n = ctx.lookUpNode(fqn); originalData = n == null ? null : new HashMap(n.getDataDirect()); } return super.perform(ctx); } public void rollback() { if (trace) log.trace("rollback(" + globalTransaction + ", \"" + fqn + "\", " + originalData + ")"); if (originalData != null && !originalData.isEmpty()) { 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); } } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/legacy/write/PessPutDataMapCommand.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/legacy/write/PessPutDataMapCommand.j0000644000175000017500000000543111111047320033027 0ustar moellermoeller/* * 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.commands.legacy.write; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.legacy.ReversibleCommand; import org.jboss.cache.commands.write.PutDataMapCommand; import org.jboss.cache.transaction.GlobalTransaction; import java.util.HashMap; import java.util.Map; /** * A version of {@link org.jboss.cache.commands.write.PutDataMapCommand} which can be rolled back, for use with * pessimistic locking where changes are made directly on the data structures and may need to be reversed. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated public class PessPutDataMapCommand extends PutDataMapCommand implements ReversibleCommand { Map oldData; public PessPutDataMapCommand(GlobalTransaction globalTransaction, Fqn fqn, Map data) { super(globalTransaction, fqn, data); } public PessPutDataMapCommand() { } @Override public Object perform(InvocationContext ctx) { // first get a hold of existing data. NodeSPI node = ctx.lookUpNode(fqn); Map existingData = node == null ? null : node.getDataDirect(); if (existingData != null && !existingData.isEmpty()) { oldData = new HashMap(existingData); // defensive copy } return super.perform(ctx); } 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); } } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/legacy/write/PessRemoveNodeCommand.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/legacy/write/PessRemoveNodeCommand.j0000644000175000017500000000767611111047320033107 0ustar moellermoeller/* * 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.commands.legacy.write; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.legacy.ReversibleCommand; import org.jboss.cache.commands.write.RemoveNodeCommand; import org.jboss.cache.transaction.GlobalTransaction; import java.util.HashMap; import java.util.Map; /** * A version of {@link org.jboss.cache.commands.write.RemoveNodeCommand} which can be rolled back, for use with * pessimistic locking where changes are made directly on the data structures and may need to be reversed. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated public class PessRemoveNodeCommand extends RemoveNodeCommand implements ReversibleCommand { protected Map originalData; public PessRemoveNodeCommand(GlobalTransaction globalTransaction, Fqn fqn) { super(globalTransaction, fqn); } public PessRemoveNodeCommand() { } @Override public Object perform(InvocationContext ctx) { NodeSPI targetNode = peekVersioned(ctx); if (targetNode != null) { Map data = targetNode.getDataDirect(); if (data != null && !data.isEmpty()) originalData = new HashMap(data); } boolean found = (Boolean) super.perform(ctx); // now record rollback info. if (globalTransaction != null && found) { NodeSPI parentNode = targetNode.getParentDirect(); prepareForRollback(parentNode); } return found; } @Override protected void recursivelyMarkAsRemoved(NodeSPI node, InvocationContext ctx) { node.markAsDeleted(true, true); } private void prepareForRollback(NodeSPI parentNode) { parentFqn = parentNode.getFqn(); Map targetData = targetNode.getDataDirect(); if (!targetData.isEmpty()) { originalData = new HashMap(targetNode.getDataDirect()); } } 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); } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/legacy/write/LegacyEvictCommand.java0000644000175000017500000000575011111047320033067 0ustar moellermoeller/* * 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.commands.legacy.write; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.write.EvictCommand; import java.util.Collection; /** * Evict command for legacy locking schemes like OL and PL. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated public class LegacyEvictCommand extends EvictCommand { public LegacyEvictCommand(Fqn fqn) { super(fqn); } public LegacyEvictCommand() { } @Override protected NodeSPI lookupForEviction(InvocationContext ctx, Fqn fqn) { return dataContainer.peek(fqn, false, true); } @Override protected Collection getRecursiveEvictionNodes() { return dataContainer.getNodesForEviction(fqn, true); } @Override protected boolean evictNode(Fqn fqn, InvocationContext ctx, NodeSPI node) { notifier.notifyNodeEvicted(fqn, true, ctx); try { if (node == null) return true; if (node.hasChildrenDirect() || fqn.isRoot()) { if (trace) log.trace("removing DATA as node has children: evict(" + fqn + ")"); node.clearDataDirect(); node.setDataLoaded(false); return false; } else { if (trace) log.trace("removing NODE as it is a leaf: evict(" + fqn + ")"); NodeSPI parentNode = lookupForEviction(ctx, fqn.getParent()); if (parentNode != null) { parentNode.removeChildDirect(fqn.getLastElement()); parentNode.setChildrenLoaded(false); } node.setValid(false, false); node.markAsDeleted(true); return true; } } finally { notifier.notifyNodeEvicted(fqn, false, ctx); } } } ././@LongLink0000000000000000000000000000015500000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/legacy/write/VersionedInvalidateCommand.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/legacy/write/VersionedInvalidateComm0000644000175000017500000001504311111047320033220 0ustar moellermoeller/* * 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.commands.legacy.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.commands.write.InvalidateCommand; import org.jboss.cache.config.Option; import org.jboss.cache.optimistic.DataVersion; import org.jboss.cache.optimistic.DataVersioningException; 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 * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated public class VersionedInvalidateCommand extends InvalidateCommand implements VersionedDataCommand { private static final Log log = LogFactory.getLog(VersionedInvalidateCommand.class); private static final boolean trace = log.isTraceEnabled(); /* dependencies */ private TransactionManager transactionManager; /** * Params. */ protected GlobalTransaction globalTransaction; private DataVersion dataVersion; public VersionedInvalidateCommand(Fqn fqn) { super(fqn); } public VersionedInvalidateCommand() { } 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, true, 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; } else if (node.getVersion() == null) { throw new NullPointerException("Node " + node.getFqn() + " has a null data version, and is of type " + node.getClass().getSimpleName() + ". This command expects versioned nodes."); } else if (dataVersion != null && node.getVersion().newerThan(dataVersion)) // dataVersion *could* be null if the invalidate was triggered by removing a node that did not exist in the first place. { String errMsg = new StringBuilder("Node found, but version is not equal to or less than the expected [").append(dataVersion).append("]. Is [").append(node.getVersion()).append("] instead!").toString(); log.warn(errMsg); throw new DataVersioningException(errMsg); } removeData(node, ctx); invalidateNode(node); node.setVersion(dataVersion); 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); } } protected void removeData(NodeSPI n, InvocationContext ctx) throws CacheException { notifier.notifyNodeInvalidated(fqn, true, ctx); n.clearDataDirect(); n.setDataLoaded(false); notifier.notifyNodeInvalidated(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]; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/legacy/read/0000755000175000017500000000000011675221770026322 5ustar moellermoeller././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/legacy/read/LegacyGravitateDataCommand.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/legacy/read/LegacyGravitateDataComma0000644000175000017500000000460011111047320033045 0ustar moellermoeller/* * 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.commands.legacy.read; import org.jboss.cache.Fqn; import org.jboss.cache.NodeSPI; import org.jboss.cache.buddyreplication.BuddyManager; import org.jboss.cache.commands.read.GravitateDataCommand; import org.jgroups.Address; import java.util.Set; /** * Legacy version that uses old data container peeks * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated public class LegacyGravitateDataCommand extends GravitateDataCommand { public LegacyGravitateDataCommand(Fqn fqn, boolean searchSubtrees, Address localAddress) { super(fqn, searchSubtrees, localAddress); } public LegacyGravitateDataCommand(Address localAddress) { super(localAddress); } /** * @return a Set of child node names that hang directly off the backup tree root, or null if the backup tree root doesn't exist. */ @Override @SuppressWarnings("unchecked") protected Set getBackupRoots() { NodeSPI backupSubtree = dataContainer.peek(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN); if (backupSubtree == null) return null; return backupSubtree.getChildrenNamesDirect(); } void setSearchSubtrees(boolean searchSubtrees) { this.searchSubtrees = searchSubtrees; } } ././@LongLink0000000000000000000000000000015500000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/legacy/read/PessGetChildrenNamesCommand.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/legacy/read/PessGetChildrenNamesComm0000644000175000017500000000544411111047320033055 0ustar moellermoeller/* * 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.commands.legacy.read; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.Node; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.read.GetChildrenNamesCommand; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated public class PessGetChildrenNamesCommand extends GetChildrenNamesCommand { public PessGetChildrenNamesCommand() { } public PessGetChildrenNamesCommand(Fqn fqn) { super(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. */ @SuppressWarnings("unchecked") @Override public Object perform(InvocationContext ctx) { NodeSPI n = fqn == null ? null : ctx.lookUpNode(fqn); if (n == null || n.isDeleted()) return null; Map> childrenMap = n.getChildrenMapDirect(); Collection> children = (Collection>) (childrenMap.isEmpty() ? Collections.emptySet() : childrenMap.values()); return getCorrectedChildNames(children); } private Set getCorrectedChildNames(Collection> children) { // prune deleted children - JBCACHE-1136 Set childNames = new HashSet(); for (NodeSPI child : children) { if (!child.isDeleted()) { Object e = child.getFqn().getLastElement(); childNames.add(e); } } return childNames; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/VersionedDataCommand.java0000644000175000017500000000370211111047320031015 0ustar moellermoeller/* * 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.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 AT jboss DOT org) * @since 2.2.0 */ public interface VersionedDataCommand extends WriteCommand { /** * @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-3.2.8.GA/src/main/java/org/jboss/cache/commands/PessimisticCommandsFactoryImpl.java0000644000175000017500000001774311111047320033130 0ustar moellermoeller/* * 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.commands; import org.jboss.cache.Fqn; import org.jboss.cache.commands.legacy.read.PessGetChildrenNamesCommand; import org.jboss.cache.commands.legacy.write.PessClearDataCommand; import org.jboss.cache.commands.legacy.write.PessMoveCommand; import org.jboss.cache.commands.legacy.write.PessPutDataMapCommand; import org.jboss.cache.commands.legacy.write.PessPutForExternalReadCommand; import org.jboss.cache.commands.legacy.write.PessPutKeyValueCommand; import org.jboss.cache.commands.legacy.write.PessRemoveKeyCommand; import org.jboss.cache.commands.legacy.write.PessRemoveNodeCommand; import org.jboss.cache.commands.read.GetChildrenNamesCommand; import org.jboss.cache.commands.write.ClearDataCommand; 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 org.jboss.cache.transaction.GlobalTransaction; import java.util.Map; /** * This specific implementation of {@link CommandsFactory} specifically creates * pessimistic commands where appropriate, with the ability to roll back. * * @author Manik Surtani (manik AT jboss DOT org) * @see org.jboss.cache.commands.legacy.ReversibleCommand * @since 3.0 * @deprecated will be removed with possimistic locking */ @Deprecated @SuppressWarnings("deprecation") public class PessimisticCommandsFactoryImpl extends OptimisticCommandsFactoryImpl { @Override public GetChildrenNamesCommand buildGetChildrenNamesCommand(Fqn fqn) { GetChildrenNamesCommand command = new PessGetChildrenNamesCommand(fqn); command.initialize(dataContainer); return command; } @Override public PutDataMapCommand buildPutDataMapCommand(GlobalTransaction gtx, Fqn fqn, Map data) { PutDataMapCommand cmd = new PessPutDataMapCommand(gtx, fqn, data); cmd.initialize(notifier, dataContainer); return cmd; } @Override public PutKeyValueCommand buildPutKeyValueCommand(GlobalTransaction gtx, Fqn fqn, Object key, Object value) { PutKeyValueCommand cmd = new PessPutKeyValueCommand(gtx, fqn, key, value); cmd.initialize(notifier, dataContainer); return cmd; } @Override public PutForExternalReadCommand buildPutForExternalReadCommand(GlobalTransaction gtx, Fqn fqn, Object key, Object value) { PutForExternalReadCommand cmd = new PessPutForExternalReadCommand(gtx, fqn, key, value); cmd.initialize(notifier, dataContainer); return cmd; } @Override public RemoveNodeCommand buildRemoveNodeCommand(GlobalTransaction gtx, Fqn fqn) { RemoveNodeCommand cmd = new PessRemoveNodeCommand(gtx, fqn); cmd.initialize(notifier, dataContainer); return cmd; } @Override public ClearDataCommand buildClearDataCommand(GlobalTransaction gtx, Fqn fqn) { ClearDataCommand cmd = new PessClearDataCommand(gtx, fqn); cmd.initialize(notifier, dataContainer); return cmd; } @Override public RemoveKeyCommand buildRemoveKeyCommand(GlobalTransaction tx, Fqn fqn, Object key) { RemoveKeyCommand cmd = new PessRemoveKeyCommand(tx, fqn, key); cmd.initialize(notifier, dataContainer); return cmd; } @Override public MoveCommand buildMoveCommand(Fqn from, Fqn to) { MoveCommand cmd = new PessMoveCommand(from, to); cmd.initialize(notifier, dataContainer); return cmd; } @Override public InvalidateCommand buildInvalidateCommand(Fqn fqn) { InvalidateCommand command = new InvalidateCommand(fqn); command.initialize(cacheSpi, dataContainer, notifier); return command; } @Override public ReplicableCommand fromStream(int id, Object[] parameters) { ReplicableCommand command; boolean skipSetParams = false; switch (id) { case GetChildrenNamesCommand.METHOD_ID: { GetChildrenNamesCommand returnValue = new PessGetChildrenNamesCommand(); returnValue.initialize(dataContainer); command = returnValue; break; } case MoveCommand.METHOD_ID: { MoveCommand returnValue = new PessMoveCommand(); 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 PessPutDataMapCommand(); returnValue.initialize(notifier, dataContainer); command = returnValue; break; } case PutKeyValueCommand.METHOD_ID: case PutKeyValueCommand.VERSIONED_METHOD_ID: { PutKeyValueCommand returnValue = new PessPutKeyValueCommand(); returnValue.initialize(notifier, dataContainer); command = returnValue; break; } case PutForExternalReadCommand.METHOD_ID: case PutForExternalReadCommand.VERSIONED_METHOD_ID: { PutForExternalReadCommand returnValue = new PessPutForExternalReadCommand(); returnValue.initialize(notifier, dataContainer); command = returnValue; break; } case ClearDataCommand.METHOD_ID: case ClearDataCommand.VERSIONED_METHOD_ID: { ClearDataCommand returnValue = new PessClearDataCommand(); returnValue.initialize(notifier, dataContainer); command = returnValue; break; } case RemoveKeyCommand.METHOD_ID: case RemoveKeyCommand.VERSIONED_METHOD_ID: { RemoveKeyCommand returnValue = new PessRemoveKeyCommand(); returnValue.initialize(notifier, dataContainer); command = returnValue; break; } case RemoveNodeCommand.METHOD_ID: case RemoveNodeCommand.VERSIONED_METHOD_ID: { RemoveNodeCommand returnValue = new PessRemoveNodeCommand(); returnValue.initialize(notifier, dataContainer); command = returnValue; break; } case InvalidateCommand.METHOD_ID: { InvalidateCommand returnValue = new InvalidateCommand(null); returnValue.initialize(cacheSpi, dataContainer, notifier); command = returnValue; break; } default: // pass up to superclass command = super.fromStream(id, parameters); skipSetParams = true; } if (!skipSetParams) command.setParameters(id, parameters); return command; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/CommandsFactory.java0000644000175000017500000001433111157534206030075 0ustar moellermoeller/* * 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.commands; import org.jboss.cache.Fqn; import org.jboss.cache.buddyreplication.BuddyGroup; import org.jboss.cache.commands.legacy.write.CreateNodeCommand; 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.remote.StateTransferControlCommand; 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; import org.jboss.cache.transaction.GlobalTransaction; import org.jgroups.Address; 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
     * 
    *

    * Note: As of 3.0, this is now an interface. *

    * * @author Mircea.Markus@jboss.com * @since 2.2 */ public interface CommandsFactory { PutDataMapCommand buildPutDataMapCommand(GlobalTransaction gtx, Fqn fqn, Map data); PutKeyValueCommand buildPutKeyValueCommand(GlobalTransaction gtx, Fqn fqn, Object key, Object value); PutForExternalReadCommand buildPutForExternalReadCommand(GlobalTransaction gtx, Fqn fqn, Object key, Object value); ReplicateCommand buildReplicateCommand(ReplicableCommand command); ReplicateCommand buildReplicateCommand(List modifications); PrepareCommand buildPrepareCommand(GlobalTransaction gtx, WriteCommand command, boolean onePhaseCommit); PrepareCommand buildPrepareCommand(GlobalTransaction gtx, List modifications, Address address, boolean onePhaseCommit); CommitCommand buildCommitCommand(GlobalTransaction gtx); DataGravitationCleanupCommand buildDataGravitationCleanupCommand(Fqn primaryFqn, Fqn backupFqn); GravitateDataCommand buildGravitateDataCommand(Fqn fqn, Boolean searchSubtrees); RemoveNodeCommand buildRemoveNodeCommand(GlobalTransaction gtx, Fqn fqn); ClearDataCommand buildClearDataCommand(GlobalTransaction gtx, Fqn fqn); EvictCommand buildEvictFqnCommand(Fqn fqn); InvalidateCommand buildInvalidateCommand(Fqn fqn); RemoveKeyCommand buildRemoveKeyCommand(GlobalTransaction tx, Fqn fqn, Object key); GetDataMapCommand buildGetDataMapCommand(Fqn fqn); ExistsCommand buildExistsNodeCommand(Fqn fqn); GetKeyValueCommand buildGetKeyValueCommand(Fqn fqn, Object key, boolean sendNodeEvent); GetNodeCommand buildGetNodeCommand(Fqn fqn); GetKeysCommand buildGetKeysCommand(Fqn fqn); GetChildrenNamesCommand buildGetChildrenNamesCommand(Fqn fqn); MoveCommand buildMoveCommand(Fqn from, Fqn to); RollbackCommand buildRollbackCommand(GlobalTransaction gtx); OptimisticPrepareCommand buildOptimisticPrepareCommand(GlobalTransaction gtx, List modifications, Address address, boolean onePhaseCommit); AnnounceBuddyPoolNameCommand buildAnnounceBuddyPoolNameCommand(Address address, String buddyPoolName); RemoveFromBuddyGroupCommand buildRemoveFromBuddyGroupCommand(String groupName); AssignToBuddyGroupCommand buildAssignToBuddyGroupCommand(BuddyGroup group, Map state); ClusteredGetCommand buildClusteredGetCommand(Boolean searchBackupSubtrees, DataCommand dataCommand); CreateNodeCommand buildCreateNodeCommand(Fqn fqn); /** * 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 */ ReplicableCommand fromStream(int id, Object[] parameters); StateTransferControlCommand buildStateTransferControlCommand(boolean b); } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/WriteCommand.java0000644000175000017500000000327711111047320027366 0ustar moellermoeller/* * 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.commands; import org.jboss.cache.transaction.GlobalTransaction; /** * A write command that has a reference to a {@link org.jboss.cache.transaction.GlobalTransaction}. * * @author Mircea.Markus@jboss.com * @author Manik Surtani (manik AT jboss DOT org) * @since 2.2.0 */ public interface WriteCommand extends DataCommand { /** * @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-3.2.8.GA/src/main/java/org/jboss/cache/commands/read/0000755000175000017500000000000011675221773025061 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/read/GravitateDataCommand.java0000644000175000017500000002431211241335610031726 0ustar moellermoeller/* * 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.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.InternalNode; import org.jboss.cache.InvocationContext; import org.jboss.cache.Node; import org.jboss.cache.NodeNotValidException; 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.ArrayList; 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 */ protected boolean searchSubtrees; private Address localAddress; private static final Log log = LogFactory.getLog(GravitateDataCommand.class); private static final 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) { // TODO: Test this with MVCC. // 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."); // need to loop through backupSubtree's children Set allGroupNames = getBackupRoots(); if (allGroupNames != null) { // JBCACHE-1530 -- give preference to non-dead owners List sortedGroupNames = sortBackupRoots(allGroupNames); for (Object groupName : sortedGroupNames) { // 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, (String) 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 { try { actualNode.getData(); // and children! actualNode.getChildrenNames(); } catch (NodeNotValidException e) { if (trace) log.trace("Found node " + actualNode.getFqn() + " but it is not valid. Returning 'no data found'", e); return GravitateResult.noDataFound(); } } if (backupNodeFqn == null && searchSubtrees) { backupNodeFqn = buddyFqnTransformer.getBackupFqn(buddyFqnTransformer.getGroupNameFromAddress(localAddress), fqn); } List list = dataContainer.buildNodeData(new LinkedList(), (NodeSPI) actualNode, false); return GravitateResult.subtreeResult(list, backupNodeFqn); } catch (RuntimeException re) { if (trace) log.trace("Caught throwable", re); throw re; } finally { ctx.setOriginLocal(true); } } /** * @return a Set of child node names that hang directly off the backup tree root, or null if the backup tree root doesn't exist. */ protected Set getBackupRoots() { InternalNode backupSubtree = dataContainer.peekInternalNode(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, false); if (backupSubtree == null) return null; return backupSubtree.getChildrenNames(); } protected List sortBackupRoots(Set allGroupNames) { List sorted = new ArrayList(allGroupNames.size()); for (Object child : allGroupNames) { if (child instanceof String && ((String) child).endsWith(":DEAD") == false) { sorted.add(0, child); } else { sorted.add(child); } } return sorted; } 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 + '}'; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/read/GetKeyValueCommand.java0000644000175000017500000001131411111047320031363 0ustar moellermoeller/* * 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.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 final 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) { NodeSPI n = ctx.lookUpNode(fqn); if (n == null) { if (trace) log.trace("Node not found"); return null; } if (n.isDeleted()) { if (trace) log.trace("Node has been deleted and is of type " + n.getClass().getSimpleName()); return null; } if (sendNodeEvent) notifier.notifyNodeVisited(fqn, true, ctx); Object result = n.getDirect(key); if (trace) log.trace("Found value " + result); 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-3.2.8.GA/src/main/java/org/jboss/cache/commands/read/GetNodeCommand.java0000644000175000017500000000534511111047320030532 0ustar moellermoeller/* * 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.commands.read; 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; /** * 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; private static final Log log = LogFactory.getLog(GetNodeCommand.class); private static final boolean trace = log.isTraceEnabled(); 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) { NodeSPI node = ctx.lookUpNode(fqn); if (node != null && node.isDeleted()) { if (trace) log.trace("Node of type [" + node.getClass().getSimpleName() + "] found but marked as deleted in current scope. Returning null."); return null; } if (trace) log.trace("Found node, returning " + node); return node; } public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable { return visitor.visitGetNodeCommand(ctx, this); } public int getCommandId() { return METHOD_ID; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/read/GetDataMapCommand.java0000644000175000017500000000462711111047320031156 0ustar moellermoeller/* * 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.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.Immutables; /** * 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 = ctx.lookUpNode(fqn); if (n == null || n.isDeleted()) return null; return Immutables.immutableMapCopy(n.getDataDirect()); } public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable { return visitor.visitGetDataMapCommand(ctx, this); } public int getCommandId() { return METHOD_ID; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/read/GetKeysCommand.java0000644000175000017500000000452311111047320030555 0ustar moellermoeller/* * 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.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. */ public Object perform(InvocationContext ctx) { NodeSPI n = ctx.lookUpNode(fqn); if (n == null || n.isDeleted()) 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-3.2.8.GA/src/main/java/org/jboss/cache/commands/read/GetChildrenNamesCommand.java0000644000175000017500000001055311166436061032374 0ustar moellermoeller/* * 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.commands.read; import org.jboss.cache.Fqn; import org.jboss.cache.InternalNode; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.Visitor; import org.jboss.cache.mvcc.ReadCommittedNode; 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. */ @SuppressWarnings("unchecked") public Object perform(InvocationContext ctx) { ReadCommittedNode n = (ReadCommittedNode) (fqn == null ? null : ctx.lookUpNode(fqn)); if (n == null || n.isDeleted()) return null; Map> childrenMap = n.getDelegationTarget().getChildrenMap(); Collection children = (Collection) (childrenMap.isEmpty() ? Collections.emptySet() : childrenMap.values()); return getCorrectedChildNames(children, ctx); } /** * Cleans the collection of children passed in to extract child names into a Set. This implementation provides * additional filtering such as removing known deleted children and adding known created ones. * * @param children children set. Must not be null. * @param ctx invocation context * @return a set of valid children names. */ @SuppressWarnings("unchecked") private Set getCorrectedChildNames(Collection children, InvocationContext ctx) { Set childNames = new HashSet(); for (InternalNode realChild : children) { Fqn childFqn = realChild.getFqn(); NodeSPI childNode = ctx.lookUpNode(childFqn); // deletion flag should be checked on what we get from the ctx. // if childNode is null then it hasn't been removed in the current scope and hence should be included in this list. if (childNode == null || !childNode.isDeleted()) childNames.add(childFqn.getLastElement()); } // now check for any *new* children added. for (Map.Entry n : ctx.getLookedUpNodes().entrySet()) { Fqn childFqn = n.getKey(); if (childFqn.isDirectChildOf(fqn)) { ReadCommittedNode childNode = (ReadCommittedNode) n.getValue(); if (childNode != null && childNode.isCreated()) childNames.add(childFqn.getLastElement()); } } return childNames; } public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable { return visitor.visitGetChildrenNamesCommand(ctx, this); } public int getCommandId() { return METHOD_ID; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/commands/read/AbstractDataCommand.java0000644000175000017500000000514211111047320031535 0ustar moellermoeller/* * 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.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 AT jboss DOT 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-3.2.8.GA/src/main/java/org/jboss/cache/commands/read/ExistsCommand.java0000644000175000017500000000503511111047320030460 0ustar moellermoeller/* * 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.commands.read; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; 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) { NodeSPI node = ctx.lookUpNode(fqn); return node != null && !node.isDeleted(); } public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable { return visitor.visitExistsNodeCommand(ctx, this); } public int getCommandId() { return METHOD_ID; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/RegionEmptyException.java0000644000175000017500000000313011111047320027301 0ustar moellermoeller/* * 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; /** * Exception to represent a region being empty when state was expected in that region. * * @author Manik Surtani (manik AT jboss DOT 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-3.2.8.GA/src/main/java/org/jboss/cache/config/0000755000175000017500000000000011675222010023573 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/config/CloneableConfigurationComponent.java0000644000175000017500000000262511111047320032734 0ustar moellermoeller/* * 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.config; import java.io.Serializable; /** * Interface for all configurable components * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ public interface CloneableConfigurationComponent extends Serializable, Cloneable { CloneableConfigurationComponent clone() throws CloneNotSupportedException; } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/config/ConfigurationRegistry.java0000755000175000017500000000575011111047320031002 0ustar moellermoeller/* * 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.config; import java.util.Set; /** * A registry for {@link Configuration}s. * * @author Brian Stansberry * @version $Revision: 7168 $ */ 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 a set of configuration names */ Set getConfigurationNames(); }jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/config/XmlParsingConfigurationRegistry.java0000755000175000017500000000715511111047320033010 0ustar moellermoeller/* * 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.config; import org.jboss.cache.config.parsing.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: 7168 $ */ 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-3.2.8.GA/src/main/java/org/jboss/cache/config/EvictionConfig.java0000644000175000017500000003214511111047320027343 0ustar moellermoeller/* * 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.config; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Fqn; import org.jboss.cache.RegionManagerImpl; import org.jboss.cache.annotations.Compat; import org.jboss.cache.eviction.DefaultEvictionActionPolicy; import org.jboss.cache.eviction.EvictionAlgorithm; import org.jboss.cache.eviction.EvictionPolicy; import org.jboss.cache.eviction.ModernizablePolicy; import org.jboss.cache.util.Util; import java.util.LinkedList; import java.util.List; import java.util.concurrent.TimeUnit; public class EvictionConfig extends ConfigurationComponent { /** * The serialVersionUID */ private static final long serialVersionUID = -7979639000026975201L; public static final int WAKEUP_DEFAULT = 5000; public static final int EVENT_QUEUE_SIZE_DEFAULT = 200000; public static final String EVICTION_ACTION_POLICY_CLASS_DEFAULT = DefaultEvictionActionPolicy.class.getName(); /** * value expressed in millis */ @Dynamic private long wakeupInterval = WAKEUP_DEFAULT; private int defaultEventQueueSize = EVENT_QUEUE_SIZE_DEFAULT; // Dynamic to support runtime adds/removes of regions @Dynamic private List evictionRegionConfigs; private EvictionRegionConfig defaultEvictionRegionConfig; @Deprecated private String defaultEvictionPolicyClass; public EvictionConfig() { evictionRegionConfigs = new LinkedList(); defaultEvictionRegionConfig = new EvictionRegionConfig(Fqn.ROOT); defaultEvictionRegionConfig.setEventQueueSize(EVENT_QUEUE_SIZE_DEFAULT); defaultEvictionRegionConfig.setEvictionActionPolicyClassName(DefaultEvictionActionPolicy.class.getName()); } /** * @deprecated Use {@link #EvictionConfig(EvictionRegionConfig)} instead. */ @Deprecated public EvictionConfig(String defaultEvictionPolicyClass) { this(); setDefaultEvictionPolicyClass(defaultEvictionPolicyClass); } public EvictionConfig(EvictionRegionConfig defaultEvictionRegionConfig) { evictionRegionConfigs = new LinkedList(); try { this.defaultEvictionRegionConfig = defaultEvictionRegionConfig.clone(); } catch (CloneNotSupportedException e) { throw new ConfigurationException(e); } this.defaultEvictionRegionConfig.setEventQueueSize(EVENT_QUEUE_SIZE_DEFAULT); if (this.defaultEvictionRegionConfig.getEvictionActionPolicyClassName() == null) this.defaultEvictionRegionConfig.setEvictionActionPolicyClassName(DefaultEvictionActionPolicy.class.getName()); } public EvictionConfig(EvictionRegionConfig defaultEvictionRegionConfig, int wakeupInterval) { this(defaultEvictionRegionConfig); this.wakeupInterval = wakeupInterval; } public boolean isValidConfig() { return (defaultEvictionRegionConfig != null && defaultEvictionRegionConfig.getEvictionActionPolicyClassName() != null && defaultEvictionRegionConfig.getEvictionAlgorithmConfig() != null) || (evictionRegionConfigs != null && evictionRegionConfigs.size() > 0); } public EvictionRegionConfig getDefaultEvictionRegionConfig() { return defaultEvictionRegionConfig; } public void setDefaultEvictionRegionConfig(EvictionRegionConfig defaultEvictionRegionConfig) { this.defaultEvictionRegionConfig = defaultEvictionRegionConfig; this.defaultEvictionRegionConfig.setEventQueueSizeIfUnset(EVENT_QUEUE_SIZE_DEFAULT); } /** * @deprecated use {@link #getDefaultEvictionRegionConfig()} instead. */ @Deprecated public String getDefaultEvictionPolicyClass() { return defaultEvictionPolicyClass; } /** * @deprecated use {@link #setDefaultEvictionRegionConfig(EvictionRegionConfig)} instead. */ @Deprecated public void setDefaultEvictionPolicyClass(String defaultEvictionPolicyClass) { assertIsTransformable(defaultEvictionPolicyClass); try { EvictionPolicy policy = (EvictionPolicy) Util.getInstance(defaultEvictionPolicyClass); EvictionAlgorithm ea = policy.getEvictionAlgorithm(); defaultEvictionRegionConfig.setEvictionAlgorithmConfig(Util.getInstance(ea.getConfigurationClass())); this.defaultEvictionPolicyClass = defaultEvictionPolicyClass; } catch (Exception e) { throw new ConfigurationException(e); } } /** * Tests whether an eviction policy class can be transformed from the legacy format to the new interfaces introduced * in 3.x. If not, this methow throws a {@link UnsupportedEvictionImplException}. * * @param evictionPolicyClass class to test * @throws UnsupportedEvictionImplException * thrown if the eviction policy passed in is unusable */ @Compat(notes = "here to test legacy impls") @Deprecated public static final void assertIsTransformable(String evictionPolicyClass) throws UnsupportedEvictionImplException { boolean throwException = true; if (evictionPolicyClass.indexOf("org.jboss.cache.eviction") > -1) { EvictionPolicy ep = null; try { ep = (EvictionPolicy) Util.getInstance(evictionPolicyClass); if (ep instanceof ModernizablePolicy) throwException = false; } catch (Exception e) { // do nothing Log l = LogFactory.getLog(EvictionConfig.class); if (l.isTraceEnabled()) l.trace(e); } } if (throwException) throw new UnsupportedEvictionImplException("Unsupported custom eviction policy [" + evictionPolicyClass + "]. Starting with 3.x the eviction API has changed, the code needs to be manually migrated. Please re-implement your custom policy."); } /** * Creates an EvictionRegionConfig for the * {@link org.jboss.cache.RegionManagerImpl#DEFAULT_REGION "_default_"} region using the * {@link #getDefaultEvictionPolicyClass()} 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 org.jboss.cache.RegionManagerImpl#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. * @deprecated the default region is now created when this instance is constructed. Use {@link #getDefaultEvictionRegionConfig()} instead. */ @Deprecated public EvictionRegionConfig createDefaultEvictionRegionConfig() { return getDefaultEvictionRegionConfig(); } public List getEvictionRegionConfigs() { return evictionRegionConfigs; } public void setEvictionRegionConfigs(List evictionRegionConfigs) { testImmutability("evictionRegionConfigs"); EvictionRegionConfig toRemove = null; for (EvictionRegionConfig erc : evictionRegionConfigs) { if (erc.getRegionFqn().isRoot() || erc.getRegionFqn().equals(RegionManagerImpl.DEFAULT_REGION)) { mergeWithDefault(erc); toRemove = erc; break; } } if (toRemove != null) evictionRegionConfigs.remove(toRemove); this.evictionRegionConfigs = evictionRegionConfigs; } private void mergeWithDefault(EvictionRegionConfig erc) { erc.setEventQueueSizeIfUnset(defaultEvictionRegionConfig.getEventQueueSize()); if (erc.getEvictionAlgorithmConfig() == null) erc.setEvictionAlgorithmConfig(defaultEvictionRegionConfig.getEvictionAlgorithmConfig()); defaultEvictionRegionConfig = erc; } public void addEvictionRegionConfig(EvictionRegionConfig evictionRegionConfig) { testImmutability("evictionRegionConfigs"); if (evictionRegionConfig.getRegionFqn().isRoot() || evictionRegionConfig.getRegionFqn().equals(RegionManagerImpl.DEFAULT_REGION)) { mergeWithDefault(evictionRegionConfig); } else { evictionRegionConfigs.add(evictionRegionConfig); } } /** * @return the wake up interval of the eviction thread, in milliseconds. */ public long getWakeupInterval() { return wakeupInterval; } /** * Set the wake up interval for the eviction thread. 0 or a negative number disables the eviction thread. * * @param wakeupInterval interval, in milliseconds. */ public void setWakeupInterval(long wakeupInterval) { testImmutability("wakeupInterval"); this.wakeupInterval = wakeupInterval; } /** * Set the wake up interval for the eviction thread. 0 or a negative number disables the eviction thread. * * @param wakeupInterval interval * @param timeUnit for the interval provided */ public void setWakeupInterval(long wakeupInterval, TimeUnit timeUnit) { testImmutability("wakeupInterval"); this.wakeupInterval = timeUnit.toMillis(wakeupInterval); } /** * @deprecated Use {@link #getWakeupIntervalSeconds()}. */ @Deprecated public int getWakeupIntervalSeconds() { return (int) TimeUnit.MILLISECONDS.toSeconds(wakeupInterval); } /** * @deprecated Use {@link #setWakeupInterval(long)}. */ @Deprecated public void setWakeupIntervalSeconds(int wakeupIntervalSeconds) { setWakeupInterval(wakeupIntervalSeconds, TimeUnit.SECONDS); } public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof EvictionConfig)) return false; EvictionConfig that = (EvictionConfig) o; if (defaultEventQueueSize != that.defaultEventQueueSize) return false; if (wakeupInterval != that.wakeupInterval) return false; if (defaultEvictionRegionConfig != null ? !defaultEvictionRegionConfig.equals(that.defaultEvictionRegionConfig) : that.defaultEvictionRegionConfig != null) return false; if (evictionRegionConfigs != null ? !evictionRegionConfigs.equals(that.evictionRegionConfigs) : that.evictionRegionConfigs != null) return false; return true; } public int hashCode() { int result; result = 31 + (int) (wakeupInterval ^ (wakeupInterval >>> 32)); result = 31 * result + defaultEventQueueSize; result = 31 * result + (evictionRegionConfigs != null ? evictionRegionConfigs.hashCode() : 0); return result; } @Override public EvictionConfig clone() throws CloneNotSupportedException { EvictionConfig clone = (EvictionConfig) super.clone(); if (evictionRegionConfigs != null) { // needs to be a deep copy clone.evictionRegionConfigs = new LinkedList(); for (EvictionRegionConfig erc : evictionRegionConfigs) clone.addEvictionRegionConfig(erc.clone()); } 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; } /** * Applies defaults to a config passed in * * @param config config to apply defaults to */ public void applyDefaults(EvictionRegionConfig config) { if (config == null) return; // no op config.setDefaults(defaultEvictionRegionConfig); } /** * @deprecated set these attributes on the default eviction region config. */ @Deprecated public void setDefaultEventQueueSize(int queueSize) { defaultEvictionRegionConfig.setEventQueueSize(queueSize); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/config/OldFileFormatException.java0000644000175000017500000000276511111047320031010 0ustar moellermoeller/* * 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.config; /** * This exception indicates that an old configuration file is passed to a parser that only knows how to handle newer * configuration file. * * @author Mircea.Markus@jboss.com * @since 3.0 */ public class OldFileFormatException extends ConfigurationException { public OldFileFormatException(String string) { super(string); } public OldFileFormatException() { this("The configuration file has an old format."); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/config/Configuration.java0000644000175000017500000011137211162403335027254 0ustar moellermoeller/* * 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.config; import org.jboss.cache.Version; import org.jboss.cache.buddyreplication.BuddyManager; import org.jboss.cache.config.parsing.JGroupsStackParser; import org.jboss.cache.factories.annotations.NonVolatile; import org.jboss.cache.factories.annotations.Start; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.marshall.Marshaller; import org.w3c.dom.Element; import java.net.URL; import java.util.Collections; import java.util.List; import java.util.Locale; /** * Encapsulates the configuration of a Cache. * * @author Manik Surtani (manik AT jboss DOT org) */ @NonVolatile public class Configuration extends ConfigurationComponent { private static final long serialVersionUID = 5553791890144997466L; private transient JGroupsStackParser jGroupsStackParser = new JGroupsStackParser(); /** * 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 locked using the MVCC locking scheme. This is the default locking scheme in JBoss Cache 3.0.0. * * @see http://wiki.jboss.org/wiki/JBossCacheMVCC */ MVCC, /** * 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; /** * @return true if the node locking scheme uses versioning. */ public boolean isVersionedScheme() { return this == 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 boolean nonBlockingStateTransfer = false; 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; @Deprecated private NodeLockingScheme nodeLockingScheme = NodeLockingScheme.MVCC; 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; private List customInterceptors = Collections.emptyList(); private boolean writeSkewCheck = false; private int concurrencyLevel = 500; private int listenerAsyncPoolSize = 1; private int listenerAsyncQueueSize = 50000; private int serializationExecutorPoolSize = 0; private int serializationExecutorQueueSize = 50000; private Marshaller marshaller; private boolean invocationBatchingEnabled; private boolean useLockStriping = true; private URL jgroupsConfigFile; @Start(priority = 1) void correctIsolationLevels() { // ensure the correct isolation level upgrades and/or downgrades are performed. if (nodeLockingScheme == NodeLockingScheme.MVCC) { switch (isolationLevel) { case NONE: case READ_UNCOMMITTED: isolationLevel = IsolationLevel.READ_COMMITTED; break; case SERIALIZABLE: isolationLevel = IsolationLevel.REPEATABLE_READ; break; } } } // ------------------------------------------------------------------------------------------------------------ // SETTERS - MAKE SURE ALL SETTERS PERFORM testImmutability()!!! // ------------------------------------------------------------------------------------------------------------ public void setCacheMarshaller(Marshaller instance) { marshaller = instance; } public Marshaller getMarshaller() { return marshaller; } public boolean isWriteSkewCheck() { return writeSkewCheck; } public boolean isUseLockStriping() { return useLockStriping; } public void setWriteSkewCheck(boolean writeSkewCheck) { testImmutability("writeSkewCheck"); this.writeSkewCheck = writeSkewCheck; } public void setUseLockStriping(boolean useLockStriping) { testImmutability("useLockStriping"); this.useLockStriping = useLockStriping; } public int getConcurrencyLevel() { return concurrencyLevel; } public void setConcurrencyLevel(int concurrencyLevel) { testImmutability("concurrencyLevel"); this.concurrencyLevel = concurrencyLevel; } /** * Converts a list of elements to a Java Groups property string. */ public void setClusterConfig(Element config) { setClusterConfig(jGroupsStackParser.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; } /** * Enables invocation batching if set to true. You still need to use {@link org.jboss.cache.Cache#startBatch()} * and {@link org.jboss.cache.Cache#endBatch(boolean)} to demarcate the start and end of batches. * * @param enabled if true, batching is enabled. * @since 3.0 */ public void setInvocationBatchingEnabled(boolean enabled) { testImmutability("invocationBatchingEnabled"); this.invocationBatchingEnabled = enabled; } 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. *

    */ 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; } /** * Sets the size of the asynchronous listener notification thread pool size. Defaults to 1, and if set to below 1, * all async listeners (specified with {@link org.jboss.cache.notifications.annotation.CacheListener#sync()} are notified * synchronously. * * @param listenerAsyncPoolSize number of threads in pool * @since 3.0 */ public void setListenerAsyncPoolSize(int listenerAsyncPoolSize) { testImmutability("listenerAsyncPoolSize"); this.listenerAsyncPoolSize = listenerAsyncPoolSize; } /** * Sets the queue size of the bounded queue used to store async listener events on. This defaults to 50,000. * * @param listenerAsyncQueueSize queue size to use */ public void setListenerAsyncQueueSize(int listenerAsyncQueueSize) { testImmutability("listenerAsyncQueueSize"); this.listenerAsyncQueueSize = listenerAsyncQueueSize; } /** * Sets the queue size of the bounded queue used to store async serialization events on. This defaults to 50,000. * * @param serializationExecutorQueueSize queue size to use */ public void setSerializationExecutorQueueSize(int serializationExecutorQueueSize) { testImmutability("serializationExecutorQueueSize"); this.serializationExecutorQueueSize = serializationExecutorQueueSize; } public void setBuddyReplicationConfig(BuddyReplicationConfig config) { testImmutability("buddyReplicationConfig"); replaceChildConfig(this.buddyReplicationConfig, config); this.buddyReplicationConfig = config; } /** * @deprecated will default to MVCC once optimistic and pessimistic schemes are removed. * @param nodeLockingScheme */ @Deprecated public void setNodeLockingScheme(NodeLockingScheme nodeLockingScheme) { testImmutability("nodeLockingScheme"); testImmutability("nodeLockingOptimistic"); this.nodeLockingScheme = nodeLockingScheme; } public void setUseReplQueue(boolean useReplQueue) { testImmutability("useReplQueue"); this.useReplQueue = useReplQueue; } public void setIsolationLevel(IsolationLevel isolationLevel) { testImmutability("isolationLevel"); this.isolationLevel = isolationLevel; } /** * Starting with 3.x there are 3 locking schemes, so if true is passed in then state is not defined. * It is here for backward compatibility reasons only and should not be used by new code. */ @Deprecated public void setNodeLockingOptimistic(boolean nodeLockingOptimistic) { testImmutability("nodeLockingOptimistic"); if (nodeLockingOptimistic) setNodeLockingScheme(NodeLockingScheme.OPTIMISTIC); else setNodeLockingScheme(NodeLockingScheme.PESSIMISTIC); } public void setStateRetrievalTimeout(long stateRetrievalTimeout) { testImmutability("stateRetrievalTimeout"); this.stateRetrievalTimeout = stateRetrievalTimeout; } public void setNodeLockingScheme(String nodeLockingScheme) { testImmutability("nodeLockingScheme"); 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; } } @Deprecated 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; } /** * Sets the async replication serialization executor pool size for async replication. Has no effect if the * replication queue is used. * * @param serializationExecutorPoolSize number of threads to use */ public void setSerializationExecutorPoolSize(int serializationExecutorPoolSize) { testImmutability("serializationExecutorPoolSize"); this.serializationExecutorPoolSize = serializationExecutorPoolSize; } // ------------------------------------------------------------------------------------------------------------ // GETTERS // ------------------------------------------------------------------------------------------------------------ public ShutdownHookBehavior getShutdownHookBehavior() { return this.shutdownHookBehavior; } /** * This helper method is deprecated and will be removed when optimistic and pessimistic locking support is dropped. * * @return true if node locking scheme is optimistic. * @deprecated use {@link #getNodeLockingScheme()} to determine node locking scheme used. */ @Deprecated public boolean isNodeLockingOptimistic() { return nodeLockingScheme == NodeLockingScheme.OPTIMISTIC; } 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; } /** * @deprecated use isExposeManagementStatistics() */ @Deprecated public boolean getExposeManagementStatistics() { return exposeManagementStatistics; } public boolean isExposeManagementStatistics() { return exposeManagementStatistics; } /** * @return true if invocation batching is enabled. * @since 3.0 */ public boolean isInvocationBatchingEnabled() { return invocationBatchingEnabled; } 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; } 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; } /** * Gets the size of the asynchronous listener notification thread pool size. Defaults to 1, and if set to below 1, * all async listeners (specified with {@link org.jboss.cache.notifications.annotation.CacheListener#sync()} are notified * synchronously. * * @return thread pool size * @since 3.0 */ public int getListenerAsyncPoolSize() { return listenerAsyncPoolSize; } public BuddyReplicationConfig getBuddyReplicationConfig() { return buddyReplicationConfig; } /** * @deprecated will be removed once optimistic and pessimistic locking is removed. * @return node locking scheme in use */ @Deprecated 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; } public URL getJGroupsConfigFile() { return jgroupsConfigFile; } public void setJgroupsConfigFile(URL jgroupsConfigFile) { this.jgroupsConfigFile = jgroupsConfigFile; } /** * @return the serialization executor pool size. */ public int getSerializationExecutorPoolSize() { return serializationExecutorPoolSize; } /** * * @return the bounded queue size for async listeners */ public int getListenerAsyncQueueSize() { return listenerAsyncQueueSize; } /** * * @return the bounded queue size for async serializers */ public int getSerializationExecutorQueueSize() { return serializationExecutorQueueSize; } // ------------------------------------------------------------------------------------------------------------ // 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 (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; if (listenerAsyncPoolSize != that.listenerAsyncPoolSize) return false; if (serializationExecutorPoolSize != that.serializationExecutorPoolSize) return false; if (jgroupsConfigFile != that.jgroupsConfigFile) return false; if (listenerAsyncQueueSize != that.listenerAsyncQueueSize) return false; if (serializationExecutorQueueSize != that.serializationExecutorQueueSize) 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 + (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; result = 31 * result + serializationExecutorPoolSize; result = 31 * result + listenerAsyncPoolSize; result = 31 * result + serializationExecutorQueueSize; result = 31 * result + listenerAsyncQueueSize; result = 31 * result + (jgroupsConfigFile != null ? jgroupsConfigFile.hashCode() : 0); 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; } public String getMuxStackName() { return muxStackName; } public void setMuxStackName(String muxStackName) { this.muxStackName = muxStackName; } /** * Returns the {@link org.jboss.cache.config.CustomInterceptorConfig}, if any, associated with this configuration * object. The custom interceptors will be added to the cache at startup in the sequence defined by this list. * * @return List of cutom interceptors, never null */ @SuppressWarnings("unchecked") public List getCustomInterceptors() { return customInterceptors == null ? Collections.EMPTY_LIST : customInterceptors; } /** * @see #getCustomInterceptors() */ public void setCustomInterceptors(List customInterceptors) { testImmutability("customInterceptors"); this.customInterceptors = customInterceptors; } public BuddyManager getConsistentHashing() { return null; } public boolean isNonBlockingStateTransfer() { return nonBlockingStateTransfer; } public void setNonBlockingStateTransfer(boolean nonBlockingStateTransfer) { testImmutability("nonBlockingStateTransfer"); this.nonBlockingStateTransfer = nonBlockingStateTransfer; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/config/Option.java0000644000175000017500000004343411144362365025727 0ustar moellermoeller/* * 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.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 AT jboss DOT org) * @author Galder Zamarreno * @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; private boolean suppressPersistence; private boolean suppressEventNotification; /** * @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 * @deprecated this is to support a deprecated locking scheme (Optimistic Locking). Will be removed when Optimistic Locking support is removed. */ @Deprecated @SuppressWarnings("deprecation") 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 * @deprecated this is to support a deprecated locking scheme (Optimistic Locking). Will be removed when Optimistic Locking support is removed. */ @Deprecated @SuppressWarnings("deprecation") 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 + ", suppressPersistence=" + suppressPersistence + ", suppressEventNotification=" + suppressEventNotification + '}'; } /** * @see #copy() * @deprecated this method may disappear in future, please use copy() instead. */ @Override @Deprecated public Option clone() throws CloneNotSupportedException { return (Option) super.clone(); } /** * @return a new Option instance with all fields shallow-copied. */ public Option copy() { try { return (Option) super.clone(); } catch (CloneNotSupportedException e) { // should never happen return null; } } @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; if (suppressPersistence != option.suppressPersistence) return false; if (suppressEventNotification != option.suppressEventNotification) 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); result = 29 * result + (suppressPersistence ? 0 : 1); result = 29 * result + (suppressEventNotification ? 0 : 1); 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; this.suppressPersistence = false; this.suppressEventNotification = false; } /** * Forces a write lock to be acquired on the call, regardless of whether it is a read or write. *

    * Note that this only applies to {@link org.jboss.cache.config.Configuration.NodeLockingScheme#MVCC} and {@link org.jboss.cache.config.Configuration.NodeLockingScheme#PESSIMISTIC} * node locking schemes, and is ignored if {@link org.jboss.cache.config.Configuration.NodeLockingScheme#OPTIMISTIC} is used. *

    * * @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. *

    * Note that this only applies to {@link org.jboss.cache.config.Configuration.NodeLockingScheme#MVCC} and {@link org.jboss.cache.config.Configuration.NodeLockingScheme#PESSIMISTIC} * node locking schemes, and is ignored if {@link org.jboss.cache.config.Configuration.NodeLockingScheme#OPTIMISTIC} is used. *

    * * @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; } /** * If set to true, any persistence to a cache loader will be suppressed for the current invocation only. Does not apply to transactional calls. * * @return true if persistence is suppressed. * @since 3.0 */ public boolean isSuppressPersistence() { return suppressPersistence; } /** * If set to true, any persistence to a cache loader will be suppressed for the current invocation only. Does not apply to transactional calls. * * @param suppressPersistence if true, will suppress persistence. * @since 3.0 */ public void setSuppressPersistence(boolean suppressPersistence) { this.suppressPersistence = suppressPersistence; } /** * Get whether event notifications for this invocation will be suppresed. By * default is false which means that corresponding events are sent depending * on the type of invocation. * * @return true, if event notification will be suppressed for this invocation. */ public boolean isSuppressEventNotification() { return suppressEventNotification; } /** * Set whether event notifications should be suppressed for this particular * cache or transaction invocation. * * @param suppressEventNotification true if event notification * should be skipped; false if events * should be notified if there're any listeners. */ public void setSuppressEventNotification(boolean suppressEventNotification) { this.suppressEventNotification = suppressEventNotification; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/config/MissingPolicyException.java0000644000175000017500000000247011111047320031103 0ustar moellermoeller/* * 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.config; public class MissingPolicyException extends ConfigurationException { /** * The serialVersionUID */ private static final long serialVersionUID = 6107098975617303157L; public MissingPolicyException(String message) { super(message); } }jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/config/RuntimeConfig.java0000644000175000017500000002405311111047320027205 0ustar moellermoeller/* * 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.config; 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 org.jgroups.util.ThreadFactory; import javax.transaction.TransactionManager; import java.util.concurrent.ExecutorService; 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 BuddyGroup buddyGroup; private RPCManager rpcManager; private transient ThreadFactory evictionTimerThreadFactory; private transient ExecutorService asyncSerializationExecutor; private transient ExecutorService asyncCacheListenerExecutor; /** * Resets the runtime to default values. */ public void reset() { 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 #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 channel factory */ 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 channel to set */ public void setChannel(Channel channel) { this.channel = channel; } public TransactionManager getTransactionManager() { return transactionManager; } public void setTransactionManager(TransactionManager transactionManager) { testImmutability("transactionManager"); this.transactionManager = transactionManager; } /** * This is only relevant if an eviction timer thread factory has been set using {@link #setEvictionTimerThreadFactory(org.jgroups.util.ThreadFactory)}. * Will return a null if the eviction timer thread factory needs to be created internally. *

    * * @return the thread factory used by the eviction timer's scheduled executor. * @since 3.0 */ public ThreadFactory getEvictionTimerThreadFactory() { return evictionTimerThreadFactory; } /** * Sets the eviction timer thread factory to use when creating a scheduled executor. If this is not set, the eviction * timer task will use a default thread factory. * * @param evictionTimerThreadFactory factory to use * @since 3.0 */ public void setEvictionTimerThreadFactory(ThreadFactory evictionTimerThreadFactory) { this.evictionTimerThreadFactory = evictionTimerThreadFactory; } /** * This is only relevant if the async cache replication executor has been set using {@link #setAsyncSerializationExecutor(java.util.concurrent.ExecutorService)}. * If the executor is created internally, this method will return null. *

    * * @return the executor used for async replication. * @since 3.0 */ public ExecutorService getAsyncSerializationExecutor() { return asyncSerializationExecutor; } /** * This is used to set the executor to use for async cache replucation, and effectively overrides {@link org.jboss.cache.config.Configuration#setSerializationExecutorPoolSize(int)} *

    * * @param asyncSerializationExecutor executor to set * @since 3.0 */ public void setAsyncSerializationExecutor(ExecutorService asyncSerializationExecutor) { this.asyncSerializationExecutor = asyncSerializationExecutor; } /** * This is only relevant if the async cache listener executor has been set using {@link #setAsyncCacheListenerExecutor(java.util.concurrent.ExecutorService)}. * If the executor is created internally, this method will return null. *

    * * @return the executor to use for async cache listeners * @since 3.0 */ public ExecutorService getAsyncCacheListenerExecutor() { return asyncCacheListenerExecutor; } /** * This is used to set the executor to use for async cache listeners, and effectively overrides {@link org.jboss.cache.config.Configuration#setListenerAsyncPoolSize(int)} *

    * * @param asyncCacheListenerExecutor the executor to use for async cache listeners * @since 3.0 */ public void setAsyncCacheListenerExecutor(ExecutorService asyncCacheListenerExecutor) { this.asyncCacheListenerExecutor = asyncCacheListenerExecutor; } @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) && Util.safeEquals(evictionTimerThreadFactory, other.evictionTimerThreadFactory) && Util.safeEquals(asyncCacheListenerExecutor, other.asyncCacheListenerExecutor) && Util.safeEquals(asyncSerializationExecutor, other.asyncSerializationExecutor); } 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()); result = result * 29 + (evictionTimerThreadFactory == null ? 0 : evictionTimerThreadFactory.hashCode()); result = result * 29 + (asyncCacheListenerExecutor == null ? 0 : asyncCacheListenerExecutor.hashCode()); result = result * 29 + (asyncSerializationExecutor == null ? 0 : asyncSerializationExecutor.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-3.2.8.GA/src/main/java/org/jboss/cache/config/EvictionRegionConfig.java0000644000175000017500000002537411155773343030540 0ustar moellermoeller/* * 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.config; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheException; import org.jboss.cache.Fqn; import org.jboss.cache.eviction.ModernizableConfig; import org.jboss.cache.util.Util; import java.lang.reflect.Method; /** * It is imperative that a region Fqn is set, either via one of the constructors or using {@link #setRegionFqn(org.jboss.cache.Fqn)}. */ 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"; private Fqn regionFqn; @Dynamic private Integer eventQueueSize; private EvictionAlgorithmConfig evictionAlgorithmConfig; @Deprecated private EvictionPolicyConfig deprecatedConfig; private String evictionActionPolicyClassName; public EvictionRegionConfig() { } /** * @deprecated use {@link #EvictionRegionConfig(org.jboss.cache.Fqn, EvictionAlgorithmConfig)} instead. */ @Deprecated @SuppressWarnings("deprecation") public EvictionRegionConfig(Fqn regionFqn, EvictionPolicyConfig evictionPolicyConfig) { this.regionFqn = regionFqn; if (evictionPolicyConfig instanceof ModernizableConfig) { this.evictionAlgorithmConfig = ((ModernizableConfig) evictionPolicyConfig).modernizeConfig(); deprecatedConfig = evictionPolicyConfig; } else { throw new ConfigurationException("Unable to convert " + evictionPolicyConfig.getClass().getName() + " to a more modern format, implementing " + EvictionAlgorithmConfig.class.getSimpleName() + ". Please use " + EvictionAlgorithmConfig.class.getSimpleName() + " which replaces the deprecated " + EvictionPolicyConfig.class.getSimpleName()); } } public EvictionRegionConfig(Fqn regionFqn, EvictionAlgorithmConfig evictionAlgorithmConfig) { this.regionFqn = regionFqn; this.evictionAlgorithmConfig = evictionAlgorithmConfig; } public EvictionRegionConfig(Fqn regionFqn, EvictionAlgorithmConfig evictionAlgorithmConfig, int queueSize) { this.regionFqn = regionFqn; this.evictionAlgorithmConfig = evictionAlgorithmConfig; this.eventQueueSize = queueSize; } public EvictionRegionConfig(Fqn fqn) { this.regionFqn = fqn; } /** * @deprecated use {@link #getEvictionAlgorithmConfig()} instead. */ @Deprecated public EvictionPolicyConfig getEvictionPolicyConfig() { if (deprecatedConfig != null) return deprecatedConfig; else throw new CacheException("Not supported. Please use " + EvictionAlgorithmConfig.class.getSimpleName() + " instead of " + EvictionPolicyConfig.class.getSimpleName()); } public EvictionAlgorithmConfig getEvictionAlgorithmConfig() { return evictionAlgorithmConfig; } /** * @deprecated see {@link #setEvictionAlgorithmConfig(EvictionAlgorithmConfig)} */ @Deprecated public void setEvictionPolicyConfig(EvictionPolicyConfig evictionPolicyConfig) { if (evictionPolicyConfig instanceof ModernizableConfig) { deprecatedConfig = evictionPolicyConfig; setEvictionAlgorithmConfig(((ModernizableConfig) evictionPolicyConfig).modernizeConfig()); } else { throw new UnsupportedEvictionImplException("Unable to convert " + evictionPolicyConfig.getClass().getName() + " to a more modern format, implementing " + EvictionAlgorithmConfig.class.getSimpleName() + ". Please use " + EvictionAlgorithmConfig.class.getSimpleName() + " which replaces the deprecated " + EvictionPolicyConfig.class.getSimpleName()); } } public void setEvictionAlgorithmConfig(EvictionAlgorithmConfig config) { testImmutability("evictionAlgorithmConfig"); this.evictionAlgorithmConfig = 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 setDefaults(EvictionRegionConfig defaults) { // go thru each element that is unset here and copy from "defaults" if (eventQueueSize == null) eventQueueSize = defaults.getEventQueueSize(); if (evictionAlgorithmConfig == null) evictionAlgorithmConfig = defaults.getEvictionAlgorithmConfig(); if (evictionActionPolicyClassName == null) evictionActionPolicyClassName = defaults.getEvictionActionPolicyClassName(); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj instanceof EvictionRegionConfig) { EvictionRegionConfig other = (EvictionRegionConfig) obj; boolean equalRegions = Util.safeEquals(this.regionFqn, other.regionFqn); boolean equalConfigurations = Util.safeEquals(this.evictionAlgorithmConfig, other.evictionAlgorithmConfig); boolean equalEventQueuSizes = this.getEventQueueSize() == other.getEventQueueSize(); return equalRegions && equalConfigurations && equalConfigurations && equalEventQueuSizes; } 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 (evictionAlgorithmConfig != null) { if (evictionAlgorithmConfig instanceof ConfigurationComponent) { clone.setEvictionAlgorithmConfig((EvictionAlgorithmConfig) ((ConfigurationComponent) evictionAlgorithmConfig).clone()); } else { try { Method cloneMethod = this.evictionAlgorithmConfig.getClass().getDeclaredMethod("clone"); EvictionAlgorithmConfig evictionAlgorithmConfig = (EvictionAlgorithmConfig) cloneMethod.invoke(this.evictionAlgorithmConfig); clone.setEvictionAlgorithmConfig(evictionAlgorithmConfig); } catch (Exception e) { CloneNotSupportedException cnse = new CloneNotSupportedException("Cannot invoke clone() on " + evictionAlgorithmConfig); cnse.initCause(e); throw cnse; } } } if (deprecatedConfig != null) { if (!(deprecatedConfig instanceof Cloneable)) { throw new CloneNotSupportedException(deprecatedConfig + " is not Cloneable"); } if (deprecatedConfig instanceof ConfigurationComponent) { clone.setEvictionAlgorithmConfig((EvictionAlgorithmConfig) ((ConfigurationComponent) deprecatedConfig).clone()); } else { try { Method cloneMethod = this.deprecatedConfig.getClass().getDeclaredMethod("clone"); EvictionAlgorithmConfig evictionAlgorithmConfig = (EvictionAlgorithmConfig) cloneMethod.invoke(this.deprecatedConfig); clone.setEvictionAlgorithmConfig(evictionAlgorithmConfig); } catch (Exception e) { CloneNotSupportedException cnse = new CloneNotSupportedException("Cannot invoke clone() on " + deprecatedConfig); cnse.initCause(e); throw cnse; } } } clone.evictionActionPolicyClassName = evictionActionPolicyClassName; return clone; } public boolean isDefaultRegion() { return regionFqn.isRoot(); } public String getEvictionActionPolicyClassName() { return evictionActionPolicyClassName == null ? EvictionConfig.EVICTION_ACTION_POLICY_CLASS_DEFAULT : evictionActionPolicyClassName; } public void setEvictionActionPolicyClassName(String evictionActionPolicyClassName) { this.evictionActionPolicyClassName = evictionActionPolicyClassName; } public void setEventQueueSizeIfUnset(int eventQueueSize) { if (this.eventQueueSize == null) this.eventQueueSize = eventQueueSize; } /** * Ensure this is a valid eviction region configuration. */ public void validate() { if (eventQueueSize < 1) throw new ConfigurationException("Eviction event queue size cannot be less than 1!"); if (evictionAlgorithmConfig == null) throw new MissingPolicyException("Eviction algorithm configuration cannot be null!"); evictionAlgorithmConfig.validate(); } @Override public String toString() { return "EvictionRegionConfig{" + "regionFqn=" + regionFqn + ", eventQueueSize=" + eventQueueSize + ", evictionAlgorithmConfig=" + evictionAlgorithmConfig + ", evictionActionPolicyClassName='" + evictionActionPolicyClassName + '\'' + '}'; } }jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/config/LegacyConfigurationException.java0000644000175000017500000000331611111047320032246 0ustar moellermoeller/* * 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.config; /** * Thrown when a legacy configuration XML element is passed into a modern 3.0 parser * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ public class LegacyConfigurationException extends ConfigurationException { public LegacyConfigurationException(Exception e) { super(e); } public LegacyConfigurationException(String string) { super(string); } public LegacyConfigurationException(String string, String erroneousAttribute) { super(string, erroneousAttribute); } public LegacyConfigurationException(String string, Throwable throwable) { super(string, throwable); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/config/PluggableConfigurationComponent.java0000644000175000017500000000730611111047320032753 0ustar moellermoeller/* * 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.config; import org.jboss.cache.config.parsing.XmlConfigHelper; 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 AT jboss DOT 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 = XmlConfigHelper.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-3.2.8.GA/src/main/java/org/jboss/cache/config/EvictionAlgorithmConfig.java0000644000175000017500000000465211111047320031214 0ustar moellermoeller/* * 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.config; /** * An interface used to configure an eviction algorithm. Replaces the deprecated {@link org.jboss.cache.config.EvictionPolicyConfig}. *

    * In its most basic form, it is implemented by {@link org.jboss.cache.eviction.EvictionAlgorithmConfigBase}, but * more specific eviction policies may subclass {@link org.jboss.cache.eviction.EvictionAlgorithmConfigBase} or re-implement * this interface to provide access to more config variables. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ public interface EvictionAlgorithmConfig extends CloneableConfigurationComponent { /** * Gets the class name of the {@link org.jboss.cache.eviction.EvictionAlgorithm} implementation * this object will configure. * * @return fully qualified class name */ String getEvictionAlgorithmClassName(); /** * 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(); /** * @return a clone of the EvictionAlgorithmConfig. */ EvictionAlgorithmConfig clone() throws CloneNotSupportedException; } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/config/CustomInterceptorConfig.java0000644000175000017500000001524511111047320031256 0ustar moellermoeller/* * 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.config; import net.jcip.annotations.Immutable; import org.jboss.cache.interceptors.base.CommandInterceptor; /** * Holds information about the custom interceptors defined in the configuration file. * * @author Mircea.Markus@jboss.com * @since 3.0 */ @Immutable public class CustomInterceptorConfig extends ConfigurationComponent { private CommandInterceptor interceptor; private boolean isFirst; private boolean isLast; private int index = -1; private String afterClass; private String beforeClass; /** * Builds a custom interceptor. * * @param interceptor interceptor instance, already initialized with all attributes specified in the configuration * @param first true if you wan this to be the first interceptor in the chain * @param last true if you wan this to be the last interceptor in the chain * @param index an absolute position within the interceptor chain * @param afterClass if you want this interceptor immediately after the specified class in the chain * @param beforeClass immediately before the specified class in the chain */ public CustomInterceptorConfig(CommandInterceptor interceptor, boolean first, boolean last, int index, String afterClass, String beforeClass) { this.interceptor = interceptor; isFirst = first; isLast = last; this.index = index; this.afterClass = afterClass; this.beforeClass = beforeClass; } /** * Constructs an interceptor config based on the supplied interceptor instance. * * @param interceptor */ public CustomInterceptorConfig(CommandInterceptor interceptor) { this.interceptor = interceptor; } /** * Shall this interceptor be the first one in the chain? */ public void setFirst(boolean first) { testImmutability("first"); isFirst = first; } /** * Shall this intercepto be the last one in the chain? */ public void setLast(boolean last) { testImmutability("last"); isLast = last; } /** * Put this interceptor at the specified index, after the default chain is built. * If the index is not valid (negative or grater than the size of the chain) * an {@link org.jboss.cache.config.ConfigurationException} is thrown at construction time. */ public void setIndex(int index) { testImmutability("index"); this.index = index; } /** * Adds the interceptor immediately after the first occurance of an interceptor having the given class. * If the chain does not contain such an interceptor then this interceptor definition is ignored. */ public void setAfterClass(String afterClass) { testImmutability("afterClass"); this.afterClass = afterClass; } /** * Adds the interceptor immediately before the first occurance of an interceptor having the given class. * If the chain does not contain such an interceptor then this interceptor definition is ignored. */ public void setBeforeClass(String beforeClass) { testImmutability("beforeClass"); this.beforeClass = beforeClass; } /** * Returns a the interceptor that we want to add to the chain. */ public CommandInterceptor getInterceptor() { return interceptor; } /** * @see #setFirst(boolean) */ public boolean isFirst() { return isFirst; } /** * @see #setLast(boolean) */ public boolean isLast() { return isLast; } /** * @see #getIndex() */ public int getIndex() { return index; } /** * @see #getAfterClass() */ public String getAfterClass() { return afterClass; } /** * @see #getBeforeClass() */ public String getBeforeClass() { return beforeClass; } public String toString() { return "CustomInterceptorConfig{" + "interceptor='" + interceptor + '\'' + ", isFirst=" + isFirst + ", isLast=" + isLast + ", index=" + index + ", afterClass='" + afterClass + '\'' + ", beforeClass='" + beforeClass + '\'' + '}'; } public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof CustomInterceptorConfig)) return false; CustomInterceptorConfig that = (CustomInterceptorConfig) o; if (index != that.index) return false; if (isFirst != that.isFirst) return false; if (isLast != that.isLast) return false; if (afterClass != null ? !afterClass.equals(that.afterClass) : that.afterClass != null) return false; if (beforeClass != null ? !beforeClass.equals(that.beforeClass) : that.beforeClass != null) return false; if (interceptor != null ? !interceptor.equals(that.interceptor) : that.interceptor != null) return false; return true; } public int hashCode() { int result; result = (interceptor != null ? interceptor.hashCode() : 0); result = 31 * result + (isFirst ? 1 : 0); result = 31 * result + (isLast ? 1 : 0); result = 31 * result + index; result = 31 * result + (afterClass != null ? afterClass.hashCode() : 0); result = 31 * result + (beforeClass != null ? beforeClass.hashCode() : 0); return result; } @Override public CustomInterceptorConfig clone() throws CloneNotSupportedException { CustomInterceptorConfig dolly = (CustomInterceptorConfig) super.clone(); dolly.interceptor = interceptor; dolly.isFirst = isFirst; dolly.isLast = isLast; dolly.afterClass = afterClass; dolly.beforeClass = beforeClass; return dolly; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/config/CacheLoaderConfig.java0000644000175000017500000003363511111047320027722 0ustar moellermoeller/* * 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.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 AT jboss DOT 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 AT jboss DOT 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-3.2.8.GA/src/main/java/org/jboss/cache/config/parsing/0000755000175000017500000000000011675222007025244 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/config/parsing/XmlConfigurationParser2x.java0000644000175000017500000005674611620334373033050 0ustar moellermoeller/* * 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.config.parsing; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.RegionManagerImpl; 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.EvictionAlgorithmConfig; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.config.MissingPolicyException; import org.jboss.cache.eviction.EvictionAlgorithm; import org.jboss.cache.eviction.EvictionPolicy; import org.jboss.cache.eviction.ModernizablePolicy; import org.jboss.cache.util.FileLookup; import org.jboss.cache.util.Util; 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.io.InputStream; 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 AT jboss DOT org) * @author Galder Zamarreno * @since 2.00. * @deprecated */ public class XmlConfigurationParser2x { private static final Log log = LogFactory.getLog(XmlConfigurationParser2x.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 = new FileLookup().lookupFile(filename); if (is == null) { throw new ConfigurationException("Unable to find config file " + filename + " either in classpath or on the filesystem!"); } 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 = XmlConfigHelper.getDocumentRoot(stream); Element mbeanElement = getMBeanElement(root); return parseConfiguration(mbeanElement); } public Configuration parseConfiguration(Element configurationRoot) { ParsedAttributes attributes = XmlConfigHelper.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(); XmlConfigHelper.setValues(c, attributes.stringAttribs, false, false); // Special handling for XML elements -- we hard code the parsing setXmlValues(c, attributes.xmlAttribs); if (c.getEvictionConfig() != null) correctEvictionUnlimitedValues(c.getEvictionConfig()); return c; } private void correctEvictionUnlimitedValues(EvictionConfig ec) { EvictionRegionConfig def = ec.getDefaultEvictionRegionConfig(); def.getEvictionAlgorithmConfig(); } /** * 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 Element getMBeanElement(Element root) { // This is following JBoss convention. NodeList list = root.getElementsByTagName(XmlConfigHelper.ROOT); if (list == null) throw new ConfigurationException("Can't find " + XmlConfigHelper.ROOT + " tag"); if (list.getLength() > 1) throw new ConfigurationException("Has multiple " + XmlConfigHelper.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 " + XmlConfigHelper.ROOT + " element"); } return element; } 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(XmlConfigHelper.readBooleanContents(element, "buddyReplicationEnabled")); brc.setDataGravitationRemoveOnFind(XmlConfigHelper.readBooleanContents(element, "dataGravitationRemoveOnFind", true)); brc.setDataGravitationSearchBackupTrees(XmlConfigHelper.readBooleanContents(element, "dataGravitationSearchBackupTrees", true)); brc.setAutoDataGravitation(brc.isEnabled() && XmlConfigHelper.readBooleanContents(element, "autoDataGravitation", false)); String strBuddyCommunicationTimeout = XmlConfigHelper.readStringContents(element, "buddyCommunicationTimeout"); try { brc.setBuddyCommunicationTimeout(Integer.parseInt(strBuddyCommunicationTimeout)); } catch (Exception e) { if (log.isTraceEnabled()) log.trace(e.getMessage()); } finally { if (log.isDebugEnabled()) { log.debug("Using buddy communication timeout of " + brc.getBuddyCommunicationTimeout() + " millis"); } } String buddyPoolName = XmlConfigHelper.readStringContents(element, "buddyPoolName"); if ("".equals(buddyPoolName)) { buddyPoolName = null; } brc.setBuddyPoolName(buddyPoolName); // now read the buddy locator details String buddyLocatorClass = XmlConfigHelper.readStringContents(element, "buddyLocatorClass"); if (buddyLocatorClass == null || buddyLocatorClass.length() == 0) { buddyLocatorClass = NextMemberBuddyLocator.class.getName(); } Properties props = null; props = XmlConfigHelper.readPropertiesContents(element, "buddyLocatorProperties"); 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(XmlConfigHelper.readBooleanContents(element, "passivation")); String s = XmlConfigHelper.readStringContents(element, "preload"); if (s != null && s.length() > 0) clc.setPreload(s); clc.setShared(XmlConfigHelper.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(XmlConfigHelper.readBooleanContents(indivElement, "async", false)); iclc.setIgnoreModifications(XmlConfigHelper.readBooleanContents(indivElement, "ignoreModifications", false)); iclc.setFetchPersistentState(XmlConfigHelper.readBooleanContents(indivElement, "fetchPersistentState", false)); iclc.setPurgeOnStartup(XmlConfigHelper.readBooleanContents(indivElement, "purgeOnStartup", false)); iclc.setClassName(XmlConfigHelper.readStringContents(indivElement, "class")); iclc.setProperties(XmlConfigHelper.readPropertiesContents(indivElement, "properties")); SingletonStoreConfig ssc = parseSingletonStoreConfig(indivElement); if (ssc != null) { iclc.setSingletonStoreConfig(ssc); } clc.addIndividualCacheLoaderConfig(iclc); } } return clc; } private static CacheLoaderConfig.IndividualCacheLoaderConfig.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 = XmlConfigHelper.readBooleanContents(singletonStoreElement, "enabled"); String singletonStoreClass = XmlConfigHelper.readStringContents(singletonStoreElement, "class"); Properties singletonStoreproperties; singletonStoreproperties = XmlConfigHelper.readPropertiesContents(singletonStoreElement, "properties"); CacheLoaderConfig.IndividualCacheLoaderConfig.SingletonStoreConfig ssc = new CacheLoaderConfig.IndividualCacheLoaderConfig.SingletonStoreConfig(); ssc.setSingletonStoreEnabled(singletonStoreEnabled); ssc.setSingletonStoreClass(singletonStoreClass); ssc.setSingletonStoreproperties(singletonStoreproperties); return ssc; } return null; } @SuppressWarnings("unchecked") public static EvictionConfig parseEvictionConfig(Element element) { EvictionConfig evictionConfig = 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 = XmlConfigHelper.getTagContents(element, "policyClass", ATTR, NAME); String defaultEvPolicyClassName = null; if (temp != null && temp.length() > 0) { defaultEvPolicyClassName = temp; EvictionAlgorithmConfig eac = getEvictionAlgorithmConfig(temp); evictionConfig.getDefaultEvictionRegionConfig().setEvictionAlgorithmConfig(eac); } temp = XmlConfigHelper.getTagContents(element, "wakeUpIntervalSeconds", ATTR, NAME); int wakeupIntervalSeconds = 0; if (temp != null) { wakeupIntervalSeconds = Integer.parseInt(temp); } if (wakeupIntervalSeconds > 0) { evictionConfig.setWakeupInterval(wakeupIntervalSeconds * 1000); } int eventQueueSize = 0; temp = XmlConfigHelper.getTagContents(element, "eventQueueSize", ATTR, NAME); if (temp != null) { eventQueueSize = Integer.parseInt(temp); } if (eventQueueSize <= 0) { eventQueueSize = EvictionConfig.EVENT_QUEUE_SIZE_DEFAULT; } evictionConfig.getDefaultEvictionRegionConfig().setEventQueueSize(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 { EvictionRegionConfig evictionRegionConfig = parseEvictionRegionConfig((Element) node, defaultEvPolicyClassName, eventQueueSize); if (!evictionRegionConfig.getRegionFqn().equals(RegionManagerImpl.DEFAULT_REGION)) { regionConfigs.add(evictionRegionConfig); } else { evictionConfig.getDefaultEvictionRegionConfig().setEventQueueSize(evictionRegionConfig.getEventQueueSize()); evictionConfig.getDefaultEvictionRegionConfig().setEvictionAlgorithmConfig(evictionRegionConfig.getEvictionAlgorithmConfig()); } } catch (MissingPolicyException missingPolicy) { LogFactory.getLog(EvictionConfig.class).warn(missingPolicy.getLocalizedMessage()); throw missingPolicy; } } evictionConfig.setEvictionRegionConfigs(regionConfigs); } } return evictionConfig; } private static EvictionRegionConfig parseEvictionRegionConfig(Element element, String defaultEvPolicyClassName, int defaultQueueCapacity) { EvictionRegionConfig erc = new EvictionRegionConfig(); erc.setRegionName(element.getAttribute(EvictionRegionConfig.NAME)); String temp = element.getAttribute("eventQueueSize"); if (temp != null && temp.length() > 0) { erc.setEventQueueSize(Integer.parseInt(temp)); } else { erc.setEventQueueSize(defaultQueueCapacity); } String evictionClass = element.getAttribute("policyClass"); if (evictionClass == null || evictionClass.length() == 0) { evictionClass = defaultEvPolicyClassName; // 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!"); } } EvictionAlgorithmConfig algorithmConfig = getEvictionAlgorithmConfig(evictionClass); parseEvictionPolicyConfig(element, algorithmConfig); erc.setEvictionAlgorithmConfig(algorithmConfig); return erc; } private static EvictionAlgorithmConfig getEvictionAlgorithmConfig(String evictionClass) { EvictionConfig.assertIsTransformable(evictionClass); EvictionAlgorithm algorithm; try { EvictionPolicy ep = (EvictionPolicy) Util.getInstance(evictionClass); Class algoClass = ((ModernizablePolicy) ep).modernizePolicy(); if (log.isTraceEnabled()) log.trace("Using algo class " + algoClass); algorithm = Util.getInstance(algoClass); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException("Eviction class is not properly loaded in classloader", e); } EvictionAlgorithmConfig algorithmConfig; try { algorithmConfig = algorithm.getConfigurationClass().newInstance(); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException("Failed to instantiate eviction configuration of class " + algorithm.getConfigurationClass(), e); } return algorithmConfig; } @SuppressWarnings("unchecked") private static void parseEvictionPolicyConfig(Element element, EvictionAlgorithmConfig target) { target.reset(); ParsedAttributes attributes = XmlConfigHelper.extractAttributes(element); Map updatedElements = new HashMap(); for (Map.Entry entry : attributes.stringAttribs.entrySet()) { String key = (String) entry.getKey(); String value = (String) entry.getValue(); if (key.indexOf("Seconds") > 0) { key = key.substring(0, key.length() - "Seconds".length()); value = value.trim() + "000"; } int intval = 1; try { intval = Integer.parseInt(value); } catch (NumberFormatException e) { // do nothing log.debug("Unable to parse number", e); } value = intval < 1 ? "-1" : value; updatedElements.put(key, value); } attributes.stringAttribs.clear(); attributes.stringAttribs.putAll(updatedElements); XmlConfigHelper.setValues(target, attributes.stringAttribs, false, true); XmlConfigHelper.setValues(target, attributes.xmlAttribs, true, true); } /** * 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(); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/config/parsing/RootElementBuilder.java0000644000175000017500000001446411236775757031706 0ustar moellermoeller/* * 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.config.parsing; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.config.ConfigurationException; import org.jboss.util.xml.JBossEntityResolver; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import java.io.InputStream; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.ArrayList; /** * Parses an xml files and validates xml elements. * * @author Mircea.Markus@jboss.com * @since 3.0 */ public class RootElementBuilder { private static final JBossEntityResolver resolver = new JBossEntityResolver(); public static final Map ALLOWED_CORE_NAMESPACES = new HashMap(4, 1f); public static final Map ALLOWED_REPO_NAMESPACES = new HashMap(4, 1f); public static final String DEFAULT_CORE_NS = "urn:jboss:jbosscache-core:config:3.2"; public static final String DEFAULT_REPO_NS = "urn:jboss:jbosscache-core:cache-repo:3.2"; static { ALLOWED_CORE_NAMESPACES.put("urn:jboss:jbosscache-core:config:3.2", "jbosscache-config-3.2.xsd"); ALLOWED_CORE_NAMESPACES.put("urn:jboss:jbosscache-core:config:3.1", "jbosscache-config-3.1.xsd"); ALLOWED_CORE_NAMESPACES.put("urn:jboss:jbosscache-core:config:3.0", "jbosscache-config-3.0.xsd"); ALLOWED_REPO_NAMESPACES.put("urn:jboss:jbosscache-core:cache-repo:3.2", "jbosscache-registry-3.2.xsd"); ALLOWED_REPO_NAMESPACES.put("urn:jboss:jbosscache-core:cache-repo:3.1", "jbosscache-registry-3.1.xsd"); ALLOWED_REPO_NAMESPACES.put("urn:jboss:jbosscache-core:cache-repo:3.0", "jbosscache-registry-3.0.xsd"); // Globally register the namespaces for (Map.Entry entry: ALLOWED_CORE_NAMESPACES.entrySet()) JBossEntityResolver.registerEntity(entry.getKey(), entry.getValue()); for (Map.Entry entry: ALLOWED_REPO_NAMESPACES.entrySet()) JBossEntityResolver.registerEntity(entry.getKey(), entry.getValue()); } private static final Log log = LogFactory.getLog(RootElementBuilder.class); private ErrorHandler errorHandler; private boolean isValidating; public static final String VALIDATING_SYSTEM_PROPERTY = "jbosscache.config.validate"; public RootElementBuilder(ErrorHandler errorHandler) { this.errorHandler = errorHandler; isValidating = System.getProperty(VALIDATING_SYSTEM_PROPERTY) == null || Boolean.getBoolean(VALIDATING_SYSTEM_PROPERTY); } public RootElementBuilder(ErrorHandler errorHandler, boolean validating) { this.errorHandler = errorHandler; isValidating = validating; } public RootElementBuilder() { this(new FailureErrorHandler()); } public RootElementBuilder(boolean validating) { this(new FailureErrorHandler(), validating); } private String[] namespaces() { List ns = new ArrayList(ALLOWED_CORE_NAMESPACES.size() + ALLOWED_REPO_NAMESPACES.size()); for (String candidate: ALLOWED_CORE_NAMESPACES.keySet()) ns.add(candidate); for (String candidate: ALLOWED_REPO_NAMESPACES.keySet()) ns.add(candidate); return ns.toArray(new String[ns.size()]); } public Element readRoot(InputStream config) { try { DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); docBuilderFactory.setNamespaceAware(true); if (isValidating) { docBuilderFactory.setValidating(true); docBuilderFactory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage", "http://www.w3.org/2001/XMLSchema"); docBuilderFactory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaSource", namespaces()); } DocumentBuilder parser = docBuilderFactory.newDocumentBuilder(); parser.setEntityResolver(resolver); parser.setErrorHandler(errorHandler); Document doc = parser.parse(config); Element root = doc.getDocumentElement(); root.normalize(); return root; } catch (Exception e) { throw new ConfigurationException("Could not parse the config file", e); } } /** * Default schema validation error handler, that throws an exception on validation errors. */ private static class FailureErrorHandler implements ErrorHandler { public void warning(SAXParseException exception) throws SAXException { logAndThrowException(exception); } public void error(SAXParseException exception) throws SAXException { logAndThrowException(exception); } public void fatalError(SAXParseException exception) throws SAXException { logAndThrowException(exception); } private void logAndThrowException(SAXParseException exception) { log.error("Configuration warning: " + exception.getMessage()); throw new ConfigurationException("Incorrect configuration file. Use '-Djbosscache.config.validate=false' to disable validation.", exception); } } public boolean isValidating() { return isValidating; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/config/parsing/ParsedAttributes.java0000644000175000017500000000276411111047320031371 0ustar moellermoeller/* * 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.config.parsing; import org.w3c.dom.Element; import java.util.Map; /** * Helper class for holding attributes defined in configuration file. * * @author Mircea.Markus@jboss.com * @since 3.0 */ public class ParsedAttributes { public final Map stringAttribs; public final Map xmlAttribs; ParsedAttributes(Map strings, Map elements) { this.stringAttribs = strings; this.xmlAttribs = elements; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/config/parsing/XmlConfigurationParser.java0000644000175000017500000004370711236775757032611 0ustar moellermoeller/* * 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.config.parsing; 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.config.ConfigurationException; import org.jboss.cache.config.CustomInterceptorConfig; import org.jboss.cache.config.parsing.element.BuddyElementParser; import org.jboss.cache.config.parsing.element.CustomInterceptorsElementParser; import org.jboss.cache.config.parsing.element.EvictionElementParser; import org.jboss.cache.config.parsing.element.LoadersElementParser; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.util.FileLookup; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.xml.sax.ErrorHandler; import java.io.InputStream; import java.net.URL; import java.util.List; /** * Reads in XMLconfiguration files and spits out a {@link org.jboss.cache.config.Configuration} object. * By default this class uses a validating parser (configurable). *

    * Following system properties can be used for customizing parser behavior: *

      *
    • -Djbosscache.config.validate=false will make the parser non-validating
    • *
    • -Djbosscache.config.schemaLocation=url allows one to specify a validation schema that would override the one specified in the the xml document
    • *
    * This class is stateful and one instance should be used for parsing a single configuration file. * * @author Mircea.Markus@jboss.com * @see org.jboss.cache.config.parsing.RootElementBuilder * @since 3.0 */ public class XmlConfigurationParser extends XmlParserBase { private RootElementBuilder rootElementBuilder; /** * the resulting configuration. */ private Configuration config = new Configuration(); private Element root; /** * If validation is on (default) one can specify an error handler for handling validation errors. * The default error handler just logs parsing errors received. */ public XmlConfigurationParser(ErrorHandler errorHandler) { rootElementBuilder = new RootElementBuilder(errorHandler); } /** * Same as {@link #XmlConfigurationParser(org.xml.sax.ErrorHandler)}. * * @param validating should the underlaying parser disable the validation? */ public XmlConfigurationParser(boolean validating, ErrorHandler errorHandler) { rootElementBuilder = new RootElementBuilder(errorHandler, validating); } /** * Constructs a parser having validation enabled with a ErrorHandler that only logs the parser errors. */ public XmlConfigurationParser() { rootElementBuilder = new RootElementBuilder(); } /** * Parses an XML file and returns a new configuration. * For looking up the file, {@link org.jboss.cache.util.FileLookup} is used. * * @see org.jboss.cache.util.FileLookup */ public Configuration parseFile(String filename) { InputStream is = new FileLookup().lookupFile(filename); if (is == null) { throw new ConfigurationException("Unable to find config file " + filename + " either in classpath or on the filesystem!"); } return parseStream(is); } /** * Similar to {@link #parseFile(String)}, just that it does not create the input stream. */ public Configuration parseStream(InputStream configStream) { readRoot(configStream); return processElements(false); } /** * Root should be the jbosscache element in the configuration file. */ public Configuration parseElement(Element root) { this.root = root; this.root.normalize(); return processElements(false); } public Configuration parseElementIgnoringRoot(Element root) { this.root = root; this.root.normalize(); return processElements(true); } public boolean isValidating() { return rootElementBuilder.isValidating(); } private Configuration processElements(boolean ignoreRoot) { coreNamespace = root.getNamespaceURI(); if (coreNamespace == null) coreNamespace = RootElementBuilder.DEFAULT_CORE_NS; // use the default if (!ignoreRoot && (!"jbosscache".equals(root.getLocalName()) || !isAllowedCoreNamespace(coreNamespace)) ) { throw new ConfigurationException("Expected root element " + (isValidating() ? " in either of " + getAllowedCoreNamespaces() + " namespaces" : "")); } try { configureLocking(getSingleElement("locking")); configureTransaction(getSingleElement("transaction")); configureClustering(getSingleElement("clustering")); configureSerialization(getSingleElement("serialization")); configureInvalidation(getSingleElement("invalidation")); configureStartup(getSingleElement("startup")); configureShutdown(getSingleElement("shutdown")); configureJmxStatistics(getSingleElement("jmxStatistics")); configureEviction(getSingleElement("eviction")); configureCacheLoaders(getSingleElement("loaders")); configureCustomInterceptors(getSingleElement("customInterceptors")); configureListeners(getSingleElement("listeners")); configureInvocationBatching(getSingleElement("invocationBatching")); } catch (Exception e) { throw new ConfigurationException("Unexpected exception while parsing the configuration file", e); } return config; } private void configureClustering(Element e) { if (e == null) return; //we might not have this configured // there are 2 attribs - mode and clusterName boolean repl = true; String mode = getAttributeValue(e, "mode").toUpperCase(); if (mode.startsWith("R")) repl = true; else if (mode.startsWith("I")) repl = false; Element asyncEl = getSingleElementInCoreNS("async", e); Element syncEl = getSingleElementInCoreNS("sync", e); if (syncEl != null && asyncEl != null) throw new ConfigurationException("Cannot have sync and async elements within the same cluster element!"); boolean sync = asyncEl == null; // even if both are null, we default to sync if (sync) { config.setCacheMode(repl ? CacheMode.REPL_SYNC : CacheMode.INVALIDATION_SYNC); configureSyncMode(syncEl); } else { config.setCacheMode(repl ? CacheMode.REPL_ASYNC : CacheMode.INVALIDATION_ASYNC); configureAsyncMode(asyncEl); } String cn = getAttributeValue(e, "clusterName"); if (existsAttribute(cn)) config.setClusterName(cn); configureBuddyReplication(getSingleElementInCoreNS("buddy", e)); configureStateRetrieval(getSingleElementInCoreNS("stateRetrieval", e)); configureTransport(getSingleElementInCoreNS("jgroupsConfig", e)); } private void configureStateRetrieval(Element element) { if (element == null) return; //we might not have this configured String tmp = getAttributeValue(element, "fetchInMemoryState"); if (existsAttribute(tmp)) config.setFetchInMemoryState(getBoolean(tmp)); tmp = getAttributeValue(element, "timeout"); if (existsAttribute(tmp)) config.setStateRetrievalTimeout(getLong(tmp)); tmp = getAttributeValue(element, "nonBlocking"); if (existsAttribute(tmp)) config.setNonBlockingStateTransfer(getBoolean(tmp)); } private void configureTransaction(Element element) { if (element == null) return; String attrName = "transactionManagerLookupClass"; String txMngLookupClass = getAttributeValue(element, attrName); if (existsAttribute(txMngLookupClass)) config.setTransactionManagerLookupClass(txMngLookupClass); String syncRollbackPhase = getAttributeValue(element, "syncRollbackPhase"); if (existsAttribute(syncRollbackPhase)) config.setSyncRollbackPhase(getBoolean(syncRollbackPhase)); String syncCommitPhase = getAttributeValue(element, "syncCommitPhase"); if (existsAttribute(syncCommitPhase)) config.setSyncCommitPhase(getBoolean(syncCommitPhase)); } private void configureSerialization(Element element) { if (element == null) return; String objectInputStreamPoolSize = getAttributeValue(element, "objectInputStreamPoolSize"); if (existsAttribute(objectInputStreamPoolSize)) config.setObjectInputStreamPoolSize(getInt(objectInputStreamPoolSize)); String objectOutputStreamPoolSize = getAttributeValue(element, "objectOutputStreamPoolSize"); if (existsAttribute(objectOutputStreamPoolSize)) config.setObjectOutputStreamPoolSize(getInt(objectOutputStreamPoolSize)); String version = getAttributeValue(element, "version"); if (existsAttribute(version)) config.setReplVersionString(version); String marshallerClass = getAttributeValue(element, "marshallerClass"); if (existsAttribute(marshallerClass)) config.setMarshallerClass(marshallerClass); String useLazyDeserialization = getAttributeValue(element, "useLazyDeserialization"); if (existsAttribute(useLazyDeserialization)) config.setUseLazyDeserialization(getBoolean(useLazyDeserialization)); String useRegionBasedMarshalling = getAttributeValue(element, "useRegionBasedMarshalling"); if (existsAttribute(useRegionBasedMarshalling)) config.setUseRegionBasedMarshalling(getBoolean(useRegionBasedMarshalling)); } private void configureCustomInterceptors(Element element) { if (element == null) return; //this element might be missing CustomInterceptorsElementParser parser = new CustomInterceptorsElementParser(coreNamespace); List interceptorConfigList = parser.parseCustomInterceptors(element); config.setCustomInterceptors(interceptorConfigList); } private void configureListeners(Element element) { if (element == null) return; //this element is optional String asyncPoolSizeStr = getAttributeValue(element, "asyncPoolSize"); if (existsAttribute(asyncPoolSizeStr)) config.setListenerAsyncPoolSize(getInt(asyncPoolSizeStr)); String asyncQueueSizeStr = getAttributeValue(element, "asyncQueueSize"); if (existsAttribute(asyncQueueSizeStr)) config.setListenerAsyncQueueSize(getInt(asyncQueueSizeStr)); } private void configureInvocationBatching(Element element) { if (element == null) return; //this element is optional boolean enabled = getBoolean(getAttributeValue(element, "enabled")); config.setInvocationBatchingEnabled(enabled); } private void configureBuddyReplication(Element element) { if (element == null) return;//buddy config might not exist, expect that BuddyElementParser buddyElementParser = new BuddyElementParser(coreNamespace); BuddyReplicationConfig brConfig = buddyElementParser.parseBuddyElement(element); config.setBuddyReplicationConfig(brConfig); } private void configureCacheLoaders(Element element) { if (element == null) return; //null cache loaders are allowed LoadersElementParser clElementParser = new LoadersElementParser(coreNamespace); CacheLoaderConfig cacheLoaderConfig = clElementParser.parseLoadersElement(element); config.setCacheLoaderConfig(cacheLoaderConfig); } private void configureEviction(Element element) { if (element == null) return; //no eviction might be configured EvictionElementParser evictionElementParser = new EvictionElementParser(coreNamespace); config.setEvictionConfig(evictionElementParser.parseEvictionElement(element)); } private void configureJmxStatistics(Element element) { if (element == null) return; //might not be specified String enabled = getAttributeValue(element, "enabled"); config.setExposeManagementStatistics(getBoolean(enabled)); } private void configureShutdown(Element element) { if (element == null) return; String hookBehavior = getAttributeValue(element, "hookBehavior"); if (existsAttribute(hookBehavior)) config.setShutdownHookBehavior(hookBehavior); } private void configureTransport(Element element) { if (element == null) return; //transport might be missing // first see if a configFile is provided String cfgFile = getAttributeValue(element, "configFile"); if (existsAttribute(cfgFile)) { // try and load this file URL u = new FileLookup().lookupFileLocation(cfgFile); config.setJgroupsConfigFile(u); } else { String multiplexerStack = getAttributeValue(element, "multiplexerStack"); if (existsAttribute(multiplexerStack)) { config.setMultiplexerStack(multiplexerStack); } else { JGroupsStackParser stackParser = new JGroupsStackParser(); String clusterConfigStr = stackParser.parseClusterConfigXml(element); if (clusterConfigStr != null && clusterConfigStr.trim().length() > 0) config.setClusterConfig(clusterConfigStr); } } } private void configureStartup(Element element) { if (element == null) return; //we might not have this configured String inactiveOnStartup = getAttributeValue(element, "regionsInactiveOnStartup"); if (existsAttribute(inactiveOnStartup)) config.setInactiveOnStartup(getBoolean(inactiveOnStartup)); } private void configureInvalidation(Element element) { if (element == null) return; //might be replication Element async = getSingleElement("async"); if (async != null) { config.setCacheMode(Configuration.CacheMode.INVALIDATION_ASYNC); configureAsyncMode(getSingleElementInCoreNS("async", element)); } Element sync = getSingleElement("sync"); if (sync != null) { config.setCacheMode(Configuration.CacheMode.INVALIDATION_SYNC); configureSyncMode(getSingleElementInCoreNS("sync", element)); } } private void configureSyncMode(Element element) { String replTimeout = getAttributeValue(element, "replTimeout"); if (existsAttribute(replTimeout)) config.setSyncReplTimeout(getLong(replTimeout)); } private void configureAsyncMode(Element element) { String useReplQueue = getAttributeValue(element, "useReplQueue"); if (existsAttribute(useReplQueue)) config.setUseReplQueue(getBoolean(useReplQueue)); String replQueueInterval = getAttributeValue(element, "replQueueInterval"); if (existsAttribute(replQueueInterval)) config.setReplQueueInterval(getLong(replQueueInterval)); String replQueueMaxElements = getAttributeValue(element, "replQueueMaxElements"); if (existsAttribute(replQueueMaxElements)) config.setReplQueueMaxElements(getInt(replQueueMaxElements)); String serializationExecutorPoolSize = getAttributeValue(element, "serializationExecutorPoolSize"); if (existsAttribute(serializationExecutorPoolSize)) config.setSerializationExecutorPoolSize(getInt(serializationExecutorPoolSize)); String serializationExecutorQueueSize = getAttributeValue(element, "serializationExecutorQueueSize"); if (existsAttribute(serializationExecutorQueueSize)) config.setSerializationExecutorQueueSize(getInt(serializationExecutorQueueSize)); } private void configureLocking(Element element) { String tmp = getAttributeValue(element, "isolationLevel"); if (existsAttribute(tmp)) config.setIsolationLevel(IsolationLevel.valueOf(tmp)); tmp = getAttributeValue(element, "lockParentForChildInsertRemove"); if (existsAttribute(tmp)) config.setLockParentForChildInsertRemove(getBoolean(tmp)); tmp = getAttributeValue(element, "lockAcquisitionTimeout"); if (existsAttribute(tmp)) config.setLockAcquisitionTimeout(getLong(tmp)); tmp = getAttributeValue(element, "nodeLockingScheme"); if (existsAttribute(tmp)) config.setNodeLockingScheme(tmp); tmp = getAttributeValue(element, "writeSkewCheck"); if (existsAttribute(tmp)) config.setWriteSkewCheck(getBoolean(tmp)); tmp = getAttributeValue(element, "useLockStriping"); if (existsAttribute(tmp)) config.setUseLockStriping(getBoolean(tmp)); tmp = getAttributeValue(element, "concurrencyLevel"); if (existsAttribute(tmp)) config.setConcurrencyLevel(getInt(tmp)); } private Element getSingleElement(String elementName) { return getSingleElementInCoreNS(elementName, root); } private void readRoot(InputStream config) { root = rootElementBuilder.readRoot(config); } /** * Tests whether the element passed in is a modern (3.0) config element rather than a legacy one. * * @param element element to test * @return true of the element is a modern one and can be parsed using the current parser. */ public boolean isValidElementRoot(Element element) { // simply test for the "jbosscache" element. NodeList elements = element.getElementsByTagName("jbosscache"); return elements != null && elements.getLength() > 0; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/config/parsing/XmlConfigHelper.java0000644000175000017500000005560011240253605031137 0ustar moellermoeller/* * 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.config.parsing; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.util.BeanUtils; 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 javax.xml.transform.Result; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import java.beans.PropertyEditor; import java.beans.PropertyEditorManager; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import java.util.Properties; /** * A simple XML utility class for reading configuration elements * * @author Manik Surtani (manik AT jboss DOT org) */ public class XmlConfigHelper { private static final Log log = LogFactory.getLog(XmlConfigHelper.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; } /** * 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) { String stringContents = readStringContents(element, elementName); if (stringContents == null) return new Properties(); // JBCACHE-531: escape all backslash characters stringContents = escapeBackslashes(stringContents); ByteArrayInputStream is = null; Properties properties = null; try { is = new ByteArrayInputStream(stringContents.trim().getBytes("ISO8859_1")); properties = new Properties(); properties.load(is); is.close(); } catch (IOException e) { log.warn("Unexpected", e); throw new ConfigurationException("Exception occured while reading properties from XML document", e); } 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 stringToElementInCoreNS(String xml) throws Exception { xml = "" + xml + ""; ByteArrayInputStream bais = new ByteArrayInputStream(xml.getBytes("utf8")); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); DocumentBuilder builder = factory.newDocumentBuilder(); Document d = builder.parse(bais); bais.close(); return getFirstChildElement(d.getDocumentElement()); } /** * 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")); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document d = builder.parse(bais); bais.close(); return d.getDocumentElement(); } /** * Gets the first child element of an element * * @param element the parent * @return the first child element or null if there isn't one */ public static Element getFirstChildElement(Element element) { Node child = element.getFirstChild(); while (child != null && child.getNodeType() != Node.ELEMENT_NODE) { child = child.getNextSibling(); } return (Element) child; } /** * 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(); docBuilderFactory.setNamespaceAware(true); 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.valueOf(val); } return defaultValue; } public static void setValues(Object target, Map attribs, boolean isXmlAttribs, boolean failOnMissingSetter) { Class objectClass = target.getClass(); // go thru simple string setters first. for (Map.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) { // try other setters that may fit later on. Don't throw this exception though. } catch (Exception e) { throw new ConfigurationException("Unable to invoke setter " + setter + " on " + objectClass, e); } boolean setterFound = false; // 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) { if (log.isTraceEnabled()) { log.trace("Rejecting setter " + m + " on class " + objectClass + " due to incorrect number of parameters"); } continue; // try another param with the same name. } 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); setterFound = true; break; } catch (Exception e) { throw new ConfigurationException("Unable to invoke setter " + setter + " on " + objectClass, e); } } } if (!setterFound && failOnMissingSetter) { throw new ConfigurationException("Couldn't find a setter named [" + setter + "] which takes a single parameter, for parameter " + propName + " on class [" + objectClass + "]"); } } } public static ParsedAttributes extractAttributes(Element source) { Map stringAttribs = new HashMap(); Map xmlAttribs = new HashMap(); NodeList list = source.getElementsByTagName(ATTR); if (log.isTraceEnabled()) log.trace("Attribute size: " + list.getLength()); // loop through attributes for (int loop = 0; loop < list.getLength(); loop++) { Node node = list.item(loop); if (node.getNodeType() != Node.ELEMENT_NODE) continue; // for each element (attribute) ... Element element = (Element) node; String name = element.getAttribute(NAME); String valueStr = getElementContent(element, true); Element valueXml = null; if (valueStr.length() == 0) { // This may be an XML element ... valueXml = getSubElement(element, CONFIG_ATTR); if (valueXml != null) xmlAttribs.put(name, valueXml); } else { if (valueStr.length() > 0) stringAttribs.put(name, valueStr); } } return new ParsedAttributes(stringAttribs, xmlAttribs); } public static Properties extractProperties(Element source) { Properties p = new Properties(); NodeList list = source.getElementsByTagName("property"); // loop through attributes for (int loop = 0; loop < list.getLength(); loop++) { Node node = list.item(loop); if (node.getNodeType() != Node.ELEMENT_NODE) continue; // for each element (attribute) ... Element element = (Element) node; String name = element.getAttribute(NAME); String valueStr = element.getAttribute("value"); if (valueStr.length() > 0) { valueStr = valueStr.trim(); valueStr = StringPropertyReplacer.replaceProperties(valueStr); p.put(name, valueStr); } } return p; } public static String toString(Element e) { try { TransformerFactory tfactory = TransformerFactory.newInstance(); Transformer xform = tfactory.newTransformer(); Source src = new DOMSource(e); java.io.StringWriter writer = new StringWriter(); Result result = new javax.xml.transform.stream.StreamResult(writer); xform.transform(src, result); return writer.toString(); } catch (Exception ex) { return "Unable to convert to string: " + ex.toString(); } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/config/parsing/XmlParserBase.java0000644000175000017500000000765411236775757030655 0ustar moellermoeller/* * 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.config.parsing; import org.jboss.cache.config.LegacyConfigurationException; import org.jboss.cache.util.Immutables; import org.jboss.util.StringPropertyReplacer; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import java.util.List; /** * Contains utility methods that might be useful to most of the parsers. * * @author Mircea.Markus@jboss.com * @since 3.0 */ public abstract class XmlParserBase { protected String coreNamespace; /** * @see Integer#parseInt(String) */ protected int getInt(String intStr) { return Integer.parseInt(intStr); } /** * @see Long#parseLong(String) */ protected long getLong(String longStr) { return Long.parseLong(longStr); } /** * @see Boolean#valueOf(String) */ protected boolean getBoolean(String str) { return str == null ? false : Boolean.valueOf(str); } /** * @return true if the given value is not empty. */ protected boolean existsAttribute(String attrValue) { return attrValue != null && attrValue.length() > 0; } /** * Convenient method for retrieving a single element with the give name. */ protected Element getSingleElement(String namespace, String elementName, Element parent) { NodeList nodeList = parent.getElementsByTagNameNS(namespace, elementName); if (nodeList.getLength() == 0) { return null; } return (Element) nodeList.item(0); } /** * Convenient method for retrieving a single element with the give name. */ protected Element getSingleElementInCoreNS(String elementName, Element parent) { return getSingleElement(coreNamespace, elementName, parent); } /** * Beside querying the element for its attribute value, it will look into the value, if any, and replace the * jboss properties(e.g. ${someValue:defaultValue}. *

    * {@link org.jboss.util.StringPropertyReplacer#replaceProperties(String)} */ protected String getAttributeValue(Element element, String attrName) { if (element == null || attrName == null) return null; String value = element.getAttribute(attrName); return value == null ? null : StringPropertyReplacer.replaceProperties(value); } protected void assertNotLegacyElement(Element e) { // is this a legacy element?!? String name = e.getNodeName(); if ("config".equals(name)) throw new LegacyConfigurationException("Legacy element encountered when using parser " + getClass().getSimpleName()); } protected boolean isAllowedCoreNamespace(String namespace) { for (String ns: RootElementBuilder.ALLOWED_CORE_NAMESPACES.keySet()) { if (ns.equals(namespace)) return true; } return false; } protected List getAllowedCoreNamespaces() { return Immutables.immutableListConvert(RootElementBuilder.ALLOWED_CORE_NAMESPACES.keySet()); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/config/parsing/element/0000755000175000017500000000000011675222006026674 5ustar moellermoeller././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/config/parsing/element/LoadersElementParser.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/config/parsing/element/LoadersElementParser.j0000644000175000017500000001443411236775757033160 0ustar moellermoeller/* * 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.config.parsing.element; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.config.parsing.XmlConfigHelper; import org.jboss.cache.config.parsing.XmlParserBase; import org.jboss.cache.config.parsing.RootElementBuilder; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import java.util.Properties; /** * Utility class for parsing the 'loaders' element in the .xml configuration file. *

     * Note: class does not rely on element position in the configuration file.
     *       It does not rely on element's name either.
     * 
    * * @author Mircea.Markus@jboss.com * @since 3.0 */ public class LoadersElementParser extends XmlParserBase { public LoadersElementParser() { this(RootElementBuilder.DEFAULT_CORE_NS); } public LoadersElementParser(String coreNamespace) { this.coreNamespace = coreNamespace; } public CacheLoaderConfig parseLoadersElement(Element element) { assertNotLegacyElement(element); CacheLoaderConfig cacheLoaderConfig = new CacheLoaderConfig(); String passivation = getAttributeValue(element, "passivation"); if (existsAttribute(passivation)) cacheLoaderConfig.setPassivation(getBoolean(passivation)); String shared = getAttributeValue(element, "shared"); if (existsAttribute(shared)) cacheLoaderConfig.setShared(getBoolean(shared)); String preload = getPreloadString(getSingleElementInCoreNS("preload", element)); if (preload != null) cacheLoaderConfig.setPreload(preload); NodeList cacheLoaderNodes = element.getElementsByTagName("loader"); for (int i = 0; i < cacheLoaderNodes.getLength(); i++) { Element indivElement = (Element) cacheLoaderNodes.item(i); CacheLoaderConfig.IndividualCacheLoaderConfig iclc = parseIndividualCacheLoaderConfig(indivElement); cacheLoaderConfig.addIndividualCacheLoaderConfig(iclc); } return cacheLoaderConfig; } private CacheLoaderConfig.IndividualCacheLoaderConfig parseIndividualCacheLoaderConfig(Element indivElement) { CacheLoaderConfig.IndividualCacheLoaderConfig iclc = new CacheLoaderConfig.IndividualCacheLoaderConfig(); String async = getAttributeValue(indivElement, "async"); if (existsAttribute(async)) iclc.setAsync(getBoolean(async)); String fetchPersistentState = getAttributeValue(indivElement, "fetchPersistentState"); if (existsAttribute(fetchPersistentState)) iclc.setFetchPersistentState(getBoolean(fetchPersistentState)); String ignoreModifications = getAttributeValue(indivElement, "ignoreModifications"); if (existsAttribute(ignoreModifications)) iclc.setIgnoreModifications(getBoolean(ignoreModifications)); String purgeOnStartup = getAttributeValue(indivElement, "purgeOnStartup"); if (existsAttribute(purgeOnStartup)) iclc.setPurgeOnStartup(getBoolean(purgeOnStartup)); String clClass = getAttributeValue(indivElement, "class"); if (!existsAttribute(clClass)) throw new ConfigurationException("Missing 'class' attribute for cache loader configuration"); iclc.setClassName(clClass); iclc.setProperties(XmlConfigHelper.readPropertiesContents(indivElement, "properties")); CacheLoaderConfig.IndividualCacheLoaderConfig.SingletonStoreConfig ssc = parseSingletonStoreConfig(getSingleElementInCoreNS("singletonStore", indivElement)); if (ssc != null) { iclc.setSingletonStoreConfig(ssc); } return iclc; } private String getPreloadString(Element preloadElement) { if (preloadElement == null) return null; //might be no preload NodeList nodesToPreload = preloadElement.getElementsByTagName("node"); StringBuilder result = new StringBuilder(); for (int i = 0; i < nodesToPreload.getLength(); i++) { Element node = (Element) nodesToPreload.item(i); String fqn2preload = getAttributeValue(node, "fqn"); if (!existsAttribute(fqn2preload)) throw new ConfigurationException("Missing 'fqn' attribute in 'preload' element"); if (i > 0) result.append(","); result.append(fqn2preload); } //no elements defined for preload so by default load the root if (nodesToPreload.getLength() == 0) { result.append("/"); } return result.toString(); } public CacheLoaderConfig.IndividualCacheLoaderConfig.SingletonStoreConfig parseSingletonStoreConfig(Element element) { if (element == null) return null; //might happen, this config option is not mandatory boolean singletonStoreEnabled = getBoolean(getAttributeValue(element, "enabled")); String singletonStoreClass = getAttributeValue(element, "class"); CacheLoaderConfig.IndividualCacheLoaderConfig.SingletonStoreConfig ssc = new CacheLoaderConfig.IndividualCacheLoaderConfig.SingletonStoreConfig(); if (existsAttribute(singletonStoreClass)) ssc.setSingletonStoreClass(singletonStoreClass); Properties singletonStoreproperties = XmlConfigHelper.readPropertiesContents(element, "properties"); ssc.setSingletonStoreEnabled(singletonStoreEnabled); ssc.setSingletonStoreClass(singletonStoreClass); ssc.setSingletonStoreproperties(singletonStoreproperties); return ssc; } } ././@LongLink0000000000000000000000000000016300000000000011565 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/config/parsing/element/CustomInterceptorsElementParser.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/config/parsing/element/CustomInterceptorsElem0000644000175000017500000001130311236775757033317 0ustar moellermoeller/* * 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.config.parsing.element; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.config.CustomInterceptorConfig; import org.jboss.cache.config.parsing.XmlConfigHelper; import org.jboss.cache.config.parsing.XmlParserBase; import org.jboss.cache.config.parsing.RootElementBuilder; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.util.Util; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import java.util.ArrayList; import java.util.List; import java.util.Properties; /** * Utility class for parsing 'buddy' element in the .xml configuration file. *
     * Note: class does not rely on element position in the configuration file.
     *       It does not rely on element's name either.
     * 
    * * @author Mircea.Markus@jboss.com * @since 3.0 */ public class CustomInterceptorsElementParser extends XmlParserBase { public CustomInterceptorsElementParser() { this(RootElementBuilder.DEFAULT_CORE_NS); } public CustomInterceptorsElementParser(String coreNamespace) { this.coreNamespace = coreNamespace; } /** * Iterates within the given element looking for custom interceptors. * * @param element should not be null * @return a list which might be empty, never null */ public List parseCustomInterceptors(Element element) { NodeList interceptorNodes = element.getElementsByTagName("interceptor"); List interceptorConfigs = new ArrayList(interceptorNodes.getLength()); for (int i = 0; i < interceptorNodes.getLength(); i++) { boolean first = false; boolean last = false; int index = -1; String after = null; String before = null; Element interceptorElement = (Element) interceptorNodes.item(i); String position = getAttributeValue(interceptorElement, "position"); if (existsAttribute(position) && "first".equalsIgnoreCase(position)) { first = true; } if (existsAttribute(position) && "last".equalsIgnoreCase(position)) { last = true; } String indexStr = getAttributeValue(interceptorElement, "index"); index = existsAttribute(indexStr) ? getInt(indexStr) : -1; before = getAttributeValue(interceptorElement, "before"); if (!existsAttribute(before)) before = null; after = getAttributeValue(interceptorElement, "after"); if (!existsAttribute(after)) after = null; CommandInterceptor interceptor = buildCommandInterceptor(interceptorElement); CustomInterceptorConfig customInterceptorConfig = new CustomInterceptorConfig(interceptor, first, last, index, after, before); interceptorConfigs.add(customInterceptorConfig); } return interceptorConfigs; } /** * Builds the interceptor based on the interceptor class and also sets all its attributes. */ private CommandInterceptor buildCommandInterceptor(Element element) { String interceptorClass = getAttributeValue(element, "class"); if (!existsAttribute(interceptorClass)) throw new ConfigurationException("Interceptor class cannot be empty!"); CommandInterceptor result; try { result = (CommandInterceptor) Util.loadClass(interceptorClass).newInstance(); } catch (Exception e) { throw new ConfigurationException("CommandInterceptor class is not properly loaded in classloader", e); } Properties p = XmlConfigHelper.extractProperties(element); XmlConfigHelper.setValues(result, p, false, true); return result; } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/config/parsing/element/EvictionElementParser.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/config/parsing/element/EvictionElementParser.0000644000175000017500000001637311236775757033201 0ustar moellermoeller/* * 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.config.parsing.element; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.config.EvictionAlgorithmConfig; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.config.MissingPolicyException; import org.jboss.cache.config.parsing.XmlConfigHelper; import org.jboss.cache.config.parsing.XmlParserBase; import org.jboss.cache.config.parsing.RootElementBuilder; import org.jboss.cache.eviction.EvictionAlgorithm; import org.jboss.cache.util.Util; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import java.util.LinkedList; import java.util.List; import java.util.Properties; /** * Knows how to parse the eviction xml element. *
     * Note: class does not rely on element position in the configuration file.
     *       It does not rely on element's name either.
     * 
    * * @author Mircea.Markus@jboss.com * @since 3.0 */ public class EvictionElementParser extends XmlParserBase { public EvictionElementParser() { this(RootElementBuilder.DEFAULT_CORE_NS); } public EvictionElementParser(String coreNamespace) { this.coreNamespace = coreNamespace; } public EvictionConfig parseEvictionElement(Element evictionElement) { assertNotLegacyElement(evictionElement); EvictionConfig evictionConfig = new EvictionConfig(); String wakeUpInterval = getAttributeValue(evictionElement, "wakeUpInterval"); if (existsAttribute(wakeUpInterval)) { evictionConfig.setWakeupInterval(getInt(wakeUpInterval)); } else { throw new ConfigurationException("Missing mandatory attribute wakeUpInterval"); } List evictionRegionConfigs = new LinkedList(); Element defaultRegion = getSingleElementInCoreNS("default", evictionElement); if (defaultRegion != null) { EvictionRegionConfig defaultRegionConfig = getEvictionRegionConfig(defaultRegion, null, true); if (defaultRegionConfig.getEvictionAlgorithmConfig() == null) throw new MissingPolicyException("Default eviction region should have an evictionAlgorithmClass defined."); evictionConfig.setDefaultEvictionRegionConfig(defaultRegionConfig); } NodeList regions = evictionElement.getElementsByTagName("region"); for (int i = 0; i < regions.getLength(); i++) { Element regionConfig = (Element) regions.item(i); EvictionRegionConfig erc = getEvictionRegionConfig(regionConfig, evictionConfig.getDefaultEvictionRegionConfig(), false); evictionConfig.applyDefaults(erc); evictionRegionConfigs.add(erc); } evictionConfig.setEvictionRegionConfigs(evictionRegionConfigs); return evictionConfig; } @SuppressWarnings("unchecked") private EvictionRegionConfig getEvictionRegionConfig(Element element, EvictionRegionConfig defaultRegion, boolean isDefault) { EvictionRegionConfig erc = new EvictionRegionConfig(); erc.setRegionName(getAttributeValue(element, "name")); String queueSize = getAttributeValue(element, "eventQueueSize"); if (existsAttribute(queueSize)) { erc.setEventQueueSize(getInt(queueSize)); } else if (defaultRegion == null) { erc.setEventQueueSize(EvictionConfig.EVENT_QUEUE_SIZE_DEFAULT); } String algorithmClassName = getAttributeValue(element, "algorithmClass"); EvictionAlgorithmConfig algorithmConfig = null; // every eviction region config needs an algorithm config. if (existsAttribute(algorithmClassName)) { EvictionAlgorithm algorithm; Class algorithmClass; // try using a 'getInstance()' factory. try { algorithmClass = Util.loadClass(algorithmClassName); } catch (Exception e) { throw new RuntimeException("Unable to load eviction algorithm class [" + algorithmClassName + "]", e); } try { algorithm = Util.getInstance(algorithmClass); } catch (Exception e) { throw new ConfigurationException("Unable to construct eviction algorithm class [" + algorithmClassName + "]", e); } try { algorithmConfig = Util.getInstance(algorithm.getConfigurationClass()); } catch (Exception e) { throw new RuntimeException("Failed to instantiate eviction algorithm configuration class [" + algorithm.getConfigurationClass() + "]", e); } } else { if (!isDefault) { if (defaultRegion == null || defaultRegion.getEvictionAlgorithmConfig() == null) { throw new MissingPolicyException("There is no Eviction Algorithm Class specified on the region or for the entire cache!"); } else { try { algorithmConfig = defaultRegion.getEvictionAlgorithmConfig().clone(); } catch (CloneNotSupportedException e) { throw new ConfigurationException("Unable to clone eviction algorithm configuration from default", e); } } } } if (algorithmConfig != null) { parseEvictionPolicyConfig(element, algorithmConfig); erc.setEvictionAlgorithmConfig(algorithmConfig); } String actionPolicyClass = getAttributeValue(element, "actionPolicyClass"); if (existsAttribute(actionPolicyClass)) { erc.setEvictionActionPolicyClassName(actionPolicyClass); } else if (defaultRegion == null) { // this is the default region. Make sure we set the default EvictionActionPolicyClass. erc.setEvictionActionPolicyClassName(EvictionConfig.EVICTION_ACTION_POLICY_CLASS_DEFAULT); } return erc; } public static void parseEvictionPolicyConfig(Element element, EvictionAlgorithmConfig target) { // target.reset(); Properties p = XmlConfigHelper.extractProperties(element); XmlConfigHelper.setValues(target, p, false, true); } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/config/parsing/element/BuddyElementParser.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/config/parsing/element/BuddyElementParser.jav0000644000175000017500000001107611236775757033164 0ustar moellermoeller/* * 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.config.parsing.element; import org.jboss.cache.buddyreplication.NextMemberBuddyLocator; import org.jboss.cache.config.BuddyReplicationConfig; import org.jboss.cache.config.parsing.XmlConfigHelper; import org.jboss.cache.config.parsing.XmlParserBase; import org.jboss.cache.config.parsing.RootElementBuilder; import org.w3c.dom.Element; import java.util.Properties; /** * Utility class for parsing 'buddy' element in the .xml configuration file. *
     * Note: class does not rely on element position in the configuration file.
     *       It does not rely on element's name either.
     * 
    * * @author Mircea.Markus@jboss.com * @since 3.0 */ public class BuddyElementParser extends XmlParserBase { public BuddyElementParser() { this(RootElementBuilder.DEFAULT_CORE_NS); } public BuddyElementParser(String coreNamespace) { this.coreNamespace = coreNamespace; } public BuddyReplicationConfig parseBuddyElement(Element element) { assertNotLegacyElement(element); BuddyReplicationConfig brc = new BuddyReplicationConfig(); String enabled = getAttributeValue(element, "enabled"); brc.setEnabled(getBoolean(enabled)); String buddyPoolName = getAttributeValue(element, "poolName"); if (existsAttribute(buddyPoolName)) brc.setBuddyPoolName(buddyPoolName); String buddyCommunicationTimeout = getAttributeValue(element, "communicationTimeout"); if (existsAttribute(buddyCommunicationTimeout)) brc.setBuddyCommunicationTimeout(getInt(buddyCommunicationTimeout)); parseDataGravitationElement(getSingleElementInCoreNS("dataGravitation", element), brc); BuddyReplicationConfig.BuddyLocatorConfig blc = parseBuddyLocatorConfig(getSingleElementInCoreNS("locator", element)); brc.setBuddyLocatorConfig(blc); return brc; } private BuddyReplicationConfig.BuddyLocatorConfig parseBuddyLocatorConfig(Element element) { if (element == null) return defaultBuddyLocatorConfig(); BuddyReplicationConfig.BuddyLocatorConfig result = new BuddyReplicationConfig.BuddyLocatorConfig(); String buddyLocatorClass = getAttributeValue(element, "class"); if (existsAttribute(buddyLocatorClass)) result.setBuddyLocatorClass(buddyLocatorClass); Properties existing = new Properties(); Properties configured = XmlConfigHelper.readPropertiesContents(element, "properties"); existing.putAll(configured); result.setBuddyLocatorClass(buddyLocatorClass); result.setBuddyLocatorProperties(existing); return result; } private BuddyReplicationConfig.BuddyLocatorConfig defaultBuddyLocatorConfig() { BuddyReplicationConfig.BuddyLocatorConfig result = new BuddyReplicationConfig.BuddyLocatorConfig(); result.setBuddyLocatorClass(NextMemberBuddyLocator.class.getName()); Properties props = new Properties(); result.setBuddyLocatorProperties(props); return result; } private void parseDataGravitationElement(Element element, BuddyReplicationConfig brc) { if (element == null) return; String auto = getAttributeValue(element, "auto"); if (existsAttribute(auto)) brc.setAutoDataGravitation(getBoolean(auto)); String removeOnFind = getAttributeValue(element, "removeOnFind"); if (existsAttribute(removeOnFind)) brc.setDataGravitationRemoveOnFind(getBoolean(removeOnFind)); String searchBackupTrees = getAttributeValue(element, "searchBackupTrees"); if (existsAttribute(searchBackupTrees)) brc.setDataGravitationSearchBackupTrees(getBoolean(searchBackupTrees)); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/config/parsing/CacheConfigsXmlParser.java0000755000175000017500000001270411111047320032254 0ustar moellermoeller/* * 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.config.parsing; 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.util.FileLookup; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import java.io.InputStream; import java.util.HashMap; import java.util.Map; /** * 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: 7168 $ */ 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"; public static final String QUALIFIED_CONFIG_ROOT = "registry: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); public Map parseConfigs(String fileName) throws CloneNotSupportedException { FileLookup fileLookup = new FileLookup(); InputStream is = fileLookup.lookupFile(fileName); if (is == null) { throw new ConfigurationException("Unable to find config file " + fileName + " either in classpath or on the filesystem!"); } return parseConfigs(is, fileName); } public Map parseConfigs(InputStream stream, String fileName) throws CloneNotSupportedException { // loop through all elements in XML. Element root = getDocumentRoot(stream); NodeList list = root.getElementsByTagName(CONFIG_ROOT); if (list == null || list.getLength() == 0) { // try looking for a QUALIFIED_CONFIG_ROOT list = root.getElementsByTagName(QUALIFIED_CONFIG_ROOT); if (list == null || list.getLength() == 0) throw new ConfigurationException("Can't find " + CONFIG_ROOT + " or " + QUALIFIED_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"); XmlConfigurationParser parser = new XmlConfigurationParser(); Configuration c; if (parser.isValidElementRoot(element)) { // FIXME - This should be using a valid schema!!! c = parser.parseElementIgnoringRoot(element); } else { if (fileName == null) log.debug("Detected legacy configuration file format when parsing configuration XML from input stream ["+stream+"]. Migrating to the new (3.x) file format is recommended. See FAQs for details."); else log.debug("Detected legacy configuration file format when parsing configuration file ["+fileName+"]. Migrating to the new (3.x) file format is recommended. See FAQs for details."); XmlConfigurationParser2x oldParser = new XmlConfigurationParser2x(); c = oldParser.parseConfiguration(element); } // Prove that we can successfully clone it c = c.clone(); result.put(name.trim(), c); } return result; } private Element getDocumentRoot(InputStream stream) { RootElementBuilder rootElementBuilder = new RootElementBuilder(false); return rootElementBuilder.readRoot(stream); } }jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/config/parsing/ConfigFilesConvertor.java0000644000175000017500000001234211111047320032167 0ustar moellermoeller/* * 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.config.parsing; import org.jboss.cache.util.FileLookup; import org.w3c.dom.Document; import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; /** * Class used for converting a config file from from 2.x version to 3.x verison. * * @author Mircea.Markus@jboss.com * @since 3.0 */ public class ConfigFilesConvertor { /** * Writes to the os the 3.x configuration file resulted by transforming the 2.x configuration file passed in * as is. Transformation is performed according to the xsltFile. The xslt file is looked up using a * {@link org.jboss.cache.util.FileLookup} */ public void parse(InputStream is, OutputStream os, String xsltFile) throws Exception { InputStream xsltInStream = new FileLookup().lookupFile(xsltFile); Document document = getInputDocument(is); // Use a Transformer for output Transformer transformer = getTransformer(xsltInStream); DOMSource source = new DOMSource(document); StreamResult result = new StreamResult(os); transformer.transform(source, result); xsltInStream.close(); } /** * Writes to the os the 3.x configuration file resulted by transforming the 2.x configuration file passed in * as inputFile. Transformation is performed according to the xsltFile. Both inputFile and he xslt * file are looked up using a {@link org.jboss.cache.util.FileLookup} */ public void parse(String inputFile, OutputStream os, String xsltFile) throws Exception { InputStream stream = new FileLookup().lookupFile(inputFile); try { parse(stream, os, xsltFile); } finally { stream.close(); } } /** * usage : java org.jboss.cache.config.parsing.ConfigFilesConvertor -Dsource=config-2.x.xml -Ddestination=config-3.x.xnl */ public static void main(String[] argv) throws Exception { String sourceName = System.getProperty("source"); if (sourceName == null) { System.err.println("Missing property 'source'."); System.exit(1); } String destinationName = System.getProperty("destination"); if (destinationName == null) { System.err.println("Missing property 'destination'."); System.exit(1); } File oldConfig = new File(sourceName); if (!oldConfig.exists()) { System.err.println("File specified as input ('" + sourceName + ") does not exist."); System.exit(1); } ConfigFilesConvertor convertor = new ConfigFilesConvertor(); File destination = new File(destinationName); if (!destination.exists()) destination.createNewFile(); FileInputStream is = new FileInputStream(oldConfig); FileOutputStream fos = new FileOutputStream(destinationName); convertor.parse(is, fos, "config2to3.xslt"); is.close(); System.out.println("---"); System.out.println("New configuration file ["+destinationName+"] successfully created."); System.out.println("---"); } private Transformer getTransformer(InputStream xsltInStream) throws TransformerConfigurationException { TransformerFactory tFactory = TransformerFactory.newInstance(); StreamSource stylesource = new StreamSource(xsltInStream); return tFactory.newTransformer(stylesource); } private Document getInputDocument(InputStream is) throws ParserConfigurationException, SAXException, IOException { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); return builder.parse(is); } }jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/config/parsing/JGroupsStackParser.java0000644000175000017500000000725211162403335031645 0ustar moellermoeller/* * 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.config.parsing; import org.w3c.dom.Attr; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.NodeList; /** * The purpose of this class is to parse the jgroups configuration from the config file into an compact string * that can be passed as a config to the channel. * * @author Mircea.Markus@jboss.com * @since 3.0 */ public class JGroupsStackParser { // ***************** // NOTE: DO NOT MOVE THIS CLASS. In r7769 (http://lists.jboss.org/pipermail/jbosscache-commits/2009-February/005353.html) // I moved this to the element subpackage to sit alongside other parsers, as would be logical, but this broke // POJO Cache since POJO Cache releases expect this class to be in this package. - Manik Surtani (March 2009) // ***************** /** * Parses the cluster config which is used to start a JGroups channel * * @param config an old-style JGroups protocol config String */ public 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; } // Ignore Namespace until JGroups defines one Element tag = (Element) node; String protocol = tag.getLocalName(); if (protocol == null) { protocol = tag.getNodeName(); // try a non-namespace aware version } buffer.append(protocol); processAttributes(buffer, tag); buffer.append(':'); } if (buffer.length() > 0) { //Remove the trailing ':' buffer.setLength(buffer.length() - 1); } return buffer.toString(); } private void processAttributes(StringBuilder buffer, Element tag) { 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); processSingleAttribute(buffer, attr); if (a < attrLength - 1) { buffer.append(';'); } } if (attrLength > 0) { buffer.append(')'); } } private void processSingleAttribute(StringBuilder buffer, Attr attr) { String name = attr.getName(); String value = attr.getValue(); buffer.append(name); buffer.append('='); buffer.append(value); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/config/Dynamic.java0000644000175000017500000000331411111047320026015 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/config/EvictionPolicyConfig.java0000644000175000017500000000450011111047320030515 0ustar moellermoeller/* * 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.config; import org.jboss.cache.eviction.EvictionPolicy; import org.jboss.cache.eviction.EvictionPolicyConfigBase; /** * This class encapsulates the configuration element for an eviction policy. *

    * In its 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 AT jboss DOT org) * @deprecated see {@link org.jboss.cache.config.EvictionAlgorithmConfig} */ @Deprecated 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-3.2.8.GA/src/main/java/org/jboss/cache/config/ConfigurationException.java0000644000175000017500000000450011111047320031115 0ustar moellermoeller/* * 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.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 AT jboss DOT 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-3.2.8.GA/src/main/java/org/jboss/cache/config/BuddyReplicationConfig.java0000644000175000017500000001446311111047320031027 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/config/ConfigurationComponent.java0000644000175000017500000001201511111047320031121 0ustar moellermoeller/* * 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.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.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: 7168 $ * @see #testImmutability(String) */ public abstract class ConfigurationComponent implements CloneableConfigurationComponent { 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; } } public void setCache(CacheSPI cache) { this.cache = cache; synchronized (children) { for (ConfigurationComponent child : children) { child.setCache(cache); } } } @Inject void injectDependencies(ComponentRegistry cr) { this.cr = cr; } @Start void start() { setCache(cr.getComponent(CacheSPI.class)); } @Override public CloneableConfigurationComponent clone() throws CloneNotSupportedException { ConfigurationComponent c = (ConfigurationComponent) super.clone(); c.setCache(null); return c; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/config/UnsupportedEvictionImplException.java0000644000175000017500000000342411111047320033165 0ustar moellermoeller/* * 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.config; /** * Thrown if an eviction configuration that cannot be translated to the new 3.x eviction interfaces is used. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ public class UnsupportedEvictionImplException extends ConfigurationException { public UnsupportedEvictionImplException(Exception e) { super(e); } public UnsupportedEvictionImplException(String string) { super(string); } public UnsupportedEvictionImplException(String string, String erroneousAttribute) { super(string, erroneousAttribute); } public UnsupportedEvictionImplException(String string, Throwable throwable) { super(string, throwable); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/NodeNotExistsException.java0000644000175000017500000000321611111047320027612 0ustar moellermoeller/* * 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; /** * Thrown when an operation is attempted on a non-existing node in the cache * * @author Bela Ban. * @version $Id: NodeNotExistsException.java 7168 2008-11-19 17:37:20Z 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-3.2.8.GA/src/main/java/org/jboss/cache/util/0000755000175000017500000000000011675222022023306 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/util/MinMapUtil.java0000644000175000017500000000617611111047320026171 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/util/Immutables.java0000644000175000017500000003604611251745631026272 0ustar moellermoeller/* * 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.util; import java.io.Serializable; import java.lang.reflect.Array; import java.util.*; import java.util.Map.Entry; /** * Factory for generating immutable type wrappers. * * @author Jason T. Greene */ public class Immutables { /** * Whether or not this collection type is immutable * * @param o a Collection, Set, List, or Map * @return true if immutable, false if not */ public static boolean isImmutable(Object o) { return o instanceof Immutable; } /** * Converts a Collection to an immutable List by copying it. * * @param source the collection to convert * @return a copied/converted immutable list */ public static List immutableListConvert(Collection source) { return new ImmutableListCopy(source); } /** * Creates an immutable copy of the list. * * @param list the list to copy * @return the immutable copy */ public static List immutableListCopy(List list) { if (list == null) return null; if (list.isEmpty()) return Collections.emptyList(); if (list.size() == 1) return Collections. singletonList(list.get(0)); return new ImmutableListCopy(list); } /** * Wraps an array with an immutable list. There is no copying involved. * * @param * @param array the array to wrap * @return a list containing the array */ public static List immutableListWrap(T... array) { return new ImmutableListCopy(array); } /** * Creates a new immutable list containing the union (combined entries) of both lists. * * @param list1 contains the first elements of the new list * @param list2 contains the successor elements of the new list * @return a new immutable merged copy of list1 and list2 */ public static List immutableListMerge(List list1, List list2) { return new ImmutableListCopy(list1, list2); } /** * Converts a Collections into an immutable Set by copying it. * * @param collection the collection to convert/copy * @return a new immutable set containing the elements in collection */ public static Set immutableSetConvert(Collection collection) { return immutableSetWrap(new HashSet(collection)); } /** * Wraps a set with an immutable set. There is no copying involved. * * @param set the set to wrap * @return an immutable set wrapper that delegates to the original set */ public static Set immutableSetWrap(Set set) { return new ImmutableSetWrapper(set); } /** * Creates an immutable copy of the specified set. * * @param set the set to copy from * @return an immutable set copy */ public static Set immutableSetCopy(Set set) { if (set == null) return null; if (set.isEmpty()) return Collections.emptySet(); if (set.size() == 1) return Collections. singleton(set.iterator().next()); Set copy = attemptKnownSetCopy(set); if (copy == null) attemptClone(set); if (copy == null) // Set uses Collection copy-ctor copy = attemptCopyConstructor(set, Collection.class); if (copy == null) copy = new HashSet(set); return new ImmutableSetWrapper(copy); } /** * Wraps a map with an immutable map. There is no copying involved. * * @param map the map to wrap * @return an immutable map wrapper that delegates to the original map */ public static Map immutableMapWrap(Map map) { return new ImmutableMapWrapper(map); } /** * Creates an immutable copy of the specified map. * * @param map the map to copy from * @return an immutable map copy */ @SuppressWarnings("unchecked") public static Map immutableMapCopy(Map map) { if (map == null) return null; if (map.isEmpty()) return Collections.emptyMap(); if (map.size() == 1) { Map.Entry me = map.entrySet().iterator().next(); return Collections. singletonMap(me.getKey(), me.getValue()); } Map copy = attemptKnownMapCopy(map); if (copy == null) attemptClone(map); if (copy == null) copy = attemptCopyConstructor(map, Map.class); if (copy == null) copy = new HashMap(map); return new ImmutableMapWrapper(copy); } /** * Creates a new immutable copy of the specified Collection. * * @param collection the collection to copy * @return an immutable copy */ @SuppressWarnings("unchecked") public static Collection immutableCollectionCopy(Collection collection) { if (collection == null) return null; if (collection.isEmpty()) return Collections.emptySet(); if (collection.size() == 1) return Collections. singleton(collection.iterator().next()); Collection copy = attemptKnownSetCopy(collection); if (copy == null) copy = attemptClone(collection); if (copy == null) copy = attemptCopyConstructor(collection, Collection.class); if (copy == null) copy = new ArrayList(collection); return new ImmutableCollectionWrapper(copy); } @SuppressWarnings("unchecked") private static T attemptKnownMapCopy(T map) { if (map instanceof FastCopyHashMap) return (T) ((FastCopyHashMap) map).clone(); if (map instanceof LinkedHashMap) return (T) ((LinkedHashMap) map).clone(); if (map instanceof HashMap) return (T) ((HashMap) map).clone(); if (map instanceof TreeMap) return (T) ((TreeMap) map).clone(); return null; } @SuppressWarnings("unchecked") private static T attemptKnownSetCopy(T set) { if (set instanceof LinkedHashSet) return (T) ((LinkedHashSet) set).clone(); if (set instanceof HashSet) return (T) ((HashSet) set).clone(); if (set instanceof TreeSet) return (T) ((TreeSet) set).clone(); return null; } @SuppressWarnings("unchecked") private static T attemptClone(T source) { if (source instanceof Cloneable) { try { return (T) source.getClass().getMethod("clone").invoke(source); } catch (Exception e) { // do nothing. } } return null; } @SuppressWarnings("unchecked") private static T attemptCopyConstructor(T source, Class clazz) { try { return (T) source.getClass().getConstructor(clazz).newInstance(source); } catch (Exception e) { // do nothing. } return null; } public interface Immutable { } /* * Immutable wrapper types. * * We have to re-implement Collections.unmodifiableXXX, since it is not * simple to detect them (the class names are JDK dependent). */ private static class ImmutableIteratorWrapper implements Iterator { private Iterator iterator; public ImmutableIteratorWrapper(Iterator iterator) { this.iterator = iterator; } public boolean hasNext() { return iterator.hasNext(); } public E next() { return iterator.next(); } public void remove() { throw new UnsupportedOperationException(); } } private static class ImmutableCollectionWrapper implements Collection, Serializable, Immutable { private static final long serialVersionUID = 6777564328198393535L; Collection collection; public ImmutableCollectionWrapper(Collection collection) { this.collection = collection; } public boolean add(E o) { throw new UnsupportedOperationException(); } public boolean addAll(Collection c) { throw new UnsupportedOperationException(); } public void clear() { throw new UnsupportedOperationException(); } public boolean contains(Object o) { return collection.contains(o); } public boolean containsAll(Collection c) { return collection.containsAll(c); } public boolean equals(Object o) { return collection.equals(o); } public int hashCode() { return collection.hashCode(); } public boolean isEmpty() { return collection.isEmpty(); } public Iterator iterator() { return new ImmutableIteratorWrapper(collection.iterator()); } public boolean remove(Object o) { throw new UnsupportedOperationException(); } public boolean removeAll(Collection c) { throw new UnsupportedOperationException(); } public boolean retainAll(Collection c) { throw new UnsupportedOperationException(); } public int size() { return collection.size(); } public Object[] toArray() { return collection.toArray(); } public T[] toArray(T[] a) { return collection.toArray(a); } public String toString() { return collection.toString(); } } private static class ImmutableSetWrapper extends ImmutableCollectionWrapper implements Set, Serializable, Immutable { private static final long serialVersionUID = 7991492805176142615L; public ImmutableSetWrapper(Set set) { super(set); } } static class ImmutableEntry implements Map.Entry { private K key; private V value; private int hash; ImmutableEntry(Map.Entry entry) { this.key = entry.getKey(); this.value = entry.getValue(); this.hash = entry.hashCode(); } public K getKey() { return key; } public V getValue() { return value; } public V setValue(V value) { throw new UnsupportedOperationException(); } private static boolean eq(Object o1, Object o2) { return o1 == o2 || (o1 != null && o1.equals(o2)); } @SuppressWarnings("unchecked") public boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry entry = (Map.Entry) o; return eq(entry.getKey(), key) && eq(entry.getValue(), value); } public int hashCode() { return hash; } public String toString() { return getKey() + "=" + getValue(); } } private static class ImmutableEntrySetWrapper extends ImmutableSetWrapper> { private static final long serialVersionUID = 6378667653889667692L; @SuppressWarnings("unchecked") public ImmutableEntrySetWrapper(Set> set) { super((Set>) set); } public Object[] toArray() { Object[] array = new Object[collection.size()]; int i = 0; for (Map.Entry entry : this) array[i++] = entry; return array; } @SuppressWarnings("unchecked") public T[] toArray(T[] array) { int size = collection.size(); if (array.length < size) array = (T[]) Array.newInstance(array.getClass().getComponentType(), size); int i = 0; Object[] result = array; for (Map.Entry entry : this) result[i++] = entry; return array; } public Iterator> iterator() { return new ImmutableIteratorWrapper>(collection.iterator()) { public Entry next() { return new ImmutableEntry(super.next()); } }; } } private static class ImmutableMapWrapper implements Map, Serializable, Immutable { private static final long serialVersionUID = 708144227046742221L; private Map map; public ImmutableMapWrapper(Map map) { this.map = map; } public void clear() { throw new UnsupportedOperationException(); } public boolean containsKey(Object key) { return map.containsKey(key); } public boolean containsValue(Object value) { return map.containsValue(value); } public Set> entrySet() { return new ImmutableEntrySetWrapper(map.entrySet()); } public boolean equals(Object o) { return map.equals(o); } public V get(Object key) { return map.get(key); } public int hashCode() { return map.hashCode(); } public boolean isEmpty() { return map.isEmpty(); } public Set keySet() { return new ImmutableSetWrapper(map.keySet()); } public V put(K key, V value) { throw new UnsupportedOperationException(); } public void putAll(Map t) { throw new UnsupportedOperationException(); } public V remove(Object key) { throw new UnsupportedOperationException(); } public int size() { return map.size(); } public Collection values() { return new ImmutableCollectionWrapper(map.values()); } public String toString() { return map.toString(); } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/util/concurrent/0000755000175000017500000000000011675222014025471 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/util/concurrent/BoundedExecutors.java0000644000175000017500000000600511111047320031605 0ustar moellermoeller/* * 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.util.concurrent; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; /** * Similar to JDK {@link java.util.concurrent.Executors} except that the factory methods here allow you to specify the * size of the blocking queue that backs the executor. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ public class BoundedExecutors { /** * Creates a thread pool that reuses a fixed set of threads * operating off a shared bounded queue. If any thread * terminates due to a failure during execution prior to shutdown, * a new one will take its place if needed to execute subsequent * tasks. * * @param nThreads the number of threads in the pool * @param boundedQueueSize size of the bounded queue * @return the newly created thread pool */ public static ExecutorService newFixedThreadPool(int nThreads, int boundedQueueSize) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(boundedQueueSize)); } /** * Creates a thread pool that reuses a fixed set of threads * operating off a shared bounded queue, using the provided * ThreadFactory to create new threads when needed. * * @param nThreads the number of threads in the pool * @param threadFactory the factory to use when creating new threads * @param boundedQueueSize size of the bounded queue * @return the newly created thread pool */ public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory, int boundedQueueSize) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(boundedQueueSize), threadFactory); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/util/concurrent/ConcurrentHashSet.java0000644000175000017500000001027611111047320031732 0ustar moellermoeller/* * 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.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 concurrencyLevel passed in to the underlying CHM. See {@link java.util.concurrent.ConcurrentHashMap#ConcurrentHashMap(int, float, int)} javadocs for details. */ public ConcurrentHashSet(int concurrencyLevel) { map = new ConcurrentHashMap(16, 0.75f, concurrencyLevel); } /** * Params passed in to the underlying CHM. See {@link java.util.concurrent.ConcurrentHashMap#ConcurrentHashMap(int, float, int)} javadocs for details. */ public ConcurrentHashSet(int initSize, float loadFactor, int concurrencyLevel) { map = new ConcurrentHashMap(initSize, loadFactor, concurrencyLevel); } @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-3.2.8.GA/src/main/java/org/jboss/cache/util/concurrent/ReclosableLatch.java0000644000175000017500000000631611166413025031370 0ustar moellermoeller/* * 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.util.concurrent; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.AbstractQueuedSynchronizer; /** * This latch allows you to set a default state (open or closed), and repeatedly open or close * the latch and have threads wait on it. *

    * This is a better impl of the old org.jboss.cache.util.ThreadGate (which doesn't exist anymore), that uses an * {@link java.util.concurrent.locks.AbstractQueuedSynchronizer} while ThreadGate used to use explicit {@link java.util.concurrent.locks.Lock} * objects. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ public class ReclosableLatch extends AbstractQueuedSynchronizer { private static final long serialVersionUID = 1744280161777661090l; // the following states are used in the AQS. private static final int OPEN_STATE = 0, CLOSED_STATE = 1; public ReclosableLatch() { setState(CLOSED_STATE); } public ReclosableLatch(boolean defaultOpen) { setState(defaultOpen ? OPEN_STATE : CLOSED_STATE); } @Override public final int tryAcquireShared(int ignored) { // return 1 if we allow the requestor to proceed, -1 if we want the requestor to block. return getState() == OPEN_STATE ? 1 : -1; } @Override public final boolean tryReleaseShared(int state) { // used as a mechanism to set the state of the Sync. setState(state); return true; } public final void open() { // do not use setState() directly since this won't notify parked threads. releaseShared(OPEN_STATE); } public final void close() { // do not use setState() directly since this won't notify parked threads. releaseShared(CLOSED_STATE); } public final void await() throws InterruptedException { acquireSharedInterruptibly(1); // the 1 is a dummy value that is not used. } public final boolean await(long time, TimeUnit unit) throws InterruptedException { return tryAcquireSharedNanos(1, unit.toNanos(time)); // the 1 is a dummy value that is not used. } public boolean isOpen() { return getState() == OPEN_STATE; } } ././@LongLink0000000000000000000000000000015600000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/util/concurrent/SelfInitializingConcurrentHashMap.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/util/concurrent/SelfInitializingConcurrentHas0000644000175000017500000001133211111047320033345 0ustar moellermoeller/* * 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.util.concurrent; import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * Primarily used to hold child maps for nodes. Underlying CHM is null initially, and once threads start * writing to this map, the CHM is initialized. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ public class SelfInitializingConcurrentHashMap implements ConcurrentMap { private volatile ConcurrentMap delegate; // -------------- initialization methods and helpers ---------------------- private ConcurrentMap getDelegate() { if (delegate == null) init(); return delegate; } private synchronized void init() { // Reminiscent of DCL but the delegate here is volatile so construction reordering should not affect. if (delegate == null) delegate = new ConcurrentHashMap(1, 0.75f, 4); } // -------------- Public API methods that will trigger initialization ---------------------- public final V put(K key, V value) { return getDelegate().put(key, value); } public final V remove(Object key) { return getDelegate().remove(key); } public final void putAll(Map m) { getDelegate().putAll(m); } public final V putIfAbsent(K key, V value) { return getDelegate().putIfAbsent(key, value); } public final boolean replace(K key, V oldValue, V newValue) { return getDelegate().replace(key, oldValue, newValue); } public final V replace(K key, V value) { return getDelegate().replace(key, value); } // -------------- Public API methods that won't trigger initialization ---------------------- public final boolean remove(Object key, Object value) { return delegate != null && delegate.remove(key, value); } public final int size() { return delegate == null ? 0 : delegate.size(); } public final boolean isEmpty() { return delegate == null || delegate.isEmpty(); } public final boolean containsKey(Object key) { return delegate != null && delegate.containsKey(key); } public final boolean containsValue(Object value) { return delegate != null && delegate.containsValue(value); } public final V get(Object key) { return delegate == null ? null : delegate.get(key); } public final void clear() { if (delegate != null) delegate.clear(); } public final Set keySet() { if (delegate == null || delegate.isEmpty()) return Collections.emptySet(); return delegate.keySet(); } public final Collection values() { if (delegate == null || delegate.isEmpty()) return Collections.emptySet(); return delegate.values(); } public final Set> entrySet() { if (delegate == null || delegate.isEmpty()) return Collections.emptySet(); return delegate.entrySet(); } @Override public String toString() { return "SelfInitializingConcurrentHashMap{" + "delegate=" + delegate + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SelfInitializingConcurrentHashMap that = (SelfInitializingConcurrentHashMap) o; return !(delegate != null ? !delegate.equals(that.delegate) : that.delegate != null); } @Override public int hashCode() { int result; result = (delegate != null ? delegate.hashCode() : 0); return result; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/util/concurrent/WithinThreadExecutor.java0000644000175000017500000001067711111047320032446 0ustar moellermoeller/* * 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.util.concurrent; import org.jboss.cache.CacheException; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * An executor that works within the current thread. * * @author Manik Surtani (manik AT jboss DOT org) * @see Java Concurrency In Practice * @since 3.0 */ public class WithinThreadExecutor implements ExecutorService { boolean shutDown = false; public void execute(Runnable command) { command.run(); } public void shutdown() { shutDown = true; } public List shutdownNow() { shutDown = true; return Collections.emptyList(); } public boolean isShutdown() { return shutDown; } public boolean isTerminated() { return shutDown; } public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { return shutDown; } public Future submit(Callable task) { try { final T resp = task.call(); return new Future() { public boolean cancel(boolean mayInterruptIfRunning) { return false; } public boolean isCancelled() { return false; } public boolean isDone() { return true; } public T get() throws InterruptedException, ExecutionException { return resp; } public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return resp; } }; } catch (Exception e) { throw new CacheException(e); } } public Future submit(Runnable task, T result) { throw new UnsupportedOperationException(); } public Future submit(Runnable task) { throw new UnsupportedOperationException(); } @SuppressWarnings("unchecked") // unchecked on purpose due to http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6267833 public List invokeAll(Collection tasks) throws InterruptedException { throw new UnsupportedOperationException(); } @SuppressWarnings("unchecked") // unchecked on purpose due to http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6267833 public List invokeAll(Collection tasks, long timeout, TimeUnit unit) throws InterruptedException { throw new UnsupportedOperationException(); } @SuppressWarnings("unchecked") // unchecked on purpose due to http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6267833 public Object invokeAny(Collection tasks) throws InterruptedException, ExecutionException { throw new UnsupportedOperationException(); } @SuppressWarnings("unchecked") // unchecked on purpose due to http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6267833 public Object invokeAny(Collection tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { throw new UnsupportedOperationException(); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/util/concurrent/SynchronizedRestarter.java0000644000175000017500000000535711133375606032726 0ustar moellermoellerpackage org.jboss.cache.util.concurrent; import org.jboss.cache.Lifecycle; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.LockSupport; /** * A class that handles restarts of components via multiple threads. Specifically, if a component needs to be restarted * and several threads may demand a restart but only one thread should be allowed to restart the component, then use this * class. *

    * What this class guarantees is that several threads may come in while a component is being restarted, but they will * block until the restart is complete. *

    * This is different from other techniques in that: *

      *
    • A simple compare-and-swap to check whether another thread is already performing a restart will result in the * requesting thread returning immediately and potentially attempting to use the resource being restarted.
    • *
    • A synchronized method or use of a lock would result in the thread waiting for the restart to complete, but on * completion will attempt to restart the component again.
    • *
    * This implementation combines a compare-and-swap to detect a concurrent restart, as well as registering for notification * for when the restart completes and then parking the thread if the CAS variable still indicates a restart in progress, * and finally deregistering itself in the end. * * @author Manik Surtani */ public class SynchronizedRestarter { private AtomicBoolean restartInProgress = new AtomicBoolean(false); private ConcurrentHashSet restartWaiters = new ConcurrentHashSet(); public void restartComponent(Lifecycle component) throws Exception { // will only enter this block if no one else is restarting the socket // and will atomically set the flag so others won't enter if (restartInProgress.compareAndSet(false, true)) { try { component.stop(); component.start(); } finally { restartInProgress.set(false); for (Thread waiter : restartWaiters) { try { LockSupport.unpark(waiter); } catch (Throwable t) { // do nothing; continue notifying the rest } } } } else { // register interest in being notified after the restart restartWaiters.add(Thread.currentThread()); // check again to ensure the restarting thread hasn't finished, then wait for that thread to finish if (restartInProgress.get()) LockSupport.park(); // de-register interest in notification restartWaiters.remove(Thread.currentThread()); } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/util/concurrent/locks/0000755000175000017500000000000011675222016026606 5ustar moellermoeller././@LongLink0000000000000000000000000000016300000000000011565 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/util/concurrent/locks/PerElementReentrantLockContainer.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/util/concurrent/locks/PerElementReentrantLock0000644000175000017500000000166011160417135033265 0ustar moellermoellerpackage org.jboss.cache.util.concurrent.locks; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * Per-element container for {@link java.util.concurrent.locks.ReentrantLock}s * * @author Manik Surtani * @since 3.1.0 */ public class PerElementReentrantLockContainer extends PerElementLockContainer { public PerElementReentrantLockContainer(int concurrencyLevel) { super(concurrencyLevel); } public boolean ownsLock(E object, Object owner) { ReentrantLock l = getLockFromMap(object); return l != null && l.isHeldByCurrentThread(); } public boolean isLocked(E object) { ReentrantLock l = getLockFromMap(object); return l != null && l.isLocked(); } private ReentrantLock getLockFromMap(E key) { return (ReentrantLock) locks.get(key); } protected final Lock newLock() { return new ReentrantLock(); } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/util/concurrent/locks/OwnableReentrantLock.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/util/concurrent/locks/OwnableReentrantLock.ja0000644000175000017500000001446011111047320033176 0ustar moellermoeller/* * 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.util.concurrent.locks; import net.jcip.annotations.ThreadSafe; import org.jboss.cache.invocation.InvocationContextContainer; import org.jboss.cache.transaction.GlobalTransaction; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.AbstractQueuedSynchronizer; import java.util.concurrent.locks.Lock; /** * A lock that supports reentrancy based on owner (and not on current thread). For this to work, the lock needs to be * constructed with a reference to the {@link org.jboss.cache.invocation.InvocationContextContainer}, so it is able * to determine whether the caller's "owner" reference is the current thread or a {@link org.jboss.cache.transaction.GlobalTransaction} * instance. *

    * This makes this lock implementation very closely tied to JBoss Cache internals, but it provides for a very clean, efficient * and moreover familiar interface to work with, since it implements {@link java.util.concurrent.locks.Lock}. *

    * For the sake of performance, this lock only supports nonfair queueing. *

    * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ @ThreadSafe public class OwnableReentrantLock extends AbstractQueuedSynchronizer implements Lock { /** * Current owner */ transient Object owner; /** * Invocation context to consult when testing the current requestor */ transient InvocationContextContainer invocationContextContainer; /** * Creates a new lock instance. * * @param invocationContextContainer InvocationContextContainer instance to consult for the invocation context of the call. */ public OwnableReentrantLock(InvocationContextContainer invocationContextContainer) { if (invocationContextContainer == null) throw new IllegalArgumentException("Invocation context container cannot be null!"); this.invocationContextContainer = invocationContextContainer; } /** * @return a GlobalTransaction instance if the current call is participating in a transaction, or the current thread otherwise. */ protected final Object currentRequestor() { GlobalTransaction gtx; return (gtx = invocationContextContainer.get().getGlobalTransaction()) == null ? Thread.currentThread() : gtx; } public void lock() { if (compareAndSetState(0, 1)) owner = currentRequestor(); else acquire(1); } public void lockInterruptibly() throws InterruptedException { acquireInterruptibly(1); } public boolean tryLock() { return tryAcquire(1); } public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { return tryAcquireNanos(1, unit.toNanos(time)); } public void unlock() { release(1); } public ConditionObject newCondition() { throw new UnsupportedOperationException("Not supported in this implementation!"); } @Override protected final boolean tryAcquire(int acquires) { final Object current = currentRequestor(); int c = getState(); if (c == 0) { if (compareAndSetState(0, acquires)) { owner = current; return true; } } else if (current.equals(owner)) { setState(c + acquires); return true; } return false; } @Override protected final boolean tryRelease(int releases) { int c = getState() - releases; if (!currentRequestor().equals(owner)) throw new IllegalMonitorStateException(this.toString()); boolean free = false; if (c == 0) { free = true; owner = null; } setState(c); return free; } @Override protected final boolean isHeldExclusively() { return getState() != 0 && currentRequestor().equals(owner); } /** * @return the owner of the lock, or null if it is currently unlocked. */ public final Object getOwner() { int c = getState(); Object o = owner; return (c == 0) ? null : o; } /** * @return the hold count of the current lock, or 0 if it is not locked. */ public final int getHoldCount() { int c = getState(); Object o = owner; return (currentRequestor().equals(o)) ? c : 0; } /** * @return true if the lock is locked, false otherwise */ public final boolean isLocked() { return getState() != 0; } /** * Reconstitute this lock instance from a stream, resetting the lock to an unlocked state. * * @param s the stream */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); setState(0); // reset to unlocked state } /** * Returns a string identifying this lock, as well as its lock * state. The state, in brackets, includes either the String * "Unlocked" or the String "Locked by" * followed by the String representation of the lock owner. * * @return a string identifying this lock, as well as its lock state. */ public String toString() { Object owner = getOwner(); return super.toString() + ((owner == null) ? "[Unlocked]" : "[Locked by " + owner + "]"); } } ././@LongLink0000000000000000000000000000015700000000000011570 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/util/concurrent/locks/ReentrantSharedLockContainer.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/util/concurrent/locks/ReentrantSharedLockCont0000644000175000017500000000572411160156274033270 0ustar moellermoeller/* * 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.util.concurrent.locks; import net.jcip.annotations.ThreadSafe; import java.util.Arrays; import java.util.concurrent.locks.ReentrantLock; /** * A LockContainer that holds ReentrantLocks * * @author Manik Surtani (manik AT jboss DOT org) * @see OwnableReentrantSharedLockContainer * @since 3.0 */ @ThreadSafe public class ReentrantSharedLockContainer extends AbstractSharedLockContainer { ReentrantLock[] sharedLocks; /** * Creates a new LockContainer which uses a certain number of shared locks across all elements that need to be locked. * * @param concurrencyLevel concurrency level for number of stripes to create. Stripes are created in powers of two, with a minimum of concurrencyLevel created. */ public ReentrantSharedLockContainer(int concurrencyLevel) { initLocks(calculateNumberOfSegments(concurrencyLevel)); } protected void initLocks(int numLocks) { sharedLocks = new ReentrantLock[numLocks]; for (int i = 0; i < numLocks; i++) sharedLocks[i] = new ReentrantLock(); } public final ReentrantLock getLock(E object) { return sharedLocks[hashToIndex(object)]; } public final int getNumLocksHeld() { int i = 0; for (ReentrantLock l : sharedLocks) if (l.isLocked()) i++; return i; } public final boolean ownsLock(E object, Object owner) { ReentrantLock lock = getLock(object); return lock.isHeldByCurrentThread(); } public final boolean isLocked(E object) { ReentrantLock lock = getLock(object); return lock.isLocked(); } public String toString() { return "ReentrantSharedLockContainer{" + "sharedLocks=" + (sharedLocks == null ? null : Arrays.asList(sharedLocks)) + '}'; } public void reset() { initLocks(sharedLocks.length); } public int size() { return sharedLocks.length; } } ././@LongLink0000000000000000000000000000015600000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/util/concurrent/locks/AbstractSharedLockContainer.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/util/concurrent/locks/AbstractSharedLockConta0000644000175000017500000000606611160156274033232 0ustar moellermoeller/* * 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.util.concurrent.locks; import net.jcip.annotations.ThreadSafe; import java.util.concurrent.locks.Lock; import java.util.concurrent.TimeUnit; /** * A container for locks. Used with lock striping. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ @ThreadSafe public abstract class AbstractSharedLockContainer implements LockContainer { private int lockSegmentMask; private int lockSegmentShift; protected int calculateNumberOfSegments(int concurrencyLevel) { int tempLockSegShift = 0; int numLocks = 1; while (numLocks < concurrencyLevel) { ++tempLockSegShift; numLocks <<= 1; } lockSegmentShift = 32 - tempLockSegShift; lockSegmentMask = numLocks - 1; return numLocks; } final int hashToIndex(E object) { return (hash(object) >>> 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 object the object serving as a key * @return the hash code */ final int hash(E object) { // Spread bits to regularize both segment and index locations, // using variant of single-word Wang/Jenkins hash. int h = object.hashCode(); h += (h << 15) ^ 0xffffcd7d; h ^= (h >>> 10); h += (h << 3); h ^= (h >>> 6); h += (h << 2) + (h << 14); h = h ^ (h >>> 16); return h; } protected abstract void initLocks(int numLocks); public void acquireLock(E object) { Lock lock = getLock(object); lock.lock(); } public boolean acquireLock(E object, long timeout, TimeUnit unit) throws InterruptedException { Lock lock = getLock(object); return lock.tryLock(timeout, unit); } public void releaseLock(E object) { Lock lock = getLock(object); lock.unlock(); } } ././@LongLink0000000000000000000000000000017200000000000011565 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/util/concurrent/locks/PerElementOwnableReentrantLockContainer.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/util/concurrent/locks/PerElementOwnableReentr0000644000175000017500000000215211160417135033256 0ustar moellermoellerpackage org.jboss.cache.util.concurrent.locks; import org.jboss.cache.invocation.InvocationContextContainer; import java.util.concurrent.locks.Lock; /** * Per element container for {@link org.jboss.cache.util.concurrent.locks.OwnableReentrantLock}s * * @author Manik Surtani * @since 3.1.0 */ public class PerElementOwnableReentrantLockContainer extends PerElementLockContainer { private InvocationContextContainer icc; public PerElementOwnableReentrantLockContainer(int concurrencyLevel, InvocationContextContainer icc) { super(concurrencyLevel); this.icc = icc; } public boolean ownsLock(E object, Object owner) { OwnableReentrantLock l = getLockFromMap(object); return l != null && owner.equals(l.getOwner()); } public boolean isLocked(E object) { OwnableReentrantLock l = getLockFromMap(object); return l != null && l.isLocked(); } private OwnableReentrantLock getLockFromMap(E key) { return (OwnableReentrantLock) locks.get(key); } protected final Lock newLock() { return new OwnableReentrantLock(icc); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/util/concurrent/locks/LockContainer.java0000644000175000017500000000206511160156274032210 0ustar moellermoellerpackage org.jboss.cache.util.concurrent.locks; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; /** * A container for locks * * @author Manik Surtani * @since 3.1.0 */ public interface LockContainer { /** * Tests if a give owner owns a lock on a specified object. * * @param object object to check * @param owner owner to test * @return true if owner owns lock, false otherwise */ boolean ownsLock(E object, Object owner); /** * @param object object * @return true if an object is locked, false otherwise */ boolean isLocked(E object); /** * @param object object * @return the lock for a specific object */ Lock getLock(E object); /** * @return number of locks held */ int getNumLocksHeld(); /** * Clears all locks held and re-initialises stripes. */ void reset(); int size(); void acquireLock(E object); boolean acquireLock(E object, long timeout, TimeUnit unit) throws InterruptedException; void releaseLock(E object); } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/util/concurrent/locks/PerElementLockContainer.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/util/concurrent/locks/PerElementLockContainer0000644000175000017500000000507711160417135033253 0ustar moellermoellerpackage org.jboss.cache.util.concurrent.locks; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; /** * A lock container that maintains a new lock per element * * @author Manik Surtani * @since 3.1.0 */ public abstract class PerElementLockContainer implements LockContainer { protected final ConcurrentMap locks; protected PerElementLockContainer(int concurrencyLevel) { locks = new ConcurrentHashMap(16, .75f, concurrencyLevel); } protected abstract Lock newLock(); public final Lock getLock(E object) { Lock l = locks.get(object); if (l == null) l = newLock(); Lock tmp = locks.putIfAbsent(object, l); if (tmp != null) l = tmp; return l; } public int getNumLocksHeld() { return locks.size(); } public void reset() { for (Lock l: locks.values()) { try { l.unlock(); } catch (Exception e) { // no-op } } locks.clear(); } public int size() { return locks.size(); } public void acquireLock(E object) { while (true) { Lock lock = getLock(object); lock.lock(); // now check that the lock is still valid... Lock currentLock = locks.putIfAbsent(object, lock); if (currentLock != null && lock != currentLock) { // we acquired the wrong lock! lock.unlock(); } else { // we got the right lock! break; } } } public boolean acquireLock(E object, long timeout, TimeUnit unit) throws InterruptedException { while (true) { Lock lock = getLock(object); if (lock.tryLock(timeout, unit)) { // now check that the lock is still valid... Lock currentLock = locks.putIfAbsent(object, lock); if (currentLock != null && lock != currentLock) { // we acquired the wrong lock! lock.unlock(); } else { // we got the right lock! return true; } } else { // if we haven't acquired the lock (i.e., timed out) return false return false; } } } public void releaseLock(E object) { Lock l = locks.remove(object); if (l != null) l.unlock(); } } ././@LongLink0000000000000000000000000000016600000000000011570 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/util/concurrent/locks/OwnableReentrantSharedLockContainer.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/util/concurrent/locks/OwnableReentrantSharedL0000644000175000017500000000647011160156274033256 0ustar moellermoeller/* * 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.util.concurrent.locks; import net.jcip.annotations.ThreadSafe; import org.jboss.cache.invocation.InvocationContextContainer; import java.util.Arrays; /** * A LockContainer that holds {@link org.jboss.cache.util.concurrent.locks.OwnableReentrantLock}s. * * @author Manik Surtani (manik AT jboss DOT org) * @see ReentrantSharedLockContainer * @see org.jboss.cache.util.concurrent.locks.OwnableReentrantLock * @since 3.0 */ @ThreadSafe public class OwnableReentrantSharedLockContainer extends AbstractSharedLockContainer { OwnableReentrantLock[] sharedLocks; InvocationContextContainer icc; /** * Creates a new LockContainer which uses a certain number of shared locks across all elements that need to be locked. * * @param concurrencyLevel concurrency level for number of stripes to create. Stripes are created in powers of two, with a minimum of concurrencyLevel created. * @param icc invocation context container to use */ public OwnableReentrantSharedLockContainer(int concurrencyLevel, InvocationContextContainer icc) { this.icc = icc; initLocks(calculateNumberOfSegments(concurrencyLevel)); } protected void initLocks(int numLocks) { sharedLocks = new OwnableReentrantLock[numLocks]; for (int i = 0; i < numLocks; i++) sharedLocks[i] = new OwnableReentrantLock(icc); } public final OwnableReentrantLock getLock(E object) { return sharedLocks[hashToIndex(object)]; } public final boolean ownsLock(E object, Object owner) { OwnableReentrantLock lock = getLock(object); return owner.equals(lock.getOwner()); } public final boolean isLocked(E object) { OwnableReentrantLock lock = getLock(object); return lock.isLocked(); } public final int getNumLocksHeld() { int i = 0; for (OwnableReentrantLock l : sharedLocks) if (l.isLocked()) i++; return i; } public String toString() { return "OwnableReentrantSharedLockContainer{" + "sharedLocks=" + (sharedLocks == null ? null : Arrays.asList(sharedLocks)) + '}'; } public void reset() { initLocks(sharedLocks.length); } public int size() { return sharedLocks.length; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/util/reflect/0000755000175000017500000000000011675222017024736 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/util/reflect/ReflectionUtil.java0000644000175000017500000001200011111047320030505 0ustar moellermoeller/* * 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.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 AT jboss DOT 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 its 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(e); } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/util/BeanUtils.java0000644000175000017500000001014311111047320026025 0ustar moellermoeller/* * 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.util; import java.lang.reflect.Method; import java.util.Locale; /** * Simple JavaBean manipulation helper methods * * @author Manik Surtani (manik AT jboss DOT 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-3.2.8.GA/src/main/java/org/jboss/cache/util/Caches.java0000644000175000017500000005121211111047320025327 0ustar moellermoeller/* * 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.util; import org.jboss.cache.Cache; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.NodeSPI; import org.jboss.cache.loader.CacheLoader; import java.util.AbstractMap; import java.util.AbstractSet; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.Map; import java.util.Set; /** * Adaptors for {@link Cache} classes, such as {@link Node}. * This is useful for integration of JBoss Cache into existing applications. *

    * Example use: *

     * Cache c = ...;
     * Map m = Caches.asMap(c);
     * m.put("a", "b"); // null
     * m.containsKey("a"); // true
     * m.remove("a"); // "b"
     * 
    */ public class Caches { private Caches() { } /** * Returns a {@link Map} from the root node. * * @param cache cache to wrap as a map * @return a map representation of the cache * @see #asMap(Node) */ public static Map asMap(Cache cache) { if (cache == null) throw new NullPointerException("cache"); return asMap(cache.getRoot()); } /** * Returns a {@link Map}, where map keys are named children of the given Node, * and values are kept under a single key for this node. * The map may be safely concurrently modified through this Map or externally, * and its contents reflect the cache state and * existing data of the Node. * This means that {@link ConcurrentModificationException} is never thrown * and all methods are thread safe. *

    * The map is not serializable. *

    * Usage note: As a single node is used for every key, it is most efficient to store * data for a single entity (e.g. Person) in a single object. *

    * Also, when using a {@link CacheLoader} for storage, keys used must be valid as * part of the {@link Fqn} used in calls. Generally speaking, simple string values are * preferred. * * @param node node in a cache to wrap * @return a Map representation of the cache */ public static Map asMap(Node node) { return new MapNode(node); } /** * Returns a {@link Map}, where map data is put and returned directly from a single Node. * This method is "simple" as data is kept under a single node. * Note that storing all data in a single Node can be inefficient when using persistence, * replication, or transactions. * The map may be safely concurrently modified through this Map or externally. * This means that {@link ConcurrentModificationException} is never thrown * and all methods are thread safe. *

    * The methods {@link Map#entrySet} and {@link Map#values} and {@link Map#keySet} * do not allow for modification of the Node. * Further all these methods return a collection which is a snapshot (copy) * of the data at time of calling. This may be very inefficient. *

    * The map is not serializable. * * @param node node to wrap * @return Map representation of the cache */ public static Map asSimpleMap(Node node) { return new SimpleMapNode(node); } /** * Returns a {@link Set}, where set entries are data entries of the given Node. * This method is "simple" as data is kept under a single node. *

    * Note that storing all data in a single Node can be inefficient when using persistence, * replication, or transactions. * The set may be safely concurrently modified through this Map or externally. * This means that {@link ConcurrentModificationException} is never thrown * and all methods are thread safe. *

    * The set is not serializable. * * @param node node to wrap * @return a Set representation of the values in a node */ public static Set asSimpleSet(Node node) { return new SimpleSetNode(node); } /** * Returns a {@link Map}, where map entries are partitioned into * children nodes, within a cache node. * The default child selector divides the data into 128 child nodes based on hash code. * Note that for large data sets, the number of child nodes should be increased. * * @param node node to cache under * @return a Map representation of the cache */ @SuppressWarnings("unchecked") public static Map asPartitionedMap(Node node) { return new PartitionedMapNode(node, HashKeySelector.DEFAULT); } /** * Returns a {@link Map}, where map entries are partitioned * into children, within a cache node, by key hash code. *

    * The map is not serializable. *

    * Usage note: This is a performance (and size) compromise between {@link #asMap(Node)} * and {@link #asSimpleMap(Node)}. For applications using a {@link org.jboss.cache.loader.CacheLoader}, * {@link #asMap(Node)} is a better choice. *

    * * @param node node to cache under * @param ss selector strategy that chooses a segment based on key * @return a Map representation of the cache */ public static Map asPartitionedMap(Node node, ChildSelector ss) { return new PartitionedMapNode(node, ss); } /** * Returns a {@link Map}, where map entries are partitioned into child nodes, * within the cache root, by key hash code. * * @param cache cache to use * @return a Map representation of the cache */ public static Map asPartitionedMap(Cache cache) { return asPartitionedMap(cache.getRoot()); } /** * Computes an improved hash code from an object's hash code. */ static protected final int hashCode(int i) { i ^= i >>> 20 ^ i >>> 12; return i ^ i >>> 7 ^ i >>> 4; } /** * Returns a segment ({@link Node#getChild(Object) child node name}) * to use based on the characteristics of a key. *

    * Here is an example class which selects a child based on a person's department: *

        * public static class DepartmentSelector implements ChildSelector<Person>
        * {
        *    public Object childName(Person key)
        *    {
        *       return key.getDepartment();
        *    }
        * }
        * 
    */ public interface ChildSelector { /** * Returns a child node name for a key. * * @param key for calls to {@link Map#put}, {@link Map#get} etc. * @return node name */ Fqn childName(T key); } /** * Class that returns a child name to use based on the hash code of a key. */ public static class HashKeySelector implements ChildSelector { static ChildSelector DEFAULT = new HashKeySelector(128); protected int segments; /** * Constructs with N segments, where N must be a power of 2. * * @param segments Number of hash segments */ public HashKeySelector(int segments) { this.segments = segments; if (Integer.bitCount(segments) != 1) throw new IllegalArgumentException(); if (segments <= 0) throw new IllegalArgumentException(); } /** * Returns the segment for this key, in the inclusive range 0 to {@link #segments} - 1. */ protected final int segmentFor(T key) { if (key == null) return 0; int hc = key.hashCode(); return Caches.hashCode(hc) & (segments - 1); } /** * Returns the node name for this segment. */ protected final Fqn childName(int segment) { return Fqn.fromElements(Integer.toString(segment)); } /** * Returns the node name for this key. * By default, returns a String containing the segment. */ public final Fqn childName(T key) { return childName(segmentFor(key)); } @Override public String toString() { return super.toString() + " segments=" + segments; } } static class MapNode extends AbstractMap { public static final String KEY = "K"; private Node node; // purposefully un-genericized public MapNode(Node node) { if (node == null) throw new NullPointerException("node"); this.node = node; } @Override public Set> entrySet() { return new AbstractSet>() { @Override public Iterator> iterator() { final Iterator> i = set().iterator(); return new Iterator>() { Object name; boolean next = false; public boolean hasNext() { return i.hasNext(); } @SuppressWarnings("unchecked") public Entry next() { Node n = i.next(); this.name = n.getFqn().getLastElement(); this.next = true; Object key = n.get(KEY); return new SimpleImmutableEntry(name, key); } public void remove() { if (!next) throw new IllegalStateException(); node.removeChild(name); } @Override public String toString() { return "Itr name=" + name; } }; } @SuppressWarnings("unchecked") private Set> set() { return node.getChildren(); } @Override public int size() { return set().size(); } }; } @Override public void clear() { for (Object o : node.getChildrenNames()) node.removeChild(o); } @Override public boolean containsKey(Object arg0) { return node.getChild(arg0) != null; } @Override @SuppressWarnings("unchecked") public V get(Object arg0) { Node child = node.getChild(arg0); if (child == null) return null; return (V) child.get(KEY); } @Override public boolean isEmpty() { return node.getChildrenNames().isEmpty(); } @Override public Set keySet() { return new AbstractSet() { private Set set() { return node.getChildrenNames(); } @Override public Iterator iterator() { final Iterator i = set().iterator(); return new Iterator() { K child; public boolean hasNext() { return i.hasNext(); } @SuppressWarnings("unchecked") public K next() { child = (K) i.next(); return child; } public void remove() { if (child == null) throw new IllegalStateException(); node.removeChild(child); // since set is read-only, invalidate } }; } @Override public boolean remove(Object key) { return node.removeChild(key); } @Override public int size() { return set().size(); } }; } @Override @SuppressWarnings("unchecked") public V put(K arg0, V arg1) { return (V) node.addChild(Fqn.fromElements(arg0)).put(KEY, arg1); } @Override @SuppressWarnings("unchecked") public V remove(Object arg0) { Node child = node.getChild(arg0); if (child == null) return null; V o = (V) child.remove(KEY); node.removeChild(arg0); return o; } @Override public int size() { return node.getChildrenNames().size(); } } static class SimpleMapNode extends AbstractMap { private Node node; public SimpleMapNode(Node node) { if (node == null) throw new NullPointerException("node"); this.node = node; } @Override public void clear() { node.clearData(); } @Override public boolean containsKey(Object key) { return node.getKeys().contains(key); } @Override public boolean containsValue(Object value) { return node.getData().containsValue(value); } /** * getData returns a snapshot of the data. */ @Override public Set> entrySet() { return node.getData().entrySet(); } @Override @SuppressWarnings("unchecked") public V get(Object key) { return node.get((K) key); } @Override public Set keySet() { return node.getKeys(); } @Override public V put(K key, V value) { return node.put(key, value); } @Override @SuppressWarnings("unchecked") public void putAll(Map map) { node.putAll((Map) map); } @Override @SuppressWarnings("unchecked") public V remove(Object key) { return node.remove((K) key); } @Override public int size() { return node.dataSize(); } } static class SimpleSetNode extends AbstractSet implements java.util.Set { private Node node; private static final String VALUE = "V"; public SimpleSetNode(Node node) { if (node == null) throw new NullPointerException("node"); this.node = node; } @Override public void clear() { node.clearData(); } @Override public boolean contains(Object key) { return node.getKeys().contains(key); } @Override @SuppressWarnings("unchecked") public boolean remove(Object key) { return node.remove(key) != null; } @Override public int size() { return node.dataSize(); } @Override @SuppressWarnings("unchecked") public boolean add(K arg0) { return node.put(arg0, VALUE) == null; } @Override public Iterator iterator() { final Iterator i = node.getKeys().iterator(); return new Iterator() { K key; boolean next = false; public boolean hasNext() { return i.hasNext(); } @SuppressWarnings("unchecked") public K next() { key = (K) i.next(); next = true; return key; } @SuppressWarnings("unchecked") public void remove() { if (!next) throw new IllegalStateException(); node.remove(key); } }; } } static class PartitionedMapNode extends AbstractMap { private NodeSPI node; private ChildSelector selector; public PartitionedMapNode(Node node, ChildSelector selector) { this.node = (NodeSPI) node; this.selector = selector; } @Override public Set> entrySet() { return new AbstractSet>() { Iterator ci = node.getChildren().iterator(); @Override public Iterator> iterator() { return new Iterator>() { Iterator ni; { nextChild(); findNext(); } @SuppressWarnings("unchecked") private void nextChild() { ni = new SimpleMapNode(ci.next()).entrySet().iterator(); } private void findNext() { while (!ni.hasNext()) { if (!ci.hasNext()) return; nextChild(); } } public boolean hasNext() { return ni.hasNext(); } @SuppressWarnings("unchecked") public Entry next() { Entry n = (Entry) ni.next(); findNext(); return n; } public void remove() { ni.remove(); } }; } @Override public int size() { return PartitionedMapNode.this.size(); } }; } @Override @SuppressWarnings("unchecked") public Set keySet() { return new AbstractSet>() { @Override @SuppressWarnings("unchecked") public Iterator> iterator() { return (Iterator>) PartitionedMapNode.super.keySet().iterator(); } @Override public boolean remove(Object o) { boolean key = PartitionedMapNode.this.containsKey(o); PartitionedMapNode.this.remove(o); return key; } @Override public boolean contains(Object o) { return PartitionedMapNode.this.containsKey(o); } @Override public int size() { return PartitionedMapNode.super.keySet().size(); } }; } @Override public void clear() { for (Object o : node.getChildrenNames()) node.getChild(o).clearData(); } @SuppressWarnings("unchecked") private Fqn fqnFor(Object o) { return Fqn.fromRelativeFqn(node.getFqn(), selector.childName((K) o)); } @Override @SuppressWarnings("unchecked") public boolean containsKey(Object o) { Fqn fqn = fqnFor(o); Set keys = node.getCache().getKeys(fqn); return keys != null && keys.contains(o); } @Override @SuppressWarnings("unchecked") public V get(Object key) { return (V) node.getCache().get(fqnFor(key), key); } @SuppressWarnings("unchecked") public Object put(Object key, Object value) { return node.getCache().put(fqnFor(key), key, value); } @SuppressWarnings("unchecked") public V remove(Object key) { return (V) node.getCache().remove(fqnFor(key), key); } public int size() { int size = 0; for (Object o : node.getChildrenNames()) { Node child = node.getChild(o); size += child.dataSize(); } return size; } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/util/ImmutableListCopy.java0000644000175000017500000002735311111047320027560 0ustar moellermoeller/* * 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.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 AT jboss DOT org) * @since 3.0 */ @Immutable public class ImmutableListCopy extends AbstractList implements Externalizable, Immutables.Immutable { private static final long serialVersionUID = 10929568968966L; private E[] elements; private int size; /** * Constructs a new ImmutableListCopy. * Required by Serialization. */ public ImmutableListCopy() {} /** * 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-3.2.8.GA/src/main/java/org/jboss/cache/util/CachePrinter.java0000644000175000017500000001071711236536237026537 0ustar moellermoeller/* * 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.util; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.DataContainer; import org.jboss.cache.DataContainerImpl; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.invocation.CacheInvocationDelegate; import java.text.NumberFormat; /** * 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) { StringBuilder sb = new StringBuilder(); int i=1; for (Cache cache: c) { // internal cast sb.append("\n--- Cache").append(i++).append(" ---\n"); DataContainer ci = ((CacheInvocationDelegate) cache).getDataContainer(); sb.append(ci.printDetails()); sb.append("\n------------\n\n"); } return sb.toString(); } /** * 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(); } /** * 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; } /** * Prints a time for display * @param millis time in millis * @return the time, represented as millis, seconds, minutes or hours as appropriate, with suffix */ public static String prettyPrint(long millis) { if (millis < 1000) return millis + " milliseconds"; NumberFormat nf = NumberFormat.getNumberInstance(); nf.setMaximumFractionDigits(2); double toPrint = ((double) millis) / 1000; if (toPrint < 300) { return nf.format(toPrint) + " seconds"; } toPrint = toPrint / 60; if (toPrint < 120) { return nf.format(toPrint) + " minutes"; } toPrint = toPrint / 60; return nf.format(toPrint) + " hours"; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/util/DeltaMap.java0000644000175000017500000002072611111047320025636 0ustar moellermoeller/* * 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.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 exclude 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; } @Override @SuppressWarnings("unchecked") 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-3.2.8.GA/src/main/java/org/jboss/cache/util/FileLookup.java0000644000175000017500000000675211111047320026223 0ustar moellermoeller/* * 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.util; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; /** * Holds the logic of looking up a file, in the following sequence: *
      *
    1. try to load it with the curent thread's context ClassLoader
    2. *
    3. if fails, the system ClassLoader
    4. *
    5. if fails, try to load it as a file from the disck
    6. *
    * * @author Mircea.Markus@jboss.com * @since 3.0 */ public class FileLookup { private static final Log log = LogFactory.getLog(FileLookup.class); /** * Looks up the file, see : {@link FileLookup}. * * @param filename might be the name of the file (too look it up in the class path) or an url to a file. * @return an input stream to the file or null if nothing found through all lookup steps. */ public InputStream lookupFile(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) { return null; } } return is; } 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; } public URL lookupFileLocation(String filename) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); URL u = cl == null ? null : cl.getResource(filename); if (u == null) { // check system class loader u = getClass().getClassLoader().getResource(filename); } if (u == null) { File f = new File(filename); if (f.exists()) try { u = f.toURL(); } catch (MalformedURLException e) { // what do we do here? } } return u; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/util/BitEncodedIntegerSet.java0000644000175000017500000000654411111047320030143 0ustar moellermoeller/* * 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.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 AT jboss DOT 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-3.2.8.GA/src/main/java/org/jboss/cache/util/FastCopyHashMap.java0000644000175000017500000005044611135430236027153 0ustar moellermoeller/* * 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.util; import java.io.IOException; import java.io.Serializable; import java.util.AbstractCollection; import java.util.AbstractMap; import java.util.AbstractSet; import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; /** * A HashMap that is optimized for fast shallow copies. If the copy-ctor is * passed another FastCopyHashMap, or clone is called on this map, the shallow * copy can be performed using little more than a single array copy. In order to * accomplish this, immutable objects must be used internally, so update * operations result in slightly more object churn than HashMap. * * Note: It is very important to use a smaller load factor than you normally * would for HashMap, since the implementation is open-addressed with linear * probing. With a 50% load-factor a get is expected to return in only 2 probes. * However, a 90% load-factor is expected to return in around 50 probes. * * @author Jason T. Greene */ public class FastCopyHashMap extends AbstractMap implements Map, Cloneable, Serializable { /** * Marks null keys. */ private static final Object NULL = new Object(); /** * Serialization ID */ private static final long serialVersionUID = 10929568968762L; /** * Same default as HashMap, must be a power of 2 */ private static final int DEFAULT_CAPACITY = 8; /** * MAX_INT - 1 */ private static final int MAXIMUM_CAPACITY = 1 << 30; /** * 67%, just like IdentityHashMap */ private static final float DEFAULT_LOAD_FACTOR = 0.67f; /** * The open-addressed table */ private transient Entry[] table; /** * The current number of key-value pairs */ private transient int size; /** * The next resize */ private transient int threshold; /** * The user defined load factor which defines when to resize */ private final float loadFactor; /** * Counter used to detect changes made outside of an iterator */ private transient int modCount; // Cached views private transient KeySet keySet; private transient Values values; private transient EntrySet entrySet; public FastCopyHashMap(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Can not have a negative size table!"); if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; if (!(loadFactor > 0F && loadFactor <= 1F)) throw new IllegalArgumentException("Load factor must be greater than 0 and less than or equal to 1"); this.loadFactor = loadFactor; init(initialCapacity, loadFactor); } @SuppressWarnings("unchecked") public FastCopyHashMap(Map map) { if (map instanceof FastCopyHashMap) { FastCopyHashMap fast = (FastCopyHashMap) map; this.table = (Entry[]) fast.table.clone(); this.loadFactor = fast.loadFactor; this.size = fast.size; this.threshold = fast.threshold; } else { this.loadFactor = DEFAULT_LOAD_FACTOR; init(map.size(), this.loadFactor); putAll(map); } } @SuppressWarnings("unchecked") private void init(int initialCapacity, float loadFactor) { int c = 1; for (; c < initialCapacity; c <<= 1) ; threshold = (int) (c * loadFactor); // Include the load factor when sizing the table for the first time if (initialCapacity > threshold && c < MAXIMUM_CAPACITY) { c <<= 1; threshold = (int) (c * loadFactor); } this.table = (Entry[]) new Entry[c]; } public FastCopyHashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } public FastCopyHashMap() { this(DEFAULT_CAPACITY); } // The normal bit spreader... private static final int hash(Object key) { int h = key.hashCode(); h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); } @SuppressWarnings("unchecked") private static final K maskNull(K key) { return key == null ? (K) NULL : key; } private static final K unmaskNull(K key) { return key == NULL ? null : key; } private int nextIndex(int index, int length) { index = (index >= length - 1) ? 0 : index + 1; return index; } private static final boolean eq(Object o1, Object o2) { return o1 == o2 || (o1 != null && o1.equals(o2)); } private static final int index(int hashCode, int length) { return hashCode & (length - 1); } public int size() { return size; } public boolean isEmpty() { return size == 0; } public V get(Object key) { key = maskNull(key); int hash = hash(key); int length = table.length; int index = index(hash, length); for (int start = index; ;) { Entry e = table[index]; if (e == null) return null; if (e.hash == hash && eq(key, e.key)) return e.value; index = nextIndex(index, length); if (index == start) // Full table return null; } } public boolean containsKey(Object key) { key = maskNull(key); int hash = hash(key); int length = table.length; int index = index(hash, length); for (int start = index; ;) { Entry e = table[index]; if (e == null) return false; if (e.hash == hash && eq(key, e.key)) return true; index = nextIndex(index, length); if (index == start) // Full table return false; } } public boolean containsValue(Object value) { for (Entry e : table) if (e != null && eq(value, e.value)) return true; return false; } public V put(K key, V value) { key = maskNull(key); Entry[] table = this.table; int hash = hash(key); int length = table.length; int index = index(hash, length); for (int start = index; ;) { Entry e = table[index]; if (e == null) break; if (e.hash == hash && eq(key, e.key)) { table[index] = new Entry(e.key, e.hash, value); return e.value; } index = nextIndex(index, length); if (index == start) throw new IllegalStateException("Table is full!"); } modCount++; table[index] = new Entry(key, hash, value); if (++size >= threshold) resize(length); return null; } @SuppressWarnings("unchecked") private void resize(int from) { int newLength = from << 1; // Can't get any bigger if (newLength > MAXIMUM_CAPACITY || newLength <= from) return; Entry[] newTable = new Entry[newLength]; Entry[] old = table; for (Entry e : old) { if (e == null) continue; int index = index(e.hash, newLength); while (newTable[index] != null) index = nextIndex(index, newLength); newTable[index] = e; } threshold = (int) (loadFactor * newLength); table = newTable; } public void putAll(Map map) { int size = map.size(); if (size == 0) return; if (size > threshold) { if (size > MAXIMUM_CAPACITY) size = MAXIMUM_CAPACITY; int length = table.length; for (; length < size; length <<= 1) ; resize(length); } for (Map.Entry e : map.entrySet()) put(e.getKey(), e.getValue()); } public V remove(Object key) { key = maskNull(key); Entry[] table = this.table; int length = table.length; int hash = hash(key); int start = index(hash, length); for (int index = start; ;) { Entry e = table[index]; if (e == null) return null; if (e.hash == hash && eq(key, e.key)) { table[index] = null; relocate(index); modCount++; size--; return e.value; } index = nextIndex(index, length); if (index == start) return null; } } private void relocate(int start) { Entry[] table = this.table; int length = table.length; int current = nextIndex(start, length); for (; ;) { Entry e = table[current]; if (e == null) return; // A Doug Lea variant of Knuth's Section 6.4 Algorithm R. // This provides a non-recursive method of relocating // entries to their optimal positions once a gap is created. int prefer = index(e.hash, length); if ((current < prefer && (prefer <= start || start <= current)) || (prefer <= start && start <= current)) { table[start] = e; table[current] = null; start = current; } current = nextIndex(current, length); } } public void clear() { modCount++; Entry[] table = this.table; for (int i = 0; i < table.length; i++) table[i] = null; size = 0; } @SuppressWarnings("unchecked") public Object clone() { try { FastCopyHashMap clone = (FastCopyHashMap) super.clone(); clone.table = table.clone(); clone.entrySet = null; clone.values = null; clone.keySet = null; return clone; } catch (CloneNotSupportedException e) { // should never happen throw new IllegalStateException(e); } } public void printDebugStats() { int optimal = 0; int total = 0; int totalSkew = 0; int maxSkew = 0; for (int i = 0; i < table.length; i++) { Entry e = table[i]; if (e != null) { total++; int target = index(e.hash, table.length); if (i == target) optimal++; else { int skew = Math.abs(i - target); if (skew > maxSkew) maxSkew = skew; totalSkew += skew; } } } System.out.println(" Size: " + size); System.out.println(" Real Size: " + total); System.out.println(" Optimal: " + optimal + " (" + (float) optimal * 100 / total + "%)"); System.out.println(" Average Distnce: " + ((float) totalSkew / (total - optimal))); System.out.println(" Max Distance: " + maxSkew); } public Set> entrySet() { if (entrySet == null) entrySet = new EntrySet(); return entrySet; } public Set keySet() { if (keySet == null) keySet = new KeySet(); return keySet; } public Collection values() { if (values == null) values = new Values(); return values; } @SuppressWarnings("unchecked") private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); int size = s.readInt(); init(size, loadFactor); for (int i = 0; i < size; i++) { K key = (K) s.readObject(); V value = (V) s.readObject(); putForCreate(key, value); } this.size = size; } @SuppressWarnings("unchecked") private void putForCreate(K key, V value) { key = maskNull(key); Entry[] table = this.table; int hash = hash(key); int length = table.length; int index = index(hash, length); Entry e = table[index]; while (e != null) { index = nextIndex(index, length); e = table[index]; } table[index] = new Entry(key, hash, value); } private void writeObject(java.io.ObjectOutputStream s) throws IOException { s.defaultWriteObject(); s.writeInt(size); for (Entry e : table) { if (e != null) { s.writeObject(unmaskNull(e.key)); s.writeObject(e.value); } } } private static final class Entry { final K key; final int hash; final V value; Entry(K key, int hash, V value) { this.key = key; this.hash = hash; this.value = value; } } private abstract class FasyCopyHashMapIterator implements Iterator { private int next = 0; private int expectedCount = modCount; private int current = -1; private boolean hasNext; Entry table[] = FastCopyHashMap.this.table; public boolean hasNext() { if (hasNext == true) return true; Entry table[] = this.table; for (int i = next; i < table.length; i++) { if (table[i] != null) { next = i; return hasNext = true; } } next = table.length; return false; } protected Entry nextEntry() { if (modCount != expectedCount) throw new ConcurrentModificationException(); if (!hasNext && !hasNext()) throw new NoSuchElementException(); current = next++; hasNext = false; return table[current]; } @SuppressWarnings("unchecked") public void remove() { if (modCount != expectedCount) throw new ConcurrentModificationException(); int current = this.current; int delete = current; if (current == -1) throw new IllegalStateException(); // Invalidate current (prevents multiple remove) this.current = -1; // Start were we relocate next = delete; Entry[] table = this.table; if (table != FastCopyHashMap.this.table) { FastCopyHashMap.this.remove(table[delete].key); table[delete] = null; expectedCount = modCount; return; } int length = table.length; int i = delete; table[delete] = null; size--; for (; ;) { i = nextIndex(i, length); Entry e = table[i]; if (e == null) break; int prefer = index(e.hash, length); if ((i < prefer && (prefer <= delete || delete <= i)) || (prefer <= delete && delete <= i)) { // Snapshot the unseen portion of the table if we have // to relocate an entry that was already seen by this iterator if (i < current && current <= delete && table == FastCopyHashMap.this.table) { int remaining = length - current; Entry[] newTable = (Entry[]) new Entry[remaining]; System.arraycopy(table, current, newTable, 0, remaining); // Replace iterator's table. // Leave table local var pointing to the real table this.table = newTable; next = 0; } // Do the swap on the real table table[delete] = e; table[i] = null; delete = i; } } } } private class KeyIterator extends FasyCopyHashMapIterator { public K next() { return unmaskNull(nextEntry().key); } } private class ValueIterator extends FasyCopyHashMapIterator { public V next() { return nextEntry().value; } } private class EntryIterator extends FasyCopyHashMapIterator> { private class WriteThroughEntry extends SimpleEntry { WriteThroughEntry(K key, V value) { super(key, value); } public V setValue(V value) { if (table != FastCopyHashMap.this.table) FastCopyHashMap.this.put(getKey(), value); return super.setValue(value); } } public Map.Entry next() { Entry e = nextEntry(); return new WriteThroughEntry(unmaskNull(e.key), e.value); } } private class KeySet extends AbstractSet { public Iterator iterator() { return new KeyIterator(); } public void clear() { FastCopyHashMap.this.clear(); } public boolean contains(Object o) { return containsKey(o); } public boolean remove(Object o) { int size = size(); FastCopyHashMap.this.remove(o); return size() < size; } public int size() { return FastCopyHashMap.this.size(); } } private class Values extends AbstractCollection { public Iterator iterator() { return new ValueIterator(); } public void clear() { FastCopyHashMap.this.clear(); } public int size() { return FastCopyHashMap.this.size(); } } private class EntrySet extends AbstractSet> { public Iterator> iterator() { return new EntryIterator(); } public boolean contains(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry entry = (Map.Entry) o; Object value = get(entry.getKey()); return eq(entry.getValue(), value); } public void clear() { FastCopyHashMap.this.clear(); } public boolean isEmpty() { return FastCopyHashMap.this.isEmpty(); } public int size() { return FastCopyHashMap.this.size(); } } protected static class SimpleEntry implements Map.Entry { private K key; private V value; SimpleEntry(K key, V value) { this.key = key; this.value = value; } SimpleEntry(Map.Entry entry) { this.key = entry.getKey(); this.value = entry.getValue(); } public K getKey() { return key; } public V getValue() { return value; } public V setValue(V value) { V old = this.value; this.value = value; return old; } public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Map.Entry)) return false; Map.Entry e = (Map.Entry) o; return eq(key, e.getKey()) && eq(value, e.getValue()); } public int hashCode() { return (key == null ? 0 : hash(key)) ^ (value == null ? 0 : hash(value)); } public String toString() { return getKey() + "=" + getValue(); } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/util/SimpleImmutableEntry.java0000644000175000017500000000451611111047320030261 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/util/Util.java0000644000175000017500000001446511111047320025067 0ustar moellermoeller/* * 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.util; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; /** * General utility methods used throughout the JBC code base. * * @author Brian Stansberry * @version $Revision: 7168 $ */ 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); } @SuppressWarnings("unchecked") public static T getInstance(Class clazz) throws Exception { // first look for a getInstance() constructor T instance; try { Method factoryMethod = clazz.getMethod("getInstance", new Class[]{}); instance = (T) factoryMethod.invoke(null); } catch (Exception e) { // no factory method or factory method failed. Try a constructor. instance = clazz.newInstance(); } return instance; } @SuppressWarnings("unchecked") public static Object getInstance(String classname) throws Exception { if (classname == null) throw new IllegalArgumentException("Cannot load null class!"); Class clazz = loadClass(classname); return getInstance(clazz); } /** * 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-3.2.8.GA/src/main/java/org/jboss/cache/CacheManager.java0000755000175000017500000001003511111047320025463 0ustar moellermoeller/* * 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; import org.jboss.cache.config.Configuration; import org.jgroups.ChannelFactory; import java.util.Set; /** * Factory and registry for JBoss Cache instances configured using * named configurations. * * @author Brian Stansberry * @version $Revision: 7168 $ */ 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 a channel factory */ 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-3.2.8.GA/src/main/java/org/jboss/cache/Lifecycle.java0000644000175000017500000000037711133375606025110 0ustar moellermoellerpackage org.jboss.cache; /** * Defines lifecycle operations for various components * * @author Manik Surtani */ public interface Lifecycle { void create() throws Exception; void start() throws Exception; void stop(); void destroy(); } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/NodeSPI.java0000644000175000017500000005123411140322163024434 0ustar moellermoeller/* * 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; 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 AT jboss DOT 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 * @deprecated should use the {@link org.jboss.cache.NodeFactory} instead. */ @Deprecated 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); /** * Returns true if a mapping exists for this key. Returns false if no * mapping exists. * * @param key The key checked for inclusion in the node data. * @return true if a mapping exists for the key, false if not. */ boolean containsKeyDirect(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 getParentDirect(); /** * @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); /** * @return true if the node is a null marker node. Only used with MVCC. */ boolean isNullNode(); /** * Marks a node for updating. Internally, this creates a copy of the delegate and performs any checks necessary to * maintain isolation level. *

    * Only used with MVCC. * * @param container data container * @param writeSkewCheck if true, and the node supports write skew checking, nodes are tested for write skews. */ void markForUpdate(DataContainer container, boolean writeSkewCheck); /** * Commits any updates made on this node to the underlying data structure, making it visible to all other transactions. *

    * Only used with MVCC. * * @param ctx invocation context * @param container data container */ void commitUpdate(InvocationContext ctx, DataContainer container); /** * Only used with MVCC. *

    * * @return true if this node has been marked for update, false otherwise. */ boolean isChanged(); /** * Only used with MVCC. *

    * * @return true if this node has been newly created in the current scope. */ boolean isCreated(); InternalNode getDelegationTarget(); /** * Sets the created flag on a node. *

    * Only used with MVCC. * * @param created flag */ void setCreated(boolean created); /** * Rolls back any changes made to a node. *

    * Only used with MVCC. */ void rollbackUpdate(); } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/batch/0000755000175000017500000000000011675222024023414 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/batch/BatchContainer.java0000644000175000017500000000621711111047320027136 0ustar moellermoeller/* * 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.batch; import org.jboss.cache.CacheException; import org.jboss.cache.factories.annotations.Inject; import javax.transaction.Transaction; import javax.transaction.TransactionManager; /** * A container for holding thread locals for batching, to be used with the {@link org.jboss.cache.Cache#startBatch()} and * {@link org.jboss.cache.Cache#endBatch(boolean)} calls. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ public class BatchContainer { TransactionManager transactionManager; private ThreadLocal batchTransactionContainer = new ThreadLocal(); @Inject void inject(TransactionManager transactionManager) { this.transactionManager = transactionManager; } public void startBatch() throws CacheException { try { if (transactionManager.getTransaction() != null) return; if (batchTransactionContainer.get() == null) { transactionManager.begin(); batchTransactionContainer.set(transactionManager.suspend()); } } catch (Exception e) { throw new CacheException("Unable to start batch", e); } } public void endBatch(boolean success) { Transaction tx = batchTransactionContainer.get(); if (tx == null) return; Transaction existingTx = null; try { existingTx = transactionManager.getTransaction(); transactionManager.resume(tx); if (success) tx.commit(); else tx.rollback(); } catch (Exception e) { throw new CacheException("Unable to end batch", e); } finally { batchTransactionContainer.remove(); try { if (existingTx != null) transactionManager.resume(existingTx); } catch (Exception e) { throw new CacheException("Failed resuming existing transaction " + existingTx, e); } } } public Transaction getBatchTransaction() { return batchTransactionContainer.get(); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/optimistic/0000755000175000017500000000000011675222026024521 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/optimistic/DataVersion.java0000644000175000017500000000437611111047320027600 0ustar moellermoeller/* * 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.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 AT jboss DOT org) * @deprecated this is to support a deprecated locking scheme (Optimistic Locking). Will be removed when Optimistic Locking support is removed. */ @Deprecated 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-3.2.8.GA/src/main/java/org/jboss/cache/optimistic/OptimisticNodeFactory.java0000644000175000017500000000471611111047320031641 0ustar moellermoeller/* * 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.optimistic; import org.jboss.cache.AbstractNodeFactory; import org.jboss.cache.Fqn; import org.jboss.cache.NodeSPI; import org.jboss.cache.UnversionedNode; import org.jboss.cache.VersionedNode; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.lock.LockStrategyFactory; import java.util.Map; /** * Node factory specific to optimistic locking. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated public class OptimisticNodeFactory extends AbstractNodeFactory { private LockStrategyFactory lockStrategyFactory; @Inject private void injectLockStrategyFactory(LockStrategyFactory lockStrategyFactory) { this.lockStrategyFactory = lockStrategyFactory; } @Override protected UnversionedNode createInternalNode(Object childName, Fqn fqn, NodeSPI parent, Map data) { VersionedNode internal = new VersionedNode(fqn, parent, data, cache); internal.injectDependencies(cache, commandsFactory, this); internal.injectLockStrategyFactory(lockStrategyFactory); return internal; } @Override public WorkspaceNode createWrappedNode(NodeSPI dataNode, TransactionWorkspace workspace) { return new WorkspaceNodeImpl(dataNode, workspace, this); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/optimistic/DefaultDataVersion.java0000644000175000017500000000772211111047320031103 0ustar moellermoeller/* * 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.optimistic; import net.jcip.annotations.Immutable; /** * 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 AT jboss DOT org) * @deprecated this is to support a deprecated locking scheme (Optimistic Locking). Will be removed when Optimistic Locking support is removed. */ @Immutable @Deprecated 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-3.2.8.GA/src/main/java/org/jboss/cache/optimistic/TransactionWorkspace.java0000755000175000017500000000644411111047320031526 0ustar moellermoeller/* * 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.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.OptimisticTransactionContext}, which creates and maintains * an instance of TransactionWorkspace for each * transaction running. * * @author Manik Surtani (manik AT jboss DOT org) * @author Steve Woodcock (stevew@jofti.com) * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated 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-3.2.8.GA/src/main/java/org/jboss/cache/optimistic/WorkspaceNode.java0000644000175000017500000001571411111047320030123 0ustar moellermoeller/* * 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.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 AT jboss DOT org) * @author Steve Woodcock (stevew@jofti.com) * @since 1.3.0 * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated public interface WorkspaceNode// extends Node { Fqn getFqn(); /** * 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 its 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); /** * 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 getChildDirect(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 getChildDirect(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); boolean isRemoved(); void setRemoved(boolean marker); void markAsRemoved(boolean marker, boolean recursive); void clearData(); Map getData(); V remove(K removeKey); V get(K key); Set getKeys(); Set getChildrenNames(); boolean removeChild(Object nodeName); void putAll(Map data); V put(K key, V value); } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/optimistic/TransactionWorkspaceImpl.java0000755000175000017500000000612411111047320032343 0ustar moellermoeller/* * 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.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 AT jboss DOT org) * @author Steve Woodcock (stevew@jofti.com) * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated 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-3.2.8.GA/src/main/java/org/jboss/cache/optimistic/WorkspaceNodeImpl.java0000644000175000017500000003304411111047320030741 0ustar moellermoeller/* * 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.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 AT jboss DOT org) * @author Steve Woodcock (stevew@jofti.com) * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated 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; private NodeFactory nodeFactory; /** * Constructs with a node and workspace. */ public WorkspaceNodeImpl(NodeSPI node, TransactionWorkspace workspace, NodeFactory nodeFactory) { 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(); this.nodeFactory = nodeFactory; } 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 markAsRemoved(boolean marker, boolean recursive) { setFlag(NodeFlags.REMOVED, marker); if (recursive && children != null) { synchronized (this) { for (Object child : children.values()) { ((NodeSPI) child).markAsDeleted(marker, true); } } } 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() || isRemoved(); } 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 = 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 childName, NodeSPI parent, CacheSPI cache, DataVersion version) { if (childName == null) { return null; } NodeSPI child = nodeFactory.createNode(childName, parent); 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 getNode() { return node; } @Override public DataVersion getVersion() { return version; } @Override 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 (isRemoved()) sb.append("del "); if (isModified()) sb.append("modified "); if (isCreated()) sb.append("new "); return getClass().getSimpleName() + " [ fqn=" + getFqn() + " " + sb + "ver=" + version + " " + (isVersioningImplicit() ? "implicit" : "explicit") + "]"; } @Override public NodeSPI addChildDirect(Fqn f) { CacheSPI cache = getCache(); NodeSPI newNode = null; GlobalTransaction gtx = cache.getInvocationContext().getGlobalTransaction(); if (f.size() == 1) { newNode = createChild(f.get(0), node, getCache(), version); } else { // recursively create children NodeSPI currentParent = this.getNode(); 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 = 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(); } @Override public NodeSPI getChildDirect(Fqn f) { if (f.size() > 1) { throw new UnsupportedOperationException("Workspace node does not support fetching indirect children"); } return getChildDirect(f.getLastElement()); } @Override 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-3.2.8.GA/src/main/java/org/jboss/cache/optimistic/DataVersioningException.java0000644000175000017500000000314711111047320032150 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/Modification.java0000644000175000017500000001474211111047320025600 0ustar moellermoeller/* * 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; 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: 7168 $ */ 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-3.2.8.GA/src/main/java/org/jboss/cache/SuspectException.java0000644000175000017500000000307711111047320026477 0ustar moellermoeller/* * 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; /** * Thrown when a member is suspected during remote method invocation * * @author Bela Ban * @version $Id: SuspectException.java 7168 2008-11-19 17:37:20Z jason.greene@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-3.2.8.GA/src/main/java/org/jboss/cache/lock/0000755000175000017500000000000011675222041023262 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/lock/IdentityLock.java0000755000175000017500000004413611111047320026530 0ustar moellermoeller/* * 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 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 * @deprecated will be removed when we drop support for Pessimistic Locking and Optimistic Locking */ @Deprecated @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-3.2.8.GA/src/main/java/org/jboss/cache/lock/LockStrategySerializable.java0000644000175000017500000000466011111047320031063 0ustar moellermoeller/* * 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 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: 7168 $ */ 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-3.2.8.GA/src/main/java/org/jboss/cache/lock/ReadWriteLockWithUpgrade.java0000644000175000017500000003733411111047320030770 0ustar moellermoeller/* * 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 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-3.2.8.GA/src/main/java/org/jboss/cache/lock/LockStrategyFactory.java0000755000175000017500000000551611111047320030070 0ustar moellermoeller/* * 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 org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.NodeLockingScheme; 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.getNodeLockingScheme() == NodeLockingScheme.OPTIMISTIC ? 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-3.2.8.GA/src/main/java/org/jboss/cache/lock/TimeoutException.java0000644000175000017500000000350111111047320027417 0ustar moellermoeller/* * 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 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: 7168 $ *

    *

    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-3.2.8.GA/src/main/java/org/jboss/cache/lock/PessimisticNodeBasedLockManager.java0000644000175000017500000003335111237012371032274 0ustar moellermoeller/* * 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 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.PessimisticUnversionedNode; import org.jboss.cache.commands.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 AT jboss DOT 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 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.getLockAcquisitionTimeout(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; int createdLevel = -1; // 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 { boolean skipLockAcquire = false; if (currentNode == null) { if (createIfNotExists) { // if the new node is to be marked as deleted, do not notify! PessimisticUnversionedNode parentInternalNode = (PessimisticUnversionedNode) parent.getDelegationTarget(); currentNode = parentInternalNode.addChildAndAcquireLock(childName, !skipNotification, new LockAcquirer(ctx, WRITE, timeout, owner)); skipLockAcquire = true; if (!created) { created = true; createdLevel = currentIndex; } 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(); if (!skipLockAcquire) { // actually acquire the lock we need. This method blocks. acquireNodeLock(ctx, currentNode, owner, 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; created = false; createdLevel = -1; } else { currentNode = parent; if (createdLevel == currentIndex) { created = false; createdLevel = -1; } 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, LockType lockType, long lockTimeout) throws LockingException, TimeoutException, InterruptedException { NodeLock lock = node.getLock(); boolean acquired = lock.acquire(owner, lockTimeout, lockType); // Record the lock for release on method return or tx commit/rollback if (acquired) ctx.addLock(lock); } public class LockAcquirer { InvocationContext ctx; LockType type; long timeout; Object owner; public LockAcquirer(InvocationContext ctx, LockType type, long timeout, Object owner) { this.ctx = ctx; this.type = type; this.timeout = timeout; this.owner = owner; } public void acquire(NodeSPI node) { try { acquireNodeLock(ctx, node, owner, type, timeout); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/lock/LockingException.java0000644000175000017500000000463011111047320027363 0ustar moellermoeller/* * 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 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-3.2.8.GA/src/main/java/org/jboss/cache/lock/UpgradeException.java0000644000175000017500000000303111111047320027356 0ustar moellermoeller/* * 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; /** * Used when a read-lock cannot be upgraded to a write-lock * * @author Bela Ban * @version $Revision: 7168 $ */ 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-3.2.8.GA/src/main/java/org/jboss/cache/lock/AbstractLockManager.java0000644000175000017500000000373211111047320027767 0ustar moellermoeller/* * 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 org.jboss.cache.InvocationContext; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.Start; /** * Common lock manager functionality * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ public abstract class AbstractLockManager implements LockManager { protected Configuration configuration; protected long lockAcquisitionTimeout; @Inject public void injectConfiguration(Configuration configuration) { this.configuration = configuration; } @Start public void setLockAcquisitionTimeout() { this.lockAcquisitionTimeout = configuration.getLockAcquisitionTimeout(); } public Object getLockOwner(InvocationContext ctx) { return ctx.getGlobalTransaction() != null ? ctx.getGlobalTransaction() : Thread.currentThread(); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/lock/NonBlockingWriterLock.java0000644000175000017500000000422111111047320030323 0ustar moellermoeller/* * 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; /** * 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 7168 2008-11-19 17:37:20Z jason.greene@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-3.2.8.GA/src/main/java/org/jboss/cache/lock/LockStrategy.java0000755000175000017500000000322611111047320026534 0ustar moellermoeller/* * 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 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-3.2.8.GA/src/main/java/org/jboss/cache/lock/LockStrategyReadCommitted.java0000644000175000017500000000412611111047320031173 0ustar moellermoeller/* * 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 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: 7168 $ */ 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-3.2.8.GA/src/main/java/org/jboss/cache/lock/ThreadLocalMap.java0000644000175000017500000000553311111047320026741 0ustar moellermoeller/* * 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 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 7168 2008-11-19 17:37:20Z jason.greene@jboss.com $ */ public class ThreadLocalMap implements Map { private final ThreadLocal> threadLocal = new ThreadLocal>() { @Override protected Map initialValue() { return new HashMap(); } }; private Map getThreadLocalMap() { return 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-3.2.8.GA/src/main/java/org/jboss/cache/lock/OwnerNotExistedException.java0000755000175000017500000000325111111047320031077 0ustar moellermoeller/* * 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; /** * @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-3.2.8.GA/src/main/java/org/jboss/cache/lock/LockStrategyReadUncommitted.java0000644000175000017500000000463311111047320031541 0ustar moellermoeller/* * 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 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: 7168 $ */ 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-3.2.8.GA/src/main/java/org/jboss/cache/lock/LockManager.java0000644000175000017500000003374711111047320026314 0ustar moellermoeller/* * 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 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 AT jboss DOT 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) throws InterruptedException; /** * 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) throws InterruptedException; /** * 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) throws InterruptedException; /** * 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) throws InterruptedException; /** * 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 OR transaction entry using {@link org.jboss.cache.InvocationContext#addLock(Object)}. *

    * 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) throws InterruptedException; /** * 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 OR transaction entry using {@link org.jboss.cache.InvocationContext#addLock(Object)}. *

    * 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) throws InterruptedException; /** * 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) throws InterruptedException; /** * 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) throws InterruptedException; /** * 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) throws InterruptedException; /** * 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 OR transaction entry using {@link org.jboss.cache.InvocationContext#addLock(Object)}. *

    * 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) throws InterruptedException; /** * 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 OR transaction entry using {@link org.jboss.cache.InvocationContext#addLock(Object)}. *

    * 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) throws InterruptedException; /** * 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 fqn node to inspect * @return true of locked; false if not. */ boolean isLocked(Fqn fqn); /** * 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 its children) to a String. * * @param node node to inspect */ String printLockInfo(NodeSPI node); /** * Prints lock information for all locks. * * @return lock information */ String printLockInfo(); } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/lock/NullLock.java0000755000175000017500000000411311111047320025640 0ustar moellermoeller/* * 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 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-3.2.8.GA/src/main/java/org/jboss/cache/lock/StripedLock.java0000644000175000017500000001224611111047320026343 0ustar moellermoeller/* * 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. * * @param x the object serving as a key * @return the hash code */ final int hash(Object x) { // Spread bits to regularize both segment and index locations, // using variant of single-word Wang/Jenkins hash. int h = x.hashCode(); h += (h << 15) ^ 0xffffcd7d; h ^= (h >>> 10); h += (h << 3); h ^= (h >>> 6); h += (h << 2) + (h << 14); h = h ^ (h >>> 16); 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-3.2.8.GA/src/main/java/org/jboss/cache/lock/DeadlockException.java0000644000175000017500000000313011111047320027475 0ustar moellermoeller/* * 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; /** * 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: 7168 $ */ 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-3.2.8.GA/src/main/java/org/jboss/cache/lock/LockUtil.java0000644000175000017500000002427611111047320025654 0ustar moellermoeller/* * 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 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.CommandsFactory; import org.jboss.cache.commands.write.PutDataMapCommand; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.TransactionContext; 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(LockUtil.class); private static final boolean trace = log.isTraceEnabled(); private static interface TransactionLockStatus extends Status { int STATUS_BROKEN = Integer.MIN_VALUE; } public static boolean breakTransactionLock(Fqn fqn, 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(fqn, gtx)) { int status = breakTransactionLock(gtx, fqn, 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, Fqn fqn, 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(fqn, 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(fqn, 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(fqn, gtx); status = TransactionLockStatus.STATUS_BROKEN; } } catch (Exception e) { log.error("Exception breaking locks held by " + gtx, e); lockManager.unlock(fqn, 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(fqn, gtx)) { // perhaps we should throw an exception? lockManager.unlock(fqn, 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(); TransactionContext transactionContext = ctx.getTransactionContext(); boolean needToReverseRemove = reverseRemoveCheck && childNode.isDeleted() && transactionContext != null && transactionContext.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 transactionContext.addModification(command); //we're prepared for rollback, now reset the node childNode.clearDataDirect(); if (createdNodes != null) { createdNodes.add(childNode); } } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/lock/LockMap.java0000755000175000017500000001333611111047320025452 0ustar moellermoeller/* * 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 org.jboss.cache.util.concurrent.ConcurrentHashSet; import java.util.Collection; /** * Provide lock ownership mapping. * * @author Ben Wang * @version $Id: LockMap.java 7168 2008-11-19 17:37:20Z jason.greene@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 true if the caller is the owner of the current lock * @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 lock information */ 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-3.2.8.GA/src/main/java/org/jboss/cache/lock/FqnLockManager.java0000644000175000017500000000464211111047320026751 0ustar moellermoeller/* * 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 org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import java.util.Collection; /** * An abstract lock manager that deals with Fqns rather than nodes. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ public abstract class FqnLockManager extends AbstractLockManager { public boolean lock(NodeSPI node, LockType lockType, Object owner) throws InterruptedException { return lock(node.getFqn(), lockType, owner); } public boolean lock(NodeSPI node, LockType lockType, Object owner, long timeout) throws InterruptedException { return lock(node.getFqn(), lockType, owner, timeout); } public boolean lockAndRecord(NodeSPI node, LockType lockType, InvocationContext ctx) throws InterruptedException { return lockAndRecord(node.getFqn(), lockType, ctx); } public void unlock(NodeSPI node, Object owner) { unlock(node.getFqn(), owner); } public boolean ownsLock(NodeSPI node, Object owner) { return ownsLock(node.getFqn(), owner); } public Object getWriteOwner(NodeSPI node) { return getWriteOwner(node.getFqn()); } public Collection getReadOwners(NodeSPI node) { return getReadOwners(node.getFqn()); } public boolean isLocked(NodeSPI node) { return isLocked(node.getFqn()); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/lock/NodeBasedLockManager.java0000644000175000017500000002303311111047320030044 0ustar moellermoeller/* * 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 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.factories.annotations.Inject; import org.jboss.cache.factories.annotations.Start; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.ListIterator; /** * @author Mircea.Markus@jboss.com * @since 2.2 * @deprecated since this is specific to legacy locking schemes */ @SuppressWarnings("deprecation") @Deprecated public class NodeBasedLockManager extends AbstractLockManager { private static final Log log = LogFactory.getLog(NodeBasedLockManager.class); private static final boolean trace = log.isTraceEnabled(); protected DataContainer dataContainer; protected NodeSPI rootNode; @Inject public void inject(DataContainer dataContainer) { this.dataContainer = dataContainer; } @Start public void setRootNode() { 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 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.getLockAcquisitionTimeout(lockAcquisitionTimeout)); if (lock != null) { ctx.addLock(lock); return true; } else { return false; } } public void unlock(InvocationContext ctx) { List locks = ctx.getLocks(); if (locks.isEmpty()) return; Object owner = getLockOwner(ctx); // unlocking needs to be done in reverse order. ListIterator it = locks.listIterator(locks.size()); while (it.hasPrevious()) { NodeLock l = it.previous(); if (trace) log.trace("releasing lock for " + l.getFqn() + " (" + l + "), owner " + owner); l.release(owner); } ctx.clearLocks(); } 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.getLockAcquisitionTimeout(lockAcquisitionTimeout), false); if (locks == null) return false; if (locks.size() > 0) { ctx.addAllLocks(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(Fqn fqn) { return isLocked(dataContainer.peek(fqn)); } 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(); } public String printLockInfo() { return printLockInfo(dataContainer.getRoot()); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/lock/IsolationLevel.java0000755000175000017500000000322011111047320027044 0ustar moellermoeller/* * 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; /** * Various transaction isolation levels as an enumerated class. Note that MVCC * in JBoss Cache 3.0.0 and above only supports {@link #READ_COMMITTED} and {@link #REPEATABLE_READ}, upgrading where possible. *

    * Also note that JBoss Cache defaults to {@link #REPEATABLE_READ}. *

    * * @see Isolation levels */ public enum IsolationLevel { /** * No isolation. */ NONE, SERIALIZABLE, REPEATABLE_READ, READ_COMMITTED, READ_UNCOMMITTED } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/lock/LockType.java0000644000175000017500000000241311111047320025645 0ustar moellermoeller/* * 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; /** * An enumeration to define different types of locks. * * @author Manik Surtani (manik AT jboss DOT org) * @since 2.2.0 */ public enum LockType { NONE, READ, WRITE } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/lock/LockStrategyNone.java0000644000175000017500000000344511111047320027354 0ustar moellermoeller/* * 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 java.util.concurrent.locks.Lock; /** * Transaction isolation level of None. * * @author Lari Hotari * @version $Revision: 7168 $ */ 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-3.2.8.GA/src/main/java/org/jboss/cache/lock/LockStrategyRepeatableRead.java0000644000175000017500000000447711111047320031323 0ustar moellermoeller/* * 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 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: 7168 $ */ 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-3.2.8.GA/src/main/java/org/jboss/cache/lock/MVCCLockManager.java0000644000175000017500000003047311256720356026777 0ustar moellermoeller/* * 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 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.InternalNode; import org.jboss.cache.InvocationContext; import org.jboss.cache.Node; import org.jboss.cache.NodeSPI; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.Start; import org.jboss.cache.invocation.InvocationContextContainer; import org.jboss.cache.jmx.annotations.ManagedAttribute; import static org.jboss.cache.lock.LockType.READ; import org.jboss.cache.util.concurrent.locks.LockContainer; import org.jboss.cache.util.concurrent.locks.OwnableReentrantLock; import org.jboss.cache.util.concurrent.locks.OwnableReentrantSharedLockContainer; import org.jboss.cache.util.concurrent.locks.ReentrantSharedLockContainer; import org.jboss.cache.util.concurrent.locks.PerElementReentrantLockContainer; import org.jboss.cache.util.concurrent.locks.PerElementOwnableReentrantLockContainer; import javax.transaction.TransactionManager; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Set; import static java.util.concurrent.TimeUnit.MILLISECONDS; import java.util.concurrent.locks.Lock; /** * This lock manager acquires and releases locks based on the Fqn passed in and not on the node itself. The main benefit * here is that locks can be acquired and held even for nonexistent nodes. *

    * Uses lock striping so that a fixed number of locks are maintained for the entire cache, and not a single lock per node. *

    * This implementation only acquires exclusive WRITE locks, and throws an exception if you attempt to use it to acquire a * READ lock. JBoss Cache's MVCC design doesn't use read locks at all. *

    * The concept of lock owners is implicit in this implementation and any owners passed in as parameters (where required) * will be ignored. See {@link org.jboss.cache.util.concurrent.locks.OwnableReentrantLock} for details on how ownership * is determined. *

    * * @author Manik Surtani (manik AT jboss DOT org) * @see org.jboss.cache.util.concurrent.locks.OwnableReentrantLock * @since 3.0 */ public class MVCCLockManager extends FqnLockManager { LockContainer lockContainer; DataContainer dataContainer; private Set internalFqns; private CacheSPI cache; private TransactionManager transactionManager; private InvocationContextContainer invocationContextContainer; private static final Log log = LogFactory.getLog(MVCCLockManager.class); private static final boolean trace = log.isTraceEnabled(); @Inject public void injectDependencies(DataContainer dataContainer, CacheSPI cache, TransactionManager transactionManager, InvocationContextContainer invocationContextContainer) { this.dataContainer = dataContainer; this.cache = cache; this.transactionManager = transactionManager; this.invocationContextContainer = invocationContextContainer; } @Start public void startLockManager() { // don't we all love nested ternary operators? :-) lockContainer = configuration.isUseLockStriping() ? transactionManager == null ? new ReentrantSharedLockContainer(configuration.getConcurrencyLevel()) : new OwnableReentrantSharedLockContainer(configuration.getConcurrencyLevel(), invocationContextContainer) : transactionManager == null ? new PerElementReentrantLockContainer(configuration.getConcurrencyLevel()) : new PerElementOwnableReentrantLockContainer(configuration.getConcurrencyLevel(), invocationContextContainer); } @Start public void setInternalFqns() { internalFqns = cache.getInternalFqns(); } public boolean lock(Fqn fqn, LockType lockType, Object owner) throws InterruptedException { if (lockType == READ) return true; // we don't support read locks. if (trace) log.trace("Attempting to lock " + fqn); return lockContainer.acquireLock(fqn, lockAcquisitionTimeout, MILLISECONDS); } public boolean lock(Fqn fqn, LockType lockType, Object owner, long timeoutMillis) throws InterruptedException { if (lockType == READ) return true; // we don't support read locks. if (trace) log.trace("Attempting to lock " + fqn); return lockContainer.acquireLock(fqn, lockAcquisitionTimeout, MILLISECONDS); } public boolean lockAndRecord(Fqn fqn, LockType lockType, InvocationContext ctx) throws InterruptedException { if (lockType == READ) return true; // we don't support read locks. if (trace) log.trace("Attempting to lock " + fqn); if (lockContainer.acquireLock(fqn, ctx.getLockAcquisitionTimeout(lockAcquisitionTimeout), MILLISECONDS)) { ctx.addLock(fqn); return true; } // couldn't acquire lock! return false; } public void unlock(Fqn fqn, Object owner) { if (trace) log.trace("Attempting to unlock " + fqn); try { lockContainer.releaseLock(fqn); } catch (IllegalMonitorStateException imse) { if (trace) log.trace("Caught exception and ignoring.", imse); } } @SuppressWarnings("unchecked") public void unlock(InvocationContext ctx) { List locks = ctx.getLocks(); if (!locks.isEmpty()) { // unlocking needs to be done in reverse order. ListIterator it = locks.listIterator(locks.size()); while (it.hasPrevious()) { Fqn f = it.previous(); if (trace) log.trace("Attempting to unlock " + f); try { lockContainer.releaseLock(f); } catch (IllegalMonitorStateException imse) { if (trace) log.trace("Caught exception and ignoring.", imse); } } } } @SuppressWarnings("unchecked") private boolean lockRecursively(InternalNode node, long timeoutMillis, boolean excludeInternalFqns, InvocationContext ctx) throws InterruptedException { if (excludeInternalFqns && internalFqns.contains(node.getFqn())) return true; // this will stop the recursion from proceeding down this subtree. boolean locked = ctx == null ? lock(node.getFqn(), LockType.WRITE, null, timeoutMillis) : lockAndRecord(node.getFqn(), LockType.WRITE, ctx); if (!locked) return false; boolean needToUnlock = false; // need to recursively walk through the node's children and acquire locks. This needs to happen using API methods // since any cache loading will need to happen. Map children = node.getChildrenMap(); try { for (InternalNode child : children.values()) { locked = lockRecursively(child, timeoutMillis, excludeInternalFqns, ctx); if (!locked) { needToUnlock = true; break; } } } finally { if (needToUnlock) { for (InternalNode child : children.values()) { Fqn childFqn = child.getFqn(); unlock(childFqn, null); if (ctx != null) ctx.removeLock(childFqn); } unlock(node.getFqn(), null); if (ctx != null) ctx.removeLock(node.getFqn()); } } return locked; } public boolean lockAll(NodeSPI node, LockType lockType, Object owner) throws InterruptedException { if (lockType == READ) return true; // we don't support read locks. return lockRecursively(node.getDelegationTarget(), lockAcquisitionTimeout, false, null); } public boolean lockAll(NodeSPI node, LockType lockType, Object owner, long timeout) throws InterruptedException { if (lockType == READ) return true; // we don't support read locks. return lockRecursively(node.getDelegationTarget(), timeout, false, null); } public boolean lockAll(NodeSPI node, LockType lockType, Object owner, long timeout, boolean excludeInternalFqns) throws InterruptedException { if (lockType == READ) return true; // we don't support read locks. return lockRecursively(node.getDelegationTarget(), timeout, excludeInternalFqns, null); } public boolean lockAllAndRecord(NodeSPI node, LockType lockType, InvocationContext ctx) throws InterruptedException { if (lockType == READ) return true; // we don't support read locks. return lockRecursively(node.getDelegationTarget(), ctx.getLockAcquisitionTimeout(lockAcquisitionTimeout), false, ctx); } public boolean lockAllAndRecord(Fqn fqn, LockType lockType, InvocationContext ctx) throws InterruptedException { return lockRecursively(dataContainer.peekInternalNode(fqn, false), ctx.getLockAcquisitionTimeout(lockAcquisitionTimeout), false, ctx); } public void unlockAll(NodeSPI node, Object owner) { // depth first. Set> children = node.getChildren(); if (children != null) { for (Node child : children) unlockAll((NodeSPI) child, null); } unlock(node.getFqn(), null); } public void unlockAll(NodeSPI node) { unlockAll(node, null); } public boolean ownsLock(Fqn fqn, LockType lockType, Object owner) { if (lockType == READ) return false; // we don't support read locks. return lockContainer.ownsLock(fqn, owner); } public boolean ownsLock(Fqn fqn, Object owner) { return lockContainer.ownsLock(fqn, owner); } public boolean isLocked(Fqn fqn) { return lockContainer.isLocked(fqn); } public boolean isLocked(NodeSPI n, LockType lockType) { if (lockType == READ) return false; // we don't support read locks. return lockContainer.isLocked(n.getFqn()); } public Object getWriteOwner(Fqn f) { if (lockContainer.isLocked(f)) { Lock l = lockContainer.getLock(f); if (l instanceof OwnableReentrantLock) { return ((OwnableReentrantLock) l).getOwner(); } else { // cannot determine owner. return null; } } else return null; } public Collection getReadOwners(Fqn f) { return Collections.emptySet(); } public String printLockInfo(NodeSPI node) { return printLockInfo(); } public String printLockInfo() { return lockContainer.toString(); } @ManagedAttribute(name = "concurrency level", writable = false, description = "The concurrency level that the MVCC Lock Manager has been configured with.") public int getConcurrencyLevel() { return configuration.getConcurrencyLevel(); } @ManagedAttribute(name = "locks held", writable = false, description = "The number of exclusive locks that are held.") public int getNumberOfLocksHeld() { return lockContainer.getNumLocksHeld(); } @ManagedAttribute(name = "locks held", writable = false, description = "The number of exclusive locks that are available.") public int getNumberOfLocksAvailable() { return lockContainer.size() - lockContainer.getNumLocksHeld(); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/lock/SemaphoreLock.java0000755000175000017500000000423711111047320026660 0ustar moellermoeller/* * 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 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-3.2.8.GA/src/main/java/org/jboss/cache/lock/NodeLock.java0000755000175000017500000001123411111047320025615 0ustar moellermoeller/* * 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 org.jboss.cache.Fqn; import java.util.Collection; import java.util.Set; /** * Interface for a lock for nodes. *

    * * @deprecated will be removed when we drop support for Pessimistic Locking and Optimistic Locking */ @Deprecated 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-3.2.8.GA/src/main/java/org/jboss/cache/annotations/0000755000175000017500000000000011675222042024670 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/annotations/Compat.java0000644000175000017500000000350611111047320026747 0ustar moellermoeller/* * 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.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 never be used for any * new code. Similar to deprecated, but provides additional meaning to developers on why certain classes exist. *

    * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ @Retention(RetentionPolicy.SOURCE) @Target({ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.METHOD, ElementType.PACKAGE, ElementType.PARAMETER}) public @interface Compat { String notes() default ""; }jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/annotations/Experimental.java0000644000175000017500000000350711111047320030162 0ustar moellermoeller/* * 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.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 AT jboss DOT 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-3.2.8.GA/src/main/java/org/jboss/cache/Node.java0000755000175000017500000003253611236260260024074 0ustar moellermoeller/* * 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; 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 AT jboss DOT 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. This is a relative Fqn. * @return null if the child does not exist. */ Node getChild(Fqn f); /** * @param name name of the child. Note that passing in a String of "/a/b/c" will not return a node called 'c', * 3 nodes deep. Instead it will try and look for a child called '/a/b/c' directly under this node. *

    * If you wish to retrieve a child more than one level deep, use the {@link #getChild(Fqn)} version of this method. *

    * @return a direct child of the current node, named by the name parameter. */ 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); /** * @return true if the current node is a leaf node (i.e., has no children), or false otherwise. */ boolean isLeaf(); } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/invocation/0000755000175000017500000000000011675222045024507 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/invocation/InvocationContextContainer.java0000644000175000017500000000353211111047320032660 0ustar moellermoeller/* * 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.invocation; import org.jboss.cache.InvocationContext; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.NonVolatile; import org.jboss.cache.factories.context.ContextFactory; /** * Container and factory for thread locals * * @author Manik Surtani (manik AT jboss DOT org) * @since 2.1.0 */ @NonVolatile public class InvocationContextContainer extends ThreadLocal { ContextFactory contextFactory; @Inject public void injectContextFactory(ContextFactory contextFactory) { this.contextFactory = contextFactory; } @Override protected InvocationContext initialValue() { // create if this is initially unset return contextFactory.createInvocationContext(); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/invocation/LegacyInvocationContext.java0000644000175000017500000000451111111047320032140 0ustar moellermoeller/* * 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.invocation; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import java.util.Collections; import java.util.Map; /** * This is to provide backward compatibility with legacy locking schemes. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated public class LegacyInvocationContext extends InvocationContext { DataContainer container; public LegacyInvocationContext(DataContainer container) { this.container = container; } public NodeSPI lookUpNode(Fqn fqn) { return container.peek(fqn); } public void putLookedUpNode(Fqn f, NodeSPI n) { // a no-op by default. } public void putLookedUpNodes(Map lookedUpNodes) { // a no-op by default. } public void clearLookedUpNodes() { // no-op } public Map getLookedUpNodes() { // a no-op by default. return Collections.emptyMap(); } public InvocationContext copy() { LegacyInvocationContext copy = new LegacyInvocationContext(container); doCopy(copy); return copy; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/invocation/MVCCInvocationContext.java0000644000175000017500000001163511225331374031504 0ustar moellermoeller/* * 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.invocation; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.transaction.MVCCTransactionContext; import org.jboss.cache.transaction.TransactionContext; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * An invocation context that is specific to MVCC locking * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ public class MVCCInvocationContext extends InvocationContext { private HashMap lookedUpNodes = null; private MVCCTransactionContext mvccTCtx; @Override public void setTransactionContext(TransactionContext transactionContext) { super.setTransactionContext(transactionContext); mvccTCtx = (MVCCTransactionContext) transactionContext; } /** * Retrieves a node from the registry of looked up nodes in the current scope. *

    * If a transaction is in progress, implementations should delegate to {@link org.jboss.cache.transaction.MVCCTransactionContext#lookUpNode(Fqn)} *

    * * @param fqn fqn to look up * @return a node, or null if it cannot be found. */ public NodeSPI lookUpNode(Fqn fqn) { if (mvccTCtx != null) return mvccTCtx.lookUpNode(fqn); return lookedUpNodes == null ? null : lookedUpNodes.get(fqn); } /** * Puts an entry in the registry of looked up nodes in the current scope. *

    * If a transaction is in progress, implementations should delegate to {@link org.jboss.cache.transaction.MVCCTransactionContext#putLookedUpNode(Fqn, NodeSPI)} *

    * * @param f fqn to add * @param n node to add */ public void putLookedUpNode(Fqn f, NodeSPI n) { if (mvccTCtx != null) mvccTCtx.putLookedUpNode(f, n); else { if (lookedUpNodes == null) lookedUpNodes = new HashMap(4); lookedUpNodes.put(f, n); } } public void putLookedUpNodes(Map lookedUpNodes) { if (mvccTCtx != null) mvccTCtx.putLookedUpNodes(lookedUpNodes); else { if (this.lookedUpNodes == null) this.lookedUpNodes = new HashMap(lookedUpNodes); else this.lookedUpNodes.putAll(lookedUpNodes); } } /** * Clears the registry of looked up nodes. *

    * If a transaction is in progress, implementations should delegate to {@link org.jboss.cache.transaction.MVCCTransactionContext#clearLookedUpNodes()}. */ public void clearLookedUpNodes() { // TODO: see if we can reinstate common behaviour once we have the ICI calling ctx.reset() instead of ctx.clearLookedUpNodes() // if (mvccTCtx != null) // mvccTCtx.clearLookedUpNodes(); // else if (lookedUpNodes != null) lookedUpNodes.clear(); } /** * Retrieves a map of nodes looked up within the current invocation's scope. *

    * If a transaction is in progress, implementations should delegate to {@link org.jboss.cache.transaction.MVCCTransactionContext#getLookedUpNodes()}. *

    * * @return a map of looked up nodes. */ @SuppressWarnings("unchecked") public Map getLookedUpNodes() { if (mvccTCtx != null) return mvccTCtx.getLookedUpNodes(); return (Map) (lookedUpNodes == null ? Collections.emptyMap() : lookedUpNodes); } @Override public void reset() { super.reset(); if (lookedUpNodes != null) { lookedUpNodes.clear(); lookedUpNodes = null; } } @SuppressWarnings("unchecked") public InvocationContext copy() { MVCCInvocationContext copy = new MVCCInvocationContext(); doCopy(copy); if (lookedUpNodes != null) copy.lookedUpNodes = (HashMap) lookedUpNodes.clone(); return copy; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/invocation/AbstractInvocationDelegate.java0000644000175000017500000000516611111047320032574 0ustar moellermoeller/* * 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.invocation; import org.jboss.cache.InvocationContext; 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. *

    * * @author Manik Surtani (manik AT jboss DOT org) * @see org.jboss.cache.interceptors.base.CommandInterceptor * @see InvocationContext * @since 2.1.0 */ public abstract class AbstractInvocationDelegate { 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-3.2.8.GA/src/main/java/org/jboss/cache/invocation/CacheInvocationDelegate.java0000644000175000017500000005205211363314565032051 0ustar moellermoeller/* * 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.invocation; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.*; import org.jboss.cache.batch.BatchContainer; import org.jboss.cache.buddyreplication.BuddyManager; import org.jboss.cache.buddyreplication.GravitateResult; import org.jboss.cache.commands.CommandsFactory; 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.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.Configuration.NodeLockingScheme; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.config.Option; import org.jboss.cache.factories.ComponentRegistry; 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.interceptors.base.CommandInterceptor; import org.jboss.cache.loader.CacheLoaderManager; import org.jboss.cache.marshall.Marshaller; import org.jboss.cache.mvcc.MVCCNodeHelper; 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.jboss.cache.util.Immutables; import org.jgroups.Address; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Iterator; /** * 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 AT jboss DOT org) * @since 2.1.0 */ @NonVolatile public class CacheInvocationDelegate extends AbstractInvocationDelegate implements CacheSPI { private static final Log log = LogFactory.getLog(CacheInvocationDelegate.class); // 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; private MVCCNodeHelper mvccHelper; private boolean usingMvcc; private BatchContainer batchContainer; @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, MVCCNodeHelper mvccHelper, BatchContainer batchContainer) { 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; this.mvccHelper = mvccHelper; this.batchContainer = batchContainer; } @Start void setNodeLockingScheme() { usingMvcc = configuration.getNodeLockingScheme() == NodeLockingScheme.MVCC; } private void reset() { this.usingMvcc = false; 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 getNode(Fqn.ROOT); } public TransactionManager getTransactionManager() { return transactionManager; } public void addInterceptor(CommandInterceptor i, int position) { invoker.addInterceptor(i, position); } public void addInterceptor(CommandInterceptor i, Class afterInterceptor) { invoker.addAfterInterceptor(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) { if (usingMvcc) { InvocationContext ctx = invocationContextContainer.get(); cacheStatusCheck(ctx); ExistsCommand command = commandsFactory.buildExistsNodeCommand(fqn); return (Boolean) invoker.invoke(ctx, command); } else { return peek(fqn, 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); } @SuppressWarnings("unchecked") public NodeSPI peek(Fqn fqn, boolean includeDeletedNodes, boolean includeInvalidNodes) { // TODO: clean this up somehow! Anyway, this method should NOT be used outside of testing frameworks. return (usingMvcc) ? mvccPeek(fqn) : (NodeSPI) dataContainer.peek(fqn, includeDeletedNodes, includeInvalidNodes); } @SuppressWarnings("unchecked") public NodeSPI peek(Fqn fqn, boolean includeDeletedNodes) { // TODO: clean this up somehow! Anyway, this method should NOT be used outside of testing frameworks. return (usingMvcc) ? mvccPeek(fqn) : (NodeSPI) dataContainer.peek(fqn, includeDeletedNodes); } @SuppressWarnings("unchecked") private NodeSPI mvccPeek(Fqn f) { NodeSPI n; try { n = mvccHelper.wrapNodeForReading(getInvocationContext(), f, false); } catch (InterruptedException e) { throw new CacheException(e); } if (n == null || n.isNullNode()) return null; return n; } 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); } @SuppressWarnings("unchecked") 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().copy(); Set internalFqns = getInternalFqns(); for (Object childName : peek(fqn, false, false).getChildrenNames()) { if (!internalFqns.contains(Fqn.fromElements(childName))) { ctx.setOptionOverrides(o.copy()); 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)); } @SuppressWarnings("unchecked") 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)); } @SuppressWarnings("unchecked") 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) { invokePut(fqn, data, false); } 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."); } } } @SuppressWarnings("unchecked") 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); } @SuppressWarnings("unchecked") public Map getData(Fqn fqn) { InvocationContext ctx = invocationContextContainer.get(); cacheStatusCheck(ctx); GetDataMapCommand command = commandsFactory.buildGetDataMapCommand(fqn); return (Map) invoker.invoke(ctx, command); } public Set getKeys(String fqn) { return getKeys(Fqn.fromString(fqn)); } @SuppressWarnings("unchecked") 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)); } public void startBatch() { if (!configuration.isInvocationBatchingEnabled()) { throw new ConfigurationException("Invocation batching not enabled in current configuration! Please use the element."); } batchContainer.startBatch(); } public void endBatch(boolean successful) { if (!configuration.isInvocationBatchingEnabled()) { throw new ConfigurationException("Invocation batching not enabled in current configuration! Please use the element."); } batchContainer.endBatch(successful); } public boolean isLeaf(String fqn) throws NodeNotExistsException { return isLeaf(Fqn.fromString(fqn)); } public boolean isLeaf(Fqn fqn) throws NodeNotExistsException { Set names = getChildrenNamesInternal(fqn); if (names == null) throw new NodeNotExistsException("Node " + fqn + " does not exist!"); return names.isEmpty(); } public Set getChildrenNames(Fqn fqn) { Set names = getChildrenNamesInternal(fqn); return names == null ? Collections.emptySet() : names; } /** * Will return a null if the node doesnt exist! * @param fqn to check * @return set or null */ @SuppressWarnings("unchecked") private Set getChildrenNamesInternal(Fqn fqn) { InvocationContext ctx = invocationContextContainer.get(); cacheStatusCheck(ctx); GetChildrenNamesCommand command = commandsFactory.buildGetChildrenNamesCommand(fqn); Set retval = (Set) invoker.invoke(ctx, command); // this is needed to work around JBCACHE-1480 if (retval != null && !retval.isEmpty()) { for (Iterator i = retval.iterator(); i.hasNext();) { Object child = getNode(Fqn.fromRelativeElements(fqn, i.next())); if (child == null) i.remove(); } } if (retval != null) { retval = Immutables.immutableSetWrap(retval); // this is already copied in the command } return retval; } @SuppressWarnings("unchecked") public Set getChildrenNames(String fqn) { return (Set) getChildrenNames(Fqn.fromString(fqn)); } public ComponentRegistry getComponentRegistry() { return componentRegistry; } public DataContainer getDataContainer() { return dataContainer; } protected void cacheStatusCheck(InvocationContext ctx) { assertIsConstructed(); if (!ctx.getOptionOverrides().isSkipCacheStatusCheck() && !componentRegistry.invocationsAllowed(true)) // this should always be true since we should never wait for the cache to start at this stage { throw new CacheNotReadyException("Cache not in a valid STARTED state! Cache state is " + componentRegistry.getState()); } } private void invokePut(Fqn fqn, Map data, boolean erase) { InvocationContext ctx = invocationContextContainer.get(); cacheStatusCheck(ctx); PutDataMapCommand command = commandsFactory.buildPutDataMapCommand(null, fqn, data); command.setErase(erase); invoker.invoke(ctx, command); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/invocation/CacheNotReadyException.java0000644000175000017500000000075511236536237031715 0ustar moellermoellerpackage org.jboss.cache.invocation; /** * Thrown when a cache is in an invalid state * * @author Manik Surtani * @since 3.2 */ public class CacheNotReadyException extends IllegalStateException { public CacheNotReadyException() { } public CacheNotReadyException(String s) { super(s); } public CacheNotReadyException(String message, Throwable cause) { super(message, cause); } public CacheNotReadyException(Throwable cause) { super(cause); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/invocation/NodeInvocationDelegate.java0000644000175000017500000003427211364023324031726 0ustar moellermoeller/* * 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.invocation; 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.InternalNode; import org.jboss.cache.InvocationContext; import org.jboss.cache.Node; import org.jboss.cache.NodeNotValidException; import org.jboss.cache.NodeSPI; 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 AT jboss DOT org) * @since 2.1.0 */ public class NodeInvocationDelegate extends AbstractInvocationDelegate implements NodeSPI { private static final Log log = LogFactory.getLog(NodeInvocationDelegate.class); private static final boolean trace = log.isTraceEnabled(); protected volatile InternalNode node; private CacheSPI spi; public NodeInvocationDelegate(InternalNode node) { this.node = node; } public InternalNode 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); } @SuppressWarnings("deprecation") public NodeLock getLock() { return node.getLock(); } public void setFqn(Fqn f) { node.setFqn(f); } public boolean isDeleted() { return node.isRemoved(); } public void markAsDeleted(boolean marker) { node.setRemoved(marker); } public void markAsDeleted(boolean marker, boolean recursive) { node.markAsRemoved(marker, recursive); } public void addChild(Object nodeName, Node nodeToAdd) { node.addChildDirect(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.removeChildren(); } 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.removeChild(fqn); } public boolean removeChildDirect(Object childName) { return node.removeChild(childName); } public V removeDirect(K key) { if (node != null) { return node.remove(key); } else { return null; } } public V putDirect(K key, V value) { return node.put(key, value); } public void putAllDirect(Map data) { node.putAll(data); } public Map getDataDirect() { return node.getData(); } public V getDirect(K key) { return node.get(key); } public void clearDataDirect() { if (node != null) node.clear(); } public boolean containsKeyDirect(K key) { return node.containsKey(key); } public Set getKeysDirect() { return node.getKeys(); } public Set getChildrenNamesDirect() { return node.getChildrenNames(); } public CacheSPI getCache() { return spi; } public NodeSPI getParent() { Fqn f = getFqn(); if (f.isRoot()) return this; return spi.getNode(f.getParent()); } public NodeSPI getParentDirect() { 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 boolean isLeaf() { assertValid(); return getChildrenNames().isEmpty(); } public Map getData() { assertValid(); if (spi == null) return Collections.emptyMap(); return spi.getData(getFqn()); } public Set getKeys() { assertValid(); Set keys = spi.getKeys(getFqn()); if (keys == null) return Collections.emptySet(); else return 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 = spi.getInvocationContext().getOptionOverrides().copy(); Node child = getChild(f); if (child == null) { if (trace) log.trace("Child is null; creating."); Option o2 = o1.copy(); spi.getInvocationContext().setOptionOverrides(o1); spi.put(nf, null); if (trace) log.trace("Created. Now getting again."); spi.getInvocationContext().setOptionOverrides(o2); child = getChild(f); if (trace) log.trace("Got child " + child); } 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.hasChildren(); } 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(); } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; NodeInvocationDelegate that = (NodeInvocationDelegate) o; if (node != null ? !node.equals(that.node) : that.node != null) return false; return true; } public int hashCode() { return (node != null ? node.hashCode() : 0); } // -------------- NO OP methods so that subclasses can work. Specifically for MVCC, todo: rethink once we drop PL/OL support ----------- public boolean isNullNode() { throw new UnsupportedOperationException(); } public void markForUpdate(DataContainer container, boolean writeSkewCheck) { throw new UnsupportedOperationException(); } public void commitUpdate(InvocationContext ctx, DataContainer container) { throw new UnsupportedOperationException(); } public boolean isChanged() { throw new UnsupportedOperationException(); } public boolean isCreated() { throw new UnsupportedOperationException(); } public void setCreated(boolean b) { throw new UnsupportedOperationException(); } public void rollbackUpdate() { throw new UnsupportedOperationException(); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/notifications/0000755000175000017500000000000011675222054025207 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/notifications/Notifier.java0000644000175000017500000001215311111047320027615 0ustar moellermoeller/* * 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.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 nodeInvalidated event. */ void notifyNodeInvalidated(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 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); void notifyCacheBlocked(boolean 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(); } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/notifications/IncorrectCacheListenerException.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/notifications/IncorrectCacheListenerException0000644000175000017500000000305611111047320033361 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/notifications/NotifierImpl.java0000644000175000017500000007345611236536237030476 0ustar moellermoeller/* * 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.notifications; 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.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.buddyreplication.BuddyGroup; import org.jboss.cache.config.Configuration; 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.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.Immutables; import org.jboss.cache.util.concurrent.BoundedExecutors; import org.jboss.cache.util.concurrent.WithinThreadExecutor; import org.jgroups.View; import javax.transaction.Transaction; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; 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.CopyOnWriteArrayList; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; /** * Helper class that handles all notifications to registered listeners. * * @author Manik Surtani (manik AT jboss DOT org) * @author Galder Zamarreno */ @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, NodeInvalidated.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, NodeInvalidatedEvent.class }; final Map, List> listenersMap = new HashMap, List>(32); final List cacheStartedListeners = new CopyOnWriteArrayList(); final List cacheStoppedListeners = new CopyOnWriteArrayList(); final List cacheBlockedListeners = new CopyOnWriteArrayList(); final List cacheUnblockedListeners = new CopyOnWriteArrayList(); final List nodeCreatedListeners = new CopyOnWriteArrayList(); final List nodeRemovedListeners = new CopyOnWriteArrayList(); final List nodeVisitedListeners = new CopyOnWriteArrayList(); final List nodeModifiedListeners = new CopyOnWriteArrayList(); final List nodeMovedListeners = new CopyOnWriteArrayList(); final List nodeActivatedListeners = new CopyOnWriteArrayList(); final List nodePassivatedListeners = new CopyOnWriteArrayList(); final List nodeLoadedListeners = new CopyOnWriteArrayList(); final List nodeInvalidatedListeners = new CopyOnWriteArrayList(); final List nodeEvictedListeners = new CopyOnWriteArrayList(); final List transactionRegisteredListeners = new CopyOnWriteArrayList(); final List transactionCompletedListeners = new CopyOnWriteArrayList(); final List viewChangedListeners = new CopyOnWriteArrayList(); final List buddyGroupChangedListeners = new CopyOnWriteArrayList(); // final Map> listenerInvocations = new ConcurrentHashMap>(); private Cache cache; private boolean useMarshalledValueMaps; private Configuration config; // two separate executor services, one for sync and one for async listeners private ExecutorService syncProcessor; private ExecutorService asyncProcessor; private static final AtomicInteger asyncNotifierThreadNumber = new AtomicInteger(0); public NotifierImpl() { listenersMap.put(CacheStarted.class, cacheStartedListeners); listenersMap.put(CacheStopped.class, cacheStoppedListeners); listenersMap.put(CacheBlocked.class, cacheBlockedListeners); listenersMap.put(CacheUnblocked.class, cacheUnblockedListeners); listenersMap.put(NodeCreated.class, nodeCreatedListeners); listenersMap.put(NodeRemoved.class, nodeRemovedListeners); listenersMap.put(NodeVisited.class, nodeVisitedListeners); listenersMap.put(NodeModified.class, nodeModifiedListeners); listenersMap.put(NodeMoved.class, nodeMovedListeners); listenersMap.put(NodeActivated.class, nodeActivatedListeners); listenersMap.put(NodePassivated.class, nodePassivatedListeners); listenersMap.put(NodeLoaded.class, nodeLoadedListeners); listenersMap.put(NodeEvicted.class, nodeEvictedListeners); listenersMap.put(TransactionRegistered.class, transactionRegisteredListeners); listenersMap.put(TransactionCompleted.class, transactionCompletedListeners); listenersMap.put(ViewChanged.class, viewChangedListeners); listenersMap.put(BuddyGroupChanged.class, buddyGroupChangedListeners); listenersMap.put(NodeInvalidated.class, nodeInvalidatedListeners); } @Inject void injectDependencies(CacheSPI cache, Configuration config) { this.cache = cache; this.config = config; } @Stop void stop() { if (syncProcessor != null) syncProcessor.shutdownNow(); if (asyncProcessor != null) asyncProcessor.shutdownNow(); } @Destroy void destroy() { removeAllCacheListeners(); } @Start void start() { useMarshalledValueMaps = config.isUseLazyDeserialization(); syncProcessor = new WithinThreadExecutor(); // first try and use an injected executor for async listeners if ((asyncProcessor = config.getRuntimeConfig().getAsyncCacheListenerExecutor()) == null) { // create one if needed if (config.getListenerAsyncPoolSize() > 0) { asyncProcessor = BoundedExecutors.newFixedThreadPool(config.getListenerAsyncPoolSize(), new ThreadFactory() { public Thread newThread(Runnable r) { return new Thread(r, "AsyncNotifier-" + asyncNotifierThreadNumber.getAndIncrement()); } }, config.getListenerAsyncQueueSize()); } else { // use the same sync executor asyncProcessor = syncProcessor; } } } /** * 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. */ @SuppressWarnings("unchecked") private void validateAndAddListenerInvocation(Object listener) { boolean sync = 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, sync)); 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."); } /** * Tests if a class is properly annotated as a CacheListener and returns whether callbacks on this class should be invoked * synchronously or asynchronously. * * @param listenerClass class to inspect * @return true if callbacks on this class should use the syncProcessor; false if it should use the asyncProcessor. */ private static boolean testListenerClassValidity(Class listenerClass) { CacheListener cl = listenerClass.getAnnotation(CacheListener.class); if (cl == null) 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!"); return cl.sync(); } 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 = getListenerCollectionForAnnotation(annotation); result.add(li); } public void addCacheListener(Object listener) { validateAndAddListenerInvocation(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 = getListenerCollectionForAnnotation(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(); } public Set getCacheListeners() { Set result = new HashSet(); for (List list : listenersMap.values()) { for (ListenerInvocation li : list) result.add(li.target); } return Collections.unmodifiableSet(result); } public void notifyNodeCreated(Fqn fqn, boolean pre, InvocationContext ctx) { if (!nodeCreatedListeners.isEmpty() && !ctx.getOptionOverrides().isSuppressEventNotification()) { 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() && !ctx.getOptionOverrides().isSuppressEventNotification()) { 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() && !ctx.getOptionOverrides().isSuppressEventNotification()) { 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() && !ctx.getOptionOverrides().isSuppressEventNotification()) { 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() && !ctx.getOptionOverrides().isSuppressEventNotification()) { 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() && !ctx.getOptionOverrides().isSuppressEventNotification()) { 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 notifyNodeInvalidated(final Fqn fqn, final boolean pre, InvocationContext ctx) { if (!nodeInvalidatedListeners.isEmpty() && !ctx.getOptionOverrides().isSuppressEventNotification()) { 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_INVALIDATED); for (ListenerInvocation listener : nodeInvalidatedListeners) listener.invoke(e); restoreInvocationContext(backup); } } public void notifyNodeLoaded(Fqn fqn, boolean pre, Map data, InvocationContext ctx) { if (!nodeLoadedListeners.isEmpty() && !ctx.getOptionOverrides().isSuppressEventNotification()) { 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() && !ctx.getOptionOverrides().isSuppressEventNotification()) { 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() && !ctx.getOptionOverrides().isSuppressEventNotification()) { 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 = 999) public void notifyCacheStoppedPost() { if (!cacheStoppedListeners.isEmpty()) { EventImpl e = new EventImpl(); e.setCache(cache); e.setType(CACHE_STOPPED); e.setPre(false); for (ListenerInvocation listener : cacheStoppedListeners) listener.invoke(e); } } /** * Notifies all registered listeners of a cacheStopped event. */ @Stop(priority = 0) public void notifyCacheStoppedPre() { if (!cacheStoppedListeners.isEmpty()) { EventImpl e = new EventImpl(); e.setCache(cache); e.setType(CACHE_STOPPED); e.setPre(true); 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() && !ctx.getOptionOverrides().isSuppressEventNotification()) { 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() && !ctx.getOptionOverrides().isSuppressEventNotification()) { 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 = Immutables.immutableMapCopy(data); return useMarshalledValueMaps ? new MarshalledValueMap(defensivelyCopiedData) : defensivelyCopiedData; } private void restoreInvocationContext(InvocationContext backup) { InvocationContext currentIC = cache.getInvocationContext(); backup.putLookedUpNodes(currentIC.getLookedUpNodes()); 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); // get a new Invocation Context InvocationContext newContext = cache.getInvocationContext(); newContext.putLookedUpNodes(ctx.getLookedUpNodes()); 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.ImmutableMapCopy}, 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 || Immutables.isImmutable(map) || 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; private final boolean sync; public ListenerInvocation(Object target, Method method, boolean sync) { this.target = target; this.method = method; this.sync = sync; } public void invoke(final Event e) { Runnable r = new Runnable() { public void run() { try { method.invoke(target, e); } catch (InvocationTargetException exception) { Throwable cause = exception.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, exception); } catch (IllegalAccessException exception) { log.warn("Unable to invoke method " + method + " on Object instance " + target + " - removing this target object from list of listeners!", exception); removeCacheListener(target); } } }; if (sync) syncProcessor.execute(r); else asyncProcessor.execute(r); } } private List getListenerCollectionForAnnotation(Class annotation) { List list = listenersMap.get(annotation); if (list == null) throw new CacheException("Unknown listener annotation: " + annotation); return list; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/notifications/event/0000755000175000017500000000000011675222054026330 5ustar moellermoeller././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/notifications/event/BuddyGroupChangedEvent.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/notifications/event/BuddyGroupChangedEvent.ja0000644000175000017500000000275111111047320033173 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/notifications/event/NodeLoadedEvent.java0000644000175000017500000000325011111047320032155 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/notifications/event/EventImpl.java0000644000175000017500000001643111111047320031065 0ustar moellermoeller/* * 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.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, NodeInvalidatedEvent { 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-3.2.8.GA/src/main/java/org/jboss/cache/notifications/event/TransactionalEvent.java0000644000175000017500000000335611111047320032770 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/notifications/event/CacheStoppedEvent.java0000644000175000017500000000251111111047320032520 0ustar moellermoeller/* * 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.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 { } ././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/notifications/event/TransactionRegisteredEvent.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/notifications/event/TransactionRegisteredEven0000644000175000017500000000303111111047320033353 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/notifications/event/NodeRemovedEvent.java0000644000175000017500000000316511111047320032373 0ustar moellermoeller/* * 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.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(); } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/notifications/event/NodePassivatedEvent.java0000644000175000017500000000301211111047320033064 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/notifications/event/NodeActivatedEvent.java0000644000175000017500000000301311111047320032666 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/notifications/event/NodeVisitedEvent.java0000644000175000017500000000251311111047320032375 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/notifications/event/NodeCreatedEvent.java0000644000175000017500000000251311111047320032335 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/notifications/event/Event.java0000644000175000017500000000371311111047320030242 0ustar moellermoeller/* * 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.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, NODE_INVALIDATED } /** * @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(); } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/notifications/event/NodeMovedEvent.java0000644000175000017500000000267511111047320032051 0ustar moellermoeller/* * 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.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(); } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/notifications/event/TransactionCompletedEvent.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/notifications/event/TransactionCompletedEvent0000644000175000017500000000334311111047320033364 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/notifications/event/ViewChangedEvent.java0000644000175000017500000000270211111047320032344 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/notifications/event/NodeEvent.java0000644000175000017500000000266311111047320031053 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/notifications/event/NodeModifiedEvent.java0000644000175000017500000000572611111047320032517 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/notifications/event/CacheBlockedEvent.java0000644000175000017500000000251111111047320032445 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/notifications/event/NodeEvictedEvent.java0000644000175000017500000000251311111047320032351 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/notifications/event/NodeInvalidatedEvent.java0000644000175000017500000000244011111047320033211 0ustar moellermoeller/* * 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.notifications.event; /** * Notifies a listener of an invalidation event * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ public interface NodeInvalidatedEvent extends NodeEvent { } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/notifications/event/CacheUnblockedEvent.java0000644000175000017500000000251511111047320033014 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/notifications/event/CacheStartedEvent.java0000644000175000017500000000251111111047320032510 0ustar moellermoeller/* * 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.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 { } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/notifications/annotation/0000755000175000017500000000000011675222061027357 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/notifications/annotation/CacheStopped.java0000644000175000017500000000346711111047320032562 0ustar moellermoeller/* * 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.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 { } ././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/notifications/annotation/TransactionRegistered.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/notifications/annotation/TransactionRegistere0000644000175000017500000000446711111047320033440 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/notifications/annotation/NodeRemoved.java0000644000175000017500000000366611111047320032430 0ustar moellermoeller/* * 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.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 { } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/notifications/annotation/CacheListener.java0000644000175000017500000003235611111047320032730 0ustar moellermoeller/* * 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.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. *

    * Note: Since 3.0, a new parameter, sync, has been introduced on this annotation. This defaults to true * which provides the above semantics. Alternatively, if you set sync to false, then invocations are made in a * separate thread, which will not cause any blocking on the caller or network thread. The separate thread is taken * from a pool, which can be configured using {@link org.jboss.cache.config.Configuration#setListenerAsyncPoolSize(int)}. *

    * 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 its 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 its membership.
    {@link NodeInvalidated}{@link org.jboss.cache.notifications.event.NodeInvalidatedEvent}A node was invalidated by a remote cache. Only if cache mode is INVALIDATION_SYNC or INVALIDATION_ASYNC.
    *

    *

    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 NodeInvalidated * @see org.jboss.cache.Cache#addCacheListener(Object) * @see org.jboss.cache.Cache#removeCacheListener(Object) * @see org.jboss.cache.Cache#getCacheListeners() * @since 2.0.0 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface CacheListener { /** * Specifies whether callbacks on any class annotated with this annotation happens synchronously (in the caller's thread) * or asynchronously (using a separate thread). Defaults to true. * * @return true if the expectation is that callbacks are called using the caller's thread; false if they are to be made in a separate thread. * @since 3.0 */ boolean sync() default true; } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/notifications/annotation/NodePassivated.java0000644000175000017500000000364611111047320033130 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/notifications/annotation/NodeActivated.java0000644000175000017500000000362011111047320032721 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/notifications/annotation/NodeCreated.java0000644000175000017500000000351211111047320032364 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/notifications/annotation/NodeVisited.java0000644000175000017500000000363511111047320032432 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/notifications/annotation/NodeMoved.java0000644000175000017500000000376411111047320032100 0ustar moellermoeller/* * 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.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 { } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/notifications/annotation/TransactionCompleted.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/notifications/annotation/TransactionCompleted0000644000175000017500000000437611111047320033422 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/notifications/annotation/ViewChanged.java0000644000175000017500000000377311111047320032404 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/notifications/annotation/NodeModified.java0000644000175000017500000000353311111047320032540 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/notifications/annotation/CacheBlocked.java0000644000175000017500000000362511111047320032503 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/notifications/annotation/NodeEvicted.java0000644000175000017500000000360611111047320032404 0ustar moellermoeller/* * 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.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 { } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/notifications/annotation/CacheUnblocked.java0000644000175000017500000000353511111047320033046 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/notifications/annotation/NodeInvalidated.java0000644000175000017500000000357411111047320033251 0ustar moellermoeller/* * 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.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 invalidated. *

    * Methods annotated with this annotation should be public and take in a single parameter, a {@link org.jboss.cache.notifications.event.NodeInvalidatedEvent} * otherwise an {@link org.jboss.cache.notifications.IncorrectCacheListenerException} will be thrown when registering * your cache listener. * * @author Manik Surtani * @see org.jboss.cache.notifications.annotation.CacheListener * @since 3.0 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface NodeInvalidated { }jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/notifications/annotation/CacheStarted.java0000644000175000017500000000346711111047320032552 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/notifications/annotation/NodeLoaded.java0000644000175000017500000000357211111047320032213 0ustar moellermoeller/* * 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.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 { } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/notifications/annotation/BuddyGroupChanged.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/notifications/annotation/BuddyGroupChanged.ja0000644000175000017500000000411611111047320033217 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/DataContainerImpl.java0000644000175000017500000005540111157534263026547 0ustar moellermoeller/* * 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; 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.config.Configuration.NodeLockingScheme; 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.jmx.annotations.MBean; import org.jboss.cache.jmx.annotations.ManagedOperation; import org.jboss.cache.lock.LockManager; import org.jboss.cache.marshall.NodeData; import org.jboss.cache.util.CachePrinter; 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 @MBean(objectName = "DataContainer", description = "Core container for all cached items") public class DataContainerImpl implements DataContainer { private static final Log log = LogFactory.getLog(DataContainerImpl.class); private static boolean trace = log.isTraceEnabled(); /** * Root node. */ private volatile NodeSPI root; private volatile InternalNode rootInternal; /** * 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; private Configuration config; private boolean usingMvcc; volatile boolean started = false; private static final InternalNode[] NULL_ARRAY = {null, null}; @Inject public void injectDependencies(NodeFactory nodeFactory, LockManager lockManager, BuddyFqnTransformer transformer, Configuration configuration) { started = false; setDependencies(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; config = configuration; } public void setDependencies(NodeFactory nodeFactory, LockManager lockManager) { this.nodeFactory = nodeFactory; this.lockManager = lockManager; } @Start(priority = 12) public void start() { createRootNode(); started = true; } public void createRootNode() { usingMvcc = config != null && config.getNodeLockingScheme() == NodeLockingScheme.MVCC; if (trace) log.trace("Starting data container. Using MVCC? " + usingMvcc); // create a new root temporarily. NodeSPI tempRoot = nodeFactory.createRootNode(); // 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 : root.getDelegationTarget().getClass(); Class tempRootType = tempRoot.getDelegationTarget().getClass(); if (!tempRootType.equals(currentRootType)) { if (trace) log.trace("Setting root node to an instance of " + tempRootType); setRoot(tempRoot); } if (usingMvcc && rootInternal == null) setRoot(root); // sets the "internal root" } @Stop(priority = 100) public void stop() { started = false; // empty in-memory state if (root != null) { root.clearDataDirect(); root.removeChildrenDirect(); } else if (rootInternal != null) { rootInternal.clear(); rootInternal.removeChildren(); } } @Deprecated public NodeSPI getRoot() { return root; } /** * Sets the root node reference to the node passed in. * * @param root node */ public void setRoot(Object root) { if (root == null) throw new CacheException("Attempting to set a null node as a root node!"); // Mega-Ugh! if (usingMvcc && root instanceof InternalNode) { if (log.isDebugEnabled()) log.debug("Setting rootInternal to " + root); rootInternal = (InternalNode) root; this.root = null; } else { this.root = (NodeSPI) root; if (usingMvcc) { if (log.isDebugEnabled()) log.debug("Setting rootInternal to " + this.root.getDelegationTarget()); rootInternal = this.root.getDelegationTarget(); this.root = null; } } } public boolean isResident(Fqn fqn) { if (usingMvcc) { InternalNode in = peekInternalNode(fqn, false); return in != null && in.isResident(); } else { NodeSPI nodeSPI = peek(fqn, false, false); return nodeSPI != null && nodeSPI.isResident(); } } public void registerInternalFqn(Fqn fqn) { internalFqns.add(fqn); } public NodeSPI peek(Fqn fqn) { return peek(fqn, false, false); } public NodeSPI peek(Fqn fqn, boolean includeDeletedNodes) { return peek(fqn, includeDeletedNodes, false); } public NodeSPI peek(Fqn fqn, boolean includeDeletedNodes, boolean includeInvalidNodes) { if (trace) { log.trace("peek " + fqn + ", includeDeletedNodes:" + includeDeletedNodes + ", includeInvalidNodes:" + 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 usingMvcc ? peekInternalNode(fqn, false) != null : peek(fqn, false, false) != null; } public boolean hasChildren(Fqn fqn) { if (fqn == null) return false; if (usingMvcc) { InternalNode in = peekInternalNode(fqn, false); return in != null && in.hasChildren(); } else { NodeSPI n = peek(fqn); return n != null && n.hasChildrenDirect(); } } public List buildNodeData(List list, NodeSPI node, boolean mapSafe) { if (usingMvcc) { return buildNodeData(list, node.getDelegationTarget(), node.getData(), mapSafe); } else { return buildNodeDataLegacy(list, node, mapSafe); } } private List buildNodeData(List list, InternalNode node, Map dataInNode, boolean mapSafe) { NodeData data = new NodeData(buddyFqnTransformer.getActualFqn(node.getFqn()), dataInNode, mapSafe); list.add(data); for (InternalNode childNode : node.getChildrenMap().values()) { buildNodeData(list, childNode, childNode.getData(), true); } return list; } @Deprecated private List buildNodeDataLegacy(List list, NodeSPI node, boolean mapSafe) { NodeData data = new NodeData(buddyFqnTransformer.getActualFqn(node.getFqn()), node.getDataDirect(), mapSafe); list.add(data); for (Object childNode : node.getChildrenDirect()) { buildNodeData(list, (NodeSPI) childNode, true); } return list; } public List getNodesForEviction(Fqn fqn, boolean recursive) { List result = new ArrayList(); if (usingMvcc) { InternalNode node = peekInternalNode(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.getChildrenNames()) { if (!node.isResident()) result.add(Fqn.fromRelativeElements(fqn, childName)); } } else if (!node.isResident()) { result.add(fqn); } } return result; } else { 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); } } private void recursiveAddEvictionNodes(InternalNode node, List result) { for (InternalNode child : node.getChildren()) { 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(); } if (started && !usingMvcc) { for (Object n : root.getChildrenDirect()) { ((NodeSPI) n).print(sb, indent); sb.append("\n"); } } } return sb.toString(); } public int getNumberOfLocksHeld() { return started ? numLocks(root) : -1; } private int numLocks(NodeSPI n) { if (!started) return 0; int num = 0; if (n != null) { if (lockManager.isLocked(n)) { num++; if (trace) log.trace(n.getFqn() + " locked"); } for (Object cn : n.getChildrenDirect(true)) { num += numLocks((NodeSPI) cn); } } return num; } @ManagedOperation(description = "Returns the number of nodes in the data container") public int getNumberOfNodes() { if (started) { if (!usingMvcc) return numNodes(root) - 1; return numNodesMvcc(rootInternal) - 1; } else { return -1; } } private int numNodesMvcc(InternalNode node) { int count = 1; //for 'node' if (node != null) { Set children = node.getChildren(); for (InternalNode child : children) { count += numNodesMvcc(child); } } return count; } 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 */ @ManagedOperation(description = "Prints details of the data container") public String printDetails() { StringBuilder sb = new StringBuilder(); if (root == null) { rootInternal.printDetails(sb, 0); } else { root.printDetails(sb, 0); } sb.append("\n"); return sb.toString(); } @ManagedOperation(description = "Prints details of the data container, formatted as an HTML String") public String printDetailsAsHtml() { return CachePrinter.formatHtml(printDetails()); } /** * Returns lock information. * * @return lock info */ public String printLockInfo() { return lockManager.printLockInfo(root); } public int getNumberOfAttributes(Fqn fqn) { return usingMvcc ? numAttributes(peekInternalNode(fqn, false)) : 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; } private int numAttributesMvcc(InternalNode n) { int count = 0; for (Object child : n.getChildren()) { count += numAttributesMvcc((InternalNode) child); } count += n.getData().size(); return count; } private int numAttributes(InternalNode n) { int count = 0; for (Object child : n.getChildren()) { count += numAttributes((NodeSPI) child); } count += n.getData().size(); return count; } @ManagedOperation(description = "Returns the number of attributes in all nodes in the data container") public int getNumberOfAttributes() { return usingMvcc ? numAttributesMvcc(rootInternal) : numAttributes(root); } public boolean removeFromDataStructure(Fqn f, boolean skipMarkerCheck) { return usingMvcc ? removeMvcc(f, skipMarkerCheck) : removeLegacy(f, skipMarkerCheck); } private boolean removeMvcc(Fqn f, boolean skipMarkerCheck) { InternalNode n = peekInternalNode(f, true); if (n == null) { return false; } if (trace) log.trace("Performing a real remove for node " + f + ", marked for removal."); if (skipMarkerCheck || n.isRemoved()) { if (n.getFqn().isRoot()) { // do not actually delete; just remove deletion marker n.setRemoved(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.removeChildren(); 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); InternalNode parent = peekInternalNode(f.getParent(), true); return parent.removeChild(n.getFqn().getLastElement()); } } else { if (log.isDebugEnabled()) log.debug("Node " + f + " NOT marked for removal as expected, not removing!"); return false; } } private boolean removeLegacy(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); NodeSPI parent = peek(f.getParent(), true); return parent.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 (!exists(fqn)) return true; if (hasChildren(fqn)) { if (trace) { log.trace("removing DATA as node has children: evict(" + fqn + ")"); } if (usingMvcc) { removeData(fqn); } else { removeDataLegacy(fqn); } return false; } else { if (trace) log.trace("removing NODE as it is a leaf: evict(" + fqn + ")"); if (usingMvcc) { removeNode(fqn); } else { removeNodeLegacy(fqn); } return true; } } private void removeNodeLegacy(Fqn fqn) { NodeSPI targetNode = peek(fqn, false, true); if (targetNode == null) return; NodeSPI parentNode = targetNode.getParentDirect(); targetNode.setValid(false, false); if (parentNode != null) { parentNode.removeChildDirect(fqn.getLastElement()); parentNode.setChildrenLoaded(false); } } private void removeNode(Fqn fqn) { InternalNode targetNode = peekInternalNode(fqn, true); if (targetNode == null) return; InternalNode parentNode = peekInternalNode(fqn.getParent(), true); targetNode.setValid(false, false); if (parentNode != null) { parentNode.removeChild(fqn.getLastElement()); parentNode.setChildrenLoaded(false); } } private void removeDataLegacy(Fqn fqn) { NodeSPI n = peek(fqn); if (n == null) { log.warn("node " + fqn + " not found"); return; } n.clearDataDirect(); n.setDataLoaded(false); } private void removeData(Fqn fqn) { InternalNode n = peekInternalNode(fqn, false); if (n == null) { log.warn("node " + fqn + " not found"); return; } n.clear(); 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 = (NodeSPI) children.get(childName); if (childNode == null) { childNode = n.addChildDirect(Fqn.fromElements(childName)); result.add(childNode); } n = childNode; } return new Object[]{result, n}; } public InternalNode peekInternalNode(Fqn fqn, boolean includeInvalidNodes) { if (fqn == null || fqn.size() == 0) return rootInternal; InternalNode n = rootInternal; int fqnSize = fqn.size(); for (int i = 0; i < fqnSize; i++) { Object obj = fqn.get(i); n = n.getChild(obj); if (n == null) { return null; } else if (!includeInvalidNodes && !n.isValid()) { return null; } } return n; } public InternalNode[] peekInternalNodeAndDirectParent(Fqn fqn, boolean includeInvalidNodes) { if (fqn == null || fqn.size() == 0) return new InternalNode[]{rootInternal, null}; InternalNode n = rootInternal; InternalNode directParent = null; int fqnSize = fqn.size(); for (int i = 0; i < fqnSize; i++) { directParent = n; Object obj = fqn.get(i); n = directParent.getChild(obj); if (n == null) { return NULL_ARRAY; } else if (!includeInvalidNodes && !n.isValid()) { return NULL_ARRAY; } } return new InternalNode[]{n, directParent}; } public void setBuddyFqnTransformer(BuddyFqnTransformer buddyFqnTransformer) { this.buddyFqnTransformer = buddyFqnTransformer; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/AbstractNodeFactory.java0000644000175000017500000001443111160141267027100 0ustar moellermoeller/* * 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; import org.jboss.cache.commands.CommandsFactory; 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; import org.jboss.cache.invocation.InvocationContextContainer; import org.jboss.cache.invocation.NodeInvocationDelegate; import org.jboss.cache.mvcc.ReadCommittedNode; import org.jboss.cache.optimistic.TransactionWorkspace; import org.jboss.cache.optimistic.WorkspaceNode; import java.util.Map; /** * Generates new nodes based on the {@link CacheSPI} configuration. * * @author Manik Surtani (manik AT jboss DOT org) */ public abstract class AbstractNodeFactory implements NodeFactory { protected CacheSPI cache; protected boolean useVersionedNode; protected Configuration configuration; protected InvocationContextContainer invocationContextContainer; protected InterceptorChain interceptorChain; protected CommandsFactory commandsFactory; protected ComponentRegistry componentRegistry; protected DataContainer dataContainer; @Inject void injectComponentRegistry(ComponentRegistry componentRegistry, DataContainer dataContainer) { this.componentRegistry = componentRegistry; this.dataContainer = dataContainer; } @Inject public void injectDependencies(CacheSPI cache, Configuration configuration, InvocationContextContainer invocationContextContainer, InterceptorChain interceptorChain, CommandsFactory commandsFactory) { this.cache = cache; this.configuration = configuration; this.invocationContextContainer = invocationContextContainer; this.interceptorChain = interceptorChain; this.commandsFactory = commandsFactory; } private NodeSPI initializeNodeInvocationDelegate(UnversionedNode internal) { // always assume that new nodes do not have data loaded internal.setDataLoaded(false); NodeSPI nid = createNodeInvocationDelegate(internal, false); // back reference internal.setDelegate(nid); return nid; } public NodeSPI createNode(Fqn fqn, NodeSPI parent, Map data) { UnversionedNode internal = createInternalNode(fqn.getLastElement(), fqn, parent, data); return initializeNodeInvocationDelegate(internal); } public NodeSPI createNode(Object childName, NodeSPI parent, Map data) { UnversionedNode internal = createInternalNode(childName, Fqn.fromRelativeElements(parent.getFqn(), childName), parent, data); return initializeNodeInvocationDelegate(internal); } public NodeSPI createNode(Fqn fqn, NodeSPI parent) { UnversionedNode internal = createInternalNode(fqn.getLastElement(), fqn, parent, null); return initializeNodeInvocationDelegate(internal); } public InternalNode createInternalNode(Fqn fqn) { throw new UnsupportedOperationException("Unsupported in this implementation (" + getClass().getSimpleName() + ")!"); } public NodeSPI createNode(Object childName, NodeSPI parent) { UnversionedNode internal = createInternalNode(childName, Fqn.fromRelativeElements(parent.getFqn(), childName), parent, null); return initializeNodeInvocationDelegate(internal); } protected UnversionedNode createInternalNode(Object childName, Fqn fqn, NodeSPI parent, Map data) { throw new UnsupportedOperationException("Unsupported in this implementation (" + getClass().getSimpleName() + ")!"); } public WorkspaceNode createWrappedNode(NodeSPI dataNode, TransactionWorkspace workspace) { throw new UnsupportedOperationException("Unsupported in this implementation (" + getClass().getSimpleName() + ")!"); } public ReadCommittedNode createWrappedNode(InternalNode node, InternalNode parent) { throw new UnsupportedOperationException("Unsupported in this implementation (" + getClass().getSimpleName() + ")!"); } public ReadCommittedNode createWrappedNodeForRemoval(Fqn fqn, InternalNode node, InternalNode parent) { throw new UnsupportedOperationException("Unsupported in this implementation (" + getClass().getSimpleName() + ")!"); } public NodeSPI createRootNode() { return createNode(Fqn.ROOT, null); } private NodeSPI createNodeInvocationDelegate(InternalNode internalNode, boolean wrapWithNodeReference) { if (wrapWithNodeReference) throw new UnsupportedOperationException("wrapWithNodeReferences is not supported in this impl!"); NodeInvocationDelegate nid = new NodeInvocationDelegate(internalNode); nid.initialize(configuration, invocationContextContainer, componentRegistry, interceptorChain); nid.injectDependencies(cache); return nid; } public InternalNode createChildNode(Fqn fqn, InternalNode parent, InvocationContext ctx, boolean attachToParent) { throw new UnsupportedOperationException("Unsupported in this implementation (" + getClass().getSimpleName() + ")!"); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/VersionedNode.java0000644000175000017500000001047111111047320025732 0ustar moellermoeller/* * 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; 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 AT jboss DOT org) * @since 2.0.0 * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated public class VersionedNode extends PessimisticUnversionedNode { 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. static { log = LogFactory.getLog(VersionedNode.class); } /** * 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; public VersionedNode(Fqn fqn, NodeSPI parent, Map data, CacheSPI cache) { super(fqn.getLastElement(), fqn, data, cache); if (parent == null && !fqn.isRoot()) throw new NullPointerException("parent"); if (version == null) version = DefaultDataVersion.ZERO; this.parent = parent; } /** * 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); } @Override public VersionedNode copy() { VersionedNode n = new VersionedNode(fqn, getParent(), data, cache); copyInternals(n); n.version = version; return n; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/RegionImpl.java0000644000175000017500000003427711433521426025260 0ustar moellermoeller/* * 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; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.annotations.Compat; import org.jboss.cache.commands.DataCommand; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionAlgorithmConfig; import org.jboss.cache.config.EvictionPolicyConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.eviction.*; import org.jboss.cache.util.Util; import javax.transaction.Transaction; import java.util.HashMap; import java.util.Map; 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 AT jboss DOT org) */ public class RegionImpl implements Region { private static final Log log = LogFactory.getLog(RegionImpl.class); private static final boolean trace = log.isTraceEnabled(); private final RegionManager regionManager; private Fqn fqn; private Status status; private ClassLoader classLoader; private volatile BlockingQueue evictionEventQueue = null; private int capacityWarnThreshold = 0; private EvictionRegionConfig evictionRegionConfig; private EvictionAlgorithm evictionAlgorithm; /** * 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(EvictionRegionConfig config, Fqn fqn, RegionManager regionManager) { this(fqn, regionManager); this.evictionRegionConfig = config; 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 void processEvictionQueues() { if (trace) log.trace("Processing eviction queue for region ["+getFqn()+"]. Queue size is " + evictionEventQueue.size()); evictionAlgorithm.process(evictionEventQueue); } 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 BlockingQueue getEvictionEventQueue() { return evictionEventQueue; } public void markNodeCurrentlyInUse(Fqn fqn, long timeout) { registerEvictionEvent(fqn, EvictionEvent.Type.MARK_IN_USE_EVENT, 0, null, null).setInUseTimeout(timeout); } public void unmarkNodeCurrentlyInUse(Fqn fqn) { registerEvictionEvent(fqn, EvictionEvent.Type.UNMARK_USE_EVENT, 0, null, null); } @Override public String toString() { return "RegionImpl{" + "fqn=" + fqn + "; classloader=" + classLoader + "; status=" + status + "; eviction=" + (evictionAlgorithm != null) + "; evictionQueueSize=" + (evictionAlgorithm == null ? "-1" : evictionEventQueue.size()) + '}'; } 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 resetEvictionQueues() { evictionEventQueue.clear(); evictionAlgorithm.getEvictionQueue().clear(); } public void setEvictionRegionConfig(EvictionRegionConfig evictionRegionConfig) { if (trace) log.trace("setEvictionRegionConfig called with " + evictionRegionConfig); this.evictionRegionConfig = evictionRegionConfig; evictionAlgorithm = createEvictionAlgorithm(evictionRegionConfig.getEvictionAlgorithmConfig(), evictionRegionConfig.getEvictionActionPolicyClassName()); if (evictionEventQueue == null) createQueue(); evictionAlgorithm.initialize(); } public EvictionRegionConfig getEvictionRegionConfig() { return evictionRegionConfig; } public EvictionEvent registerEvictionEvent(Fqn fqn, EvictionEvent.Type eventType) { return registerEvictionEvent(fqn, eventType, 0, null, null); } public EvictionEvent registerEvictionEvent(Fqn fqn, EvictionEvent.Type eventType, int elementDifference, DataCommand command, Transaction tx) { if (evictionAlgorithm.canIgnoreEvent(eventType)) return null; EvictionEvent event = new EvictionEvent(fqn, eventType, elementDifference, command, tx); registerEvictionEvent(event); return event; } private void registerEvictionEvent(EvictionEvent ee) { try { if (evictionEventQueue == null) createQueue();// in case the queue does not exist yet. if (evictionEventQueue.size() > capacityWarnThreshold && log.isWarnEnabled()) { log.warn("putNodeEvent(): eviction node event queue size is at 98% threshold value of capacity: " + evictionRegionConfig.getEventQueueSize() + " Region: " + fqn + " You will need to reduce the wakeUpIntervalSeconds parameter."); } evictionEventQueue.put(ee); } catch (InterruptedException e) { if (log.isDebugEnabled()) log.debug("Interrupted on adding event", e); // reinstate interrupt flag Thread.currentThread().interrupt(); } } private void createQueue() { if (evictionEventQueue == null) { if (evictionRegionConfig == null) { throw new IllegalArgumentException("null eviction configuration"); } int size = evictionRegionConfig.getEventQueueSize(); capacityWarnThreshold = (98 * size) / 100 - 100; if (capacityWarnThreshold <= 0 && log.isWarnEnabled()) { log.warn("Capacity warn threshold used in eviction is smaller than 1. Defined Event queu size is:" + size); } synchronized (this) { if (evictionEventQueue == null) evictionEventQueue = new LinkedBlockingQueue(size); } } } private EvictionAlgorithm createEvictionAlgorithm(EvictionAlgorithmConfig algoConfig, String evictionActionPolicyClass) { if (trace) log.trace("Creating eviction algorithm using config " + algoConfig); if (algoConfig == null) throw new IllegalArgumentException("Eviction algorithm class must not be null!"); if (evictionActionPolicyClass == null) throw new IllegalArgumentException("Eviction action policy class must not be null!"); try { if (trace) log.trace("Instantiating " + evictionActionPolicyClass); EvictionActionPolicy actionPolicy = (EvictionActionPolicy) Util.getInstance(evictionActionPolicyClass); actionPolicy.setCache(regionManager.getCache()); if (trace) log.trace("Instantiating " + algoConfig.getEvictionAlgorithmClassName()); EvictionAlgorithm algorithm = (EvictionAlgorithm) Util.getInstance(algoConfig.getEvictionAlgorithmClassName()); algorithm.setEvictionActionPolicy(actionPolicy); algorithm.assignToRegion(fqn, regionManager.getCache(), algoConfig, regionManager.getConfiguration()); return algorithm; } catch (Exception e) { log.fatal("Unable to instantiate eviction algorithm " + algoConfig.getEvictionAlgorithmClassName(), e); throw new IllegalStateException(e); } } public Region copy(Fqn newRoot) { RegionImpl clone; clone = new RegionImpl(evictionRegionConfig, Fqn.fromRelativeFqn(newRoot, fqn), regionManager); clone.status = status; // we also need to copy all of the eviction event nodes to the clone's queue clone.createQueue(); for (EvictionEvent een : this.evictionEventQueue) { clone.registerEvictionEvent(een.getFqn(), een.getEventType(), een.getElementDifference(), een.getCommand(), een.getTransaction()); } return clone; } @Deprecated @Compat @SuppressWarnings("deprecation") public void setEvictionPolicy(EvictionPolicyConfig evictionPolicyConfig) { try { // need to create an EvictionRegionConfig from this. EvictionRegionConfig erc = new EvictionRegionConfig(getFqn()); String epClassName = evictionPolicyConfig.getEvictionPolicyClass(); EvictionAlgorithmConfig eac = getEvictionAlgorithmConfig(epClassName); erc.setEvictionAlgorithmConfig(eac); if (!erc.isDefaultRegion()) { erc.setDefaults(regionManager.getConfiguration().getEvictionConfig().getDefaultEvictionRegionConfig()); } setEvictionRegionConfig(erc); } catch (Exception e) { throw new CacheException(e); } } /** * This is to provide backward compatibility and is not guaranteed to work for every type of eviction policy out * there, particularly custom ones. * * @param evictionPolicyClass * @return An eviction algorithm config. */ @Deprecated private EvictionAlgorithmConfig getEvictionAlgorithmConfig(String evictionPolicyClass) throws Exception { Map> legacyConfigMap = new HashMap>(); legacyConfigMap.put(FIFOPolicy.class.getName(), FIFOAlgorithmConfig.class); legacyConfigMap.put(LRUPolicy.class.getName(), LRUAlgorithmConfig.class); legacyConfigMap.put(LFUPolicy.class.getName(), LFUAlgorithmConfig.class); legacyConfigMap.put(MRUPolicy.class.getName(), MRUAlgorithmConfig.class); legacyConfigMap.put(ExpirationPolicy.class.getName(), ExpirationAlgorithmConfig.class); legacyConfigMap.put(ElementSizePolicy.class.getName(), ElementSizeAlgorithmConfig.class); legacyConfigMap.put(NullEvictionPolicy.class.getName(), NullEvictionAlgorithmConfig.class); Class c = legacyConfigMap.get(evictionPolicyClass); if (c != null) { // this is one of our "shipped" policies. Easy mapping! return Util.getInstance(c); } else { // tougher. This is a custom policy. // lets default to LRUPolicy first... final EvictionPolicy ep = (EvictionPolicy) Util.getInstance(evictionPolicyClass); ep.getEvictionAlgorithm(); return new LRUAlgorithmConfig() { @Override public String getEvictionAlgorithmClassName() { return ep.getEvictionAlgorithm().getClass().getName(); } }; } } @Deprecated @Compat @SuppressWarnings("deprecation") public EvictionPolicyConfig getEvictionPolicyConfig() { return null; //TODO: Autogenerated. Implement me properly } @Deprecated @Compat @SuppressWarnings("deprecation") public EvictionPolicy getEvictionPolicy() { return null; //TODO: Autogenerated. Implement me properly } @Deprecated @Compat public int nodeEventQueueSize() { BlockingQueue q = getEvictionEventQueue(); return q == null ? 0 : q.size(); } @Compat @Deprecated @SuppressWarnings("deprecation") public EvictedEventNode takeLastEventNode() { try { EvictionEvent ee = getEvictionEventQueue().poll(0, TimeUnit.SECONDS); if (ee instanceof EvictedEventNode) return (EvictedEventNode) ee; else return new EvictedEventNode(ee); } catch (InterruptedException e) { log.debug("trace", e); } return null; } @Deprecated @Compat @SuppressWarnings("deprecation") public void putNodeEvent(EvictedEventNode event) { this.registerEvictionEvent(event); } @Deprecated @Compat public Region clone() throws CloneNotSupportedException { return (Region) super.clone(); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/CacheStatus.java0000644000175000017500000001301511111047320025372 0ustar moellermoeller/* * 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; 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: 7168 $ */ 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: case FAILED: case STOPPING: case DESTROYING: return false; 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: case FAILED: case STOPPING: case DESTROYING: return false; 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-3.2.8.GA/src/main/java/org/jboss/cache/ConsoleListener.java0000755000175000017500000001653111111047320026304 0ustar moellermoeller/* * 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; 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.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-3.2.8.GA/src/main/java/org/jboss/cache/mvcc/0000755000175000017500000000000011675222064023267 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/mvcc/NullMarkerNodeForRemoval.java0000644000175000017500000000347711160141267031016 0ustar moellermoellerpackage org.jboss.cache.mvcc; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.InternalNode; import org.jboss.cache.InvocationContext; import java.util.Collections; import java.util.Map; import java.util.Set; /** * A specific type of null marker node, used for removal of nodes that don't exist * * @author Manik Surtani * @since 3.1.0 */ public class NullMarkerNodeForRemoval extends RepeatableReadNode { private Fqn fqn; public NullMarkerNodeForRemoval(InternalNode parent, Fqn fqn) { super(null, parent); this.fqn = fqn; } @Override public Fqn getFqn() { return fqn; } /** * @return always returns true */ @Override public boolean isNullNode() { return true; } /** * @return always returns true so that any get commands, upon getting this node, will ignore the node as though it were invalid. */ @Override public boolean isValid() { return false; } /** * @return always returns true so that any get commands, upon getting this node, will ignore the node as though it were removed. */ @Override public boolean isDeleted() { return true; } @Override protected void updateNode(Fqn fqn, InvocationContext ctx, DataContainer dataContainer) { // no-op since the only updates that are allowed to happen here are the removal of the node, which only affects the parent. } @Override public Map getDataDirect() { return Collections.emptyMap(); } @Override public Set getChildrenNamesDirect() { return Collections.emptySet(); } @Override public Set getChildrenDirect() { return Collections.emptySet(); } @Override public void setValid(boolean valid, boolean recursive) { // no-op } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/mvcc/NullMarkerNode.java0000644000175000017500000000334411160141267027012 0ustar moellermoeller/* * 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.mvcc; import org.jboss.cache.DataContainer; /** * A marker node to represent a null node for repeatable read, so that a read that returns a null can continue to return * null. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ public class NullMarkerNode extends NullMarkerNodeForRemoval { private static final NullMarkerNode INSTANCE = new NullMarkerNode(); private NullMarkerNode() { super(null, null); } public static NullMarkerNode getInstance() { return INSTANCE; } /** * A no-op. */ @Override public void markForUpdate(DataContainer d, boolean b) { // no op } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/mvcc/NodeReference.java0000644000175000017500000002300411140322163026621 0ustar moellermoeller/* * 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.mvcc; import net.jcip.annotations.ThreadSafe; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.InternalNode; 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.Map; import java.util.Set; import java.util.concurrent.ConcurrentMap; /** * A node reference that delegates all calls to a different {@link org.jboss.cache.InternalNode}. Simple indirection * is all this class does, allowing other processes to change the delegate. *

    * The delegate is a volatile field so the class is thread safe. *

    * This is used to wrap all {@link org.jboss.cache.invocation.NodeInvocationDelegate}s in the {@link org.jboss.cache.DataContainer} * when using {@link org.jboss.cache.config.Configuration.NodeLockingScheme#MVCC} and {@link org.jboss.cache.lock.IsolationLevel#READ_COMMITTED}. *

    * * @author Manik Surtani (manik AT jboss DOT org) * @see org.jboss.cache.mvcc.ReadCommittedNode * @since 3.0 */ @ThreadSafe public class NodeReference implements InternalNode { transient volatile InternalNode delegate; public NodeReference(InternalNode delegate) { this.delegate = delegate; } /** * @return the InternalNode being delegated to. */ public final InternalNode getDelegate() { return delegate; } /** * Sets the internal node to delegate to. * * @param delegate node to delegate to. */ public final void setDelegate(InternalNode delegate) { this.delegate = delegate; } public final NodeSPI getParent() { return delegate.getParent(); } public final CacheSPI getCache() { return delegate.getCache(); } public final boolean isChildrenLoaded() { return delegate.isChildrenLoaded(); } public final void setChildrenLoaded(boolean flag) { delegate.setChildrenLoaded(flag); } public final V get(K key) { return delegate.get(key); } public final Map getData() { return delegate.getData(); } public final V put(K key, V value) { return delegate.put(key, value); } public final NodeSPI getOrCreateChild(Object child_name, GlobalTransaction gtx) { return delegate.getOrCreateChild(child_name, gtx); } public final InternalNode getChild(Fqn f) { return delegate.getChild(f); } public final InternalNode getChild(Object childName) { return delegate.getChild(childName); } public final Set> getChildren() { return delegate.getChildren(); } public final Set> getChildren(boolean includeMarkedForRemoval) { return delegate.getChildren(includeMarkedForRemoval); } public final ConcurrentMap> getChildrenMap() { return delegate.getChildrenMap(); } public final void setChildrenMap(ConcurrentMap> children) { delegate.setChildrenMap(children); } public final void addChild(Object nodeName, InternalNode nodeToAdd) { delegate.addChild(nodeName, nodeToAdd); } public final void addChild(InternalNode child) { delegate.addChild(child); } public final void addChild(InternalNode child, boolean safe) { delegate.addChild(child, safe); } public final V remove(K key) { return delegate.remove(key); } public final NodeSPI addChildDirect(Fqn f) { return delegate.addChildDirect(f); } public final NodeSPI addChildDirect(Fqn f, boolean notify) { return delegate.addChildDirect(f, notify); } public final NodeSPI addChildDirect(Object o, boolean notify) { return delegate.addChildDirect(o, notify); } public final void clear() { delegate.clear(); } public final NodeSPI getChildDirect(Fqn fqn) { return delegate.getChildDirect(fqn); } public final Set getChildrenNames() { return delegate.getChildrenNames(); } public final Set getKeys() { return delegate.getKeys(); } public final boolean containsKey(K key) { return delegate.containsKey(key); } public final void setInternalState(Map state) { delegate.setInternalState(state); } public final boolean removeChild(Object childName) { return delegate.removeChild(childName); } public final boolean removeChild(Fqn f) { return delegate.removeChild(f); } public final Map> getChildrenMapDirect() { return delegate.getChildrenMapDirect(); } public final void setChildrenMapDirect(Map> children) { delegate.setChildrenMapDirect(children); } public final void putAll(Map data) { delegate.putAll(data); } public final void removeChildren() { delegate.removeChildren(); } public final void setVersion(DataVersion version) { delegate.setVersion(version); } public final DataVersion getVersion() { return delegate.getVersion(); } public final Fqn getFqn() { return delegate.getFqn(); } public final void setFqn(Fqn fqn) { delegate.setFqn(fqn); } public final NodeSPI getChildDirect(Object childName) { return delegate.getChildDirect(childName); } public final Set> getChildrenDirect() { return delegate.getChildrenDirect(); } public final boolean hasChildren() { return delegate.hasChildren(); } public final Set> getChildrenDirect(boolean includeMarkedForRemoval) { return delegate.getChildrenDirect(includeMarkedForRemoval); } public final boolean isDataLoaded() { return delegate.isDataLoaded(); } public final void setDataLoaded(boolean dataLoaded) { delegate.setDataLoaded(dataLoaded); } public final boolean isValid() { return delegate.isValid(); } public final void setValid(boolean valid, boolean recursive) { delegate.setValid(valid, recursive); } public final boolean isLockForChildInsertRemove() { return delegate.isLockForChildInsertRemove(); } public final void setLockForChildInsertRemove(boolean lockForChildInsertRemove) { delegate.setLockForChildInsertRemove(lockForChildInsertRemove); } public final Map getInternalState(boolean onlyInternalState) { return delegate.getInternalState(onlyInternalState); } public final void releaseObjectReferences(boolean recursive) { delegate.releaseObjectReferences(recursive); } public final boolean isRemoved() { return delegate.isRemoved(); } public final void setRemoved(boolean marker) { delegate.setRemoved(marker); } public final void markAsRemoved(boolean marker, boolean recursive) { delegate.markAsRemoved(marker, recursive); } public final void setResident(boolean resident) { delegate.setResident(resident); } public final boolean isResident() { return delegate.isResident(); } public final InternalNode copy() { InternalNode cloneDelegate = delegate.copy(); return new NodeReference(cloneDelegate); } public final NodeLock getLock() { return delegate.getLock(); } public final void addChildDirect(Object nodeName, Node nodeToAdd) { delegate.addChildDirect(nodeName, nodeToAdd); } public final void addChildDirect(NodeSPI child) { delegate.addChildDirect(child); } public final void printDetails(StringBuilder sb, int indent) { delegate.printDetails(sb, indent); } @Override public final String toString() { return "NodeReference{" + "delegate=" + delegate + '}'; } @Override public final boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; NodeReference that = (NodeReference) o; if (delegate != null ? !delegate.equals(that.delegate) : that.delegate != null) return false; return true; } @Override public final int hashCode() { return (delegate != null ? delegate.hashCode() : 0); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/mvcc/MVCCNodeFactory.java0000644000175000017500000001415711160141267027022 0ustar moellermoeller/* * 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.mvcc; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.AbstractNodeFactory; import org.jboss.cache.Fqn; import org.jboss.cache.InternalNode; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.UnversionedNode; import org.jboss.cache.factories.annotations.Start; import org.jboss.cache.lock.IsolationLevel; import java.util.Map; /** * Creates nodes specific to MVCC logic. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ public class MVCCNodeFactory extends AbstractNodeFactory { private boolean useRepeatableRead; private static final Log log = LogFactory.getLog(MVCCNodeFactory.class); private static final boolean trace = log.isTraceEnabled(); private boolean lockChildForInsertRemove; /** * Initialises the node factory with the configuration from the cache. */ @Start public void init() { useRepeatableRead = configuration.getIsolationLevel() == IsolationLevel.REPEATABLE_READ; lockChildForInsertRemove = configuration.isLockParentForChildInsertRemove(); } /** * Creates an MVCC wrapped node - either a {@link org.jboss.cache.mvcc.ReadCommittedNode} or its subclass, a * {@link org.jboss.cache.mvcc.RepeatableReadNode} based on cache configuration. If a null is passed in as the InternalNode, * this method will return a special {@link org.jboss.cache.mvcc.NullMarkerNode} instance if using repeatable read, * or a null if read committed. * * @param node internal node to wrap. * @return a ReadCommittedNode */ @Override public ReadCommittedNode createWrappedNode(InternalNode node, InternalNode parent) { return createWrappedNode(null, node, parent, false); } @Override public ReadCommittedNode createWrappedNodeForRemoval(Fqn fqn, InternalNode node, InternalNode parent) { return createWrappedNode(fqn, node, parent, true); } @SuppressWarnings("unchecked") private ReadCommittedNode createWrappedNode(Fqn fqn, InternalNode node, InternalNode parent, boolean forRemoval) { ReadCommittedNode rcn; if (node == null) { if (useRepeatableRead) { if (forRemoval) { // create but do not return this just yet as it needs to be initialized rcn = new NullMarkerNodeForRemoval(parent, fqn); } else { return NullMarkerNode.getInstance(); } } else { // if we are using read-committed, just return a null return null; } } else { rcn = useRepeatableRead ? new RepeatableReadNode(node, parent) : new ReadCommittedNode(node, parent); } rcn.initialize(configuration, invocationContextContainer, componentRegistry, interceptorChain); rcn.injectDependencies(cache); return rcn; } @Override public NodeSPI createNode(Fqn fqn, NodeSPI parent, Map data) { throw new UnsupportedOperationException(); } @Override public NodeSPI createNode(Fqn fqn, NodeSPI parent) { throw new UnsupportedOperationException(); } @Override @SuppressWarnings("unchecked") public NodeSPI createRootNode() { return createWrappedNode(createInternalNode(Fqn.ROOT), null); } @Override public InternalNode createInternalNode(Fqn fqn) { UnversionedNode un = new UnversionedNode(fqn, cache, lockChildForInsertRemove); // always assume that new nodes don't have their data loaded, unless root. if (!fqn.isRoot()) un.setDataLoaded(false); return useRepeatableRead ? un : new NodeReference(un); } @Override public NodeSPI createNode(Object childName, NodeSPI parent, Map data) { return createNode(Fqn.fromRelativeElements(parent.getFqn(), childName), parent, data); } @Override public NodeSPI createNode(Object childName, NodeSPI parent) { return createNode(Fqn.fromRelativeElements(parent.getFqn(), childName), parent); } @Override @SuppressWarnings("unchecked") public InternalNode createChildNode(Fqn fqn, InternalNode parent, InvocationContext ctx, boolean attachToParent) { InternalNode child; if (fqn == null) throw new IllegalArgumentException("null child fqn"); child = dataContainer.peekInternalNode(fqn, false); if (child == null) { cache.getNotifier().notifyNodeCreated(fqn, true, ctx); InternalNode newChild = createInternalNode(fqn); if (attachToParent) parent.addChild(newChild); // addChild actually succeeded! child = newChild; if (trace) log.trace("Created new child with fqn [" + fqn + "]"); // notify if we actually created a new child cache.getNotifier().notifyNodeCreated(fqn, false, ctx); } return child; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/mvcc/RepeatableReadNode.java0000644000175000017500000000672611160141267027605 0ustar moellermoeller/* * 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.mvcc; 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.InternalNode; import org.jboss.cache.InvocationContext; import static org.jboss.cache.mvcc.ReadCommittedNode.Flags.CHANGED; import static org.jboss.cache.mvcc.ReadCommittedNode.Flags.DELETED; import org.jboss.cache.optimistic.DataVersioningException; /** * A node delegate that encapsulates repeatable read semantics when writes are initiated, committed or rolled back. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ public class RepeatableReadNode extends ReadCommittedNode { private static final Log log = LogFactory.getLog(RepeatableReadNode.class); public RepeatableReadNode(InternalNode node, InternalNode parent) { super(node, parent); } @Override public void markForUpdate(DataContainer container, boolean writeSkewCheck) { if (isFlagSet(CHANGED)) return; // already copied Fqn fqn = getFqn(); // mark node as changed. setFlag(CHANGED); if (writeSkewCheck) { // check for write skew. InternalNode underlyingNode = container.peekInternalNode(fqn, true); if (underlyingNode != null && underlyingNode != node) { String errormsg = new StringBuilder().append("Detected write skew on Fqn [").append(fqn).append("]. Another process has changed the node since we last read it!").toString(); if (log.isWarnEnabled()) log.warn(errormsg + ". Unable to copy node for update."); throw new DataVersioningException(errormsg); } } // make a backup copy backup = node; node = copyNode(backup); } private InternalNode copyNode(InternalNode nodeToCopy) { return nodeToCopy == null ? null : nodeToCopy.copy(); } @Override @SuppressWarnings("unchecked") protected void updateNode(Fqn fqn, InvocationContext ctx, DataContainer dataContainer) { if (fqn.isRoot()) { dataContainer.setRoot(node); } else if (!isFlagSet(DELETED)) { InternalNode parent = lookupParent(fqn, ctx, dataContainer); parent.addChild(node, true); // we know this is safe since we calculated the parent from the child. No need to have the parent re-do checks when adding the child again. } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/mvcc/ReadCommittedNode.java0000644000175000017500000002114411160221630027446 0ustar moellermoeller/* * 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.mvcc; 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.InternalNode; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeNotExistsException; import org.jboss.cache.NodeSPI; import org.jboss.cache.invocation.NodeInvocationDelegate; import static org.jboss.cache.mvcc.ReadCommittedNode.Flags.*; /** * A node delegate that encapsulates read committed semantics when writes are initiated, committed or rolled back. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ public class ReadCommittedNode extends NodeInvocationDelegate { private static final Log log = LogFactory.getLog(ReadCommittedNode.class); private static final boolean trace = log.isTraceEnabled(); protected volatile InternalNode backup; protected byte flags = 0; protected InternalNode parent; protected static enum Flags { CHANGED(1), CREATED(1<<1), DELETED(1<<2), ORIG_DATA_LOADED(1<<3), ORIG_CHILDREN_LOADED(1<<4); final byte mask; Flags(int mask) { this.mask = (byte) mask; } } @SuppressWarnings("unchecked") public ReadCommittedNode(InternalNode node, InternalNode parent) { super(node); this.parent = parent; } public InternalNode getInternalParent() { return parent; } /** * Tests whether a flag is set. * * @param flag flag to test * @return true if set, false otherwise. */ protected final boolean isFlagSet(Flags flag) { return (flags & flag.mask) != 0; } /** * Unility method that sets the value of the given flag to true. * * @param flag flag to set */ protected final void setFlag(Flags flag) { flags |= flag.mask; } /** * Utility method that sets the value of the flag to false. * * @param flag flag to unset */ protected final void unsetFlag(Flags flag) { flags &= ~flag.mask; } @Override public boolean isNullNode() { return false; } @Override public void markForUpdate(DataContainer container, boolean writeSkewCheck) { if (isFlagSet(CHANGED)) return; // already copied setFlag(CHANGED); // mark as changed if (!isFlagSet(CREATED)) // if newly created, then nothing to copy. { backup = node; // don't copy the NodeReference but the InternalNode that the NodeReference delegates to. InternalNode backupDelegationTarget = ((NodeReference) backup).getDelegate(); node = backupDelegationTarget.copy(); } } @Override @SuppressWarnings("unchecked") public void commitUpdate(InvocationContext ctx, DataContainer container) { // only do stuff if there are changes. if (isFlagSet(CHANGED)) { Fqn fqn = getFqn(); if (trace) { log.trace("Updating node [" + fqn + "]. deleted=" + isDeleted() + " valid=" + isValid() + " changed=" + isChanged() + " created=" + isFlagSet(CREATED)); } // check if node has been deleted. if (isFlagSet(DELETED)) { if (!fqn.isRoot()) { InternalNode parent = lookupParent(fqn, ctx, container); parent.removeChild(fqn.getLastElement()); setValid(false, false); updateNode(fqn, ctx, container); } else { // should never get here. Other layers should prevent a delete on root. log.warn("Attempting to remove the root node. Not doing anything!"); } } else if (isFlagSet(CREATED)) { // add newly created nodes to parents. InternalNode parent = lookupParent(fqn, ctx, container); parent.addChild(node, true); // we know this is safe since we calculated the parent from the child. No need to have the parent re-do checks when adding the child again. } else { // Only content has been updated, just update refs. updateNode(fqn, ctx, container); } // reset internal flags and refs to backups, etc. reset(); } } private void reset() { backup = null; flags = 0; } /** * Performs a lookup for the parent node of the Fqn passed in. The context is checked first, and failing that, the * data container is consulted. * * @param fqn Fqn whos parent to locate * @param ctx invocation context * @param container data container * @return Parent node, never null. * @throws NodeNotExistsException if the parent node cannot be found in any scope or data container. */ protected final InternalNode lookupParent(Fqn fqn, InvocationContext ctx, DataContainer container) throws NodeNotExistsException { if (parent != null) return parent; InternalNode retval; Fqn parentFqn = fqn.getParent(); NodeSPI parent = ctx.lookUpNode(parentFqn); // first check if the parent is cached in the context. if (parent != null) { retval = parent.getDelegationTarget(); } else { // if not, get it from the data container. No need to wrap here, we're just going to update the parent's child map. retval = container.peekInternalNode(parentFqn, false); } if (retval == null) { throw new NodeNotExistsException("Node " + parentFqn + " cannot be found in any context or data container!"); } return retval; } /** * Updates state changes on the current node in the underlying data structure. * * @param ctx invocation context * @param dataContainer data container */ @SuppressWarnings("unchecked") protected void updateNode(Fqn fqn, InvocationContext ctx, DataContainer dataContainer) { // swap refs if (!isFlagSet(CREATED)) ((NodeReference) backup).setDelegate(node); node = backup; } @Override public void rollbackUpdate() { node = backup; if (node != null) { super.setChildrenLoaded(isFlagSet(ORIG_CHILDREN_LOADED)); super.setDataLoaded(isFlagSet(ORIG_DATA_LOADED)); } reset(); } @Override public boolean isChanged() { return isFlagSet(CHANGED); } @Override public boolean isCreated() { return isFlagSet(CREATED); } @Override public void setCreated(boolean created) { if (created) { setFlag(CREATED); } else { unsetFlag(CREATED); } } // do not delegate deletion flags to the InternalNode since this will cause problems with concurrent readers. Maintain // deletion information here and update on commit. @Override public boolean isDeleted() { return isFlagSet(DELETED); } @Override public void markAsDeleted(boolean deleted) { if (deleted) { setFlag(DELETED); } else { unsetFlag(DELETED); } } @Override public void markAsDeleted(boolean deleted, boolean recursive) { throw new UnsupportedOperationException("Recursive deletion not allowed!"); } @Override public void setChildrenLoaded(boolean loaded) { if (node.isChildrenLoaded()) setFlag(ORIG_CHILDREN_LOADED); super.setChildrenLoaded(loaded); } @Override public void setDataLoaded(boolean loaded) { if (node.isDataLoaded()) setFlag(ORIG_DATA_LOADED); super.setDataLoaded(loaded); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/mvcc/MVCCNodeHelper.java0000644000175000017500000005416111354162635026640 0ustar moellermoeller/* * 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.mvcc; import org.apache.commons.logging.Log; 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.InternalNode; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeFactory; import org.jboss.cache.NodeNotExistsException; import org.jboss.cache.NodeSPI; 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.lock.IsolationLevel; import org.jboss.cache.lock.LockManager; import static org.jboss.cache.lock.LockType.WRITE; import org.jboss.cache.lock.TimeoutException; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; /** * Utility functions to manipulate wrapping {@link org.jboss.cache.InternalNode}s as {@link * org.jboss.cache.mvcc.ReadCommittedNode} or {@link org.jboss.cache.mvcc.RepeatableReadNode}s. Would also entail * locking, if necessary. * * @author Manik Surtani (manik AT jboss DOT org) * @author Galder Zamarreño * @since 3.0 */ @NonVolatile public class MVCCNodeHelper { DataContainer dataContainer; NodeFactory nodeFactory; private static final Log log = LogFactory.getLog(MVCCNodeHelper.class); private static final boolean trace = log.isTraceEnabled(); private long defaultLockAcquisitionTimeout; private LockManager lockManager; private Configuration configuration; private boolean writeSkewCheck; private boolean lockParentForChildInsertRemove; @Inject public void injectDependencies(DataContainer dataContainer, NodeFactory nodeFactory, LockManager lockManager, Configuration configuration) { this.nodeFactory = nodeFactory; this.dataContainer = dataContainer; this.configuration = configuration; this.lockManager = lockManager; } @Start public void start() { defaultLockAcquisitionTimeout = configuration.getLockAcquisitionTimeout(); writeSkewCheck = configuration.isWriteSkewCheck(); lockParentForChildInsertRemove = configuration.isLockParentForChildInsertRemove(); } /** * Attempts to provide the context with a set of wrapped nodes based on the Collection of fqns passed in. If the * nodes already exist in the context then the node is not wrapped again. *

    * {@link InternalNode}s are wrapped using {@link org.jboss.cache.NodeFactory#createWrappedNode(org.jboss.cache.InternalNode, org.jboss.cache.InternalNode)} and as such, null internal nodes are treated according to isolation level used. * See {@link org.jboss.cache.NodeFactory#createWrappedNode(org.jboss.cache.InternalNode, org.jboss.cache.InternalNode)} for details on this behaviour. *

    * Note that if the context has the {@link org.jboss.cache.config.Option#isForceWriteLock()} option set, then write * locks are acquired and the node is copied. *

    * * @param ctx current invocation context * @param fqns collection of Fqns. Should not be null. * @throws InterruptedException if write locks are forced and the lock manager is interrupted. */ public void wrapNodesForReading(InvocationContext ctx, Collection fqns) throws InterruptedException { boolean forceWriteLock = ctx.getOptionOverrides().isForceWriteLock(); // does the node exist in the context? for (Fqn f : fqns) wrapNodeForReading(ctx, f, forceWriteLock, true); } /** * Similar to {@link #wrapNodesForReading(org.jboss.cache.InvocationContext, java.util.Collection)} except that this * version takes a single Fqn parameter to wrap a single node. * * @param ctx current invocation context * @param fqn fqn to fetch and wrap * @param putInContext * @return read committed node, or null if one is not found. * @throws InterruptedException if write locks are forced and the lock manager is interrupted. */ public NodeSPI wrapNodeForReading(InvocationContext ctx, Fqn fqn, boolean putInContext) throws InterruptedException { return wrapNodeForReading(ctx, fqn, ctx.getOptionOverrides().isForceWriteLock(), putInContext); } @SuppressWarnings("unchecked") private NodeSPI wrapNodeForReading(InvocationContext ctx, Fqn f, boolean writeLockForced, boolean putInContext) throws InterruptedException { NodeSPI n; if (writeLockForced) { if (trace) log.trace("Forcing lock on reading node " + f); return wrapNodeForWriting(ctx, f, true, false, false, false, false); } else if ((n = ctx.lookUpNode(f)) == null) { if (trace) log.trace("Node " + f + " is not in context, fetching from container."); // simple implementation. Peek the node, wrap it, put wrapped node in the context. InternalNode[] nodes = dataContainer.peekInternalNodeAndDirectParent(f, false); ReadCommittedNode wrapped = nodeFactory.createWrappedNode(nodes[0], nodes[1]); // even though parents aren't needed for reading, we hold on to this ref in case the node is later written to. if (putInContext && wrapped != null) ctx.putLookedUpNode(f, wrapped); return wrapped; } else { if (trace) log.trace("Node " + f + " is already in context."); return n; } } /** * Attempts to lock a node if the lock isn't already held in the current scope, and records the lock in the context. * * @param ctx context * @param fqn Fqn to lock * @return true if a lock was needed and acquired, false if it didn't need to acquire the lock (i.e., lock was * already held) * @throws InterruptedException if interrupted * @throws TimeoutException if we are unable to acquire the lock after a specified timeout. */ private boolean acquireLock(InvocationContext ctx, Fqn fqn) throws InterruptedException, TimeoutException { // don't EVER use lockManager.isLocked() since with lock striping it may be the case that we hold the relevant // lock which may be shared with another Fqn that we have a lock for already. // nothing wrong, just means that we fail to record the lock. And that is a problem. // Better to check our records and lock again if necessary. if (!ctx.hasLock(fqn)) { if (!lockManager.lockAndRecord(fqn, WRITE, ctx)) { Object owner = lockManager.getWriteOwner(fqn); throw new TimeoutException("Unable to acquire lock on Fqn [" + fqn + "] after [" + ctx.getLockAcquisitionTimeout(defaultLockAcquisitionTimeout) + "] milliseconds for requestor [" + lockManager.getLockOwner(ctx) + "]! Lock held by [" + owner + "]"); } return true; } return false; } /** * First checks in contexts for the existence of the node. If it does exist, it will return it, acquiring a lock if * necessary. Otherwise, it will peek in the dataContainer, wrap the node, lock if necessary, and add it to the * context. If it doesn't even exist in the dataContainer and createIfAbsent is true, it will create a new node and * add it to the data structure. It will lock the node, and potentially the parent as well, if necessary. If the * parent is locked, it too will be added to the context if it wasn't there already. * * @param context invocation context * @param fqn to retrieve * @param lockForWriting if true, a lock will be acquired. * @param createIfAbsent if true, will be created if absent. * @param includeInvalidNodes if true, invalid nodes are included. * @param forRemoval if true, the parent may also be locked if locking parents for removal is necessary. * @param force if true, will force the write lock even if the node is null. * @return a wrapped node, or null. * @throws InterruptedException if interrupted */ @SuppressWarnings("unchecked") public ReadCommittedNode wrapNodeForWriting(InvocationContext context, Fqn fqn, boolean lockForWriting, boolean createIfAbsent, boolean includeInvalidNodes, boolean forRemoval, boolean force) throws InterruptedException { return wrapNodeForWriting(context, fqn, lockForWriting, createIfAbsent, includeInvalidNodes, forRemoval, force, false); } public ReadCommittedNode wrapNodeForWriting(InvocationContext context, Fqn fqn, boolean lockForWriting, boolean createIfAbsent, boolean includeInvalidNodes, boolean forRemoval, boolean force, boolean childIsNull) throws InterruptedException { Fqn parentFqn = null; ReadCommittedNode n = (ReadCommittedNode) context.lookUpNode(fqn); if (createIfAbsent && n != null && n.isNullNode()) n = null; if (n != null) // exists in context! Just acquire lock if needed, and wrap. { // acquire lock if needed if (lockForWriting && acquireLock(context, fqn)) { // create a copy of the underlying node n.markForUpdate(dataContainer, writeSkewCheck); } if (n.isDeleted() && createIfAbsent) { if (trace) log.trace("Node is deleted in current scope. Need to un-delete."); n.markAsDeleted(false); n.setValid(true, false); n.clearData(); // a delete and re-add should flush any old state on the node! // has the parent been deleted too? :-( wrapNodeForWriting(context, fqn.getParent(), true, true, includeInvalidNodes, false, force); } } else { // else, fetch from dataContainer. InternalNode[] nodes = dataContainer.peekInternalNodeAndDirectParent(fqn, includeInvalidNodes); InternalNode in = nodes[0]; if (in != null) { // exists in cache! Just acquire lock if needed, and wrap. // do we need a lock? boolean needToCopy = false; if (lockForWriting && acquireLock(context, fqn)) { needToCopy = true; // re-peek in case we waited for a lock and some other thread modified the data while we're waiting nodes = dataContainer.peekInternalNodeAndDirectParent(fqn, includeInvalidNodes); in = nodes[0]; } if (in != null) { n = nodeFactory.createWrappedNode(in, nodes[1]); context.putLookedUpNode(fqn, n); if (needToCopy) n.markForUpdate(dataContainer, writeSkewCheck); } else if (createIfAbsent) { n = createAbsentNode(parentFqn, fqn, context); } } else if (createIfAbsent) // else, do we need to create one? { n = createAbsentNode(parentFqn, fqn, context); } } // see if we need to force the lock on nonexistent nodes. if (n == null && force) { parentFqn = fqn.getParent(); if (isParentLockNeeded(parentFqn, context) && !childIsNull) wrapNodeForWriting(context, parentFqn, true, false, includeInvalidNodes, false, force, true); acquireLock(context, fqn); } // now test if we need to lock the parent as well. if ((n != null || force) && forRemoval && (parentFqn == null ? parentFqn = fqn.getParent() : parentFqn) != null && isParentLockNeeded(parentFqn, context) && !childIsNull) wrapNodeForWriting(context, parentFqn, true, false, includeInvalidNodes, false, force, n == null); return n; } private ReadCommittedNode createAbsentNode(Fqn parentFqn, Fqn fqn, InvocationContext context) throws InterruptedException { parentFqn = fqn.getParent(); NodeSPI parent = wrapNodeForWriting(context, parentFqn, false, true, false, false, false); // do we need to lock the parent to create children? boolean parentLockNeeded = isParentLockNeeded(parent.getDelegationTarget()); // get a lock on the parent. if (parentLockNeeded && acquireLock(context, parentFqn)) { ReadCommittedNode parentRCN = (ReadCommittedNode) context.lookUpNode(parentFqn); parentRCN.markForUpdate(dataContainer, writeSkewCheck); } // now to lock and create the node. Lock first to prevent concurrent creation! acquireLock(context, fqn); InternalNode in = nodeFactory.createChildNode(fqn, null, context, false); ReadCommittedNode n = nodeFactory.createWrappedNode(in, parent.getDelegationTarget()); n.setCreated(true); n.setDataLoaded(true); // created here so we are loading it here context.putLookedUpNode(fqn, n); n.markForUpdate(dataContainer, writeSkewCheck); return n; } /** * The same as {@link #wrapNodeForWriting(org.jboss.cache.InvocationContext, org.jboss.cache.Fqn, boolean, boolean, * boolean, boolean, boolean)} except that it takes in an {@link org.jboss.cache.InternalNode} instead of a {@link * Fqn}. Saves on a lookup. *

    * Also assumes that the node exists, and hence will not be created. *

    * * @param context invocation context * @param node node to wrap * @return a wrapped node, or null. * @throws InterruptedException if interrupted */ @SuppressWarnings("unchecked") public NodeSPI wrapNodeForWriting(InvocationContext context, InternalNode node, InternalNode parent) throws InterruptedException { Fqn fqn = node.getFqn(); NodeSPI n = context.lookUpNode(fqn); if (n != null) // exists in context! Just acquire lock if needed, and wrap. { // acquire lock if needed if (acquireLock(context, fqn)) { // create a copy of the underlying node n.markForUpdate(dataContainer, writeSkewCheck); } if (trace) log.trace("Retrieving wrapped node " + fqn); } else { // exists in cache! Just acquire lock if needed, and wrap. // do we need a lock? boolean needToCopy = false; if (acquireLock(context, fqn)) { needToCopy = true; } n = nodeFactory.createWrappedNode(node, parent); context.putLookedUpNode(fqn, n); if (needToCopy) n.markForUpdate(dataContainer, writeSkewCheck); } return n; } /** * Wraps a node and all its subnodes and adds them to the context, acquiring write locks for them all. * * @param ctx context * @param fqn fqn to wrap * @return a list of Fqns of locks acquired in this call. * @throws InterruptedException if the lock manager is interrupted. */ @SuppressWarnings("unchecked") public List wrapNodesRecursivelyForRemoval(InvocationContext ctx, Fqn fqn) throws InterruptedException { // when removing a node we want to get a lock on the Fqn anyway and return the wrapped node. if (fqn.isRoot()) throw new CacheException("Attempting to remove Fqn.ROOT!"); Fqn parentFqn = fqn.getParent(); // inspect parent boolean needToCopyParent = false; boolean parentLockNeeded = isParentLockNeeded(parentFqn, ctx); ReadCommittedNode parent = null; if (parentLockNeeded) { needToCopyParent = acquireLock(ctx, parentFqn); // Ensure the node is in the context. parent = wrapAndPutInContext(ctx, parentFqn, needToCopyParent); } boolean needToCopyNode = acquireLock(ctx, fqn); // Ensure the node is in the context. ReadCommittedNode node = wrapAndPutInContext(ctx, fqn, needToCopyNode); if (node == null || node.isNullNode()) { // node does not exist; return an empty list since there is nothing to remove! return Collections.emptyList(); } else { // update child ref on parent to point to child as this is now a copy. if (parentLockNeeded && (needToCopyNode || needToCopyParent)) { if (parent == null) throw new NodeNotExistsException("Parent node " + parentFqn + " does not exist!"); InternalNode ref = null; if (configuration.getIsolationLevel() == IsolationLevel.READ_COMMITTED) { ref = new NodeReference(node.getDelegationTarget()); } else { ref = node.getDelegationTarget(); } parent.getDelegationTarget().addChild(ref); } // now deal with children. Map> childMap = node.getDelegationTarget().getChildrenMap(); List fqnsToBeRemoved = new LinkedList(); fqnsToBeRemoved.add(fqn); if (!childMap.isEmpty()) { for (InternalNode n : childMap.values()) lockForWritingRecursive(n.getFqn(), ctx, fqnsToBeRemoved); } return fqnsToBeRemoved; } } /** * Locks a node recursively for writing, not creating if it doesn't exist. * * @param fqn Fqn to lock * @param ctx invocation context to add wrapped node to * @param fqnList fqnList to update - this list should not be null but should be initially empty and will be * populated with a list of all Fqns locked in this call. * @throws InterruptedException if interrupted */ @SuppressWarnings("unchecked") private void lockForWritingRecursive(Fqn fqn, InvocationContext ctx, List fqnList) throws InterruptedException { acquireLock(ctx, fqn); // lock node if (fqnList != null) fqnList.add(fqn); // now wrap and add to the context ReadCommittedNode rcn = wrapNodeForWriting(ctx, fqn, true, false, true, false, false); if (rcn != null) { rcn.markForUpdate(dataContainer, writeSkewCheck); Map> children = rcn.getDelegationTarget().getChildrenMap(); for (InternalNode child : children.values()) lockForWritingRecursive(child, rcn.getInternalParent(), ctx, fqnList); } } /** * Identical to {@link #lockForWritingRecursive(org.jboss.cache.Fqn, org.jboss.cache.InvocationContext, * java.util.List)} except that it uses an {@link org.jboss.cache.InternalNode} instead of an {@link Fqn} - saves a * lookup. * * @param node node to lock recursively * @param ctx invocation context * @param fqnList list of Fqns to add to * @throws InterruptedException if interrupted */ @SuppressWarnings("unchecked") private void lockForWritingRecursive(InternalNode node, InternalNode parent, InvocationContext ctx, List fqnList) throws InterruptedException { Fqn fqn = node.getFqn(); acquireLock(ctx, fqn); // lock node if (fqnList != null) fqnList.add(fqn); // now wrap and add to the context NodeSPI rcn = wrapNodeForWriting(ctx, node, parent); if (rcn != null) { rcn.markForUpdate(dataContainer, writeSkewCheck); Map> children = node.getChildrenMap(); for (InternalNode child : children.values()) lockForWritingRecursive(child, node, ctx, fqnList); } } /** * Wraps a node and puts it in the context, optionally copying the node for updating if forUpdate is * true. If the node is already in the context, a new wrapped node is not created, but the existing one is * still checked for changes and potentially marked for update if forUpdate is true. * * @param ctx invocation context to add node to * @param fqn fqn of node to add * @param forUpdate if true, the wrapped node is marked for update before adding to the context. * @return the ReadCommittedNode wrapper, or null if the node does not exist. */ @SuppressWarnings("unchecked") private ReadCommittedNode wrapAndPutInContext(InvocationContext ctx, Fqn fqn, boolean forUpdate) { ReadCommittedNode node = (ReadCommittedNode) ctx.lookUpNode(fqn); if (node == null || node.isNullNode()) { InternalNode[] nodes = dataContainer.peekInternalNodeAndDirectParent(fqn, false); node = nodeFactory.createWrappedNodeForRemoval(fqn, nodes[0], nodes[1]); ctx.putLookedUpNode(fqn, node); } // node could be null if using read-committed if (forUpdate && node != null && !node.isChanged()) node.markForUpdate(dataContainer, writeSkewCheck); return node; } /** * An overloaded version of {@link #isParentLockNeeded(org.jboss.cache.Fqn, org.jboss.cache.InvocationContext)} which * takes in an {@link org.jboss.cache.InternalNode} instead of a {@link Fqn}. * * @param parent parent node to test * @return true if parent lock is needed, false otherwise. */ private boolean isParentLockNeeded(InternalNode parent) { return lockParentForChildInsertRemove || (parent != null && parent.isLockForChildInsertRemove()); } /** * Tests if locking the parent is necessary when locking a specific node. * * @param parent Fqn of parent node to check * @param ctx invocation context * @return true if parent lock is needed, false otherwise. */ private boolean isParentLockNeeded(Fqn parent, InvocationContext ctx) { ReadCommittedNode parentNodeTmp = (ReadCommittedNode) ctx.lookUpNode(parent); InternalNode in = parentNodeTmp == null ? dataContainer.peekInternalNode(parent, true) : parentNodeTmp.getDelegationTarget(); return isParentLockNeeded(in); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/DataContainer.java0000644000175000017500000002216511111047320025705 0ustar moellermoeller/* * 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; import org.jboss.cache.marshall.NodeData; 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 * @deprecated use Cache.getRoot(); */ @Deprecated 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. * @deprecated Note that this only supports legacy locking schemes (OL and PL) and will be removed when those schemes are removed. */ @Deprecated NodeSPI peek(Fqn fqn); /** * 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. * @deprecated Note that this only supports legacy locking schemes (OL and PL) and will be removed when those schemes are removed. */ @Deprecated 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. * @deprecated Note that this only supports legacy locking schemes (OL and PL) and will be removed when those schemes are removed. */ @Deprecated 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 * @param mapSafe if true, the node's data map reference is passed to the NodeData instance created. Otherwise, the map is copied. * @return the same list passed in */ List buildNodeData(List list, NodeSPI node, boolean mapSafe); /** * 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); // TODO: See if this is still needed here /** *

        * 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); // TODO: See if this is still needed here /** * 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); /** * Similar to {@link #peek(Fqn)} except that the underlying node is NOT wrapped as a {@link org.jboss.cache.NodeSPI}. * * @param f fqn to peek * @param includeInvalidNodes if true, invalid nodes will be considered as well. * @return internal node */ InternalNode peekInternalNode(Fqn f, boolean includeInvalidNodes); /** * Similar to {@link #peekInternalNode(Fqn, boolean)} except that the node AND its *direct* parent are retrieved. * * @param fqn fqn to find * @param includeInvalidNodes if true, invalid nodes are considered. * @return an array of InternalNodes, containing 2 elements. Element [0] is the node being peeked, and element [1] is its direct parent. */ public InternalNode[] peekInternalNodeAndDirectParent(Fqn fqn, boolean includeInvalidNodes); /** * Sets a new root node * * @param nodeInvocationDelegate */ // TODO: 3.0.0: FIx this so that it can take in a NodeSPI for OL/PL and an InternalNode for MVCC void setRoot(Object nodeInvocationDelegate); /** * @param fqn fqn to check * @return true if the node exists and is marked as resident, false otherwise. */ boolean isResident(Fqn fqn); /** * @return a string representation of the contents of the data container */ String printDetails(); } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/FqnComparator.java0000644000175000017500000000646211111047320025747 0ustar moellermoeller/* * 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; import net.jcip.annotations.Immutable; import java.io.Serializable; 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 AT jboss DOT org) * @author Steve Woodcock (stevew@jofti.com) */ @Immutable public class FqnComparator implements Comparator, Serializable { 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-3.2.8.GA/src/main/java/org/jboss/cache/jmx/0000755000175000017500000000000011675222074023136 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/jmx/JmxStatisticsExposer.java0000644000175000017500000000410111111047320030134 0ustar moellermoeller/* * 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.jmx; import java.util.Map; /** * Interface containing common cache management operations * * @author Jerry Gauthier * @version $Id: JmxStatisticsExposer.java 7168 2008-11-19 17:37:20Z jason.greene@jboss.com $ */ public interface JmxStatisticsExposer { /** * 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(); } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/jmx/CacheNotificationListener.java0000644000175000017500000002143511111047320031046 0ustar moellermoeller/* * 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.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: 7168 $ */ @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-3.2.8.GA/src/main/java/org/jboss/cache/jmx/CacheJmxWrapper.java0000644000175000017500000007235511564307102027031 0ustar moellermoeller/* * 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.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.LegacyConfigurationException; import org.jboss.cache.config.parsing.JGroupsStackParser; import org.jboss.cache.config.parsing.XmlConfigurationParser2x; import org.jboss.cache.config.parsing.element.BuddyElementParser; import org.jboss.cache.config.parsing.element.EvictionElementParser; import org.jboss.cache.config.parsing.element.LoadersElementParser; import org.jboss.cache.util.CachePrinter; import org.jgroups.Address; import org.jgroups.ChannelFactory; 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 * @author Galder Zamarreno * @version $Revision: 8457 $ * For all purposes use {@link org.jboss.cache.jmx.JmxRegistrationManager}. Use this class only when you want to obtain * a reference to a Cache object through JMX. */ public class CacheJmxWrapper extends NotificationBroadcasterSupport implements CacheJmxWrapperMBean, MBeanRegistration, CacheNotificationBroadcaster { private Log log = LogFactory.getLog(getClass().getName()); private MBeanServer server; private String cacheObjectName; private boolean jmxResourceRegistered; private CacheSPI cache; private Configuration config; private boolean registerJmxResource = 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 BuddyElementParser buddyElementParser = new BuddyElementParser(); private LoadersElementParser loadersElementParser = new LoadersElementParser(); private EvictionElementParser evictionElementParser = new EvictionElementParser(); private JGroupsStackParser stackParser = new JGroupsStackParser(); // ----------------------------------------------------------- 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" : CachePrinter.formatHtml(cfg.toString()); } public String printCacheDetails() { return cache == null ? "Cache is null" : CachePrinter.printCacheDetails(cache); } public String printCacheDetailsAsHtml() { return cache == null ? "Cache is null" : CachePrinter.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" : CachePrinter.formatHtml(CachePrinter.printCacheLockingInfo(cache)); } public boolean getRegisterJmxResource() { return registerJmxResource; } public void setRegisterJmxResource(boolean register) { this.registerJmxResource = 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 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) { try { brc = buddyElementParser.parseBuddyElement(config); } catch (LegacyConfigurationException lce) { brc = XmlConfigurationParser2x.parseBuddyReplicationConfig(config); } } getConfiguration().setBuddyReplicationConfig(brc); this.buddyReplConfig = config; } public void setCacheLoaderConfig(Element cacheLoaderConfig) { CacheLoaderConfig clc = null; if (cacheLoaderConfig != null) { try { clc = loadersElementParser.parseLoadersElement(cacheLoaderConfig); } catch (LegacyConfigurationException lce) { clc = XmlConfigurationParser2x.parseCacheLoaderConfig(cacheLoaderConfig); } } getConfiguration().setCacheLoaderConfig(clc); this.cacheLoaderConfig = cacheLoaderConfig; } 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 = stackParser.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) { try { ec = evictionElementParser.parseEvictionElement(config); } catch (LegacyConfigurationException ce) { ec = XmlConfigurationParser2x.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 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(); registerJmxResources(); 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 jmx resources unregisterJmxResources(); } 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 ResourcesDMBean // in JMX, even if we didn't register them in create unregisterJmxResources(); 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) { cacheObjectName = objName.getCanonicalName(); } else { getCacheObjectName(); } } // Inform our CacheNotificationListener of the ObjectName it should transmit if (notificationServiceName == null) notificationServiceName = cacheObjectName; cacheNotificationListener.setServiceName(notificationServiceName); return new ObjectName(cacheObjectName); } /** * Registers the cache's MBean resources, if {@link #getRegisterJmxResource()} * 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 { registerJmxResources(); } catch (Exception e) { log.error("Caught exception registering cache ResourcesDMBean with JMX", e); } } registered = true; } } /** * No-op. */ public void preDeregister() throws Exception { } /** * Unregisters the ResourcesDMBean, if {@link #getRegisterJmxResource()} is * true. */ public void postDeregister() { unregisterJmxResources(); 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) { if (config.getClusterName() == null) { cacheObjectName = JmxRegistrationManager.LOCAL_CACHE_PREFIX + "Cache" + System.currentTimeMillis(); } else { cacheObjectName = JmxRegistrationManager.REPLICATED_CACHE_PREFIX + config.getClusterName(); } } 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; } 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)); } protected boolean registerJmxResources() throws CacheException { if (registerJmxResource && config.getExposeManagementStatistics() && !jmxResourceRegistered && server != null) { log.debug("Registering jmx resources"); JmxRegistrationManager registrationManager = new JmxRegistrationManager(server, cache, this.cacheObjectName); registrationManager.registerAllMBeans(); jmxResourceRegistered = true; return true; } return false; } protected void unregisterJmxResources() { if (registerJmxResource && jmxResourceRegistered && server != null) { log.debug("Unregistering interceptors"); JmxRegistrationManager registrationManager = new JmxRegistrationManager(server, cache, this.cacheObjectName); registrationManager.unregisterAllMBeans(); jmxResourceRegistered = false; } } // -------------------------------------------------------- Private methods /** * 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-3.2.8.GA/src/main/java/org/jboss/cache/jmx/CacheNotificationBroadcaster.java0000644000175000017500000000441711111047320031513 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/jmx/JmxRegistrationManager.java0000644000175000017500000002071311157534240030424 0ustar moellermoeller/* * 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.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.CacheSPI; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.ComponentRegistry; import javax.management.InstanceAlreadyExistsException; import javax.management.MBeanServer; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import java.lang.management.ManagementFactory; import java.util.ArrayList; import java.util.List; /** * Registers all the MBeans from an Cache instance to a MBeanServer. * It iterates over all the components within ComponentRegistry and registers all the components * annotated with ManagedAttribute, ManagedOperation or/and MBean. * If no MBean server is provided, then the {@link java.lang.management.ManagementFactory#getPlatformMBeanServer()} * is being used. *

    * It is immutable: both cache instance and MBeanServer are being passed as arguments to the constructor. *

    *

    * Note that by default object names used are prefixed with jboss.cache:service=JBossCache. While this format * works for and is consistent with JBoss AS and the JMX console, it has been known to cause problems with other JMX * servers such as Websphere. To work around this, you can provide the following VM system property to override this * prefix with a prefix of your choice: * -Djbosscache.jmx.prefix=JBossCache *

    * * @author Mircea.Markus@jboss.com * @author Galder Zamarreno * @since 3.0 */ public class JmxRegistrationManager { private static final Log log = LogFactory.getLog(JmxRegistrationManager.class); private static final String GENERAL_PREFIX = System.getProperty("jbosscache.jmx.prefix", "jboss.cache:service=JBossCache"); /** * default ObjectName for clusterd caches. Cluster name should pe appended. */ public static final String REPLICATED_CACHE_PREFIX = GENERAL_PREFIX + ",cluster="; /** * default ObjectName for non clustered caches. An unique identifier should be appended. */ public static final String LOCAL_CACHE_PREFIX = GENERAL_PREFIX + ",uniqueId="; /** * Key for every Dynamic mbean added. */ public static final String JMX_RESOURCE_KEY = ",jmx-resource="; private MBeanServer mBeanServer; private String objectNameBase; private CacheSPI cacheSpi; /** * C-tor. * * @param mBeanServer the server where mbeans are being registered * @param cache cache that needs to be monitored * @param objectNameBase path in the MBeanServer where to register cache MBeans */ public JmxRegistrationManager(MBeanServer mBeanServer, Cache cache, ObjectName objectNameBase) { this.mBeanServer = mBeanServer; this.cacheSpi = (CacheSPI) cache; processBaseName(objectNameBase); log.debug("Base name is: " + this.objectNameBase); } /** * @throws IllegalArgumentException if the supplied objectNameBase name isn't valid * @see #JmxRegistrationManager(javax.management.MBeanServer, org.jboss.cache.Cache, javax.management.ObjectName) */ public JmxRegistrationManager(MBeanServer mBeanServer, Cache cache, String objectNameBase) { this.mBeanServer = mBeanServer; this.cacheSpi = (CacheSPI) cache; try { processBaseName(new ObjectName(objectNameBase)); log.debug("Base name is: " + this.objectNameBase); } catch (MalformedObjectNameException e) { throw new IllegalArgumentException("Invalid Object Name : " + objectNameBase, e); } } /** * Defaults to platform to platform MBeanServer. * * @see java.lang.management.ManagementFactory#getPlatformMBeanServer() * @see platform MBeanServer */ public JmxRegistrationManager(Cache cache, ObjectName objectNameBase) { this(ManagementFactory.getPlatformMBeanServer(), cache, objectNameBase); } public JmxRegistrationManager(Cache cache) { this(cache, null); } /** * Performs the MBean registration. */ public void registerAllMBeans() throws CacheException { try { List resourceDMBeans = getResourceDMBeans(); for (ResourceDMBean resource : resourceDMBeans) { String resourceName = resource.getObjectName(); ObjectName objectName = new ObjectName(getObjectName(resourceName)); if (!mBeanServer.isRegistered(objectName)) { try { mBeanServer.registerMBean(resource, objectName); } catch (InstanceAlreadyExistsException e) { log.warn("There same instance is already registred!", e); } } } } catch (Exception e) { throw new CacheException("Failure while registering mbeans", e); } } /** * Unregisters all the MBeans registered through {@link #registerAllMBeans()}. */ public void unregisterAllMBeans() throws CacheException { log.trace("Unregistering jmx resources.."); try { List resourceDMBeans = getResourceDMBeans(); for (ResourceDMBean resource : resourceDMBeans) { String resourceName = resource.getObjectName(); ObjectName objectName = new ObjectName(getObjectName(resourceName)); if (mBeanServer.isRegistered(objectName)) { mBeanServer.unregisterMBean(objectName); } } } catch (Exception e) { throw new CacheException("Failure while unregistering mbeans", e); } } private List getResourceDMBeans() { List resourceDMBeans = new ArrayList(); for (ComponentRegistry.Component component : cacheSpi.getComponentRegistry().getRegisteredComponents()) { ResourceDMBean resourceDMBean = new ResourceDMBean(component.getInstance()); if (resourceDMBean.isManagedResource()) { resourceDMBeans.add(resourceDMBean); } } return resourceDMBeans; } private void processBaseName(ObjectName baseName) { if (baseName != null) { this.objectNameBase = baseName.getCanonicalName(); return; } if (cacheSpi.getConfiguration().getCacheMode().equals(Configuration.CacheMode.LOCAL)) { // CurrentTimeMillis is not good enaugh as an unique id generator. I am constantly // getting conflicts in several parallel tests on my box. Maybe some more sofisticated // unique id generator should be provided? // For example: use identity hashcode in hex format. objectNameBase = LOCAL_CACHE_PREFIX + Integer.toHexString(System.identityHashCode(cacheSpi)); } else //the cache is clustered { objectNameBase = REPLICATED_CACHE_PREFIX + cacheSpi.getConfiguration().getClusterName(); } } public String getObjectName(String resourceName) { return objectNameBase + JMX_RESOURCE_KEY + resourceName; } public String getObjectNameBase() { return objectNameBase; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/jmx/CacheJmxWrapperMBean.java0000644000175000017500000001313511223355330027721 0ustar moellermoeller/* * 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.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 * For all purposes use {@link org.jboss.cache.jmx.JmxRegistrationManager}. Use this class only when you want to obtain * a reference to a Cache object through JMX. */ 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 #create()}. *

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

      * Default is true. */ void setRegisterJmxResource(boolean register); } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/jmx/PlatformMBeanServerRegistration.java0000644000175000017500000000661211111047320032237 0ustar moellermoeller/* * 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.jmx; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheSPI; 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; /** * If {@link org.jboss.cache.config.Configuration#getExposeManagementStatistics()} is true, then class will register * all the MBeans from the ConfigurationRegistry to the pltform MBean server. *

      * Note: to enable platform MBeanServer the following system property should be passet to the JVM: * -Dcom.sun.management.jmxremote. * * @author Mircea.Markus@jboss.com * @see java.lang.management.ManagementFactory#getPlatformMBeanServer() * @since 3.0 */ @NonVolatile public class PlatformMBeanServerRegistration { private static final Log log = LogFactory.getLog(PlatformMBeanServerRegistration.class); private CacheSPI cache; private JmxRegistrationManager jmxRegistrationManager; @Inject public void initialize(CacheSPI cache) { this.cache = cache; } /** * Here is where the registration is being performed. */ @Start(priority = 14) public void registerToPlatformMBeanServer() { if (cache == null) throw new IllegalStateException("The cache should had been injected before a call to this method"); Configuration config = cache.getConfiguration(); if (config.getExposeManagementStatistics()) { jmxRegistrationManager = new JmxRegistrationManager(cache); jmxRegistrationManager.registerAllMBeans(); log.info("JBossCache MBeans were successfully registered to the platform mbean server."); } } /** * Unregister when the cache is being stoped. */ @Stop public void unregisterMBeans() { //this method might get called several times. // After the first call the cache will become null, so we guard this if (cache == null) return; Configuration config = cache.getConfiguration(); if (config.getExposeManagementStatistics()) { jmxRegistrationManager.unregisterAllMBeans(); log.trace("JBossCache MBeans were successfully unregistered from the platform mbean server."); } cache = null; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/jmx/JmxUtil.java0000644000175000017500000000665511111047320025371 0ustar moellermoeller/* * 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.jmx; import org.jboss.cache.config.Configuration; import javax.management.JMException; import javax.management.MBeanServer; import javax.management.ObjectName; /** * Various JMX related utilities * * @author Jerry Gauthier * @author Manik Surtani * @version $Id: JmxUtil.java 7168 2008-11-19 17:37:20Z jason.greene@jboss.com $ */ @Deprecated 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 JMX_RESOURCE_KEY = ",jmx-resource="; 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); } } 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)); } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/jmx/LegacyConfiguration.java0000644000175000017500000002713111236270336027736 0ustar moellermoeller/* * 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.jmx; import org.jboss.cache.Region; import org.jgroups.ChannelFactory; import org.w3c.dom.Element; import javax.transaction.TransactionManager; /** * Legacy configuration attributes from JBC 1.x. * * @author Brian Stansberry * @version $Revision: 8150 $ * @deprecated use {@link org.jboss.cache.jmx.JmxRegistrationManager} */ @Deprecated 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 cacheLoaderConfig); /** * @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); }jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/jmx/annotations/0000755000175000017500000000000011675222073025472 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/jmx/annotations/ManagedAttribute.java0000644000175000017500000000344111111047320031540 0ustar moellermoeller/* * 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.jmx.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Indicates that a public method or a field (any visibility) in * an MBean class defines an MBean attribute. This annotation can * be applied to either a field or a public setter and/or getter * method of a public class that is itself is optionally annotated * with an @MBean annotation, or inherits such an annotation from * a superclass. */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.FIELD}) public @interface ManagedAttribute { String description() default ""; String name() default ""; boolean writable() default false; } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/jmx/annotations/MBean.java0000644000175000017500000000345511111047320027307 0ustar moellermoeller/* * 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.jmx.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Classes anotaded with this will be exposed as MBeans. * If you are looking for more fined grained way of exposing jmx attributes/operations, take a look at * {@link org.jboss.cache.jmx.annotations.ManagedAttribute} and {@link org.jboss.cache.jmx.annotations.ManagedOperation} * * @author Mircea.Markus@jboss.com * @since 3.0 */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Inherited public @interface MBean { String objectName() default ""; boolean exposeAll() default false; String description() default ""; } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/jmx/annotations/ManagedOperation.java0000644000175000017500000000320711111047320031535 0ustar moellermoeller/* * 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.jmx.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Indicates that a method in an MBean class defines an MBean * operation. @ManagedOperation annotation can be applied to a * public method of a public class that is itself optionally * annotated with an @MBean annotation, or inherits such an * annotation from a superclass. */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface ManagedOperation { String description() default ""; } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/jmx/ResourceDMBean.java0000644000175000017500000006034611111047320026570 0ustar moellermoeller/* * 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.jmx; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.jmx.annotations.MBean; import org.jboss.cache.jmx.annotations.ManagedAttribute; import org.jboss.cache.jmx.annotations.ManagedOperation; import javax.management.Attribute; import javax.management.AttributeList; import javax.management.DynamicMBean; import javax.management.MBeanAttributeInfo; import javax.management.MBeanException; import javax.management.MBeanInfo; import javax.management.MBeanOperationInfo; import javax.management.ReflectionException; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * This class was entirely copied from jgroups 2.7 (same name there). * Couldn't simply reuse it because jgroups does not ship with MBean, ManagedAttribute and ManagedOperation. * Once jgroups will ship these classes, the code can be dinalmically reused from there. * * @author Mircea.Markus@jboss.com * @since 3.0 */ public class ResourceDMBean implements DynamicMBean { private static final Class[] primitives = {int.class, byte.class, short.class, long.class, float.class, double.class, boolean.class, char.class}; private static final String MBEAN_DESCRITION = "Dynamic MBean Description"; private final Log log = LogFactory.getLog(ResourceDMBean.class); private final Object obj; private String description = ""; private final MBeanAttributeInfo[] attrInfo; private final MBeanOperationInfo[] opInfo; private final HashMap atts = new HashMap(); private final List ops = new ArrayList(); public ResourceDMBean(Object instance) { if (instance == null) throw new NullPointerException("Cannot make an MBean wrapper for null instance"); this.obj = instance; findDescription(); findFields(); findMethods(); attrInfo = new MBeanAttributeInfo[atts.size()]; int i = 0; if (log.isTraceEnabled()) { log.trace("Processing class " + instance.getClass()); log.trace("Number of attributes: " + atts.size()); } MBeanAttributeInfo info; for (AttributeEntry entry : atts.values()) { info = entry.getInfo(); attrInfo[i++] = info; if (log.isTraceEnabled()) { log.trace("Attribute " + info.getName() + "[r=" + info.isReadable() + ",w=" + info.isWritable() + ",is=" + info.isIs() + ",type=" + info.getType() + "]"); } } opInfo = new MBeanOperationInfo[ops.size()]; ops.toArray(opInfo); if (log.isTraceEnabled()) { if (ops.size() > 0) log.trace("Operations are:"); for (MBeanOperationInfo op : opInfo) { log.trace("Operation " + op.getReturnType() + " " + op.getName()); } } } Object getObject() { return obj; } private void findDescription() { MBean mbean = getObject().getClass().getAnnotation(MBean.class); if (mbean != null && mbean.description() != null && mbean.description().trim().length() > 0) { description = mbean.description(); if (log.isTraceEnabled()) { log.trace("@MBean description set - " + mbean.description()); } MBeanAttributeInfo info = new MBeanAttributeInfo(MBEAN_DESCRITION, "java.lang.String", "@MBean description", true, false, false); try { atts.put(MBEAN_DESCRITION, new FieldAttributeEntry(info, getClass().getDeclaredField("description"))); } catch (NoSuchFieldException e) { //this should not happen unless somebody removes description field log.warn("Could not reflect field description of this class. Was it removed?"); } } } public synchronized MBeanInfo getMBeanInfo() { return new MBeanInfo(getObject().getClass().getCanonicalName(), description, attrInfo, null, opInfo, null); } public synchronized Object getAttribute(String name) { if (name == null || name.length() == 0) throw new NullPointerException("Invalid attribute requested " + name); if (log.isTraceEnabled()) { log.trace("getAttribute called for " + name); } Attribute attr = getNamedAttribute(name); if (log.isTraceEnabled()) { log.trace("getAttribute value found " + attr.getValue()); } return attr.getValue(); } public synchronized void setAttribute(Attribute attribute) { if (attribute == null || attribute.getName() == null) throw new NullPointerException("Invalid attribute requested " + attribute); setNamedAttribute(attribute); } public synchronized AttributeList getAttributes(String[] names) { AttributeList al = new AttributeList(); for (String name : names) { Attribute attr = getNamedAttribute(name); if (attr != null) { al.add(attr); } else { log.warn("Did not find attribute " + name); } } return al; } public synchronized AttributeList setAttributes(AttributeList list) { AttributeList results = new AttributeList(); for (int i = 0; i < list.size(); i++) { Attribute attr = (Attribute) list.get(i); if (log.isTraceEnabled()) { log.trace("Attribute name " + attr.getName() + " new value is " + attr.getValue()); } if (setNamedAttribute(attr)) { results.add(attr); } else { if (log.isTraceEnabled()) { log.trace("Failed to update attribute name " + attr.getName() + " with value " + attr.getValue()); } } } return results; } public Object invoke(String name, Object[] args, String[] sig) throws MBeanException, ReflectionException { try { if (log.isTraceEnabled()) { log.trace("Invoke method called on " + name); } Class[] classes = new Class[sig.length]; for (int i = 0; i < classes.length; i++) { classes[i] = getClassForName(sig[i]); } Method method = getObject().getClass().getMethod(name, classes); return method.invoke(getObject(), args); } catch (Exception e) { throw new MBeanException(e); } } public static Class getClassForName(String name) throws ClassNotFoundException { try { Class c = Class.forName(name); return c; } catch (ClassNotFoundException cnfe) { //Could be a primitive - let's check for (int i = 0; i < primitives.length; i++) { if (name.equals(primitives[i].getName())) { return primitives[i]; } } } throw new ClassNotFoundException("Class " + name + " cannot be found"); } private void findMethods() { //find all methods but don't include methods from Object class List methods = new ArrayList(Arrays.asList(getObject().getClass().getMethods())); List objectMethods = new ArrayList(Arrays.asList(Object.class.getMethods())); methods.removeAll(objectMethods); for (Method method : methods) { //does method have @ManagedAttribute annotation? ManagedAttribute attr = method.getAnnotation(ManagedAttribute.class); if (attr != null) { String methodName = method.getName(); if (!methodName.startsWith("get") && !methodName.startsWith("set") && !methodName.startsWith("is")) { if (log.isWarnEnabled()) { log.warn("method name " + methodName + " doesn't start with \"get\", \"set\", or \"is\"" + ", but is annotated with @ManagedAttribute: will be ignored"); } } else { MBeanAttributeInfo info = null; //Is name field of @ManagedAttributed used? String attributeName = attr.name().length() > 0 ? attr.name().trim() : null; boolean writeAttribute = false; if (isSetMethod(method)) { // setter attributeName = (attributeName == null) ? methodName.substring(3) : attributeName; info = new MBeanAttributeInfo(attributeName, method.getParameterTypes()[0].getCanonicalName(), attr.description(), true, true, false); writeAttribute = true; } else { // getter if (method.getParameterTypes().length == 0 && method.getReturnType() != java.lang.Void.TYPE) { boolean hasSetter = atts.containsKey(attributeName); //we found is method if (methodName.startsWith("is")) { attributeName = (attributeName == null) ? methodName.substring(2) : attributeName; info = new MBeanAttributeInfo(attributeName, method.getReturnType().getCanonicalName(), attr.description(), true, hasSetter, true); } else { //this has to be get attributeName = (attributeName == null) ? methodName.substring(3) : attributeName; info = new MBeanAttributeInfo(attributeName, method.getReturnType().getCanonicalName(), attr.description(), true, hasSetter, false); } } else { if (log.isWarnEnabled()) { log.warn("Method " + method.getName() + " must have a valid return type and zero parameters"); } continue; } } if (log.isTraceEnabled()) { log.trace("@Attr found for method " + method.getName() + " and registered as " + attributeName); } AttributeEntry ae = atts.get(attributeName); //is it a read method? if (!writeAttribute) { //we already have annotated field as read if (ae instanceof FieldAttributeEntry && ae.getInfo().isReadable()) { log.warn("not adding annotated method " + method + " since we already have read attribute"); } //we already have annotated set method else if (ae instanceof MethodAttributeEntry) { MethodAttributeEntry mae = (MethodAttributeEntry) ae; if (mae.hasSetMethod()) { atts.put(attributeName, new MethodAttributeEntry(mae.getInfo(), mae.getSetMethod(), method)); } } //we don't have such entry else { atts.put(attributeName, new MethodAttributeEntry(info, null, method)); } }//is it a set method? else { if (ae instanceof FieldAttributeEntry) { //we already have annotated field as write if (ae.getInfo().isWritable()) { log.warn("Not adding annotated method " + methodName + " since we already have writable attribute"); } else { //we already have annotated field as read //lets make the field writable Field f = ((FieldAttributeEntry) ae).getField(); MBeanAttributeInfo i = new MBeanAttributeInfo(ae.getInfo().getName(), f.getType().getCanonicalName(), attr.description(), true, Modifier.isFinal(f.getModifiers()) ? false : true, false); atts.put(attributeName, new FieldAttributeEntry(i, f)); } } //we already have annotated getOrIs method else if (ae instanceof MethodAttributeEntry) { MethodAttributeEntry mae = (MethodAttributeEntry) ae; if (mae.hasIsOrGetMethod()) { atts.put(attributeName, new MethodAttributeEntry(info, method, mae.getIsOrGetMethod())); } } //we don't have such entry else { atts.put(attributeName, new MethodAttributeEntry(info, method, null)); } } } } else if (method.isAnnotationPresent(ManagedOperation.class) || isMBeanAnnotationPresentWithExposeAll()) { ManagedOperation op = method.getAnnotation(ManagedOperation.class); String attName = method.getName(); if (isSetMethod(method) || isGetMethod(method)) { attName = attName.substring(3); } else if (isIsMethod(method)) { attName = attName.substring(2); } //expose unless we already exposed matching attribute field boolean isAlreadyExposed = atts.containsKey(attName); if (!isAlreadyExposed) { ops.add(new MBeanOperationInfo(op != null ? op.description() : "", method)); if (log.isTraceEnabled()) { log.trace("@Operation found for method " + method.getName()); } } } } } private boolean isSetMethod(Method method) { return (method.getName().startsWith("set") && method.getParameterTypes().length == 1 && method.getReturnType() == java.lang.Void.TYPE); } private boolean isGetMethod(Method method) { return (method.getParameterTypes().length == 0 && method.getReturnType() != java.lang.Void.TYPE && method.getName().startsWith("get")); } private boolean isIsMethod(Method method) { return (method.getParameterTypes().length == 0 && (method.getReturnType() == boolean.class || method.getReturnType() == Boolean.class) && method.getName().startsWith("is")); } private void findFields() { //traverse class hierarchy and find all annotated fields for (Class clazz = getObject().getClass(); clazz != null; clazz = clazz.getSuperclass()) { Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { ManagedAttribute attr = field.getAnnotation(ManagedAttribute.class); if (attr != null) { String fieldName = renameToJavaCodingConvention(field.getName()); MBeanAttributeInfo info = new MBeanAttributeInfo(fieldName, field.getType().getCanonicalName(), attr.description(), true, Modifier.isFinal(field.getModifiers()) ? false : attr.writable(), false); atts.put(fieldName, new FieldAttributeEntry(info, field)); if (log.isTraceEnabled()) { log.trace("@Attr found for field " + field.getName()); } } } } } private Attribute getNamedAttribute(String name) { Attribute result = null; if (name.equals(MBEAN_DESCRITION)) { result = new Attribute(MBEAN_DESCRITION, this.description); } else { AttributeEntry entry = atts.get(name); if (entry != null) { MBeanAttributeInfo i = entry.getInfo(); try { result = new Attribute(name, entry.invoke(null)); if (log.isTraceEnabled()) { log.trace("Attribute " + name + " has r=" + i.isReadable() + ",w=" + i.isWritable() + ",is=" + i.isIs() + " and value " + result.getValue()); } } catch (Exception e) { log.warn("Exception while reading value of attribute " + name, e); } } else { log.warn("Did not find queried attribute with name " + name); } } return result; } private boolean setNamedAttribute(Attribute attribute) { boolean result = false; if (log.isTraceEnabled()) { log.trace("Invoking set on attribute " + attribute.getName() + " with value " + attribute.getValue()); } AttributeEntry entry = atts.get(attribute.getName()); if (entry != null) { try { entry.invoke(attribute); result = true; } catch (Exception e) { log.warn("Exception while writing value for attribute " + attribute.getName(), e); } } else { log.warn("Could not invoke set on attribute " + attribute.getName() + " with value " + attribute.getValue()); } return result; } private String renameToJavaCodingConvention(String fieldName) { if (fieldName.contains("_")) { Pattern p = Pattern.compile("_."); Matcher m = p.matcher(fieldName); StringBuffer sb = new StringBuffer(); while (m.find()) { m.appendReplacement(sb, fieldName.substring(m.end() - 1, m.end()).toUpperCase()); } m.appendTail(sb); char first = sb.charAt(0); if (Character.isLowerCase(first)) { sb.setCharAt(0, Character.toUpperCase(first)); } return sb.toString(); } else { if (Character.isLowerCase(fieldName.charAt(0))) { return fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); } else { return fieldName; } } } private boolean isMBeanAnnotationPresentWithExposeAll() { Class c = getObject().getClass(); return c.isAnnotationPresent(MBean.class) && c.getAnnotation(MBean.class).exposeAll(); } private class MethodAttributeEntry implements AttributeEntry { final MBeanAttributeInfo info; final Method isOrGetmethod; final Method setMethod; public MethodAttributeEntry(final MBeanAttributeInfo info, final Method setMethod, final Method isOrGetMethod) { super(); this.info = info; this.setMethod = setMethod; this.isOrGetmethod = isOrGetMethod; } public Object invoke(Attribute a) throws Exception { if (a == null && isOrGetmethod != null) return isOrGetmethod.invoke(getObject(), new Object[]{}); else if (a != null && setMethod != null) return setMethod.invoke(getObject(), new Object[]{a.getValue()}); else return null; } public MBeanAttributeInfo getInfo() { return info; } public boolean hasIsOrGetMethod() { return isOrGetmethod != null; } public boolean hasSetMethod() { return setMethod != null; } public Method getIsOrGetMethod() { return isOrGetmethod; } public Method getSetMethod() { return setMethod; } } private class FieldAttributeEntry implements AttributeEntry { private final MBeanAttributeInfo info; private final Field field; public FieldAttributeEntry(final MBeanAttributeInfo info, final Field field) { super(); this.info = info; this.field = field; if (!field.isAccessible()) { field.setAccessible(true); } } public Field getField() { return field; } public Object invoke(Attribute a) throws Exception { if (a == null) { return field.get(getObject()); } else { field.set(getObject(), a.getValue()); return null; } } public MBeanAttributeInfo getInfo() { return info; } } private interface AttributeEntry { public Object invoke(Attribute a) throws Exception; public MBeanAttributeInfo getInfo(); } public boolean isManagedResource() { return !atts.isEmpty() || !ops.isEmpty(); } public String getObjectName() { MBean mBean = obj.getClass().getAnnotation(MBean.class); if (mBean != null && mBean.objectName() != null && mBean.objectName().trim().length() > 0) { return mBean.objectName(); } return obj.getClass().getSimpleName(); } public boolean isOperationRegistred(String operationName) { for (MBeanOperationInfo opInfo : this.ops) { if (opInfo.getName().equals(operationName)) { return true; } } return false; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/xml/0000755000175000017500000000000011675222074023140 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/TreeCacheViewMBean.java0000644000175000017500000000276411111047320026555 0ustar moellermoeller/* * 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; 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-3.2.8.GA/src/main/java/org/jboss/cache/buddyreplication/0000755000175000017500000000000011675222100025667 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/buddyreplication/BuddyFqnTransformer.java0000644000175000017500000001532111371344767032514 0ustar moellermoeller/* * 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.buddyreplication; import org.jboss.cache.CacheException; import org.jboss.cache.Fqn; import org.jgroups.Address; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * 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 = BuddyManager.BUDDY_BACKUP_SUBTREE; public static final Fqn BUDDY_BACKUP_SUBTREE_FQN = BuddyManager.BUDDY_BACKUP_SUBTREE_FQN; /** * 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 static 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 boolean isDead = isDeadBackupFqn(fqn); int fqnSz = fqn.size(); if (isDead && fqnSz == 2) return Fqn.ROOT; return fqn.getSubFqn(isDead ? 3 : 2, fqnSz); } /** * 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. */ 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 */ public Fqn getBackupRoot(Address dataOwner) { return 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 */ public Fqn getDeadBackupRoot(Address dataOwner) { return 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 * @return a backup root from an Fqn */ public Fqn getBackupRootFromFqn(Fqn fqn) { if (isBackupFqn(fqn) && fqn.size() > 1) { return fqn.getSubFqn(0, isDeadBackupFqn(fqn) ? 3 : 2); } else { return Fqn.root(); } } /** * Takes a set of names that represent the children of a * {@link #isDeadBackupRoot(Fqn) dead backup root} and ensure they are * all of the expected type, tranforming any that are not. See JBCACHE-1580. * * * @param children names that represent the children of a dead backup root node * * @return set, based on names whose members are all of the * expected type. */ public Set getDeadBackupRootChildren(Set children) { Set result = new HashSet(children.size()); for (Object child : children) { result.add((child instanceof Integer ? (Integer) child : Integer.valueOf(child.toString()))); } return result; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/buddyreplication/BuddyManager.java0000644000175000017500000014145211264744202031111 0ustar moellermoeller/* * 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.buddyreplication; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.*; import org.jboss.cache.commands.CommandsFactory; 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.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.jmx.annotations.ManagedAttribute; 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.*; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; 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 AT jboss DOT 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; private volatile boolean enabled = false; /** * 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; private ConcurrentMap> defunctDataHistory = new ConcurrentHashMap>(); // Backing out fix for JBCACHE-1531 for 3.2.0. Maybe this can come in in a future release, after more thought and // analysis. - Manik, 18 Aug 2009 // private final ReclosableLatch buddyMembershipInFluxLatch = new ReclosableLatch(true); public BuddyManager() { } public BuddyManager(BuddyReplicationConfig config) { setupInternals(config); } private void setupInternals(BuddyReplicationConfig config) { this.config = config; enabled = this.config.isEnabled(); 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 enabled; } 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) @SuppressWarnings("unchecked") 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)); // make sure the NextMemberBuddyLocator has a ref to the Channel, to be able to convert a LogicalAddress to a PhysicalAddress. if (buddyLocator instanceof NextMemberBuddyLocator) { ((NextMemberBuddyLocator) buddyLocator).setChannel(rpcManager.getChannel()); } if (config.getBuddyPoolName() != null) { buddyPool.put(buddyGroup.getDataOwner(), config.getBuddyPoolName()); } broadcastBuddyPoolMembership(); cache.getInvocationContext().getOptionOverrides().setSkipCacheStatusCheck(true); if (!cache.exists(BUDDY_BACKUP_SUBTREE_FQN)) { // need to get the root DIRECTLY. cache.getRoot() will pass a call up the interceptor chain and we will // have a problem with the cache not being started. cache.getInvocationContext().getOptionOverrides().setSkipCacheStatusCheck(true); cache.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache.put(BUDDY_BACKUP_SUBTREE_FQN, (Map) Collections.emptyMap()); } // allow waiting threads to process. initialisationLatch.countDown(); // register a 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; } /** * Returns the list of nodes that were in the old view and are not in the new view, and which are also in the * filter param. */ public Set
          getDroppedNodes(Collection
          filter) { if (oldMembers == null || oldMembers.isEmpty()) return Collections.emptySet(); Set
          result = new HashSet
          (); for (Address oldMember : oldMembers) { if (!newMembers.contains(oldMember) && filter.contains(oldMember)) { result.add(oldMember); } } return result; } } 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. /* Do not clear the queue here. It might happen that there is an new memebr added there, which must be * notified about the pool membership. */ 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 { // buddyMembershipInFluxLatch.close(); // try // { 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."); // } // finally // { // buddyMembershipInFluxLatch.open(); // } } /** * 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)) { if (log.isTraceEnabled()) log.trace("handleRemoveFromBuddyGroup removing " + me.getKey()); 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 {@link AssignToBuddyGroupCommand} thic command 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.put(Fqn.fromElements(BUDDY_BACKUP_SUBTREE, newGroup.getGroupName()), (Map) Collections.emptyMap()); } 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; } public List> getNewlyDeadBackupFqns(Fqn backupFqn) { if (buddyFqnTransformer.isDeadBackupFqn(backupFqn)) { return null; } if (!buddyFqnTransformer.isBackupFqn(backupFqn) || backupFqn.size() < BUDDY_BACKUP_SUBTREE_FQN.size() + 2) { return null; } List> result = null; String owner = (String) backupFqn.get(BUDDY_BACKUP_SUBTREE_FQN.size()); Set historySet = defunctDataHistory.get(owner); if (historySet != null) { Fqn coreFqn = null; for (Iterator it = historySet.iterator(); it.hasNext(); ) { DefunctDataHistory gen = it.next(); if (gen.isStale()) { it.remove(); } else { if (result == null) { result = new ArrayList>(); } if (coreFqn == null) { coreFqn = buddyFqnTransformer.getActualFqn(backupFqn); } Fqn base = Fqn.fromRelativeElements(buddyFqnTransformer.getDeadBackupRoot(gen.owner), Integer.valueOf(gen.generation)); result.add(Fqn.fromRelativeFqn(base, coreFqn)); } } } return result; } // -------------- 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() { // try // { // buddyMembershipInFluxLatch.await(); // } // catch (InterruptedException e) // { // Thread.currentThread().interrupt(); // } return buddyGroup.getBuddies(); } /** * Created as an optimisation for JGroups, which uses vectors. * * @since 2.2.0 */ public Vector
          getBuddyAddressesAsVector() { // try // { // buddyMembershipInFluxLatch.await(); // } // catch (InterruptedException e) // { // Thread.currentThread().interrupt(); // } 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.copy(); 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 toBe = new BuddyGroup(buddyGroup.getGroupName(), buddyGroup.getDataOwner()); toBe.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(toBe, stateMap); int attemptsLeft = UNINIT_BUDDIES_RETRIES; int currentAttempt = 0; while (attemptsLeft-- > 0) { try { if (log.isTraceEnabled()) log.trace("Executing assignment call " + membershipCall); 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 { if (attemptsLeft > 0) { log.error("Unable to communicate with Buddy for some reason", e); } else { throw new BuddyNotInitException("Unable to contact buddy after " + UNINIT_BUDDIES_RETRIES + " retries"); } } } } buddyGroup.addBuddies(buddies); 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(NodeSPI backupRoot, Address dataOwner) { Fqn defunctBackupRootFqn = getDefunctBackupRootFqn(dataOwner); if (log.isDebugEnabled()) log.debug("Migrating defunct data. Backup root is " + backupRoot + ". New backup root is " + defunctBackupRootFqn); if (trace) log.trace("Children of backup root are " + backupRoot.getChildren()); String ownerName = buddyFqnTransformer.getGroupNameFromAddress(dataOwner); Set newHistorySet = new ConcurrentHashSet(); Set historySet = defunctDataHistory.putIfAbsent(ownerName, newHistorySet); if (historySet == null) { historySet = newHistorySet; } DefunctDataHistory history = new DefunctDataHistory(dataOwner, (Integer) defunctBackupRootFqn.getLastElement(), System.currentTimeMillis()); historySet.add(history); for (Object child : backupRoot.getChildren()) { Fqn childFqn = ((Node) child).getFqn(); cache.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache.move(childFqn, defunctBackupRootFqn); } history.recordDataMoved(); cache.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); backupRoot.getParentDirect().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); cache.getInvocationContext().getOptionOverrides().setSkipCacheStatusCheck(true); Node root = cache.getRoot(); cache.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache.getInvocationContext().getOptionOverrides().setSkipCacheStatusCheck(true); Node defunctRootNode = root.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"); 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 { if (trace) log.trace("Waiting for enqueued view change events"); MembershipChange members = queue.take(); if (trace) log.trace("Processing membership change: " + members); if (members == STOP_NOTIFIER) { log.trace("Caught stop notifier, time to go home."); // time to go home isRunning = false; return; } // BES 2009/10/12 This next bit is highly suspect and causes // JBCACHE-1549, so I've commented it out // // 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. Set
          toRemove = members.getDroppedNodes(buddyGroupsIParticipateIn.keySet()); if (log.isTraceEnabled()) log.trace("removed members are: " + toRemove); for (Address a : toRemove) { if (log.isTraceEnabled()) log.trace("handleEnqueuedViewChange is removing: " + a); BuddyGroup bg = buddyGroupsIParticipateIn.remove(a); Fqn backupRootFqn = buddyFqnTransformer.getBackupRoot(bg.getDataOwner()); NodeSPI 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) { 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. 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); } } @ManagedAttribute(description = "A String representation of the cache's buddy group") public String getBuddyGroup() { return buddyGroup.toString(); } @ManagedAttribute(description = "A String representation of buddy groups the cache participates in") public String getBuddyGroupsIParticipateIn() { return buddyGroupsIParticipateIn.toString(); } private class DefunctDataHistory { private final Address owner; private final int generation; private final long timestamp; private long dataMoved; private long moveElapsedTime; private DefunctDataHistory(Address owner, int generation, long timestamp) { this.owner = owner; this.generation = generation; this.timestamp = timestamp; } private void recordDataMoved() { this.dataMoved = System.currentTimeMillis(); this.moveElapsedTime = this.dataMoved - this.timestamp; } private boolean isStale() { if (dataMoved == 0) return false; // migration is still ongoing long max = Math.max(BuddyManager.this.configuration.getLockAcquisitionTimeout(), 60000); max = max + moveElapsedTime; return System.currentTimeMillis() - max > dataMoved; } } }jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/buddyreplication/NextMemberBuddyLocator.java0000644000175000017500000002037711236271477033143 0ustar moellermoeller/* * 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.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.Channel; 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 AT jboss DOT org) */ @ThreadSafe public class NextMemberBuddyLocator implements BuddyLocator { private final Log log = LogFactory.getLog(NextMemberBuddyLocator.class); private NextMemberBuddyLocatorConfig config = new NextMemberBuddyLocatorConfig(); private AddressLocator addressLocator; private Channel channel; public BuddyLocatorConfig getConfig() { return config; } public void setChannel(Channel channel) { this.channel = channel; } 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(); } // test if JGroups 2.8 is in use! try { getClass().getClassLoader().loadClass("org.jgroups.PhysicalAddress"); // this is new in JG 2.8 addressLocator = new JGroups28AddressLocator(); } catch (ClassNotFoundException e) { // fall back to JGroups 2.6 addressLocator = new JGroups26AddressLocator(); } } 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; } protected 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); } protected boolean isColocated(Address candidate, Address dataOwner) { InetAddress inetC = addressLocator.locate(channel, candidate); InetAddress inetD = addressLocator.locate(channel, dataOwner); 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-3.2.8.GA/src/main/java/org/jboss/cache/buddyreplication/Fqn2BuddyFqnVisitor.java0000644000175000017500000002264011236274723032374 0ustar moellermoeller/* * 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.buddyreplication; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.AbstractVisitor; import org.jboss.cache.commands.CommandsFactory; import org.jboss.cache.commands.WriteCommand; import org.jboss.cache.commands.legacy.write.CreateNodeCommand; 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; 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(command.getGlobalTransaction()); } @Override public Object visitOptimisticPrepareCommand(InvocationContext ctx, OptimisticPrepareCommand command) throws Throwable { List transformed = transformBatch(command.getModifications()); return factory.buildOptimisticPrepareCommand(command.getGlobalTransaction(), transformed, 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 (WriteCommand com : toTransform) { transformedCommands.add((WriteCommand) 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; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/buddyreplication/BuddyGroup.java0000644000175000017500000000674011135635137030636 0ustar moellermoeller/* * 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.buddyreplication; import net.jcip.annotations.ThreadSafe; import org.jboss.cache.util.Immutables; 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 AT jboss DOT 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 BuddyGroup() { } public BuddyGroup(String groupName, Address dataOwner) { this.groupName = groupName; this.dataOwner = dataOwner; } 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 Immutables.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-3.2.8.GA/src/main/java/org/jboss/cache/buddyreplication/GravitateResult.java0000755000175000017500000000760211131164605031671 0ustar moellermoeller/* * 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.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 "GravitateResult 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; } } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/buddyreplication/NextMemberBuddyLocatorConfig.javajbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/buddyreplication/NextMemberBuddyLocatorConfig0000644000175000017500000001005711111047320033317 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/buddyreplication/AddressLocator.java0000644000175000017500000000046311236271477031464 0ustar moellermoellerpackage org.jboss.cache.buddyreplication; import org.jgroups.Address; import org.jgroups.Channel; import java.net.InetAddress; /** * Locates the InetAddress of an Address * * @author Manik Surtani * @since 3.2.0 */ public interface AddressLocator { InetAddress locate(Channel c, Address a); } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/buddyreplication/BuddyLocator.java0000644000175000017500000000700411111047320031120 0ustar moellermoeller/* * 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.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 AT jboss DOT 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-3.2.8.GA/src/main/java/org/jboss/cache/buddyreplication/BuddyNotInitException.java0000644000175000017500000000316711111047320032766 0ustar moellermoeller/* * 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.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-3.2.8.GA/src/main/java/org/jboss/cache/buddyreplication/JGroups26AddressLocator.java0000644000175000017500000000131111236271477033137 0ustar moellermoellerpackage org.jboss.cache.buddyreplication; import org.jgroups.Address; import org.jgroups.Channel; import org.jgroups.stack.IpAddress; import java.net.InetAddress; /** * Address locator that has no knowledge of the new JGroups PhysicalAddress interface * * @author Manik Surtani * @since 3.2.0 */ public class JGroups26AddressLocator implements AddressLocator { public InetAddress locate(Channel c, Address a) { if (a instanceof IpAddress) { return ((IpAddress) a).getIpAddress(); } else { throw new RuntimeException("Expected Address to be of type IpAddress. Instead, was " + a.getClass() + "! Don't know how to handle, giving up!"); } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/buddyreplication/JGroups28AddressLocator.java0000644000175000017500000000222311236317515033136 0ustar moellermoellerpackage org.jboss.cache.buddyreplication; import org.jgroups.Address; import org.jgroups.Channel; import org.jgroups.Event; import org.jgroups.stack.IpAddress; import java.lang.reflect.Field; import java.net.InetAddress; /** * An address locator that is able to deal with the JGroups 2.8 PhysicalAddress interface * * @author Manik Surtani * @since 3.2.0 */ public class JGroups28AddressLocator extends JGroups26AddressLocator { int getPhysicalAddressEventId; public JGroups28AddressLocator() { try { Class eventClass = Event.class; Field f = eventClass.getField("GET_PHYSICAL_ADDRESS"); getPhysicalAddressEventId = (Integer) f.get(null); } catch (Exception e) { throw new RuntimeException("Unable to initialize AddressLocator", e); } } @Override public InetAddress locate(Channel channel, Address a) { if (a instanceof IpAddress) { return super.locate(channel, a); } else { Address physicalAddress = (Address) channel.downcall(new Event(getPhysicalAddressEventId, a)); return super.locate(channel, physicalAddress); } } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/InternalNode.java0000644000175000017500000001403411140322163025552 0ustar moellermoeller/* * 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; 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; import java.util.concurrent.ConcurrentMap; /** * An internal node interface, that represents nodes that directly form the tree structure in the cache. This is as opposed * to {@link Node} and its {@link NodeSPI} sub-interface, which are typically implemented by delegates which then delegate calls * to internal nodes, potentially after passing the call up an interceptor chain. *

          * All calls on an InternalNode are executed directly on the data structure. Usually, XXXDirect() calls on {@link NodeSPI} * delegate to calls on an InternalNode, for example, {@link NodeSPI#putDirect(Object, Object)} delegating to {@link #put(Object, Object)}. *

          * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 */ public interface InternalNode { // --------------- basic access to he internal state of the node. ------------------------- V get(K key); Map getData(); V put(K key, V value); void putAll(Map data); V remove(K key); void clear(); Set getKeys(); boolean containsKey(K key); void setInternalState(Map state); Map getInternalState(boolean onlyInternalState); void releaseObjectReferences(boolean recursive); // --------------- node naming and tree structure --------------------- /** * @return the node's Fqn */ Fqn getFqn(); /** * Sets the node's Fqn * * @param fqn Fqn to set to */ void setFqn(Fqn fqn); boolean hasChildren(); // ***************** Old legacy methods that assume that the child map maintained contains NodeSPIs. These are still retained to support Optimistic and Pessimistic Locking *************** @Deprecated NodeSPI getChildDirect(Fqn fqn); @Deprecated NodeSPI getChildDirect(Object childName); @Deprecated Set> getChildrenDirect(); @Deprecated Set> getChildrenDirect(boolean includeMarkedForRemoval); @Deprecated Map> getChildrenMapDirect(); @Deprecated void setChildrenMapDirect(Map> children); @Deprecated void addChildDirect(Object nodeName, Node nodeToAdd); @Deprecated void addChildDirect(NodeSPI child); @Deprecated NodeSPI addChildDirect(Fqn f); @Deprecated NodeSPI addChildDirect(Fqn f, boolean notify); @Deprecated NodeSPI addChildDirect(Object o, boolean notify); /** * @deprecated should use the {@link org.jboss.cache.NodeFactory} instead. */ @Deprecated NodeSPI getOrCreateChild(Object childName, GlobalTransaction gtx); // ***************** End old legacy methods. See new versions below, which are supported by MVCC. ********************************* InternalNode getChild(Fqn f); InternalNode getChild(Object childName); Set> getChildren(); Set> getChildren(boolean includeMarkedForRemoval); ConcurrentMap> getChildrenMap(); void setChildrenMap(ConcurrentMap> children); void addChild(Object nodeName, InternalNode nodeToAdd); void addChild(InternalNode child); /** * Same as above, except that if safe is true, any Fqn ancestry checking is skipped. Don't set safe to true unless * you really know what you are doing! * * @param child child to add * @param safe safety flag */ void addChild(InternalNode child, boolean safe); // *****************End new methods ***************** Set getChildrenNames(); void removeChildren(); boolean removeChild(Object childName); boolean removeChild(Fqn f); /** * Creates a new instance of the same type and copies internal state. Note that a shallow copy is made for all fields * except the data map, where a new map is created. * * @return a copy. */ InternalNode copy(); // ------------- Getters and setters for various flags ------------- boolean isChildrenLoaded(); void setChildrenLoaded(boolean flag); boolean isDataLoaded(); void setDataLoaded(boolean dataLoaded); boolean isValid(); void setValid(boolean valid, boolean recursive); boolean isLockForChildInsertRemove(); void setLockForChildInsertRemove(boolean lockForChildInsertRemove); boolean isRemoved(); void setRemoved(boolean marker); void markAsRemoved(boolean marker, boolean recursive); void setResident(boolean resident); boolean isResident(); // ------------- Utility stuff, mainly for backward compatibility ----------------- void printDetails(StringBuilder sb, int indent); NodeSPI getParent(); CacheSPI getCache(); void setVersion(DataVersion version); DataVersion getVersion(); NodeLock getLock(); } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/Region.java0000644000175000017500000002670111433521426024427 0ustar moellermoeller/* * 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; import org.jboss.cache.annotations.Compat; import org.jboss.cache.commands.DataCommand; 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.EvictionEvent; import org.jboss.cache.eviction.EvictionPolicy; import javax.transaction.Transaction; /** * 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.EvictionRegionConfig} set using {@link #setEvictionRegionConfig(org.jboss.cache.config.EvictionRegionConfig)}. *

          * 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 AT jboss DOT org) * @see RegionManager * @since 2.0.0 */ @Compat(notes = "Cloneable is extended for backward compat.") 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(); /** * Processes the eviction queues (primary and recycle queues) associated with this region. A no-op if this is not an eviction region. * * @since 3.0 */ void processEvictionQueues(); /** * Clears the node event queue used for processing eviction. */ void resetEvictionQueues(); /** * Configures this region for eviction. * * @param evictionRegionConfig configuration to set */ void setEvictionRegionConfig(EvictionRegionConfig evictionRegionConfig); /** * @return the eviction region config, if any, set on the current region. */ EvictionRegionConfig getEvictionRegionConfig(); /** * Registers an eviction event on the region's eviction event queue for later processing by * {@link #processEvictionQueues()}. * * @param fqn passed in to the constructor of {@link org.jboss.cache.eviction.EvictionEvent} * @param eventType passed in to the constructor of {@link org.jboss.cache.eviction.EvictionEvent} * @param elementDifference passed in to the constructor of {@link org.jboss.cache.eviction.EvictionEvent} * @return an EvictedEventNode that has been created for this queue */ EvictionEvent registerEvictionEvent(Fqn fqn, EvictionEvent.Type eventType, int elementDifference, DataCommand command, Transaction tx); /** * An overloaded version of {@link #registerEvictionEvent(Fqn, org.jboss.cache.eviction.EvictionEvent.Type, int, DataCommand, Transaction)} which * uses a default elementDifference value. * * @param fqn passed in to the constructor of {@link org.jboss.cache.eviction.EvictionEvent} * @param eventType passed in to the constructor of {@link org.jboss.cache.eviction.EvictionEvent} * @return an EvictedEventNode that has been created for this queue */ EvictionEvent registerEvictionEvent(Fqn fqn, EvictionEvent.Type eventType); /** * Marks a {@link org.jboss.cache.Node} as currently in use, by adding an event to the eviction queue. * If there is an {@link org.jboss.cache.config.EvictionRegionConfig} associated with this region, and * it respects this event (e.g., {@link org.jboss.cache.eviction.LRUAlgorithm} 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(); /** * copies the region - including eviction queue events - to a new Region instance, attached to a new Fqn root. * Typically used with Buddy Replication where region roots need to be adjusted. * * @param newRoot new root for the region - e.g., a buddy backup root. * @return a new Region instance. */ Region copy(Fqn newRoot); // -------- deprecated interfaces retained for compatibility with 2.x. ----------- /** * Configures an eviction policy for this region. *

          * Note: This is deprecated since this is an internal method and never was * meant to be a part of the public API. Please do not treat this as public API, it may be removed in a future release * and its functionality is not guaranteed. *

          * * @param evictionPolicyConfig configuration to set * @deprecated */ @Deprecated @Compat void setEvictionPolicy(EvictionPolicyConfig evictionPolicyConfig); /** * Returns an eviction policy configuration. *

          * Note: This is deprecated since this is an internal method and never was * meant to be a part of the public API. Please do not treat this as public API, it may be removed in a future release * and its functionality is not guaranteed. *

          * * @return an eviction policy configuration * @deprecated */ @Deprecated @Compat EvictionPolicyConfig getEvictionPolicyConfig(); /** * Returns an eviction policy. *

          * Note: This is deprecated since this is an internal method and never was * meant to be a part of the public API. Please do not treat this as public API, it may be removed in a future release * and its functionality is not guaranteed. *

          * * @return an eviction policy * @deprecated */ @Deprecated @Compat EvictionPolicy getEvictionPolicy(); /** * Returns the size of the node event queue, used by the eviction thread. *

          * Note: This is deprecated since this is an internal method and never was * meant to be a part of the public API. Please do not treat this as public API, it may be removed in a future release * and its functionality is not guaranteed. *

          * * @return number of events * @deprecated */ @Deprecated @Compat int nodeEventQueueSize(); /** * Returns the most recent {@link org.jboss.cache.eviction.EvictedEventNode} added to the event queue by * {@link #putNodeEvent(org.jboss.cache.eviction.EvictedEventNode)}. *

          * Note: This is deprecated since this is an internal method and never was * meant to be a part of the public API. Please do not treat this as public API, it may be removed in a future release * and its functionality is not guaranteed. *

          * * @return the last {@link org.jboss.cache.eviction.EvictedEventNode}, or null if no more events exist * @deprecated */ @Compat @Deprecated EvictedEventNode takeLastEventNode(); /** * Adds an {@link org.jboss.cache.eviction.EvictedEventNode} to the internal queue for processing * by the eviction thread. *

          * Note: This is deprecated since this is an internal method and never was * meant to be a part of the public API. Please do not treat this as public API, it may be removed in a future release * and its functionality is not guaranteed. *

          * * @param event event to add * @deprecated */ @Deprecated @Compat void putNodeEvent(EvictedEventNode event); /** * @return a clone * @deprecated */ @Deprecated @Compat Region clone() throws CloneNotSupportedException; } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/ReplicationException.java0000644000175000017500000000302311111047320027311 0ustar moellermoeller/* * 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; /** * 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-3.2.8.GA/src/main/java/org/jboss/cache/PessimisticNodeFactory.java0000644000175000017500000000414711111047320027623 0ustar moellermoeller/* * 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; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.lock.LockStrategyFactory; import java.util.Map; /** * Node factory specific to pessimistic locking. * * @author Manik Surtani (manik AT jboss DOT org) * @since 3.0 * @deprecated will be removed along with optimistic and pessimistic locking. */ @Deprecated public class PessimisticNodeFactory extends AbstractNodeFactory { private LockStrategyFactory lockStrategyFactory; @Inject private void injectLockStrategyFactory(LockStrategyFactory lockStrategyFactory) { this.lockStrategyFactory = lockStrategyFactory; } @Override protected UnversionedNode createInternalNode(Object childName, Fqn fqn, NodeSPI parent, Map data) { PessimisticUnversionedNode internal = new PessimisticUnversionedNode(childName, fqn, data, cache); internal.injectDependencies(cache, commandsFactory, this); internal.injectLockStrategyFactory(lockStrategyFactory); return internal; } } jbosscache-core-3.2.8.GA/src/main/java/org/jboss/cache/CacheFactory.java0000644000175000017500000001626211111047320025525 0ustar moellermoeller/* * 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; 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 AT jboss DOT 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-3.2.8.GA/src/main/tutorial/0000755000175000017500000000000011675222134020305 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/tutorial/README-Tutorial.txt0000644000175000017500000000015611111047320023571 0ustar moellermoellerPlease see the tutorial in the /doc directory. This directory contains script and code to run the tutorial. jbosscache-core-3.2.8.GA/src/main/tutorial/build.xml0000644000175000017500000000467211111047320022122 0ustar moellermoeller jbosscache-core-3.2.8.GA/src/main/resources/0000755000175000017500000000000011675222143020454 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/resources/schema/0000755000175000017500000000000011675222137021717 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/resources/schema/jbosscache-config-3.0.xsd0000644000175000017500000002754511150773520026314 0ustar moellermoeller jbosscache-core-3.2.8.GA/src/main/resources/schema/jbosscache-config-3.1.xsd0000644000175000017500000002775211160156274026317 0ustar moellermoeller jbosscache-core-3.2.8.GA/src/main/resources/schema/jbosscache-config-3.2.xsd0000644000175000017500000002775211236775757026341 0ustar moellermoeller jbosscache-core-3.2.8.GA/src/main/resources/schema/jbosscache-registry-3.0.xsd0000644000175000017500000000223511150773520026704 0ustar moellermoeller jbosscache-core-3.2.8.GA/src/main/resources/schema/jbosscache-registry-3.1.xsd0000644000175000017500000000223511147030762026705 0ustar moellermoeller jbosscache-core-3.2.8.GA/src/main/resources/schema/jbosscache-registry-3.2.xsd0000644000175000017500000000223511236775757026731 0ustar moellermoeller jbosscache-core-3.2.8.GA/src/main/resources/config2to3.bat0000644000175000017500000000107311037126064023117 0ustar moellermoeller@echo off if "%1a" == "a" goto noParams if "%2a" == "a" goto noParams setlocal enabledelayedexpansion set LIB= for %%f in (..\lib\compile\*.jar) do set LIB=!LIB!;%%f for %%f in (..\lib\test\*.jar) do set LIB=!LIB!;%%f for %%f in (..\lib\*.jar) do set LIB=!LIB!;%%f rem echo libs: %LIB% set CP=%LIB%;..\jbosscache-core.jar;%CP% rem echo cp is %CP% java -classpath "%CP%" -Dsource=%1 -Ddestination=%2 org.jboss.cache.config.parsing.ConfigFilesConvertor goto fileEnd :noParams echo usage: "%0 " :fileEndjbosscache-core-3.2.8.GA/src/main/resources/config2to3.sh0000755000175000017500000000105711111047320022756 0ustar moellermoeller#!/bin/bash if [ -z $1 ] then echo Usage: echo $0 [source_file] [destination_file] exit 1; fi if [ -e ../lib/compile ] then for JAR in ../lib/compile/* do CLASSPATH=$CLASSPATH:$JAR done fi if [ -e ../lib/test ] then for JAR in ../lib/test/* do CLASSPATH=$CLASSPATH:$JAR done fi for JAR in ../lib/* do CLASSPATH=$CLASSPATH:$JAR done CLASSPATH=../jbosscache-core.jar:$CLASSPATH echo classpath is $CLASSPATH java -classpath $CLASSPATH -Dsource=$1 -Ddestination=$2 org.jboss.cache.config.parsing.ConfigFilesConvertorjbosscache-core-3.2.8.GA/src/main/resources/config2to3.xslt0000644000175000017500000005667211236775757023406 0ustar moellermoeller A custom eviction policy is used for '/_default_' region. Starting with JBossCache 3.x the eviction API changed, so this config file will require manual transformation. Custom eviction policies require manual transformation. Custom eviction policies require manual transformation. jbosscache-core-3.2.8.GA/src/main/resources/config-samples/0000755000175000017500000000000011675222143023363 5ustar moellermoellerjbosscache-core-3.2.8.GA/src/main/resources/config-samples/non-blocking-state-transfer.xml0000644000175000017500000000047711236775757031456 0ustar moellermoeller jbosscache-core-3.2.8.GA/src/main/resources/config-samples/cacheloader-enabled.xml0000644000175000017500000000450511236775757027754 0ustar moellermoeller 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=BINARY cache.jdbc.parent.column=parent cache.jdbc.driver=org.hsqldb.jdbcDriver cache.jdbc.url=jdbc:hsqldb:mem:jbosscache cache.jdbc.user=sa cache.jdbc.password= jbosscache-core-3.2.8.GA/src/main/resources/config-samples/multiplexer-enabled.xml0000644000175000017500000000077111236775757030075 0ustar moellermoeller jbosscache-core-3.2.8.GA/src/main/resources/config-samples/string-property-replaced.xml0000644000175000017500000000377311236775757031105 0ustar moellermoeller ignoreColocatedBuddies = true numBuddies = ${test.property.BuddyReplicationConfig.numBuddies:1} location=${test.property.CacheLoaderConfiguration.location,java.io.tmpdir:/tmp} jbosscache-core-3.2.8.GA/src/main/resources/config-samples/external-jgroups-file.xml0000644000175000017500000000126211236775757030355 0ustar moellermoeller jbosscache-core-3.2.8.GA/src/main/resources/config-samples/all.xml0000644000175000017500000002275311236775757024707 0ustar moellermoeller 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=BINARY cache.jdbc.parent.column=parent cache.jdbc.driver=org.hsqldb.jdbcDriver cache.jdbc.url=jdbc:hsqldb:mem:jbosscache cache.jdbc.user=sa cache.jdbc.password= pushStateWhenCoordinator=true pushStateWhenCoordinatorTimeout=20000 jbosscache-core-3.2.8.GA/src/main/resources/config-samples/eviction-enabled.xml0000644000175000017500000000360611236775757027343 0ustar moellermoeller jbosscache-core-3.2.8.GA/src/main/resources/config-samples/local.xml0000644000175000017500000000063011236775757025217 0ustar moellermoeller jbosscache-core-3.2.8.GA/src/main/resources/config-samples/total-replication.xml0000644000175000017500000000466411236775757027572 0ustar moellermoeller jbosscache-core-3.2.8.GA/src/main/resources/config-samples/invalidation-async.xml0000644000175000017500000000542111236775757027724 0ustar moellermoeller jbosscache-core-3.2.8.GA/src/main/resources/config-samples/buddy-replication.xml0000644000175000017500000001055611236775757027553 0ustar moellermoeller numBuddies = 1 ignoreColocatedBuddies = true jbosscache-core-3.2.8.GA/src/main/resources/cache-jdbc.properties0000755000175000017500000000373211131164620024535 0ustar moellermoeller# Standard JBoss Cache 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=BINARY cache.jdbc.parent.column=parent # JBoss Cache Table properties for Hypersonic, just overrides #cache.jdbc.node.type=OBJECT ## # DataSource #cache.jdbc.datasource=DefaultDS ## # JDBC driver specific properties ## MySql #cache.jdbc.driver=com.mysql.jdbc.Driver #cache.jdbc.url=jdbc:mysql://localhost:3306/jbossdb #cache.jdbc.user=root #cache.jdbc.password=admin #cache.jdbc.node.type=BLOB ## 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 #cache.jdbc.node.type=BLOB ## 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 #cache.jdbc.node.type=BLOB ## 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 #cache.jdbc.node.type=BLOB ## HSQL cache.jdbc.driver = org.hsqldb.jdbcDriver cache.jdbc.url=jdbc:hsqldb:mem:jbosscache cache.jdbc.user=sa cache.jdbc.password= jbosscache-core-3.2.8.GA/README-i18n.txt0000644000175000017500000000223411111047320017166 0ustar moellermoellerInternationalising 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-3.2.8.GA/release.sh0000755000175000017500000000462611372020153016705 0ustar moellermoeller#!/bin/bash ################################################################################################### # CONFIGURABLE VARIABLES # Base SVN directory for this release. There should be a "tags" and "trunk" directory under this. svnBase="https://svn.jboss.org/repos/jbosscache/core" # Name of the "tags" directory svnTags="tags" # Where do you locally check out tags? localTagsDir="/Users/manik/Code/jbosscache/core/tags" ################################################################################################### # Functions help() { echo 'Usage: ' echo ' $ release.sh ' echo ' where version is represented by major.minor.micro.modifier' echo ' e.g.:' echo ' $ release.sh 3.0.1.BETA1' echo ' version modifier needs to be uppercase, and end with a numeric, except if it is GA.' echo } validate() { if [ ! $ver ] then echo "Missing version number!" help exit 0 fi if [[ $ver =~ ^[1-9]\.[0-9]\.[0-9]\.(GA|(ALPHA|BETA|CR|SP)[1-9][0-9]?)$ ]] then #matches! echo "Releasing version $ver" else echo "Incorrect version format for version $ver!" help exit 0 fi } tag() { svn cp ${svnBase}/trunk ${svnBase}/${svnTags}/$ver -m "JBoss Cache Release Script: Tagging $ver" cd $localTagsDir svn co ${svnBase}/${svnTags}/$ver cd $ver } setVersion() { # Change pom.xml and Version.java sed -e "s/[1-9]\.[0-9]\.[0-9]-SNAPSHOT/$ver/g" pom.xml > newpom.xml mv newpom.xml pom.xml verBytes=`echo $ver | sed -e "s/\([0-9A-Z]\)/'\1',/g" -e "s/,$/};/" -e "s/^/{'0',/" -e "s/\.//g"` sed -e "s/\"[1-9]\.[0-9]\.[0-9]-SNAPSHOT\"/\"$ver\"/g" -e "s/version_id = {[0-9A-Z', ]*};/version_id = $verBytes/g" src/main/java/org/jboss/cache/Version.java > tmp.java mv tmp.java src/main/java/org/jboss/cache/Version.java svn ci -m "JBoss Cache Release Script: Updating $ver" pom.xml src/main/java/org/jboss/cache/Version.java } build() { mvn clean deploy -Dmaven.test.skip.exec=true -PDocs } ### The actual script ver=${1} echo "JBoss Cache Release Script" validate tag setVersion build docs echo 'Done! Now all you need to do is:' echo ' 1. Update the website (http://www.jbosscache.org)' echo ' 2. Upload docs if needed' echo ' 3. Update wiki pages (main wiki page, docs and download)' echo ' 4. Announce and blog about this!' echo ' 5. Log in to http://repository.jboss.org/nexus and promote the release from the staging repo' echo jbosscache-core-3.2.8.GA/pom.xml0000644000175000017500000005406411622063211016244 0ustar moellermoeller 4.0.0 3.2.8.GA unit install org.jboss.cache jbosscache-common-parent 1.6 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.13.GA org.jboss.javaee jboss-transaction-api 1.0.1.GA commons-logging commons-logging 1.1.1 org.jboss jboss-common-core 2.2.14.GA jdbm jdbm 1.0 true c3p0 c3p0 0.9.1.2 true com.sleepycat je 4.0.92 true net.jcip jcip-annotations 1.0 true net.noderunner amazon-s3 1.0.0.1 true hsqldb hsqldb 1.8.0.7 test org.easymock easymock 2.4 test jboss.jbossts jbossjta 4.4.0.GA test jboss.jbossts jbossjts 4.6.1.GA test jboss.jbossts jbossts-common 4.6.1.GA test org.beanshell bsh 2.0b4 test net.noderunner http 1.0.3 test javax.servlet servlet-api 2.5 test org.testng testng 5.8 test jdk15 org.jboss.logging jboss-logging-spi 2.0.5.GA test org.apache.maven.plugins maven-surefire-plugin 2.4.3-JBOSS tests 10 none jgroups.stack ${protocol.stack} false listener org.jboss.cache.util.UnitTestTestNGListener maven-assembly-plugin 2.2-beta-1 assemble install attached assembly/bin.xml assembly/doc.xml assembly/all.xml assembly/src.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 org.apache.maven.plugins maven-surefire-report-plugin 2.4.3-JBOSS berkeleydb-je.repository http://download.oracle.com/maven/ 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 true functional,unit tcp test-functional functional tcp test-unit unit test-jgroups jgroups test-transaction transaction profiling profiling test-integration integration udp JBossAS false 3.2.8.GA-JBossAS functional,unit tcp jgroups jgroups 2.6.9.GA org.jboss.javaee jboss-javaee 5.0.1.GA org.jboss jboss-common-core 2.2.14.GA commons-logging commons-logging 1.1.0.jboss jboss.jbossts jbossjta 4.4.0.GA test