diff --git a/.gitignore b/.gitignore index 1830fcc7ef..fac95318bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,22 +1,4 @@ -*/target/ - -build/ -*/src/generated/ +# Gradle .gradle/ - -.project -.classpath -.settings/org.eclipse.m2e.core.prefs -*/.classpath -*.prefs - -*.iml -.idea -out/ - -.nb-gradle -.nb-gradle-properties - -/classes - -**/*.*~ +**/build/ +!**/src/**/build/ diff --git a/107/.gitignore b/107/.gitignore deleted file mode 100755 index ae3c172604..0000000000 --- a/107/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/bin/ diff --git a/107/gradle.properties b/107/gradle.properties deleted file mode 100644 index 2be5b29d91..0000000000 --- a/107/gradle.properties +++ /dev/null @@ -1,4 +0,0 @@ -subPomName = Ehcache 3 JSR-107 module -subPomDesc = The JSR-107 compatibility module of Ehcache 3 -osgi = {"Export-Package" : ["!org.ehcache.jsr107.tck", "!org.ehcache.jsr107.internal*"],\ - "Import-Package" : ["javax.cache.*;resolution:=optional", "!sun.misc.*", "!sun.security.action.*", "!com.sun.jmx.mbeanserver.*"]} diff --git a/107/src/main/java/org/ehcache/jsr107/DefaultJsr107SerializationProvider.java b/107/src/main/java/org/ehcache/jsr107/DefaultJsr107SerializationProvider.java deleted file mode 100644 index 5267f646c9..0000000000 --- a/107/src/main/java/org/ehcache/jsr107/DefaultJsr107SerializationProvider.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ehcache.jsr107; - -import org.ehcache.impl.config.serializer.DefaultSerializationProviderConfiguration; -import org.ehcache.impl.internal.spi.serialization.DefaultSerializationProvider; -import org.ehcache.impl.serialization.PlainJavaSerializer; - -/** - * @author Ludovic Orban - */ -class DefaultJsr107SerializationProvider extends DefaultSerializationProvider { - - @SuppressWarnings("unchecked") - DefaultJsr107SerializationProvider() { - super(new DefaultSerializationProviderConfiguration() - .addSerializerFor(Object.class, (Class) PlainJavaSerializer.class)); - } -} diff --git a/CONTRIBUTING.adoc b/CONTRIBUTING.adoc index 8dab9e13a5..a8701cacac 100644 --- a/CONTRIBUTING.adoc +++ b/CONTRIBUTING.adoc @@ -54,7 +54,7 @@ An example of `Service` being the `org.ehcache.core.spi.store.Store.Provider`, i `Service` instances are created using Java's https://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html[`java.util.ServiceLoader` service-provider loading facility]. It is used to locate all `org.ehcache.core.spi.service.ServiceFactory` implementations on the classpath. -These are in turn used to create `Service` instances. Each `CacheManager` uses its own `org.ehcache.core.internal.service.ServiceLocator` facility to locate `Service` instances, which it then in turn life cycles. +These are in turn used to create `Service` instances. Each `CacheManager` uses its own `org.ehcache.core.spi.ServiceLocator` facility to locate `Service` instances, which it then in turn life cycles. `Service` instances are configured by their own respective `ServiceConfiguration` at `Service.start()` invocation time. `CacheManager` and its `Service` instances can then use these services. diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000000..ab6247ebda --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,18 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. + * + * 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. + */ + + //See Jenkins wiki pages for info +checkmarxBuild checkmarx_project_name: 'Terracotta DB (TDB) Ehcache OSS' diff --git a/README.adoc b/README.adoc index f9de4e9bb5..5ece1c5e00 100644 --- a/README.adoc +++ b/README.adoc @@ -1,6 +1,6 @@ = The Ehcache 3.x line is currently the development line. -Status of the build: +Status of the build: [link="https://dev.azure.com/TerracottaCI/ehcache/_build/latest?definitionId=14"] image::https://dev.azure.com/TerracottaCI/ehcache/_apis/build/status/ehcache3[Build Status] @@ -12,16 +12,15 @@ For samples, documentation, and usage information, please see http://ehcache.org == Current release -We released 3.6.1 on September 21st 2018. +We released 3.9.0 on August 25th 2020. -The https://github.com/ehcache/ehcache3/releases/tag/v3.6.1[release notes] contain the links to the artifacts and the documentation to help you get started. +The https://github.com/ehcache/ehcache3/releases/tag/v3.9.0[release notes] contain the links to the artifacts and the documentation to help you get started. -You should consider upgrading to 3.6.x as it does all previous 3.x do and more with a fully compatible API. -The only thing to note compared to 3.0.x is that transactional support has been moved to a separate jar. +You should consider upgrading to 3.9.x as it does all previous 3.x do and more with a fully compatible API. == Current development & next release -We are still working on the missing features of the clustering tier of Ehcache 3 which will be included in upcoming releases. +We released the missing features of the clustering tier of Ehcache 3 in 3.7.0. Starting with version 3.5, Ehcache only supports Java 8 and later. diff --git a/api/.gitignore b/api/.gitignore deleted file mode 100755 index ae3c172604..0000000000 --- a/api/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/bin/ diff --git a/api/gradle.properties b/api/gradle.properties deleted file mode 100644 index 3736cff405..0000000000 --- a/api/gradle.properties +++ /dev/null @@ -1,18 +0,0 @@ -# -# Copyright Terracotta, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -subPomName = Ehcache 3 API module -subPomDesc = The API module of Ehcache 3 diff --git a/azure-pipelines-static-analysis.yml b/azure-pipelines-static-analysis.yml index 481793ad3e..4c3f99b9a8 100644 --- a/azure-pipelines-static-analysis.yml +++ b/azure-pipelines-static-analysis.yml @@ -24,6 +24,6 @@ resources: name: terracotta/terracotta jobs: -- template: build-templates/maven-common.yml@templates +- template: build-templates/gradle-common.yml@templates parameters: gradleTasks: 'check' diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 807c859598..590980b08b 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -20,8 +20,30 @@ resources: repositories: - repository: templates - type: git + type: git name: terracotta/terracotta jobs: -- template: build-templates/gradle-common.yml@templates +- template: build-templates/gradle-common.yml@templates + parameters: + jdkVersion: '1.8' + jobName: 'LinuxJava8' + +- template: build-templates/gradle-common.yml@templates + parameters: + jdkVersion: '1.8' + options: '-PtestVM=java11Home' + jobName: 'LinuxJava11' + +- template: build-templates/gradle-common.yml@templates + parameters: + vmImage: 'windows-latest' + jdkVersion: '1.8' + jobName: 'WindowsJava8' + +- template: build-templates/gradle-common.yml@templates + parameters: + vmImage: 'windows-latest' + jdkVersion: '1.8' + options: '-PtestVM=java11Home' + jobName: 'WindowsJava11' diff --git a/build-logic/build.gradle b/build-logic/build.gradle new file mode 100644 index 0000000000..78eed08c1a --- /dev/null +++ b/build-logic/build.gradle @@ -0,0 +1,67 @@ +plugins { + id 'java-gradle-plugin' +} + +repositories { + gradlePluginPortal() + mavenCentral() +} + +gradlePlugin { + plugins { + internalModule { + id = 'org.ehcache.build.internal-module' + implementationClass = 'org.ehcache.build.InternalEhcacheModule' + } + publicModule { + id = 'org.ehcache.build.public-module' + implementationClass = 'org.ehcache.build.PublicEhcacheModule' + } + clusteredModule { + id = 'org.ehcache.build.clustered-module' + implementationClass = 'org.ehcache.build.ClusteredEhcacheModule' + } + serverModule { + id = 'org.ehcache.build.clustered-server-module' + implementationClass = 'org.ehcache.build.ClusteredServerModule' + } + distribution { + id = 'org.ehcache.build.package' + implementationClass = 'org.ehcache.build.EhcachePackage' + } + + variant { + id = 'org.ehcache.build.plugins.variant' + implementationClass = 'org.ehcache.build.plugins.VariantPlugin' + } + + base { + id = 'org.ehcache.build.conventions.base' + implementationClass = 'org.ehcache.build.conventions.BaseConvention' + } + java { + id = 'org.ehcache.build.conventions.java' + implementationClass = 'org.ehcache.build.conventions.JavaConvention' + } + javaLibrary { + id = 'org.ehcache.build.conventions.java-library' + implementationClass = 'org.ehcache.build.conventions.JavaLibraryConvention' + } + war { + id = 'org.ehcache.build.conventions.war' + implementationClass = 'org.ehcache.build.conventions.WarConvention' + } + } +} + +dependencies { + api gradleApi() + api 'biz.aQute.bnd:biz.aQute.bnd.gradle:6.0.0' + api 'gradle.plugin.com.github.jengelman.gradle.plugins:shadow:7.0.0' + api 'org.unbroken-dome.gradle-plugins:gradle-xjc-plugin:2.0.0' + api 'com.github.spotbugs.snom:spotbugs-gradle-plugin:4.7.9' + implementation 'biz.aQute.bnd:biz.aQute.bndlib:6.0.0' + implementation 'org.osgi:org.osgi.service.component.annotations:1.5.0' + implementation 'org.apache.felix:org.apache.felix.scr.generator:1.18.4' + implementation 'org.apache.felix:org.apache.felix.scr.ds-annotations:1.2.10' +} diff --git a/build-logic/src/main/java/org/ehcache/build/ClusteredEhcacheModule.java b/build-logic/src/main/java/org/ehcache/build/ClusteredEhcacheModule.java new file mode 100644 index 0000000000..5ca1afbab9 --- /dev/null +++ b/build-logic/src/main/java/org/ehcache/build/ClusteredEhcacheModule.java @@ -0,0 +1,12 @@ +package org.ehcache.build; + +import org.gradle.api.Project; + +public class ClusteredEhcacheModule extends EhcacheModule { + + @Override + public void apply(Project project) { + project.setGroup("org.ehcache.modules.clustered"); + super.apply(project); + } +} diff --git a/build-logic/src/main/java/org/ehcache/build/ClusteredServerModule.java b/build-logic/src/main/java/org/ehcache/build/ClusteredServerModule.java new file mode 100644 index 0000000000..64be60977d --- /dev/null +++ b/build-logic/src/main/java/org/ehcache/build/ClusteredServerModule.java @@ -0,0 +1,17 @@ +package org.ehcache.build; + +import org.ehcache.build.conventions.DeployConvention; +import org.ehcache.build.plugins.VoltronPlugin; +import org.gradle.api.Plugin; +import org.gradle.api.Project; + +public class ClusteredServerModule implements Plugin { + + @Override + public void apply(Project project) { + project.setGroup("org.ehcache.modules.clustered"); + + project.getPlugins().apply(DeployConvention.class); + project.getPlugins().apply(VoltronPlugin.class); + } +} diff --git a/build-logic/src/main/java/org/ehcache/build/EhcacheModule.java b/build-logic/src/main/java/org/ehcache/build/EhcacheModule.java new file mode 100644 index 0000000000..dd3aa59140 --- /dev/null +++ b/build-logic/src/main/java/org/ehcache/build/EhcacheModule.java @@ -0,0 +1,17 @@ +package org.ehcache.build; + +import org.ehcache.build.conventions.BndConvention; +import org.ehcache.build.conventions.JavaLibraryConvention; +import org.ehcache.build.conventions.DeployConvention; +import org.gradle.api.Plugin; +import org.gradle.api.Project; + +public abstract class EhcacheModule implements Plugin { + + @Override + public void apply(Project project) { + project.getPlugins().apply(JavaLibraryConvention.class); + project.getPlugins().apply(DeployConvention.class); + project.getPlugins().apply(BndConvention.class); + } +} diff --git a/build-logic/src/main/java/org/ehcache/build/EhcachePackage.java b/build-logic/src/main/java/org/ehcache/build/EhcachePackage.java new file mode 100644 index 0000000000..112fd3d0d2 --- /dev/null +++ b/build-logic/src/main/java/org/ehcache/build/EhcachePackage.java @@ -0,0 +1,16 @@ +package org.ehcache.build; + +import org.ehcache.build.conventions.DeployConvention; +import org.ehcache.build.plugins.PackagePlugin; +import org.gradle.api.Plugin; +import org.gradle.api.Project; + +public class EhcachePackage implements Plugin { + + @Override + public void apply(Project project) { + project.setGroup("org.ehcache"); + project.getPlugins().apply(PackagePlugin.class); + project.getPlugins().apply(DeployConvention.class); + } +} diff --git a/build-logic/src/main/java/org/ehcache/build/InternalEhcacheModule.java b/build-logic/src/main/java/org/ehcache/build/InternalEhcacheModule.java new file mode 100644 index 0000000000..1ba6b69b8a --- /dev/null +++ b/build-logic/src/main/java/org/ehcache/build/InternalEhcacheModule.java @@ -0,0 +1,13 @@ +package org.ehcache.build; + +import org.gradle.api.Project; + +public class InternalEhcacheModule extends EhcacheModule { + + @Override + public void apply(Project project) { + project.setGroup("org.ehcache.modules"); + super.apply(project); + } +} + diff --git a/build-logic/src/main/java/org/ehcache/build/PublicEhcacheModule.java b/build-logic/src/main/java/org/ehcache/build/PublicEhcacheModule.java new file mode 100644 index 0000000000..477c54b3e7 --- /dev/null +++ b/build-logic/src/main/java/org/ehcache/build/PublicEhcacheModule.java @@ -0,0 +1,11 @@ +package org.ehcache.build; + +import org.gradle.api.Project; + +public class PublicEhcacheModule extends EhcacheModule { + @Override + public void apply(Project project) { + project.setGroup("org.ehcache"); + super.apply(project); + } +} diff --git a/build-logic/src/main/java/org/ehcache/build/conventions/BaseConvention.java b/build-logic/src/main/java/org/ehcache/build/conventions/BaseConvention.java new file mode 100644 index 0000000000..edf95548a1 --- /dev/null +++ b/build-logic/src/main/java/org/ehcache/build/conventions/BaseConvention.java @@ -0,0 +1,23 @@ +package org.ehcache.build.conventions; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.artifacts.ResolutionStrategy; +import org.gradle.api.plugins.BasePlugin; + +import java.net.URI; + +public class BaseConvention implements Plugin { + + @Override + public void apply(Project project) { + project.getPlugins().apply(BasePlugin.class); + + project.getRepositories().mavenCentral(); + project.getRepositories().maven(repo -> repo.setUrl(URI.create("https://repo.terracotta.org/maven2"))); + + project.getConfigurations().configureEach( + config -> config.resolutionStrategy(ResolutionStrategy::failOnVersionConflict) + ); + } +} diff --git a/build-logic/src/main/java/org/ehcache/build/conventions/BndConvention.java b/build-logic/src/main/java/org/ehcache/build/conventions/BndConvention.java new file mode 100644 index 0000000000..bbb915168e --- /dev/null +++ b/build-logic/src/main/java/org/ehcache/build/conventions/BndConvention.java @@ -0,0 +1,89 @@ +package org.ehcache.build.conventions; + +import aQute.bnd.gradle.BndBuilderPlugin; +import aQute.bnd.gradle.BundleTaskExtension; +import aQute.bnd.osgi.Constants; +import org.ehcache.build.plugins.osgids.OsgiDsPlugin; +import org.gradle.api.Action; +import org.gradle.api.GradleException; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.artifacts.Dependency; +import org.gradle.api.artifacts.ExternalDependency; +import org.gradle.api.artifacts.ProjectDependency; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.provider.MapProperty; +import org.gradle.api.publish.PublishingExtension; +import org.gradle.api.publish.maven.MavenPublication; +import org.gradle.api.publish.plugins.PublishingPlugin; +import org.gradle.api.tasks.bundling.Jar; + +import static java.lang.System.lineSeparator; +import static java.util.stream.Collectors.joining; + +public class BndConvention implements Plugin { + + @Override + public void apply(Project project) { + project.getPlugins().apply(BndBuilderPlugin.class); + project.getPlugins().apply(OsgiDsPlugin.class); + project.getPlugins().apply(DeployConvention.class); + + project.getTasks().named(JavaPlugin.JAR_TASK_NAME, Jar.class, jar -> { + jar.getExtensions().configure(BundleTaskExtension.class, bundle -> configureBundleDefaults(project, bundle)); + }); + + project.getConfigurations().named("baseline", config -> { + config.getResolutionStrategy().getComponentSelection().all(selection -> { + if (!selection.getCandidate().getVersion().matches("^\\d+(?:\\.\\d+)*$")) { + selection.reject("Only full releases can be used as OSGi baselines"); + } + }); + }); + + String dependencyNotation = project.getGroup() + ":" + project.getName() + ":(," + project.getVersion() + "["; + Dependency baseline = project.getDependencies().add("baseline", dependencyNotation); + if (baseline instanceof ProjectDependency) { + throw new GradleException("Baseline should not be a project dependency"); + } else if (baseline instanceof ExternalDependency) { + ((ExternalDependency) baseline).setForce(true); + ((ExternalDependency) baseline).setTransitive(false); + } else { + throw new IllegalArgumentException("Unexpected dependency type: " + baseline); + } + } + + public static void configureBundleDefaults(Project project, BundleTaskExtension bundle) { + MapProperty defaultInstructions = project.getObjects().mapProperty(String.class, String.class); + bundleDefaults(project).execute(defaultInstructions); + bundle.bnd(defaultInstructions.map(kv -> kv.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(joining(lineSeparator())))); + } + + public static Action> bundleDefaults(Project project) { + return properties -> { + project.getPlugins().withType(PublishingPlugin.class).configureEach(publishingPlugin -> { + project.getExtensions().getByType(PublishingExtension.class).getPublications().withType(MavenPublication.class).stream().findAny().ifPresent(publication -> { + properties.put(Constants.BUNDLE_NAME, publication.getPom().getName()); + properties.put(Constants.BUNDLE_DESCRIPTION, publication.getPom().getDescription()); + }); + }); + properties.put(Constants.BUNDLE_SYMBOLICNAME, project.getGroup() + "." + project.getName()); + properties.put(Constants.BUNDLE_DOCURL, "http://ehcache.org"); + properties.put(Constants.BUNDLE_LICENSE, "LICENSE"); + properties.put(Constants.BUNDLE_VENDOR, "Terracotta Inc., a wholly-owned subsidiary of Software AG USA, Inc."); + properties.put(Constants.BUNDLE_VERSION, osgiFixedVersion(project.getVersion().toString())); + properties.put(Constants.SERVICE_COMPONENT, "OSGI-INF/*.xml"); + }; + } + + public static String osgiFixedVersion(String version) { + /* + * The bnd gradle plugin does not handle our 2-digit snapshot versioning scheme very well. It maps `x.y-SNAPSHOT` + * to `x.y.0.SNAPSHOT`. This is bad since `x.y.0.SNAPSHOT` is considered to be less than *all* `x.y.z`. This means + * the baseline version range expression `(,x.y.0.SNAPSHOT[` will always pick the last release from the previous + * minor line. To fix this we manually map to a 3-digit snapshot version where the 3rd digit is a number chosen + * to be higher than we would ever release ('All the worlds a VAX'). + */ + return version.replaceAll("^(\\d+.\\d+)-SNAPSHOT$", "$1.999-SNAPSHOT"); + } +} diff --git a/build-logic/src/main/java/org/ehcache/build/conventions/CheckstyleConvention.java b/build-logic/src/main/java/org/ehcache/build/conventions/CheckstyleConvention.java new file mode 100644 index 0000000000..0973fc1237 --- /dev/null +++ b/build-logic/src/main/java/org/ehcache/build/conventions/CheckstyleConvention.java @@ -0,0 +1,22 @@ +package org.ehcache.build.conventions; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.plugins.quality.CheckstyleExtension; +import org.gradle.api.plugins.quality.CheckstylePlugin; + +import java.util.Map; + +public class CheckstyleConvention implements Plugin { + @Override + public void apply(Project project) { + project.getPlugins().apply(CheckstylePlugin.class); + + project.getExtensions().configure(CheckstyleExtension.class, checkstyle -> { + checkstyle.setConfigFile(project.getRootProject().file("config/checkstyle.xml")); + Map properties = checkstyle.getConfigProperties(); + properties.put("projectDir", project.getProjectDir()); + properties.put("rootDir", project.getRootDir()); + }); + } +} diff --git a/build-logic/src/main/java/org/ehcache/build/conventions/DeployConvention.java b/build-logic/src/main/java/org/ehcache/build/conventions/DeployConvention.java new file mode 100644 index 0000000000..bd7376ef8b --- /dev/null +++ b/build-logic/src/main/java/org/ehcache/build/conventions/DeployConvention.java @@ -0,0 +1,167 @@ +package org.ehcache.build.conventions; + +import org.gradle.api.Action; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.Task; +import org.gradle.api.artifacts.ProjectDependency; +import org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectComponentPublication; +import org.gradle.api.internal.component.SoftwareComponentInternal; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.plugins.JavaPluginExtension; +import org.gradle.api.publish.PublishingExtension; +import org.gradle.api.publish.maven.MavenPublication; +import org.gradle.api.publish.maven.internal.publication.MavenPomInternal; +import org.gradle.api.publish.maven.internal.publisher.MavenProjectIdentity; +import org.gradle.api.publish.maven.plugins.MavenPublishPlugin; +import org.gradle.api.publish.maven.tasks.AbstractPublishToMaven; +import org.gradle.api.publish.maven.tasks.GenerateMavenPom; +import org.gradle.api.publish.maven.tasks.PublishToMavenRepository; +import org.gradle.api.tasks.TaskProvider; +import org.gradle.api.tasks.WriteProperties; +import org.gradle.jvm.tasks.Jar; +import org.gradle.plugins.signing.SigningExtension; +import org.gradle.plugins.signing.SigningPlugin; + +import java.io.File; +import java.util.Collection; +import java.util.concurrent.Callable; +import java.util.function.Predicate; + +import static java.util.stream.Collectors.toList; +import static org.gradle.api.publish.plugins.PublishingPlugin.PUBLISH_TASK_GROUP; + +/** + * Deploy plugin for published artifacts. This is an abstraction over the {@code maven-publish} plugin. + * + * Defaults: + *
    + *
  • POM: population of general content: organization, issue-management, scm, etc.
  • + *
  • POM copied to {@code META-INF/maven/groupId/artifactId/pom.xml}
  • + *
  • POM properties file copied to {@code META-INF/maven/groupId/artifactId/pom.properties}
  • + *
  • Javadoc and Source JAR Publishing
  • + *
  • {@code install} as alias of {@code publishToMavenLocal}
  • + *
+ */ +public class DeployConvention implements Plugin { + + private static final Predicate IS_RELEASE = p -> !p.getVersion().toString().endsWith("-SNAPSHOT"); + + @Override + public void apply(Project project) { + project.getPlugins().apply(SigningPlugin.class); + project.getPlugins().apply(MavenPublishPlugin.class); + + project.getExtensions().configure(PublishingExtension.class, publishing -> { + publishing.getPublications().withType(MavenPublication.class).configureEach(mavenPublication -> mavenPublication.pom(pom -> { + pom.getUrl().set("http://ehcache.org"); + pom.organization(org -> { + org.getName().set("Terracotta Inc., a wholly-owned subsidiary of Software AG USA, Inc."); + org.getUrl().set("http://terracotta.org"); + }); + pom.issueManagement(issue -> { + issue.getSystem().set("Github"); + issue.getUrl().set("https://github.com/ehcache/ehcache3/issues"); + }); + pom.scm(scm -> { + scm.getUrl().set("https://github.com/ehcache/ehcache3"); + scm.getConnection().set("scm:git:https://github.com/ehcache/ehcache3.git"); + scm.getDeveloperConnection().set("scm:git:git@github.com:ehcache/ehcache3.git"); + }); + pom.licenses(licenses -> licenses.license(license -> { + license.getName().set("The Apache Software License, Version 2.0"); + license.getUrl().set("http://www.apache.org/licenses/LICENSE-2.0.txt"); + license.getDistribution().set("repo"); + })); + pom.developers(devs -> devs.developer(dev -> { + dev.getName().set("Terracotta Engineers"); + dev.getEmail().set("tc-oss@softwareag.com"); + dev.getOrganization().set("Terracotta Inc., a wholly-owned subsidiary of Software AG USA, Inc."); + dev.getOrganizationUrl().set("http://ehcache.org"); + })); + })); + publishing.repositories(repositories -> repositories.maven(maven -> { + if (IS_RELEASE.test(project)) { + maven.setUrl(project.property("deployUrl")); + maven.credentials(creds -> { + creds.setUsername(project.property("deployUser").toString()); + creds.setPassword(project.property("deployPwd").toString()); + }); + } else { + maven.setName("sonatype-nexus-snapshot"); + maven.setUrl("https://oss.sonatype.org/content/repositories/snapshots"); + maven.credentials(creds -> { + creds.setUsername(project.property("sonatypeUser").toString()); + creds.setPassword(project.property("sonatypePwd").toString()); + }); + } + })); + }); + + project.getExtensions().configure(SigningExtension.class, signing -> { + signing.setRequired((Callable) () -> IS_RELEASE.test(project) && project.getGradle().getTaskGraph().getAllTasks().stream().anyMatch(t -> t instanceof PublishToMavenRepository)); + signing.sign(project.getExtensions().getByType(PublishingExtension.class).getPublications()); + }); + + /* + * Do **not** convert the anonymous Action here to a lambda expression - it will break Gradle's up-to-date tracking + * and cause tasks to be needlessly rerun. + */ + //noinspection Convert2Lambda + project.getTasks().withType(AbstractPublishToMaven.class).configureEach(publishTask -> publishTask.doFirst(new Action() { + @Override + public void execute(Task task) { + MavenPublication publication = publishTask.getPublication(); + if (publication instanceof ProjectComponentPublication) { + SoftwareComponentInternal component = ((ProjectComponentPublication) publication).getComponent(); + if (component != null) { //The shadow plugin doesn"t associate a component with the publication + Collection unpublishedDeps = component.getUsages().stream().flatMap(usage -> + usage.getDependencies().stream().filter(ProjectDependency.class::isInstance).map(ProjectDependency.class::cast).filter(moduleDependency -> + !moduleDependency.getDependencyProject().getPlugins().hasPlugin(DeployConvention.class))).collect(toList()); + if (!unpublishedDeps.isEmpty()) { + project.getLogger().warn("{} has applied the deploy plugin but has unpublished project dependencies: {}", project, unpublishedDeps); + } + } + } + } + })); + + project.getTasks().register("install", task -> + task.dependsOn(project.getTasks().named(MavenPublishPlugin.PUBLISH_LOCAL_LIFECYCLE_TASK_NAME)) + ); + + project.getPlugins().withType(JavaPlugin.class).configureEach(plugin -> { + project.getExtensions().configure(JavaPluginExtension.class, java -> { + java.withJavadocJar(); + java.withSourcesJar(); + }); + + project.afterEvaluate(p -> { + p.getExtensions().configure(PublishingExtension.class, publishing -> { + if (publishing.getPublications().isEmpty()) { + publishing.publications(publications -> publications.register("mavenJava", MavenPublication.class, mavenJava -> mavenJava.from(p.getComponents().getByName("java")))); + } + }); + + p.getTasks().withType(GenerateMavenPom.class).all(pomTask -> { + MavenProjectIdentity identity = ((MavenPomInternal) pomTask.getPom()).getProjectIdentity(); + TaskProvider pomPropertiesTask = project.getTasks().register(pomTask.getName().replace("PomFile", "PomProperties"), WriteProperties.class, task -> { + task.dependsOn(pomTask); + task.setGroup(PUBLISH_TASK_GROUP); + task.setOutputFile(new File(pomTask.getDestination().getParentFile(), "pom.properties")); + task.property("groupId", identity.getGroupId()); + task.property("artifactId", identity.getArtifactId()); + task.property("version", identity.getVersion()); + }); + + project.getTasks().withType(Jar.class).configureEach(jar -> { + jar.into("META-INF/maven/" + identity.getGroupId().get() + "/" + identity.getArtifactId().get(), spec -> { + spec.from(pomTask, pom -> pom.rename(".*", "pom.xml")); + spec.from(pomPropertiesTask); + }); + }); + }); + }); + }); + } +} diff --git a/build-logic/src/main/java/org/ehcache/build/conventions/JacocoConvention.java b/build-logic/src/main/java/org/ehcache/build/conventions/JacocoConvention.java new file mode 100644 index 0000000000..66f96ef814 --- /dev/null +++ b/build-logic/src/main/java/org/ehcache/build/conventions/JacocoConvention.java @@ -0,0 +1,28 @@ +package org.ehcache.build.conventions; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.tasks.testing.Test; +import org.gradle.testing.jacoco.plugins.JacocoPlugin; +import org.gradle.testing.jacoco.plugins.JacocoTaskExtension; +import org.gradle.testing.jacoco.tasks.JacocoReport; + +public class JacocoConvention implements Plugin { + + @Override + public void apply(Project project) { + project.getPlugins().apply(JacocoPlugin.class); + + project.getTasks().withType(JacocoReport.class).configureEach(jacocoReport -> { + jacocoReport.getReports().configureEach(report -> { + report.getRequired().set(false); + }); + }); + + project.getTasks().withType(Test.class).configureEach(test -> { + test.getExtensions().configure(JacocoTaskExtension.class, jacoco -> { + jacoco.getExcludes().add("org.terracotta.tripwire.*"); + }); + }); + } +} diff --git a/build-logic/src/main/java/org/ehcache/build/conventions/JavaBaseConvention.java b/build-logic/src/main/java/org/ehcache/build/conventions/JavaBaseConvention.java new file mode 100644 index 0000000000..83ce040a2c --- /dev/null +++ b/build-logic/src/main/java/org/ehcache/build/conventions/JavaBaseConvention.java @@ -0,0 +1,131 @@ +package org.ehcache.build.conventions; + +import org.gradle.api.JavaVersion; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.java.archives.Attributes; +import org.gradle.api.plugins.JavaBasePlugin; +import org.gradle.api.plugins.JavaPluginExtension; +import org.gradle.api.tasks.bundling.Jar; +import org.gradle.api.tasks.compile.JavaCompile; +import org.gradle.api.tasks.javadoc.Javadoc; +import org.gradle.api.tasks.testing.Test; +import org.gradle.external.javadoc.CoreJavadocOptions; +import org.gradle.internal.jvm.JavaInfo; +import org.gradle.internal.jvm.Jvm; +import org.gradle.process.internal.ExecException; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.OutputStream; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static java.util.Arrays.asList; + +public class JavaBaseConvention implements Plugin { + @Override + public void apply(Project project) { + project.getPlugins().apply(JavaBasePlugin.class); + project.getPlugins().apply(BaseConvention.class); + + JavaInfo testJava = fetchTestJava(project); + project.getExtensions().getExtraProperties().set("testJava", testJava); + + project.getExtensions().configure(JavaPluginExtension.class, java -> { + java.setSourceCompatibility(JavaVersion.VERSION_1_8); + java.setTargetCompatibility(JavaVersion.VERSION_1_8); + }); + + project.getTasks().withType(Jar.class).configureEach(jar -> { + jar.manifest(manifest -> { + Attributes attributes = manifest.getAttributes(); + attributes.put("Implementation-Title", project.getName()); + attributes.put("Implementation-Vendor-Id", project.getGroup()); + attributes.put("Implementation-Version", project.getVersion()); + attributes.put("Implementation-Revision", getRevision(project)); + attributes.put("Built-By", System.getProperty("user.name")); + attributes.put("Built-JDK", System.getProperty("java.version")); + }); + jar.from(project.getRootProject().file("LICENSE")); + }); + + project.getTasks().withType(Test.class).configureEach(test -> { + test.setExecutable(testJava.getJavaExecutable()); + test.setMaxHeapSize("256m"); + test.setMaxParallelForks(16); + test.systemProperty("java.awt.headless", "true"); + }); + + project.getTasks().withType(JavaCompile.class).configureEach(compile -> { + compile.getOptions().setEncoding("UTF-8"); + compile.getOptions().setCompilerArgs(asList("-Werror", "-Xlint:all")); + }); + + project.getTasks().withType(Javadoc.class).configureEach(javadoc -> { + javadoc.setTitle(project.getName() + " " + project.getVersion() + " API"); + javadoc.exclude("**/internal/**"); + javadoc.getOptions().setEncoding("UTF-8"); + ((CoreJavadocOptions) javadoc.getOptions()).addStringOption("Xdoclint:none", "-quiet"); + }); + } + + private static JavaInfo fetchTestJava(Project project) { + Object testVM = project.findProperty("testVM"); + if (testVM == null) { + return Jvm.current(); + } else { + File jvmHome = project.file(testVM); + if (!jvmHome.exists() && project.hasProperty(testVM.toString())) { + testVM = project.property(testVM.toString()); + jvmHome = project.file(testVM); + } + + return jvmForHome(project, jvmHome); + } + } + + private static final Pattern VERSION_OUTPUT = Pattern.compile("\\w+ version \"(?.+)\""); + private static Jvm jvmForHome(Project project, File home) { + File java = Jvm.forHome(home).getJavaExecutable(); + + OutputStream stdout = new ByteArrayOutputStream(); + OutputStream stderr = new ByteArrayOutputStream(); + project.exec(spec -> { + spec.executable(java); + spec.args("-version"); + spec.setStandardOutput(stdout); + spec.setErrorOutput(stderr); + }); + String versionOutput = stderr.toString(); + Matcher matcher = VERSION_OUTPUT.matcher(versionOutput); + if (matcher.find()) { + return Jvm.discovered(home, null, JavaVersion.toVersion(matcher.group("version"))); + } else { + throw new IllegalArgumentException("Could not parse version of " + java + " from output:\n" + versionOutput); + } + } + + + private static Object getRevision(Project project) { + String envCommit = System.getenv("GIT_COMMIT"); + if(envCommit != null) { + return envCommit; + } else { + try { + ByteArrayOutputStream stdout = new ByteArrayOutputStream(); + ByteArrayOutputStream stderr = new ByteArrayOutputStream(); + project.exec(spec -> { + spec.executable("git"); + spec.args("rev-parse", "HEAD"); + spec.setStandardOutput(stdout); + spec.setErrorOutput(stderr); + }).assertNormalExitValue(); + + return stdout.toString().trim(); + } catch (ExecException e) { + return "Unknown"; + } + } + } +} diff --git a/build-logic/src/main/java/org/ehcache/build/conventions/JavaConvention.java b/build-logic/src/main/java/org/ehcache/build/conventions/JavaConvention.java new file mode 100644 index 0000000000..5e50b677ae --- /dev/null +++ b/build-logic/src/main/java/org/ehcache/build/conventions/JavaConvention.java @@ -0,0 +1,27 @@ +package org.ehcache.build.conventions; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.artifacts.dsl.DependencyHandler; +import org.gradle.api.plugins.JavaPlugin; + +public class JavaConvention implements Plugin { + @Override + public void apply(Project project) { + project.getPlugins().apply(JavaBaseConvention.class); + project.getPlugins().apply(JavaPlugin.class); + project.getPlugins().apply(CheckstyleConvention.class); + project.getPlugins().apply(JacocoConvention.class); + project.getPlugins().apply(SpotbugsConvention.class); + + DependencyHandler dependencies = project.getDependencies(); + dependencies.add(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME, "org.slf4j:slf4j-api:" + project.property("slf4jVersion")); + dependencies.add(JavaPlugin.TEST_RUNTIME_ONLY_CONFIGURATION_NAME, "org.slf4j:slf4j-simple:" + project.property("slf4jVersion")); + + dependencies.add(JavaPlugin.TEST_IMPLEMENTATION_CONFIGURATION_NAME, "junit:junit:" + project.property("junitVersion")); + dependencies.add(JavaPlugin.TEST_IMPLEMENTATION_CONFIGURATION_NAME, "org.assertj:assertj-core:" + project.property("assertjVersion")); + dependencies.add(JavaPlugin.TEST_IMPLEMENTATION_CONFIGURATION_NAME, "org.hamcrest:hamcrest-library:" + project.property("hamcrestVersion")); + dependencies.add(JavaPlugin.TEST_IMPLEMENTATION_CONFIGURATION_NAME, "org.mockito:mockito-core:" + project.property("mockitoVersion")); + dependencies.add(JavaPlugin.TEST_IMPLEMENTATION_CONFIGURATION_NAME, "org.terracotta:terracotta-utilities-test-tools:" + project.property("terracottaUtilitiesVersion")); + } +} diff --git a/build-logic/src/main/java/org/ehcache/build/conventions/JavaLibraryConvention.java b/build-logic/src/main/java/org/ehcache/build/conventions/JavaLibraryConvention.java new file mode 100644 index 0000000000..77b15e4b37 --- /dev/null +++ b/build-logic/src/main/java/org/ehcache/build/conventions/JavaLibraryConvention.java @@ -0,0 +1,14 @@ +package org.ehcache.build.conventions; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.plugins.JavaLibraryPlugin; + +public class JavaLibraryConvention implements Plugin { + + @Override + public void apply(Project project) { + project.getPlugins().apply(JavaConvention.class); + project.getPlugins().apply(JavaLibraryPlugin.class); + } +} diff --git a/build-logic/src/main/java/org/ehcache/build/conventions/SpotbugsConvention.java b/build-logic/src/main/java/org/ehcache/build/conventions/SpotbugsConvention.java new file mode 100644 index 0000000000..9815385453 --- /dev/null +++ b/build-logic/src/main/java/org/ehcache/build/conventions/SpotbugsConvention.java @@ -0,0 +1,53 @@ +package org.ehcache.build.conventions; + +import com.github.spotbugs.snom.SpotBugsExtension; +import com.github.spotbugs.snom.SpotBugsPlugin; +import com.github.spotbugs.snom.SpotBugsTask; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.plugins.JavaBasePlugin; +import org.gradle.api.plugins.JavaPluginExtension; + +public class SpotbugsConvention implements Plugin { + + @Override + public void apply(Project project) { + project.getPlugins().apply(SpotBugsPlugin.class); + + SpotBugsExtension spotbugs = project.getExtensions().getByType(SpotBugsExtension.class); + + spotbugs.getIgnoreFailures().set(false); + // Later versions of Spotbugs have stupid heuristics for EI_EXPOSE_REP* + spotbugs.getToolVersion().set("4.2.3"); + + project.getPlugins().withType(JavaBasePlugin.class).configureEach(plugin -> { + + project.getExtensions().configure(JavaPluginExtension.class, java -> { + java.getSourceSets().configureEach(sourceSet -> { + project.getDependencies().add(sourceSet.getCompileOnlyConfigurationName(), + "com.github.spotbugs:spotbugs-annotations:" + spotbugs.getToolVersion().get()); + }); + + project.getTasks().withType(SpotBugsTask.class).configureEach(task -> { + if (task.getName().contains("Test")) { + task.setEnabled(false); + } else { + task.getReports().register("xml", report -> report.setEnabled(true)); + task.getReports().register("html", report -> report.setEnabled(false)); + } + }); + }); + + }); + + + project.getConfigurations().named("spotbugs", config -> { + config.getResolutionStrategy().dependencySubstitution(subs -> { + subs.substitute(subs.module("org.apache.commons:commons-lang3:3.11")) + .using(subs.module("org.apache.commons:commons-lang3:3.12.0")) + .because("Spotbugs has dependency divergences"); + }); + }); + + } +} diff --git a/build-logic/src/main/java/org/ehcache/build/conventions/WarConvention.java b/build-logic/src/main/java/org/ehcache/build/conventions/WarConvention.java new file mode 100644 index 0000000000..ec469eda6a --- /dev/null +++ b/build-logic/src/main/java/org/ehcache/build/conventions/WarConvention.java @@ -0,0 +1,13 @@ +package org.ehcache.build.conventions; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.plugins.WarPlugin; + +public class WarConvention implements Plugin { + @Override + public void apply(Project project) { + project.getPlugins().apply(WarPlugin.class); + project.getPlugins().apply(JavaConvention.class); + } +} diff --git a/build-logic/src/main/java/org/ehcache/build/plugins/PackagePlugin.java b/build-logic/src/main/java/org/ehcache/build/plugins/PackagePlugin.java new file mode 100644 index 0000000000..40070ea348 --- /dev/null +++ b/build-logic/src/main/java/org/ehcache/build/plugins/PackagePlugin.java @@ -0,0 +1,285 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.build.plugins; + +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar; +import org.ehcache.build.conventions.BaseConvention; +import org.ehcache.build.conventions.BndConvention; +import org.ehcache.build.conventions.JavaBaseConvention; +import org.ehcache.build.util.OsgiManifestJarExtension; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.capabilities.Capability; +import org.gradle.api.component.AdhocComponentWithVariants; +import org.gradle.api.component.SoftwareComponentFactory; +import org.gradle.api.file.DuplicatesStrategy; +import org.gradle.api.file.FileCollection; +import org.gradle.api.file.FileTree; +import org.gradle.api.internal.project.ProjectInternal; +import org.gradle.api.plugins.BasePlugin; +import org.gradle.api.plugins.JavaBasePlugin; +import org.gradle.api.plugins.jvm.internal.JvmPluginServices; +import org.gradle.api.provider.Provider; +import org.gradle.api.publish.PublishingExtension; +import org.gradle.api.publish.maven.MavenPublication; +import org.gradle.api.publish.maven.plugins.MavenPublishPlugin; +import org.gradle.api.tasks.Sync; +import org.gradle.api.tasks.TaskProvider; +import org.gradle.api.tasks.bundling.Jar; +import org.gradle.api.tasks.bundling.Zip; +import org.gradle.api.tasks.javadoc.Javadoc; +import org.gradle.internal.jvm.Jvm; +import org.gradle.internal.resolve.ArtifactResolveException; +import org.gradle.internal.service.ServiceRegistry; +import org.gradle.language.base.plugins.LifecycleBasePlugin; + +import java.io.File; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import static java.lang.Integer.parseInt; +import static java.util.Collections.emptyList; +import static org.ehcache.build.plugins.VariantPlugin.COMMON_SOURCE_SET_NAME; +import static org.ehcache.build.util.PluginUtils.bucket; +import static org.ehcache.build.util.PluginUtils.createBucket; +import static org.ehcache.build.util.PluginUtils.capitalize; +import static org.gradle.api.attributes.DocsType.JAVADOC; +import static org.gradle.api.attributes.DocsType.SOURCES; +import static org.gradle.api.attributes.DocsType.USER_MANUAL; +import static org.gradle.api.internal.artifacts.JavaEcosystemSupport.configureDefaultTargetPlatform; +import static org.gradle.api.plugins.JavaPlugin.API_CONFIGURATION_NAME; +import static org.gradle.api.plugins.JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME; +import static org.gradle.api.plugins.JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME; +import static org.gradle.api.plugins.JavaPlugin.COMPILE_ONLY_API_CONFIGURATION_NAME; +import static org.gradle.api.plugins.JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME; +import static org.gradle.api.plugins.JavaPlugin.JAVADOC_ELEMENTS_CONFIGURATION_NAME; +import static org.gradle.api.plugins.JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME; +import static org.gradle.api.plugins.JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME; +import static org.gradle.api.plugins.JavaPlugin.RUNTIME_ONLY_CONFIGURATION_NAME; +import static org.gradle.api.plugins.JavaPlugin.SOURCES_ELEMENTS_CONFIGURATION_NAME; + +/** + * EhDistribute + */ +public class PackagePlugin implements Plugin { + + private static final String CONTENTS_CONFIGURATION_NAME = "contents"; + + @Override + public void apply(Project project) { + project.getPlugins().apply(BaseConvention.class); + project.getPlugins().apply(JavaBaseConvention.class); + + ServiceRegistry projectServices = ((ProjectInternal) project).getServices(); + JvmPluginServices jvmPluginServices = projectServices.get(JvmPluginServices.class); + SoftwareComponentFactory softwareComponentFactory = projectServices.get(SoftwareComponentFactory.class); + AdhocComponentWithVariants javaComponent = softwareComponentFactory.adhoc("java"); + project.getComponents().add(javaComponent); + + TaskProvider asciidocZip = project.getTasks().register("asciidocZip", Zip.class, zip -> { + zip.getArchiveClassifier().set("docs"); + zip.from(project.getTasks().getByPath(":docs:userDoc")); + }); + Configuration userdocElements = jvmPluginServices.createOutgoingElements("userdocElements", builder -> + builder.published().artifact(asciidocZip).providesAttributes(attributes -> attributes.documentation(USER_MANUAL))); + javaComponent.addVariantsFromConfiguration(userdocElements, variantDetails -> {}); + + createDefaultPackage(project); + + project.getPlugins().withType(VariantPlugin.class).configureEach(plugin -> { + Configuration commonContents = createBucket(project, CONTENTS_CONFIGURATION_NAME, COMMON_SOURCE_SET_NAME); + Configuration commonApi = createBucket(project, API_CONFIGURATION_NAME, COMMON_SOURCE_SET_NAME); + Configuration commonImplementation = createBucket(project, IMPLEMENTATION_CONFIGURATION_NAME, COMMON_SOURCE_SET_NAME).extendsFrom(commonApi); + Configuration commonCompileOnlyApi = createBucket(project, COMPILE_ONLY_API_CONFIGURATION_NAME, COMMON_SOURCE_SET_NAME); + Configuration commonRuntimeOnly = createBucket(project, RUNTIME_ONLY_CONFIGURATION_NAME, COMMON_SOURCE_SET_NAME); + + project.getConfigurations().named(CONTENTS_CONFIGURATION_NAME).configure(conf -> conf.extendsFrom(commonContents)); + project.getConfigurations().named(API_CONFIGURATION_NAME).configure(conf -> conf.extendsFrom(commonApi)); + project.getConfigurations().named(IMPLEMENTATION_CONFIGURATION_NAME).configure(conf -> conf.extendsFrom(commonImplementation)); + project.getConfigurations().named(COMPILE_ONLY_API_CONFIGURATION_NAME).configure(conf -> conf.extendsFrom(commonCompileOnlyApi)); + project.getConfigurations().named(RUNTIME_ONLY_CONFIGURATION_NAME).configure(conf -> conf.extendsFrom(commonRuntimeOnly)); + + project.getExtensions().configure(VariantPlugin.VariantExtension.class, variants -> { + variants.getVariants().configureEach(variant -> { + createPackage(project, variant.getName(), variant.getCapabilities().get()); + + bucket(project, CONTENTS_CONFIGURATION_NAME, variant.getName()).extendsFrom(commonContents); + bucket(project, API_CONFIGURATION_NAME, variant.getName()).extendsFrom(commonApi); + bucket(project, IMPLEMENTATION_CONFIGURATION_NAME, variant.getName()).extendsFrom(commonImplementation); + bucket(project, COMPILE_ONLY_API_CONFIGURATION_NAME, variant.getName()).extendsFrom(commonCompileOnlyApi); + bucket(project, RUNTIME_ONLY_CONFIGURATION_NAME, variant.getName()).extendsFrom(commonRuntimeOnly); + }); + }); + }); + + project.getPlugins().withType(MavenPublishPlugin.class).configureEach(plugin -> { + project.getExtensions().configure(PublishingExtension.class, publishing -> { + publishing.getPublications().register("mavenJava", MavenPublication.class, mavenPublication -> { + mavenPublication.from(javaComponent); + }); + }); + }); + } + + private void createDefaultPackage(Project project) { + createPackage(project, null, emptyList()); + } + + private void createPackage(Project project, String variant, List capabilities) { + ServiceRegistry projectServices = ((ProjectInternal) project).getServices(); + JvmPluginServices jvmPluginServices = projectServices.get(JvmPluginServices.class); + + Configuration contents = createBucket(project, CONTENTS_CONFIGURATION_NAME, variant); + + Configuration contentsRuntimeElements = jvmPluginServices.createResolvableConfiguration(camelPrefix(variant, "contentsRuntimeElements"), builder -> + builder.extendsFrom(contents).requiresJavaLibrariesRuntime()); + + Configuration contentSourcesElements = jvmPluginServices.createResolvableConfiguration(camelPrefix(variant, "contentsSourcesElements"), builder -> + builder.extendsFrom(contents).requiresAttributes(refiner -> refiner.documentation(SOURCES))); + + TaskProvider shadowJar = project.getTasks().register(camelPrefix(variant, "jar"), ShadowJar.class, shadow -> { + shadow.setGroup(BasePlugin.BUILD_GROUP); + shadow.getArchiveClassifier().set(variant); + + shadow.setConfigurations(Collections.singletonList(contentsRuntimeElements)); + shadow.relocate("org.terracotta.statistics.", "org.ehcache.shadow.org.terracotta.statistics."); + shadow.relocate("org.terracotta.offheapstore.", "org.ehcache.shadow.org.terracotta.offheapstore."); + shadow.relocate("org.terracotta.context.", "org.ehcache.shadow.org.terracotta.context."); + shadow.relocate("org.terracotta.utilities.", "org.ehcache.shadow.org.terracotta.utilities."); + + shadow.mergeServiceFiles(); + + shadow.exclude("META-INF/MANIFEST.MF", "LICENSE", "NOTICE"); + + // LICENSE is included in root gradle build + shadow.from(new File(project.getRootDir(), "NOTICE")); + shadow.setDuplicatesStrategy(DuplicatesStrategy.EXCLUDE); + }); + + Provider sourcesTree = project.provider(() -> contentSourcesElements.getResolvedConfiguration().getLenientConfiguration().getAllModuleDependencies().stream().flatMap(d -> d.getModuleArtifacts().stream()) + .map(artifact -> { + try { + return Optional.of(artifact.getFile()); + } catch (ArtifactResolveException e) { + return Optional.empty(); + } + }).filter(Optional::isPresent).map(Optional::get).distinct().map(file -> { + if (file.isFile()) { + return project.zipTree(file); + } else { + return project.fileTree(file); + } + }).reduce(FileTree::plus).orElse(project.files().getAsFileTree())); + + TaskProvider sources = project.getTasks().register(camelPrefix(variant, "sources"), Sync.class, sync -> { + sync.dependsOn(contentSourcesElements); + sync.from(sourcesTree, spec -> spec.exclude("META-INF/**", "LICENSE", "NOTICE")); + sync.into(project.getLayout().getBuildDirectory().dir(camelPrefix(variant,"sources"))); + }); + + TaskProvider sourcesJar = project.getTasks().register(camelPrefix(variant, "sourcesJar"), Jar.class, jar -> { + jar.setGroup(BasePlugin.BUILD_GROUP); + jar.from(sources); + jar.from(shadowJar, spec -> spec.include("META-INF/**", "LICENSE", "NOTICE")); + jar.getArchiveClassifier().set(kebabPrefix(variant, "sources")); + }); + + TaskProvider javadoc = project.getTasks().register(camelPrefix(variant, "javadoc"), Javadoc.class, task -> { + task.setGroup(JavaBasePlugin.DOCUMENTATION_GROUP); + task.setTitle(project.getName() + " " + project.getVersion() + " API"); + task.source(sources); + task.include("*.java"); + task.setClasspath(contentsRuntimeElements); + task.setDestinationDir(new File(project.getBuildDir(), "docs/" + camelPrefix(variant, "javadoc"))); + }); + TaskProvider javadocJar = project.getTasks().register(camelPrefix(variant, "javadocJar"), Jar.class, jar -> { + jar.setGroup(BasePlugin.BUILD_GROUP); + jar.from(javadoc); + jar.getArchiveClassifier().set(kebabPrefix(variant, "javadoc")); + }); + + Configuration api = createBucket(project, API_CONFIGURATION_NAME, variant); + Configuration implementation = createBucket(project, IMPLEMENTATION_CONFIGURATION_NAME, variant).extendsFrom(api); + Configuration compileOnlyApi = createBucket(project, COMPILE_ONLY_API_CONFIGURATION_NAME, variant); + Configuration runtimeOnly = createBucket(project, RUNTIME_ONLY_CONFIGURATION_NAME, variant); + + Configuration apiElements = jvmPluginServices.createOutgoingElements(camelPrefix(variant, API_ELEMENTS_CONFIGURATION_NAME), builder -> + builder.extendsFrom(api, compileOnlyApi).published().providesApi().withCapabilities(capabilities).artifact(shadowJar)); + configureDefaultTargetPlatform(apiElements, parseInt(Jvm.current().getJavaVersion().getMajorVersion())); + Configuration compileClasspath = jvmPluginServices.createResolvableConfiguration(camelPrefix(variant, COMPILE_CLASSPATH_CONFIGURATION_NAME), builder -> + builder.extendsFrom(apiElements).requiresJavaLibrariesRuntime()); + Configuration runtimeElements = jvmPluginServices.createOutgoingElements(camelPrefix(variant, RUNTIME_ELEMENTS_CONFIGURATION_NAME), builder -> + builder.extendsFrom(implementation, runtimeOnly).published().providesRuntime().withCapabilities(capabilities).artifact(shadowJar)); + configureDefaultTargetPlatform(runtimeElements, parseInt(Jvm.current().getJavaVersion().getMajorVersion())); + Configuration runtimeClasspath = jvmPluginServices.createResolvableConfiguration(camelPrefix(variant, RUNTIME_CLASSPATH_CONFIGURATION_NAME), builder -> + builder.extendsFrom(runtimeElements).requiresJavaLibrariesRuntime()); + + Configuration sourcesElements = jvmPluginServices.createOutgoingElements(camelPrefix(variant, SOURCES_ELEMENTS_CONFIGURATION_NAME), builder -> + builder.published().artifact(sourcesJar).withCapabilities(capabilities).providesAttributes(attributes -> attributes.documentation(SOURCES).asJar())); + Configuration javadocElements = jvmPluginServices.createOutgoingElements(camelPrefix(variant, JAVADOC_ELEMENTS_CONFIGURATION_NAME), builder -> + builder.published().artifact(javadocJar).withCapabilities(capabilities).providesAttributes(attributes -> attributes.documentation(JAVADOC).asJar())); + + shadowJar.configure(shadow -> { + OsgiManifestJarExtension osgiExtension = new OsgiManifestJarExtension(shadow); + osgiExtension.getClasspath().from(runtimeClasspath); + osgiExtension.getSources().from(sources); + BndConvention.bundleDefaults(project).execute(osgiExtension.getInstructions()); + }); + + project.getComponents().named("java", AdhocComponentWithVariants.class, java -> { + java.addVariantsFromConfiguration(apiElements, variantDetails -> { + variantDetails.mapToMavenScope("compile"); + if (variant != null) { + variantDetails.mapToOptional(); + } + }); + java.addVariantsFromConfiguration(runtimeElements, variantDetails -> { + variantDetails.mapToMavenScope("runtime"); + if (variant != null) { + variantDetails.mapToOptional(); + } + }); + java.addVariantsFromConfiguration(sourcesElements, variantDetails -> {}); + java.addVariantsFromConfiguration(javadocElements, variantDetails -> {}); + }); + + + project.getTasks().named(LifecycleBasePlugin.ASSEMBLE_TASK_NAME).configure(task -> { + task.dependsOn(shadowJar); + task.dependsOn(javadocJar); + task.dependsOn(sourcesJar); + }); + } + + private static String camelPrefix(String variant, String thing) { + if (variant == null) { + return thing; + } else { + return variant + capitalize(thing); + } + } + + private static String kebabPrefix(String variant, String thing) { + if (variant == null) { + return thing; + } else { + return variant + "-" + thing; + } + } +} diff --git a/build-logic/src/main/java/org/ehcache/build/plugins/VariantPlugin.java b/build-logic/src/main/java/org/ehcache/build/plugins/VariantPlugin.java new file mode 100644 index 0000000000..8cc14e802b --- /dev/null +++ b/build-logic/src/main/java/org/ehcache/build/plugins/VariantPlugin.java @@ -0,0 +1,228 @@ +package org.ehcache.build.plugins; + +import aQute.bnd.gradle.BndBuilderPlugin; +import aQute.bnd.gradle.BundleTaskExtension; +import org.ehcache.build.conventions.BndConvention; +import org.ehcache.build.util.PluginUtils; +import org.gradle.api.Action; +import org.gradle.api.NamedDomainObjectContainer; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.capabilities.Capability; +import org.gradle.api.file.Directory; +import org.gradle.api.file.SourceDirectorySet; +import org.gradle.api.internal.HasConvention; +import org.gradle.api.internal.artifacts.dsl.CapabilityNotationParserFactory; +import org.gradle.api.internal.project.ProjectInternal; +import org.gradle.api.model.ObjectFactory; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.plugins.JavaPluginExtension; +import org.gradle.api.plugins.jvm.internal.JvmPluginServices; +import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.provider.Provider; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.Sync; +import org.gradle.api.tasks.TaskProvider; +import org.gradle.api.tasks.bundling.Jar; +import org.gradle.internal.typeconversion.NotationParser; +import org.gradle.language.base.plugins.LifecycleBasePlugin; +import org.unbrokendome.gradle.plugins.xjc.XjcPlugin; +import org.unbrokendome.gradle.plugins.xjc.XjcSourceSetConvention; + +import java.util.function.Function; + +import static java.util.Objects.requireNonNull; +import static org.ehcache.build.util.PluginUtils.capitalize; +import static org.gradle.api.attributes.DocsType.SOURCES; +import static org.gradle.api.plugins.JavaPlugin.API_CONFIGURATION_NAME; +import static org.gradle.api.plugins.JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME; +import static org.gradle.api.plugins.JavaPlugin.COMPILE_ONLY_API_CONFIGURATION_NAME; +import static org.gradle.api.plugins.JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME; +import static org.gradle.api.plugins.JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME; +import static org.gradle.api.plugins.JavaPlugin.RUNTIME_ONLY_CONFIGURATION_NAME; +import static org.gradle.api.plugins.JavaPlugin.SOURCES_ELEMENTS_CONFIGURATION_NAME; +import static org.gradle.api.tasks.SourceSet.MAIN_SOURCE_SET_NAME; + +public class VariantPlugin implements Plugin { + + protected static final String COMMON_SOURCE_SET_NAME = "common"; + + @Override + public void apply(Project project) { + VariantExtension variants = project.getExtensions().create("variants", VariantExtension.class, project); + configureJavaPluginBehavior(project, variants); + } + + private void configureJavaPluginBehavior(Project project, VariantExtension variants) { + project.getPlugins().withType(JavaPlugin.class).configureEach(javaPlugin -> { + JavaPluginExtension java = project.getExtensions().getByType(JavaPluginExtension.class); + + variants.getVariants().configureEach(variant -> { + if (variant.hasSources().get()) { + SourceSet commonSources = java.getSourceSets().findByName(COMMON_SOURCE_SET_NAME); + if (commonSources == null) { + commonSources = java.getSourceSets().create(COMMON_SOURCE_SET_NAME, common -> { + project.getTasks().named(common.getCompileJavaTaskName(), task -> task.setEnabled(false)); + project.getTasks().named(common.getClassesTaskName(), task -> task.setEnabled(false)); + linkToCommonSource(project, common, java.getSourceSets().getByName(MAIN_SOURCE_SET_NAME)); + }); + } + SourceSet variantSources = java.getSourceSets().create(variant.getName()); + + linkToCommonSource(project, commonSources, variantSources); + + java.registerFeature(variant.getName(), feature -> { + feature.usingSourceSet(variantSources); + feature.withSourcesJar(); + variant.getCapabilities().get().forEach(capability -> { + feature.capability(capability.getGroup(), capability.getName(), requireNonNull(capability.getVersion())); + }); + }); + + project.getPlugins().withType(BndBuilderPlugin.class).configureEach(bnd -> { + project.getTasks().named(variantSources.getJarTaskName(), Jar.class, jar -> { + jar.setDescription("Assembles a bundle containing the " + variant + " variant classes."); + BundleTaskExtension extension = jar.getExtensions().create(BundleTaskExtension.NAME, BundleTaskExtension.class, jar); + BndConvention.configureBundleDefaults(project, extension); + jar.doLast("buildBundle", extension.buildAction()); + }); + }); + + project.getTasks().named(LifecycleBasePlugin.ASSEMBLE_TASK_NAME).configure(task -> { + task.dependsOn(variantSources.getJarTaskName()); + task.dependsOn(variantSources.getSourcesJarTaskName()); + }); + } else { + SourceSet mainSource = java.getSourceSets().getByName(MAIN_SOURCE_SET_NAME); + + JvmPluginServices jvmPluginServices = ((ProjectInternal) project).getServices().get(JvmPluginServices.class); + + Configuration commonApi = PluginUtils.createBucket(project, API_CONFIGURATION_NAME, COMMON_SOURCE_SET_NAME); + project.getConfigurations().named(mainSource.getApiConfigurationName()).configure(config -> config.extendsFrom(commonApi)); + Configuration commonCompileOnlyApi = PluginUtils.createBucket(project, COMPILE_ONLY_API_CONFIGURATION_NAME, COMMON_SOURCE_SET_NAME); + project.getConfigurations().named(mainSource.getCompileOnlyApiConfigurationName()).configure(config -> config.extendsFrom(commonCompileOnlyApi)); + Configuration commonImplementation = PluginUtils.createBucket(project, IMPLEMENTATION_CONFIGURATION_NAME, COMMON_SOURCE_SET_NAME); + project.getConfigurations().named(mainSource.getImplementationConfigurationName()).configure(config -> config.extendsFrom(commonImplementation)); + Configuration commonRuntimeOnly = PluginUtils.createBucket(project, RUNTIME_ONLY_CONFIGURATION_NAME, COMMON_SOURCE_SET_NAME); + project.getConfigurations().named(mainSource.getRuntimeOnlyConfigurationName()).configure(config -> config.extendsFrom(commonRuntimeOnly)); + + Configuration api = PluginUtils.createBucket(project, API_CONFIGURATION_NAME, variant.getName()).extendsFrom(commonApi); + Configuration implementation = PluginUtils.createBucket(project, IMPLEMENTATION_CONFIGURATION_NAME, variant.getName()).extendsFrom(api, commonImplementation); + Configuration compileOnlyApi = PluginUtils.createBucket(project, COMPILE_ONLY_API_CONFIGURATION_NAME, variant.getName()).extendsFrom(commonCompileOnlyApi); + Configuration runtimeOnly = PluginUtils.createBucket(project, RUNTIME_ONLY_CONFIGURATION_NAME, variant.getName()).extendsFrom(commonRuntimeOnly); + + Configuration apiElements = jvmPluginServices.createOutgoingElements(variant.getName() + capitalize(API_ELEMENTS_CONFIGURATION_NAME), builder -> + builder.fromSourceSet(mainSource).withCapabilities(variant.getCapabilities().get()) + .extendsFrom(api, compileOnlyApi).withClassDirectoryVariant().providesApi()); + project.getConfigurations().named(mainSource.getApiElementsConfigurationName(), + config -> config.getOutgoing().getArtifacts().configureEach(artifact -> apiElements.getOutgoing().getArtifacts().add(artifact))); + + Configuration runtimeElements = jvmPluginServices.createOutgoingElements(variant.getName() + capitalize(RUNTIME_ELEMENTS_CONFIGURATION_NAME), builder -> + builder.fromSourceSet(mainSource).withCapabilities(variant.getCapabilities().get()).published() + .extendsFrom(implementation, runtimeOnly).providesRuntime()); + project.getConfigurations().named(mainSource.getRuntimeElementsConfigurationName(), + config -> config.getOutgoing().getArtifacts().configureEach(artifact -> runtimeElements.getOutgoing().getArtifacts().add(artifact))); + + Configuration sourcesElements = jvmPluginServices.createOutgoingElements(variant.getName() + capitalize(SOURCES_ELEMENTS_CONFIGURATION_NAME), builder -> + builder.fromSourceSet(mainSource).withCapabilities(variant.getCapabilities().get()).published() + .providesAttributes(attributes -> attributes.documentation(SOURCES).asJar())); + project.getConfigurations().named(mainSource.getSourcesElementsConfigurationName(), + config -> config.getOutgoing().getArtifacts().configureEach(artifact -> sourcesElements.getOutgoing().getArtifacts().add(artifact))); + } + }); + }); + } + + private static void linkToCommonSource(Project project, SourceSet commonSources, SourceSet derivedSources) { + registerCommonCopyTask(project, commonSources, derivedSources, SourceSet::getJava); + registerCommonCopyTask(project, commonSources, derivedSources, SourceSet::getResources); + + Configuration commonApi = project.getConfigurations().maybeCreate(commonSources.getApiConfigurationName()); + project.getConfigurations().maybeCreate(derivedSources.getApiConfigurationName()).extendsFrom(commonApi); + Configuration commonImplementation = project.getConfigurations().maybeCreate(commonSources.getImplementationConfigurationName()); + project.getConfigurations().maybeCreate(derivedSources.getImplementationConfigurationName()).extendsFrom(commonImplementation); + + project.getPlugins().withType(XjcPlugin.class).configureEach(plugin -> { + Function xjc = sourceSet -> ((HasConvention) sourceSet).getConvention().getPlugin(XjcSourceSetConvention.class); + + XjcSourceSetConvention commonXjc = xjc.apply(commonSources); + project.getTasks().named(commonXjc.getXjcGenerateTaskName(), task -> task.setEnabled(false)); + + registerCommonCopyTask(project, commonSources, derivedSources, xjc.andThen(XjcSourceSetConvention::getXjcSchema)); + registerCommonCopyTask(project, commonSources, derivedSources, xjc.andThen(XjcSourceSetConvention::getXjcCatalog)); + registerCommonCopyTask(project, commonSources, derivedSources, xjc.andThen(XjcSourceSetConvention::getXjcBinding)); + registerCommonCopyTask(project, commonSources, derivedSources, xjc.andThen(XjcSourceSetConvention::getXjcUrl)); + }); + } + + private static void registerCommonCopyTask(Project project, SourceSet common, SourceSet variant, Function type) { + SourceDirectorySet commonSource = type.apply(common); + Provider variantLocation = project.getLayout().getBuildDirectory().dir("generated/sources/common/" + variant.getName() + "/" + commonSource.getName()); + TaskProvider variantTask = project.getTasks().register(variant.getTaskName("copyCommon", commonSource.getName()), Sync.class, sync -> { + sync.from(commonSource); + sync.into(variantLocation); + }); + type.apply(variant).srcDir(variantTask); + } + + public static class Variant { + + private static final NotationParser CAPABILITY_NOTATION_PARSER = new CapabilityNotationParserFactory(true).create(); + + private final String name; + private final Property hasSources; + private final ListProperty capabilities; + + public Variant(String name, ObjectFactory objectFactory) { + this.name = name; + this.hasSources = objectFactory.property(Boolean.class).convention(false); + this.capabilities = objectFactory.listProperty(Capability.class); + + this.hasSources.finalizeValueOnRead(); + this.capabilities.finalizeValueOnRead(); + } + + public String getName() { + return name; + } + + public Property hasSources() { + return hasSources; + } + + public ListProperty getCapabilities() { + return capabilities; + } + + public void withSeparateSource() { + this.hasSources.set(true); + } + + public void capability(Object notation) { + this.capabilities.add(CAPABILITY_NOTATION_PARSER.parseNotation(notation)); + } + } + + public static class VariantExtension { + + private final ObjectFactory objectFactory; + private final NamedDomainObjectContainer variants; + + public VariantExtension(Project project) { + this.objectFactory = project.getObjects(); + this.variants = project.container(Variant.class); + } + + public void variant(String variant, Action action) { + Variant v = new Variant(variant, objectFactory); + action.execute(v); + variants.add(v); + } + + public NamedDomainObjectContainer getVariants() { + return variants; + } + } +} diff --git a/build-logic/src/main/java/org/ehcache/build/plugins/VoltronPlugin.java b/build-logic/src/main/java/org/ehcache/build/plugins/VoltronPlugin.java new file mode 100644 index 0000000000..db5299198f --- /dev/null +++ b/build-logic/src/main/java/org/ehcache/build/plugins/VoltronPlugin.java @@ -0,0 +1,66 @@ +package org.ehcache.build.plugins; + +import org.ehcache.build.conventions.JavaLibraryConvention; +import org.gradle.api.Action; +import org.gradle.api.NamedDomainObjectProvider; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.Task; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.dsl.DependencyHandler; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.tasks.bundling.Jar; + +import java.io.File; +import java.util.jar.Attributes; +import java.util.stream.Collectors; + +import static java.util.Collections.singletonMap; + +public class VoltronPlugin implements Plugin { + + private static final String VOLTRON_CONFIGURATION_NAME = "voltron"; + private static final String SERVICE_CONFIGURATION_NAME = "service"; + + @Override + public void apply(Project project) { + project.getPlugins().apply(JavaLibraryConvention.class); + + NamedDomainObjectProvider voltron = project.getConfigurations().register(VOLTRON_CONFIGURATION_NAME, config -> { + config.setDescription("Dependencies provided by Voltron from server/lib"); + config.setCanBeConsumed(true); + config.setCanBeResolved(true); + + DependencyHandler dependencyHandler = project.getDependencies(); + String terracottaApisVersion = project.property("terracottaApisVersion").toString(); + String slf4jVersion = project.property("slf4jVersion").toString(); + config.getDependencies().add(dependencyHandler.create("org.terracotta:entity-server-api:" + terracottaApisVersion)); + config.getDependencies().add(dependencyHandler.create("org.terracotta:standard-cluster-services:" + terracottaApisVersion)); + config.getDependencies().add(dependencyHandler.create("org.terracotta:packaging-support:" + terracottaApisVersion)); + config.getDependencies().add(dependencyHandler.create("org.slf4j:slf4j-api:" + slf4jVersion)); + }); + + NamedDomainObjectProvider service = project.getConfigurations().register(SERVICE_CONFIGURATION_NAME, config -> { + config.setDescription("Services consumed by this plugin"); + config.setCanBeResolved(true); + config.setCanBeConsumed(true); + }); + + project.getConfigurations().named(JavaPlugin.API_CONFIGURATION_NAME, config -> { + config.extendsFrom(voltron.get()); + config.extendsFrom(service.get()); + }); + + project.getTasks().named(JavaPlugin.JAR_TASK_NAME, Jar.class, jar -> { + //noinspection Convert2Lambda + jar.doFirst(new Action() { + @Override + public void execute(Task task) { + jar.manifest(manifest -> manifest.attributes(singletonMap(Attributes.Name.CLASS_PATH.toString(), + (project.getConfigurations().getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME).minus(voltron.get()).minus(service.get())) + .getFiles().stream().map(File::getName).collect(Collectors.joining(" "))))); + } + }); + }); + } +} diff --git a/build-logic/src/main/java/org/ehcache/build/plugins/osgids/GenerateDeclarativeServicesDescriptors.java b/build-logic/src/main/java/org/ehcache/build/plugins/osgids/GenerateDeclarativeServicesDescriptors.java new file mode 100644 index 0000000000..c285f97550 --- /dev/null +++ b/build-logic/src/main/java/org/ehcache/build/plugins/osgids/GenerateDeclarativeServicesDescriptors.java @@ -0,0 +1,113 @@ +package org.ehcache.build.plugins.osgids; + +import org.apache.felix.scrplugin.Options; +import org.apache.felix.scrplugin.Project; +import org.apache.felix.scrplugin.SCRDescriptorException; +import org.apache.felix.scrplugin.SCRDescriptorFailureException; +import org.apache.felix.scrplugin.SCRDescriptorGenerator; +import org.apache.felix.scrplugin.Source; +import org.gradle.api.DefaultTask; +import org.gradle.api.GradleException; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.file.EmptyFileVisitor; +import org.gradle.api.file.FileCollection; +import org.gradle.api.file.FileVisitDetails; +import org.gradle.api.tasks.Classpath; +import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.OutputDirectory; +import org.gradle.api.tasks.TaskAction; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Set; + +public abstract class GenerateDeclarativeServicesDescriptors extends DefaultTask { + + @InputFiles + public abstract ConfigurableFileCollection getInputFiles(); + + @Classpath + public abstract ConfigurableFileCollection getClasspath(); + + @OutputDirectory + public abstract DirectoryProperty getOutputDirectory(); + + @TaskAction + public void generateDeclarativeServicesDescriptors() throws SCRDescriptorException, SCRDescriptorFailureException, IOException { + final Options scrOptions = createOptions(); + + try (GradleScrProject scrProject = new GradleScrProject(getInputFiles(), getClasspath())) { + final SCRDescriptorGenerator scrGenerator = new SCRDescriptorGenerator(new ScrLoggerAdapter(getLogger())); + scrGenerator.setOptions(scrOptions); + scrGenerator.setProject(scrProject); + + scrGenerator.execute(); + } + } + + private Options createOptions() { + final Options scrOptions = new Options(); + scrOptions.setOutputDirectory(getOutputDirectory().get().getAsFile()); + scrOptions.setStrictMode(false); + scrOptions.setSpecVersion(null); + + return scrOptions; + } + + static class GradleScrProject extends Project implements Closeable { + + private final URLClassLoader urlClassLoader; + + GradleScrProject(FileCollection input, FileCollection classpath) { + Set classpathFiles = classpath.getFiles(); + URL[] classpathUrls = classpathFiles.stream().map(f -> { + try { + return f.toURI().toURL(); + } catch (MalformedURLException e) { + throw new GradleException("Malformed URL in classpath", e); + } + }).toArray(URL[]::new); + this.urlClassLoader = URLClassLoader.newInstance(classpathUrls, getClass().getClassLoader()); + setClassLoader(urlClassLoader); + setDependencies(classpathFiles); + setSources(createScrSources(input)); + } + + @Override + public void close() throws IOException { + urlClassLoader.close(); + } + + private static Collection createScrSources(FileCollection input) { + Collection sources = new ArrayList<>(); + + input.getAsFileTree().matching(f -> f.include("**/*.class")).visit(new EmptyFileVisitor() { + @Override + public void visitFile(FileVisitDetails fileVisitDetails) { + String dotSeparated = String.join(".", fileVisitDetails.getRelativePath().getSegments()); + String className = dotSeparated.substring(0, dotSeparated.length() - ".class".length()); + File file = fileVisitDetails.getFile(); + sources.add(new Source() { + @Override + public String getClassName() { + return className; + } + + @Override + public File getFile() { + return file; + } + }); + } + }); + return sources; + } + } +} diff --git a/build-logic/src/main/java/org/ehcache/build/plugins/osgids/OsgiDsPlugin.java b/build-logic/src/main/java/org/ehcache/build/plugins/osgids/OsgiDsPlugin.java new file mode 100644 index 0000000000..a844ab0af9 --- /dev/null +++ b/build-logic/src/main/java/org/ehcache/build/plugins/osgids/OsgiDsPlugin.java @@ -0,0 +1,22 @@ +package org.ehcache.build.plugins.osgids; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.tasks.SourceSetContainer; +import org.gradle.api.tasks.TaskProvider; + +public class OsgiDsPlugin implements Plugin { + @Override + public void apply(Project project) { + project.getExtensions().configure(SourceSetContainer.class, sourceSets -> sourceSets.configureEach(sourceSet -> { + String taskName = sourceSet.getTaskName("generate", "DeclarativeServicesDescriptors"); + TaskProvider generateTask = project.getTasks().register(taskName, GenerateDeclarativeServicesDescriptors.class, task -> { + task.setDescription("Generate OSGi Declarative Services XML descriptors for " + sourceSet.getName() + " classes"); + task.getInputFiles().from(sourceSet.getOutput().getClassesDirs()); + task.getClasspath().from(sourceSet.getRuntimeClasspath()); + task.getOutputDirectory().set(project.getLayout().getBuildDirectory().dir("generated/resources/osgi-ds/" + sourceSet.getName())); + }); + sourceSet.getOutput().getGeneratedSourcesDirs().plus(project.fileTree(generateTask)); + })); + } +} diff --git a/build-logic/src/main/java/org/ehcache/build/plugins/osgids/ScrLoggerAdapter.java b/build-logic/src/main/java/org/ehcache/build/plugins/osgids/ScrLoggerAdapter.java new file mode 100644 index 0000000000..279b26714f --- /dev/null +++ b/build-logic/src/main/java/org/ehcache/build/plugins/osgids/ScrLoggerAdapter.java @@ -0,0 +1,149 @@ +/** + * Copyright (C) 2016 Elmar Schug + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.build.plugins.osgids; + +import org.apache.felix.scrplugin.Log; +import org.gradle.api.logging.Logger; + + +final class ScrLoggerAdapter implements Log +{ + private final Logger logger; + + ScrLoggerAdapter(Logger logger) { + this.logger = logger; + } + + @Override + public boolean isDebugEnabled() + { + return logger.isDebugEnabled(); + } + + @Override + public void debug(String content) + { + logger.debug(content); + } + + @Override + public void debug(String content, Throwable error) + { + logger.debug(content, error); + } + + @Override + public void debug(Throwable error) + { + logger.debug(error.toString()); + } + + @Override + public boolean isInfoEnabled() + { + return logger.isInfoEnabled(); + } + + @Override + public void info(String content) + { + logger.info(content); + } + + @Override + public void info(String content, Throwable error) + { + logger.info(content, error); + } + + @Override + public void info(Throwable error) + { + logger.info(error.toString()); + } + + @Override + public boolean isWarnEnabled() + { + return logger.isWarnEnabled(); + } + + @Override + public void warn(String content) + { + logger.warn(content); + } + + @Override + public void warn(String content, String location, int lineNumber) + { + logger.warn("{} [{},{}]", content, location, lineNumber); + } + + @Override + public void warn(String content, String location, int lineNumber, int columNumber) + { + logger.warn("{} [{},{}:{}]", content, location, lineNumber, columNumber); + } + + @Override + public void warn(String content, Throwable error) + { + logger.warn(content, error); + } + + @Override + public void warn(Throwable error) + { + logger.warn(error.toString()); + } + + @Override + public boolean isErrorEnabled() + { + return logger.isErrorEnabled(); + } + + @Override + public void error(String content) + { + logger.error(content); + } + + @Override + public void error(String content, String location, int lineNumber) + { + logger.error("{} [{},}{}]", content, location, lineNumber); + } + + @Override + public void error(String content, String location, int lineNumber, int columnNumber) + { + logger.error("{} [{},{}:{}]", content, location, lineNumber, columnNumber); + } + + @Override + public void error(String content, Throwable error) + { + logger.error(content, error); + } + + @Override + public void error(Throwable error) + { + logger.error(error.toString()); + } +} diff --git a/build-logic/src/main/java/org/ehcache/build/util/OsgiManifestJarExtension.java b/build-logic/src/main/java/org/ehcache/build/util/OsgiManifestJarExtension.java new file mode 100644 index 0000000000..d2e8556173 --- /dev/null +++ b/build-logic/src/main/java/org/ehcache/build/util/OsgiManifestJarExtension.java @@ -0,0 +1,103 @@ +package org.ehcache.build.util; + +import aQute.bnd.osgi.Builder; +import aQute.bnd.osgi.Jar; +import aQute.service.reporter.Report; +import org.gradle.api.Action; +import org.gradle.api.GradleException; +import org.gradle.api.Task; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.provider.MapProperty; +import org.gradle.api.provider.Provider; +import org.gradle.api.tasks.Classpath; +import org.gradle.api.tasks.ClasspathNormalizer; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputFiles; + +import java.io.File; +import java.util.Map; +import java.util.concurrent.Callable; + +public class OsgiManifestJarExtension { + + private final org.gradle.api.tasks.bundling.Jar jarTask; + private final MapProperty instructions; + private final ConfigurableFileCollection classpath; + private final ConfigurableFileCollection sources; + + public OsgiManifestJarExtension(org.gradle.api.tasks.bundling.Jar jarTask) { + this.jarTask = jarTask; + this.instructions = jarTask.getProject().getObjects().mapProperty(String.class, String.class); + this.classpath = jarTask.getProject().getObjects().fileCollection(); + this.sources = jarTask.getProject().getObjects().fileCollection(); + + jarTask.getInputs().files(classpath).withNormalizer(ClasspathNormalizer.class).withPropertyName("osgi.classpath"); + jarTask.getInputs().files(sources).withPropertyName("osgi.sources"); + jarTask.getInputs().property("osgi.instructions", (Callable>) instructions::get); + + jarTask.getExtensions().add("osgi", this); + jarTask.doLast("buildManifest", new BuildAction()); + } + + public void instruction(String key, String value) { + instructions.put(key, value); + } + + public void instruction(String key, Provider value) { + instructions.put(key, value); + } + + @Input @Classpath + public ConfigurableFileCollection getClasspath() { + return classpath; + } + + @InputFiles + public ConfigurableFileCollection getSources() { + return sources; + } + + @Input + public MapProperty getInstructions() { + return instructions; + } + + + private class BuildAction implements Action { + @Override + public void execute(Task t) { + try (Builder builder = new Builder()) { + File archiveFile = jarTask.getArchiveFile().get().getAsFile(); + + jarTask.getProject().sync(sync -> sync.from(archiveFile).into(jarTask.getTemporaryDir())); + File archiveCopyFile = new File(jarTask.getTemporaryDir(), archiveFile.getName()); + + Jar bundleJar = new Jar(archiveCopyFile); + + builder.setJar(bundleJar); + builder.setClasspath(getClasspath().getFiles()); + builder.setSourcepath(getSources().getFiles().toArray(new File[0])); + builder.addProperties(getInstructions().get()); + + try (Jar builtJar = builder.build()) { + builtJar.write(archiveFile); + } + + if (!builder.isOk()) { + jarTask.getProject().delete(archiveFile); + builder.getErrors().forEach((String msg) -> { + Report.Location location = builder.getLocation(msg); + if ((location != null) && (location.file != null)) { + jarTask.getLogger().error("{}:{}: error: {}", location.file, location.line, msg); + } else { + jarTask.getLogger().error("error : {}", msg); + } + }); + throw new GradleException("Bundle " + archiveFile.getName() + " has errors"); + } + } catch (Exception e) { + throw new GradleException("Error building bundle", e); + } + } + } +} diff --git a/build-logic/src/main/java/org/ehcache/build/util/PluginUtils.java b/build-logic/src/main/java/org/ehcache/build/util/PluginUtils.java new file mode 100644 index 0000000000..619b6413f3 --- /dev/null +++ b/build-logic/src/main/java/org/ehcache/build/util/PluginUtils.java @@ -0,0 +1,48 @@ +package org.ehcache.build.util; + +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; + +import java.util.Locale; + +public class PluginUtils { + + public static Configuration createBucket(Project project, String kind, String variant) { + if (variant == null) { + return createBucket(project, kind); + } else { + Configuration configuration = project.getConfigurations().maybeCreate(variant + capitalize(kind)); + configuration.setDescription(capitalize(kind) + " dependencies for " + variant); + configuration.setVisible(false); + configuration.setCanBeResolved(false); + configuration.setCanBeConsumed(false); + return configuration; + } + } + + public static Configuration createBucket(Project project, String kind) { + Configuration configuration = project.getConfigurations().maybeCreate(kind); + configuration.setDescription(capitalize(kind) + " dependencies"); + configuration.setVisible(false); + configuration.setCanBeResolved(false); + configuration.setCanBeConsumed(false); + return configuration; + } + + public static Configuration bucket(Project project, String kind, String variant) { + if (variant == null) { + return bucket(project, kind); + } else { + return project.getConfigurations().getByName(variant + capitalize(kind)); + } + } + + public static Configuration bucket(Project project, String kind) { + return project.getConfigurations().getByName(kind); + } + + public static String capitalize(String word) { + return word.substring(0, 1).toUpperCase(Locale.ROOT) + word.substring(1); + } + +} diff --git a/build.gradle b/build.gradle index 202ad17493..b107ffb59d 100644 --- a/build.gradle +++ b/build.gradle @@ -13,24 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import scripts.* -import org.gradle.internal.jvm.Jvm plugins { // This adds tasks to auto close or release nexus staging repos // see https://github.com/Codearte/gradle-nexus-staging-plugin/ - id 'io.codearte.nexus-staging' version '0.9.0' - // This adds the ability to print a taskTree - // ./gradlew ... taskTree - id "com.dorongold.task-tree" version "1.3" - // Declare spotbugs at the top - id 'com.github.spotbugs' version '1.6.3' apply false + id 'io.codearte.nexus-staging' + //OWASP Security Vulnerability Detection + id 'org.owasp.dependencycheck' } wrapper { distributionType = Wrapper.DistributionType.ALL } +allprojects { + version = findProperty('overrideVersion') ?: ehcacheVersion +} + if (deployUrl.contains('nexus')) { //internal terracotta config, shorten url for this plugin to end at local/ project.nexusStaging { @@ -51,182 +50,25 @@ if (deployUrl.contains('nexus')) { } } -project.nexusStaging { +nexusStaging { username = project.ext.deployUser password = project.ext.deployPwd logger.debug("Nexus Staging: Using login ${username} and url ${serverUrl}") } -// Disable automatic promotion for added safety -closeAndReleaseRepository.enabled = false - - -ext { - - baseVersion = findProperty('overrideVersion') ?: ehcacheVersion - - utils = new Utils(baseVersion, logger) - isReleaseVersion = !baseVersion.endsWith('SNAPSHOT') - isCloudbees = System.getenv('JENKINS_URL')?.contains('cloudbees') +tasks.named('closeAndReleaseRepository') { + // Disable automatic promotion for added safety + enabled = false; } - assert (JavaVersion.current().isJava8Compatible()) : 'The Ehcache 3 build requires Java 8+ to run' -ext { - testJava = Jvm.current() -} - -if (hasProperty('testVM')) { - testJava = Utils.jvmForHome(new File(testVM)) - println "Using Test JVM $testJava [Version: $testJava.javaVersion.majorVersion]" -} - -subprojects { - apply plugin: 'java-library' - apply plugin: 'eclipse' - apply plugin: 'checkstyle' - apply plugin: 'com.github.spotbugs' - apply plugin: 'jacoco' - - group = 'org.ehcache.modules' - version = baseVersion - - archivesBaseName = "ehcache-${project.name}" - - sourceCompatibility = 1.8 - targetCompatibility = 1.8 - - repositories { - if (project.hasProperty('mvnlocal')) { - mavenLocal() - } - mavenCentral() - maven { url "http://repo.terracotta.org/maven2" } - } - - sourceSets { - slowTest { - java.srcDir 'src/slow-test/java' - resources.srcDir 'src/slow-test/resources' - compileClasspath += sourceSets.test.compileClasspath - runtimeClasspath += sourceSets.test.runtimeClasspath - } - } - - dependencies { - implementation "org.slf4j:slf4j-api:$parent.slf4jVersion" - compileOnly "com.github.spotbugs:spotbugs-annotations:$parent.spotbugsVersion" - testCompileOnly "com.github.spotbugs:spotbugs-annotations:$parent.spotbugsVersion" - testImplementation "junit:junit:$junitVersion" - testImplementation "org.assertj:assertj-core:$assertjVersion" - testImplementation "org.hamcrest:hamcrest-library:$hamcrestVersion" - testImplementation "org.mockito:mockito-core:$mockitoVersion" - testCompile 'org.xmlunit:xmlunit-core:2.6.0', 'org.xmlunit:xmlunit-matchers:2.6.0' - testRuntimeOnly "org.slf4j:slf4j-simple:$parent.slf4jVersion" - } - - jar { - utils.fillManifest(manifest,"ehcache-${project.name}") - from "$rootDir/LICENSE" - } - - test { - maxHeapSize = "1408m" - systemProperty 'java.awt.headless', 'true' - if (parent.isCloudbees) { - systemProperty 'disable.concurrent.tests', 'true' - } - } - - task slowTest(type: Test) { - testClassesDirs = sourceSets.slowTest.output.classesDirs - classpath += sourceSets.slowTest.runtimeClasspath - - binResultsDir file("$buildDir/slow-tests-results/binary/$name") - reports.junitXml.destination = file("$buildDir/slow-tests-results") - reports.html.destination = file("$buildDir/reports/slow-tests") - } - task sourceJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allJava - classifier = 'sources' - } - - javadoc { - title "$project.archivesBaseName $project.version API" - exclude '**/internal/**' - } - - task javadocJar(type: Jar, dependsOn: javadoc) { - from javadoc.destinationDir - classifier = 'javadoc' - } - - artifacts { - archives jar - - archives javadocJar - archives sourceJar - } - - checkstyle { - configFile = file("$rootDir/config/checkstyle.xml") - configProperties = ['projectDir':projectDir, 'rootDir':rootDir] - toolVersion = checkstyleVersion - } - - spotbugs { - ignoreFailures = false - sourceSets = [sourceSets.main] - toolVersion = spotbugsVersion - } - - spotbugsMain { - reports { - // Switch from xml to html by changing these flags - xml.enabled = true - html.enabled = false - } - } - - jacoco { - toolVersion = jacocoVersion - } - - jacocoTestReport { - reports { - xml.enabled false - csv.enabled false - } - } - - tasks.withType(AbstractCompile) { - options.with { - fork = true - } - } - tasks.withType(Test) { - executable = testJava.javaExecutable - } - tasks.withType(Javadoc) { - options.addStringOption('Xdoclint:none', '-quiet') - } - - configurations.all { - resolutionStrategy { - failOnVersionConflict() - // If you want to override a dependency, instead of changing gradle.properties, use something like below - // force 'org.terracotta:statistics:2.0-SNAPSHOT' - } - } +dependencyCheck { + failBuildOnCVSS = 0 + suppressionFile = 'config/owasp-supressions.xml' + skipConfigurations += ['checkstyle', 'spotbugs', 'xjcClasspath'] + skipProjects += [':docs', ':demos:00-NoCache', ':demos:01-CacheAside'] } - -allprojects { - tasks.withType(JavaCompile) { - options.encoding = 'UTF-8' - options.compilerArgs += ['-Werror', '-Xlint:all'] - } - tasks.withType(Javadoc) { - options.encoding = 'UTF-8' - } +tasks.register('check') { + dependsOn dependencyCheckAggregate } diff --git a/buildSrc/src/main/groovy/EhDeploy.groovy b/buildSrc/src/main/groovy/EhDeploy.groovy deleted file mode 100644 index 4465ea89ff..0000000000 --- a/buildSrc/src/main/groovy/EhDeploy.groovy +++ /dev/null @@ -1,88 +0,0 @@ -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer -import org.gradle.api.artifacts.maven.MavenDeployment -import org.gradle.api.plugins.MavenPlugin -import org.gradle.plugins.signing.Sign -import scripts.Utils - -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * EhDeploy - */ -class EhDeploy implements Plugin { - @Override - void apply(Project project) { - - def utils = new Utils(project.baseVersion, project.logger) - - project.plugins.apply 'signing' - project.plugins.apply 'maven' - project.plugins.apply EhPomGenerate // for generating pom.* - - project.configurations { - providedApi - providedImplementation - - api.extendsFrom providedApi - implementation.extendsFrom providedImplementation - } - - project.signing { - required { project.isReleaseVersion && project.gradle.taskGraph.hasTask("uploadArchives") } - sign project.configurations.getByName('archives') - } - - def artifactFiltering = { - project.configurations.matching {it.name.startsWith('test')}.forEach { - pom.scopeMappings.mappings.remove(it) - } - pom.scopeMappings.addMapping(MavenPlugin.COMPILE_PRIORITY, project.configurations.providedApi, Conf2ScopeMappingContainer.PROVIDED) - pom.scopeMappings.addMapping(MavenPlugin.COMPILE_PRIORITY, project.configurations.providedImplementation, Conf2ScopeMappingContainer.PROVIDED) - - utils.pomFiller(pom, project.subPomName, project.subPomDesc) - - } - - project.install { - repositories.mavenInstaller artifactFiltering - } - - project.uploadArchives { - repositories { - mavenDeployer ({ - beforeDeployment { MavenDeployment deployment -> project.signing.signPom(deployment)} - - if (project.isReleaseVersion) { - repository(url: project.deployUrl) { - authentication(userName: project.deployUser, password: project.deployPwd) - } - } else { - repository(id: 'sonatype-nexus-snapshot', url: 'https://oss.sonatype.org/content/repositories/snapshots') { - authentication(userName: project.sonatypeUser, password: project.sonatypePwd) - } - } - } << artifactFiltering) - } - } - - def installer = project.install.repositories.mavenInstaller - def deployer = project.uploadArchives.repositories.mavenDeployer - - } -} diff --git a/buildSrc/src/main/groovy/EhDistribute.groovy b/buildSrc/src/main/groovy/EhDistribute.groovy deleted file mode 100644 index c3f2e59319..0000000000 --- a/buildSrc/src/main/groovy/EhDistribute.groovy +++ /dev/null @@ -1,82 +0,0 @@ -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.artifacts.ProjectDependency -import scripts.Utils - -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * EhDistribute - */ -class EhDistribute implements Plugin { - - @Override - void apply(Project project) { - def utils = new Utils(project.baseVersion, project.logger) - def hashsetOfProjects = project.configurations.compileOnly.dependencies.withType(ProjectDependency).dependencyProject - - project.plugins.apply 'java-library' - project.plugins.apply 'maven' - project.plugins.apply 'signing' - project.plugins.apply 'com.github.johnrengelman.shadow' - project.plugins.apply EhOsgi - project.plugins.apply EhPomMangle - project.plugins.apply EhDocs - project.plugins.apply EhPomGenerate - - def OSGI_OVERRIDE_KEYS = ['Import-Package', 'Export-Package', 'Private-Package', 'Tool', 'Bnd-LastModified', 'Created-By', 'Require-Capability'] - - project.configurations { - shadowCompile - shadowProvided - } - - project.shadowJar { - configurations = [[project.configurations.compileOnly]] - baseName = "$project.archivesBaseName-shadow" - classifier = '' - dependencies { - exclude({ rdep -> !['org.ehcache', 'org.terracotta'].any({ prefix -> rdep.moduleGroup.startsWith(prefix) })}) - } - mergeServiceFiles() - } - - project.jar { - dependsOn project.shadowJar - from(project.zipTree(project.shadowJar.archivePath.getPath())) { - exclude 'META-INF/MANIFEST.MF', 'LICENSE', 'NOTICE' - } - // LICENSE is included in root gradle build - from "$project.rootDir/NOTICE" - duplicatesStrategy = 'exclude' - } - - - project.sourceJar { - from hashsetOfProjects.flatten { - it.sourceSets.main.allSource - } - } - - - project.signing { - required { project.isReleaseVersion && project.gradle.taskGraph.hasTask("uploadArchives") } - sign project.configurations.getByName('archives') - } - - } -} diff --git a/buildSrc/src/main/groovy/EhDocs.groovy b/buildSrc/src/main/groovy/EhDocs.groovy deleted file mode 100644 index 0a900fb480..0000000000 --- a/buildSrc/src/main/groovy/EhDocs.groovy +++ /dev/null @@ -1,76 +0,0 @@ -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.artifacts.ProjectDependency -import org.gradle.api.tasks.bundling.Jar -import org.gradle.api.tasks.bundling.Zip -import org.gradle.api.tasks.javadoc.Javadoc -import scripts.Utils - -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * EhDocs - * Handle javadocs and API/SPI/asciidoc - */ -class EhDocs implements Plugin { - - @Override - void apply(Project project) { - def utils = new Utils(project.baseVersion, project.logger) - def hashsetOfProjects = project.configurations.compile.dependencies.withType(ProjectDependency).dependencyProject + - project.configurations.compileOnly.dependencies.withType(ProjectDependency).dependencyProject - - project.javadoc { - title "$project.archivesBaseName $project.version API" - source hashsetOfProjects.javadoc.source - classpath = project.files(hashsetOfProjects.javadoc.classpath) - project.ext.properties.javadocExclude?.tokenize(',').each { - exclude it.trim() - } - } - - if (!project.hasProperty('spiJavadocDisable')) { - - project.task('spiJavadoc', type: Javadoc) { - title "$project.archivesBaseName $project.version API & SPI" - source hashsetOfProjects.javadoc.source - classpath = project.files(hashsetOfProjects.javadoc.classpath) - exclude '**/internal/**' - destinationDir = project.file("$project.docsDir/spi-javadoc") - } - - project.task('spiJavadocJar', type: Jar, dependsOn: 'spiJavadoc') { - classifier = 'spi-javadoc' - from project.tasks.getByPath('spiJavadoc').destinationDir - } - - } - - project.task('asciidocZip', type: Zip, dependsOn: ':docs:asciidoctor') { - classifier = 'docs' - from project.tasks.getByPath(':docs:asciidoctor').outputDir - } - - project.artifacts { - archives project.asciidocZip - if (!project.hasProperty('spiJavadocDisable')) { - archives project.spiJavadocJar - } - } - - } -} diff --git a/buildSrc/src/main/groovy/EhOsgi.groovy b/buildSrc/src/main/groovy/EhOsgi.groovy deleted file mode 100644 index dda5ea0c88..0000000000 --- a/buildSrc/src/main/groovy/EhOsgi.groovy +++ /dev/null @@ -1,95 +0,0 @@ -import com.github.jengelman.gradle.plugins.shadow.tasks.DefaultInheritManifest -import groovy.json.JsonSlurper -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.artifacts.ProjectDependency -import org.gradle.api.internal.file.FileResolver -import org.gradle.api.plugins.osgi.OsgiPluginConvention -import scripts.Utils - -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * EhOsgi - * OSGI additions to the manifest controlled by osgi key in gradle.properties - * This plugin supports shadowJar if available - */ -class EhOsgi implements Plugin { - - @Override - void apply(Project project) { - def utils = new Utils(project.baseVersion, project.logger) - def hashsetOfProjects = project.configurations.compile.dependencies.withType(ProjectDependency).dependencyProject + - project.configurations.compileOnly.dependencies.withType(ProjectDependency).dependencyProject - hashsetOfProjects += project //self also, in case the invoking project defines osgi properties - - project.plugins.apply 'java-library' - project.plugins.apply 'maven' - project.plugins.apply 'signing' - - def OSGI_OVERRIDE_KEYS = ['Import-Package', 'Export-Package', 'Private-Package', 'Tool', 'Bnd-LastModified', 'Created-By', 'Require-Capability'] - - project.jar.doFirst { - manifest = new DefaultInheritManifest(getServices().get(FileResolver.class)) - if (project.hasProperty('shadowJar')) { - manifest.inheritFrom "$project.buildDir/tmp/shadowJar/MANIFEST.MF" - } - utils.fillManifest(manifest, project.archivesBaseName) - - def osgiConvention = new OsgiPluginConvention(project) - def osgiManifest = osgiConvention.osgiManifest { - - if (project.hasProperty('shadowJar')) { - classesDir = project.shadowJar.archivePath - classpath = project.files(project.configurations.shadowCompile, project.configurations.shadowProvided) - } else { - classesDir = project.sourceSets.main.java.outputDir - classpath = project.sourceSets.main.compileClasspath - } - - // Metadata - instructionReplace 'Bundle-Name', "$project.archivesBaseName 3" - instructionReplace 'Bundle-SymbolicName', "org.ehcache.$project.archivesBaseName" - instruction 'Bundle-Description', 'Ehcache is an open-source caching library, compliant with the JSR-107 standard.' - instruction 'Bundle-DocURL', 'http://ehcache.org' - instruction 'Bundle-License', 'LICENSE' - instruction 'Bundle-Vendor', 'Terracotta Inc., a wholly-owned subsidiary of Software AG USA, Inc.' - instruction 'Bundle-RequiredExecutionEnvironment', 'JavaSE-1.8' - - hashsetOfProjects.findAll({ p -> p.ext.properties.osgi}).each{ prop -> - new JsonSlurper().parseText(prop.ext.properties.osgi).each { - project.logger.info "OSGI: ${it.key}: ${it.value}" - instruction(it.key, *it.value) - } - } - - instruction 'Export-Package', '*' - instruction 'Import-Package', '*' - } - manifest.inheritFrom(osgiManifest) { - eachEntry { - if (it.getKey().startsWith('Bundle') || OSGI_OVERRIDE_KEYS.contains(it.getKey())) { - it.setValue(it.getMergeValue()) - } else { - it.setValue(it.getBaseValue()) - } - } - } - } - - } -} diff --git a/buildSrc/src/main/groovy/EhPomGenerate.groovy b/buildSrc/src/main/groovy/EhPomGenerate.groovy deleted file mode 100644 index 81761ffe63..0000000000 --- a/buildSrc/src/main/groovy/EhPomGenerate.groovy +++ /dev/null @@ -1,125 +0,0 @@ - - -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.publish.maven.MavenPublication -import scripts.Utils - -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * EhPomGenerate: - * Creates pom.xml and pom.properties to be included in produced jars - * Mimics standard maven jar layout. - */ -class EhPomGenerate implements Plugin { - - @Override - void apply(Project project) { - - def utils = new Utils(project.baseVersion, project.logger) - - project.plugins.apply 'maven-publish' // for generating pom.* - - def mavenTempResourcePath = "${project.buildDir}/mvn/META-INF/maven/${project.group}/${project.archivesBaseName}" - - project.model { - // Write pom to temp location to be picked up later, - // generatePomFileForMavenJavaPublication task comes from maven-publish. - tasks.generatePomFileForMavenJavaPublication { - destination = project.file("$mavenTempResourcePath/pom.xml") - } - } - - // Configure pom generation - project.publishing { - publications { - mavenJava(MavenPublication) { - artifactId project.archivesBaseName - from project.components.java - utils.pomFiller(pom, project.subPomName, project.subPomDesc) - if (project.hasProperty('shadowJar')) { - pom.withXml { - if (asNode().dependencies.isEmpty()) { - asNode().appendNode('dependencies') - } - project.configurations.shadowCompile.dependencies.each { - def dep = asNode().dependencies[0].appendNode('dependency') - dep.appendNode('groupId', it.group) - dep.appendNode('artifactId', it.name) - dep.appendNode('version', it.version) - dep.appendNode('scope', 'compile') - } - project.configurations.pomOnlyCompile.dependencies.each { - def dep = asNode().dependencies[0].appendNode('dependency') - dep.appendNode('groupId', it.group) - dep.appendNode('artifactId', it.name) - dep.appendNode('version', it.version) - dep.appendNode('scope', 'compile') - } - project.configurations.shadowProvided.dependencies.each { - def dep = asNode().dependencies[0].appendNode('dependency') - dep.appendNode('groupId', it.group) - dep.appendNode('artifactId', it.name) - dep.appendNode('version', it.version) - dep.appendNode('scope', 'provided') - } - project.configurations.pomOnlyProvided.dependencies.each { - def dep = asNode().dependencies[0].appendNode('dependency') - dep.appendNode('groupId', it.group) - dep.appendNode('artifactId', it.name) - dep.appendNode('version', it.version) - dep.appendNode('scope', 'provided') - } - } - } - } - } - } - - // Write pom.properties to temp location - project.task('writeMavenProperties') { - doLast { - project.file(mavenTempResourcePath).mkdirs() - def propertyFile = project.file "$mavenTempResourcePath/pom.properties" - def props = new Properties() - props.setProperty('version', project.version) - props.setProperty('groupId', project.group) - props.setProperty('artifactId', project.archivesBaseName) - props.store propertyFile.newWriter(), null - } - } - - if (utils.isReleaseVersion) { - //ensure that we generate maven stuff and delay resolution as the first task is created dynamically - project.processResources.dependsOn { - project.tasks.findAll { task -> - task.name == 'generatePomFileForMavenJavaPublication' || task.name == 'writeMavenProperties' - } - } - - // Pick up pom.xml and pom.properties from temp location - project.sourceSets { - main { - resources { - srcDir "${project.buildDir}/mvn" - } - } - } - } - } -} diff --git a/buildSrc/src/main/groovy/EhPomMangle.groovy b/buildSrc/src/main/groovy/EhPomMangle.groovy deleted file mode 100644 index 2bc761fdb9..0000000000 --- a/buildSrc/src/main/groovy/EhPomMangle.groovy +++ /dev/null @@ -1,97 +0,0 @@ -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.artifacts.ProjectDependency -import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer -import org.gradle.api.artifacts.maven.MavenDeployment -import org.gradle.api.plugins.MavenPlugin -import scripts.Utils - -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * EhPomMangle - * Removes all implicit dependencies from the pom - * and adds only what is specified in (from shadowJar) - * - * project.configurations.shadowCompile (as compile) - * project.configurations.shadowProvided (as provided) - * - * as well as (these do not affect shadow) - * - * project.configurations.pomOnlyCompile - * project.configurations.pomOnlyProvided - * - * Also defines the pom defaults (name, desc, etc) unless overridden in gradle.properties - * Also sets up upload repositories - */ -class EhPomMangle implements Plugin { - - @Override - void apply(Project project) { - def utils = new Utils(project.baseVersion, project.logger) - - project.plugins.apply 'java-library' - project.plugins.apply 'maven' - project.plugins.apply 'signing' - - project.configurations { - shadowCompile - shadowProvided - pomOnlyCompile - pomOnlyProvided - } - - def artifactFiltering = { - project.configurations.forEach { - pom.scopeMappings.mappings.remove(it) - } - - pom.scopeMappings.addMapping(MavenPlugin.COMPILE_PRIORITY, project.configurations.shadowCompile, Conf2ScopeMappingContainer.COMPILE) - pom.scopeMappings.addMapping(MavenPlugin.COMPILE_PRIORITY, project.configurations.shadowProvided, Conf2ScopeMappingContainer.PROVIDED) - - //Anything extra to add to pom that isn't in the shadowed jar or compilation - pom.scopeMappings.addMapping(MavenPlugin.COMPILE_PRIORITY, project.configurations.pomOnlyCompile, Conf2ScopeMappingContainer.COMPILE) - pom.scopeMappings.addMapping(MavenPlugin.COMPILE_PRIORITY, project.configurations.pomOnlyProvided, Conf2ScopeMappingContainer.PROVIDED) - - utils.pomFiller(pom, project.subPomName, project.subPomDesc) - - } - - project.install { - repositories.mavenInstaller artifactFiltering - } - - project.uploadArchives { - repositories { - mavenDeployer ({ - beforeDeployment { MavenDeployment deployment -> project.signing.signPom(deployment)} - - if (project.isReleaseVersion) { - repository(url: project.deployUrl) { - authentication(userName: project.deployUser, password: project.deployPwd) - } - } else { - repository(id: 'sonatype-nexus-snapshot', url: 'https://oss.sonatype.org/content/repositories/snapshots') { - authentication(userName: project.sonatypeUser, password: project.sonatypePwd) - } - } - } << artifactFiltering) - } - } - - } -} diff --git a/buildSrc/src/main/groovy/scripts/Utils.groovy b/buildSrc/src/main/groovy/scripts/Utils.groovy deleted file mode 100644 index 6f46430b9d..0000000000 --- a/buildSrc/src/main/groovy/scripts/Utils.groovy +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package scripts - -import org.gradle.api.JavaVersion -import org.gradle.internal.jvm.Jvm - -class Utils { - - String version - String revision - boolean isReleaseVersion - - Utils(version, logger) { - this.version = version - this.isReleaseVersion = !version.endsWith('SNAPSHOT') - def tmp = System.getenv("GIT_COMMIT") - if(tmp != null) { - revision = tmp - } else { - logger.debug('Revision not found in system properties, trying command line') - def cmd = 'git rev-parse HEAD' - try { - def proc = cmd.execute() - revision = proc.text.trim() - } catch (IOException) { - revision = 'Unknown' - } - } - logger.debug(revision) - } - - def fillManifest(manifest, title) { - manifest.attributes( - 'provider': 'gradle', - 'Implementation-Title': title, - 'Implementation-Version': "$version $revision", - 'Built-By': System.getProperty('user.name'), - 'Built-JDK': System.getProperty('java.version')) - if (isReleaseVersion) { - manifest.attributes('Build-Time': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")) - } - } - - def pomFiller(pom, nameVar, descriptionVar) { - pom.withXml { - asNode().version[0] + { - name nameVar - description descriptionVar - url 'http://ehcache.org' - organization { - name 'Terracotta Inc., a wholly-owned subsidiary of Software AG USA, Inc.' - url 'http://terracotta.org' - } - issueManagement { - system 'Github' - url 'https://github.com/ehcache/ehcache3/issues' - } - scm { - url 'https://github.com/ehcache/ehcache3' - connection 'scm:git:https://github.com/ehcache/ehcache3.git' - developerConnection 'scm:git:git@github.com:ehcache/ehcache3.git' - } - licenses { - license { - name 'The Apache Software License, Version 2.0' - url 'http://www.apache.org/licenses/LICENSE-2.0.txt' - distribution 'repo' - } - } - developers { - developer { - name 'Terracotta Engineers' - email 'tc-oss@softwareag.com' - organization 'Terracotta Inc., a wholly-owned subsidiary of Software AG USA, Inc.' - organizationUrl 'http://ehcache.org' - } - } - } - } - } - - static def jvmForHome(File home) { - def java = Jvm.forHome(home).javaExecutable - def versionCommand = "$java -version".execute() - def version = JavaVersion.toVersion((versionCommand.err.text =~ /\w+ version "(.+)"/)[0][1]) - return Jvm.discovered(home, version) - } -} diff --git a/clustered/client/build.gradle b/clustered/client/build.gradle deleted file mode 100644 index 2198ebd21c..0000000000 --- a/clustered/client/build.gradle +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -apply plugin: EhDeploy - -dependencies { - compileOnly project(':impl') - compileOnly project(':xml') - implementation project(':clustered:common') - implementation "org.terracotta:statistics:$parent.statisticVersion" - providedImplementation "org.terracotta:entity-client-api:$terracottaApisVersion" - providedImplementation "org.terracotta:runnel:$terracottaPlatformVersion" - providedImplementation "org.terracotta:lease-api:$terracottaPlatformVersion" - providedImplementation "org.terracotta:connection-api:$terracottaApisVersion" - - testImplementation project(':api') - testImplementation project(':impl') - testImplementation project(':xml') - testImplementation project(':transactions') - testImplementation(project(':clustered:server')) { - exclude group: 'org.terracotta.internal', module: 'tc-config-parser' - } - testImplementation "org.terracotta:entity-test-lib:$terracottaPassthroughTestingVersion" - testImplementation "org.terracotta:passthrough-server:$terracottaPassthroughTestingVersion" - testImplementation "org.terracotta.internal:common:$terracottaCoreVersion" - testImplementation "org.terracotta:passthrough-leased-connection-api:$terracottaPlatformVersion" - testImplementation (group: 'org.codehaus.btm', name: 'btm', version: '2.1.4') { - exclude group:'org.slf4j', module:'slf4j-api' - } - testCompile project(path: ':xml', configuration: 'testArchives') -} - -test { - if (testJava.javaVersion.isJava9Compatible()) { - jvmArgs += ['--add-modules', 'java.xml.bind'] - } -} diff --git a/clustered/client/gradle.properties b/clustered/client/gradle.properties deleted file mode 100644 index 56c6dfbf5d..0000000000 --- a/clustered/client/gradle.properties +++ /dev/null @@ -1,20 +0,0 @@ -# -# Copyright Terracotta, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -subPomName = Ehcache 3 Client Side Clustering module -subPomDesc = The Client Side Clustering module of Ehcache 3 -osgi = {"Export-Package" : ["!org.ehcache.clustered.client.internal.*", "!sun.misc"],\ - "Import-Package" : ["!org.ehcache.clustered.client*", "!sun.misc*"]} diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/DelegatingLoaderWriterStoreProvider.java b/clustered/client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/DelegatingLoaderWriterStoreProvider.java deleted file mode 100644 index d0e72a93e2..0000000000 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/DelegatingLoaderWriterStoreProvider.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ehcache.clustered.client.internal.loaderwriter; - -import org.ehcache.clustered.client.service.ClusteringService; -import org.ehcache.clustered.client.service.ClusteringService.ClusteredCacheIdentifier; -import org.ehcache.config.ResourceType; -import org.ehcache.core.internal.store.StoreSupport; -import org.ehcache.core.spi.store.Store; -import org.ehcache.core.spi.store.WrapperStore; -import org.ehcache.impl.internal.concurrent.ConcurrentHashMap; -import org.ehcache.impl.internal.store.loaderwriter.LoaderWriterStoreProvider.StoreRef; -import org.ehcache.spi.loaderwriter.CacheLoaderWriterConfiguration; -import org.ehcache.spi.loaderwriter.CacheLoaderWriterProvider; -import org.ehcache.spi.service.Service; -import org.ehcache.spi.service.ServiceConfiguration; -import org.ehcache.spi.service.ServiceDependencies; -import org.ehcache.spi.service.ServiceProvider; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Map; -import java.util.Set; - -import static org.ehcache.core.spi.service.ServiceUtils.findSingletonAmongst; - -@ServiceDependencies({CacheLoaderWriterProvider.class, ClusteringService.class}) -public class DelegatingLoaderWriterStoreProvider implements WrapperStore.Provider { - private volatile ServiceProvider serviceProvider; - - private final Map, StoreRef> createdStores = new ConcurrentHashMap<>(); - - @Override - public Store createStore(Store.Configuration storeConfig, ServiceConfiguration... serviceConfigs) { - - Store.Provider underlyingStoreProvider = StoreSupport - .selectStoreProvider(serviceProvider, storeConfig.getResourcePools().getResourceTypeSet(), - Arrays.asList(serviceConfigs)); - - Store store = underlyingStoreProvider.createStore(storeConfig, serviceConfigs); - DelegatingLoaderWriterStore loaderWriterStore = new DelegatingLoaderWriterStore<>(store); - createdStores.put(loaderWriterStore, new StoreRef<>(store, underlyingStoreProvider)); - return loaderWriterStore; - } - - @Override - public void releaseStore(Store resource) { - StoreRef storeRef = createdStores.remove(resource); - storeRef.getUnderlyingStoreProvider().releaseStore(storeRef.getUnderlyingStore()); - } - - @Override - public void initStore(Store resource) { - StoreRef storeRef = createdStores.get(resource); - storeRef.getUnderlyingStoreProvider().initStore(storeRef.getUnderlyingStore()); - } - - @Override - public int rank(Set> resourceTypes, Collection> serviceConfigs) { - throw new UnsupportedOperationException("Its a Wrapper store provider, does not support regular ranking"); - } - - @Override - public void start(ServiceProvider serviceProvider) { - this.serviceProvider = serviceProvider; - } - - @Override - public void stop() { - this.serviceProvider = null; - } - - @Override - public int wrapperStoreRank(Collection> serviceConfigs) { - CacheLoaderWriterConfiguration loaderWriterConfiguration = findSingletonAmongst(CacheLoaderWriterConfiguration.class, serviceConfigs); - ClusteredCacheIdentifier clusteredCacheIdentifier = findSingletonAmongst(ClusteredCacheIdentifier.class, serviceConfigs); - if (clusteredCacheIdentifier != null && loaderWriterConfiguration != null) { - return 3; - } - return 0; - } -} diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ResolvedChain.java b/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ResolvedChain.java deleted file mode 100644 index 8c8a76473b..0000000000 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ResolvedChain.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ehcache.clustered.client.internal.store; - -import org.ehcache.clustered.common.internal.store.operations.Result; -import org.ehcache.clustered.common.internal.store.Chain; - -import java.util.Collections; -import java.util.Map; - -/** - * Represents the result of a {@link Chain} resolution. - * Implementors would be wrappers over the compacted chain and the resolved operations. - * A resolver may or may not have resolved all the different keys in a chain. - * - * @param the Key type - */ -public interface ResolvedChain { - - Chain getCompactedChain(); - - Result getResolvedResult(K key); - - /** - * Indicates whether the {@link #getCompactedChain()} is effectively compacted - * compared to the original chain it was built from. - * - * @return {@code true} if the chain has been compacted during resolution, {@code false} otherwise - */ - boolean isCompacted(); - - /** - * @return the number of chain elements that were compacted if there was any compaction - */ - int getCompactionCount(); - - /** - * @return the unix epoch at which the entry should expire - */ - long getExpirationTime(); - - /** - * Represents the {@link ResolvedChain} result of a resolver that resolves - * all the keys in a {@link Chain} - */ - class Impl implements ResolvedChain { - - private final Chain compactedChain; - private final Map> resolvedOperations; - private final int compactionCount; - private final long expirationTime; - - public Impl(Chain compactedChain, Map> resolvedOperations, int compactionCount, long expirationTime) { - this.compactedChain = compactedChain; - this.resolvedOperations = resolvedOperations; - this.compactionCount = compactionCount; - this.expirationTime = expirationTime; - } - - public Impl(Chain compactedChain, K key, Result result, int compactedSize, long expirationTime) { - this(compactedChain, Collections.singletonMap(key, result), compactedSize, expirationTime); - } - - public Chain getCompactedChain() { - return this.compactedChain; - } - - public Result getResolvedResult(K key) { - return resolvedOperations.get(key); - } - - @Override - public boolean isCompacted() { - return compactionCount > 0; - } - - public int getCompactionCount() { - return compactionCount; - } - - @Override - public long getExpirationTime() { - return expirationTime; - } - } -} diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ServerStoreProxy.java b/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ServerStoreProxy.java deleted file mode 100644 index b77c884cdd..0000000000 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ServerStoreProxy.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ehcache.clustered.client.internal.store; - -import org.ehcache.clustered.common.internal.store.Chain; -import org.ehcache.clustered.common.internal.store.ServerStore; - -/** - * @author Ludovic Orban - */ -public interface ServerStoreProxy extends ServerStore { - - /** - * The invalidation listener - */ - interface ServerCallback { - /** - * Callback for invalidation of hash requests - * - * @param hash the hash of the keys to invalidate - */ - void onInvalidateHash(long hash); - - /** - * Callback for invalidation of all requests - */ - void onInvalidateAll(); - - Chain compact(Chain chain); - - default Chain compact(Chain chain, long hash) { - return compact(chain); - } - } - - /** - * Gets the identifier linking a client-side cache to a {@code ServerStore} instance. - * - * @return the cache identifier - */ - String getCacheId(); - - /** - * Closes this proxy. - */ - void close(); - -} diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/operations/ChainResolver.java b/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/operations/ChainResolver.java deleted file mode 100644 index 09e30cd6c4..0000000000 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/operations/ChainResolver.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ehcache.clustered.client.internal.store.operations; - -import org.ehcache.clustered.client.internal.store.ChainBuilder; -import org.ehcache.clustered.client.internal.store.ResolvedChain; -import org.ehcache.clustered.common.internal.store.Chain; -import org.ehcache.clustered.common.internal.store.Element; -import org.ehcache.clustered.common.internal.store.operations.Operation; -import org.ehcache.clustered.common.internal.store.operations.PutOperation; -import org.ehcache.clustered.common.internal.store.operations.codecs.OperationsCodec; - -import java.nio.ByteBuffer; -import java.util.HashMap; -import java.util.Map; - -/** - * An abstract chain resolver. - *

- * Operation application is performed in subclasses specialized for eternal and non-eternal caches. - * - * @see EternalChainResolver - * @see ExpiryChainResolver - * - * @param key type - * @param value type - */ -public abstract class ChainResolver { - protected final OperationsCodec codec; - - public ChainResolver(final OperationsCodec codec) { - this.codec = codec; - } - - /** - * Extract the {@code Element}s from the provided {@code Chain} that are not associated with the provided key - * and create a new {@code Chain} - * - * Separate the {@code Element}s from the provided {@code Chain} that are associated and not associated with - * the provided key. Create a new chain with the unassociated {@code Element}s. Resolve the associated elements - * and append the resolved {@code Element} to the newly created chain. - * - * @param chain a heterogeneous {@code Chain} - * @param key a key - * @param now time when the chain is being resolved - * @return a resolved chain, result of resolution of chain provided - */ - public ResolvedChain resolve(Chain chain, K key, long now) { - PutOperation result = null; - ChainBuilder newChainBuilder = new ChainBuilder(); - boolean matched = false; - for (Element element : chain) { - ByteBuffer payload = element.getPayload(); - Operation operation = codec.decode(payload); - - if(key.equals(operation.getKey())) { - matched = true; - result = applyOperation(key, result, operation, now); - } else { - payload.rewind(); - newChainBuilder = newChainBuilder.add(payload); - } - } - - if(result == null) { - if (matched) { - Chain newChain = newChainBuilder.build(); - return new ResolvedChain.Impl<>(newChain, key, null, chain.length() - newChain.length(), Long.MAX_VALUE); - } else { - return new ResolvedChain.Impl<>(chain, key, null, 0, Long.MAX_VALUE); - } - } else { - Chain newChain = newChainBuilder.add(codec.encode(result)).build(); - return new ResolvedChain.Impl<>(newChain, key, result, chain.length() - newChain.length(), result.expirationTime()); - } - } - - /** - * Compacts the given chain by resolving every key within. - * - * @param chain a compacted heterogenous {@code Chain} - * @param now time when the chain is being resolved - * @return a compacted chain - */ - public Chain applyOperation(Chain chain, long now) { - //absent hash-collisions this should always be a 1 entry map - Map> compacted = new HashMap<>(2); - for (Element element : chain) { - ByteBuffer payload = element.getPayload(); - Operation operation = codec.decode(payload); - compacted.compute(operation.getKey(), (k, v) -> applyOperation(k, v, operation, now)); - } - - ChainBuilder builder = new ChainBuilder(); - for (PutOperation operation : compacted.values()) { - builder = builder.add(codec.encode(operation)); - } - return builder.build(); - } - - /** - * Applies the given operation to the current state at the time specified. - * - * @param key cache key - * @param existing current state - * @param operation operation to apply - * @param now current time - * @return an equivalent put operation - */ - public abstract PutOperation applyOperation(K key, PutOperation existing, Operation operation, long now); -} diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/config/xml/ClusteredResourceConfigurationParserTest.java b/clustered/client/src/test/java/org/ehcache/clustered/client/internal/config/xml/ClusteredResourceConfigurationParserTest.java deleted file mode 100644 index 2b490639bc..0000000000 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/config/xml/ClusteredResourceConfigurationParserTest.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ehcache.clustered.client.internal.config.xml; - -import org.ehcache.clustered.client.internal.config.ClusteredResourcePoolImpl; -import org.ehcache.clustered.client.internal.config.DedicatedClusteredResourcePoolImpl; -import org.ehcache.clustered.client.internal.config.SharedClusteredResourcePoolImpl; -import org.ehcache.config.units.MemoryUnit; -import org.junit.Test; -import org.w3c.dom.Node; - -import static org.ehcache.xml.ConfigurationParserTestHelper.assertElement; - -/** - * ClusteredResourceConfigurationParserTest - */ -public class ClusteredResourceConfigurationParserTest { - - @Test - public void testTranslateClusteredResourcePoolConfiguration() { - ClusteredResourceConfigurationParser configTranslator = new ClusteredResourceConfigurationParser(); - ClusteredResourcePoolImpl clusteredResourcePool = new ClusteredResourcePoolImpl(); - Node retElement = configTranslator.unparseResourcePool(clusteredResourcePool); - String inputString = ""; - assertElement(inputString, retElement); - } - - @Test - public void testTranslateDedicatedResourcePoolConfiguration() { - ClusteredResourceConfigurationParser configTranslator = new ClusteredResourceConfigurationParser(); - DedicatedClusteredResourcePoolImpl dedicatedClusteredResourcePool = new DedicatedClusteredResourcePoolImpl("my-from", 12, MemoryUnit.GB); - Node retElement = configTranslator.unparseResourcePool(dedicatedClusteredResourcePool); - String inputString = "12"; - assertElement(inputString, retElement); - } - - @Test - public void testTranslateSharedResourcePoolConfiguration() { - ClusteredResourceConfigurationParser configTranslator = new ClusteredResourceConfigurationParser(); - SharedClusteredResourcePoolImpl sharedResourcePool = new SharedClusteredResourcePoolImpl("shared-pool"); - Node retElement = configTranslator.unparseResourcePool(sharedResourcePool); - String inputString = ""; - assertElement(inputString, retElement); - } - -} diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/ChainBuilderTest.java b/clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/ChainBuilderTest.java deleted file mode 100644 index 725ba42a48..0000000000 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/ChainBuilderTest.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ehcache.clustered.client.internal.store; - -import org.ehcache.clustered.common.internal.store.Chain; -import org.ehcache.clustered.common.internal.store.Element; -import org.ehcache.clustered.common.internal.store.Util; -import org.junit.Test; - -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; - -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; - -/** - */ -public class ChainBuilderTest { - - @Test - public void testChainBuilder() { - ChainBuilder cb1 = new ChainBuilder(); - - ChainBuilder cb2 = cb1.add(Util.createPayload(1L)) - .add(Util.createPayload(3L)) - .add(Util.createPayload(4L)); - - ChainBuilder cb3 = cb2.add(Util.createPayload(2L)); - - Chain chain1 = cb1.build(); - Chain chain2 = cb2.build(); - Chain chain3 = cb3.build(); - - assertChainHas(chain1); - assertChainHas(chain2, 1L, 3L, 4L); - assertChainHas(chain3, 1L, 3L, 4L, 2L); - - } - - private static void assertChainHas(Chain chain, long... payLoads) { - Iterator elements = chain.iterator(); - for (long payLoad : payLoads) { - assertThat(Util.readPayLoad(elements.next().getPayload()), is(Long.valueOf(payLoad))); - } - assertThat(elements.hasNext(), is(false)); - } -} diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/CommonServerStoreProxyTest.java b/clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/CommonServerStoreProxyTest.java deleted file mode 100644 index 0f7e9bf24b..0000000000 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/CommonServerStoreProxyTest.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ehcache.clustered.client.internal.store; - -import org.ehcache.clustered.client.config.ClusteredResourcePool; -import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder; -import org.ehcache.clustered.client.internal.store.ServerStoreProxy.ServerCallback; -import org.ehcache.clustered.common.internal.ServerStoreConfiguration; -import org.ehcache.clustered.common.internal.store.Chain; -import org.ehcache.clustered.common.internal.store.Element; -import org.ehcache.config.units.MemoryUnit; -import org.ehcache.impl.serialization.LongSerializer; -import org.junit.Test; - -import java.nio.ByteBuffer; -import java.util.Iterator; - -import static org.ehcache.clustered.common.internal.store.Util.createPayload; -import static org.ehcache.clustered.common.internal.store.Util.getChain; -import static org.ehcache.clustered.common.internal.store.Util.readPayLoad; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class CommonServerStoreProxyTest extends AbstractServerStoreProxyTest { - - private static ClusterTierClientEntity createClientEntity(String name) throws Exception { - ClusteredResourcePool resourcePool = ClusteredResourcePoolBuilder.clusteredDedicated(8L, MemoryUnit.MB); - - ServerStoreConfiguration serverStoreConfiguration = new ServerStoreConfiguration(resourcePool.getPoolAllocation(), Long.class - .getName(), - Long.class.getName(), LongSerializer.class.getName(), LongSerializer.class - .getName(), null, false); - - return createClientEntity(name, serverStoreConfiguration, true); - - } - - @Test - public void testGetKeyNotPresent() throws Exception { - ClusterTierClientEntity clientEntity = createClientEntity("testGetKeyNotPresent"); - CommonServerStoreProxy serverStoreProxy = new CommonServerStoreProxy("testGetKeyNotPresent", clientEntity, mock(ServerCallback.class)); - - Chain chain = serverStoreProxy.get(1); - - assertThat(chain.isEmpty(), is(true)); - } - - @Test - public void testAppendKeyNotPresent() throws Exception { - ClusterTierClientEntity clientEntity = createClientEntity("testAppendKeyNotPresent"); - CommonServerStoreProxy serverStoreProxy = new CommonServerStoreProxy("testAppendKeyNotPresent", clientEntity, mock(ServerCallback.class)); - - serverStoreProxy.append(2, createPayload(2)); - - Chain chain = serverStoreProxy.get(2); - assertThat(chain.isEmpty(), is(false)); - assertThat(readPayLoad(chain.iterator().next().getPayload()), is(2L)); - } - - @Test - public void testGetAfterMultipleAppendsOnSameKey() throws Exception { - ClusterTierClientEntity clientEntity = createClientEntity("testGetAfterMultipleAppendsOnSameKey"); - CommonServerStoreProxy serverStoreProxy = new CommonServerStoreProxy("testGetAfterMultipleAppendsOnSameKey", clientEntity, mock(ServerCallback.class)); - - serverStoreProxy.append(3L, createPayload(3L)); - serverStoreProxy.append(3L, createPayload(33L)); - serverStoreProxy.append(3L, createPayload(333L)); - - Chain chain = serverStoreProxy.get(3L); - - assertThat(chain.isEmpty(), is(false)); - - assertChainHas(chain, 3L, 33L, 333l); - } - - @Test - public void testGetAndAppendKeyNotPresent() throws Exception { - ClusterTierClientEntity clientEntity = createClientEntity("testGetAndAppendKeyNotPresent"); - CommonServerStoreProxy serverStoreProxy = new CommonServerStoreProxy("testGetAndAppendKeyNotPresent", clientEntity, mock(ServerCallback.class)); - Chain chain = serverStoreProxy.getAndAppend(4L, createPayload(4L)); - - assertThat(chain.isEmpty(), is(true)); - - chain = serverStoreProxy.get(4L); - - assertThat(chain.isEmpty(), is(false)); - assertChainHas(chain, 4L); - } - - @Test - public void testGetAndAppendMultipleTimesOnSameKey() throws Exception { - ClusterTierClientEntity clientEntity = createClientEntity("testGetAndAppendMultipleTimesOnSameKey"); - CommonServerStoreProxy serverStoreProxy = new CommonServerStoreProxy("testGetAndAppendMultipleTimesOnSameKey", clientEntity, mock(ServerCallback.class)); - serverStoreProxy.getAndAppend(5L, createPayload(5L)); - serverStoreProxy.getAndAppend(5L, createPayload(55L)); - serverStoreProxy.getAndAppend(5L, createPayload(555L)); - Chain chain = serverStoreProxy.getAndAppend(5l, createPayload(5555L)); - - assertThat(chain.isEmpty(), is(false)); - assertChainHas(chain, 5L, 55L, 555L); - } - - @Test - public void testReplaceAtHeadSuccessFull() throws Exception { - ClusterTierClientEntity clientEntity = createClientEntity("testReplaceAtHeadSuccessFull"); - CommonServerStoreProxy serverStoreProxy = new CommonServerStoreProxy("testReplaceAtHeadSuccessFull", clientEntity, mock(ServerCallback.class)); - serverStoreProxy.append(20L, createPayload(200L)); - serverStoreProxy.append(20L, createPayload(2000L)); - serverStoreProxy.append(20L, createPayload(20000L)); - - Chain expect = serverStoreProxy.get(20L); - Chain update = getChain(false, createPayload(400L)); - - serverStoreProxy.replaceAtHead(20l, expect, update); - - Chain afterReplace = serverStoreProxy.get(20L); - assertChainHas(afterReplace, 400L); - - serverStoreProxy.append(20L, createPayload(4000L)); - serverStoreProxy.append(20L, createPayload(40000L)); - - serverStoreProxy.replaceAtHead(20L, afterReplace, getChain(false, createPayload(800L))); - - Chain anotherReplace = serverStoreProxy.get(20L); - - assertChainHas(anotherReplace, 800L, 4000L, 40000L); - } - - @Test - public void testClear() throws Exception { - ClusterTierClientEntity clientEntity = createClientEntity("testClear"); - CommonServerStoreProxy serverStoreProxy = new CommonServerStoreProxy("testClear", clientEntity, mock(ServerCallback.class)); - serverStoreProxy.append(1L, createPayload(100L)); - - serverStoreProxy.clear(); - Chain chain = serverStoreProxy.get(1); - assertThat(chain.isEmpty(), is(true)); - } - - @Test - public void testResolveRequestIsProcessedAtThreshold() throws Exception { - ByteBuffer buffer = createPayload(42L); - - ClusterTierClientEntity clientEntity = createClientEntity("testResolveRequestIsProcessed"); - ServerCallback serverCallback = mock(ServerCallback.class); - when(serverCallback.compact(any(Chain.class), any(long.class))).thenReturn(getChain(false, buffer.duplicate())); - CommonServerStoreProxy serverStoreProxy = new CommonServerStoreProxy("testResolveRequestIsProcessed", clientEntity, serverCallback); - - for (int i = 0; i < 8; i++) { - serverStoreProxy.append(1L, buffer.duplicate()); - } - verify(serverCallback, never()).compact(any(Chain.class)); - assertChainHas(serverStoreProxy.get(1L), 42L, 42L, 42L, 42L, 42L, 42L, 42L, 42L); - - //trigger compaction at > 8 entries - serverStoreProxy.append(1L, buffer.duplicate()); - verify(serverCallback).compact(any(Chain.class), any(long.class)); - assertChainHas(serverStoreProxy.get(1L), 42L); - } - - private static void assertChainHas(Chain chain, long... payLoads) { - Iterator elements = chain.iterator(); - for (long payLoad : payLoads) { - assertThat(readPayLoad(elements.next().getPayload()), is(Long.valueOf(payLoad))); - } - assertThat(elements.hasNext(), is(false)); - } - -} diff --git a/clustered/client/src/test/java/org/ehcache/clustered/common/internal/store/operations/EternalChainResolverTest.java b/clustered/client/src/test/java/org/ehcache/clustered/common/internal/store/operations/EternalChainResolverTest.java deleted file mode 100644 index a2b03aa605..0000000000 --- a/clustered/client/src/test/java/org/ehcache/clustered/common/internal/store/operations/EternalChainResolverTest.java +++ /dev/null @@ -1,515 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ehcache.clustered.common.internal.store.operations; - -import org.ehcache.clustered.client.internal.store.ChainBuilder; -import org.ehcache.clustered.client.internal.store.ResolvedChain; -import org.ehcache.clustered.client.internal.store.operations.EternalChainResolver; -import org.ehcache.clustered.common.internal.store.operations.codecs.OperationsCodec; -import org.ehcache.clustered.common.internal.store.Chain; -import org.ehcache.clustered.common.internal.store.Element; -import org.ehcache.impl.serialization.LongSerializer; -import org.ehcache.impl.serialization.StringSerializer; -import org.hamcrest.Description; -import org.hamcrest.Matcher; -import org.hamcrest.TypeSafeMatcher; -import org.junit.Test; - -import java.nio.ByteBuffer; - -import static org.hamcrest.Matchers.emptyIterable; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder; -import static org.hamcrest.collection.IsIterableContainingInOrder.contains; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; - -public class EternalChainResolverTest { - - private static OperationsCodec codec = new OperationsCodec<>(new LongSerializer(), new StringSerializer()); - - @Test - @SuppressWarnings("unchecked") - public void testResolveMaintainsOtherKeysInOrder() throws Exception { - Operation expected = new PutOperation<>(1L, "Suresh", 0L); - Chain chain = getChainFromOperations( - new PutOperation<>(1L, "Albin", 0L), - new PutOperation<>(2L, "Albin", 0L), - expected, - new PutOperation<>(2L, "Suresh", 0L), - new PutOperation<>(2L, "Mathew", 0L)); - - EternalChainResolver resolver = new EternalChainResolver<>(codec); - ResolvedChain resolvedChain = resolver.resolve(chain, 1L, 0L); - Result result = resolvedChain.getResolvedResult(1L); - assertEquals(expected, result); - assertThat(resolvedChain.isCompacted(), is(true)); - - Chain compactedChain = resolvedChain.getCompactedChain(); - assertThat(compactedChain, contains( //@SuppressWarnings("unchecked") - operation(new PutOperation<>(2L, "Albin", 0L)), - operation(new PutOperation<>(2L, "Suresh", 0L)), - operation(new PutOperation<>(2L, "Mathew", 0L)), - operation(new PutOperation<>(1L, "Suresh", 0L)))); - } - - @Test - public void testResolveEmptyChain() throws Exception { - Chain chain = getChainFromOperations(); - EternalChainResolver resolver = new EternalChainResolver<>(codec); - ResolvedChain resolvedChain = resolver.resolve(chain, 1L, 0L); - Result result = resolvedChain.getResolvedResult(1L); - assertNull(result); - - assertThat(resolvedChain.isCompacted(), is(false)); - } - - @Test - public void testResolveChainWithNonExistentKey() throws Exception { - Chain chain = getChainFromOperations( - new PutOperation<>(1L, "Albin", 0L), - new PutOperation<>(2L, "Suresh", 0L), - new PutOperation<>(2L, "Mathew", 0L)); - - EternalChainResolver resolver = new EternalChainResolver<>(codec); - ResolvedChain resolvedChain = resolver.resolve(chain, 3L, 0L); - Result result = resolvedChain.getResolvedResult(3L); - assertNull(result); - assertThat(resolvedChain.isCompacted(), is(false)); - } - - @Test - public void testResolveSinglePut() throws Exception { - Operation expected = new PutOperation<>(1L, "Albin", 0L); - Chain chain = getChainFromOperations(expected); - - EternalChainResolver resolver = new EternalChainResolver<>(codec); - ResolvedChain resolvedChain = resolver.resolve(chain, 1L, 0L); - Result result = resolvedChain.getResolvedResult(1L); - assertEquals(expected, result); - assertThat(resolvedChain.isCompacted(), is(false)); - } - - @Test - public void testResolvePutsOnly() throws Exception { - Operation expected = new PutOperation<>(1L, "Mathew", 0L); - - Chain chain = getChainFromOperations( - new PutOperation<>(1L, "Albin", 0L), - new PutOperation<>(1L, "Suresh", 0L), - new PutOperation<>(1L, "Mathew", 0L)); - - EternalChainResolver resolver = new EternalChainResolver<>(codec); - ResolvedChain resolvedChain = resolver.resolve(chain, 1L, 0L); - Result result = resolvedChain.getResolvedResult(1L); - assertEquals(expected, result); - assertThat(resolvedChain.isCompacted(), is(true)); - assertThat(resolvedChain.getCompactionCount(), is(2)); - } - - @Test - public void testResolveSingleRemove() throws Exception { - Chain chain = getChainFromOperations(new RemoveOperation<>(1L, 0L)); - - EternalChainResolver resolver = new EternalChainResolver<>(codec); - ResolvedChain resolvedChain = resolver.resolve(chain, 1L, 0L); - Result result = resolvedChain.getResolvedResult(1L); - assertNull(result); - assertThat(resolvedChain.isCompacted(), is(true)); - assertThat(resolvedChain.getCompactionCount(), is(1)); - } - - @Test - public void testResolveRemovesOnly() throws Exception { - Chain chain = getChainFromOperations( - new RemoveOperation<>(1L, 0L), - new RemoveOperation<>(1L, 0L)); - - EternalChainResolver resolver = new EternalChainResolver<>(codec); - ResolvedChain resolvedChain = resolver.resolve(chain, 1L, 0L); - Result result = resolvedChain.getResolvedResult(1L); - assertNull(result); - assertThat(resolvedChain.isCompacted(), is(true)); - assertThat(resolvedChain.getCompactionCount(), is(2)); - } - - @Test - public void testPutAndRemove() throws Exception { - Chain chain = getChainFromOperations( - new PutOperation<>(1L, "Albin", 0L), - new RemoveOperation<>(1L, 0L)); - - EternalChainResolver resolver = new EternalChainResolver<>(codec); - ResolvedChain resolvedChain = resolver.resolve(chain, 1L, 0L); - Result result = resolvedChain.getResolvedResult(1L); - assertNull(result); - assertThat(resolvedChain.isCompacted(), is(true)); - } - - @Test - public void testResolvePutIfAbsentOnly() throws Exception { - Operation expected = new PutOperation<>(1L, "Mathew", 0L); - Chain chain = getChainFromOperations(new PutIfAbsentOperation<>(1L, "Mathew", 0L)); - - EternalChainResolver resolver = new EternalChainResolver<>(codec); - ResolvedChain resolvedChain = resolver.resolve(chain, 1L, 0L); - Result result = resolvedChain.getResolvedResult(1L); - assertEquals(expected, result); - assertThat(resolvedChain.isCompacted(), is(false)); - } - - @Test - public void testResolvePutIfAbsentsOnly() throws Exception { - Operation expected = new PutOperation<>(1L, "Albin", 0L); - Chain chain = getChainFromOperations( - new PutIfAbsentOperation<>(1L, "Albin", 0L), - new PutIfAbsentOperation<>(1L, "Suresh", 0L), - new PutIfAbsentOperation<>(1L, "Mathew", 0L)); - - EternalChainResolver resolver = new EternalChainResolver<>(codec); - ResolvedChain resolvedChain = resolver.resolve(chain, 1L, 0L); - Result result = resolvedChain.getResolvedResult(1L); - assertEquals(expected, result); - assertThat(resolvedChain.isCompacted(), is(true)); - } - - @Test - public void testResolvePutIfAbsentSucceeds() throws Exception { - Operation expected = new PutOperation<>(1L, "Mathew", 0L); - Chain chain = getChainFromOperations( - new PutOperation<>(1L, "Albin", 0L), - new RemoveOperation<>(1L, 0L), - new PutIfAbsentOperation<>(1L, "Mathew", 0L)); - - EternalChainResolver resolver = new EternalChainResolver<>(codec); - ResolvedChain resolvedChain = resolver.resolve(chain, 1L, 0L); - Result result = resolvedChain.getResolvedResult(1L); - assertEquals(expected, result); - assertThat(resolvedChain.isCompacted(), is(true)); - } - - @Test - public void testResolveForSingleOperationDoesNotCompact() { - Chain chain = getChainFromOperations(new PutOperation<>(1L, "Albin", 0L)); - - EternalChainResolver resolver = new EternalChainResolver<>(codec); - ResolvedChain resolvedChain = resolver.resolve(chain, 1L, 0L); - assertThat(resolvedChain.isCompacted(), is(false)); - assertThat(resolvedChain.getCompactionCount(), is(0)); - } - - @Test - public void testResolveForMultiplesOperationsAlwaysCompact() { - //create a random mix of operations - Chain chain = getChainFromOperations( - new PutIfAbsentOperation<>(1L, "Albin", 0L), - new PutOperation<>(1L, "Suresh", 0L), - new PutOperation<>(1L, "Mathew", 0L), - new PutOperation<>(2L, "Melbin", 0L), - new ReplaceOperation<>(1L, "Joseph", 0L), - new RemoveOperation<>(2L, 0L), - new ConditionalRemoveOperation<>(1L, "Albin", 0L), - new PutOperation<>(1L, "Gregory", 0L), - new ConditionalReplaceOperation<>(1L, "Albin", "Abraham", 0L), - new RemoveOperation<>(1L, 0L), - new PutIfAbsentOperation<>(2L, "Albin", 0L)); - - EternalChainResolver resolver = new EternalChainResolver<>(codec); - ResolvedChain resolvedChain = resolver.resolve(chain, 1L, 0L); - assertThat(resolvedChain.isCompacted(), is(true)); - assertThat(resolvedChain.getCompactionCount(), is(8)); - } - - @Test - public void testResolveDoesNotDecodeOtherKeyOperationValues() throws Exception { - Chain chain = getChainFromOperations( - new PutOperation<>(2L, "Albin", 0L), - new PutOperation<>(2L, "Suresh", 0L), - new PutOperation<>(2L, "Mathew", 0L)); - - CountingLongSerializer keySerializer = new CountingLongSerializer(); - CountingStringSerializer valueSerializer = new CountingStringSerializer(); - OperationsCodec customCodec = new OperationsCodec<>(keySerializer, valueSerializer); - EternalChainResolver resolver = new EternalChainResolver<>(customCodec); - resolver.resolve(chain, 1L, 0L); - - assertThat(keySerializer.decodeCount, is(3)); - assertThat(valueSerializer.decodeCount, is(0)); - assertThat(keySerializer.encodeCount, is(0)); - assertThat(valueSerializer.encodeCount, is(0)); //No operation to resolve - } - - @Test - public void testResolveDecodesOperationValueOnlyOnDemand() throws Exception { - Chain chain = getChainFromOperations( - new PutOperation<>(1L, "Albin", 1), - new PutOperation<>(1L, "Suresh", 2), - new PutOperation<>(1L, "Mathew", 3)); - - CountingLongSerializer keySerializer = new CountingLongSerializer(); - CountingStringSerializer valueSerializer = new CountingStringSerializer(); - OperationsCodec customCodec = new OperationsCodec<>(keySerializer, valueSerializer); - EternalChainResolver resolver = new EternalChainResolver<>(customCodec); - resolver.resolve(chain, 1L, 0L); - - assertThat(keySerializer.decodeCount, is(3)); - assertThat(valueSerializer.decodeCount, is(0)); - assertThat(valueSerializer.encodeCount, is(0)); - assertThat(keySerializer.encodeCount, is(1)); //One encode from encoding the resolved operation's key - } - - @Test - @SuppressWarnings("unchecked") - public void testCompactingTwoKeys() throws Exception { - Chain chain = getChainFromOperations( - new PutOperation<>(1L, "Albin", 0L), - new PutOperation<>(2L, "Albin", 0L), - new PutOperation<>(1L, "Suresh", 0L), - new PutOperation<>(2L, "Suresh", 0L), - new PutOperation<>(2L, "Mathew", 0L)); - - EternalChainResolver resolver = new EternalChainResolver<>(codec); - - Chain compactedChain = resolver.applyOperation(chain, 0L); - - assertThat(compactedChain, containsInAnyOrder( //@SuppressWarnings("unchecked") - operation(new PutOperation<>(2L, "Mathew", 0L)), - operation(new PutOperation<>(1L, "Suresh", 0L)) - )); - } - - @Test - public void testCompactEmptyChain() throws Exception { - Chain chain = (new ChainBuilder()).build(); - EternalChainResolver resolver = new EternalChainResolver<>(codec); - Chain compacted = resolver.applyOperation(chain, 0L); - assertThat(compacted, emptyIterable()); - } - - @Test - public void testCompactSinglePut() throws Exception { - Chain chain = getChainFromOperations( - new PutOperation<>(1L, "Albin", 0L) - ); - - EternalChainResolver resolver = new EternalChainResolver<>(codec); - Chain compacted = resolver.applyOperation(chain, 0L); - - assertThat(compacted, contains(operation(new PutOperation<>(1L, "Albin", 0L)))); - } - - @Test - public void testCompactMultiplePuts() throws Exception { - Chain chain = getChainFromOperations( - new PutOperation<>(1L, "Albin", 0L), - new PutOperation<>(1L, "Suresh", 0L), - new PutOperation<>(1L, "Mathew", 0L)); - - EternalChainResolver resolver = new EternalChainResolver<>(codec); - Chain compactedChain = resolver.applyOperation(chain, 0L); - assertThat(compactedChain, contains(operation(new PutOperation<>(1L, "Mathew", 0L)))); - } - - @Test - public void testCompactSingleRemove() throws Exception { - Chain chain = getChainFromOperations(new RemoveOperation<>(1L, 0L)); - - EternalChainResolver resolver = new EternalChainResolver<>(codec); - Chain compactedChain = resolver.applyOperation(chain, 0L); - assertThat(compactedChain, emptyIterable()); - } - - @Test - public void testCompactMultipleRemoves() throws Exception { - Chain chain = getChainFromOperations( - new RemoveOperation<>(1L, 0L), - new RemoveOperation<>(1L, 0L)); - - EternalChainResolver resolver = new EternalChainResolver<>(codec); - Chain compactedChain = resolver.applyOperation(chain, 0L); - assertThat(compactedChain, emptyIterable()); - } - - @Test - public void testCompactPutAndRemove() throws Exception { - Chain chain = getChainFromOperations( - new PutOperation<>(1L, "Albin", 0L), - new RemoveOperation<>(1L, 0L)); - - EternalChainResolver resolver = new EternalChainResolver<>(codec); - Chain compactedChain = resolver.applyOperation(chain, 0L); - assertThat(compactedChain, emptyIterable()); - } - - @Test - public void testCompactSinglePutIfAbsent() throws Exception { - Chain chain = getChainFromOperations(new PutIfAbsentOperation<>(1L, "Mathew", 0L)); - - EternalChainResolver resolver = new EternalChainResolver<>(codec); - Chain compactedChain = resolver.applyOperation(chain, 0L); - assertThat(compactedChain, contains(operation(new PutOperation<>(1L, "Mathew", 0L)))); - } - - @Test - public void testCompactMultiplePutIfAbsents() throws Exception { - Chain chain = getChainFromOperations( - new PutIfAbsentOperation<>(1L, "Albin", 0L), - new PutIfAbsentOperation<>(1L, "Suresh", 0L), - new PutIfAbsentOperation<>(1L, "Mathew", 0L)); - - EternalChainResolver resolver = new EternalChainResolver<>(codec); - Chain compactedChain = resolver.applyOperation(chain, 0L); - assertThat(compactedChain, contains(operation(new PutOperation<>(1L, "Albin", 0L)))); - } - - @Test - public void testCompactPutIfAbsentAfterRemove() throws Exception { - Chain chain = getChainFromOperations( - new PutOperation<>(1L, "Albin", 0L), - new RemoveOperation<>(1L, 0L), - new PutIfAbsentOperation<>(1L, "Mathew", 0L)); - - EternalChainResolver resolver = new EternalChainResolver<>(codec); - Chain compactedChain = resolver.applyOperation(chain, 0L); - assertThat(compactedChain, contains(operation(new PutOperation<>(1L, "Mathew", 0L)))); - } - - @Test - public void testCompactForMultipleKeysAndOperations() { - //create a random mix of operations - Chain chain = getChainFromOperations( - new PutIfAbsentOperation<>(1L, "Albin", 0L), - new PutOperation<>(1L, "Suresh", 0L), - new PutOperation<>(1L, "Mathew", 0L), - new PutOperation<>(2L, "Melbin", 0L), - new ReplaceOperation<>(1L, "Joseph", 0L), - new RemoveOperation<>(2L, 0L), - new ConditionalRemoveOperation<>(1L, "Albin", 0L), - new PutOperation<>(1L, "Gregory", 0L), - new ConditionalReplaceOperation<>(1L, "Albin", "Abraham", 0L), - new RemoveOperation<>(1L, 0L), - new PutIfAbsentOperation<>(2L, "Albin", 0L)); - - EternalChainResolver resolver = new EternalChainResolver<>(codec); - Chain compactedChain = resolver.applyOperation(chain, 0L); - assertThat(compactedChain, contains(operation(new PutOperation<>(2L, "Albin", 0L)))); - } - - @Test - public void testCompactHasCorrectTimeStamp() { - Chain chain = getChainFromOperations( - new PutOperation<>(1L, "Albin1", 0), - new PutOperation<>(1L, "Albin2", 1), - new RemoveOperation<>(1L, 2), - new PutOperation<>(1L, "Albin3", 3)); - - EternalChainResolver resolver = new EternalChainResolver<>(codec); - Chain compactedChain = resolver.applyOperation(chain, 3); - - assertThat(compactedChain, contains(operation(new PutOperation<>(1L, "Albin3", 3)))); - } - - @Test - public void testCompactDecodesOperationValueOnlyOnDemand() throws Exception { - Chain chain = getChainFromOperations( - new PutOperation<>(1L, "Albin", 1), - new PutOperation<>(1L, "Suresh", 2), - new PutOperation<>(1L, "Mathew", 3)); - - CountingLongSerializer keySerializer = new CountingLongSerializer(); - CountingStringSerializer valueSerializer = new CountingStringSerializer(); - OperationsCodec customCodec = new OperationsCodec<>(keySerializer, valueSerializer); - EternalChainResolver resolver = new EternalChainResolver<>(customCodec); - resolver.applyOperation(chain, 0L); - - assertThat(keySerializer.decodeCount, is(3)); - assertThat(valueSerializer.decodeCount, is(0)); //Only one decode on creation of the resolved operation - assertThat(valueSerializer.encodeCount, is(0)); //One encode from encoding the resolved operation's key - assertThat(keySerializer.encodeCount, is(1)); //One encode from encoding the resolved operation's key - } - - @SafeVarargs - private final Chain getChainFromOperations(Operation ... operations) { - ChainBuilder chainBuilder = new ChainBuilder(); - for(Operation operation: operations) { - chainBuilder = chainBuilder.add(codec.encode(operation)); - } - return chainBuilder.build(); - } - - private Matcher operation(Operation operation) { - return new TypeSafeMatcher() { - @Override - protected boolean matchesSafely(Element item) { - return operation.equals(codec.decode(item.getPayload())); - } - - @Override - public void describeTo(Description description) { - description.appendText("is ").appendValue(operation); - } - }; - } - - private static class CountingLongSerializer extends LongSerializer { - - private int encodeCount = 0; - private int decodeCount = 0; - - @Override - public ByteBuffer serialize(final Long object) { - encodeCount++; - return super.serialize(object); - } - - @Override - public Long read(final ByteBuffer binary) throws ClassNotFoundException { - decodeCount++; - return super.read(binary); - } - - @Override - public boolean equals(final Long object, final ByteBuffer binary) throws ClassNotFoundException { - return super.equals(object, binary); - } - } - - private static class CountingStringSerializer extends StringSerializer { - - private int encodeCount = 0; - private int decodeCount = 0; - - @Override - public ByteBuffer serialize(final String object) { - encodeCount++; - return super.serialize(object); - } - - @Override - public String read(final ByteBuffer binary) throws ClassNotFoundException { - decodeCount++; - return super.read(binary); - } - - @Override - public boolean equals(final String object, final ByteBuffer binary) throws ClassNotFoundException { - return super.equals(object, binary); - } - } -} diff --git a/clustered/client/src/test/java/org/ehcache/clustered/common/internal/store/operations/ExpiryChainResolverExpiryTest.java b/clustered/client/src/test/java/org/ehcache/clustered/common/internal/store/operations/ExpiryChainResolverExpiryTest.java deleted file mode 100644 index f1468cb6c4..0000000000 --- a/clustered/client/src/test/java/org/ehcache/clustered/common/internal/store/operations/ExpiryChainResolverExpiryTest.java +++ /dev/null @@ -1,293 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ehcache.clustered.common.internal.store.operations; - -import org.ehcache.clustered.client.TestTimeSource; -import org.ehcache.clustered.client.internal.store.ChainBuilder; -import org.ehcache.clustered.client.internal.store.ResolvedChain; -import org.ehcache.clustered.client.internal.store.operations.ExpiryChainResolver; -import org.ehcache.clustered.common.internal.store.operations.codecs.OperationsCodec; -import org.ehcache.clustered.common.internal.store.Chain; -import org.ehcache.clustered.common.internal.store.Element; -import org.ehcache.expiry.ExpiryPolicy; -import org.ehcache.impl.serialization.LongSerializer; -import org.ehcache.impl.serialization.StringSerializer; -import org.junit.Before; -import org.junit.Test; -import org.mockito.InOrder; - -import java.time.Duration; -import java.util.ArrayList; -import java.util.List; - -import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.hamcrest.Matchers.is; - -public class ExpiryChainResolverExpiryTest { - - private static final OperationsCodec codec = new OperationsCodec<>(new LongSerializer(), new StringSerializer()); - - private static TestTimeSource timeSource = null; - - @Before - public void initialSetup() { - timeSource = new TestTimeSource(); - } - - @Test - @SuppressWarnings("unchecked") - public void testGetExpiryForAccessIsIgnored() { - ExpiryPolicy expiry = mock(ExpiryPolicy.class); - ExpiryChainResolver chainResolver = new ExpiryChainResolver<>(codec, expiry); - - when(expiry.getExpiryForCreation(anyLong(), anyString())).thenReturn(ExpiryPolicy.INFINITE); - - List> list = new ArrayList<>(); - list.add(new PutOperation<>(1L, "One", timeSource.getTimeMillis())); - list.add(new PutOperation<>(1L, "Second", timeSource.getTimeMillis())); - - Chain chain = getChainFromOperations(list); - - ResolvedChain resolvedChain = chainResolver.resolve(chain, 1L, timeSource.getTimeMillis()); - - verify(expiry, times(0)).getExpiryForAccess(anyLong(), any()); - verify(expiry, times(1)).getExpiryForCreation(anyLong(), anyString()); - verify(expiry, times(1)).getExpiryForUpdate(anyLong(), any(), anyString()); - - assertThat(resolvedChain.isCompacted(), is(true)); - } - - @Test - @SuppressWarnings("unchecked") - public void testGetExpiryForCreationIsInvokedOnlyOnce() { - ExpiryPolicy expiry = mock(ExpiryPolicy.class); - ExpiryChainResolver chainResolver = new ExpiryChainResolver<>(codec, expiry); - - when(expiry.getExpiryForCreation(anyLong(), anyString())).thenReturn(ExpiryPolicy.INFINITE); - - List> list = new ArrayList<>(); - list.add(new PutOperation<>(1L, "One", timeSource.getTimeMillis())); - list.add(new PutOperation<>(1L, "Second", timeSource.getTimeMillis())); - list.add(new PutOperation<>(1L, "Three", timeSource.getTimeMillis())); - list.add(new PutOperation<>(1L, "Four", timeSource.getTimeMillis())); - - Chain chain = getChainFromOperations(list); - - ResolvedChain resolvedChain = chainResolver.resolve(chain, 1L, timeSource.getTimeMillis()); - - InOrder inOrder = inOrder(expiry); - - inOrder.verify(expiry, times(1)).getExpiryForCreation(anyLong(), anyString()); - inOrder.verify(expiry, times(3)).getExpiryForUpdate(anyLong(), any(), anyString()); - - assertThat(resolvedChain.isCompacted(), is(true)); - } - - @Test - @SuppressWarnings("unchecked") - public void testGetExpiryForCreationIsNotInvokedForReplacedChains() { - ExpiryPolicy expiry = mock(ExpiryPolicy.class); - ExpiryChainResolver chainResolver = new ExpiryChainResolver<>(codec, expiry); - - when(expiry.getExpiryForCreation(anyLong(), anyString())).thenReturn(ExpiryPolicy.INFINITE); - - List> list = new ArrayList<>(); - list.add(new PutOperation<>(1L, "Replaced", -10L)); - list.add(new PutOperation<>(1L, "SecondAfterReplace", timeSource.getTimeMillis())); - list.add(new PutOperation<>(1L, "ThirdAfterReplace", timeSource.getTimeMillis())); - list.add(new PutOperation<>(1L, "FourthAfterReplace", timeSource.getTimeMillis())); - - Chain chain = getChainFromOperations(list); - - ResolvedChain resolvedChain = chainResolver.resolve(chain, 1L, timeSource.getTimeMillis()); - verify(expiry, times(0)).getExpiryForCreation(anyLong(), anyString()); - verify(expiry, times(3)).getExpiryForUpdate(anyLong(), any(), anyString()); - - assertThat(resolvedChain.isCompacted(), is(true)); - } - - @Test - @SuppressWarnings("unchecked") - public void testGetExpiryForCreationIsInvokedAfterRemoveOperations() { - - ExpiryPolicy expiry = mock(ExpiryPolicy.class); - ExpiryChainResolver chainResolver = new ExpiryChainResolver<>(codec, expiry); - - when(expiry.getExpiryForCreation(anyLong(), anyString())).thenReturn(ExpiryPolicy.INFINITE); - - List> list = new ArrayList<>(); - list.add(new PutOperation<>(1L, "Replaced", 10L)); - list.add(new PutOperation<>(1L, "SecondAfterReplace", 3L)); - list.add(new RemoveOperation<>(1L, 4L)); - list.add(new PutOperation<>(1L, "FourthAfterReplace", 5L)); - - Chain replacedChain = getChainFromOperations(list); - - ResolvedChain resolvedChain = chainResolver.resolve(replacedChain, 1L, timeSource.getTimeMillis()); - - InOrder inOrder = inOrder(expiry); - - verify(expiry, times(0)).getExpiryForAccess(anyLong(), any()); - inOrder.verify(expiry, times(1)).getExpiryForUpdate(anyLong(), any(), anyString()); - inOrder.verify(expiry, times(1)).getExpiryForCreation(anyLong(), anyString()); - - assertThat(resolvedChain.isCompacted(), is(true)); - - reset(expiry); - - when(expiry.getExpiryForCreation(anyLong(), anyString())).thenReturn(ExpiryPolicy.INFINITE); - - list = new ArrayList<>(); - list.add(new PutOperation<>(1L, "One", timeSource.getTimeMillis())); - list.add(new PutOperation<>(1L, "Second", timeSource.getTimeMillis())); - list.add(new RemoveOperation<>(1L, timeSource.getTimeMillis())); - list.add(new PutOperation<>(1L, "Four", timeSource.getTimeMillis())); - - Chain chain = getChainFromOperations(list); - - chainResolver.resolve(chain, 1L, timeSource.getTimeMillis()); - - inOrder = inOrder(expiry); - - verify(expiry, times(0)).getExpiryForAccess(anyLong(), any()); - inOrder.verify(expiry, times(1)).getExpiryForCreation(anyLong(), anyString()); - inOrder.verify(expiry, times(1)).getExpiryForUpdate(anyLong(), any(), anyString()); - inOrder.verify(expiry, times(1)).getExpiryForCreation(anyLong(), anyString()); - - assertThat(resolvedChain.isCompacted(), is(true)); - } - - @Test - @SuppressWarnings("unchecked") - public void testNullGetExpiryForCreation() { - ExpiryPolicy expiry = mock(ExpiryPolicy.class); - ExpiryChainResolver chainResolver = new ExpiryChainResolver<>(codec, expiry); - - when(expiry.getExpiryForCreation(anyLong(), anyString())).thenReturn(null); - - List> list = new ArrayList<>(); - list.add(new PutOperation<>(1L, "Replaced", 10L)); - - Chain chain = getChainFromOperations(list); - - ResolvedChain resolvedChain = chainResolver.resolve(chain, 1L, timeSource.getTimeMillis()); - - assertTrue(resolvedChain.getCompactedChain().isEmpty()); - assertThat(resolvedChain.isCompacted(), is(true)); - } - - @Test - @SuppressWarnings("unchecked") - public void testNullGetExpiryForUpdate() { - ExpiryPolicy expiry = mock(ExpiryPolicy.class); - ExpiryChainResolver chainResolver = new ExpiryChainResolver<>(codec, expiry); - - when(expiry.getExpiryForUpdate(anyLong(), any(), anyString())).thenReturn(null); - - List> list = new ArrayList<>(); - list.add(new PutOperation<>(1L, "Replaced", -10L)); - list.add(new PutOperation<>(1L, "New", timeSource.getTimeMillis())); - Chain chain = getChainFromOperations(list); - - ResolvedChain resolvedChain = chainResolver.resolve(chain, 1L, timeSource.getTimeMillis()); - - assertThat(resolvedChain.getResolvedResult(1L).getValue(), is("New")); - assertTrue(getOperationsListFromChain(resolvedChain.getCompactedChain()).get(0).isExpiryAvailable()); - assertThat(getOperationsListFromChain(resolvedChain.getCompactedChain()).get(0).expirationTime(), is(10L)); - assertThat(resolvedChain.isCompacted(), is(true)); - } - - @Test - @SuppressWarnings("unchecked") - public void testGetExpiryForUpdateUpdatesExpirationTimeStamp() { - ExpiryPolicy expiry = mock(ExpiryPolicy.class); - ExpiryChainResolver chainResolver = new ExpiryChainResolver<>(codec, expiry); - - when(expiry.getExpiryForUpdate(anyLong(), any(), anyString())).thenReturn(Duration.ofMillis(2L)); - - List> list = new ArrayList<>(); - list.add(new PutOperation<>(1L, "Replaced", -10L)); - list.add(new PutOperation<>(1L, "New", timeSource.getTimeMillis())); - Chain chain = getChainFromOperations(list); - - ResolvedChain resolvedChain = chainResolver.resolve(chain, 1L, timeSource.getTimeMillis()); - - assertThat(resolvedChain.getResolvedResult(1L).getValue(), is("New")); - assertTrue(getOperationsListFromChain(resolvedChain.getCompactedChain()).get(0).isExpiryAvailable()); - assertThat(getOperationsListFromChain(resolvedChain.getCompactedChain()).get(0).expirationTime(), is(2L)); - assertThat(resolvedChain.isCompacted(), is(true)); - } - - @Test - @SuppressWarnings("unchecked") - public void testExpiryThrowsException() { - ExpiryPolicy expiry = mock(ExpiryPolicy.class); - ExpiryChainResolver chainResolver = new ExpiryChainResolver<>(codec, expiry); - - when(expiry.getExpiryForUpdate(anyLong(), any(), anyString())).thenThrow(new RuntimeException("Test Update Expiry")); - when(expiry.getExpiryForCreation(anyLong(), anyString())).thenThrow(new RuntimeException("Test Create Expiry")); - - List> list = new ArrayList<>(); - list.add(new PutOperation<>(1L, "One", -10L)); - list.add(new PutOperation<>(1L, "Two", timeSource.getTimeMillis())); - Chain chain = getChainFromOperations(list); - - ResolvedChain resolvedChain = chainResolver.resolve(chain, 1L, timeSource.getTimeMillis()); - - assertThat(resolvedChain.getResolvedResult(1L), nullValue()); - - list = new ArrayList<>(); - list.add(new PutOperation<>(1L, "One", timeSource.getTimeMillis())); - list.add(new PutOperation<>(1L, "Two", timeSource.getTimeMillis())); - chain = getChainFromOperations(list); - - resolvedChain = chainResolver.resolve(chain, 1L, timeSource.getTimeMillis()); - - assertThat(resolvedChain.getResolvedResult(1L), nullValue()); - - assertThat(resolvedChain.isCompacted(), is(true)); - } - - private Chain getChainFromOperations(List> operations) { - ChainBuilder chainBuilder = new ChainBuilder(); - for(Operation operation: operations) { - chainBuilder = chainBuilder.add(codec.encode(operation)); - } - return chainBuilder.build(); - } - - private List> getOperationsListFromChain(Chain chain) { - List> list = new ArrayList<>(); - for (Element element : chain) { - Operation operation = codec.decode(element.getPayload()); - list.add(operation); - } - return list; - } - -} diff --git a/clustered/client/src/test/java/org/ehcache/clustered/common/internal/store/operations/ExpiryChainResolverTest.java b/clustered/client/src/test/java/org/ehcache/clustered/common/internal/store/operations/ExpiryChainResolverTest.java deleted file mode 100644 index 4bf85351b1..0000000000 --- a/clustered/client/src/test/java/org/ehcache/clustered/common/internal/store/operations/ExpiryChainResolverTest.java +++ /dev/null @@ -1,583 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ehcache.clustered.common.internal.store.operations; - -import org.ehcache.clustered.client.internal.store.ChainBuilder; -import org.ehcache.clustered.client.internal.store.ResolvedChain; -import org.ehcache.clustered.client.internal.store.operations.ExpiryChainResolver; -import org.ehcache.clustered.common.internal.store.operations.codecs.OperationsCodec; -import org.ehcache.clustered.common.internal.store.Chain; -import org.ehcache.clustered.common.internal.store.Element; -import org.ehcache.config.builders.ExpiryPolicyBuilder; -import org.ehcache.impl.serialization.LongSerializer; -import org.ehcache.impl.serialization.StringSerializer; -import org.hamcrest.Description; -import org.hamcrest.Matcher; -import org.hamcrest.TypeSafeMatcher; -import org.junit.Test; - -import java.nio.ByteBuffer; -import java.time.Duration; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.Matchers.emptyIterable; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder; -import static org.hamcrest.collection.IsIterableContainingInOrder.contains; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -public class ExpiryChainResolverTest { - - private static OperationsCodec codec = new OperationsCodec<>(new LongSerializer(), new StringSerializer()); - - @Test - @SuppressWarnings("unchecked") - public void testResolveMaintainsOtherKeysInOrder() throws Exception { - Operation expected = new PutOperation<>(1L, "Suresh", 0L); - Chain chain = getChainFromOperations( - new PutOperation<>(1L, "Albin", 0L), - new PutOperation<>(2L, "Albin", 0L), - expected, - new PutOperation<>(2L, "Suresh", 0L), - new PutOperation<>(2L, "Mathew", 0L)); - - ExpiryChainResolver resolver = new ExpiryChainResolver<>(codec, ExpiryPolicyBuilder.noExpiration()); - ResolvedChain resolvedChain = resolver.resolve(chain, 1L, 0L); - Result result = resolvedChain.getResolvedResult(1L); - assertEquals(expected, result); - assertThat(resolvedChain.isCompacted(), is(true)); - - Chain compactedChain = resolvedChain.getCompactedChain(); - assertThat(compactedChain, contains( //@SuppressWarnings("unchecked") - operation(new PutOperation<>(2L, "Albin", 0L)), - operation(new PutOperation<>(2L, "Suresh", 0L)), - operation(new PutOperation<>(2L, "Mathew", 0L)), - operation(new PutOperation<>(1L, "Suresh", 0L)))); - } - - @Test - public void testResolveEmptyChain() throws Exception { - Chain chain = getChainFromOperations(); - ExpiryChainResolver resolver = new ExpiryChainResolver<>(codec, ExpiryPolicyBuilder.noExpiration()); - ResolvedChain resolvedChain = resolver.resolve(chain, 1L, 0L); - Result result = resolvedChain.getResolvedResult(1L); - assertNull(result); - - assertThat(resolvedChain.isCompacted(), is(false)); - } - - @Test - public void testResolveChainWithNonExistentKey() throws Exception { - Chain chain = getChainFromOperations( - new PutOperation<>(1L, "Albin", 0L), - new PutOperation<>(2L, "Suresh", 0L), - new PutOperation<>(2L, "Mathew", 0L)); - - ExpiryChainResolver resolver = new ExpiryChainResolver<>(codec, ExpiryPolicyBuilder.noExpiration()); - ResolvedChain resolvedChain = resolver.resolve(chain, 3L, 0L); - Result result = resolvedChain.getResolvedResult(3L); - assertNull(result); - assertThat(resolvedChain.isCompacted(), is(false)); - } - - @Test - public void testResolveSinglePut() throws Exception { - Operation expected = new PutOperation<>(1L, "Albin", 0L); - Chain chain = getChainFromOperations(expected); - - ExpiryChainResolver resolver = new ExpiryChainResolver<>(codec, ExpiryPolicyBuilder.noExpiration()); - ResolvedChain resolvedChain = resolver.resolve(chain, 1L, 0L); - Result result = resolvedChain.getResolvedResult(1L); - assertEquals(expected, result); - assertThat(resolvedChain.isCompacted(), is(false)); - } - - @Test - public void testResolvePutsOnly() throws Exception { - Operation expected = new PutOperation<>(1L, "Mathew", 0L); - - Chain chain = getChainFromOperations( - new PutOperation<>(1L, "Albin", 0L), - new PutOperation<>(1L, "Suresh", 0L), - new PutOperation<>(1L, "Mathew", 0L)); - - ExpiryChainResolver resolver = new ExpiryChainResolver<>(codec, ExpiryPolicyBuilder.noExpiration()); - ResolvedChain resolvedChain = resolver.resolve(chain, 1L, 0L); - Result result = resolvedChain.getResolvedResult(1L); - assertEquals(expected, result); - assertThat(resolvedChain.isCompacted(), is(true)); - assertThat(resolvedChain.getCompactionCount(), is(2)); - } - - @Test - public void testResolveSingleRemove() throws Exception { - Chain chain = getChainFromOperations(new RemoveOperation<>(1L, 0L)); - - ExpiryChainResolver resolver = new ExpiryChainResolver<>(codec, ExpiryPolicyBuilder.noExpiration()); - ResolvedChain resolvedChain = resolver.resolve(chain, 1L, 0L); - Result result = resolvedChain.getResolvedResult(1L); - assertNull(result); - assertThat(resolvedChain.isCompacted(), is(true)); - assertThat(resolvedChain.getCompactionCount(), is(1)); - } - - @Test - public void testResolveRemovesOnly() throws Exception { - Chain chain = getChainFromOperations( - new RemoveOperation<>(1L, 0L), - new RemoveOperation<>(1L, 0L)); - - ExpiryChainResolver resolver = new ExpiryChainResolver<>(codec, ExpiryPolicyBuilder.noExpiration()); - ResolvedChain resolvedChain = resolver.resolve(chain, 1L, 0L); - Result result = resolvedChain.getResolvedResult(1L); - assertNull(result); - assertThat(resolvedChain.isCompacted(), is(true)); - assertThat(resolvedChain.getCompactionCount(), is(2)); - } - - @Test - public void testPutAndRemove() throws Exception { - Chain chain = getChainFromOperations( - new PutOperation<>(1L, "Albin", 0L), - new RemoveOperation<>(1L, 0L)); - - ExpiryChainResolver resolver = new ExpiryChainResolver<>(codec, ExpiryPolicyBuilder.noExpiration()); - ResolvedChain resolvedChain = resolver.resolve(chain, 1L, 0L); - Result result = resolvedChain.getResolvedResult(1L); - assertNull(result); - assertThat(resolvedChain.isCompacted(), is(true)); - } - - @Test - public void testResolvePutIfAbsentOnly() throws Exception { - Operation expected = new PutOperation<>(1L, "Mathew", 0L); - Chain chain = getChainFromOperations(new PutIfAbsentOperation<>(1L, "Mathew", 0L)); - - ExpiryChainResolver resolver = new ExpiryChainResolver<>(codec, ExpiryPolicyBuilder.noExpiration()); - ResolvedChain resolvedChain = resolver.resolve(chain, 1L, 0L); - Result result = resolvedChain.getResolvedResult(1L); - assertEquals(expected, result); - assertThat(resolvedChain.isCompacted(), is(false)); - } - - @Test - public void testResolvePutIfAbsentsOnly() throws Exception { - Operation expected = new PutOperation<>(1L, "Albin", 0L); - Chain chain = getChainFromOperations( - new PutIfAbsentOperation<>(1L, "Albin", 0L), - new PutIfAbsentOperation<>(1L, "Suresh", 0L), - new PutIfAbsentOperation<>(1L, "Mathew", 0L)); - - ExpiryChainResolver resolver = new ExpiryChainResolver<>(codec, ExpiryPolicyBuilder.noExpiration()); - ResolvedChain resolvedChain = resolver.resolve(chain, 1L, 0L); - Result result = resolvedChain.getResolvedResult(1L); - assertEquals(expected, result); - assertThat(resolvedChain.isCompacted(), is(true)); - } - - @Test - public void testResolvePutIfAbsentSucceeds() throws Exception { - Operation expected = new PutOperation<>(1L, "Mathew", 0L); - Chain chain = getChainFromOperations( - new PutOperation<>(1L, "Albin", 0L), - new RemoveOperation<>(1L, 0L), - new PutIfAbsentOperation<>(1L, "Mathew", 0L)); - - ExpiryChainResolver resolver = new ExpiryChainResolver<>(codec, ExpiryPolicyBuilder.noExpiration()); - ResolvedChain resolvedChain = resolver.resolve(chain, 1L, 0L); - Result result = resolvedChain.getResolvedResult(1L); - assertEquals(expected, result); - assertThat(resolvedChain.isCompacted(), is(true)); - } - - @Test - public void testResolveForSingleOperationDoesNotCompact() { - Chain chain = getChainFromOperations(new PutOperation<>(1L, "Albin", 0L)); - - ExpiryChainResolver resolver = new ExpiryChainResolver<>(codec, ExpiryPolicyBuilder.noExpiration()); - ResolvedChain resolvedChain = resolver.resolve(chain, 1L, 0L); - assertThat(resolvedChain.isCompacted(), is(false)); - assertThat(resolvedChain.getCompactionCount(), is(0)); - } - - @Test - public void testResolveForMultiplesOperationsAlwaysCompact() { - //create a random mix of operations - Chain chain = getChainFromOperations( - new PutIfAbsentOperation<>(1L, "Albin", 0L), - new PutOperation<>(1L, "Suresh", 0L), - new PutOperation<>(1L, "Mathew", 0L), - new PutOperation<>(2L, "Melbin", 0L), - new ReplaceOperation<>(1L, "Joseph", 0L), - new RemoveOperation<>(2L, 0L), - new ConditionalRemoveOperation<>(1L, "Albin", 0L), - new PutOperation<>(1L, "Gregory", 0L), - new ConditionalReplaceOperation<>(1L, "Albin", "Abraham", 0L), - new RemoveOperation<>(1L, 0L), - new PutIfAbsentOperation<>(2L, "Albin", 0L)); - - ExpiryChainResolver resolver = new ExpiryChainResolver<>(codec, ExpiryPolicyBuilder.noExpiration()); - ResolvedChain resolvedChain = resolver.resolve(chain, 1L, 0L); - assertThat(resolvedChain.isCompacted(), is(true)); - assertThat(resolvedChain.getCompactionCount(), is(8)); - } - - @Test - public void testResolveForMultipleOperationHasCorrectIsFirstAndTimeStamp() { - Chain chain = getChainFromOperations( - new PutOperation<>(1L, "Albin1", 0), - new PutOperation<>(1L, "Albin2", 1), - new RemoveOperation<>(1L, 2), - new PutOperation<>(1L, "AlbinAfterRemove", 3)); - - ExpiryChainResolver resolver = new ExpiryChainResolver<>(codec, ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofHours(1))); - ResolvedChain resolvedChain = resolver.resolve(chain, 1L, 3); - - Operation operation = codec.decode(resolvedChain.getCompactedChain().iterator().next().getPayload()); - - assertThat(operation.isExpiryAvailable(), is(true)); - assertThat(operation.expirationTime(), is(TimeUnit.HOURS.toMillis(1) + 3)); - try { - operation.timeStamp(); - fail(); - } catch (Exception ex) { - assertThat(ex.getMessage(), is("Timestamp not available")); - } - assertThat(resolvedChain.isCompacted(), is(true)); - } - - @Test - public void testResolveForMultipleOperationHasCorrectIsFirstAndTimeStampWithExpiry() { - Chain chain = getChainFromOperations( - new PutOperation<>(1L, "Albin1", 0L), - new PutOperation<>(1L, "Albin2", 1L), - new PutOperation<>(1L, "Albin3", 2L), - new PutOperation<>(1L, "Albin4", 3L) - ); - - ExpiryChainResolver resolver = new ExpiryChainResolver<>(codec, ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMillis(1L))); - ResolvedChain resolvedChain = resolver.resolve(chain, 1L, 3L); - - Operation operation = codec.decode(resolvedChain.getCompactedChain().iterator().next().getPayload()); - - assertThat(operation.isExpiryAvailable(), is(true)); - assertThat(operation.expirationTime(), is(4L)); - - try { - operation.timeStamp(); - fail(); - } catch (Exception ex) { - assertThat(ex.getMessage(), is("Timestamp not available")); - } - assertThat(resolvedChain.isCompacted(), is(true)); - } - - @Test - public void testResolveDoesNotDecodeOtherKeyOperationValues() throws Exception { - Chain chain = getChainFromOperations( - new PutOperation<>(2L, "Albin", 0L), - new PutOperation<>(2L, "Suresh", 0L), - new PutOperation<>(2L, "Mathew", 0L)); - - CountingLongSerializer keySerializer = new CountingLongSerializer(); - CountingStringSerializer valueSerializer = new CountingStringSerializer(); - OperationsCodec customCodec = new OperationsCodec<>(keySerializer, valueSerializer); - ExpiryChainResolver resolver = new ExpiryChainResolver<>(customCodec, ExpiryPolicyBuilder.timeToIdleExpiration(Duration.ofSeconds(5))); - resolver.resolve(chain, 1L, 0L); - - assertThat(keySerializer.decodeCount, is(3)); - assertThat(valueSerializer.decodeCount, is(0)); - assertThat(keySerializer.encodeCount, is(0)); - assertThat(valueSerializer.encodeCount, is(0)); - } - - @Test - public void testResolveDecodesOperationValueOnlyOnDemand() throws Exception { - Chain chain = getChainFromOperations( - new PutOperation<>(1L, "Albin", 1), - new PutOperation<>(1L, "Suresh", 2), - new PutOperation<>(1L, "Mathew", 3)); - - CountingLongSerializer keySerializer = new CountingLongSerializer(); - CountingStringSerializer valueSerializer = new CountingStringSerializer(); - OperationsCodec customCodec = new OperationsCodec<>(keySerializer, valueSerializer); - ExpiryChainResolver resolver = new ExpiryChainResolver<>(customCodec, ExpiryPolicyBuilder.timeToIdleExpiration(Duration.ofSeconds(5))); - resolver.resolve(chain, 1L, 0L); - - assertThat(keySerializer.decodeCount, is(3)); - assertThat(valueSerializer.decodeCount, is(3)); - assertThat(valueSerializer.encodeCount, is(0)); - assertThat(keySerializer.encodeCount, is(1)); - } - - @Test - @SuppressWarnings("unchecked") - public void testCompactingTwoKeys() throws Exception { - Chain chain = getChainFromOperations( - new PutOperation<>(1L, "Albin", 0L), - new PutOperation<>(2L, "Albin", 0L), - new PutOperation<>(1L, "Suresh", 0L), - new PutOperation<>(2L, "Suresh", 0L), - new PutOperation<>(2L, "Mathew", 0L)); - - ExpiryChainResolver resolver = new ExpiryChainResolver<>(codec, ExpiryPolicyBuilder.noExpiration()); - - Chain compactedChain = resolver.applyOperation(chain, 0L); - - assertThat(compactedChain, containsInAnyOrder( //@SuppressWarnings("unchecked") - operation(new PutOperation<>(2L, "Mathew", 0L)), - operation(new PutOperation<>(1L, "Suresh", 0L)) - )); - } - - @Test - public void testCompactEmptyChain() throws Exception { - Chain chain = (new ChainBuilder()).build(); - ExpiryChainResolver resolver = new ExpiryChainResolver<>(codec, ExpiryPolicyBuilder.noExpiration()); - Chain compacted = resolver.applyOperation(chain, 0L); - assertThat(compacted, emptyIterable()); - } - - @Test - public void testCompactSinglePut() throws Exception { - Chain chain = getChainFromOperations( - new PutOperation<>(1L, "Albin", 0L) - ); - - ExpiryChainResolver resolver = new ExpiryChainResolver<>(codec, ExpiryPolicyBuilder.noExpiration()); - Chain compacted = resolver.applyOperation(chain, 0L); - - assertThat(compacted, contains(operation(new PutOperation<>(1L, "Albin", 0L)))); - } - - @Test - public void testCompactMultiplePuts() throws Exception { - Chain chain = getChainFromOperations( - new PutOperation<>(1L, "Albin", 0L), - new PutOperation<>(1L, "Suresh", 0L), - new PutOperation<>(1L, "Mathew", 0L)); - - ExpiryChainResolver resolver = new ExpiryChainResolver<>(codec, ExpiryPolicyBuilder.noExpiration()); - Chain compactedChain = resolver.applyOperation(chain, 0L); - assertThat(compactedChain, contains(operation(new PutOperation<>(1L, "Mathew", 0L)))); - } - - @Test - public void testCompactSingleRemove() throws Exception { - Chain chain = getChainFromOperations(new RemoveOperation<>(1L, 0L)); - - ExpiryChainResolver resolver = new ExpiryChainResolver<>(codec, ExpiryPolicyBuilder.noExpiration()); - Chain compactedChain = resolver.applyOperation(chain, 0L); - assertThat(compactedChain, emptyIterable()); - } - - @Test - public void testCompactMultipleRemoves() throws Exception { - Chain chain = getChainFromOperations( - new RemoveOperation<>(1L, 0L), - new RemoveOperation<>(1L, 0L)); - - ExpiryChainResolver resolver = new ExpiryChainResolver<>(codec, ExpiryPolicyBuilder.noExpiration()); - Chain compactedChain = resolver.applyOperation(chain, 0L); - assertThat(compactedChain, emptyIterable()); - } - - @Test - public void testCompactPutAndRemove() throws Exception { - Chain chain = getChainFromOperations( - new PutOperation<>(1L, "Albin", 0L), - new RemoveOperation<>(1L, 0L)); - - ExpiryChainResolver resolver = new ExpiryChainResolver<>(codec, ExpiryPolicyBuilder.noExpiration()); - Chain compactedChain = resolver.applyOperation(chain, 0L); - assertThat(compactedChain, emptyIterable()); - } - - @Test - public void testCompactSinglePutIfAbsent() throws Exception { - Chain chain = getChainFromOperations(new PutIfAbsentOperation<>(1L, "Mathew", 0L)); - - ExpiryChainResolver resolver = new ExpiryChainResolver<>(codec, ExpiryPolicyBuilder.noExpiration()); - Chain compactedChain = resolver.applyOperation(chain, 0L); - assertThat(compactedChain, contains(operation(new PutOperation<>(1L, "Mathew", 0L)))); - } - - @Test - public void testCompactMultiplePutIfAbsents() throws Exception { - Chain chain = getChainFromOperations( - new PutIfAbsentOperation<>(1L, "Albin", 0L), - new PutIfAbsentOperation<>(1L, "Suresh", 0L), - new PutIfAbsentOperation<>(1L, "Mathew", 0L)); - - ExpiryChainResolver resolver = new ExpiryChainResolver<>(codec, ExpiryPolicyBuilder.noExpiration()); - Chain compactedChain = resolver.applyOperation(chain, 0L); - assertThat(compactedChain, contains(operation(new PutOperation<>(1L, "Albin", 0L)))); - } - - @Test - public void testCompactPutIfAbsentAfterRemove() throws Exception { - Chain chain = getChainFromOperations( - new PutOperation<>(1L, "Albin", 0L), - new RemoveOperation<>(1L, 0L), - new PutIfAbsentOperation<>(1L, "Mathew", 0L)); - - ExpiryChainResolver resolver = new ExpiryChainResolver<>(codec, ExpiryPolicyBuilder.noExpiration()); - Chain compactedChain = resolver.applyOperation(chain, 0L); - assertThat(compactedChain, contains(operation(new PutOperation<>(1L, "Mathew", 0L)))); - } - - @Test - public void testCompactForMultipleKeysAndOperations() { - //create a random mix of operations - Chain chain = getChainFromOperations( - new PutIfAbsentOperation<>(1L, "Albin", 0L), - new PutOperation<>(1L, "Suresh", 0L), - new PutOperation<>(1L, "Mathew", 0L), - new PutOperation<>(2L, "Melbin", 0L), - new ReplaceOperation<>(1L, "Joseph", 0L), - new RemoveOperation<>(2L, 0L), - new ConditionalRemoveOperation<>(1L, "Albin", 0L), - new PutOperation<>(1L, "Gregory", 0L), - new ConditionalReplaceOperation<>(1L, "Albin", "Abraham", 0L), - new RemoveOperation<>(1L, 0L), - new PutIfAbsentOperation<>(2L, "Albin", 0L)); - - ExpiryChainResolver resolver = new ExpiryChainResolver<>(codec, ExpiryPolicyBuilder.noExpiration()); - Chain compactedChain = resolver.applyOperation(chain, 0L); - assertThat(compactedChain, contains(operation(new PutOperation<>(2L, "Albin", 0L)))); - } - - @Test - public void testCompactHasCorrectTimeStamp() { - Chain chain = getChainFromOperations( - new PutOperation<>(1L, "Albin1", 0), - new PutOperation<>(1L, "Albin2", 1), - new RemoveOperation<>(1L, 2), - new PutOperation<>(1L, "Albin3", 3)); - - ExpiryChainResolver resolver = new ExpiryChainResolver<>(codec, ExpiryPolicyBuilder.noExpiration()); - Chain compactedChain = resolver.applyOperation(chain, 3); - - assertThat(compactedChain, contains(operation(new PutOperation<>(1L, "Albin3", 3)))); - } - - @Test - public void testCompactHasCorrectWithExpiry() { - Chain chain = getChainFromOperations( - new PutOperation<>(1L, "Albin1", 0L), - new PutOperation<>(1L, "Albin2", 1L), - new PutOperation<>(1L, "Albin3", 2L), - new PutOperation<>(1L, "Albin4", 3L) - ); - - ExpiryChainResolver resolver = new ExpiryChainResolver<>(codec, ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMillis(1L))); - Chain compactedChain = resolver.applyOperation(chain, 3L); - - assertThat(compactedChain, contains(operation(new PutOperation<>(1L, "Albin4", 3L)))); - } - - @Test - public void testCompactDecodesOperationValueOnlyOnDemand() throws Exception { - Chain chain = getChainFromOperations( - new PutOperation<>(1L, "Albin", 1), - new PutOperation<>(1L, "Suresh", 2), - new PutOperation<>(1L, "Mathew", 3)); - - CountingLongSerializer keySerializer = new CountingLongSerializer(); - CountingStringSerializer valueSerializer = new CountingStringSerializer(); - OperationsCodec customCodec = new OperationsCodec<>(keySerializer, valueSerializer); - ExpiryChainResolver resolver = new ExpiryChainResolver<>(customCodec, ExpiryPolicyBuilder.noExpiration()); - resolver.applyOperation(chain, 0L); - - assertThat(keySerializer.decodeCount, is(3)); - assertThat(valueSerializer.decodeCount, is(3)); - assertThat(valueSerializer.encodeCount, is(0)); - assertThat(keySerializer.encodeCount, is(1)); - } - - @SafeVarargs - private final Chain getChainFromOperations(Operation ... operations) { - ChainBuilder chainBuilder = new ChainBuilder(); - for(Operation operation: operations) { - chainBuilder = chainBuilder.add(codec.encode(operation)); - } - return chainBuilder.build(); - } - - private Matcher operation(Operation operation) { - return new TypeSafeMatcher() { - @Override - protected boolean matchesSafely(Element item) { - return operation.equals(codec.decode(item.getPayload())); - } - - @Override - public void describeTo(Description description) { - description.appendText("is ").appendValue(operation); - } - }; - } - - private static class CountingLongSerializer extends LongSerializer { - - private int encodeCount = 0; - private int decodeCount = 0; - - @Override - public ByteBuffer serialize(final Long object) { - encodeCount++; - return super.serialize(object); - } - - @Override - public Long read(final ByteBuffer binary) throws ClassNotFoundException { - decodeCount++; - return super.read(binary); - } - - @Override - public boolean equals(final Long object, final ByteBuffer binary) throws ClassNotFoundException { - return super.equals(object, binary); - } - } - - private static class CountingStringSerializer extends StringSerializer { - - private int encodeCount = 0; - private int decodeCount = 0; - - @Override - public ByteBuffer serialize(final String object) { - encodeCount++; - return super.serialize(object); - } - - @Override - public String read(final ByteBuffer binary) throws ClassNotFoundException { - decodeCount++; - return super.read(binary); - } - - @Override - public boolean equals(final String object, final ByteBuffer binary) throws ClassNotFoundException { - return super.equals(object, binary); - } - } -} diff --git a/clustered/client/src/test/java/org/ehcache/clustered/loaderWriter/BasicClusteredLoaderWriterTest.java b/clustered/client/src/test/java/org/ehcache/clustered/loaderWriter/BasicClusteredLoaderWriterTest.java deleted file mode 100644 index 1dd41248bf..0000000000 --- a/clustered/client/src/test/java/org/ehcache/clustered/loaderWriter/BasicClusteredLoaderWriterTest.java +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ehcache.clustered.loaderWriter; - -import org.ehcache.Cache; -import org.ehcache.CacheManager; -import org.ehcache.CachePersistenceException; -import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder; -import org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder; -import org.ehcache.clustered.client.internal.UnitTestConnectionService; -import org.ehcache.clustered.client.internal.service.ClusterTierValidationException; -import org.ehcache.clustered.util.ThrowingResilienceStrategy; -import org.ehcache.config.CacheConfiguration; -import org.ehcache.config.builders.CacheConfigurationBuilder; -import org.ehcache.config.builders.CacheManagerBuilder; -import org.ehcache.config.builders.ResourcePoolsBuilder; -import org.ehcache.config.units.EntryUnit; -import org.ehcache.config.units.MemoryUnit; -import org.ehcache.spi.loaderwriter.CacheLoaderWriter; -import org.hamcrest.Matchers; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import java.net.URI; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ThreadLocalRandom; - -import static org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder.cluster; -import static org.ehcache.config.builders.CacheConfigurationBuilder.*; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.assertThat; - -public class BasicClusteredLoaderWriterTest { - - private static final URI CLUSTER_URI = URI.create("terracotta://example.com:9540/clustered-loader-writer"); - - @Before - public void definePassthroughServer() throws Exception { - UnitTestConnectionService.add(CLUSTER_URI, - new UnitTestConnectionService.PassthroughServerBuilder() - .resource("primary-server-resource", 4, MemoryUnit.MB) - .build()); - } - - @After - public void removePassthroughServer() throws Exception { - UnitTestConnectionService.remove(CLUSTER_URI); - } - - @Test - public void testAllClientsNeedToHaveLoaderWriterConfigured() { - TestCacheLoaderWriter loaderWriter = new TestCacheLoaderWriter(); - CacheConfiguration cacheConfiguration = getCacheConfiguration(loaderWriter); - - CacheManager cacheManager = CacheManagerBuilder - .newCacheManagerBuilder() - .with(cluster(CLUSTER_URI).autoCreate()) - .withCache("cache-1", cacheConfiguration) - .build(true); - - CacheConfiguration withoutLoaderWriter = newCacheConfigurationBuilder(Long.class, String.class, - ResourcePoolsBuilder - .newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES).offheap(1, MemoryUnit.MB) - .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 2, MemoryUnit.MB))) - .withResilienceStrategy(new ThrowingResilienceStrategy()) - .build(); - - try { - CacheManager anotherManager = CacheManagerBuilder - .newCacheManagerBuilder() - .with(cluster(CLUSTER_URI).autoCreate()) - .withCache("cache-1", withoutLoaderWriter) - .build(true); - } catch (RuntimeException e) { - assertThat(e.getCause().getCause().getCause().getCause(), instanceOf(CachePersistenceException.class)); - assertThat(e.getCause().getCause().getCause().getCause().getCause(), instanceOf(ClusterTierValidationException.class)); - } - } - - @Test - public void testBasicClusteredCacheLoaderWriter() { - - TestCacheLoaderWriter loaderWriter = new TestCacheLoaderWriter(); - CacheConfiguration cacheConfiguration = getCacheConfiguration(loaderWriter); - - CacheManager cacheManager = CacheManagerBuilder - .newCacheManagerBuilder() - .with(cluster(CLUSTER_URI).autoCreate()) - .withCache("cache-1", cacheConfiguration) - .build(true); - - Cache cache = cacheManager.getCache("cache-1", Long.class, String.class); - - cache.put(1L, "1"); - - assertThat(cache.get(1L), is("1")); - - assertThat(loaderWriter.storeMap.get(1L), is("1")); - - } - - @Test - public void testLoaderWriterMultipleClients() { - - TestCacheLoaderWriter loaderWriter = new TestCacheLoaderWriter(); - - CacheConfiguration cacheConfiguration = getCacheConfiguration(loaderWriter); - - CacheManager cacheManager1 = CacheManagerBuilder - .newCacheManagerBuilder() - .with(cluster(CLUSTER_URI).autoCreate()) - .withCache("cache-1", cacheConfiguration) - .build(true); - - CacheManager cacheManager2 = CacheManagerBuilder - .newCacheManagerBuilder() - .with(cluster(CLUSTER_URI).autoCreate()) - .withCache("cache-1", cacheConfiguration) - .build(true); - - Cache client1 = cacheManager1.getCache("cache-1", Long.class, String.class); - Cache client2 = cacheManager2.getCache("cache-1", Long.class, String.class); - - client1.put(1L, "1"); - client2.put(1L, "2"); - - assertThat(client1.get(1L), is("2")); - assertThat(loaderWriter.storeMap.get(1L), is("2")); - - client1.remove(1L); - - assertThat(client2.get(1L), nullValue()); - assertThat(loaderWriter.storeMap.get(1L), nullValue()); - - } - - @Test - public void testCASOpsMultipleClients() { - TestCacheLoaderWriter loaderWriter = new TestCacheLoaderWriter(); - - CacheConfiguration cacheConfiguration = getCacheConfiguration(loaderWriter); - - CacheManager cacheManager1 = CacheManagerBuilder - .newCacheManagerBuilder() - .with(cluster(CLUSTER_URI).autoCreate()) - .withCache("cache-1", cacheConfiguration) - .build(true); - - CacheManager cacheManager2 = CacheManagerBuilder - .newCacheManagerBuilder() - .with(cluster(CLUSTER_URI).autoCreate()) - .withCache("cache-1", cacheConfiguration) - .build(true); - - Cache client1 = cacheManager1.getCache("cache-1", Long.class, String.class); - Cache client2 = cacheManager2.getCache("cache-1", Long.class, String.class); - - assertThat(client1.putIfAbsent(1L, "1"), nullValue()); - assertThat(client2.putIfAbsent(1L, "2"), is("1")); - - assertThat(client1.get(1L), is("1")); - assertThat(loaderWriter.storeMap.get(1L), is("1")); - - assertThat(client1.replace(1L, "2"), is("1")); - assertThat(client2.replace(1L, "3"), is("2")); - - assertThat(client1.get(1L), is("3")); - assertThat(loaderWriter.storeMap.get(1L), is("3")); - - assertThat(client1.replace(1L, "2", "4"), is(false)); - assertThat(client2.replace(1L, "3", "4"), is(true)); - - assertThat(client1.get(1L), is("4")); - assertThat(loaderWriter.storeMap.get(1L), is("4")); - - assertThat(client1.remove(1L, "5"), is(false)); - assertThat(client2.remove(1L, "4"), is(true)); - - } - - @Test - public void testBulkOps() { - TestCacheLoaderWriter loaderWriter = new TestCacheLoaderWriter(); - CacheConfiguration cacheConfiguration = getCacheConfiguration(loaderWriter); - - CacheManager cacheManager = CacheManagerBuilder - .newCacheManagerBuilder() - .with(cluster(CLUSTER_URI).autoCreate()) - .withCache("cache-1", cacheConfiguration) - .build(true); - - Cache cache = cacheManager.getCache("cache-1", Long.class, String.class); - - Map mappings = new HashMap<>(); - - for (int i = 1; i <= 5; i++) { - mappings.put((long) i, "" + i); - } - - cache.putAll(mappings); - - assertThat(loaderWriter.storeMap.keySet(), containsInAnyOrder(mappings.keySet().toArray())); - - cache.clear(); - - Map loadedData = cache.getAll(mappings.keySet()); - - assertThat(mappings.keySet(), containsInAnyOrder(loadedData.keySet().toArray())); - - cache.removeAll(mappings.keySet()); - - assertThat(loaderWriter.storeMap.isEmpty(), is(true)); - } - - @Test - public void testCASOps() { - TestCacheLoaderWriter loaderWriter = new TestCacheLoaderWriter(); - - CacheConfiguration cacheConfiguration = getCacheConfiguration(loaderWriter); - - CacheManager cacheManager1 = CacheManagerBuilder - .newCacheManagerBuilder() - .with(cluster(CLUSTER_URI).autoCreate()) - .withCache("cache-1", cacheConfiguration) - .build(true); - - CacheManager cacheManager2 = CacheManagerBuilder - .newCacheManagerBuilder() - .with(cluster(CLUSTER_URI).autoCreate()) - .withCache("cache-1", cacheConfiguration) - .build(true); - - Cache client1 = cacheManager1.getCache("cache-1", Long.class, String.class); - Cache client2 = cacheManager2.getCache("cache-1", Long.class, String.class); - - assertThat(loaderWriter.storeMap.isEmpty(), is(true)); - - Set keys = new HashSet<>(); - ThreadLocalRandom.current().longs(10).forEach(x -> { - keys.add(x); - client1.put(x, Long.toString(x)); - }); - assertThat(loaderWriter.storeMap.size(), is(10)); - - - keys.forEach(x -> assertThat(client2.putIfAbsent(x, "Again" + x), is(Long.toString(x)))); - - keys.stream().limit(5).forEach(x -> - assertThat(client2.replace(x , "Replaced" + x), is(Long.toString(x)))); - - keys.forEach(x -> client1.remove(x, Long.toString(x))); - - assertThat(loaderWriter.storeMap.size(), is(5)); - - } - - private CacheConfiguration getCacheConfiguration(TestCacheLoaderWriter loaderWriter) { - return newCacheConfigurationBuilder(Long.class, String.class, - ResourcePoolsBuilder - .newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES).offheap(1, MemoryUnit.MB) - .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 2, MemoryUnit.MB))) - .withLoaderWriter(loaderWriter) - .withResilienceStrategy(new ThrowingResilienceStrategy()) - .build(); - } - -} diff --git a/clustered/client/src/test/java/org/ehcache/clustered/util/ThrowingResilienceStrategy.java b/clustered/client/src/test/java/org/ehcache/clustered/util/ThrowingResilienceStrategy.java deleted file mode 100644 index 0708ff3934..0000000000 --- a/clustered/client/src/test/java/org/ehcache/clustered/util/ThrowingResilienceStrategy.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ehcache.clustered.util; - -import org.ehcache.Cache; -import org.ehcache.spi.resilience.ResilienceStrategy; -import org.ehcache.spi.resilience.StoreAccessException; - -import java.util.Map; - -public class ThrowingResilienceStrategy implements ResilienceStrategy { - @Override - public String getFailure(Long key, StoreAccessException e) { - throw new AssertionError("Cache op failed for key " + key, e); - } - - @Override - public boolean containsKeyFailure(Long key, StoreAccessException e) { - throw new AssertionError("Cache op failed for key " + key, e); - } - - @Override - public void putFailure(Long key, String value, StoreAccessException e) { - throw new AssertionError("Cache op failed for key " + key, e); - } - - @Override - public void removeFailure(Long key, StoreAccessException e) { - throw new AssertionError("Cache op failed for key " + key, e); - } - - @Override - public void clearFailure(StoreAccessException e) { - throw new AssertionError("Cache op failed", e); - } - - @Override - public Cache.Entry iteratorFailure(StoreAccessException e) { - throw new AssertionError("Cache op failed", e); - } - - @Override - public String putIfAbsentFailure(Long key, String value, StoreAccessException e) { - throw new AssertionError("Cache op failed for key " + key, e); - } - - @Override - public boolean removeFailure(Long key, String value, StoreAccessException e) { - throw new AssertionError("Cache op failed for key " + key, e); - } - - @Override - public String replaceFailure(Long key, String value, StoreAccessException e) { - throw new AssertionError("Cache op failed for key " + key, e); - } - - @Override - public boolean replaceFailure(Long key, String value, String newValue, StoreAccessException e) { - throw new AssertionError("Cache op failed for key " + key, e); - } - - @Override - public Map getAllFailure(Iterable keys, StoreAccessException e) { - throw new AssertionError("Cache op failed", e); - } - - @Override - public void putAllFailure(Map entries, StoreAccessException e) { - throw new AssertionError("Cache op failed", e); - } - - @Override - public void removeAllFailure(Iterable keys, StoreAccessException e) { - throw new AssertionError("Cache op failed", e); - } -} diff --git a/clustered/client/src/test/resources/configs/docs/tc-config.xml b/clustered/client/src/test/resources/configs/docs/tc-config.xml deleted file mode 100644 index 57d1553f1c..0000000000 --- a/clustered/client/src/test/resources/configs/docs/tc-config.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - 128 - 96 - - - - diff --git a/clustered/clustered-dist/build.gradle b/clustered/clustered-dist/build.gradle deleted file mode 100644 index f49a141127..0000000000 --- a/clustered/clustered-dist/build.gradle +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * NOTE: this directory had to be named clustered-dist instead of just dist - * because gradle creatively substitutes :dist for :clustered:dist or vice versa - * if groups are the same - * https://discuss.gradle.org/t/dependency-substitution-wrong-with-more-than-one-sub-project-with-same-name/7253/6 - */ - -group = 'org.ehcache' -archivesBaseName = 'ehcache-clustered' - -ext { - docsFolder = "$buildDir/docs/asciidoc" -} - -dependencies { - compileOnly project(':clustered:client') - compileOnly project(':clustered:common') - // Needed because declared as provided in the different projects - compileOnly "org.terracotta:runnel:$parent.terracottaPlatformVersion" -} - -apply plugin: 'distribution' -apply plugin: EhDistribute - -configurations { - kit - serverLibs -} - -dependencies { - compileOnly "org.terracotta.internal:client-runtime:$terracottaCoreVersion" - compileOnly "org.terracotta:lease-api:$terracottaPlatformVersion" - - serverLibs(project(':clustered:server')) { - exclude group: 'org.terracotta', module: 'entity-server-api' - exclude group: 'org.terracotta', module: 'entity-common-api' - exclude group: 'org.terracotta', module: 'packaging-support' - exclude group: 'org.terracotta.internal', module: 'tc-config-parser' - } - - // Needed because declared as provided in the different projects - serverLibs "org.terracotta:runnel:$parent.terracottaPlatformVersion" - - serverLibs "org.terracotta:lease-entity-server:$parent.terracottaPlatformVersion" - - kit "org.terracotta.internal:terracotta-kit:$terracottaCoreVersion@tar.gz" - - shadowCompile "org.slf4j:slf4j-api:$parent.slf4jVersion" - pomOnlyCompile "org.ehcache:ehcache:$parent.baseVersion" -} - -task copyDocs(type: Copy) { - dependsOn asciidocZip - from zipTree(asciidocZip.archivePath) - into docsFolder -} - -distributions { - main { - baseName = archivesBaseName - contents { - //tc kit - into ('') { - from configurations.kit.files.collect { tarTree(it) } - eachFile { f -> - // remove top level directory from the kit - f.path = f.path.replace("terracotta-$terracottaCoreVersion/", "") - } - exclude { f -> - // Exclude tc's client subdir and README.txt - Issue 1273 - f.path.contains('client/') || f.path.contains('README.txt') || f.path.contains('server/conf') - } - includeEmptyDirs = false - } - into ("server/plugins/lib") { - from configurations.serverLibs - } - into ('client/ehcache') { - from jar - from project(':dist').jar.archivePath.getPath() - exclude { f -> - !f.path.contains('ehcache') // do not add any transitives in this directory - } - } - into ('client/ehcache/documentation') { - from "$docsFolder/user" - } - into ('client/ehcache/javadoc') { - from javadocJar.archivePath.getPath() - from project(':dist').javadocJar.archivePath.getPath() - } - into ('client/lib') { - from configurations.shadowCompile - } - into ('') { - from 'src/assemble' - } - } - } -} - -distTar { - classifier = 'kit' - compression = Compression.GZIP -} - -distZip { - classifier = 'kit' -} - -[distTar, distZip, installDist]*.dependsOn copyDocs, javadocJar, project(':dist').jar, project(':dist').javadocJar diff --git a/clustered/clustered-dist/gradle.properties b/clustered/clustered-dist/gradle.properties deleted file mode 100644 index 048caca19b..0000000000 --- a/clustered/clustered-dist/gradle.properties +++ /dev/null @@ -1,25 +0,0 @@ -# -# Copyright Terracotta, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -subPomName = Ehcache 3 Clustered Module -subPomDesc = Ehcache 3 Clustered: Defines the client jar and the kit containing the Terracotta server -javadocExclude = **/core/**, **/impl/**, **/xml/**, **/jsr107/**, **/transactions/**, **/management/**, **/tck/** - -# Set to anything to disable SPI doc and jar generation -spiJavadocDisable = true - -osgi = {"Export-Package" : ["!com.tc*", "!com.terracotta*", "!org.terracotta*"],\ - "Import-Package" : ["!com.tc*", "!com.terracotta*", "!org.terracotta*"]} diff --git a/clustered/clustered-dist/src/assemble/server/conf/tc-config.xml b/clustered/clustered-dist/src/assemble/server/conf/tc-config.xml deleted file mode 100644 index 731ef5efcf..0000000000 --- a/clustered/clustered-dist/src/assemble/server/conf/tc-config.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - 512 - - - - - - - - %H/terracotta-logs - - - - - - - - - - - - - - - - 120 - - diff --git a/clustered/common/gradle.properties b/clustered/common/gradle.properties deleted file mode 100644 index e3f551d033..0000000000 --- a/clustered/common/gradle.properties +++ /dev/null @@ -1,21 +0,0 @@ -# -# Copyright Terracotta, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -subPomName = Ehcache 3 Common Clustering module -subPomDesc = The Common Clustering module of Ehcache 3 - -osgi = {"Export-Package" : ["!org.ehcache.clustered.common.internal.*"],\ - "Import-Package" : ["!org.ehcache.clustered.common.*"]} diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/EhcacheResponseType.java b/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/EhcacheResponseType.java deleted file mode 100644 index fe4804e3e8..0000000000 --- a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/EhcacheResponseType.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ehcache.clustered.common.internal.messages; - -import org.terracotta.runnel.EnumMapping; - -import static org.terracotta.runnel.EnumMappingBuilder.newEnumMappingBuilder; - -/** - * EhcacheResponseType - */ -public enum EhcacheResponseType { - SUCCESS, - FAILURE, - GET_RESPONSE, - HASH_INVALIDATION_DONE, - CLIENT_INVALIDATE_HASH, - CLIENT_INVALIDATE_ALL, - SERVER_INVALIDATE_HASH, - MAP_VALUE, - ALL_INVALIDATION_DONE, - PREPARE_FOR_DESTROY, - RESOLVE_REQUEST, - LOCK_SUCCESS, - LOCK_FAILURE; - - - public static final String RESPONSE_TYPE_FIELD_NAME = "opCode"; - public static final int RESPONSE_TYPE_FIELD_INDEX = 10; - public static final EnumMapping EHCACHE_RESPONSE_TYPES_ENUM_MAPPING = newEnumMappingBuilder(EhcacheResponseType.class) - .mapping(EhcacheResponseType.SUCCESS, 80) - .mapping(EhcacheResponseType.FAILURE, 81) - .mapping(EhcacheResponseType.GET_RESPONSE, 82) - .mapping(EhcacheResponseType.HASH_INVALIDATION_DONE, 83) - .mapping(EhcacheResponseType.ALL_INVALIDATION_DONE, 84) - .mapping(EhcacheResponseType.CLIENT_INVALIDATE_HASH, 85) - .mapping(EhcacheResponseType.CLIENT_INVALIDATE_ALL, 86) - .mapping(EhcacheResponseType.SERVER_INVALIDATE_HASH, 87) - .mapping(EhcacheResponseType.MAP_VALUE, 88) - .mapping(EhcacheResponseType.PREPARE_FOR_DESTROY, 89) - .mapping(EhcacheResponseType.RESOLVE_REQUEST, 90) - .mapping(EhcacheResponseType.LOCK_SUCCESS, 91) - .mapping(EhcacheResponseType.LOCK_FAILURE, 92) - .build(); -} diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/Util.java b/clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/Util.java deleted file mode 100644 index 42c14ba6df..0000000000 --- a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/Util.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ehcache.clustered.common.internal.store; - -import org.ehcache.clustered.common.internal.util.ByteBufferInputStream; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; -import java.util.function.Predicate; - -public class Util { - - public static final Iterator reverseIterator(List list) { - final ListIterator listIterator = list.listIterator(list.size()); - return new Iterator() { - @Override - public boolean hasNext() { - return listIterator.hasPrevious(); - } - - @Override - public T next() { - return listIterator.previous(); - } - - @Override - public void remove() { - listIterator.remove(); - } - }; - } - - public static long readPayLoad(ByteBuffer byteBuffer) { - return byteBuffer.getLong(); - } - - public static ByteBuffer createPayload(long key) { - ByteBuffer byteBuffer = ByteBuffer.allocate(8).putLong(key); - byteBuffer.flip(); - return byteBuffer.asReadOnlyBuffer(); - } - - public static ByteBuffer createPayload(long key, int payloadSize) { - if (payloadSize < 8) { - throw new IllegalArgumentException("payload must be at least 8 bytes long"); - } - ByteBuffer byteBuffer = ByteBuffer.allocate(payloadSize); - byteBuffer.putLong(key); - for (int i = 0; i < payloadSize - 8; i++) { - byteBuffer.put((byte) 0); - } - byteBuffer.flip(); - return byteBuffer.asReadOnlyBuffer(); - } - - public static boolean chainsEqual(Chain chain1, Chain chain2) { - Iterator it1 = chain1.iterator(); - Iterator it2 = chain2.iterator(); - - while (it1.hasNext() && it2.hasNext()) { - Element next1 = it1.next(); - Element next2 = it2.next(); - - if (!next1.getPayload().equals(next2.getPayload())) { - return false; - } - } - - return !it1.hasNext() && !it2.hasNext(); - } - - public static Element getElement(final ByteBuffer payload) { - return payload::duplicate; - } - - public static Chain getChain(boolean isSequenced, ByteBuffer... buffers) { - List elements = new ArrayList<>(); - long counter = 0; - for (final ByteBuffer buffer : buffers) { - if (isSequenced) { - elements.add(getElement(counter++, buffer)); - } else { - elements.add(getElement(buffer)); - } - - } - return getChain(elements); - } - - public static Chain getChain(final List elements) { - return new Chain() { - private final List list = Collections.unmodifiableList(elements); - @Override - public Iterator reverseIterator() { - return Util.reverseIterator(list); - } - - @Override - public boolean isEmpty() { - return list.isEmpty(); - } - - @Override - public int length() { - return list.size(); - } - - @Override - public Iterator iterator() { - return list.iterator(); - } - }; - } - - public static SequencedElement getElement(final long sequence, final ByteBuffer payload) { - return new SequencedElement() { - @Override - public long getSequenceNumber() { - return sequence; - } - - @Override - public ByteBuffer getPayload() { - return payload.duplicate(); - } - }; - } - - public static Object unmarshall(ByteBuffer payload, Predicate> isClassPermitted) { - try (ObjectInputStream objectInputStream = - new FilteredObjectInputStream(new ByteBufferInputStream(payload), isClassPermitted, null)) { - return objectInputStream.readObject(); - } catch (IOException | ClassNotFoundException ex) { - throw new IllegalArgumentException(ex); - } - } - - public static byte[] marshall(Object message) { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try(ObjectOutputStream oout = new ObjectOutputStream(out)) { - oout.writeObject(message); - } catch (IOException e) { - throw new IllegalArgumentException(e); - } - return out.toByteArray(); - } - - public static final Chain EMPTY_CHAIN = new Chain() { - @Override - public Iterator reverseIterator() { - return Collections.emptyList().iterator(); - } - - @Override - public boolean isEmpty() { - return true; - } - - @Override - public int length() { - return 0; - } - - @Override - public Iterator iterator() { - return Collections.emptyList().iterator(); - } - }; -} diff --git a/clustered/common/src/test/java/org/ehcache/clustered/common/internal/messages/ChainCodecTest.java b/clustered/common/src/test/java/org/ehcache/clustered/common/internal/messages/ChainCodecTest.java deleted file mode 100644 index 31786a9b69..0000000000 --- a/clustered/common/src/test/java/org/ehcache/clustered/common/internal/messages/ChainCodecTest.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ehcache.clustered.common.internal.messages; - -import org.ehcache.clustered.common.internal.store.Chain; -import org.ehcache.clustered.common.internal.store.Element; -import org.ehcache.clustered.common.internal.store.SequencedElement; -import org.junit.Test; - -import java.util.Iterator; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.ehcache.clustered.common.internal.store.Util.createPayload; -import static org.ehcache.clustered.common.internal.store.Util.readPayLoad; -import static org.ehcache.clustered.common.internal.store.Util.getChain; - -public class ChainCodecTest { - - @Test - public void testChainWithSingleElement() { - Chain chain = getChain(false, createPayload(1L)); - - assertThat(chain.isEmpty(), is(false)); - Iterator chainIterator = chain.iterator(); - assertThat(readPayLoad(chainIterator.next().getPayload()), is(1L)); - assertThat(chainIterator.hasNext(), is(false)); - - Chain decoded = ChainCodec.decode(ChainCodec.encode(chain)); - - assertThat(decoded.isEmpty(), is(false)); - chainIterator = decoded.iterator(); - assertThat(readPayLoad(chainIterator.next().getPayload()), is(1L)); - assertThat(chainIterator.hasNext(), is(false)); - } - - @Test - public void testChainWithSingleSequencedElement() { - Chain chain = getChain(true, createPayload(1L)); - - assertThat(chain.isEmpty(), is(false)); - Iterator chainIterator = chain.iterator(); - assertThat(readPayLoad(chainIterator.next().getPayload()), is(1L)); - assertThat(chainIterator.hasNext(), is(false)); - - Chain decoded = ChainCodec.decode(ChainCodec.encode(chain)); - - assertThat(decoded.isEmpty(), is(false)); - chainIterator = decoded.iterator(); - assertThat(readPayLoad(chainIterator.next().getPayload()), is(1L)); - assertThat(chainIterator.hasNext(), is(false)); - - assertSameSequenceChain(chain, decoded); - } - - @Test - public void testChainWithMultipleElements() { - Chain chain = getChain(false, createPayload(1L), createPayload(2L), createPayload(3L)); - - assertThat(chain.isEmpty(), is(false)); - Util.assertChainHas(chain, 1L, 2L, 3L); - - Chain decoded = ChainCodec.decode(ChainCodec.encode(chain)); - - assertThat(decoded.isEmpty(), is(false)); - Util.assertChainHas(decoded, 1L, 2L, 3L); - } - - @Test - public void testChainWithMultipleSequencedElements() { - Chain chain = getChain(true, createPayload(1L), createPayload(2L), createPayload(3L)); - - assertThat(chain.isEmpty(), is(false)); - Util.assertChainHas(chain, 1L, 2L, 3L); - - Chain decoded = ChainCodec.decode(ChainCodec.encode(chain)); - - assertThat(decoded.isEmpty(), is(false)); - Util.assertChainHas(decoded, 1L, 2L, 3L); - - assertSameSequenceChain(chain, decoded); - } - - @Test - public void testEmptyChain() { - Chain decoded = ChainCodec.decode(ChainCodec.encode(getChain(false))); - - assertThat(decoded.isEmpty(), is(true)); - } - - private static void assertSameSequenceChain(Chain original, Chain decoded) { - Iterator decodedIterator = decoded.iterator(); - for (Element element : original) { - assertEquals(((SequencedElement) element).getSequenceNumber(), - ((SequencedElement) decodedIterator.next()).getSequenceNumber()); - } - } -} diff --git a/clustered/ehcache-client/build.gradle b/clustered/ehcache-client/build.gradle new file mode 100644 index 0000000000..bff6116577 --- /dev/null +++ b/clustered/ehcache-client/build.gradle @@ -0,0 +1,61 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id 'org.ehcache.build.clustered-module' +} + +publishing.publications.withType(MavenPublication) { + pom { + name = 'Ehcache 3 Client Side Clustering module' + description = 'The Client Side Clustering module of Ehcache 3' + } +} + +dependencies { + api project(':ehcache-impl') + api project(':ehcache-xml') + + implementation project(':clustered:ehcache-common') + implementation "org.terracotta:entity-client-api:$terracottaApisVersion" + implementation "org.terracotta:runnel:$terracottaPlatformVersion" + implementation "org.terracotta:lease-api:$terracottaPlatformVersion" + implementation "org.terracotta.dynamic-config.entities:dynamic-config-topology-entity-client:$terracottaPlatformVersion" + implementation "org.terracotta:connection-api:$terracottaApisVersion" + + compileOnly 'org.osgi:org.osgi.service.component.annotations:1.3.0' + + testImplementation(project(':ehcache-transactions')) { + capabilities { + requireCapability("org.ehcache:ehcache-transactions-modules") + } + } + testImplementation(project(':clustered:server:ehcache-entity')) { + exclude group: 'org.terracotta.internal', module: 'tc-config-parser' + } + testImplementation project(':clustered:server:ehcache-service') + testImplementation "org.terracotta:client-message-tracker:$terracottaPlatformVersion" + testImplementation project(':clustered:test-utils') + testImplementation "org.terracotta:entity-test-lib:$terracottaPassthroughTestingVersion" + testImplementation "org.terracotta:passthrough-server:$terracottaPassthroughTestingVersion" + testImplementation "org.terracotta.internal:common:$terracottaCoreVersion" + testImplementation "org.terracotta:passthrough-leased-connection-api:$terracottaPlatformVersion" + testImplementation (group: 'org.codehaus.btm', name: 'btm', version: '2.1.4') { + exclude group:'org.slf4j', module:'slf4j-api' + } + testImplementation testFixtures(project(':ehcache-xml')) + testImplementation ("org.terracotta:statistics:$parent.statisticVersion") +} diff --git a/clustered/client/config/checkstyle-suppressions.xml b/clustered/ehcache-client/config/checkstyle-suppressions.xml similarity index 100% rename from clustered/client/config/checkstyle-suppressions.xml rename to clustered/ehcache-client/config/checkstyle-suppressions.xml diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/config/ClusteredResourcePool.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/config/ClusteredResourcePool.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/config/ClusteredResourcePool.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/config/ClusteredResourcePool.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/config/ClusteredResourceType.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/config/ClusteredResourceType.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/config/ClusteredResourceType.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/config/ClusteredResourceType.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/config/ClusteredStoreConfiguration.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/config/ClusteredStoreConfiguration.java similarity index 86% rename from clustered/client/src/main/java/org/ehcache/clustered/client/config/ClusteredStoreConfiguration.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/config/ClusteredStoreConfiguration.java index 7faa760d85..5c39d03ce3 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/config/ClusteredStoreConfiguration.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/config/ClusteredStoreConfiguration.java @@ -23,7 +23,7 @@ /** * {@link ServiceConfiguration} for the {@link ClusteredStore}. */ -public class ClusteredStoreConfiguration implements ServiceConfiguration { +public class ClusteredStoreConfiguration implements ServiceConfiguration { private final Consistency consistency; @@ -59,4 +59,14 @@ public Class getServiceType() { public Consistency getConsistency() { return consistency; } + + @Override + public Consistency derive() { + return getConsistency(); + } + + @Override + public ClusteredStoreConfiguration build(Consistency representation) { + return new ClusteredStoreConfiguration(representation); + } } diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/config/ClusteringServiceConfiguration.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/config/ClusteringServiceConfiguration.java similarity index 73% rename from clustered/client/src/main/java/org/ehcache/clustered/client/config/ClusteringServiceConfiguration.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/config/ClusteringServiceConfiguration.java index 7f07e1fffa..d8082c55b6 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/config/ClusteringServiceConfiguration.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/config/ClusteringServiceConfiguration.java @@ -18,6 +18,7 @@ import org.ehcache.CacheManager; import org.ehcache.PersistentCacheManager; +import org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder; import org.ehcache.clustered.client.internal.ConnectionSource; import org.ehcache.clustered.client.service.ClusteringService; import org.ehcache.config.builders.CacheManagerBuilder; @@ -33,17 +34,45 @@ import org.ehcache.clustered.common.ServerSideConfiguration; +import static java.util.Objects.requireNonNull; +import static org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder.seededFrom; + /** * Specifies the configuration for a {@link ClusteringService}. */ public class ClusteringServiceConfiguration - implements ServiceCreationConfiguration, + implements ServiceCreationConfiguration, CacheManagerConfiguration, HumanReadable { - public static final boolean DEFAULT_AUTOCREATE = false; + /** + * An enumeration of configurable client to server connection behaviors. + */ + public enum ClientMode { + /** + * Connect to the cluster with no expectations regarding the cluster state. + */ + CONNECT, + /** + * Connect to the cluster and validate the cluster state is compatible with {@link #getServerConfiguration()}. + */ + EXPECTING, + /** + * Connect to the cluster and create or validate the cluster state is compatible with {@link #getServerConfiguration()}. + */ + AUTO_CREATE, + /** + * Auto creates the necessary state on reconnecting to a cluster as well as on initial connection like {@link #AUTO_CREATE}. + */ + AUTO_CREATE_ON_RECONNECT + } + + public static final ClientMode DEFAULT_CLIENT_MODE = ClientMode.CONNECT; + @Deprecated + public static final boolean DEFAULT_AUTOCREATE = DEFAULT_CLIENT_MODE.equals(ClientMode.AUTO_CREATE); + private final ConnectionSource connectionSource; - private final boolean autoCreate; + private final ClientMode clientMode; private final ServerSideConfiguration serverConfiguration; private final Timeouts timeouts; private final Properties properties; @@ -55,7 +84,9 @@ public class ClusteringServiceConfiguration * * @throws NullPointerException if {@code clusterUri} is {@code null} * @throws IllegalArgumentException if {@code clusterUri} is not URI valid for cluster operations + * @deprecated In favor of {@link ClusteringServiceConfigurationBuilder#cluster(URI)} */ + @Deprecated public ClusteringServiceConfiguration(URI clusterUri) { this(clusterUri, Timeouts.DEFAULT); } @@ -67,7 +98,9 @@ public ClusteringServiceConfiguration(URI clusterUri) { * @param clusterTierManager the non-{@code null} cluster tier manager identifier * * @throws NullPointerException if {@code servers} is {@code null} + * @deprecated In favor of {@link ClusteringServiceConfigurationBuilder#cluster(Iterable, String)} */ + @Deprecated public ClusteringServiceConfiguration(Iterable servers, String clusterTierManager) { this(servers, clusterTierManager, Timeouts.DEFAULT); } @@ -80,7 +113,9 @@ public ClusteringServiceConfiguration(Iterable servers, Strin * * @throws NullPointerException if {@code clusterUri} is {@code null} * @throws IllegalArgumentException if {@code clusterUri} is not URI valid for cluster operations + * @deprecated In favor of {@link ClusteringServiceConfigurationBuilder#cluster(URI)} */ + @Deprecated public ClusteringServiceConfiguration(URI clusterUri, Timeouts timeouts) { this(clusterUri, timeouts, null); } @@ -93,7 +128,9 @@ public ClusteringServiceConfiguration(URI clusterUri, Timeouts timeouts) { * @param timeouts the {@link Timeouts} specifying the time limit for clustered cache operations * * @throws NullPointerException if {@code servers} is {@code null} + * @deprecated In favor of {@link ClusteringServiceConfigurationBuilder#cluster(Iterable, String)} */ + @Deprecated public ClusteringServiceConfiguration(Iterable servers, String clusterTierManager, Timeouts timeouts) { this(servers, clusterTierManager, timeouts, null); } @@ -106,7 +143,9 @@ public ClusteringServiceConfiguration(Iterable servers, Strin * * @throws NullPointerException if {@code clusterUri} is {@code null} * @throws IllegalArgumentException if {@code clusterUri} is not URI valid for cluster operations + * @deprecated In favor of {@link ClusteringServiceConfigurationBuilder#cluster(URI)} */ + @Deprecated public ClusteringServiceConfiguration(URI clusterUri, ServerSideConfiguration serverConfig) { this(clusterUri, Timeouts.DEFAULT, serverConfig); } @@ -120,7 +159,9 @@ public ClusteringServiceConfiguration(URI clusterUri, ServerSideConfiguration se * * @throws NullPointerException if {@code clusterUri} is {@code null} * @throws IllegalArgumentException if {@code clusterUri} is not URI valid for cluster operations + * @deprecated In favor of {@link ClusteringServiceConfigurationBuilder#cluster(URI)} */ + @Deprecated public ClusteringServiceConfiguration(URI clusterUri, Timeouts timeouts, ServerSideConfiguration serverConfig) { this(clusterUri, timeouts, DEFAULT_AUTOCREATE, serverConfig); } @@ -134,7 +175,9 @@ public ClusteringServiceConfiguration(URI clusterUri, Timeouts timeouts, ServerS * @param serverConfig the server side entity configuration required * * @throws NullPointerException if {@code servers} is {@code null} + * @deprecated In favor of {@link ClusteringServiceConfigurationBuilder#cluster(Iterable, String)} */ + @Deprecated public ClusteringServiceConfiguration(Iterable servers, String clusterTierManager, Timeouts timeouts, ServerSideConfiguration serverConfig) { this(servers, clusterTierManager, timeouts, DEFAULT_AUTOCREATE, serverConfig); @@ -149,7 +192,9 @@ public ClusteringServiceConfiguration(Iterable servers, Strin * * @throws NullPointerException if {@code clusterUri} is {@code null} * @throws IllegalArgumentException if {@code clusterUri} is not URI valid for cluster operations + * @deprecated In favor of {@link ClusteringServiceConfigurationBuilder#cluster(URI)} */ + @Deprecated public ClusteringServiceConfiguration(URI clusterUri, boolean autoCreate, ServerSideConfiguration serverConfig) { this(clusterUri, Timeouts.DEFAULT, autoCreate, serverConfig); } @@ -163,7 +208,9 @@ public ClusteringServiceConfiguration(URI clusterUri, boolean autoCreate, Server * @param serverConfig the server side entity configuration required * * @throws NullPointerException if {@code servers} is {@code null} + * @deprecated In favor of {@link ClusteringServiceConfigurationBuilder#cluster(Iterable, String)} */ + @Deprecated public ClusteringServiceConfiguration(Iterable servers, String clusterTierManager, boolean autoCreate, ServerSideConfiguration serverConfig) { this(servers, clusterTierManager, Timeouts.DEFAULT, autoCreate, serverConfig); @@ -179,7 +226,9 @@ public ClusteringServiceConfiguration(Iterable servers, Strin * * @throws NullPointerException if {@code clusterUri} is {@code null} * @throws IllegalArgumentException if {@code clusterUri} is not URI valid for cluster operations + * @deprecated In favor of {@link ClusteringServiceConfigurationBuilder#cluster(URI)} */ + @Deprecated public ClusteringServiceConfiguration(URI clusterUri, Timeouts timeouts, boolean autoCreate, ServerSideConfiguration serverConfig) { this(clusterUri, timeouts, autoCreate, serverConfig, new Properties()); } @@ -194,7 +243,9 @@ public ClusteringServiceConfiguration(URI clusterUri, Timeouts timeouts, boolean * @param serverConfig the server side entity configuration required * * @throws NullPointerException if {@code servers} is {@code null} + * @deprecated In favor of {@link ClusteringServiceConfigurationBuilder#cluster(Iterable, String)} */ + @Deprecated public ClusteringServiceConfiguration(Iterable servers, String clusterTierManager, Timeouts timeouts, boolean autoCreate, ServerSideConfiguration serverConfig) { this(servers, clusterTierManager, timeouts, autoCreate, serverConfig, new Properties()); @@ -211,7 +262,9 @@ public ClusteringServiceConfiguration(Iterable servers, Strin * * @throws NullPointerException if {@code clusterUri} is {@code null} * @throws IllegalArgumentException if {@code clusterUri} is not URI valid for cluster operations + * @deprecated In favor of {@link ClusteringServiceConfigurationBuilder#cluster(URI)} */ + @Deprecated public ClusteringServiceConfiguration(URI clusterUri, Timeouts timeouts, boolean autoCreate, ServerSideConfiguration serverConfig, Properties properties) { this(new ConnectionSource.ClusterUri(clusterUri), timeouts, autoCreate, serverConfig, properties); } @@ -227,7 +280,9 @@ public ClusteringServiceConfiguration(URI clusterUri, Timeouts timeouts, boolean * @param properties the non-{@code null} connection Properties * * @throws NullPointerException if {@code servers} is {@code null} + * @deprecated In favor of {@link ClusteringServiceConfigurationBuilder#cluster(Iterable, String)} */ + @Deprecated public ClusteringServiceConfiguration(Iterable servers, String clusterTierManager, Timeouts timeouts, boolean autoCreate, ServerSideConfiguration serverConfig, Properties properties) { this(new ConnectionSource.ServerList(servers, clusterTierManager), timeouts, autoCreate, serverConfig, properties); @@ -243,21 +298,39 @@ public ClusteringServiceConfiguration(Iterable servers, Strin * @param properties the non-{@code null} connection Properties * * @throws NullPointerException if {@code servers} is {@code null} + * @deprecated In favor of {@link #ClusteringServiceConfiguration(ConnectionSource, Timeouts, ClientMode, ServerSideConfiguration, Properties)} )} */ + @Deprecated public ClusteringServiceConfiguration(ConnectionSource connectionSource, Timeouts timeouts, boolean autoCreate, ServerSideConfiguration serverSideConfiguration, Properties properties) { - this.connectionSource = connectionSource; - this.autoCreate = autoCreate; + this(connectionSource, timeouts, + autoCreate ? ClientMode.AUTO_CREATE : (serverSideConfiguration == null ? ClientMode.CONNECT : ClientMode.EXPECTING), + serverSideConfiguration, properties); + } + + /** + * Creates a {@code ClusteringServiceConfiguration} from the properties provided. + * + * @param connectionSource the non-{@code null} {@code ConnectionSource} identifying the source of connection to servers in the cluster + * @param timeouts the {@link Timeouts} specifying the time limit for clustered cache operations + * @param clientMode behavioral mode when connecting to the cluster + * @param serverSideConfiguration the server side entity configuration required + * @param properties the non-{@code null} connection Properties + */ + public ClusteringServiceConfiguration(ConnectionSource connectionSource, Timeouts timeouts, ClientMode clientMode, + ServerSideConfiguration serverSideConfiguration, Properties properties) { + this.connectionSource = requireNonNull(connectionSource); + this.clientMode = requireNonNull(clientMode); this.serverConfiguration = serverSideConfiguration; - this.timeouts = Objects.requireNonNull(timeouts, "Operation timeouts cannot be null"); - this.properties = (Properties) Objects.requireNonNull(properties, "Properties cannot be null").clone(); + this.timeouts = requireNonNull(timeouts, "Operation timeouts cannot be null"); + this.properties = (Properties) requireNonNull(properties, "Properties cannot be null").clone(); } protected ClusteringServiceConfiguration(ClusteringServiceConfiguration baseConfig) { Objects.requireNonNull(baseConfig, "Base configuration cannot be null"); this.connectionSource = baseConfig.getConnectionSource(); this.timeouts = baseConfig.getTimeouts(); - this.autoCreate = baseConfig.isAutoCreate(); + this.clientMode = baseConfig.getClientMode(); this.serverConfiguration = baseConfig.getServerConfiguration(); this.properties = baseConfig.getProperties(); } @@ -285,9 +358,21 @@ public ConnectionSource getConnectionSource() { * Returns {@code true} is server side components should be automatically created. * * @return {@code true} is auto-create is enabled + * @deprecated Deprecated in favor of {@link #getClientMode()} */ + @Deprecated public boolean isAutoCreate() { - return autoCreate; + final ClientMode clientMode = getClientMode(); + return ClientMode.AUTO_CREATE.equals(clientMode) || ClientMode.AUTO_CREATE_ON_RECONNECT.equals(clientMode); + } + + /** + * Returns the client connection mode. + * + * @return the client mode + */ + public ClientMode getClientMode() { + return clientMode; } /** @@ -345,20 +430,34 @@ public String readableString() { return this.getClass().getName() + ":\n " + getConnectionSource() + "\n " + "timeouts: " + getTimeouts()+ "\n " + - "autoCreate: " + isAutoCreate() + "\n " + - "defaultServerResource: " + serverConfiguration.getDefaultServerResource() + "\n " + + "clientMode: " + getClientMode() + "\n " + + "defaultServerResource: " + (serverConfiguration == null ? null : serverConfiguration.getDefaultServerResource()) + "\n " + readablePoolsString(); } private String readablePoolsString() { StringBuilder pools = new StringBuilder("resourcePools:\n"); - serverConfiguration.getResourcePools().forEach((key, value) -> { - pools.append(" "); - pools.append(key); - pools.append(": "); - pools.append(value); - pools.append("\n"); - }); + if (serverConfiguration != null) { + serverConfiguration.getResourcePools().forEach((key, value) -> { + pools.append(" "); + pools.append(key); + pools.append(": "); + pools.append(value); + pools.append("\n"); + }); + } else { + pools.append(" None."); + } return pools.toString(); } + + @Override + public ClusteringServiceConfigurationBuilder derive() { + return seededFrom(this); + } + + @Override + public ClusteringServiceConfiguration build(ClusteringServiceConfigurationBuilder representation) { + return representation.build(); + } } diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/config/DedicatedClusteredResourcePool.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/config/DedicatedClusteredResourcePool.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/config/DedicatedClusteredResourcePool.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/config/DedicatedClusteredResourcePool.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/config/SharedClusteredResourcePool.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/config/SharedClusteredResourcePool.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/config/SharedClusteredResourcePool.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/config/SharedClusteredResourcePool.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/config/Timeouts.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/config/Timeouts.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/config/Timeouts.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/config/Timeouts.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/config/builders/ClusteredResourcePoolBuilder.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/config/builders/ClusteredResourcePoolBuilder.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/config/builders/ClusteredResourcePoolBuilder.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/config/builders/ClusteredResourcePoolBuilder.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/config/builders/ClusteredStoreConfigurationBuilder.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/config/builders/ClusteredStoreConfigurationBuilder.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/config/builders/ClusteredStoreConfigurationBuilder.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/config/builders/ClusteredStoreConfigurationBuilder.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/config/builders/ClusteringServiceConfigurationBuilder.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/config/builders/ClusteringServiceConfigurationBuilder.java similarity index 53% rename from clustered/client/src/main/java/org/ehcache/clustered/client/config/builders/ClusteringServiceConfigurationBuilder.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/config/builders/ClusteringServiceConfigurationBuilder.java index 86ab700b8f..3c0e3722d1 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/config/builders/ClusteringServiceConfigurationBuilder.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/config/builders/ClusteringServiceConfigurationBuilder.java @@ -25,12 +25,15 @@ import java.util.Objects; import java.util.Properties; import java.util.concurrent.TimeUnit; +import java.util.function.UnaryOperator; + +import org.ehcache.clustered.client.config.ClusteringServiceConfiguration.ClientMode; import org.ehcache.clustered.client.config.Timeouts; import org.ehcache.clustered.client.internal.ConnectionSource; import org.ehcache.clustered.common.ServerSideConfiguration; import org.ehcache.config.Builder; -import static org.ehcache.clustered.client.config.ClusteringServiceConfiguration.DEFAULT_AUTOCREATE; +import static org.ehcache.clustered.client.config.ClusteringServiceConfiguration.DEFAULT_CLIENT_MODE; /** * A builder of ClusteringService configurations. @@ -39,7 +42,9 @@ public final class ClusteringServiceConfigurationBuilder implements Builder servers, String clusterTierManager) { - return new ClusteringServiceConfigurationBuilder(new ConnectionSource.ServerList(servers, clusterTierManager), TimeoutsBuilder.timeouts().build(), DEFAULT_AUTOCREATE); + return new ClusteringServiceConfigurationBuilder(new ConnectionSource.ServerList(servers, clusterTierManager), TimeoutsBuilder.timeouts().build(), DEFAULT_CLIENT_MODE, null, new Properties()); } - private ClusteringServiceConfigurationBuilder(ConnectionSource connectionSource, Timeouts timeouts, boolean autoCreate) { + /** + * Creates a new builder seeded from an existing configuration. + * + * @param configuration existing clustering configuration + * @return a clustering service configuration builder + */ + public static ClusteringServiceConfigurationBuilder seededFrom(ClusteringServiceConfiguration configuration) { + + ServerSideConfiguration serverSideConfiguration = configuration.getServerConfiguration(); + if (serverSideConfiguration == null) { + return new ClusteringServiceConfigurationBuilder(configuration.getConnectionSource(), configuration.getTimeouts(), + configuration.getClientMode(), null, configuration.getProperties()); + } else { + return new ClusteringServiceConfigurationBuilder(configuration.getConnectionSource(), configuration.getTimeouts(), + configuration.getClientMode(), new ServerSideConfigurationBuilder(serverSideConfiguration), configuration.getProperties()); + } + } + + private ClusteringServiceConfigurationBuilder(ConnectionSource connectionSource, Timeouts timeouts, ClientMode clientMode, ServerSideConfigurationBuilder serverSideConfiguration, Properties properties) { this.connectionSource = connectionSource; this.timeouts = Objects.requireNonNull(timeouts, "Timeouts can't be null"); - this.autoCreate = autoCreate; + this.clientMode = clientMode; + this.serverSideConfiguration = serverSideConfiguration; + this.properties = properties; + } + + /** + * Reconfigure to connect to a different URI. + * + * @return a clustering service configuration builder + */ + public ClusteringServiceConfigurationBuilder usingUri(URI clusterUri) { + return new ClusteringServiceConfigurationBuilder(new ConnectionSource.ClusterUri(clusterUri), timeouts, clientMode, serverSideConfiguration, properties); + } + + /** + * Reconfigure to connect to a different cluster. + * + * @return a clustering service configuration builder + */ + public ClusteringServiceConfigurationBuilder usingServers(Iterable servers) { + return new ClusteringServiceConfigurationBuilder(new ConnectionSource.ServerList(servers, connectionSource.getClusterTierManager()), timeouts, clientMode, serverSideConfiguration, properties); + } + + /** + * Reconfigure to connect to a different cluster and manager name. + * + * @return a clustering service configuration builder + */ + public ClusteringServiceConfigurationBuilder usingServers(Iterable servers, String clusterTierManager) { + return new ClusteringServiceConfigurationBuilder(new ConnectionSource.ServerList(servers, clusterTierManager), timeouts, clientMode, serverSideConfiguration, properties); } /** * Support connection to an existing entity or create if the entity if absent. * * @return a clustering service configuration builder + * @deprecated in favor of {@link ClusteringServiceConfigurationBuilder#autoCreate(UnaryOperator)} */ + @Deprecated public ServerSideConfigurationBuilder autoCreate() { - return new ServerSideConfigurationBuilder(new ClusteringServiceConfigurationBuilder(this.connectionSource, this.timeouts, true)); + return new ServerSideConfigurationBuilder(new ClusteringServiceConfigurationBuilder(this.connectionSource, this.timeouts, ClientMode.AUTO_CREATE, serverSideConfiguration, properties)); } /** * Only support connection to an existing entity. * * @return a clustering service configuration builder + * @deprecated in favor of {@link ClusteringServiceConfigurationBuilder#expecting(UnaryOperator)} */ + @Deprecated public ServerSideConfigurationBuilder expecting() { - return new ServerSideConfigurationBuilder(new ClusteringServiceConfigurationBuilder(this.connectionSource, this.timeouts, false)); + return new ServerSideConfigurationBuilder(new ClusteringServiceConfigurationBuilder(this.connectionSource, this.timeouts, ClientMode.EXPECTING, serverSideConfiguration, properties)); + } + + /** + * Support connection to an existing entity or create if the entity if absent. + *

+ * An empty server-side configuration can be created by performing no operations on the supplied builder: + * {@code builder.autoCreate(b -> b)} + * + * @return a clustering service configuration builder + */ + public ClusteringServiceConfigurationBuilder autoCreate(UnaryOperator serverSideConfig) { + return new ClusteringServiceConfigurationBuilder(this.connectionSource, this.timeouts, ClientMode.AUTO_CREATE, + serverSideConfig.apply(new ServerSideConfigurationBuilder()), properties); + } + + /** + * Support connection to an existing entity or create if the entity if absent on initial connection or any subsequent reconnect attempt. + *

+ * An empty server-side configuration can be created by performing no operations on the supplied builder: + * {@code builder.autoCreateOnReconnect(b -> b)} + * + * @return a clustering service configuration builder + */ + public ClusteringServiceConfigurationBuilder autoCreateOnReconnect(UnaryOperator serverSideConfig) { + return new ClusteringServiceConfigurationBuilder(this.connectionSource, this.timeouts, ClientMode.AUTO_CREATE_ON_RECONNECT, + serverSideConfig.apply(new ServerSideConfigurationBuilder()), properties); + } + + /** + * Only support connection to an existing entity. + *

+ * An empty server-side configuration can be requested by performing no operations on the supplied builder: + * {@code builder.expecting(b -> b)} + * + * @return a clustering service configuration builder + */ + public ClusteringServiceConfigurationBuilder expecting(UnaryOperator serverSideConfig) { + return new ClusteringServiceConfigurationBuilder(this.connectionSource, this.timeouts, ClientMode.EXPECTING, + serverSideConfig.apply(new ServerSideConfigurationBuilder()), properties); } /** @@ -101,7 +196,7 @@ public ServerSideConfigurationBuilder expecting() { * @throws NullPointerException if {@code timeouts} is {@code null} */ public ClusteringServiceConfigurationBuilder timeouts(Timeouts timeouts) { - return new ClusteringServiceConfigurationBuilder(this.connectionSource, timeouts, this.autoCreate); + return new ClusteringServiceConfigurationBuilder(connectionSource, timeouts, clientMode, serverSideConfiguration, properties); } /** @@ -117,7 +212,7 @@ public ClusteringServiceConfigurationBuilder timeouts(Timeouts timeouts) { * @throws NullPointerException if {@code timeouts} is {@code null} */ public ClusteringServiceConfigurationBuilder timeouts(Builder timeoutsBuilder) { - return new ClusteringServiceConfigurationBuilder(this.connectionSource, timeoutsBuilder.build(), this.autoCreate); + return new ClusteringServiceConfigurationBuilder(connectionSource, timeoutsBuilder.build(), clientMode, serverSideConfiguration, properties); } /** @@ -143,7 +238,11 @@ public ClusteringServiceConfigurationBuilder readOperationTimeout(long duration, @Override public ClusteringServiceConfiguration build() { - return build(null); + if (serverSideConfiguration == null) { + return build(null); + } else { + return build(serverSideConfiguration.buildServerSideConfiguration()); + } } /** @@ -155,7 +254,7 @@ public ClusteringServiceConfiguration build() { * {@code ClusteringServiceConfigurationBuilder} and the {@code serverSideConfiguration} provided */ ClusteringServiceConfiguration build(ServerSideConfiguration serverSideConfiguration) { - return new ClusteringServiceConfiguration(connectionSource, timeouts, autoCreate, serverSideConfiguration, new Properties()); + return new ClusteringServiceConfiguration(connectionSource, timeouts, clientMode, serverSideConfiguration, properties); } private static ChronoUnit toChronoUnit(TimeUnit unit) { @@ -173,5 +272,4 @@ private static ChronoUnit toChronoUnit(TimeUnit unit) { default: throw new AssertionError("Unknown unit: " + unit); } } - } diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/config/builders/ServerSideConfigurationBuilder.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/config/builders/ServerSideConfigurationBuilder.java similarity index 90% rename from clustered/client/src/main/java/org/ehcache/clustered/client/config/builders/ServerSideConfigurationBuilder.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/config/builders/ServerSideConfigurationBuilder.java index e956aec4fb..5b54784588 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/config/builders/ServerSideConfigurationBuilder.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/config/builders/ServerSideConfigurationBuilder.java @@ -39,6 +39,12 @@ public class ServerSideConfigurationBuilder implements Builder pools; + ServerSideConfigurationBuilder() { + this.clientSideBuilder = null; + this.defaultServerResource = null; + this.pools = emptyMap(); + } + ServerSideConfigurationBuilder(ClusteringServiceConfigurationBuilder clientSideBuilder) { if (clientSideBuilder == null) { throw new NullPointerException("clientSideBuilder can not be null"); @@ -48,6 +54,12 @@ public class ServerSideConfigurationBuilder implements Builder maintenanceHolds = new ConcurrentHashMap<>(); private final Map fetchHolds = new ConcurrentHashMap<>(); + private final Executor asyncWorker; private final Timeouts entityTimeouts; - public ClusterTierManagerClientEntityFactory(Connection connection) { - this(connection, TimeoutsBuilder.timeouts().build()); + public ClusterTierManagerClientEntityFactory(Connection connection, Executor asyncWorker) { + this(connection, asyncWorker, TimeoutsBuilder.timeouts().build()); } - public ClusterTierManagerClientEntityFactory(Connection connection, Timeouts entityTimeouts) { + public ClusterTierManagerClientEntityFactory(Connection connection, Executor asyncWorker, Timeouts entityTimeouts) { this.connection = connection; + this.asyncWorker = requireNonNull(asyncWorker); this.entityTimeouts = entityTimeouts; } @@ -81,8 +86,8 @@ public boolean acquireLeadership(String entityIdentifier) { } } - public boolean abandonAllHolds(String entityIdentifier) { - return abandonLeadership(entityIdentifier) | abandonFetchHolds(entityIdentifier); + public boolean abandonAllHolds(String entityIdentifier, boolean healthyConnection) { + return abandonLeadership(entityIdentifier, healthyConnection) | abandonFetchHolds(entityIdentifier, healthyConnection); } /** @@ -91,9 +96,9 @@ public boolean abandonAllHolds(String entityIdentifier) { * @param entityIdentifier the master entity identifier * @return true of abandoned false otherwise */ - public boolean abandonLeadership(String entityIdentifier) { + public boolean abandonLeadership(String entityIdentifier, boolean healthyConnection) { Hold hold = maintenanceHolds.remove(entityIdentifier); - return (hold != null) && silentlyUnlock(hold, entityIdentifier); + return (hold != null) && healthyConnection && silentlyUnlock(hold, entityIdentifier); } /** @@ -102,9 +107,9 @@ public boolean abandonLeadership(String entityIdentifier) { * @param entityIdentifier the master entity identifier * @return true of abandoned false otherwise */ - private boolean abandonFetchHolds(String entityIdentifier) { + private boolean abandonFetchHolds(String entityIdentifier, boolean healthyConnection) { Hold hold = fetchHolds.remove(entityIdentifier); - return (hold != null) && silentlyUnlock(hold, entityIdentifier); + return (hold != null) && healthyConnection && silentlyUnlock(hold, entityIdentifier); } /** @@ -272,7 +277,7 @@ private EntityRef entityRef; try { entityRef = connection.getEntityRef(InternalClusterTierClientEntity.class, ENTITY_VERSION, entityName(clusterTierManagerIdentifier, storeIdentifier)); @@ -280,19 +285,19 @@ public ClusterTierClientEntity fetchOrCreateClusteredStoreEntity(String clusterT throw new AssertionError(e); } - if (autoCreate) { + if ((ClientMode.AUTO_CREATE.equals(clientMode) && !isReconnect) || ClientMode.AUTO_CREATE_ON_RECONNECT.equals(clientMode)) { while (true) { try { entityRef.create(new ClusterTierEntityConfiguration(clusterTierManagerIdentifier, storeIdentifier, clientStoreConfiguration)); } catch (EntityAlreadyExistsException e) { // Ignore - entity exists } catch (EntityConfigurationException e) { - throw new CachePersistenceException("Unable to create cluster tier", e); + throw new PerpetualCachePersistenceException("Unable to create cluster tier", e); } catch (EntityException e) { throw new AssertionError(e); } try { - return entityRef.fetchEntity(new ClusterTierUserData(entityTimeouts, storeIdentifier)); + return entityRef.fetchEntity(new ClusterTierUserData(entityTimeouts, storeIdentifier, asyncWorker)); } catch (EntityNotFoundException e) { // Ignore - will try to create again } catch (EntityException e) { @@ -304,22 +309,11 @@ public ClusterTierClientEntity fetchOrCreateClusteredStoreEntity(String clusterT } } - public ClusterTierClientEntity getClusterTierClientEntity(String clusterTierManagerIdentifier, String storeIdentifier) throws EntityNotFoundException { - EntityRef entityRef; - try { - entityRef = connection.getEntityRef(InternalClusterTierClientEntity.class, ENTITY_VERSION, entityName(clusterTierManagerIdentifier, storeIdentifier)); - } catch (EntityNotProvidedException e) { - throw new AssertionError(e); - } - - return fetchClusterTierClientEntity(storeIdentifier, entityRef); - } - private ClusterTierClientEntity fetchClusterTierClientEntity(String storeIdentifier, EntityRef entityRef) throws EntityNotFoundException { try { - return entityRef.fetchEntity(new ClusterTierUserData(entityTimeouts, storeIdentifier)); + return entityRef.fetchEntity(new ClusterTierUserData(entityTimeouts, storeIdentifier, asyncWorker)); } catch (EntityNotFoundException e) { throw e; } catch (EntityException e) { @@ -342,4 +336,9 @@ public void destroyClusteredStoreEntity(String clusterTierManagerIdentifier, Str private static String entityName(String clusterTierManagerIdentifier, String storeIdentifier) { return clusterTierManagerIdentifier + "$" + storeIdentifier; } + + // For test purposes + public Map getMaintenanceHolds() { + return maintenanceHolds; + } } diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/ClusterTierManagerClientEntityService.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/ClusterTierManagerClientEntityService.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/ClusterTierManagerClientEntityService.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/ClusterTierManagerClientEntityService.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/ClusterTierManagerCreationException.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/ClusterTierManagerCreationException.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/ClusterTierManagerCreationException.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/ClusterTierManagerCreationException.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/ClusterTierManagerNotFoundException.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/ClusterTierManagerNotFoundException.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/ClusterTierManagerNotFoundException.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/ClusterTierManagerNotFoundException.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/ClusterTierManagerValidationException.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/ClusterTierManagerValidationException.java similarity index 85% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/ClusterTierManagerValidationException.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/ClusterTierManagerValidationException.java index 27c793b278..e0aac0f71d 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/ClusterTierManagerValidationException.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/ClusterTierManagerValidationException.java @@ -19,7 +19,7 @@ /** * Thrown to indicate a failure in validating an {@code Entity} supporting clustered operations. */ -public class ClusterTierManagerValidationException extends RuntimeException { +public class ClusterTierManagerValidationException extends PerpetualCachePersistenceException { private static final long serialVersionUID = -428725072152588216L; @@ -30,8 +30,4 @@ public ClusterTierManagerValidationException(String message) { public ClusterTierManagerValidationException(String message, Throwable cause) { super(message, cause); } - - public ClusterTierManagerValidationException(Throwable cause) { - super(cause); - } } diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/ConnectionSource.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/ConnectionSource.java similarity index 52% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/ConnectionSource.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/ConnectionSource.java index ac9b882a96..1b5bafe547 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/ConnectionSource.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/ConnectionSource.java @@ -16,9 +16,20 @@ package org.ehcache.clustered.client.internal; import org.terracotta.connection.ConnectionException; +import org.terracotta.connection.entity.Entity; +import org.terracotta.connection.entity.EntityRef; +import org.terracotta.dynamic_config.api.model.Cluster; +import org.terracotta.dynamic_config.api.model.Node; +import org.terracotta.dynamic_config.api.model.UID; +import org.terracotta.dynamic_config.entity.topology.client.DynamicTopologyEntity; +import org.terracotta.dynamic_config.entity.topology.common.DynamicTopologyEntityConstants; +import org.terracotta.exception.EntityNotFoundException; +import org.terracotta.exception.EntityNotProvidedException; +import org.terracotta.exception.EntityVersionMismatchException; import org.terracotta.lease.connection.LeasedConnection; import org.terracotta.lease.connection.LeasedConnectionFactory; +import java.io.IOException; import java.net.InetSocketAddress; import java.net.URI; import java.net.URISyntaxException; @@ -26,6 +37,11 @@ import java.util.List; import java.util.Objects; import java.util.Properties; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public abstract class ConnectionSource { @@ -81,14 +97,21 @@ private static URI extractClusterUri(URI uri) { public static class ServerList extends ConnectionSource { - private final Iterable servers; + private final CopyOnWriteArraySet servers; private final String clusterTierManager; public ServerList(Iterable servers, String clusterTierManager) { - this.servers = cloneServers(Objects.requireNonNull(servers, "Servers cannot be null")); + this.servers = createServerList(servers); this.clusterTierManager = Objects.requireNonNull(clusterTierManager, "Cluster tier manager identifier cannot be null"); } + private CopyOnWriteArraySet createServerList(Iterable servers) { + Objects.requireNonNull(servers, "Servers cannot be null"); + CopyOnWriteArraySet serverList = new CopyOnWriteArraySet<>(); + servers.forEach(server -> serverList.add(server)); + return serverList; + } + @Override public String getClusterTierManager() { return clusterTierManager; @@ -96,7 +119,52 @@ public String getClusterTierManager() { @Override public LeasedConnection connect(Properties connectionProperties) throws ConnectionException { - return LeasedConnectionFactory.connect(servers, connectionProperties); + LeasedConnection connection = LeasedConnectionFactory.connect(servers, connectionProperties); + try { + EntityRef ref = connection.getEntityRef(DynamicTopologyEntity.class, 1, DynamicTopologyEntityConstants.ENTITY_NAME); + DynamicTopologyEntity dynamicTopologyEntity = ref.fetchEntity(null); + dynamicTopologyEntity.setListener(new DynamicTopologyEntity.Listener() { + @Override + public void onNodeRemoval(Cluster cluster, UID stripeUID, Node removedNode) { + removedNode.getEndpoints().forEach(e -> servers.remove(e.getAddress())); + } + + @Override + public void onNodeAddition(Cluster cluster, UID addedNodeUID) { + servers.add(cluster.determineEndpoint(addedNodeUID, servers).get().getAddress()); + } + }); + return new LeasedConnection() { + @Override + public EntityRef getEntityRef(Class cls, long version, String name) throws EntityNotProvidedException { + return connection.getEntityRef(cls, version, name); + } + + @Override + public void close() throws IOException { + Future close = dynamicTopologyEntity.releaseEntity(); + try { + close.get(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } catch (ExecutionException e) { + throw new IOException(e.getCause()); + } catch (TimeoutException e) { + } finally { + connection.close(); + } + } + + @Override + public boolean isValid() { + return connection.isValid(); + } + + + }; + } catch (EntityNotProvidedException | EntityVersionMismatchException | EntityNotFoundException e) { + throw new AssertionError(e); + } } @Override diff --git a/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/PerpetualCachePersistenceException.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/PerpetualCachePersistenceException.java new file mode 100644 index 0000000000..fbf83a626c --- /dev/null +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/PerpetualCachePersistenceException.java @@ -0,0 +1,51 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.clustered.client.internal; + +import org.ehcache.CachePersistenceException; + +/** + * Thrown to indicate a perpetual (non-transient) failure in a persistent cache manager. + *

+ * Receiving this exception indicates that future interactions with the throwing entity will continue to fail without + * corrective action. + * + * @see CachePersistenceException + */ +public class PerpetualCachePersistenceException extends CachePersistenceException { + + private static final long serialVersionUID = -5858875151420107041L; + + /** + * Creates a {@code PerpetualCachePersistenceException} with the provided message. + * + * @param message information about the exception + */ + public PerpetualCachePersistenceException(String message) { + super(message); + } + + /** + * Creates a {@code PerpetualCachePersistenceException} with the provided message and cause. + * + * @param message information about the exception + * @param cause the cause of this exception + */ + public PerpetualCachePersistenceException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/SimpleClusterTierManagerClientEntity.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/SimpleClusterTierManagerClientEntity.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/SimpleClusterTierManagerClientEntity.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/SimpleClusterTierManagerClientEntity.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/config/ClusteredResourcePoolImpl.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/config/ClusteredResourcePoolImpl.java similarity index 96% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/config/ClusteredResourcePoolImpl.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/config/ClusteredResourcePoolImpl.java index 5e91d126a9..35bab3ea89 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/config/ClusteredResourcePoolImpl.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/config/ClusteredResourcePoolImpl.java @@ -20,7 +20,7 @@ import org.ehcache.clustered.client.config.ClusteredResourcePool; import org.ehcache.clustered.common.PoolAllocation; import org.ehcache.config.ResourcePool; -import org.ehcache.core.config.AbstractResourcePool; +import org.ehcache.impl.config.AbstractResourcePool; /** * Implementation for {@link ClusteredResourcePool}. diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/config/DedicatedClusteredResourcePoolImpl.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/config/DedicatedClusteredResourcePoolImpl.java similarity index 98% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/config/DedicatedClusteredResourcePoolImpl.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/config/DedicatedClusteredResourcePoolImpl.java index dd5eed3d30..b45f234bc6 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/config/DedicatedClusteredResourcePoolImpl.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/config/DedicatedClusteredResourcePoolImpl.java @@ -21,7 +21,7 @@ import org.ehcache.config.ResourcePool; import org.ehcache.config.units.MemoryUnit; import org.ehcache.core.HumanReadable; -import org.ehcache.core.config.SizedResourcePoolImpl; +import org.ehcache.impl.config.SizedResourcePoolImpl; import org.ehcache.clustered.client.config.DedicatedClusteredResourcePool; diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/config/SharedClusteredResourcePoolImpl.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/config/SharedClusteredResourcePoolImpl.java similarity index 97% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/config/SharedClusteredResourcePoolImpl.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/config/SharedClusteredResourcePoolImpl.java index 5f74e57cce..338bd2046a 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/config/SharedClusteredResourcePoolImpl.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/config/SharedClusteredResourcePoolImpl.java @@ -20,7 +20,7 @@ import org.ehcache.clustered.client.config.SharedClusteredResourcePool; import org.ehcache.clustered.common.PoolAllocation; import org.ehcache.config.ResourcePool; -import org.ehcache.core.config.AbstractResourcePool; +import org.ehcache.impl.config.AbstractResourcePool; /** * Implementation for {@link SharedClusteredResourcePool}. diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/config/xml/ClusteredCacheConstants.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/config/xml/ClusteredCacheConstants.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/config/xml/ClusteredCacheConstants.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/config/xml/ClusteredCacheConstants.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/config/xml/ClusteredResourceConfigurationParser.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/config/xml/ClusteredResourceConfigurationParser.java similarity index 89% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/config/xml/ClusteredResourceConfigurationParser.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/config/xml/ClusteredResourceConfigurationParser.java index f2858511f3..639b05ef58 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/config/xml/ClusteredResourceConfigurationParser.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/config/xml/ClusteredResourceConfigurationParser.java @@ -23,7 +23,9 @@ import org.ehcache.config.units.MemoryUnit; import org.ehcache.xml.BaseConfigParser; import org.ehcache.xml.CacheResourceConfigurationParser; +import org.ehcache.xml.JaxbParsers; import org.ehcache.xml.exceptions.XmlConfigurationException; +import org.osgi.service.component.annotations.Component; import org.w3c.dom.Attr; import org.w3c.dom.DOMException; import org.w3c.dom.Document; @@ -46,6 +48,7 @@ /** * Provides a parser for the {@code /config/cache/resources} extension elements. */ +@Component public class ClusteredResourceConfigurationParser extends BaseConfigParser implements CacheResourceConfigurationParser { private static final String CLUSTERED_ELEMENT_NAME = "clustered"; @@ -72,16 +75,16 @@ public URI getNamespace() { protected ResourcePool parseResourceConfig(final Element fragment) { final String elementName = fragment.getLocalName(); switch (elementName) { - case "clustered-shared": - final String sharing = fragment.getAttribute("sharing"); + case SHARED_ELEMENT_NAME: + final String sharing = JaxbParsers.parsePropertyOrString(fragment.getAttribute(SHARING_ELEMENT_NAME)); return new SharedClusteredResourcePoolImpl(sharing); - case "clustered-dedicated": + case DEDICATED_ELEMENT_NAME: // 'from' attribute is optional on 'clustered-dedicated' element - final Attr fromAttr = fragment.getAttributeNode("from"); - final String from = (fromAttr == null ? null : fromAttr.getValue()); + final Attr fromAttr = fragment.getAttributeNode(FROM_ELEMENT_NAME); + final String from = (fromAttr == null ? null : JaxbParsers.parsePropertyOrString(fromAttr.getValue())); - final String unitValue = fragment.getAttribute("unit").toUpperCase(); + final String unitValue = fragment.getAttribute(UNIT_ELEMENT_NAME).toUpperCase(); final MemoryUnit sizeUnits; try { sizeUnits = MemoryUnit.valueOf(unitValue); @@ -91,19 +94,19 @@ protected ResourcePool parseResourceConfig(final Element fragment) { final String sizeValue; try { - sizeValue = fragment.getFirstChild().getNodeValue(); + sizeValue = fragment.getFirstChild().getNodeValue().trim(); } catch (DOMException e) { throw new XmlConfigurationException(String.format("XML configuration element <%s> value is not valid", elementName), e); } final long size; try { - size = Long.parseLong(sizeValue); + size = JaxbParsers.parsePropertyOrPositiveInteger(sizeValue).longValueExact(); } catch (NumberFormatException e) { throw new XmlConfigurationException(String.format("XML configuration element <%s> value '%s' is not valid", elementName, sizeValue), e); } return new DedicatedClusteredResourcePoolImpl(from, size, sizeUnits); - case "clustered": + case CLUSTERED_ELEMENT_NAME: return new ClusteredResourcePoolImpl(); } return null; diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/config/xml/ClusteringCacheManagerServiceConfigurationParser.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/config/xml/ClusteringCacheManagerServiceConfigurationParser.java similarity index 75% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/config/xml/ClusteringCacheManagerServiceConfigurationParser.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/config/xml/ClusteringCacheManagerServiceConfigurationParser.java index 975989101e..6eb41f4a32 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/config/xml/ClusteringCacheManagerServiceConfigurationParser.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/config/xml/ClusteringCacheManagerServiceConfigurationParser.java @@ -16,6 +16,7 @@ package org.ehcache.clustered.client.internal.config.xml; import org.ehcache.clustered.client.config.ClusteringServiceConfiguration; +import org.ehcache.clustered.client.config.ClusteringServiceConfiguration.ClientMode; import org.ehcache.clustered.client.config.Timeouts; import org.ehcache.clustered.client.config.builders.TimeoutsBuilder; import org.ehcache.clustered.client.internal.ConnectionSource; @@ -25,8 +26,10 @@ import org.ehcache.spi.service.ServiceCreationConfiguration; import org.ehcache.xml.BaseConfigParser; import org.ehcache.xml.CacheManagerServiceConfigurationParser; +import org.ehcache.xml.JaxbParsers; import org.ehcache.xml.exceptions.XmlConfigurationException; -import org.ehcache.xml.model.TimeType; +import org.ehcache.xml.model.TimeTypeWithPropSubst; +import org.osgi.service.component.annotations.Component; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -45,11 +48,13 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Properties; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; +import javax.xml.bind.helpers.DefaultValidationEventHandler; import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; @@ -63,6 +68,7 @@ * * @see ClusteredCacheConstants#XSD */ +@Component public class ClusteringCacheManagerServiceConfigurationParser extends BaseConfigParser implements CacheManagerServiceConfigurationParser { public static final String CLUSTER_ELEMENT_NAME = "cluster"; @@ -80,6 +86,7 @@ public class ClusteringCacheManagerServiceConfigurationParser extends BaseConfig public static final String SHARED_POOL_ELEMENT_NAME = "shared-pool"; public static final String SERVER_SIDE_CONFIG = "server-side-config"; public static final String AUTO_CREATE_ATTRIBUTE_NAME = "auto-create"; + public static final String CLIENT_MODE_ATTRIBUTE_NAME = "client-mode"; public static final String UNIT_ATTRIBUTE_NAME = "unit"; public static final String NAME_ATTRIBUTE_NAME = "name"; public static final String FROM_ATTRIBUTE_NAME = "from"; @@ -106,12 +113,13 @@ public URI getNamespace() { * This method presumes the element presented is valid according to the XSD. * * @param fragment the XML fragment to process + * @param classLoader * @return a {@link org.ehcache.clustered.client.config.ClusteringServiceConfiguration ClusteringServiceConfiguration} */ @Override - public ServiceCreationConfiguration parseServiceCreationConfiguration(final Element fragment) { + public ServiceCreationConfiguration parseServiceCreationConfiguration(final Element fragment, ClassLoader classLoader) { - if ("cluster".equals(fragment.getLocalName())) { + if (CLUSTER_ELEMENT_NAME.equals(fragment.getLocalName())) { ClusteringCacheManagerServiceConfigurationParser.ServerSideConfig serverConfig = null; URI connectionUri = null; @@ -123,12 +131,12 @@ public ServiceCreationConfiguration parseServiceCreationConfi final Node item = childNodes.item(i); if (Node.ELEMENT_NODE == item.getNodeType()) { switch (item.getLocalName()) { - case "connection": + case CONNECTION_ELEMENT_NAME: /* * is a required element in the XSD */ - final Attr urlAttribute = ((Element)item).getAttributeNode("url"); - final String urlValue = urlAttribute.getValue(); + final Attr urlAttribute = ((Element)item).getAttributeNode(URL_ATTRIBUTE_NAME); + final String urlValue = JaxbParsers.parseStringWithProperties(urlAttribute.getValue()); try { connectionUri = new URI(urlValue); } catch (URISyntaxException e) { @@ -138,50 +146,50 @@ public ServiceCreationConfiguration parseServiceCreationConfi } break; - case "cluster-connection": - clusterTierManager = ((Element)item).getAttribute("cluster-tier-manager"); + case CLUSTER_CONNECTION_ELEMENT_NAME: + clusterTierManager = JaxbParsers.parsePropertyOrString(((Element)item).getAttribute(CLUSTER_TIER_MANAGER_ATTRIBUTE_NAME)); final NodeList serverNodes = item.getChildNodes(); for (int j = 0; j < serverNodes.getLength(); j++) { final Node serverNode = serverNodes.item(j); - final String host = ((Element)serverNode).getAttributeNode("host").getValue(); - final Attr port = ((Element)serverNode).getAttributeNode("port"); + final String host = JaxbParsers.parsePropertyOrString(((Element)serverNode).getAttributeNode(HOST_ATTRIBUTE_NAME).getValue().trim()); + final Attr port = ((Element)serverNode).getAttributeNode(PORT_ATTRIBUTE_NAME); InetSocketAddress address; if (port == null) { address = InetSocketAddress.createUnresolved(host, 0); } else { - String portString = port.getValue(); + String portString = JaxbParsers.parsePropertyOrString(port.getValue()); address = InetSocketAddress.createUnresolved(host, Integer.parseInt(portString)); } serverAddresses.add(address); } break; - case "read-timeout": + case READ_TIMEOUT_ELEMENT_NAME: /* * is an optional element */ getTimeout = processTimeout(fragment, item); break; - case "write-timeout": + case WRITE_TIMEOUT_ELEMENT_NAME: /* * is an optional element */ putTimeout = processTimeout(fragment, item); break; - case "connection-timeout": + case CONNECTION_TIMEOUT_ELEMENT_NAME: /* * is an optional element */ connectionTimeout = processTimeout(fragment, item); break; - case "server-side-config": + case SERVER_SIDE_CONFIG: /* * is an optional element */ - serverConfig = processServerSideConfig(item); + serverConfig = processServerSideConfig((Element) item); break; default: throw new XmlConfigurationException( @@ -192,26 +200,25 @@ public ServiceCreationConfiguration parseServiceCreationConfi } try { - Timeouts timeouts = getTimeouts(getTimeout, putTimeout, connectionTimeout); - if (serverConfig == null) { - if (connectionUri != null) { - return new ClusteringServiceConfiguration(connectionUri, timeouts); - } else { - return new ClusteringServiceConfiguration(serverAddresses, clusterTierManager, timeouts); - } - } - - ServerSideConfiguration serverSideConfiguration; - if (serverConfig.defaultServerResource == null) { - serverSideConfiguration = new ServerSideConfiguration(serverConfig.pools); + ConnectionSource connectionSource; + if (connectionUri != null) { + connectionSource = new ConnectionSource.ClusterUri(connectionUri); } else { - serverSideConfiguration = new ServerSideConfiguration(serverConfig.defaultServerResource, serverConfig.pools); + connectionSource = new ConnectionSource.ServerList(serverAddresses, clusterTierManager); } - if (connectionUri != null) { - return new ClusteringServiceConfiguration(connectionUri, timeouts, serverConfig.autoCreate, serverSideConfiguration); + Timeouts timeouts = getTimeouts(getTimeout, putTimeout, connectionTimeout); + + if (serverConfig == null) { + return new ClusteringServiceConfiguration(connectionSource, timeouts, ClientMode.CONNECT, null, new Properties()); } else { - return new ClusteringServiceConfiguration(serverAddresses, clusterTierManager, timeouts, serverConfig.autoCreate, serverSideConfiguration); + ServerSideConfiguration serverSideConfiguration; + if (serverConfig.defaultServerResource == null) { + serverSideConfiguration = new ServerSideConfiguration(serverConfig.pools); + } else { + serverSideConfiguration = new ServerSideConfiguration(serverConfig.defaultServerResource, serverConfig.pools); + } + return new ClusteringServiceConfiguration(connectionSource, timeouts, serverConfig.clientMode, serverSideConfiguration, new Properties()); } } catch (IllegalArgumentException e) { throw new XmlConfigurationException(e); @@ -232,7 +239,7 @@ public Class getServiceType() { * @param serviceCreationConfiguration */ @Override - public Element unparseServiceCreationConfiguration(final ServiceCreationConfiguration serviceCreationConfiguration) { + public Element unparseServiceCreationConfiguration(final ServiceCreationConfiguration serviceCreationConfiguration) { Element rootElement = unparseConfig(serviceCreationConfiguration); return rootElement; } @@ -290,7 +297,9 @@ protected Element createRootElement(Document doc, ClusteringServiceConfiguration processTimeUnits(doc, rootElement, clusteringServiceConfiguration); Element serverSideConfigurationElem = processServerSideElements(doc, clusteringServiceConfiguration); - rootElement.appendChild(serverSideConfigurationElem); + if (serverSideConfigurationElem != null) { + rootElement.appendChild(serverSideConfigurationElem); + } return rootElement; } @@ -325,32 +334,37 @@ private Element createTimeoutElement(Document doc, String timeoutName, Duration } protected Element processServerSideElements(Document doc, ClusteringServiceConfiguration clusteringServiceConfiguration) { - Element serverSideConfigurationElem = createServerSideConfigurationElement(doc, clusteringServiceConfiguration); - - if (clusteringServiceConfiguration.getServerConfiguration() != null) { - ServerSideConfiguration serverSideConfiguration = clusteringServiceConfiguration.getServerConfiguration(); - String defaultServerResource = serverSideConfiguration.getDefaultServerResource(); - if (!(defaultServerResource == null || defaultServerResource.trim().length() == 0)) { - Element defaultResourceElement = createDefaultServerResourceElement(doc, defaultServerResource); - serverSideConfigurationElem.appendChild(defaultResourceElement); - } - Map resourcePools = serverSideConfiguration.getResourcePools(); - if (resourcePools != null) { - resourcePools.forEach( - (key, value) -> { - Element poolElement = createSharedPoolElement(doc, key, value); - serverSideConfigurationElem.appendChild(poolElement); - } - ); - } + switch (clusteringServiceConfiguration.getClientMode()) { + case CONNECT: + return null; + case EXPECTING: + case AUTO_CREATE: + case AUTO_CREATE_ON_RECONNECT: + Element serverSideConfigurationElem = createServerSideConfigurationElement(doc, clusteringServiceConfiguration); + ServerSideConfiguration serverSideConfiguration = clusteringServiceConfiguration.getServerConfiguration(); + String defaultServerResource = serverSideConfiguration.getDefaultServerResource(); + if (!(defaultServerResource == null || defaultServerResource.trim().length() == 0)) { + Element defaultResourceElement = createDefaultServerResourceElement(doc, defaultServerResource); + serverSideConfigurationElem.appendChild(defaultResourceElement); + } + Map resourcePools = serverSideConfiguration.getResourcePools(); + if (resourcePools != null) { + resourcePools.forEach( + (key, value) -> { + Element poolElement = createSharedPoolElement(doc, key, value); + serverSideConfigurationElem.appendChild(poolElement); + } + ); + } + return serverSideConfigurationElem; } - return serverSideConfigurationElem; + throw new AssertionError(); } private Element createServerSideConfigurationElement(Document doc, ClusteringServiceConfiguration clusteringServiceConfiguration) { Element serverSideConfigurationElem = doc.createElement(TC_CLUSTERED_NAMESPACE_PREFIX + SERVER_SIDE_CONFIG); - serverSideConfigurationElem.setAttribute(AUTO_CREATE_ATTRIBUTE_NAME, Boolean.toString(clusteringServiceConfiguration - .isAutoCreate())); + serverSideConfigurationElem.setAttribute(CLIENT_MODE_ATTRIBUTE_NAME, clusteringServiceConfiguration.getClientMode() + .name().toLowerCase(Locale.ROOT).replace('_', '-')); return serverSideConfigurationElem; } @@ -377,30 +391,43 @@ private Element createDefaultServerResourceElement(Document doc, String defaultS return defaultResourceElement; } - private ClusteringCacheManagerServiceConfigurationParser.ServerSideConfig processServerSideConfig(Node serverSideConfigElement) { + private ClusteringCacheManagerServiceConfigurationParser.ServerSideConfig processServerSideConfig(Element serverSideConfigElement) { ClusteringCacheManagerServiceConfigurationParser.ServerSideConfig serverSideConfig = new ClusteringCacheManagerServiceConfigurationParser.ServerSideConfig(); - serverSideConfig.autoCreate = Boolean.parseBoolean(((Element)serverSideConfigElement).getAttribute("auto-create")); + + String autoCreateAttr = serverSideConfigElement.getAttribute(AUTO_CREATE_ATTRIBUTE_NAME); + String clientModeAttr = serverSideConfigElement.getAttribute(CLIENT_MODE_ATTRIBUTE_NAME); + if (clientModeAttr.isEmpty()) { + if (!autoCreateAttr.isEmpty()) { + serverSideConfig.clientMode = Boolean.parseBoolean(autoCreateAttr) ? ClientMode.AUTO_CREATE : ClientMode.EXPECTING; + } + } else if (autoCreateAttr.isEmpty()) { + clientModeAttr = JaxbParsers.parsePropertyOrString(clientModeAttr); + serverSideConfig.clientMode = ClientMode.valueOf(clientModeAttr.toUpperCase(Locale.ROOT).replace('-', '_')); + } else { + throw new XmlConfigurationException("Cannot define both '" + AUTO_CREATE_ATTRIBUTE_NAME + "' and '" + CLIENT_MODE_ATTRIBUTE_NAME + "' attributes"); + } + final NodeList serverSideNodes = serverSideConfigElement.getChildNodes(); for (int i = 0; i < serverSideNodes.getLength(); i++) { final Node item = serverSideNodes.item(i); if (Node.ELEMENT_NODE == item.getNodeType()) { String nodeLocalName = item.getLocalName(); - if ("default-resource".equals(nodeLocalName)) { - serverSideConfig.defaultServerResource = ((Element)item).getAttribute("from"); + if (DEFAULT_RESOURCE_ELEMENT_NAME.equals(nodeLocalName)) { + serverSideConfig.defaultServerResource = JaxbParsers.parsePropertyOrString(((Element)item).getAttribute(FROM_ATTRIBUTE_NAME)); - } else if ("shared-pool".equals(nodeLocalName)) { + } else if (SHARED_POOL_ELEMENT_NAME.equals(nodeLocalName)) { Element sharedPoolElement = (Element)item; - String poolName = sharedPoolElement.getAttribute("name"); // required - Attr fromAttr = sharedPoolElement.getAttributeNode("from"); // optional + String poolName = sharedPoolElement.getAttribute(NAME_ATTRIBUTE_NAME); // required + Attr fromAttr = sharedPoolElement.getAttributeNode(FROM_ATTRIBUTE_NAME); // optional String fromResource = (fromAttr == null ? null : fromAttr.getValue()); - Attr unitAttr = sharedPoolElement.getAttributeNode("unit"); // optional - default 'B' + Attr unitAttr = sharedPoolElement.getAttributeNode(UNIT_ATTRIBUTE_NAME); // optional - default 'B' String unit = (unitAttr == null ? "B" : unitAttr.getValue()); MemoryUnit memoryUnit = MemoryUnit.valueOf(unit.toUpperCase(Locale.ENGLISH)); String quantityValue = sharedPoolElement.getFirstChild().getNodeValue(); long quantity; try { - quantity = Long.parseLong(quantityValue); + quantity = JaxbParsers.parsePropertyOrPositiveInteger(quantityValue).longValueExact(); } catch (NumberFormatException e) { throw new XmlConfigurationException("Magnitude of value specified for is too large"); @@ -410,7 +437,7 @@ private ClusteringCacheManagerServiceConfigurationParser.ServerSideConfig proces if (fromResource == null) { poolDefinition = new ServerSideConfiguration.Pool(memoryUnit.toBytes(quantity)); } else { - poolDefinition = new ServerSideConfiguration.Pool(memoryUnit.toBytes(quantity), fromResource); + poolDefinition = new ServerSideConfiguration.Pool(memoryUnit.toBytes(quantity), JaxbParsers.parsePropertyOrString(fromResource)); } if (serverSideConfig.pools.put(poolName, poolDefinition) != null) { @@ -425,11 +452,12 @@ private ClusteringCacheManagerServiceConfigurationParser.ServerSideConfig proces private Duration processTimeout(Element parentElement, Node timeoutNode) { try { // are direct subtype of ehcache:time-type; use JAXB to interpret it - JAXBContext context = JAXBContext.newInstance(TimeType.class); + JAXBContext context = JAXBContext.newInstance(TimeTypeWithPropSubst.class); Unmarshaller unmarshaller = context.createUnmarshaller(); - JAXBElement jaxbElement = unmarshaller.unmarshal(timeoutNode, TimeType.class); + unmarshaller.setEventHandler(new DefaultValidationEventHandler()); + JAXBElement jaxbElement = unmarshaller.unmarshal(timeoutNode, TimeTypeWithPropSubst.class); - TimeType timeType = jaxbElement.getValue(); + TimeTypeWithPropSubst timeType = jaxbElement.getValue(); BigInteger amount = timeType.getValue(); if (amount.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0) { throw new XmlConfigurationException( @@ -458,7 +486,7 @@ private Timeouts getTimeouts(Duration getTimeout, Duration putTimeout, Duration } private static final class ServerSideConfig { - private boolean autoCreate = false; + private ClientMode clientMode = ClientMode.CONNECT; private String defaultServerResource = null; private final Map pools = new HashMap<>(); } diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/config/xml/ClusteringCacheServiceConfigurationParser.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/config/xml/ClusteringCacheServiceConfigurationParser.java similarity index 91% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/config/xml/ClusteringCacheServiceConfigurationParser.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/config/xml/ClusteringCacheServiceConfigurationParser.java index f85cd0aa5b..67b7c4e7bf 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/config/xml/ClusteringCacheServiceConfigurationParser.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/config/xml/ClusteringCacheServiceConfigurationParser.java @@ -22,6 +22,7 @@ import org.ehcache.xml.BaseConfigParser; import org.ehcache.xml.CacheServiceConfigurationParser; import org.ehcache.xml.exceptions.XmlConfigurationException; +import org.osgi.service.component.annotations.Component; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -40,6 +41,7 @@ * * @see ClusteredCacheConstants#XSD */ +@Component public class ClusteringCacheServiceConfigurationParser extends BaseConfigParser implements CacheServiceConfigurationParser { public static final String CLUSTERED_STORE_ELEMENT_NAME = "clustered-store"; @@ -60,10 +62,10 @@ public URI getNamespace() { } @Override - public ServiceConfiguration parseServiceConfiguration(Element fragment) { + public ServiceConfiguration parseServiceConfiguration(Element fragment, ClassLoader classLoader) { if (CLUSTERED_STORE_ELEMENT_NAME.equals(fragment.getLocalName())) { if (fragment.hasAttribute(CONSISTENCY_ATTRIBUTE_NAME)) { - return new ClusteredStoreConfiguration(Consistency.valueOf(fragment.getAttribute("consistency").toUpperCase())); + return new ClusteredStoreConfiguration(Consistency.valueOf(fragment.getAttribute(CONSISTENCY_ATTRIBUTE_NAME).toUpperCase())); } else { return new ClusteredStoreConfiguration(); } @@ -78,7 +80,7 @@ public Class getServiceType() { } @Override - public Element unparseServiceConfiguration(ServiceConfiguration serviceConfiguration) { + public Element unparseServiceConfiguration(ServiceConfiguration serviceConfiguration) { return unparseConfig(serviceConfiguration); } diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/ClusteredLoaderWriterStore.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/ClusteredLoaderWriterStore.java similarity index 71% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/ClusteredLoaderWriterStore.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/ClusteredLoaderWriterStore.java index 8acf69ff81..998e481e0c 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/ClusteredLoaderWriterStore.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/ClusteredLoaderWriterStore.java @@ -15,31 +15,31 @@ */ package org.ehcache.clustered.client.internal.loaderwriter; -import org.ehcache.CacheIterationException; import org.ehcache.clustered.client.internal.store.ClusteredStore; import org.ehcache.clustered.client.internal.store.ClusteredValueHolder; -import org.ehcache.clustered.client.internal.store.ResolvedChain; import org.ehcache.clustered.client.internal.store.ServerStoreProxy; -import org.ehcache.clustered.client.internal.store.lock.LockManager; +import org.ehcache.clustered.client.internal.store.lock.LockingServerStoreProxy; import org.ehcache.clustered.client.internal.store.operations.ChainResolver; import org.ehcache.clustered.client.internal.store.operations.EternalChainResolver; +import org.ehcache.clustered.client.service.ClusteringService; import org.ehcache.clustered.common.internal.store.operations.ConditionalRemoveOperation; import org.ehcache.clustered.common.internal.store.operations.ConditionalReplaceOperation; import org.ehcache.clustered.common.internal.store.operations.PutIfAbsentOperation; import org.ehcache.clustered.common.internal.store.operations.PutOperation; import org.ehcache.clustered.common.internal.store.operations.RemoveOperation; import org.ehcache.clustered.common.internal.store.operations.ReplaceOperation; -import org.ehcache.clustered.common.internal.store.operations.Result; import org.ehcache.clustered.common.internal.store.operations.codecs.OperationsCodec; -import org.ehcache.clustered.client.service.ClusteringService; -import org.ehcache.clustered.common.internal.store.Chain; import org.ehcache.config.ResourceType; +import org.ehcache.core.events.StoreEventDispatcher; import org.ehcache.core.exceptions.StorePassThroughException; +import org.ehcache.core.spi.service.StatisticsService; import org.ehcache.core.spi.store.tiering.AuthoritativeTier; import org.ehcache.core.spi.time.TimeSource; import org.ehcache.core.spi.time.TimeSourceService; +import org.ehcache.impl.store.DefaultStoreEventDispatcher; import org.ehcache.spi.loaderwriter.CacheLoaderWriter; import org.ehcache.spi.loaderwriter.CacheLoaderWriterConfiguration; +import org.ehcache.spi.loaderwriter.CacheLoadingException; import org.ehcache.spi.resilience.StoreAccessException; import org.ehcache.spi.service.ServiceConfiguration; import org.ehcache.spi.service.ServiceDependencies; @@ -50,7 +50,6 @@ import java.util.concurrent.TimeoutException; import static org.ehcache.core.exceptions.ExceptionFactory.newCacheLoadingException; -import static org.ehcache.core.exceptions.ExceptionFactory.newCacheWritingException; import static org.ehcache.core.exceptions.StorePassThroughException.handleException; public class ClusteredLoaderWriterStore extends ClusteredStore implements AuthoritativeTier { @@ -59,8 +58,8 @@ public class ClusteredLoaderWriterStore extends ClusteredStore imple private final boolean useLoaderInAtomics; public ClusteredLoaderWriterStore(Configuration config, OperationsCodec codec, ChainResolver resolver, TimeSource timeSource, - CacheLoaderWriter loaderWriter, boolean useLoaderInAtomics) { - super(config, codec, resolver, timeSource); + CacheLoaderWriter loaderWriter, boolean useLoaderInAtomics, StoreEventDispatcher storeEventDispatcher, StatisticsService statisticsService) { + super(config, codec, resolver, timeSource, storeEventDispatcher, statisticsService); this.cacheLoaderWriter = loaderWriter; this.useLoaderInAtomics = useLoaderInAtomics; } @@ -69,14 +68,14 @@ public ClusteredLoaderWriterStore(Configuration config, OperationsCodec config, OperationsCodec codec, EternalChainResolver resolver, - ServerStoreProxy proxy, TimeSource timeSource, CacheLoaderWriter loaderWriter) { - super(config, codec, resolver, proxy, timeSource); + ServerStoreProxy proxy, TimeSource timeSource, CacheLoaderWriter loaderWriter, StatisticsService statisticsService) { + super(config, codec, resolver, proxy, timeSource, null, statisticsService); this.cacheLoaderWriter = loaderWriter; this.useLoaderInAtomics = true; } - private LockManager getProxy() { - return (LockManager) storeProxy; + private LockingServerStoreProxy getProxy() { + return (LockingServerStoreProxy) storeProxy; } @Override @@ -88,11 +87,11 @@ protected ValueHolder getInternal(K key) throws StoreAccessException, Timeout boolean unlocked = false; getProxy().lock(hash); try { - V value = null; + V value; try { value = cacheLoaderWriter.load(key); } catch (Exception e) { - throw new StorePassThroughException(new CacheIterationException(e)); + throw new StorePassThroughException(new CacheLoadingException(e)); } if (value == null) { return null; @@ -101,9 +100,7 @@ protected ValueHolder getInternal(K key) throws StoreAccessException, Timeout unlocked = true; return new ClusteredValueHolder<>(value); } finally { - if (!unlocked) { - getProxy().unlock(hash); - } + getProxy().unlock(hash, unlocked); } } } catch (RuntimeException re) { @@ -112,6 +109,15 @@ protected ValueHolder getInternal(K key) throws StoreAccessException, Timeout return holder; } + @Override + public boolean containsKey(K key) throws StoreAccessException { + try { + return super.getInternal(key) != null; + } catch (TimeoutException e) { + return false; + } + } + private void append(K key, V value) throws TimeoutException { PutOperation operation = new PutOperation<>(key, value, timeSource.getTimeMillis()); ByteBuffer payload = codec.encode(operation); @@ -120,7 +126,7 @@ private void append(K key, V value) throws TimeoutException { } @Override - protected PutStatus silentPut(K key, V value) throws StoreAccessException { + protected void silentPut(K key, V value) throws StoreAccessException { try { long hash = extractLongKey(key); boolean unlocked = false; @@ -130,38 +136,47 @@ protected PutStatus silentPut(K key, V value) throws StoreAccessException { append(key, value); unlocked = true; } finally { - if (!unlocked) { - getProxy().unlock(hash); - } + getProxy().unlock(hash, unlocked); } - return PutStatus.PUT; } catch (Exception e) { throw handleException(e); } } @Override - protected boolean silentRemove(K key) throws StoreAccessException { + protected ValueHolder silentGetAndPut(K key, V value) throws StoreAccessException { + try { + long hash = extractLongKey(key); + boolean unlocked = false; + final ServerStoreProxy.ChainEntry chain = getProxy().lock(hash); + try { + cacheLoaderWriter.write(key, value); + append(key, value); + unlocked = true; + return resolver.resolve(chain, key, timeSource.getTimeMillis()); + } finally { + getProxy().unlock(hash, unlocked); + } + } catch (Exception e) { + throw handleException(e); + } + } + + @Override + protected ValueHolder silentRemove(K key) throws StoreAccessException { try { long hash = extractLongKey(key); boolean unlocked = false; RemoveOperation operation = new RemoveOperation<>(key, timeSource.getTimeMillis()); ByteBuffer payLoad = codec.encode(operation); - Chain chain = getProxy().lock(hash); + ServerStoreProxy.ChainEntry chain = getProxy().lock(hash); try { cacheLoaderWriter.delete(key); storeProxy.append(hash, payLoad); unlocked = true; - ResolvedChain resolvedChain = resolver.resolve(chain, key, timeSource.getTimeMillis()); - if (resolvedChain.getResolvedResult(key) != null) { - return true; - } else { - return false; - } + return resolver.resolve(chain, key, timeSource.getTimeMillis()); } finally { - if (!unlocked) { - getProxy().unlock(hash); - } + getProxy().unlock(hash, unlocked); } } catch (Exception e) { throw handleException(e); @@ -169,15 +184,13 @@ protected boolean silentRemove(K key) throws StoreAccessException { } @Override - protected V silentPutIfAbsent(K key, V value) throws StoreAccessException { + protected ValueHolder silentPutIfAbsent(K key, V value) throws StoreAccessException { try { long hash = extractLongKey(key); boolean unlocked = false; - Chain existing = getProxy().lock(hash); + ServerStoreProxy.ChainEntry existing = getProxy().lock(hash); try { - ResolvedChain resolvedChain = resolver.resolve(existing, key, timeSource.getTimeMillis()); - Result result = resolvedChain.getResolvedResult(key); - V existingVal = result == null ? null : result.getValue(); + ValueHolder existingVal = resolver.resolve(existing, key, timeSource.getTimeMillis()); if (existingVal != null) { return existingVal; } else { @@ -192,9 +205,7 @@ protected V silentPutIfAbsent(K key, V value) throws StoreAccessException { return existingVal; } } finally { - if (!unlocked) { - getProxy().unlock(hash); - } + getProxy().unlock(hash, unlocked); } } catch (Exception e) { throw handleException(e); @@ -202,15 +213,13 @@ protected V silentPutIfAbsent(K key, V value) throws StoreAccessException { } @Override - protected V silentReplace(K key, V value) throws StoreAccessException { + protected ValueHolder silentReplace(K key, V value) throws StoreAccessException { try { long hash = extractLongKey(key); boolean unlocked = false; - Chain existing = getProxy().lock(hash); + ServerStoreProxy.ChainEntry existing = getProxy().lock(hash); try { - ResolvedChain resolvedChain = resolver.resolve(existing, key, timeSource.getTimeMillis()); - Result result = resolvedChain.getResolvedResult(key); - V existingVal = result == null ? null : result.getValue(); + ValueHolder existingVal = resolver.resolve(existing, key, timeSource.getTimeMillis()); if (existingVal != null) { cacheLoaderWriter.write(key, value); ReplaceOperation operation = new ReplaceOperation<>(key, value, timeSource.getTimeMillis()); @@ -219,7 +228,7 @@ protected V silentReplace(K key, V value) throws StoreAccessException { unlocked = true; return existingVal; } else { - V inCache = loadFromLoaderWriter(key); + ValueHolder inCache = loadFromLoaderWriter(key); if (inCache != null) { cacheLoaderWriter.write(key, value); ReplaceOperation operation = new ReplaceOperation<>(key, value, timeSource.getTimeMillis()); @@ -232,9 +241,7 @@ protected V silentReplace(K key, V value) throws StoreAccessException { } } } finally { - if (!unlocked) { - getProxy().unlock(hash); - } + getProxy().unlock(hash, unlocked); } } catch (Exception e) { throw handleException(e); @@ -242,19 +249,17 @@ protected V silentReplace(K key, V value) throws StoreAccessException { } @Override - protected V silentRemove(K key, V value) throws StoreAccessException { + protected ValueHolder silentRemove(K key, V value) throws StoreAccessException { try { long hash = extractLongKey(key); boolean unlocked = false; - Chain existing = getProxy().lock(hash); + ServerStoreProxy.ChainEntry existing = getProxy().lock(hash); try { - ResolvedChain resolvedChain = resolver.resolve(existing, key, timeSource.getTimeMillis()); - Result result = resolvedChain.getResolvedResult(key); - V existingVal = result == null ? null : result.getValue(); + ValueHolder existingVal = resolver.resolve(existing, key, timeSource.getTimeMillis()); if (existingVal == null) { existingVal = loadFromLoaderWriter(key); } - if (value.equals(existingVal)) { + if (existingVal != null && value.equals(existingVal.get())) { cacheLoaderWriter.delete(key); ConditionalRemoveOperation operation = new ConditionalRemoveOperation<>(key, value, timeSource.getTimeMillis()); ByteBuffer payLoad = codec.encode(operation); @@ -263,9 +268,7 @@ protected V silentRemove(K key, V value) throws StoreAccessException { } return existingVal; } finally { - if (!unlocked) { - getProxy().unlock(hash); - } + getProxy().unlock(hash, unlocked); } } catch (Exception e) { throw handleException(e); @@ -273,19 +276,17 @@ protected V silentRemove(K key, V value) throws StoreAccessException { } @Override - protected V silentReplace(K key, V oldValue, V newValue) throws StoreAccessException { + protected ValueHolder silentReplace(K key, V oldValue, V newValue) throws StoreAccessException { try { long hash = extractLongKey(key); boolean unlocked = false; - Chain existing = getProxy().lock(hash); + ServerStoreProxy.ChainEntry existing = getProxy().lock(hash); try { - ResolvedChain resolvedChain = resolver.resolve(existing, key, timeSource.getTimeMillis()); - Result result = resolvedChain.getResolvedResult(key); - V existingVal = result == null ? null : result.getValue(); + ValueHolder existingVal = resolver.resolve(existing, key, timeSource.getTimeMillis()); if (existingVal == null) { existingVal = loadFromLoaderWriter(key); } - if (oldValue.equals(existingVal)) { + if (existingVal != null && oldValue.equals(existingVal.get())) { cacheLoaderWriter.write(key, newValue); ConditionalReplaceOperation operation = new ConditionalReplaceOperation<>(key, oldValue, newValue, timeSource.getTimeMillis()); ByteBuffer payLoad = codec.encode(operation); @@ -294,19 +295,22 @@ protected V silentReplace(K key, V oldValue, V newValue) throws StoreAccessExcep } return existingVal; } finally { - if (!unlocked) { - getProxy().unlock(hash); - } + getProxy().unlock(hash, unlocked); } } catch (Exception e) { throw handleException(e); } } - private V loadFromLoaderWriter(K key) { + private ValueHolder loadFromLoaderWriter(K key) { if (useLoaderInAtomics) { try { - return cacheLoaderWriter.load(key); + V loaded = cacheLoaderWriter.load(key); + if (loaded == null) { + return null; + } else { + return new ClusteredValueHolder<>(loaded); + } } catch (Exception e) { throw new StorePassThroughException(newCacheLoadingException(e)); } @@ -326,12 +330,13 @@ protected ClusteredStore createStore(Configuration storeConfi TimeSource timeSource, boolean useLoaderInAtomics, Object[] serviceConfigs) { + StoreEventDispatcher storeEventDispatcher = new DefaultStoreEventDispatcher<>(storeConfig.getDispatcherConcurrency()); return new ClusteredLoaderWriterStore<>(storeConfig, codec, resolver, timeSource, - storeConfig.getCacheLoaderWriter(), useLoaderInAtomics); + storeConfig.getCacheLoaderWriter(), useLoaderInAtomics, storeEventDispatcher, getServiceProvider().getService(StatisticsService.class)); } @Override - public int rank(Set> resourceTypes, Collection> serviceConfigs) { + public int rank(Set> resourceTypes, Collection> serviceConfigs) { int parentRank = super.rank(resourceTypes, serviceConfigs); if (parentRank == 0 || serviceConfigs.stream().noneMatch(CacheLoaderWriterConfiguration.class::isInstance)) { return 0; @@ -340,7 +345,7 @@ public int rank(Set> resourceTypes, Collection authorityResource, Collection> serviceConfigs) { + public int rankAuthority(ResourceType authorityResource, Collection> serviceConfigs) { int parentRank = super.rankAuthority(authorityResource, serviceConfigs); if (parentRank == 0 || serviceConfigs.stream().noneMatch(CacheLoaderWriterConfiguration.class::isInstance)) { return 0; diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/ClusteredLoaderWriterStoreProviderFactory.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/ClusteredLoaderWriterStoreProviderFactory.java similarity index 89% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/ClusteredLoaderWriterStoreProviderFactory.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/ClusteredLoaderWriterStoreProviderFactory.java index 7d68a59ce4..98a26168d8 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/ClusteredLoaderWriterStoreProviderFactory.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/ClusteredLoaderWriterStoreProviderFactory.java @@ -17,11 +17,13 @@ import org.ehcache.core.spi.service.ServiceFactory; import org.ehcache.spi.service.ServiceCreationConfiguration; +import org.osgi.service.component.annotations.Component; +@Component public class ClusteredLoaderWriterStoreProviderFactory implements ServiceFactory { @Override - public ClusteredLoaderWriterStore.Provider create(ServiceCreationConfiguration configuration) { + public ClusteredLoaderWriterStore.Provider create(ServiceCreationConfiguration configuration) { return new ClusteredLoaderWriterStore.Provider(); } diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/DelegatingLoaderWriterStore.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/DelegatingLoaderWriterStore.java similarity index 93% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/DelegatingLoaderWriterStore.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/DelegatingLoaderWriterStore.java index 920ea16c42..b9cb0c854e 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/DelegatingLoaderWriterStore.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/DelegatingLoaderWriterStore.java @@ -55,6 +55,11 @@ public PutStatus put(K key, V value) throws StoreAccessException { return delegate.put(key, value); } + @Override + public ValueHolder getAndPut(K key, V value) throws StoreAccessException { + return delegate.getAndPut(key, value); + } + @Override public ValueHolder putIfAbsent(K key, V value, Consumer put) throws StoreAccessException { return delegate.putIfAbsent(key, value, put); @@ -65,6 +70,11 @@ public boolean remove(K key) throws StoreAccessException { return delegate.remove(key); } + @Override + public ValueHolder getAndRemove(K key) throws StoreAccessException { + return delegate.getAndRemove(key); + } + @Override public RemoveStatus remove(K key, V value) throws StoreAccessException { return delegate.remove(key, value); @@ -92,7 +102,7 @@ public StoreEventSource getStoreEventSource() { @Override public Iterator>> iterator() { - throw new UnsupportedOperationException("Implement me"); + return delegate.iterator(); } @Override diff --git a/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/DelegatingLoaderWriterStoreProvider.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/DelegatingLoaderWriterStoreProvider.java new file mode 100644 index 0000000000..7a5bb4846d --- /dev/null +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/DelegatingLoaderWriterStoreProvider.java @@ -0,0 +1,55 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.clustered.client.internal.loaderwriter; + +import org.ehcache.clustered.client.service.ClusteringService; +import org.ehcache.clustered.client.service.ClusteringService.ClusteredCacheIdentifier; +import org.ehcache.config.ResourceType; +import org.ehcache.core.spi.store.AbstractWrapperStoreProvider; +import org.ehcache.core.spi.store.Store; +import org.ehcache.spi.loaderwriter.CacheLoaderWriterConfiguration; +import org.ehcache.spi.loaderwriter.CacheLoaderWriterProvider; +import org.ehcache.spi.service.ServiceConfiguration; +import org.ehcache.spi.service.ServiceDependencies; +import java.util.Collection; +import java.util.Set; + +import static org.ehcache.core.spi.service.ServiceUtils.findSingletonAmongst; + +@ServiceDependencies({CacheLoaderWriterProvider.class, ClusteringService.class}) +public class DelegatingLoaderWriterStoreProvider extends AbstractWrapperStoreProvider { + + @Override + protected Store wrap(Store store, Store.Configuration storeConfig, ServiceConfiguration... serviceConfigs) { + DelegatingLoaderWriterStore loaderWriterStore = new DelegatingLoaderWriterStore<>(store); + return loaderWriterStore; + } + + @Override + public int rank(Set> resourceTypes, Collection> serviceConfigs) { + throw new UnsupportedOperationException("Its a Wrapper store provider, does not support regular ranking"); + } + + @Override + public int wrapperStoreRank(Collection> serviceConfigs) { + CacheLoaderWriterConfiguration loaderWriterConfiguration = findSingletonAmongst(CacheLoaderWriterConfiguration.class, serviceConfigs); + ClusteredCacheIdentifier clusteredCacheIdentifier = findSingletonAmongst(ClusteredCacheIdentifier.class, serviceConfigs); + if (clusteredCacheIdentifier != null && loaderWriterConfiguration != null) { + return 3; + } + return 0; + } +} diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/DelegatingLoaderWriterStoreProviderFactory.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/DelegatingLoaderWriterStoreProviderFactory.java similarity index 89% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/DelegatingLoaderWriterStoreProviderFactory.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/DelegatingLoaderWriterStoreProviderFactory.java index 95828fd554..0ddca5007d 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/DelegatingLoaderWriterStoreProviderFactory.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/DelegatingLoaderWriterStoreProviderFactory.java @@ -17,11 +17,13 @@ import org.ehcache.core.spi.service.ServiceFactory; import org.ehcache.spi.service.ServiceCreationConfiguration; +import org.osgi.service.component.annotations.Component; +@Component public class DelegatingLoaderWriterStoreProviderFactory implements ServiceFactory { @Override - public DelegatingLoaderWriterStoreProvider create(ServiceCreationConfiguration configuration) { + public DelegatingLoaderWriterStoreProvider create(ServiceCreationConfiguration configuration) { return new DelegatingLoaderWriterStoreProvider(); } diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/writebehind/ClusteredWriteBehind.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/writebehind/ClusteredWriteBehind.java similarity index 91% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/writebehind/ClusteredWriteBehind.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/writebehind/ClusteredWriteBehind.java index 4df49d721a..852f0ff3e1 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/writebehind/ClusteredWriteBehind.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/writebehind/ClusteredWriteBehind.java @@ -15,7 +15,7 @@ */ package org.ehcache.clustered.client.internal.loaderwriter.writebehind; -import org.ehcache.clustered.client.internal.store.ChainBuilder; +import org.ehcache.clustered.common.internal.util.ChainBuilder; import org.ehcache.clustered.client.internal.store.operations.ChainResolver; import org.ehcache.clustered.common.internal.store.operations.ConditionalRemoveOperation; import org.ehcache.clustered.common.internal.store.operations.Operation; @@ -24,12 +24,10 @@ import org.ehcache.clustered.common.internal.store.operations.codecs.OperationsCodec; import org.ehcache.clustered.common.internal.store.Chain; import org.ehcache.clustered.common.internal.store.Element; -import org.ehcache.core.spi.time.TimeSource; import org.ehcache.spi.loaderwriter.CacheLoaderWriter; import java.nio.ByteBuffer; import java.util.HashMap; -import java.util.Iterator; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeoutException; @@ -40,11 +38,9 @@ class ClusteredWriteBehind { private final CacheLoaderWriter cacheLoaderWriter; private final OperationsCodec codec; private final ChainResolver resolver; - private final TimeSource timeSource; ClusteredWriteBehind(ClusteredWriteBehindStore clusteredWriteBehindStore, ExecutorService executorService, - TimeSource timeSource, ChainResolver resolver, CacheLoaderWriter cacheLoaderWriter, OperationsCodec codec) { @@ -53,7 +49,6 @@ class ClusteredWriteBehind { this.resolver = resolver; this.cacheLoaderWriter = cacheLoaderWriter; this.codec = codec; - this.timeSource = timeSource; } void flushWriteBehindQueue(Chain ignored, long hash) { @@ -69,8 +64,7 @@ void flushWriteBehindQueue(Chain ignored, long hash) { K key = operation.getKey(); PutOperation result = resolver.applyOperation(key, currentState.get(key), - operation, - timeSource.getTimeMillis()); + operation); try { if (result != null) { if (result != currentState.get(key) && !(operation instanceof PutOperation)) { @@ -97,7 +91,7 @@ void flushWriteBehindQueue(Chain ignored, long hash) { clusteredWriteBehindStore.replaceAtHead(hash, chain, builder.build()); } } finally { - clusteredWriteBehindStore.unlock(hash); + clusteredWriteBehindStore.unlock(hash, false); } } catch (TimeoutException e) { throw new RuntimeException(e); diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/writebehind/ClusteredWriteBehindStore.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/writebehind/ClusteredWriteBehindStore.java similarity index 69% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/writebehind/ClusteredWriteBehindStore.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/writebehind/ClusteredWriteBehindStore.java index 152424f760..1f94719e50 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/writebehind/ClusteredWriteBehindStore.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/writebehind/ClusteredWriteBehindStore.java @@ -18,9 +18,8 @@ import org.ehcache.clustered.client.internal.loaderwriter.ClusteredLoaderWriterStore; import org.ehcache.clustered.client.internal.store.ClusteredStore; import org.ehcache.clustered.client.internal.store.ClusteredValueHolder; -import org.ehcache.clustered.client.internal.store.ResolvedChain; import org.ehcache.clustered.client.internal.store.ServerStoreProxy; -import org.ehcache.clustered.client.internal.store.lock.LockManager; +import org.ehcache.clustered.client.internal.store.lock.LockingServerStoreProxy; import org.ehcache.clustered.client.internal.store.operations.ChainResolver; import org.ehcache.clustered.common.internal.store.operations.ConditionalRemoveOperation; import org.ehcache.clustered.common.internal.store.operations.ConditionalReplaceOperation; @@ -29,14 +28,16 @@ import org.ehcache.clustered.common.internal.store.operations.PutWithWriterOperation; import org.ehcache.clustered.common.internal.store.operations.RemoveOperation; import org.ehcache.clustered.common.internal.store.operations.ReplaceOperation; -import org.ehcache.clustered.common.internal.store.operations.Result; import org.ehcache.clustered.common.internal.store.operations.codecs.OperationsCodec; import org.ehcache.clustered.client.service.ClusteringService; import org.ehcache.clustered.common.internal.store.Chain; import org.ehcache.config.ResourceType; +import org.ehcache.core.events.StoreEventDispatcher; +import org.ehcache.core.spi.service.StatisticsService; import org.ehcache.core.spi.store.tiering.AuthoritativeTier; import org.ehcache.core.spi.time.TimeSource; import org.ehcache.core.spi.time.TimeSourceService; +import org.ehcache.impl.store.DefaultStoreEventDispatcher; import org.ehcache.spi.loaderwriter.CacheLoaderWriter; import org.ehcache.spi.loaderwriter.WriteBehindConfiguration; import org.ehcache.spi.resilience.StoreAccessException; @@ -63,23 +64,23 @@ private ClusteredWriteBehindStore(Configuration config, ChainResolver resolver, TimeSource timeSource, CacheLoaderWriter loaderWriter, - ExecutorService executorService) { - super(config, codec, resolver, timeSource); + ExecutorService executorService, + StoreEventDispatcher storeEventDispatcher, StatisticsService statisticsService) { + super(config, codec, resolver, timeSource, storeEventDispatcher, statisticsService); this.cacheLoaderWriter = loaderWriter; this.clusteredWriteBehind = new ClusteredWriteBehind<>(this, executorService, - timeSource, resolver, this.cacheLoaderWriter, codec); } - Chain lock(long hash) throws TimeoutException { - return ((LockManager) storeProxy).lock(hash); + ServerStoreProxy.ChainEntry lock(long hash) throws TimeoutException { + return ((LockingServerStoreProxy) storeProxy).lock(hash); } - void unlock(long hash) throws TimeoutException { - ((LockManager) storeProxy).unlock(hash); + void unlock(long hash, boolean localOnly) throws TimeoutException { + ((LockingServerStoreProxy) storeProxy).unlock(hash, localOnly); } void replaceAtHead(long key, Chain expected, Chain replacement) { @@ -89,21 +90,14 @@ void replaceAtHead(long key, Chain expected, Chain replacement) { @Override protected ValueHolder getInternal(K key) throws StoreAccessException, TimeoutException { try { - Chain chain = storeProxy.get(extractLongKey(key)); + ServerStoreProxy.ChainEntry chain = storeProxy.get(extractLongKey(key)); + /* + * XXX : This condition is wrong... it should be "are there any entries for this key in the chain" + * Most sensible fix I can think of right now would be to push the cacheLoaderWriter access in to the chain + * resolver. + */ if (!chain.isEmpty()) { - ClusteredValueHolder holder = null; - ResolvedChain resolvedChain = resolver.resolve(chain, key, timeSource.getTimeMillis()); - Result resolvedResult = resolvedChain.getResolvedResult(key); - if (resolvedResult != null) { - V value = resolvedResult.getValue(); - long expirationTime = resolvedChain.getExpirationTime(); - if (expirationTime == Long.MAX_VALUE) { - holder = new ClusteredValueHolder<>(value); - } else { - holder = new ClusteredValueHolder<>(value, expirationTime); - } - } - return holder; + return resolver.resolve(chain, key, timeSource.getTimeMillis(), Integer.MAX_VALUE); } else { long hash = extractLongKey(key); lock(hash); @@ -120,7 +114,7 @@ protected ValueHolder getInternal(K key) throws StoreAccessException, Timeout append(key, value); return new ClusteredValueHolder<>(value); } finally { - unlock(hash); + unlock(hash, false); } } } catch (RuntimeException re) { @@ -136,92 +130,90 @@ private void append(K key, V value) throws TimeoutException { } @Override - protected PutStatus silentPut(final K key, final V value) throws StoreAccessException { + protected void silentPut(final K key, final V value) throws StoreAccessException { try { PutWithWriterOperation operation = new PutWithWriterOperation<>(key, value, timeSource.getTimeMillis()); ByteBuffer payload = codec.encode(operation); long extractedKey = extractLongKey(key); storeProxy.append(extractedKey, payload); - return PutStatus.PUT; } catch (Exception re) { throw handleException(re); } } @Override - protected V silentPutIfAbsent(K key, V value) throws StoreAccessException { + protected ValueHolder silentGetAndPut(K key, V value) throws StoreAccessException { try { - PutIfAbsentOperation operation = new PutIfAbsentOperation<>(key, value, timeSource.getTimeMillis()); + PutWithWriterOperation operation = new PutWithWriterOperation<>(key, value, timeSource.getTimeMillis()); ByteBuffer payload = codec.encode(operation); long extractedKey = extractLongKey(key); - Chain chain = storeProxy.getAndAppend(extractedKey, payload); - ResolvedChain resolvedChain = resolver.resolve(chain, key, timeSource.getTimeMillis()); + final ServerStoreProxy.ChainEntry chain = storeProxy.getAndAppend(extractedKey, payload); + return resolver.resolve(chain, key, timeSource.getTimeMillis(), Integer.MAX_VALUE); + } catch (Exception re) { + throw handleException(re); + } + } - Result result = resolvedChain.getResolvedResult(key); - return result == null ? null : result.getValue(); + @Override + protected ValueHolder silentPutIfAbsent(K key, V value) throws StoreAccessException { + try { + PutIfAbsentOperation operation = new PutIfAbsentOperation<>(key, value, timeSource.getTimeMillis()); + ByteBuffer payload = codec.encode(operation); + long extractedKey = extractLongKey(key); + ServerStoreProxy.ChainEntry chain = storeProxy.getAndAppend(extractedKey, payload); + return resolver.resolve(chain, key, timeSource.getTimeMillis(), Integer.MAX_VALUE); } catch (Exception re) { throw handleException(re); } } @Override - protected boolean silentRemove(K key) throws StoreAccessException { + protected ValueHolder silentRemove(K key) throws StoreAccessException { try { RemoveOperation operation = new RemoveOperation<>(key, timeSource.getTimeMillis()); ByteBuffer payload = codec.encode(operation); long extractedKey = extractLongKey(key); - Chain chain = storeProxy.getAndAppend(extractedKey, payload); - ResolvedChain resolvedChain = resolver.resolve(chain, key, timeSource.getTimeMillis()); - - return resolvedChain.getResolvedResult(key) != null; + ServerStoreProxy.ChainEntry chain = storeProxy.getAndAppend(extractedKey, payload); + return resolver.resolve(chain, key, timeSource.getTimeMillis(), Integer.MAX_VALUE); } catch (Exception re) { throw handleException(re); } } @Override - protected V silentRemove(K key, V value) throws StoreAccessException { + protected ValueHolder silentRemove(K key, V value) throws StoreAccessException { try { ConditionalRemoveOperation operation = new ConditionalRemoveOperation<>(key, value, timeSource.getTimeMillis()); ByteBuffer payload = codec.encode(operation); long extractedKey = extractLongKey(key); - Chain chain = storeProxy.getAndAppend(extractedKey, payload); - ResolvedChain resolvedChain = resolver.resolve(chain, key, timeSource.getTimeMillis()); - - Result result = resolvedChain.getResolvedResult(key); - return result == null ? null : result.getValue(); + ServerStoreProxy.ChainEntry chain = storeProxy.getAndAppend(extractedKey, payload); + return resolver.resolve(chain, key, timeSource.getTimeMillis(), Integer.MAX_VALUE); } catch (Exception re) { throw handleException(re); } } @Override - protected V silentReplace(K key, V value) throws StoreAccessException { + protected ValueHolder silentReplace(K key, V value) throws StoreAccessException { try { ReplaceOperation operation = new ReplaceOperation<>(key, value, timeSource.getTimeMillis()); ByteBuffer payload = codec.encode(operation); long extractedKey = extractLongKey(key); - Chain chain = storeProxy.getAndAppend(extractedKey, payload); - ResolvedChain resolvedChain = resolver.resolve(chain, key, timeSource.getTimeMillis()); - - Result result = resolvedChain.getResolvedResult(key); - return result == null ? null : result.getValue(); + ServerStoreProxy.ChainEntry chain = storeProxy.getAndAppend(extractedKey, payload); + return resolver.resolve(chain, key, timeSource.getTimeMillis(), Integer.MAX_VALUE); } catch (Exception re) { throw handleException(re); } } - protected V silentReplace(K key, V oldValue, V newValue) throws StoreAccessException { + protected ValueHolder silentReplace(K key, V oldValue, V newValue) throws StoreAccessException { try { ConditionalReplaceOperation operation = new ConditionalReplaceOperation<>(key, oldValue, newValue, timeSource .getTimeMillis()); ByteBuffer payload = codec.encode(operation); long extractedKey = extractLongKey(key); - Chain chain = storeProxy.getAndAppend(extractedKey, payload); - ResolvedChain resolvedChain = resolver.resolve(chain, key, timeSource.getTimeMillis()); - - Result result = resolvedChain.getResolvedResult(key); - return result == null ? null : result.getValue(); + ServerStoreProxy.ChainEntry chain = storeProxy.getAndAppend(extractedKey, payload); + return resolver.resolve(chain, key, timeSource.getTimeMillis(), Integer.MAX_VALUE); } catch (Exception re) { throw handleException(re); } @@ -236,8 +228,8 @@ public class WriteBehindServerCallback implements ServerStoreProxy.ServerCallbac } @Override - public void onInvalidateHash(long hash) { - this.delegate.onInvalidateHash(hash); + public void onInvalidateHash(long hash, Chain evictedChain) { + this.delegate.onInvalidateHash(hash, evictedChain); } @Override @@ -246,14 +238,18 @@ public void onInvalidateAll() { } @Override - public Chain compact(Chain chain) { - return this.delegate.compact(chain); + public void onAppend(Chain beforeAppend, ByteBuffer appended) { + this.delegate.onAppend(beforeAppend, appended); + } + + @Override + public void compact(ServerStoreProxy.ChainEntry chain) { + this.delegate.compact(chain); } @Override - public Chain compact(Chain chain, long hash) { + public void compact(ServerStoreProxy.ChainEntry chain, long hash) { clusteredWriteBehind.flushWriteBehindQueue(chain, hash); - return null; } } @@ -273,23 +269,25 @@ protected ClusteredStore createStore(Configuration storeConfi TimeSource timeSource, boolean useLoaderInAtomics, Object[] serviceConfigs) { - WriteBehindConfiguration writeBehindConfiguration = findSingletonAmongst(WriteBehindConfiguration.class, serviceConfigs); + WriteBehindConfiguration writeBehindConfiguration = findSingletonAmongst(WriteBehindConfiguration.class, serviceConfigs); if (writeBehindConfiguration != null) { ExecutorService executorService = executionService.getOrderedExecutor(writeBehindConfiguration.getThreadPoolAlias(), new LinkedBlockingQueue<>()); + StoreEventDispatcher storeEventDispatcher = new DefaultStoreEventDispatcher<>(storeConfig.getDispatcherConcurrency()); return new ClusteredWriteBehindStore<>(storeConfig, codec, resolver, timeSource, storeConfig.getCacheLoaderWriter(), - executorService); + executorService, + storeEventDispatcher, getServiceProvider().getService(StatisticsService.class)); } throw new AssertionError(); } @Override - protected ServerStoreProxy.ServerCallback getServerCallback(ClusteredStore clusteredStore) { + protected ServerStoreProxy.ServerCallback getServerCallback(ClusteredStore clusteredStore) { if (clusteredStore instanceof ClusteredWriteBehindStore) { return ((ClusteredWriteBehindStore)clusteredStore).getWriteBehindServerCallback(super.getServerCallback(clusteredStore)); } @@ -297,7 +295,7 @@ protected ServerStoreProxy.ServerCallback getServerCallback(ClusteredStore } @Override - public int rank(Set> resourceTypes, Collection> serviceConfigs) { + public int rank(Set> resourceTypes, Collection> serviceConfigs) { int parentRank = super.rank(resourceTypes, serviceConfigs); if (parentRank == 0 || serviceConfigs.stream().noneMatch(WriteBehindConfiguration.class::isInstance)) { return 0; @@ -306,7 +304,7 @@ public int rank(Set> resourceTypes, Collection authorityResource, Collection> serviceConfigs) { + public int rankAuthority(ResourceType authorityResource, Collection> serviceConfigs) { int parentRank = super.rankAuthority(authorityResource, serviceConfigs); if (parentRank == 0 || serviceConfigs.stream().noneMatch(WriteBehindConfiguration.class::isInstance)) { return 0; diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/writebehind/ClusteredWriteBehindStoreProviderFactory.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/writebehind/ClusteredWriteBehindStoreProviderFactory.java similarity index 88% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/writebehind/ClusteredWriteBehindStoreProviderFactory.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/writebehind/ClusteredWriteBehindStoreProviderFactory.java index a1bec31337..434468a148 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/writebehind/ClusteredWriteBehindStoreProviderFactory.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/loaderwriter/writebehind/ClusteredWriteBehindStoreProviderFactory.java @@ -15,14 +15,15 @@ */ package org.ehcache.clustered.client.internal.loaderwriter.writebehind; -import org.ehcache.clustered.client.internal.loaderwriter.ClusteredLoaderWriterStore; import org.ehcache.core.spi.service.ServiceFactory; import org.ehcache.spi.service.ServiceCreationConfiguration; +import org.osgi.service.component.annotations.Component; +@Component public class ClusteredWriteBehindStoreProviderFactory implements ServiceFactory { @Override - public ClusteredWriteBehindStore.Provider create(ServiceCreationConfiguration configuration) { + public ClusteredWriteBehindStore.Provider create(ServiceCreationConfiguration configuration) { return new ClusteredWriteBehindStore.Provider(); } diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/lock/VoltronReadWriteLock.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/lock/VoltronReadWriteLock.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/lock/VoltronReadWriteLock.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/lock/VoltronReadWriteLock.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/lock/VoltronReadWriteLockClient.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/lock/VoltronReadWriteLockClient.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/lock/VoltronReadWriteLockClient.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/lock/VoltronReadWriteLockClient.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/lock/VoltronReadWriteLockEntityClientService.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/lock/VoltronReadWriteLockEntityClientService.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/lock/VoltronReadWriteLockEntityClientService.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/lock/VoltronReadWriteLockEntityClientService.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/AbstractClientEntityFactory.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/AbstractClientEntityFactory.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/AbstractClientEntityFactory.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/AbstractClientEntityFactory.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/ClusterStateRepository.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/ClusterStateRepository.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/ClusterStateRepository.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/ClusterStateRepository.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/ClusterTierCreationException.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/ClusterTierCreationException.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/ClusterTierCreationException.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/ClusterTierCreationException.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/ClusterTierDestructionException.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/ClusterTierDestructionException.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/ClusterTierDestructionException.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/ClusterTierDestructionException.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/ClusterTierException.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/ClusterTierException.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/ClusterTierException.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/ClusterTierException.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/ClusterTierManagerConfigurationException.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/ClusterTierManagerConfigurationException.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/ClusterTierManagerConfigurationException.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/ClusterTierManagerConfigurationException.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/ClusterTierReleaseException.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/ClusterTierReleaseException.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/ClusterTierReleaseException.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/ClusterTierReleaseException.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/ClusterTierValidationException.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/ClusterTierValidationException.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/ClusterTierValidationException.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/ClusterTierValidationException.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/ClusteredMapException.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/ClusteredMapException.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/ClusteredMapException.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/ClusteredMapException.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/ClusteredStateHolder.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/ClusteredStateHolder.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/ClusteredStateHolder.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/ClusteredStateHolder.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/ClusteringServiceFactory.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/ClusteringServiceFactory.java similarity index 85% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/ClusteringServiceFactory.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/ClusteringServiceFactory.java index d835ae6407..c02c4bdc99 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/ClusteringServiceFactory.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/ClusteringServiceFactory.java @@ -17,26 +17,27 @@ package org.ehcache.clustered.client.internal.service; import org.ehcache.clustered.client.config.ClusteringServiceConfiguration; -import org.ehcache.clustered.client.internal.service.DefaultClusteringService; import org.ehcache.clustered.client.service.ClusteringService; import org.ehcache.core.spi.service.ServiceFactory; import org.ehcache.spi.service.ServiceCreationConfiguration; +import org.osgi.service.component.annotations.Component; /** * A factory for creating a {@link ClusteringService} instance. * * @author Clifford W. Johnson */ +@Component @ServiceFactory.RequiresConfiguration public class ClusteringServiceFactory implements ServiceFactory { @Override - public ClusteringService create(final ServiceCreationConfiguration configuration) { + public ClusteringService create(final ServiceCreationConfiguration configuration) { return new DefaultClusteringService((ClusteringServiceConfiguration) configuration); } @Override - public Class getServiceType() { - return ClusteringService.class; + public Class getServiceType() { + return DefaultClusteringService.class; } } diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/ConnectionState.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/ConnectionState.java similarity index 79% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/ConnectionState.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/ConnectionState.java index 6f3f8edec8..d6d92a04f4 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/ConnectionState.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/ConnectionState.java @@ -17,12 +17,14 @@ import org.ehcache.CachePersistenceException; import org.ehcache.clustered.client.config.ClusteringServiceConfiguration; +import org.ehcache.clustered.client.config.ClusteringServiceConfiguration.ClientMode; import org.ehcache.clustered.client.config.Timeouts; import org.ehcache.clustered.client.internal.ClusterTierManagerClientEntity; import org.ehcache.clustered.client.internal.ClusterTierManagerClientEntityFactory; import org.ehcache.clustered.client.internal.ClusterTierManagerCreationException; import org.ehcache.clustered.client.internal.ClusterTierManagerValidationException; import org.ehcache.clustered.client.internal.ConnectionSource; +import org.ehcache.clustered.client.internal.PerpetualCachePersistenceException; import org.ehcache.clustered.client.internal.store.ClusterTierClientEntity; import org.ehcache.clustered.client.service.EntityBusyException; import org.ehcache.clustered.common.internal.ServerStoreConfiguration; @@ -42,15 +44,20 @@ import java.util.Random; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Executor; +import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; +import static java.util.Objects.requireNonNull; + class ConnectionState { private static final Logger LOGGER = LoggerFactory.getLogger(ConnectionState.class); private static final String CONNECTION_PREFIX = "Ehcache:"; + private volatile Executor asyncWorker; private volatile Connection clusterConnection = null; private volatile ClusterTierManagerClientEntityFactory entityFactory = null; private volatile ClusterTierManagerClientEntity entity = null; @@ -91,25 +98,25 @@ public ClusterTierManagerClientEntityFactory getEntityFactory() { return entityFactory; } + public ClusterTierManagerClientEntity getEntity() { + return entity; + } + public ClusterTierClientEntity createClusterTierClientEntity(String cacheId, ServerStoreConfiguration clientStoreConfiguration, boolean isReconnect) throws CachePersistenceException { ClusterTierClientEntity storeClientEntity; while (true) { try { - if (isReconnect) { - storeClientEntity = entityFactory.getClusterTierClientEntity(entityIdentifier, cacheId); - } else { - storeClientEntity = entityFactory.fetchOrCreateClusteredStoreEntity(entityIdentifier, cacheId, - clientStoreConfiguration, serviceConfiguration.isAutoCreate()); - } + storeClientEntity = entityFactory.fetchOrCreateClusteredStoreEntity(entityIdentifier, cacheId, + clientStoreConfiguration, serviceConfiguration.getClientMode(), isReconnect); clusterTierEntities.put(cacheId, storeClientEntity); break; } catch (EntityNotFoundException e) { - throw new CachePersistenceException("Cluster tier proxy '" + cacheId + "' for entity '" + entityIdentifier + "' does not exist.", e); + throw new PerpetualCachePersistenceException("Cluster tier proxy '" + cacheId + "' for entity '" + entityIdentifier + "' does not exist.", e); } catch (ConnectionClosedException | ConnectionShutdownException e) { LOGGER.info("Disconnected from the server", e); - handleConnectionClosedException(); + handleConnectionClosedException(true); } } @@ -120,10 +127,11 @@ public void removeClusterTierClientEntity(String cacheId) { clusterTierEntities.remove(cacheId); } - public void initClusterConnection() { + public void initClusterConnection(Executor asyncWorker) { + this.asyncWorker = requireNonNull(asyncWorker); try { connect(); - } catch (ConnectionException ex) { + } catch (ConnectionClosedException | ConnectionException ex) { LOGGER.error("Initial connection failed due to", ex); throw new RuntimeException(ex); } @@ -132,10 +140,16 @@ public void initClusterConnection() { private void reconnect() { while (true) { try { + try { + //Ensure full closure of existing connection + clusterConnection.close(); + } catch (IOException | ConnectionClosedException | IllegalStateException e) { + LOGGER.debug("Exception closing previous cluster connection", e); + } connect(); LOGGER.info("New connection to server is established, reconnect count is {}", reconnectCounter.incrementAndGet()); break; - } catch (ConnectionException e) { + } catch (ConnectionClosedException | ConnectionException e) { LOGGER.error("Re-connection to server failed, trying again", e); } } @@ -143,7 +157,7 @@ private void reconnect() { private void connect() throws ConnectionException { clusterConnection = connectionSource.connect(connectionProperties); - entityFactory = new ClusterTierManagerClientEntityFactory(clusterConnection, timeouts); + entityFactory = new ClusterTierManagerClientEntityFactory(clusterConnection, asyncWorker, timeouts); } public void closeConnection() { @@ -158,11 +172,22 @@ public void closeConnection() { } } + private boolean silentDestroyUtil() { + try { + silentDestroy(); + return true; + } catch (ConnectionClosedException | ConnectionShutdownException e) { + LOGGER.info("Disconnected from the server", e); + reconnect(); + return false; + } + } + private void silentDestroy() { LOGGER.debug("Found a broken ClusterTierManager - trying to clean it up"); try { // Random sleep to enable racing clients to have a window to do the cleanup - Thread.sleep(new Random().nextInt(1000)); + Thread.sleep(ThreadLocalRandom.current().nextInt(1000)); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } @@ -182,21 +207,28 @@ public void acquireLeadership() { } } - public void initializeState() { + public void initializeState() throws ClusterTierManagerValidationException { try { - if (serviceConfiguration.isAutoCreate()) { - autoCreateEntity(); - } else { - retrieveEntity(); + switch (serviceConfiguration.getClientMode()) { + case CONNECT: + case EXPECTING: + retrieveEntity(); + break; + case AUTO_CREATE: + case AUTO_CREATE_ON_RECONNECT: + autoCreateEntity(); + break; + default: + throw new AssertionError(serviceConfiguration.getClientMode()); } - } catch (RuntimeException e) { + } catch (Throwable t) { entityFactory = null; closeConnection(); - throw e; + throw t; } } - private void retrieveEntity() { + private void retrieveEntity() throws ClusterTierManagerValidationException { try { entity = entityFactory.retrieve(entityIdentifier, serviceConfiguration.getServerConfiguration()); } catch (DestroyInProgressException | EntityNotFoundException e) { @@ -209,9 +241,9 @@ private void retrieveEntity() { } public void destroyState(boolean healthyConnection) { - if (entityFactory != null && healthyConnection) { + if (entityFactory != null) { // proactively abandon any acquired read or write locks on a healthy connection - entityFactory.abandonAllHolds(entityIdentifier); + entityFactory.abandonAllHolds(entityIdentifier, healthyConnection); } entityFactory = null; @@ -229,7 +261,7 @@ public void destroyAll() throws CachePersistenceException { } catch (EntityBusyException e) { throw new CachePersistenceException("Cannot delete cluster tiers on " + connectionSource, e); } catch (ConnectionClosedException | ConnectionShutdownException e) { - handleConnectionClosedException(); + handleConnectionClosedException(false); } } } @@ -247,9 +279,10 @@ public void destroy(String name) throws CachePersistenceException { throw new CachePersistenceException("Could not connect to the cluster tier manager '" + entityIdentifier + "'; retrieve operation timed out", e); } catch (DestroyInProgressException e) { - silentDestroy(); - // Nothing left to do - break; + if (silentDestroyUtil()) { + // Nothing left to do + break; + } } catch (ConnectionClosedException | ConnectionShutdownException e) { reconnect(); } @@ -265,7 +298,7 @@ public void destroy(String name) throws CachePersistenceException { LOGGER.debug("Destruction of cluster tier {} failed as it does not exist", name); break; } catch (ConnectionClosedException | ConnectionShutdownException e) { - handleConnectionClosedException(); + handleConnectionClosedException(false); } } } @@ -288,7 +321,7 @@ private void autoCreateEntity() throws ClusterTierManagerValidationException, Il entity = entityFactory.retrieve(entityIdentifier, serviceConfiguration.getServerConfiguration()); break; } catch (DestroyInProgressException e) { - silentDestroy(); + silentDestroyUtil(); } catch (EntityNotFoundException e) { //ignore - loop and try to create } catch (TimeoutException e) { @@ -302,12 +335,18 @@ private void autoCreateEntity() throws ClusterTierManagerValidationException, Il } - private void handleConnectionClosedException() { + private void handleConnectionClosedException(boolean retrieve) throws ClusterTierManagerValidationException { while (true) { try { destroyState(false); reconnect(); - retrieveEntity(); + if (retrieve) { + if (serviceConfiguration.getClientMode().equals(ClientMode.AUTO_CREATE_ON_RECONNECT)) { + autoCreateEntity(); + } else { + retrieveEntity(); + } + } connectionRecoveryListener.run(); break; } catch (ConnectionClosedException | ConnectionShutdownException e) { diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/DefaultClusteringService.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/DefaultClusteringService.java similarity index 80% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/DefaultClusteringService.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/DefaultClusteringService.java index 2892a19998..eeff2f39c5 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/DefaultClusteringService.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/DefaultClusteringService.java @@ -17,6 +17,8 @@ package org.ehcache.clustered.client.internal.service; import org.ehcache.CachePersistenceException; +import org.ehcache.clustered.client.internal.ClusterTierManagerValidationException; +import org.ehcache.clustered.client.internal.PerpetualCachePersistenceException; import org.ehcache.clustered.client.config.ClusteredResourcePool; import org.ehcache.clustered.client.config.ClusteredResourceType; import org.ehcache.clustered.client.config.ClusteringServiceConfiguration; @@ -27,8 +29,7 @@ import org.ehcache.clustered.client.internal.store.ServerStoreProxy.ServerCallback; import org.ehcache.clustered.client.internal.store.StrongServerStoreProxy; import org.ehcache.clustered.client.internal.store.lock.LockManager; -import org.ehcache.clustered.client.internal.store.lock.LockManagerImpl; -import org.ehcache.clustered.client.internal.store.lock.LockingServerStoreProxy; +import org.ehcache.clustered.client.internal.store.lock.LockingServerStoreProxyImpl; import org.ehcache.clustered.client.service.ClientEntityFactory; import org.ehcache.clustered.client.service.ClusteringService; import org.ehcache.clustered.client.service.EntityService; @@ -37,7 +38,6 @@ import org.ehcache.config.CacheConfiguration; import org.ehcache.config.ResourceType; import org.ehcache.core.spi.store.Store; -import org.ehcache.spi.loaderwriter.WriteBehindProvider; import org.ehcache.spi.persistence.StateRepository; import org.ehcache.spi.service.MaintainableService; import org.ehcache.spi.service.Service; @@ -53,13 +53,15 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.TimeoutException; import java.util.stream.Stream; /** * Provides support for accessing server-based cluster services. */ -class DefaultClusteringService implements ClusteringService, EntityService { +public class DefaultClusteringService implements ClusteringService, EntityService { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultClusteringService.class); @@ -73,6 +75,7 @@ class DefaultClusteringService implements ClusteringService, EntityService { private final Collection connectionRecoveryListeners = new CopyOnWriteArrayList<>(); private volatile boolean inMaintenance = false; + private ExecutorService asyncExecutor; DefaultClusteringService(ClusteringServiceConfiguration configuration) { this.configuration = configuration; @@ -116,13 +119,19 @@ public boolean isConnected() { @Override public void start(final ServiceProvider serviceProvider) { - connectionState.initClusterConnection(); - connectionState.initializeState(); + try { + asyncExecutor = createAsyncWorker(); + connectionState.initClusterConnection(asyncExecutor); + connectionState.initializeState(); + } catch (ClusterTierManagerValidationException e) { + throw new RuntimeException(e); + } } @Override public void startForMaintenance(ServiceProvider serviceProvider, MaintenanceScope maintenanceScope) { - connectionState.initClusterConnection(); + asyncExecutor = createAsyncWorker(); + connectionState.initClusterConnection(asyncExecutor); if(maintenanceScope == MaintenanceScope.CACHE_MANAGER) { connectionState.acquireLeadership(); } @@ -142,6 +151,7 @@ public void stop() { */ connectionState.destroyState(true); inMaintenance = false; + asyncExecutor.shutdown(); connectionState.closeConnection(); } @@ -178,7 +188,7 @@ public PersistenceSpaceIdentifier getPersistenceSpaceIdentifier(String name, public void releasePersistenceSpaceIdentifier(PersistenceSpaceIdentifier identifier) throws CachePersistenceException { ClusteredCacheIdentifier clusterCacheIdentifier = (ClusteredCacheIdentifier) identifier; if (knownPersistenceSpaces.remove(clusterCacheIdentifier.getId()) == null) { - throw new CachePersistenceException("Unknown identifier: " + clusterCacheIdentifier); + throw new PerpetualCachePersistenceException("Unknown identifier: " + clusterCacheIdentifier); } } @@ -187,7 +197,7 @@ public StateRepository getStateRepositoryWithin(PersistenceSpaceIdentifier id ClusteredCacheIdentifier clusterCacheIdentifier = (ClusteredCacheIdentifier) identifier; ClusteredSpace clusteredSpace = knownPersistenceSpaces.get(clusterCacheIdentifier.getId()); if (clusteredSpace == null) { - throw new CachePersistenceException("Clustered space not found for identifier: " + clusterCacheIdentifier); + throw new PerpetualCachePersistenceException("Clustered space not found for identifier: " + clusterCacheIdentifier); } ConcurrentMap stateRepositories = clusteredSpace.stateRepositories; ClusterStateRepository currentRepo = stateRepositories.get(name); @@ -273,20 +283,30 @@ public ServerStoreProxy getServerStoreProxy(ClusteredCacheIdentifier cach } try { - storeClientEntity.validate(clientStoreConfiguration); - } catch (ClusterTierException e) { - serverStoreProxy.close(); - throw new CachePersistenceException("Unable to create cluster tier proxy '" + cacheIdentifier.getId() + "' for entity '" - + configuration.getConnectionSource().getClusterTierManager() + "'", e); - } catch (TimeoutException e) { - serverStoreProxy.close(); - throw new CachePersistenceException("Unable to create cluster tier proxy '" + cacheIdentifier.getId() + "' for entity '" - + configuration.getConnectionSource().getClusterTierManager() + "'; validate operation timed out", e); + try { + storeClientEntity.validate(clientStoreConfiguration); + } catch (ClusterTierValidationException e) { + throw new PerpetualCachePersistenceException("Unable to create cluster tier proxy '" + cacheIdentifier.getId() + "' for entity '" + + configuration.getConnectionSource().getClusterTierManager() + "'", e); + } catch (ClusterTierException e) { + throw new CachePersistenceException("Unable to create cluster tier proxy '" + cacheIdentifier.getId() + "' for entity '" + + configuration.getConnectionSource().getClusterTierManager() + "'", e); + } catch (TimeoutException e) { + throw new CachePersistenceException("Unable to create cluster tier proxy '" + cacheIdentifier.getId() + "' for entity '" + + configuration.getConnectionSource().getClusterTierManager() + "'; validate operation timed out", e); + } + } catch (Throwable t) { + try { + serverStoreProxy.close(); + } catch (Throwable u) { + t.addSuppressed(u); + } + throw t; } if (storeConfig.getCacheLoaderWriter() != null) { - LockManager lockManager = new LockManagerImpl(storeClientEntity); - serverStoreProxy = new LockingServerStoreProxy(serverStoreProxy, lockManager); + LockManager lockManager = new LockManager(storeClientEntity); + serverStoreProxy = new LockingServerStoreProxyImpl(serverStoreProxy, lockManager); } return serverStoreProxy; @@ -340,4 +360,27 @@ private static class ClusteredSpace { } } + // for test purposes + public ConnectionState getConnectionState() { + return connectionState; + } + + private static ExecutorService createAsyncWorker() { + SecurityManager s = System.getSecurityManager(); + ThreadGroup initialGroup = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); + return Executors.newSingleThreadExecutor(r -> { + ThreadGroup group = initialGroup; + while (group != null && group.isDestroyed()) { + ThreadGroup parent = group.getParent(); + if (parent == null) { + break; + } else { + group = parent; + } + } + Thread t = new Thread(group, r, "Async DefaultClusteringService Worker"); + t.setDaemon(true); + return t; + }); + } } diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/ValueCodec.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/ValueCodec.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/ValueCodec.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/ValueCodec.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/ValueCodecFactory.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/ValueCodecFactory.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/ValueCodecFactory.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/service/ValueCodecFactory.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ClusterTierClientEntity.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/ClusterTierClientEntity.java similarity index 95% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ClusterTierClientEntity.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/ClusterTierClientEntity.java index 57f7b40463..9d4516c6d0 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ClusterTierClientEntity.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/ClusterTierClientEntity.java @@ -55,8 +55,10 @@ public interface ClusterTierClientEntity extends Entity { void addReconnectListener(ReconnectListener reconnectListener); + void enableEvents(boolean enable) throws ClusterException, TimeoutException; + interface ResponseListener { - void onResponse(T response); + void onResponse(T response) throws TimeoutException; } interface DisconnectionListener { diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ClusterTierClientEntityService.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/ClusterTierClientEntityService.java similarity index 97% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ClusterTierClientEntityService.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/ClusterTierClientEntityService.java index 3353342ba6..2829d1dd7f 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ClusterTierClientEntityService.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/ClusterTierClientEntityService.java @@ -55,7 +55,7 @@ public ClusterTierEntityConfiguration deserializeConfiguration(byte[] configurat @Override public ClusterTierClientEntity create(EntityClientEndpoint endpoint, ClusterTierUserData userData) { - return new SimpleClusterTierClientEntity(endpoint, userData.getTimeouts(), userData.getStoreIdentifier()); + return new SimpleClusterTierClientEntity(endpoint, userData.getTimeouts(), userData.getStoreIdentifier(), userData.getAsyncWorker()); } @Override diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ClusterTierUserData.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/ClusterTierUserData.java similarity index 84% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ClusterTierUserData.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/ClusterTierUserData.java index c41a598410..cf5e014c2e 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ClusterTierUserData.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/ClusterTierUserData.java @@ -17,6 +17,8 @@ import org.ehcache.clustered.client.config.Timeouts; +import java.util.concurrent.Executor; + /** * ClusterTierUserData * @@ -25,10 +27,12 @@ public class ClusterTierUserData { private final Timeouts timeouts; private final String storeIdentifier; + private final Executor asyncWorker; - public ClusterTierUserData(Timeouts timeouts, String storeIdentifier) { + public ClusterTierUserData(Timeouts timeouts, String storeIdentifier, Executor asyncWorker) { this.timeouts = timeouts; this.storeIdentifier = storeIdentifier; + this.asyncWorker = asyncWorker; } public Timeouts getTimeouts() { @@ -38,4 +42,8 @@ public Timeouts getTimeouts() { public String getStoreIdentifier() { return storeIdentifier; } + + public Executor getAsyncWorker() { + return asyncWorker; + } } diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ClusteredStore.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/ClusteredStore.java similarity index 64% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ClusteredStore.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/ClusteredStore.java index 9808015103..e722a4edcb 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ClusteredStore.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/ClusteredStore.java @@ -19,35 +19,42 @@ import org.ehcache.Cache; import org.ehcache.CachePersistenceException; import org.ehcache.clustered.client.config.ClusteredResourcePool; +import org.ehcache.clustered.client.internal.PerpetualCachePersistenceException; import org.ehcache.clustered.client.config.ClusteredResourceType; import org.ehcache.clustered.client.config.ClusteredStoreConfiguration; import org.ehcache.clustered.client.internal.store.ServerStoreProxy.ServerCallback; import org.ehcache.clustered.client.internal.store.operations.ChainResolver; import org.ehcache.clustered.client.internal.store.operations.EternalChainResolver; import org.ehcache.clustered.client.internal.store.operations.ExpiryChainResolver; +import org.ehcache.clustered.client.service.ClusteringService; +import org.ehcache.clustered.client.service.ClusteringService.ClusteredCacheIdentifier; +import org.ehcache.clustered.common.Consistency; +import org.ehcache.clustered.common.internal.store.Chain; import org.ehcache.clustered.common.internal.store.operations.ConditionalRemoveOperation; import org.ehcache.clustered.common.internal.store.operations.ConditionalReplaceOperation; +import org.ehcache.clustered.common.internal.store.operations.Operation; import org.ehcache.clustered.common.internal.store.operations.PutIfAbsentOperation; import org.ehcache.clustered.common.internal.store.operations.PutOperation; import org.ehcache.clustered.common.internal.store.operations.RemoveOperation; import org.ehcache.clustered.common.internal.store.operations.ReplaceOperation; -import org.ehcache.clustered.common.internal.store.operations.Result; +import org.ehcache.clustered.common.internal.store.operations.TimestampOperation; import org.ehcache.clustered.common.internal.store.operations.codecs.OperationsCodec; -import org.ehcache.clustered.client.service.ClusteringService; -import org.ehcache.clustered.client.service.ClusteringService.ClusteredCacheIdentifier; -import org.ehcache.clustered.common.Consistency; -import org.ehcache.clustered.common.internal.store.Chain; import org.ehcache.config.ResourceType; import org.ehcache.config.builders.ExpiryPolicyBuilder; import org.ehcache.core.CacheConfigurationChangeListener; import org.ehcache.core.Ehcache; import org.ehcache.core.collections.ConcurrentWeakIdentityHashMap; -import org.ehcache.core.events.CacheEventListenerConfiguration; -import org.ehcache.core.events.NullStoreEventDispatcher; +import org.ehcache.core.events.StoreEventDispatcher; +import org.ehcache.core.events.StoreEventSink; import org.ehcache.core.spi.service.ExecutionService; +import org.ehcache.core.spi.service.StatisticsService; import org.ehcache.core.spi.store.Store; +import org.ehcache.core.spi.store.events.StoreEventFilter; +import org.ehcache.core.spi.store.events.StoreEventListener; import org.ehcache.core.spi.store.events.StoreEventSource; -import org.ehcache.impl.internal.store.basic.BaseStore; +import org.ehcache.core.statistics.OperationObserver; +import org.ehcache.core.statistics.OperationStatistic; +import org.ehcache.impl.store.BaseStore; import org.ehcache.spi.resilience.StoreAccessException; import org.ehcache.core.spi.store.tiering.AuthoritativeTier; import org.ehcache.core.spi.time.TimeSource; @@ -57,19 +64,18 @@ import org.ehcache.core.statistics.StoreOperationOutcomes.EvictionOutcome; import org.ehcache.core.statistics.TierOperationOutcomes; import org.ehcache.expiry.ExpiryPolicy; +import org.ehcache.impl.store.DefaultStoreEventDispatcher; import org.ehcache.impl.store.HashUtils; import org.ehcache.spi.persistence.StateRepository; import org.ehcache.spi.serialization.Serializer; import org.ehcache.spi.serialization.StatefulSerializer; +import org.ehcache.spi.service.OptionalServiceDependencies; import org.ehcache.spi.service.Service; import org.ehcache.spi.service.ServiceConfiguration; import org.ehcache.spi.service.ServiceDependencies; import org.ehcache.spi.service.ServiceProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.terracotta.statistics.OperationStatistic; -import org.terracotta.statistics.StatisticsManager; -import org.terracotta.statistics.observer.OperationObserver; import java.nio.ByteBuffer; import java.util.Collection; @@ -78,6 +84,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.LinkedBlockingQueue; @@ -89,6 +96,7 @@ import java.util.function.Function; import java.util.function.Supplier; +import static java.util.Collections.emptyIterator; import static org.ehcache.core.exceptions.StorePassThroughException.handleException; import static org.ehcache.core.spi.service.ServiceUtils.findSingletonAmongst; @@ -105,6 +113,7 @@ public class ClusteredStore extends BaseStore implements Authoritati protected final ChainResolver resolver; protected final TimeSource timeSource; + private final DelegatingStoreEventDispatcher storeEventDispatcher; protected volatile ServerStoreProxy storeProxy; private volatile InvalidationValve invalidationValve; @@ -121,13 +130,14 @@ public class ClusteredStore extends BaseStore implements Authoritati private final OperationObserver getAndFaultObserver; - protected ClusteredStore(Configuration config, OperationsCodec codec, ChainResolver resolver, TimeSource timeSource) { - super(config); + protected ClusteredStore(Configuration config, OperationsCodec codec, ChainResolver resolver, TimeSource timeSource, StoreEventDispatcher storeEventDispatcher, StatisticsService statisticsService) { + super(config, statisticsService); this.chainCompactionLimit = Integer.getInteger(CHAIN_COMPACTION_THRESHOLD_PROP, DEFAULT_CHAIN_COMPACTION_THRESHOLD); this.codec = codec; this.resolver = resolver; this.timeSource = timeSource; + this.storeEventDispatcher = new DelegatingStoreEventDispatcher<>(storeEventDispatcher); this.getObserver = createObserver("get", StoreOperationOutcomes.GetOutcome.class, true); this.putObserver = createObserver("put", StoreOperationOutcomes.PutOutcome.class, true); @@ -143,8 +153,8 @@ protected ClusteredStore(Configuration config, OperationsCodec codec /** * For tests */ - protected ClusteredStore(Configuration config, OperationsCodec codec, EternalChainResolver resolver, ServerStoreProxy proxy, TimeSource timeSource) { - this(config, codec, resolver, timeSource); + protected ClusteredStore(Configuration config, OperationsCodec codec, ChainResolver resolver, ServerStoreProxy proxy, TimeSource timeSource, StoreEventDispatcher storeEventDispatcher, StatisticsService statisticsService) { + this(config, codec, resolver, timeSource, storeEventDispatcher, statisticsService); this.storeProxy = proxy; } @@ -173,32 +183,12 @@ public ValueHolder get(final K key) throws StoreAccessException { } protected ValueHolder getInternal(K key) throws StoreAccessException, TimeoutException { - ClusteredValueHolder holder = null; try { - Chain chain = storeProxy.get(extractLongKey(key)); - if(!chain.isEmpty()) { - ResolvedChain resolvedChain = resolver.resolve(chain, key, timeSource.getTimeMillis()); - - if (resolvedChain.isCompacted()) { - Chain compactedChain = resolvedChain.getCompactedChain(); - storeProxy.replaceAtHead(extractLongKey(key), chain, compactedChain); - } - - Result resolvedResult = resolvedChain.getResolvedResult(key); - if (resolvedResult != null) { - V value = resolvedResult.getValue(); - long expirationTime = resolvedChain.getExpirationTime(); - if (expirationTime == Long.MAX_VALUE) { - holder = new ClusteredValueHolder<>(value); - } else { - holder = new ClusteredValueHolder<>(value, expirationTime); - } - } - } + ServerStoreProxy.ChainEntry entry = storeProxy.get(extractLongKey(key)); + return resolver.resolve(entry, key, timeSource.getTimeMillis()); } catch (RuntimeException re) { throw handleException(re); } - return holder; } protected long extractLongKey(K key) { @@ -217,27 +207,37 @@ public boolean containsKey(final K key) throws StoreAccessException { @Override public PutStatus put(final K key, final V value) throws StoreAccessException { putObserver.begin(); - PutStatus status = silentPut(key, value); - switch (status) { - case PUT: - putObserver.end(StoreOperationOutcomes.PutOutcome.PUT); - break; - case NOOP: - putObserver.end(StoreOperationOutcomes.PutOutcome.NOOP); - break; - default: - throw new AssertionError("Invalid put status: " + status); - } - return status; + silentPut(key, value); + putObserver.end(StoreOperationOutcomes.PutOutcome.PUT); + return PutStatus.PUT; } - protected PutStatus silentPut(final K key, final V value) throws StoreAccessException { + protected void silentPut(final K key, final V value) throws StoreAccessException { try { PutOperation operation = new PutOperation<>(key, value, timeSource.getTimeMillis()); ByteBuffer payload = codec.encode(operation); long extractedKey = extractLongKey(key); storeProxy.append(extractedKey, payload); - return PutStatus.PUT; + } catch (Exception re) { + throw handleException(re); + } + } + + @Override + public ValueHolder getAndPut(K key, V value) throws StoreAccessException { + putObserver.begin(); + ValueHolder oldValue = silentGetAndPut(key, value); + putObserver.end(StoreOperationOutcomes.PutOutcome.PUT); + return oldValue; + } + + protected ValueHolder silentGetAndPut(final K key, final V value) throws StoreAccessException { + try { + PutOperation operation = new PutOperation<>(key, value, timeSource.getTimeMillis()); + ByteBuffer payload = codec.encode(operation); + long extractedKey = extractLongKey(key); + ServerStoreProxy.ChainEntry chain = storeProxy.getAndAppend(extractedKey, payload); + return resolver.resolve(chain, key, timeSource.getTimeMillis()); } catch (Exception re) { throw handleException(re); } @@ -246,31 +246,23 @@ protected PutStatus silentPut(final K key, final V value) throws StoreAccessExce @Override public ValueHolder putIfAbsent(final K key, final V value, Consumer put) throws StoreAccessException { putIfAbsentObserver.begin(); - V result = silentPutIfAbsent(key, value); + ValueHolder result = silentPutIfAbsent(key, value); if(result == null) { putIfAbsentObserver.end(StoreOperationOutcomes.PutIfAbsentOutcome.PUT); return null; } else { putIfAbsentObserver.end(StoreOperationOutcomes.PutIfAbsentOutcome.HIT); - return new ClusteredValueHolder<>(result); + return result; } } - protected V silentPutIfAbsent(K key, V value) throws StoreAccessException { + protected ValueHolder silentPutIfAbsent(K key, V value) throws StoreAccessException { try { PutIfAbsentOperation operation = new PutIfAbsentOperation<>(key, value, timeSource.getTimeMillis()); ByteBuffer payload = codec.encode(operation); long extractedKey = extractLongKey(key); - Chain chain = storeProxy.getAndAppend(extractedKey, payload); - ResolvedChain resolvedChain = resolver.resolve(chain, key, timeSource.getTimeMillis()); - - if (resolvedChain.getCompactionCount() > chainCompactionLimit) { - Chain compactedChain = resolvedChain.getCompactedChain(); - storeProxy.replaceAtHead(extractedKey, chain, compactedChain); - } - - Result result = resolvedChain.getResolvedResult(key); - return result == null ? null : result.getValue(); + ServerStoreProxy.ChainEntry chain = storeProxy.getAndAppend(extractedKey, payload); + return resolver.resolve(chain, key, timeSource.getTimeMillis(), chainCompactionLimit); } catch (Exception re) { throw handleException(re); } @@ -279,7 +271,7 @@ protected V silentPutIfAbsent(K key, V value) throws StoreAccessException { @Override public boolean remove(final K key) throws StoreAccessException { removeObserver.begin(); - if(silentRemove(key)) { + if(silentRemove(key) != null) { removeObserver.end(StoreOperationOutcomes.RemoveOutcome.REMOVED); return true; } else { @@ -288,38 +280,36 @@ public boolean remove(final K key) throws StoreAccessException { } } - protected boolean silentRemove(K key) throws StoreAccessException { + public ValueHolder getAndRemove(K key) throws StoreAccessException { + removeObserver.begin(); + ValueHolder value = silentRemove(key); + if(value != null) { + removeObserver.end(StoreOperationOutcomes.RemoveOutcome.REMOVED); + } else { + removeObserver.end(StoreOperationOutcomes.RemoveOutcome.MISS); + } + return value; + } + + protected ValueHolder silentRemove(final K key) throws StoreAccessException { try { RemoveOperation operation = new RemoveOperation<>(key, timeSource.getTimeMillis()); ByteBuffer payload = codec.encode(operation); long extractedKey = extractLongKey(key); - Chain chain = storeProxy.getAndAppend(extractedKey, payload); - ResolvedChain resolvedChain = resolver.resolve(chain, key, timeSource.getTimeMillis()); - - if(resolvedChain.getResolvedResult(key) != null) { - storeProxy.replaceAtHead(extractedKey, chain, resolvedChain.getCompactedChain()); - return true; - } else { - return false; - } + ServerStoreProxy.ChainEntry chain = storeProxy.getAndAppend(extractedKey, payload); + return resolver.resolve(chain, key, timeSource.getTimeMillis()); } catch (Exception re) { throw handleException(re); } } - protected V silentRemove(K key, V value) throws StoreAccessException { + protected ValueHolder silentRemove(K key, V value) throws StoreAccessException { try { ConditionalRemoveOperation operation = new ConditionalRemoveOperation<>(key, value, timeSource.getTimeMillis()); ByteBuffer payload = codec.encode(operation); long extractedKey = extractLongKey(key); - Chain chain = storeProxy.getAndAppend(extractedKey, payload); - ResolvedChain resolvedChain = resolver.resolve(chain, key, timeSource.getTimeMillis()); - - Result result = resolvedChain.getResolvedResult(key); - if (result != null && value.equals(result.getValue())) { - storeProxy.replaceAtHead(extractedKey, chain, resolvedChain.getCompactedChain()); - } - return result == null ? null : result.getValue(); + ServerStoreProxy.ChainEntry chain = storeProxy.getAndAppend(extractedKey, payload); + return resolver.resolve(chain, key, timeSource.getTimeMillis()); } catch (Exception re) { throw handleException(re); } @@ -328,9 +318,9 @@ protected V silentRemove(K key, V value) throws StoreAccessException { @Override public RemoveStatus remove(final K key, final V value) throws StoreAccessException { conditionalRemoveObserver.begin(); - V result = silentRemove(key, value); + ValueHolder result = silentRemove(key, value); if(result != null) { - if(value.equals(result)) { + if(value.equals(result.get())) { conditionalRemoveObserver.end(StoreOperationOutcomes.ConditionalRemoveOutcome.REMOVED); return RemoveStatus.REMOVED; } else { @@ -347,52 +337,36 @@ public RemoveStatus remove(final K key, final V value) throws StoreAccessExcepti public ValueHolder replace(final K key, final V value) throws StoreAccessException { replaceObserver.begin(); - V result = silentReplace(key, value); + ValueHolder result = silentReplace(key, value); if(result == null) { replaceObserver.end(StoreOperationOutcomes.ReplaceOutcome.MISS); return null; } else { replaceObserver.end(StoreOperationOutcomes.ReplaceOutcome.REPLACED); - return new ClusteredValueHolder<>(result); + return result; } } - protected V silentReplace(K key, V value) throws StoreAccessException { + protected ValueHolder silentReplace(K key, V value) throws StoreAccessException { try { ReplaceOperation operation = new ReplaceOperation<>(key, value, timeSource.getTimeMillis()); ByteBuffer payload = codec.encode(operation); long extractedKey = extractLongKey(key); - Chain chain = storeProxy.getAndAppend(extractedKey, payload); - ResolvedChain resolvedChain = resolver.resolve(chain, key, timeSource.getTimeMillis()); - - if (resolvedChain.getCompactionCount() > chainCompactionLimit) { - Chain compactedChain = resolvedChain.getCompactedChain(); - storeProxy.replaceAtHead(extractedKey, chain, compactedChain); - } - - Result result = resolvedChain.getResolvedResult(key); - return result == null ? null : result.getValue(); + ServerStoreProxy.ChainEntry chain = storeProxy.getAndAppend(extractedKey, payload); + return resolver.resolve(chain, key, timeSource.getTimeMillis(), chainCompactionLimit); } catch (Exception re) { throw handleException(re); } } - protected V silentReplace(K key, V oldValue, V newValue) throws StoreAccessException { + protected ValueHolder silentReplace(K key, V oldValue, V newValue) throws StoreAccessException { try { ConditionalReplaceOperation operation = new ConditionalReplaceOperation<>(key, oldValue, newValue, timeSource .getTimeMillis()); ByteBuffer payload = codec.encode(operation); long extractedKey = extractLongKey(key); - Chain chain = storeProxy.getAndAppend(extractedKey, payload); - ResolvedChain resolvedChain = resolver.resolve(chain, key, timeSource.getTimeMillis()); - - if (resolvedChain.getCompactionCount() > chainCompactionLimit) { - Chain compactedChain = resolvedChain.getCompactedChain(); - storeProxy.replaceAtHead(extractedKey, chain, compactedChain); - } - - Result result = resolvedChain.getResolvedResult(key); - return result == null ? null : result.getValue(); + ServerStoreProxy.ChainEntry chain = storeProxy.getAndAppend(extractedKey, payload); + return resolver.resolve(chain, key, timeSource.getTimeMillis(), chainCompactionLimit); } catch (Exception re) { throw handleException(re); } @@ -401,9 +375,9 @@ protected V silentReplace(K key, V oldValue, V newValue) throws StoreAccessExcep @Override public ReplaceStatus replace(final K key, final V oldValue, final V newValue) throws StoreAccessException { conditionalReplaceObserver.begin(); - V result = silentReplace(key, oldValue, newValue); + ValueHolder result = silentReplace(key, oldValue, newValue); if(result != null) { - if(oldValue.equals(result)) { + if(oldValue.equals(result.get())) { conditionalReplaceObserver.end(StoreOperationOutcomes.ConditionalReplaceOutcome.REPLACED); return ReplaceStatus.HIT; } else { @@ -427,14 +401,80 @@ public void clear() throws StoreAccessException { @Override public StoreEventSource getStoreEventSource() { - // TODO: Is there a StoreEventSource for a ServerStore? - return new NullStoreEventDispatcher<>(); + return storeEventDispatcher; } @Override public Iterator>> iterator() { - // TODO: Make appropriate ServerStoreProxy call - throw new UnsupportedOperationException("Implement me"); + try { + java.util.Iterator> chainIterator = storeProxy.iterator(); + + return new Iterator>>() { + + private java.util.Iterator>> chain = nextChain(); + + @Override + public boolean hasNext() { + return chain.hasNext() || (chain = nextChain()).hasNext(); + } + + @Override + public Cache.Entry> next() { + try { + return chain.next(); + } catch (NoSuchElementException e) { + return (chain = nextChain()).next(); + } + } + + private java.util.Iterator>> nextChain() { + while (chainIterator.hasNext()) { + Map> chainContents = resolver.resolveAll(chainIterator.next().getValue(), timeSource.getTimeMillis()); + if (!chainContents.isEmpty()) { + return chainContents.entrySet().stream().map(entry -> { + K key = entry.getKey(); + + ValueHolder valueHolder = entry.getValue(); + return new Cache.Entry>() { + + @Override + public K getKey() { + return key; + } + + @Override + public ValueHolder getValue() { + return valueHolder; + } + + @Override + public String toString() { + return getKey() + "=" + getValue(); + } + }; + }).iterator(); + } + } + return emptyIterator(); + } + }; + } catch (Exception e) { + return new Iterator>>() { + + private boolean accessed; + + @Override + public boolean hasNext() { + return !accessed; + } + + @Override + public Cache.Entry> next() throws StoreAccessException { + accessed = true; + throw handleException(e); + } + }; + } } @Override @@ -466,16 +506,14 @@ public Map> bulkCompute(final Set keys, final Fun Ehcache.PutAllFunction putAllFunction = (Ehcache.PutAllFunction)remappingFunction; Map entriesToRemap = putAllFunction.getEntriesToRemap(); for(Map.Entry entry: entriesToRemap.entrySet()) { - PutStatus putStatus = silentPut(entry.getKey(), entry.getValue()); - if(putStatus == PutStatus.PUT) { - putAllFunction.getActualPutCount().incrementAndGet(); - valueHolderMap.put(entry.getKey(), new ClusteredValueHolder<>(entry.getValue())); - } + silentPut(entry.getKey(), entry.getValue()); + putAllFunction.getActualPutCount().incrementAndGet(); + valueHolderMap.put(entry.getKey(), new ClusteredValueHolder<>(entry.getValue())); } } else if(remappingFunction instanceof Ehcache.RemoveAllFunction) { Ehcache.RemoveAllFunction removeAllFunction = (Ehcache.RemoveAllFunction)remappingFunction; for (K key : keys) { - boolean removed = silentRemove(key); + boolean removed = silentRemove(key) != null; if(removed) { removeAllFunction.getActualRemoveCount().incrementAndGet(); } @@ -561,6 +599,7 @@ public void setInvalidationValve(InvalidationValve valve) { * Provider of {@link ClusteredStore} instances. */ @ServiceDependencies({TimeSourceService.class, ClusteringService.class}) + @OptionalServiceDependencies("org.ehcache.core.spi.service.StatisticsService") public static class Provider extends BaseStoreProvider implements AuthoritativeTier.Provider { private static final Logger LOGGER = LoggerFactory.getLogger(Provider.class); @@ -572,7 +611,6 @@ public static class Provider extends BaseStoreProvider implements AuthoritativeT CLUSTER_RESOURCES = Collections.unmodifiableSet(resourceTypes); } - private volatile ServiceProvider serviceProvider; private volatile ClusteringService clusteringService; protected volatile ExecutionService executionService; @@ -587,7 +625,7 @@ protected ClusteredResourceType getResourceType() { } @Override - public ClusteredStore createStore(Configuration storeConfig, ServiceConfiguration... serviceConfigs) { + public ClusteredStore createStore(Configuration storeConfig, ServiceConfiguration... serviceConfigs) { ClusteredStore store = createStoreInternal(storeConfig, serviceConfigs); tierOperationStatistics.put(store, new OperationStatistic[] { @@ -601,12 +639,6 @@ public ClusteredStore createStore(Configuration storeConfig, private ClusteredStore createStoreInternal(Configuration storeConfig, Object[] serviceConfigs) { connectLock.lock(); try { - - CacheEventListenerConfiguration eventListenerConfiguration = findSingletonAmongst(CacheEventListenerConfiguration.class, serviceConfigs); - if (eventListenerConfiguration != null) { - throw new IllegalStateException("CacheEventListener is not supported with clustered tiers"); - } - if (clusteringService == null) { throw new IllegalStateException(Provider.class.getCanonicalName() + ".createStore called without ClusteringServiceConfiguration"); } @@ -628,7 +660,7 @@ private ClusteredStore createStoreInternal(Configuration stor } ClusteredCacheIdentifier cacheId = findSingletonAmongst(ClusteredCacheIdentifier.class, serviceConfigs); - TimeSource timeSource = serviceProvider.getService(TimeSourceService.class).getTimeSource(); + TimeSource timeSource = getServiceProvider().getService(TimeSourceService.class).getTimeSource(); OperationsCodec codec = new OperationsCodec<>(storeConfig.getKeySerializer(), storeConfig.getValueSerializer()); @@ -655,7 +687,8 @@ protected ClusteredStore createStore(Configuration storeConfi TimeSource timeSource, boolean useLoaderInAtomics, Object[] serviceConfigs) { - return new ClusteredStore<>(storeConfig, codec, resolver, timeSource); + StoreEventDispatcher storeEventDispatcher = new DefaultStoreEventDispatcher<>(storeConfig.getDispatcherConcurrency()); + return new ClusteredStore<>(storeConfig, codec, resolver, timeSource, storeEventDispatcher, getServiceProvider().getService(StatisticsService.class)); } @Override @@ -667,7 +700,7 @@ public void releaseStore(Store resource) { } ClusteredStore clusteredStore = (ClusteredStore) resource; this.clusteringService.releaseServerStoreProxy(clusteredStore.storeProxy, false); - StatisticsManager.nodeFor(clusteredStore).clean(); + getStatisticsService().ifPresent(s -> s.cleanForNode(clusteredStore)); tierOperationStatistics.remove(clusteredStore); } finally { connectLock.unlock(); @@ -676,6 +709,14 @@ public void releaseStore(Store resource) { @Override public void initStore(Store resource) { + try { + initStoreInternal(resource); + } catch (CachePersistenceException e) { + throw new RuntimeException(e); + } + } + + private void initStoreInternal(Store resource) throws CachePersistenceException { connectLock.lock(); try { StoreConfig storeConfig = createdStores.get(resource); @@ -684,61 +725,86 @@ public void initStore(Store resource) { } ClusteredStore clusteredStore = (ClusteredStore) resource; ClusteredCacheIdentifier cacheIdentifier = storeConfig.getCacheIdentifier(); - try { - ServerStoreProxy storeProxy = clusteringService.getServerStoreProxy(cacheIdentifier, storeConfig.getStoreConfig(), storeConfig.getConsistency(), - getServerCallback(clusteredStore)); - ReconnectingServerStoreProxy reconnectingServerStoreProxy = new ReconnectingServerStoreProxy(storeProxy, () -> { - Runnable reconnectTask = () -> { - connectLock.lock(); + ServerStoreProxy storeProxy = clusteringService.getServerStoreProxy(cacheIdentifier, storeConfig.getStoreConfig(), storeConfig.getConsistency(), + getServerCallback(clusteredStore)); + ReconnectingServerStoreProxy reconnectingServerStoreProxy = new ReconnectingServerStoreProxy(storeProxy, () -> { + Runnable reconnectTask = () -> { + String cacheId = cacheIdentifier.getId(); + connectLock.lock(); + try { try { //TODO: handle race between disconnect event and connection closed exception being thrown // this guy should wait till disconnect event processing is complete. - String cacheId = cacheIdentifier.getId(); LOGGER.info("Cache {} got disconnected from cluster, reconnecting", cacheId); clusteringService.releaseServerStoreProxy(clusteredStore.storeProxy, true); - initStore(clusteredStore); + initStoreInternal(clusteredStore); LOGGER.info("Cache {} got reconnected to cluster", cacheId); - } finally { - connectLock.unlock(); + } catch (PerpetualCachePersistenceException t) { + LOGGER.error("Cache {} failed reconnecting to cluster (failure is perpetual)", cacheId, t); + clusteredStore.setStoreProxy(new FailedReconnectStoreProxy(cacheId, t)); } - }; - CompletableFuture.runAsync(reconnectTask, executionService.getUnorderedExecutor(null, new LinkedBlockingQueue<>())); - }); - clusteredStore.storeProxy = reconnectingServerStoreProxy; - } catch (CachePersistenceException e) { - throw new RuntimeException("Unable to create cluster tier proxy - " + cacheIdentifier, e); - } + } catch (CachePersistenceException e) { + throw new RuntimeException(e); + } finally { + connectLock.unlock(); + } + }; + CompletableFuture.runAsync(reconnectTask, executionService.getUnorderedExecutor(null, new LinkedBlockingQueue<>())); + }); + clusteredStore.setStoreProxy(reconnectingServerStoreProxy); Serializer keySerializer = clusteredStore.codec.getKeySerializer(); if (keySerializer instanceof StatefulSerializer) { - StateRepository stateRepository; - try { - stateRepository = clusteringService.getStateRepositoryWithin(cacheIdentifier, cacheIdentifier.getId() + "-Key"); - } catch (CachePersistenceException e) { - throw new RuntimeException(e); - } + StateRepository stateRepository = clusteringService.getStateRepositoryWithin(cacheIdentifier, cacheIdentifier.getId() + "-Key"); ((StatefulSerializer) keySerializer).init(stateRepository); } Serializer valueSerializer = clusteredStore.codec.getValueSerializer(); if (valueSerializer instanceof StatefulSerializer) { - StateRepository stateRepository; - try { - stateRepository = clusteringService.getStateRepositoryWithin(cacheIdentifier, cacheIdentifier.getId() + "-Value"); - } catch (CachePersistenceException e) { - throw new RuntimeException(e); - } + StateRepository stateRepository = clusteringService.getStateRepositoryWithin(cacheIdentifier, cacheIdentifier.getId() + "-Value"); ((StatefulSerializer) valueSerializer).init(stateRepository); } - } finally { connectLock.unlock(); } } - protected ServerCallback getServerCallback(ClusteredStore clusteredStore) { + protected ServerCallback getServerCallback(ClusteredStore clusteredStore) { return new ServerCallback() { @Override - public void onInvalidateHash(long hash) { + public void onAppend(Chain beforeAppend, ByteBuffer appended) { + StoreEventSink sink = clusteredStore.storeEventDispatcher.eventSink(); + try { + Operation operation = clusteredStore.codec.decode(appended); + K key = operation.getKey(); + + PutOperation resolvedBefore = clusteredStore.resolver.resolve(beforeAppend, key); + PutOperation resolvedNow = clusteredStore.resolver.applyOperation(key, resolvedBefore, + new TimestampOperation<>(key, operation.timeStamp())); + PutOperation resolvedAfter = clusteredStore.resolver.applyOperation(key, resolvedNow, operation); + + /* + * If the old value was expired then we *must* fire expiry before the other event + */ + if (resolvedBefore != null && resolvedNow == null) { + sink.expired(key, resolvedBefore::getValue); + } + + if (resolvedNow == null && resolvedAfter != null) { + sink.created(key, resolvedAfter.getValue()); + } else if (resolvedNow != null && resolvedAfter == null) { + sink.removed(key, resolvedNow::getValue); + } else if (resolvedAfter != resolvedNow) { + sink.updated(key, resolvedNow::getValue, resolvedAfter.getValue()); + } + clusteredStore.storeEventDispatcher.releaseEventSink(sink); + } catch (Exception e) { + clusteredStore.storeEventDispatcher.releaseEventSinkAfterFailure(sink, e); + LOGGER.warn("Error processing server append event", e); + } + } + + @Override + public void onInvalidateHash(long hash, Chain evictedChain) { EvictionOutcome result = EvictionOutcome.SUCCESS; clusteredStore.evictionObserver.begin(); if (clusteredStore.invalidationValve != null) { @@ -751,6 +817,21 @@ public void onInvalidateHash(long hash) { result = EvictionOutcome.FAILURE; } } + if (evictedChain != null) { + StoreEventSink sink = clusteredStore.storeEventDispatcher.eventSink(); + Map> operationMap = clusteredStore.resolver.resolveAll(evictedChain); + long now = clusteredStore.timeSource.getTimeMillis(); + for (Map.Entry> entry : operationMap.entrySet()) { + K key = entry.getKey(); + ValueHolder valueHolder = entry.getValue(); + if (valueHolder.isExpired(now)) { + sink.expired(key, valueHolder); + } else { + sink.evicted(key, valueHolder); + } + } + clusteredStore.storeEventDispatcher.releaseEventSink(sink); + } clusteredStore.evictionObserver.end(result); } @@ -768,14 +849,14 @@ public void onInvalidateAll() { } @Override - public Chain compact(Chain chain) { - return clusteredStore.resolver.applyOperation(chain, clusteredStore.timeSource.getTimeMillis()); + public void compact(ServerStoreProxy.ChainEntry chain) { + clusteredStore.resolver.compact(chain); } }; } @Override - public int rank(final Set> resourceTypes, final Collection> serviceConfigs) { + public int rank(final Set> resourceTypes, final Collection> serviceConfigs) { if (clusteringService == null || resourceTypes.size() > 1 || Collections.disjoint(resourceTypes, CLUSTER_RESOURCES)) { // A ClusteredStore requires a ClusteringService *and* ClusteredResourcePool instances return 0; @@ -784,7 +865,7 @@ public int rank(final Set> resourceTypes, final Collection authorityResource, Collection> serviceConfigs) { + public int rankAuthority(ResourceType authorityResource, Collection> serviceConfigs) { if (clusteringService == null) { return 0; } else { @@ -796,9 +877,9 @@ public int rankAuthority(ResourceType authorityResource, Collection serviceProvider) { connectLock.lock(); try { - this.serviceProvider = serviceProvider; - this.clusteringService = this.serviceProvider.getService(ClusteringService.class); - this.executionService = this.serviceProvider.getService(ExecutionService.class); + super.start(serviceProvider); + this.clusteringService = getServiceProvider().getService(ClusteringService.class); + this.executionService = getServiceProvider().getService(ExecutionService.class); } finally { connectLock.unlock(); } @@ -808,15 +889,15 @@ public void start(final ServiceProvider serviceProvider) { public void stop() { connectLock.lock(); try { - this.serviceProvider = null; createdStores.clear(); } finally { connectLock.unlock(); + super.stop(); } } @Override - public AuthoritativeTier createAuthoritativeTier(Configuration storeConfig, ServiceConfiguration... serviceConfigs) { + public AuthoritativeTier createAuthoritativeTier(Configuration storeConfig, ServiceConfiguration... serviceConfigs) { ClusteredStore authoritativeTier = createStoreInternal(storeConfig, serviceConfigs); tierOperationStatistics.put(authoritativeTier, new OperationStatistic[] { @@ -839,6 +920,13 @@ public void initAuthoritativeTier(AuthoritativeTier resource) { } + private void setStoreProxy(ServerStoreProxy storeProxy) throws CachePersistenceException { + // don't change the order of the following two lines or you'll create a race condition that can + // make the server drop some event notifications during a client reconnection + this.storeEventDispatcher.setStoreProxy(storeProxy); + this.storeProxy = storeProxy; + } + private static class StoreConfig { private final ClusteredCacheIdentifier cacheIdentifier; @@ -863,4 +951,93 @@ public Consistency getConsistency() { return consistency; } } + + static class DelegatingStoreEventDispatcher implements StoreEventDispatcher { + private int listenerCounter; + private ServerStoreProxy storeProxy; // protected by synchronized blocks + private final StoreEventDispatcher delegate; + + DelegatingStoreEventDispatcher(StoreEventDispatcher delegate) { + this.delegate = delegate; + } + + synchronized void setStoreProxy(ServerStoreProxy storeProxy) throws CachePersistenceException { + if (storeProxy != null && listenerCounter > 0) { + try { + storeProxy.enableEvents(true); + } catch (TimeoutException te) { + throw new CachePersistenceException("Error enabling events", te); + } + } + this.storeProxy = storeProxy; + } + + @Override + public StoreEventSink eventSink() { + return delegate.eventSink(); + } + @Override + public void releaseEventSink(StoreEventSink eventSink) { + delegate.releaseEventSink(eventSink); + } + @Override + public void releaseEventSinkAfterFailure(StoreEventSink eventSink, Throwable throwable) { + delegate.releaseEventSinkAfterFailure(eventSink, throwable); + } + @Override + public void reset(StoreEventSink eventSink) { + delegate.reset(eventSink); + } + @Override + public synchronized void addEventListener(StoreEventListener eventListener) { + if (listenerCounter == 0 && storeProxy != null) { + try { + storeProxy.enableEvents(true); + } catch (TimeoutException te) { + throw new RuntimeException("Error enabling events", te); + } + } + if (listenerCounter < Integer.MAX_VALUE) { + listenerCounter++; + } + delegate.addEventListener(eventListener); + } + @Override + public synchronized void removeEventListener(StoreEventListener eventListener) { + if (listenerCounter == 1 && storeProxy != null) { + try { + storeProxy.enableEvents(false); + } catch (TimeoutException te) { + throw new RuntimeException("Error disabling events", te); + } + } + if (listenerCounter > 0) { + listenerCounter--; + } + delegate.removeEventListener(eventListener); + } + @Override + public void addEventFilter(StoreEventFilter eventFilter) { + delegate.addEventFilter(eventFilter); + } + @Override + public void setEventOrdering(boolean ordering) throws IllegalArgumentException { + delegate.setEventOrdering(ordering); + } + + @Override + public void setSynchronous(boolean synchronous) throws IllegalArgumentException { + if (synchronous) { + throw new IllegalArgumentException("Synchronous CacheEventListener is not supported with clustered tiers"); + } else { + delegate.setSynchronous(synchronous); + } + } + + @Override + public boolean isEventOrdering() { + return delegate.isEventOrdering(); + } + } + } diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ClusteredStoreProviderFactory.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/ClusteredStoreProviderFactory.java similarity index 85% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ClusteredStoreProviderFactory.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/ClusteredStoreProviderFactory.java index 2f7733ae4d..a141224543 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ClusteredStoreProviderFactory.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/ClusteredStoreProviderFactory.java @@ -18,18 +18,20 @@ import org.ehcache.core.spi.service.ServiceFactory; import org.ehcache.spi.service.ServiceCreationConfiguration; +import org.osgi.service.component.annotations.Component; /** * Factory to create instances of {@link ClusteredStore.Provider}. */ +@Component public class ClusteredStoreProviderFactory implements ServiceFactory { @Override - public ClusteredStore.Provider create(final ServiceCreationConfiguration configuration) { + public ClusteredStore.Provider create(final ServiceCreationConfiguration configuration) { return new ClusteredStore.Provider(); } @Override - public Class getServiceType() { + public Class getServiceType() { return ClusteredStore.Provider.class; } } diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ClusteredValueHolder.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/ClusteredValueHolder.java similarity index 89% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ClusteredValueHolder.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/ClusteredValueHolder.java index 37359949dc..9b6c21d411 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ClusteredValueHolder.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/ClusteredValueHolder.java @@ -22,8 +22,6 @@ public class ClusteredValueHolder extends AbstractValueHolder { - public static final TimeUnit TIME_UNIT = TimeUnit.MILLISECONDS; - private final V value; public ClusteredValueHolder(V value) { @@ -38,11 +36,6 @@ public ClusteredValueHolder(V value, long expirationTime) { this.value = value; } - @Override - protected TimeUnit nativeTimeUnit() { - return TIME_UNIT; - } - @Override public V get() { return value; diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/CommonServerStoreProxy.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/CommonServerStoreProxy.java similarity index 56% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/CommonServerStoreProxy.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/CommonServerStoreProxy.java index 6537b70f41..81ef2c294c 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/CommonServerStoreProxy.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/CommonServerStoreProxy.java @@ -16,10 +16,13 @@ package org.ehcache.clustered.client.internal.store; +import org.ehcache.clustered.common.internal.exceptions.ClusterException; import org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse; import org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.ClientInvalidateAll; import org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.ClientInvalidateHash; +import org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.ServerAppend; import org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.ServerInvalidateHash; +import org.ehcache.clustered.common.internal.messages.EhcacheOperationMessage; import org.ehcache.clustered.common.internal.messages.EhcacheResponseType; import org.ehcache.clustered.common.internal.messages.ServerStoreOpMessage; import org.ehcache.clustered.common.internal.messages.ServerStoreOpMessage.AppendMessage; @@ -29,12 +32,18 @@ import org.ehcache.clustered.common.internal.messages.ServerStoreOpMessage.GetMessage; import org.ehcache.clustered.common.internal.messages.ServerStoreOpMessage.ReplaceAtHeadMessage; import org.ehcache.clustered.common.internal.store.Chain; +import org.ehcache.clustered.common.internal.store.Element; +import org.ehcache.config.units.MemoryUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.nio.ByteBuffer; +import java.util.Iterator; +import java.util.Map; +import java.util.UUID; import java.util.concurrent.TimeoutException; +import static java.lang.Math.toIntExact; import static java.util.Objects.requireNonNull; /** @@ -42,6 +51,8 @@ */ class CommonServerStoreProxy implements ServerStoreProxy { + private static final int ITERATOR_BATCH_SIZE = toIntExact(MemoryUnit.KB.toBytes(100)); + private static final Logger LOGGER = LoggerFactory.getLogger(CommonServerStoreProxy.class); private final String cacheId; @@ -54,23 +65,28 @@ class CommonServerStoreProxy implements ServerStoreProxy { entity.addDisconnectionListener(invalidation::onInvalidateAll); + entity.addResponseListener(ServerAppend.class, response -> { + LOGGER.debug("CLIENT: on cache {}, server append notification", cacheId); + invalidation.onAppend(response.getBeforeAppend(), response.getAppended()); + }); entity.addResponseListener(ServerInvalidateHash.class, response -> { long key = response.getKey(); - LOGGER.debug("CLIENT: on cache {}, server requesting hash {} to be invalidated", cacheId, key); - invalidation.onInvalidateHash(key); + Chain evictedChain = response.getEvictedChain(); + LOGGER.debug("CLIENT: on cache {}, server requesting hash {} to be invalidated (evicted chain : {})", cacheId, key, evictedChain); + invalidation.onInvalidateHash(key, evictedChain); }); entity.addResponseListener(ClientInvalidateHash.class, response -> { long key = response.getKey(); int invalidationId = response.getInvalidationId(); LOGGER.debug("CLIENT: doing work to invalidate hash {} from cache {} (ID {})", key, cacheId, invalidationId); - invalidation.onInvalidateHash(key); + // evicted chain is always null: ClientInvalidateHash is fired when another client did an append, not when the server evicted + invalidation.onInvalidateHash(key, null); try { LOGGER.debug("CLIENT: ack'ing invalidation of hash {} from cache {} (ID {})", key, cacheId, invalidationId); entity.invokeAndWaitForSend(new ClientInvalidationAck(key, invalidationId), false); - } catch (Exception e) { - //TODO: what should be done here? + } catch (ClusterException e) { LOGGER.error("error acking client invalidation of hash {} on cache {}", key, cacheId, e); } }); @@ -83,17 +99,12 @@ class CommonServerStoreProxy implements ServerStoreProxy { try { LOGGER.debug("CLIENT: ack'ing invalidation of all from cache {} (ID {})", cacheId, invalidationId); entity.invokeAndWaitForSend(new ClientInvalidationAllAck(invalidationId), false); - } catch (Exception e) { - //TODO: what should be done here? + } catch (ClusterException e) { LOGGER.error("error acking client invalidation of all on cache {}", cacheId, e); } }); entity.addResponseListener(EhcacheEntityResponse.ResolveRequest.class, response -> { - Chain incoming = response.getChain(); - Chain compacted = invalidation.compact(incoming, response.getKey()); - if (compacted != null) { - replaceAtHead(response.getKey(), incoming, compacted); - } + invalidation.compact(new SimpleEntry(response.getKey(), response.getChain()), response.getKey()); }); } @@ -113,7 +124,7 @@ public void close() { } @Override - public Chain get(long key) throws TimeoutException { + public ChainEntry get(long key) throws TimeoutException { EhcacheEntityResponse response; try { response = entity.invokeAndWaitForComplete(new GetMessage(key), false); @@ -123,7 +134,7 @@ public Chain get(long key) throws TimeoutException { throw new ServerStoreProxyException(e); } if (response != null && response.getResponseType() == EhcacheResponseType.GET_RESPONSE) { - return ((EhcacheEntityResponse.GetResponse)response).getChain(); + return new SimpleEntry(key, ((EhcacheEntityResponse.GetResponse)response).getChain()); } else { throw new ServerStoreProxyException("Response for get operation was invalid : " + (response != null ? response.getResponseType() : "null message")); @@ -140,7 +151,7 @@ public void append(long key, ByteBuffer payLoad) { } @Override - public Chain getAndAppend(long key, ByteBuffer payLoad) throws TimeoutException { + public ChainEntry getAndAppend(long key, ByteBuffer payLoad) throws TimeoutException { EhcacheEntityResponse response; try { response = entity.invokeAndWaitForRetired(new GetAndAppendMessage(key, payLoad), true); @@ -150,13 +161,22 @@ public Chain getAndAppend(long key, ByteBuffer payLoad) throws TimeoutException throw new ServerStoreProxyException(e); } if (response != null && response.getResponseType() == EhcacheResponseType.GET_RESPONSE) { - return ((EhcacheEntityResponse.GetResponse)response).getChain(); + return new SimpleEntry(key, ((EhcacheEntityResponse.GetResponse)response).getChain()); } else { throw new ServerStoreProxyException("Response for getAndAppend operation was invalid : " + (response != null ? response.getResponseType() : "null message")); } } + @Override + public void enableEvents(boolean enable) { + try { + entity.enableEvents(enable); + } catch (Exception e) { + throw new ServerStoreProxyException(e); + } + } + @Override public void replaceAtHead(long key, Chain expect, Chain update) { // TODO: Optimize this method to just send sequences for expect Chain @@ -177,4 +197,108 @@ public void clear() throws TimeoutException { throw new ServerStoreProxyException(e); } } + + @Override + public Iterator> iterator() throws TimeoutException { + EhcacheEntityResponse.IteratorBatch iteratorBatch = openIterator(); + if (iteratorBatch.isLast()) { + return iteratorBatch.getChains().iterator(); + } else { + UUID iteratorId = iteratorBatch.getIdentity(); + return new Iterator>() { + + private boolean lastBatch = false; + private Iterator> batch = iteratorBatch.getChains().iterator(); + + @Override + public boolean hasNext() { + return !lastBatch || batch.hasNext(); + } + + @Override + public Map.Entry next() { + if (lastBatch || batch.hasNext()) { + return batch.next(); + } else { + try { + EhcacheEntityResponse.IteratorBatch batchResponse = fetchBatch(iteratorId); + batch = batchResponse.getChains().iterator(); + lastBatch = batchResponse.isLast(); + return batch.next(); + } catch (TimeoutException e) { + throw new RuntimeException(e); + } + } + } + + @Override + protected void finalize() throws Throwable { + if (!lastBatch) { + entity.invokeAndWaitForReceive(new ServerStoreOpMessage.IteratorCloseMessage(iteratorId), false); + } + } + }; + } + } + + private EhcacheEntityResponse.IteratorBatch openIterator() throws TimeoutException { + return fetchBatch(new ServerStoreOpMessage.IteratorOpenMessage(ITERATOR_BATCH_SIZE)); + } + + private EhcacheEntityResponse.IteratorBatch fetchBatch(UUID id) throws TimeoutException { + return fetchBatch(new ServerStoreOpMessage.IteratorAdvanceMessage(id, ITERATOR_BATCH_SIZE)); + } + + private EhcacheEntityResponse.IteratorBatch fetchBatch(EhcacheOperationMessage message) throws TimeoutException { + EhcacheEntityResponse response; + try { + response = entity.invokeAndWaitForComplete(message, false); + } catch (TimeoutException e) { + throw e; + } catch (Exception e) { + throw new ServerStoreProxyException(e); + } + if (response != null && response.getResponseType() == EhcacheResponseType.ITERATOR_BATCH) { + return (EhcacheEntityResponse.IteratorBatch) response; + } else { + throw new ServerStoreProxyException("Response for iterator operation was invalid : " + + (response != null ? response.getResponseType() : "null message")); + } + } + + private class SimpleEntry implements ChainEntry { + + private final long key; + private final Chain chain; + + public SimpleEntry(long key, Chain chain) { + this.key = key; + this.chain = chain; + } + + @Override + public void append(ByteBuffer payLoad) throws TimeoutException { + CommonServerStoreProxy.this.append(key, payLoad); + } + + @Override + public void replaceAtHead(Chain equivalent) { + CommonServerStoreProxy.this.replaceAtHead(key, chain, equivalent); + } + + @Override + public boolean isEmpty() { + return chain.isEmpty(); + } + + @Override + public int length() { + return chain.length(); + } + + @Override + public Iterator iterator() { + return chain.iterator(); + } + } } diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/EventualServerStoreProxy.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/EventualServerStoreProxy.java similarity index 78% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/EventualServerStoreProxy.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/EventualServerStoreProxy.java index f9f35ab1f3..f5c46b05e3 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/EventualServerStoreProxy.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/EventualServerStoreProxy.java @@ -18,6 +18,8 @@ import org.ehcache.clustered.common.internal.store.Chain; import java.nio.ByteBuffer; +import java.util.Iterator; +import java.util.Map; import java.util.concurrent.TimeoutException; public class EventualServerStoreProxy implements ServerStoreProxy { @@ -39,7 +41,7 @@ public void close() { } @Override - public Chain get(long key) throws TimeoutException { + public ChainEntry get(long key) throws TimeoutException { return delegate.get(key); } @@ -49,10 +51,15 @@ public void append(final long key, final ByteBuffer payLoad) throws TimeoutExcep } @Override - public Chain getAndAppend(final long key, final ByteBuffer payLoad) throws TimeoutException { + public ChainEntry getAndAppend(final long key, final ByteBuffer payLoad) throws TimeoutException { return delegate.getAndAppend(key, payLoad); } + @Override + public void enableEvents(boolean enable) throws TimeoutException { + delegate.enableEvents(enable); + } + @Override public void replaceAtHead(long key, Chain expect, Chain update) { delegate.replaceAtHead(key, expect, update); @@ -62,4 +69,9 @@ public void replaceAtHead(long key, Chain expect, Chain update) { public void clear() throws TimeoutException { delegate.clear(); } + + @Override + public Iterator> iterator() throws TimeoutException { + return delegate.iterator(); + } } diff --git a/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/FailedReconnectStoreProxy.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/FailedReconnectStoreProxy.java new file mode 100644 index 0000000000..430ef8a71b --- /dev/null +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/FailedReconnectStoreProxy.java @@ -0,0 +1,89 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.clustered.client.internal.store; + +import org.ehcache.clustered.client.internal.store.lock.LockingServerStoreProxy; +import org.ehcache.clustered.common.internal.store.Chain; + +import java.nio.ByteBuffer; +import java.util.Iterator; +import java.util.Map; + +public class FailedReconnectStoreProxy implements LockingServerStoreProxy { + private final Throwable failure; + private final String cacheId; + + public FailedReconnectStoreProxy(String cacheId, Throwable failure) { + this.cacheId = cacheId; + this.failure = failure; + } + + @Override + public ChainEntry get(long key) { + throw new RuntimeException("Cache " + getCacheId() + " failed reconnecting to cluster", failure); + } + + @Override + public void append(long key, ByteBuffer payLoad) { + throw new RuntimeException("Cache " + getCacheId() + " failed reconnecting to cluster", failure); + } + + @Override + public ChainEntry getAndAppend(long key, ByteBuffer payLoad) { + throw new RuntimeException("Cache " + getCacheId() + " failed reconnecting to cluster", failure); + } + + @Override + public void enableEvents(boolean enable) { + //do nothing + } + + @Override + public void replaceAtHead(long key, Chain expect, Chain update) { + throw new RuntimeException("Cache " + getCacheId() + " failed reconnecting to cluster", failure); + } + + @Override + public void clear() { + throw new RuntimeException("Cache " + getCacheId() + " failed reconnecting to cluster", failure); + } + + @Override + public Iterator> iterator() { + throw new RuntimeException("Cache " + getCacheId() + " failed reconnecting to cluster", failure); + } + + @Override + public String getCacheId() { + return cacheId; + } + + @Override + public void close() { + //ignore + } + + @Override + public ChainEntry lock(long hash) { + throw new RuntimeException("Cache " + getCacheId() + " failed reconnecting to cluster", failure); + } + + @Override + public void unlock(long hash, boolean localonly) { + throw new RuntimeException("Cache " + getCacheId() + " failed reconnecting to cluster", failure); + } +} diff --git a/api/src/main/java/org/ehcache/spi/service/ServiceCreationConfiguration.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/InternalClusterTierClientEntity.java similarity index 52% rename from api/src/main/java/org/ehcache/spi/service/ServiceCreationConfiguration.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/InternalClusterTierClientEntity.java index 4db06f3209..6ffd28401c 100644 --- a/api/src/main/java/org/ehcache/spi/service/ServiceCreationConfiguration.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/InternalClusterTierClientEntity.java @@ -14,20 +14,14 @@ * limitations under the License. */ -package org.ehcache.spi.service; +package org.ehcache.clustered.client.internal.store; -/** - * A configuration type used when creating a {@link Service}. - * - * @param the service type this configuration works with - * +/* + * Since this interface has been used historically as the client-side interface that + * identifies a cluster-tier entity it must remain as **the** interface even though + * it is empty. We could remove it and hack up the server entity service to accept + * both variants but this seems like a cleaner and more future proof decision. This + * way if we need to introduce any 'internal' methods we can. */ -public interface ServiceCreationConfiguration { - - /** - * Indicates which service consumes this configuration at creation. - * - * @return the service type - */ - Class getServiceType(); +public interface InternalClusterTierClientEntity extends ClusterTierClientEntity { } diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ReconnectInProgressException.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/ReconnectInProgressException.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ReconnectInProgressException.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/ReconnectInProgressException.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ReconnectingServerStoreProxy.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/ReconnectingServerStoreProxy.java similarity index 59% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ReconnectingServerStoreProxy.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/ReconnectingServerStoreProxy.java index 7bc88d1703..c28a8d55f5 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ReconnectingServerStoreProxy.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/ReconnectingServerStoreProxy.java @@ -15,8 +15,8 @@ */ package org.ehcache.clustered.client.internal.store; -import org.ehcache.clustered.client.internal.store.lock.LockManager; import org.ehcache.clustered.client.internal.store.lock.LockingServerStoreProxy; +import org.ehcache.clustered.client.internal.store.lock.LockingServerStoreProxyImpl; import org.ehcache.clustered.common.internal.store.Chain; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,10 +24,12 @@ import org.terracotta.exception.ConnectionShutdownException; import java.nio.ByteBuffer; +import java.util.Iterator; +import java.util.Map; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; -public class ReconnectingServerStoreProxy implements ServerStoreProxy, LockManager { +public class ReconnectingServerStoreProxy implements LockingServerStoreProxy { private static final Logger LOGGER = LoggerFactory.getLogger(ReconnectingServerStoreProxy.class); @@ -38,7 +40,7 @@ public ReconnectingServerStoreProxy(ServerStoreProxy serverStoreProxy, Runnable if (serverStoreProxy instanceof LockingServerStoreProxy) { this.delegateRef = new AtomicReference<>((LockingServerStoreProxy) serverStoreProxy); } else { - this.delegateRef = new AtomicReference<>(new LockingServerStoreProxy(serverStoreProxy, new UnSupportedLockManager())); + this.delegateRef = new AtomicReference<>(unsupportedLocking(serverStoreProxy)); } this.onReconnect = onReconnect; } @@ -58,7 +60,7 @@ public void close() { } @Override - public Chain get(long key) throws TimeoutException { + public ChainEntry get(long key) throws TimeoutException { return onStoreProxy(serverStoreProxy -> serverStoreProxy.get(key)); } @@ -71,10 +73,18 @@ public void append(long key, ByteBuffer payLoad) throws TimeoutException { } @Override - public Chain getAndAppend(long key, ByteBuffer payLoad) throws TimeoutException { + public ChainEntry getAndAppend(long key, ByteBuffer payLoad) throws TimeoutException { return onStoreProxy(serverStoreProxy -> serverStoreProxy.getAndAppend(key, payLoad)); } + @Override + public void enableEvents(boolean enable) throws TimeoutException { + onStoreProxy(serverStoreProxy -> { + serverStoreProxy.enableEvents(enable); + return null; + }); + } + @Override public void replaceAtHead(long key, Chain expect, Chain update) { try { @@ -95,6 +105,11 @@ public void clear() throws TimeoutException { }); } + @Override + public Iterator> iterator() throws TimeoutException { + return onStoreProxy(LockingServerStoreProxy::iterator); + } + private LockingServerStoreProxy proxy() { return delegateRef.get(); } @@ -116,14 +131,14 @@ private T onStoreProxy(TimeoutExceptionFunction } @Override - public Chain lock(long hash) throws TimeoutException { - return onStoreProxy(lockingServerStoreProxy -> lockingServerStoreProxy.lock(hash)); + public ChainEntry lock(long key) throws TimeoutException { + return onStoreProxy(lockingServerStoreProxy -> lockingServerStoreProxy.lock(key)); } @Override - public void unlock(long hash) throws TimeoutException { + public void unlock(long key, boolean localonly) throws TimeoutException { onStoreProxy(lockingServerStoreProxy -> { - lockingServerStoreProxy.unlock(hash); + lockingServerStoreProxy.unlock(key, localonly); return null; }); } @@ -133,12 +148,11 @@ private interface TimeoutExceptionFunction { V apply(U u) throws TimeoutException; } - private static class ReconnectInProgressProxy extends LockingServerStoreProxy { + private static class ReconnectInProgressProxy implements LockingServerStoreProxy { private final String cacheId; ReconnectInProgressProxy(String cacheId) { - super(null, null); this.cacheId = cacheId; } @@ -153,7 +167,7 @@ public void close() { } @Override - public Chain get(long key) { + public ChainEntry get(long key) { throw new ReconnectInProgressException(); } @@ -163,7 +177,7 @@ public void append(long key, ByteBuffer payLoad) { } @Override - public Chain getAndAppend(long key, ByteBuffer payLoad) { + public ChainEntry getAndAppend(long key, ByteBuffer payLoad) { throw new ReconnectInProgressException(); } @@ -178,26 +192,82 @@ public void clear() { } @Override - public Chain lock(long hash) throws TimeoutException { + public Iterator> iterator() { throw new ReconnectInProgressException(); } @Override - public void unlock(long hash) throws TimeoutException { + public ChainEntry lock(long key) { throw new ReconnectInProgressException(); } - } - - private static class UnSupportedLockManager implements LockManager { @Override - public Chain lock(long hash) throws TimeoutException { - throw new UnsupportedOperationException("Lock ops are not supported"); + public void unlock(long key, boolean localonly) { + throw new ReconnectInProgressException(); } @Override - public void unlock(long hash) throws TimeoutException { - throw new UnsupportedOperationException("Lock ops are not supported"); + public void enableEvents(boolean enable) { + throw new ReconnectInProgressException(); } } + + private LockingServerStoreProxy unsupportedLocking(ServerStoreProxy serverStoreProxy) { + return new LockingServerStoreProxy() { + @Override + public ChainEntry lock(long hash) { + throw new UnsupportedOperationException("Lock ops are not supported"); + } + + @Override + public void unlock(long hash, boolean localonly) { + throw new UnsupportedOperationException("Lock ops are not supported"); + } + + @Override + public ChainEntry get(long key) throws TimeoutException { + return serverStoreProxy.get(key); + } + + @Override + public ChainEntry getAndAppend(long key, ByteBuffer payLoad) throws TimeoutException { + return serverStoreProxy.getAndAppend(key, payLoad); + } + + @Override + public void enableEvents(boolean enable) throws TimeoutException { + serverStoreProxy.enableEvents(enable); + } + + @Override + public String getCacheId() { + return serverStoreProxy.getCacheId(); + } + + @Override + public void close() { + serverStoreProxy.close(); + } + + @Override + public void append(long key, ByteBuffer payLoad) throws TimeoutException { + serverStoreProxy.append(key, payLoad); + } + + @Override + public void replaceAtHead(long key, Chain expect, Chain update) { + serverStoreProxy.replaceAtHead(key, expect, update); + } + + @Override + public void clear() throws TimeoutException { + serverStoreProxy.clear(); + } + + @Override + public Iterator> iterator() throws TimeoutException { + return serverStoreProxy.iterator(); + } + }; + } } diff --git a/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/ServerStoreProxy.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/ServerStoreProxy.java new file mode 100644 index 0000000000..2eacd7a92a --- /dev/null +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/ServerStoreProxy.java @@ -0,0 +1,150 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.clustered.client.internal.store; + +import org.ehcache.clustered.common.internal.store.Chain; +import org.ehcache.clustered.common.internal.store.Element; +import org.ehcache.clustered.common.internal.store.ServerStore; + +import java.nio.ByteBuffer; +import java.util.concurrent.TimeoutException; + +/** + * @author Ludovic Orban + */ +public interface ServerStoreProxy extends ServerStore { + + /** + * {@inheritDoc} + *

+ * {@code ServerStoreProxy} instances return {@link ChainEntry} instances that support mutation of the associated store. + * + * @return the associated chain entry + */ + @Override + ChainEntry get(long key) throws TimeoutException; + + /** + * {@inheritDoc} + *

+ * {@code ServerStoreProxy} instances return {@link ChainEntry} instances that support mutation of the associated store. + * + * @return the associated chain entry + */ + @Override + ChainEntry getAndAppend(long key, ByteBuffer payLoad) throws TimeoutException; + + /** + * The invalidation listener + */ + interface ServerCallback { + /** + * Callback for invalidation of hash requests + * + * @param hash the hash of the keys to invalidate + * @param evictedChain the evicted chain, or null if it wasn't an eviction that triggered the invalidation but + * a change on a different client or when events are disabled. + */ + void onInvalidateHash(long hash, Chain evictedChain); + + /** + * Callback for invalidation of all requests + */ + void onInvalidateAll(); + + /** + * Callback append events + */ + void onAppend(Chain beforeAppend, ByteBuffer appended); + + void compact(ChainEntry chain); + + default void compact(ChainEntry chain, long hash) { + compact(chain); + } + + } + + /** + * Enable or disable event firing from the server + * @param enable {@code true} to enable, {@code false} to disable + */ + void enableEvents(boolean enable) throws TimeoutException; + + + /** + * Gets the identifier linking a client-side cache to a {@code ServerStore} instance. + * + * @return the cache identifier + */ + String getCacheId(); + + /** + * Closes this proxy. + */ + void close(); + + interface ChainEntry extends Chain { + + /** + * Appends the provided binary to this Chain + * While appending, the payLoad is stored in {@link Element}. + * Note that the {@code payLoad}'s position and limit are left untouched. + * + * @param payLoad to be appended + * + * @throws TimeoutException if the append exceeds the timeout configured for write operations + */ + void append(ByteBuffer payLoad) throws TimeoutException; + + + /** + * Replaces the provided Chain with the equivalent Chain present at the head. + * This operation is not guaranteed to succeed. + * The replaceAtHead is successful iff the Chain associated with the key has + * a sub-sequence of elements present as expected.. + * + * If below mapping is present: + * + * hash -> |payLoadA| - |payLoadB| - |payLoadC| + * + * And replaceAtHead(hash, |payLoadA| - |payLoadB| - |payLoadC|, |payLoadC'|) is invoked + * then this operation will succeed & the final mapping would be: + * + * hash -> |payLoadC'| + * + * The same operation will also succeed if the mapping was modified by the time replace was invoked to + * + * hash -> |payLoadA| - |payLoadB| - |payLoadC| - |payLoadD| + * + * Though the final mapping would be: + * + * hash -> |payLoadC'| - |payLoadD| + * + * Failure case: + * + * But before replaceAtHead if it was modified to : + * + * hash -> |payLoadC"| - |payLoadD| + * + * then replaceAtHead(hash, |payLoadA| - |payLoadB| - |payLoadC|, |payLoadC'|) will be ignored. + * Note that the payload's position and limit of all elements of both chains are left untouched. + * + * @param equivalent the new Chain to be replaced + */ + void replaceAtHead(Chain equivalent); + } +} diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ServerStoreProxyException.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/ServerStoreProxyException.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ServerStoreProxyException.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/ServerStoreProxyException.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/SimpleClusterTierClientEntity.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/SimpleClusterTierClientEntity.java similarity index 87% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/SimpleClusterTierClientEntity.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/SimpleClusterTierClientEntity.java index e166f8d3ab..a2aa60dbe4 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/SimpleClusterTierClientEntity.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/SimpleClusterTierClientEntity.java @@ -17,7 +17,6 @@ package org.ehcache.clustered.client.internal.store; import org.ehcache.clustered.client.config.Timeouts; -import org.ehcache.clustered.client.internal.service.ClusterTierException; import org.ehcache.clustered.client.internal.service.ClusterTierValidationException; import org.ehcache.clustered.common.internal.ServerStoreConfiguration; import org.ehcache.clustered.common.internal.exceptions.ClusterException; @@ -30,6 +29,7 @@ import org.ehcache.clustered.common.internal.messages.EhcacheResponseType; import org.ehcache.clustered.common.internal.messages.LifeCycleMessageFactory; import org.ehcache.clustered.common.internal.messages.ReconnectMessageCodec; +import org.ehcache.clustered.common.internal.messages.ServerStoreOpMessage; import org.ehcache.clustered.common.internal.messages.StateRepositoryOpMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -48,9 +48,12 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.Executor; +import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeoutException; import java.util.function.LongSupplier; +import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.NANOSECONDS; import static org.ehcache.clustered.client.config.Timeouts.nanosStartingFromNow; @@ -60,7 +63,11 @@ public class SimpleClusterTierClientEntity implements InternalClusterTierClientEntity { private static final Logger LOGGER = LoggerFactory.getLogger(SimpleClusterTierClientEntity.class); - private static final Set GET_STORE_OPS = EnumSet.of(EhcacheMessageType.GET_STORE); + private static final Set GET_STORE_OPS = EnumSet.of( + EhcacheMessageType.GET_STORE, + EhcacheMessageType.ITERATOR_ADVANCE, + EhcacheMessageType.ITERATOR_OPEN, + EhcacheMessageType.ITERATOR_CLOSE); private final EntityClientEndpoint endpoint; private final LifeCycleMessageFactory messageFactory; @@ -75,12 +82,16 @@ public class SimpleClusterTierClientEntity implements InternalClusterTierClientE private final List reconnectListeners = new CopyOnWriteArrayList<>(); private volatile boolean connected = true; + private volatile boolean eventsEnabled; + + private final Executor asyncWorker; public SimpleClusterTierClientEntity(EntityClientEndpoint endpoint, - Timeouts timeouts, String storeIdentifier) { + Timeouts timeouts, String storeIdentifier, Executor asyncWorker) { this.endpoint = endpoint; this.timeouts = timeouts; this.storeIdentifier = storeIdentifier; + this.asyncWorker = requireNonNull(asyncWorker); this.messageFactory = new LifeCycleMessageFactory(); endpoint.setDelegate(new EndpointDelegate() { @Override @@ -92,7 +103,7 @@ public void handleMessage(EhcacheEntityResponse messageFromServer) { @Override public byte[] createExtendedReconnectData() { synchronized (lock) { - ClusterTierReconnectMessage reconnectMessage = new ClusterTierReconnectMessage(); + ClusterTierReconnectMessage reconnectMessage = new ClusterTierReconnectMessage(eventsEnabled); reconnectListeners.forEach(reconnectListener -> reconnectListener.onHandleReconnect(reconnectMessage)); return reconnectMessageCodec.encode(reconnectMessage); } @@ -120,7 +131,22 @@ private void fireResponseEvent(T response) { } LOGGER.debug("{} registered response listener(s) for {}", responseListeners.size(), response.getClass()); for (ResponseListener responseListener : responseListeners) { - responseListener.onResponse(response); + Runnable responseProcessing = () -> { + try { + responseListener.onResponse(response); + } catch (TimeoutException e) { + LOGGER.debug("Timeout exception processing: {} - resubmitting", response, e); + fireResponseEvent(response); + } catch (Exception e) { + LOGGER.warn("Unhandled failure processing: {}", response, e); + } + }; + try { + asyncWorker.execute(responseProcessing); + } catch (RejectedExecutionException f) { + LOGGER.warn("Response task execution rejected using inline execution: {}", response, f); + responseProcessing.run(); + } } } @@ -141,6 +167,16 @@ public void addReconnectListener(ReconnectListener reconnectListener) { this.reconnectListeners.add(reconnectListener); } + @Override + public void enableEvents(boolean enable) throws ClusterException, TimeoutException { + if (enable == this.eventsEnabled) { + return; + } + // make sure the server received and processed the message before returning + this.invokeAndWaitForComplete(new ServerStoreOpMessage.EnableEventListenerMessage(enable), true); + this.eventsEnabled = enable; + } + @Override public void addDisconnectionListener(DisconnectionListener disconnectionListener) { this.disconnectionListeners.add(disconnectionListener); @@ -162,7 +198,7 @@ public void addResponseListener(Class respo } @Override - public void validate(ServerStoreConfiguration clientStoreConfiguration) throws ClusterTierException, TimeoutException { + public void validate(ServerStoreConfiguration clientStoreConfiguration) throws ClusterTierValidationException, TimeoutException { try { invokeInternalAndWait(endpoint.beginInvoke(), timeouts.getConnectionTimeout(), messageFactory.validateServerStore(storeIdentifier , clientStoreConfiguration), false); } catch (ClusterException e) { diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/StrongServerStoreProxy.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/StrongServerStoreProxy.java similarity index 92% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/StrongServerStoreProxy.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/StrongServerStoreProxy.java index 0d5c7d1d43..571ce56c6f 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/StrongServerStoreProxy.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/StrongServerStoreProxy.java @@ -23,6 +23,8 @@ import java.nio.ByteBuffer; import java.time.Duration; +import java.util.Iterator; +import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; @@ -121,6 +123,9 @@ private T performWaitingForHashInvalidation(long key, Callable c, Duratio if (ex instanceof TimeoutException) { throw (TimeoutException)ex; } + if (ex instanceof ServerStoreProxyException) { + throw (ServerStoreProxyException)ex; + } throw new RuntimeException(ex); } } @@ -156,6 +161,9 @@ private T performWaitingForAllInvalidation(Callable c, Duration timeout) if (ex instanceof TimeoutException) { throw (TimeoutException)ex; } + if (ex instanceof ServerStoreProxyException) { + throw (ServerStoreProxyException)ex; + } throw new RuntimeException(ex); } } @@ -197,7 +205,7 @@ public void close() { } @Override - public Chain get(long key) throws TimeoutException { + public ChainEntry get(long key) throws TimeoutException { return delegate.get(key); } @@ -210,10 +218,15 @@ public void append(final long key, final ByteBuffer payLoad) throws TimeoutExcep } @Override - public Chain getAndAppend(final long key, final ByteBuffer payLoad) throws TimeoutException { + public ChainEntry getAndAppend(final long key, final ByteBuffer payLoad) throws TimeoutException { return performWaitingForHashInvalidation(key, () -> delegate.getAndAppend(key, payLoad), entity.getTimeouts().getWriteOperationTimeout()); } + @Override + public void enableEvents(boolean enable) { + delegate.enableEvents(enable); + } + @Override public void replaceAtHead(long key, Chain expect, Chain update) { delegate.replaceAtHead(key, expect, update); @@ -226,4 +239,9 @@ public void clear() throws TimeoutException { return null; }, entity.getTimeouts().getWriteOperationTimeout()); } + + @Override + public Iterator> iterator() throws TimeoutException { + return delegate.iterator(); + } } diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/lock/LockManagerImpl.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/lock/LockManager.java similarity index 86% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/lock/LockManagerImpl.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/lock/LockManager.java index 397f2fb703..220ac6ee66 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/lock/LockManagerImpl.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/lock/LockManager.java @@ -23,29 +23,28 @@ import org.ehcache.clustered.common.internal.messages.ServerStoreOpMessage.LockMessage; import org.ehcache.clustered.common.internal.messages.ServerStoreOpMessage.UnlockMessage; import org.ehcache.clustered.common.internal.store.Chain; -import org.ehcache.impl.internal.concurrent.ConcurrentHashMap; import java.util.Collections; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeoutException; import static org.ehcache.clustered.common.internal.messages.EhcacheResponseType.LOCK_FAILURE; -public class LockManagerImpl implements LockManager { +public class LockManager { private final ClusterTierClientEntity clientEntity; private final Set locksHeld = Collections.newSetFromMap(new ConcurrentHashMap<>()); - public LockManagerImpl(ClusterTierClientEntity clientEntity) { + public LockManager(ClusterTierClientEntity clientEntity) { this.clientEntity = clientEntity; clientEntity.addReconnectListener(this::reconnectListener); } - private void reconnectListener(ClusterTierReconnectMessage reconnectMessage) { + void reconnectListener(ClusterTierReconnectMessage reconnectMessage) { reconnectMessage.addLocksHeld(locksHeld); } - @Override public Chain lock(long hash) throws TimeoutException { LockSuccess response = getlockResponse(hash); locksHeld.add(hash); @@ -69,10 +68,11 @@ private LockSuccess getlockResponse(long hash) throws TimeoutException { return (LockSuccess) response; } - @Override - public void unlock(long hash) throws TimeoutException { + public void unlock(long hash, boolean localonly) throws TimeoutException { try { - clientEntity.invokeAndWaitForComplete(new UnlockMessage(hash), false); + if (!localonly) { + clientEntity.invokeAndWaitForComplete(new UnlockMessage(hash), false); + } locksHeld.remove(hash); } catch (TimeoutException tme) { throw tme; diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/lock/LockManager.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/lock/LockingServerStoreProxy.java similarity index 73% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/lock/LockManager.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/lock/LockingServerStoreProxy.java index 164ac3009b..588ea8e768 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/lock/LockManager.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/lock/LockingServerStoreProxy.java @@ -15,21 +15,22 @@ */ package org.ehcache.clustered.client.internal.store.lock; -import org.ehcache.clustered.common.internal.store.Chain; +import org.ehcache.clustered.client.internal.store.ServerStoreProxy; import java.util.concurrent.TimeoutException; -public interface LockManager { +public interface LockingServerStoreProxy extends ServerStoreProxy { + /** * * @param hash */ - Chain lock(long hash) throws TimeoutException; + ChainEntry lock(long hash) throws TimeoutException; /** * * @param hash + * @param localonly */ - void unlock(long hash) throws TimeoutException; - + void unlock(long hash, boolean localonly) throws TimeoutException; } diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/lock/LockingServerStoreProxy.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/lock/LockingServerStoreProxyImpl.java similarity index 52% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/lock/LockingServerStoreProxy.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/lock/LockingServerStoreProxyImpl.java index d231aac923..923d4926e3 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/lock/LockingServerStoreProxy.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/lock/LockingServerStoreProxyImpl.java @@ -17,16 +17,19 @@ import org.ehcache.clustered.client.internal.store.ServerStoreProxy; import org.ehcache.clustered.common.internal.store.Chain; +import org.ehcache.clustered.common.internal.store.Element; import java.nio.ByteBuffer; +import java.util.Iterator; +import java.util.Map; import java.util.concurrent.TimeoutException; -public class LockingServerStoreProxy implements ServerStoreProxy, LockManager { +public class LockingServerStoreProxyImpl implements LockingServerStoreProxy { private final ServerStoreProxy storeProxy; private final LockManager lockManager; - public LockingServerStoreProxy(ServerStoreProxy storeProxy, LockManager lockManager) { + public LockingServerStoreProxyImpl(ServerStoreProxy storeProxy, LockManager lockManager) { this.storeProxy = storeProxy; this.lockManager = lockManager; } @@ -42,17 +45,43 @@ public void close() { } @Override - public Chain lock(long hash) throws TimeoutException { - return lockManager.lock(hash); + public ChainEntry lock(long key) throws TimeoutException { + Chain chain = lockManager.lock(key); + return new ChainEntry() { + @Override + public void append(ByteBuffer payLoad) throws TimeoutException { + LockingServerStoreProxyImpl.this.append(key, payLoad); + } + + @Override + public void replaceAtHead(Chain equivalent) { + LockingServerStoreProxyImpl.this.replaceAtHead(key, chain, equivalent); + } + + @Override + public boolean isEmpty() { + return chain.isEmpty(); + } + + @Override + public int length() { + return chain.length(); + } + + @Override + public Iterator iterator() { + return chain.iterator(); + } + }; } @Override - public void unlock(long hash) throws TimeoutException { - lockManager.unlock(hash); + public void unlock(long key, boolean localonly) throws TimeoutException { + lockManager.unlock(key, localonly); } @Override - public Chain get(long key) throws TimeoutException { + public ChainEntry get(long key) throws TimeoutException { return storeProxy.get(key); } @@ -62,10 +91,15 @@ public void append(long key, ByteBuffer payLoad) throws TimeoutException { } @Override - public Chain getAndAppend(long key, ByteBuffer payLoad) throws TimeoutException { + public ChainEntry getAndAppend(long key, ByteBuffer payLoad) throws TimeoutException { return storeProxy.getAndAppend(key, payLoad); } + @Override + public void enableEvents(boolean enable) throws TimeoutException { + storeProxy.enableEvents(enable); + } + @Override public void replaceAtHead(long key, Chain expect, Chain update) { storeProxy.replaceAtHead(key, expect, update); @@ -75,4 +109,9 @@ public void replaceAtHead(long key, Chain expect, Chain update) { public void clear() throws TimeoutException { storeProxy.clear(); } + + @Override + public Iterator> iterator() throws TimeoutException { + return storeProxy.iterator(); + } } diff --git a/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/operations/ChainResolver.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/operations/ChainResolver.java new file mode 100644 index 0000000000..a5245c5507 --- /dev/null +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/operations/ChainResolver.java @@ -0,0 +1,184 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.clustered.client.internal.store.operations; + +import org.ehcache.clustered.client.internal.store.ServerStoreProxy; +import org.ehcache.clustered.common.internal.store.Chain; +import org.ehcache.clustered.common.internal.store.Element; +import org.ehcache.clustered.common.internal.store.operations.Operation; +import org.ehcache.clustered.common.internal.store.operations.PutOperation; +import org.ehcache.clustered.common.internal.store.operations.codecs.OperationsCodec; +import org.ehcache.clustered.common.internal.util.ChainBuilder; +import org.ehcache.core.spi.store.Store.ValueHolder; + +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; + +/** + * An abstract chain resolver. + *

+ * Operation application is performed in subclasses specialized for eternal and non-eternal caches. + * + * @see EternalChainResolver + * @see ExpiryChainResolver + * + * @param key type + * @param value type + */ +public abstract class ChainResolver { + protected final OperationsCodec codec; + + public ChainResolver(final OperationsCodec codec) { + this.codec = codec; + } + + /** + * Resolves the given key within the given chain entry to its current value with a specific compaction threshold. + *

+ * If the resultant chain has shrunk by more than {@code threshold} elements then an attempt is made to perform the + * equivalent compaction on the server. + * + * @param entry target chain entry + * @param key target key + * @param now current time + * @param threshold compaction threshold + * @return the current value + */ + public abstract ValueHolder resolve(ServerStoreProxy.ChainEntry entry, K key, long now, int threshold); + + /** + * Resolves the given key within the given chain entry to its current value. + *

+ * This is exactly equivalent to calling {@link #resolve(ServerStoreProxy.ChainEntry, Object, long, int)} with a zero + * compaction threshold. + * + * @param entry target chain entry + * @param key target key + * @param now current time + * @return the current value + */ + public ValueHolder resolve(ServerStoreProxy.ChainEntry entry, K key, long now) { + return resolve(entry, key, now, 0); + } + + /** + * Resolves all keys within the given chain to their current values while removing expired values. + * + * @param chain target chain + * @param now current time + * @return a map of current values + */ + public abstract Map> resolveAll(Chain chain, long now); + + /** + * Resolves all keys within the given chain to their current values while retaining expired values. + * + * @param chain target chain + * @return a map of current values + */ + public abstract Map> resolveAll(Chain chain); + + /** + * Compacts the given chain entry by resolving every key within. + * + * @param entry an uncompacted heterogenous {@link ServerStoreProxy.ChainEntry} + */ + public void compact(ServerStoreProxy.ChainEntry entry) { + ChainBuilder builder = new ChainBuilder(); + for (PutOperation operation : resolveToSimplePuts(entry).values()) { + builder = builder.add(codec.encode(operation)); + } + Chain compacted = builder.build(); + if (compacted.length() < entry.length()) { + entry.replaceAtHead(compacted); + } + } + + /** + * Resolves the given key within the given chain entry to an equivalent put operation. + *

+ * If the resultant chain has shrunk by more than {@code threshold} elements then an attempt is made to perform the + * equivalent compaction on the server. + * + * @param entry target chain entry + * @param key target key + * @param threshold compaction threshold + * @return equivalent put operation + */ + protected PutOperation resolve(ServerStoreProxy.ChainEntry entry, K key, int threshold) { + PutOperation result = null; + ChainBuilder resolvedChain = new ChainBuilder(); + for (Element element : entry) { + ByteBuffer payload = element.getPayload(); + Operation operation = codec.decode(payload); + + if(key.equals(operation.getKey())) { + result = applyOperation(key, result, operation); + } else { + payload.rewind(); + resolvedChain = resolvedChain.add(payload); + } + } + if(result != null) { + resolvedChain = resolvedChain.add(codec.encode(result)); + } + + if (entry.length() - resolvedChain.length() > threshold) { + entry.replaceAtHead(resolvedChain.build()); + } + return result; + } + + /** + * Resolves all keys within the given chain to their equivalent put operations. + * + * @param chain target chain + * @return a map of equivalent put operations + */ + public Map> resolveToSimplePuts(Chain chain) { + //absent hash-collisions this should always be a 1 entry map + Map> compacted = new HashMap<>(2); + for (Element element : chain) { + ByteBuffer payload = element.getPayload(); + Operation operation = codec.decode(payload); + compacted.compute(operation.getKey(), (k, v) -> applyOperation(k, v, operation)); + } + return compacted; + } + + /** + * Resolves a key within the given chain to its equivalent put operation. + * + * @param chain target chain + * @param key the key + * @return the equivalent put operation + */ + public PutOperation resolve(Chain chain, K key) { + return resolveToSimplePuts(chain).get(key); + } + + /** + * Applies the given operation to the current state. + * + * @param key cache key + * @param existing current state + * @param operation operation to apply + * @return an equivalent put operation + */ + public abstract PutOperation applyOperation(K key, PutOperation existing, Operation operation); +} diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/operations/EternalChainResolver.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/operations/EternalChainResolver.java similarity index 57% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/operations/EternalChainResolver.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/operations/EternalChainResolver.java index dd2cc6bf93..0fe81f3a25 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/operations/EternalChainResolver.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/operations/EternalChainResolver.java @@ -16,10 +16,19 @@ package org.ehcache.clustered.client.internal.store.operations; +import org.ehcache.clustered.client.internal.store.ClusteredValueHolder; +import org.ehcache.clustered.client.internal.store.ServerStoreProxy; +import org.ehcache.clustered.common.internal.store.Chain; import org.ehcache.clustered.common.internal.store.operations.Operation; import org.ehcache.clustered.common.internal.store.operations.PutOperation; import org.ehcache.clustered.common.internal.store.operations.Result; import org.ehcache.clustered.common.internal.store.operations.codecs.OperationsCodec; +import org.ehcache.core.spi.store.Store.ValueHolder; + +import java.util.HashMap; +import java.util.Map; + +import static java.util.Collections.unmodifiableMap; /** * A specialized chain resolver for eternal caches. @@ -35,16 +44,34 @@ public EternalChainResolver(final OperationsCodec codec) { super(codec); } + @Override + public ValueHolder resolve(ServerStoreProxy.ChainEntry entry, K key, long now, int threshold) { + PutOperation resolved = resolve(entry, key, threshold); + return resolved == null ? null : new ClusteredValueHolder<>(resolved.getValue()); + } + + @Override + public Map> resolveAll(Chain chain) { + Map> resolved = resolveToSimplePuts(chain); + + Map> values = new HashMap<>(resolved.size()); + for (Map.Entry> e : resolved.entrySet()) { + values.put(e.getKey(), new ClusteredValueHolder<>(e.getValue().getValue())); + } + return unmodifiableMap(values); + } + + @Override + public Map> resolveAll(Chain chain, long now) { + return resolveAll(chain); + } + /** * Applies the given operation returning a result that never expires. * - * @param key cache key - * @param existing current state - * @param operation operation to apply - * @param now current time - * @return the equivalent put operation + * {@inheritDoc} */ - public PutOperation applyOperation(K key, PutOperation existing, Operation operation, long now) { + public PutOperation applyOperation(K key, PutOperation existing, Operation operation) { final Result newValue = operation.apply(existing); if (newValue == null) { return null; diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/operations/ExpiryChainResolver.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/operations/ExpiryChainResolver.java similarity index 63% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/operations/ExpiryChainResolver.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/operations/ExpiryChainResolver.java index f7f5e6b544..70d9df31d9 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/operations/ExpiryChainResolver.java +++ b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/internal/store/operations/ExpiryChainResolver.java @@ -16,17 +16,26 @@ package org.ehcache.clustered.client.internal.store.operations; +import org.ehcache.clustered.client.internal.store.ClusteredValueHolder; +import org.ehcache.clustered.client.internal.store.ServerStoreProxy; +import org.ehcache.clustered.common.internal.store.Chain; import org.ehcache.clustered.common.internal.store.operations.Operation; import org.ehcache.clustered.common.internal.store.operations.PutOperation; import org.ehcache.clustered.common.internal.store.operations.Result; +import org.ehcache.clustered.common.internal.store.operations.TimestampOperation; import org.ehcache.clustered.common.internal.store.operations.codecs.OperationsCodec; import org.ehcache.core.config.ExpiryUtils; +import org.ehcache.core.spi.store.Store.ValueHolder; import org.ehcache.expiry.ExpiryPolicy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.time.Duration; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeoutException; +import static java.util.Collections.unmodifiableMap; import static java.util.Objects.requireNonNull; import static org.ehcache.core.config.ExpiryUtils.isExpiryDurationInfinite; @@ -53,6 +62,48 @@ public ExpiryChainResolver(final OperationsCodec codec, ExpiryPolicy resolve(ServerStoreProxy.ChainEntry entry, K key, long now, int threshold) { + PutOperation resolved = resolve(entry, key, threshold); + + if (resolved == null) { + return null; + } else if (now >= resolved.expirationTime()) { + try { + entry.append(codec.encode(new TimestampOperation<>(key, now))); + } catch (TimeoutException e) { + LOG.debug("Failed to append timestamp operation", e); + } + return null; + } else { + return new ClusteredValueHolder<>(resolved.getValue(), resolved.expirationTime()); + } + } + + @Override + public Map> resolveAll(Chain chain, long now) { + Map> resolved = resolveAll(chain); + + Map> values = new HashMap<>(resolved.size()); + for (Map.Entry> e : resolved.entrySet()) { + if (!e.getValue().isExpired(now)) { + values.put(e.getKey(), e.getValue()); + } + } + return unmodifiableMap(values); + } + + @Override + public Map> resolveAll(Chain chain) { + Map> resolved = resolveToSimplePuts(chain); + + Map> values = new HashMap<>(resolved.size()); + for (Map.Entry> e : resolved.entrySet()) { + values.put(e.getKey(), new ClusteredValueHolder<>(e.getValue().getValue(), e.getValue().expirationTime())); + } + return unmodifiableMap(values); + } + /** * Applies the given operation returning a result with an expiry time determined by this resolvers expiry policy. *

@@ -61,22 +112,21 @@ public ExpiryChainResolver(final OperationsCodec codec, ExpiryPolicy applyOperation(K key, PutOperation existing, Operation operation, long now) { + public PutOperation applyOperation(K key, PutOperation existing, Operation operation) { + if (existing != null && operation.timeStamp() >= existing.expirationTime()) { + existing = null; + } + final Result newValue = operation.apply(existing); if (newValue == null) { return null; + } else if (newValue == existing) { + return existing; } else { - long expirationTime = calculateExpiryTime(key, existing, operation, newValue); - - if (now >= expirationTime) { - return null; - } else { - return newValue.asOperationExpiringAt(expirationTime); - } + return newValue.asOperationExpiringAt(calculateExpiryTime(key, existing, operation, newValue)); } } diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/service/ClientEntityFactory.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/service/ClientEntityFactory.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/service/ClientEntityFactory.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/service/ClientEntityFactory.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/service/ClusteringService.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/service/ClusteringService.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/service/ClusteringService.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/service/ClusteringService.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/service/EntityBusyException.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/service/EntityBusyException.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/service/EntityBusyException.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/service/EntityBusyException.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/service/EntityService.java b/clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/service/EntityService.java similarity index 100% rename from clustered/client/src/main/java/org/ehcache/clustered/client/service/EntityService.java rename to clustered/ehcache-client/src/main/java/org/ehcache/clustered/client/service/EntityService.java diff --git a/clustered/client/src/main/resources/META-INF/services/org.ehcache.core.spi.service.ServiceFactory b/clustered/ehcache-client/src/main/resources/META-INF/services/org.ehcache.core.spi.service.ServiceFactory similarity index 100% rename from clustered/client/src/main/resources/META-INF/services/org.ehcache.core.spi.service.ServiceFactory rename to clustered/ehcache-client/src/main/resources/META-INF/services/org.ehcache.core.spi.service.ServiceFactory diff --git a/clustered/client/src/main/resources/META-INF/services/org.ehcache.xml.CacheManagerServiceConfigurationParser b/clustered/ehcache-client/src/main/resources/META-INF/services/org.ehcache.xml.CacheManagerServiceConfigurationParser similarity index 100% rename from clustered/client/src/main/resources/META-INF/services/org.ehcache.xml.CacheManagerServiceConfigurationParser rename to clustered/ehcache-client/src/main/resources/META-INF/services/org.ehcache.xml.CacheManagerServiceConfigurationParser diff --git a/clustered/client/src/main/resources/META-INF/services/org.ehcache.xml.CacheResourceConfigurationParser b/clustered/ehcache-client/src/main/resources/META-INF/services/org.ehcache.xml.CacheResourceConfigurationParser similarity index 100% rename from clustered/client/src/main/resources/META-INF/services/org.ehcache.xml.CacheResourceConfigurationParser rename to clustered/ehcache-client/src/main/resources/META-INF/services/org.ehcache.xml.CacheResourceConfigurationParser diff --git a/clustered/client/src/main/resources/META-INF/services/org.ehcache.xml.CacheServiceConfigurationParser b/clustered/ehcache-client/src/main/resources/META-INF/services/org.ehcache.xml.CacheServiceConfigurationParser similarity index 100% rename from clustered/client/src/main/resources/META-INF/services/org.ehcache.xml.CacheServiceConfigurationParser rename to clustered/ehcache-client/src/main/resources/META-INF/services/org.ehcache.xml.CacheServiceConfigurationParser diff --git a/clustered/client/src/main/resources/META-INF/services/org.terracotta.entity.EntityClientService b/clustered/ehcache-client/src/main/resources/META-INF/services/org.terracotta.entity.EntityClientService similarity index 100% rename from clustered/client/src/main/resources/META-INF/services/org.terracotta.entity.EntityClientService rename to clustered/ehcache-client/src/main/resources/META-INF/services/org.terracotta.entity.EntityClientService diff --git a/clustered/client/src/main/resources/ehcache-clustered-ext.xsd b/clustered/ehcache-client/src/main/resources/ehcache-clustered-ext.xsd similarity index 77% rename from clustered/client/src/main/resources/ehcache-clustered-ext.xsd rename to clustered/ehcache-client/src/main/resources/ehcache-clustered-ext.xsd index 1a8aeae70a..f42a6c5ff7 100644 --- a/clustered/client/src/main/resources/ehcache-clustered-ext.xsd +++ b/clustered/ehcache-client/src/main/resources/ehcache-clustered-ext.xsd @@ -51,7 +51,7 @@ - + Specifies the amount of time a cache read operation will wait for a response from a cluster @@ -59,7 +59,7 @@ - + Specifies the amount of time a cache write operation will wait for a response from a cluster @@ -67,7 +67,7 @@ - + Specifies the amount of time a cache will wait to connect to a cluster @@ -90,9 +90,13 @@ - - - + + + + + + + @@ -100,7 +104,7 @@ - + Cluster Tier Manager identifier. @@ -116,14 +120,14 @@ - + The host that the server is running on. - + The port that the server is listening on. @@ -132,14 +136,6 @@ - - - - - - - - @@ -157,10 +153,24 @@ - + + - xml:lang="en"> + True if server side components should be automatically created if they are absent. + + This attribute is deprecated, and has been replaced by the 'client-mode' attribute. + Use of both at the same time (although legal per the schema) will fail at parse time in Ehcache. + + + + + + xml:lang="en"> + The client connection behavior, either: + * expecting - expect a matching server configuration (fail otherwise) + * auto-create - expect a matching server configuration or create one if none is present + * auto-create-on-reconnect - as 'auto-create' but also auto-create on reconnection @@ -173,7 +183,7 @@ - + @@ -186,6 +196,18 @@ + + + + + + + + + + + + @@ -196,7 +218,7 @@ - + @@ -211,7 +233,7 @@ - + Name of the shared pool this resource uses. @@ -230,7 +252,7 @@ - + Optional reference to a server-side storage resource. @@ -240,7 +262,7 @@ - + Required reference to a server-side storage resource. diff --git a/clustered/client/src/test/java/org/ehcache/clustered/ClusteredResourcePoolUpdationTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/ClusteredResourcePoolUpdationTest.java similarity index 64% rename from clustered/client/src/test/java/org/ehcache/clustered/ClusteredResourcePoolUpdationTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/ClusteredResourcePoolUpdationTest.java index 946df0deba..78273ff50a 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/ClusteredResourcePoolUpdationTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/ClusteredResourcePoolUpdationTest.java @@ -21,20 +21,26 @@ import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder; import org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder; import org.ehcache.clustered.client.internal.UnitTestConnectionService; +import org.ehcache.config.CacheRuntimeConfiguration; import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.config.builders.CacheManagerBuilder; import org.ehcache.config.builders.ResourcePoolsBuilder; import org.ehcache.config.units.MemoryUnit; -import org.junit.After; import org.junit.AfterClass; -import org.junit.Before; import org.junit.BeforeClass; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import java.net.URI; +import static org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder.clusteredDedicated; +import static org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder.clusteredShared; +import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; +import static org.ehcache.config.units.MemoryUnit.MB; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasProperty; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThrows; + public class ClusteredResourcePoolUpdationTest { private static final URI CLUSTER_URI = URI.create("terracotta://example.com:9540/my-application"); @@ -43,9 +49,6 @@ public class ClusteredResourcePoolUpdationTest { private static Cache dedicatedCache; private static Cache sharedCache; - @Rule - public ExpectedException expectedException = ExpectedException.none(); - @BeforeClass public static void setUp() throws Exception { UnitTestConnectionService.add(CLUSTER_URI, @@ -55,10 +58,10 @@ public static void setUp() throws Exception { .build()); cacheManager = CacheManagerBuilder.newCacheManagerBuilder() - .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER_URI).autoCreate() + .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER_URI).autoCreate(server -> server .defaultServerResource("primary-server-resource") .resourcePool("resource-pool-a", 2, MemoryUnit.MB, "secondary-server-resource") - .resourcePool("resource-pool-b", 4, MemoryUnit.MB)) + .resourcePool("resource-pool-b", 4, MemoryUnit.MB))) .withCache("dedicated-cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 4, MemoryUnit.MB)))) @@ -74,29 +77,35 @@ public static void setUp() throws Exception { @AfterClass public static void tearDown() throws Exception { - cacheManager.close(); - UnitTestConnectionService.remove(CLUSTER_URI); + try { + cacheManager.close(); + UnitTestConnectionService.remove(CLUSTER_URI); + } finally { + cacheManager = null; + dedicatedCache = null; + sharedCache = null; + } } @Test public void testClusteredDedicatedResourcePoolUpdation() throws Exception { - expectedException.expect(UnsupportedOperationException.class); - expectedException.expectMessage("Updating CLUSTERED resource is not supported"); - dedicatedCache.getRuntimeConfiguration().updateResourcePools( - ResourcePoolsBuilder.newResourcePoolsBuilder() - .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 8, MemoryUnit.MB)) + CacheRuntimeConfiguration runtimeConfiguration = dedicatedCache.getRuntimeConfiguration(); + UnsupportedOperationException thrown = assertThrows(UnsupportedOperationException.class, () -> + runtimeConfiguration.updateResourcePools(newResourcePoolsBuilder() + .with(clusteredDedicated("primary-server-resource", 8, MB)) .build() - ); + )); + assertThat(thrown, hasProperty("message", is("Updating CLUSTERED resource is not supported"))); } @Test public void testClusteredSharedResourcePoolUpdation() throws Exception { - expectedException.expect(UnsupportedOperationException.class); - expectedException.expectMessage("Updating CLUSTERED resource is not supported"); - sharedCache.getRuntimeConfiguration().updateResourcePools( - ResourcePoolsBuilder.newResourcePoolsBuilder() - .with(ClusteredResourcePoolBuilder.clusteredShared("resource-pool-a")) + CacheRuntimeConfiguration runtimeConfiguration = sharedCache.getRuntimeConfiguration(); + UnsupportedOperationException thrown = assertThrows(UnsupportedOperationException.class, () -> + runtimeConfiguration.updateResourcePools(newResourcePoolsBuilder() + .with(clusteredShared("resource-pool-a")) .build() - ); + )); + assertThat(thrown, hasProperty("message", is("Updating CLUSTERED resource is not supported"))); } } diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/BasicClusteredCacheExpiryTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/BasicClusteredCacheExpiryTest.java similarity index 67% rename from clustered/client/src/test/java/org/ehcache/clustered/client/BasicClusteredCacheExpiryTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/BasicClusteredCacheExpiryTest.java index 7d88e1f4ea..ae0e3623f1 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/BasicClusteredCacheExpiryTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/BasicClusteredCacheExpiryTest.java @@ -37,9 +37,9 @@ import static org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder.cluster; import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; /** * @@ -49,12 +49,12 @@ public class BasicClusteredCacheExpiryTest { private static final URI CLUSTER_URI = URI.create("terracotta://example.com:9540/my-application"); private static final CacheManagerBuilder commonClusteredCacheManagerBuilder = newCacheManagerBuilder() - .with(cluster(CLUSTER_URI).autoCreate()) + .with(cluster(CLUSTER_URI).autoCreate(c -> c)) .withCache("clustered-cache", newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 2, MemoryUnit.MB))) .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMillis(1L))) - .add(ClusteredStoreConfigurationBuilder.withConsistency(Consistency.STRONG))); + .withService(ClusteredStoreConfigurationBuilder.withConsistency(Consistency.STRONG))); @Before public void definePassthroughServer() throws Exception { @@ -79,19 +79,17 @@ public void testGetExpiredSingleClient() { final CacheManagerBuilder clusteredCacheManagerBuilder = commonClusteredCacheManagerBuilder.using(timeSourceConfiguration); - final PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(true); + try (PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(true)) { - final Cache cache = cacheManager.getCache("clustered-cache", Long.class, String.class); + final Cache cache = cacheManager.getCache("clustered-cache", Long.class, String.class); - cache.put(1L, "value"); - assertThat(cache.get(1L), is("value")); + cache.put(1L, "value"); + assertThat(cache.get(1L), is("value")); - timeSource.advanceTime(1); - - assertThat(cache.get(1L), nullValue()); - - cacheManager.close(); + timeSource.advanceTime(1); + assertThat(cache.get(1L), nullValue()); + } } @Test @@ -103,22 +101,21 @@ public void testGetExpiredTwoClients() { final CacheManagerBuilder clusteredCacheManagerBuilder = commonClusteredCacheManagerBuilder.using(timeSourceConfiguration); - final PersistentCacheManager cacheManager1 = clusteredCacheManagerBuilder.build(true); - final PersistentCacheManager cacheManager2 = clusteredCacheManagerBuilder.build(true); - - final Cache cache1 = cacheManager1.getCache("clustered-cache", Long.class, String.class); - final Cache cache2 = cacheManager2.getCache("clustered-cache", Long.class, String.class); + try (PersistentCacheManager cacheManager1 = clusteredCacheManagerBuilder.build(true)) { + try (PersistentCacheManager cacheManager2 = clusteredCacheManagerBuilder.build(true)) { - assertThat(cache2.get(1L), nullValue()); - cache1.put(1L, "value1"); - assertThat(cache1.get(1L), is("value1")); - timeSource.advanceTime(1L); + final Cache cache1 = cacheManager1.getCache("clustered-cache", Long.class, String.class); + final Cache cache2 = cacheManager2.getCache("clustered-cache", Long.class, String.class); - assertThat(cache2.get(1L), nullValue()); - assertThat(cache1.get(1L), nullValue()); + assertThat(cache2.get(1L), nullValue()); + cache1.put(1L, "value1"); + assertThat(cache1.get(1L), is("value1")); + timeSource.advanceTime(1L); - cacheManager2.close(); - cacheManager1.close(); + assertThat(cache2.get(1L), nullValue()); + assertThat(cache1.get(1L), nullValue()); + } + } } @Test @@ -130,23 +127,21 @@ public void testContainsKeyExpiredTwoClients() { final CacheManagerBuilder clusteredCacheManagerBuilder = commonClusteredCacheManagerBuilder.using(timeSourceConfiguration); - final PersistentCacheManager cacheManager1 = clusteredCacheManagerBuilder.build(true); - final PersistentCacheManager cacheManager2 = clusteredCacheManagerBuilder.build(true); - - final Cache cache1 = cacheManager1.getCache("clustered-cache", Long.class, String.class); - final Cache cache2 = cacheManager2.getCache("clustered-cache", Long.class, String.class); - - assertThat(cache2.get(1L), nullValue()); - cache1.put(1L, "value1"); - assertThat(cache1.containsKey(1L), is(true)); - timeSource.advanceTime(1L); + try (PersistentCacheManager cacheManager1 = clusteredCacheManagerBuilder.build(true)) { + try (PersistentCacheManager cacheManager2 = clusteredCacheManagerBuilder.build(true)) { - assertThat(cache1.containsKey(1L), is(false)); - assertThat(cache2.containsKey(1L), is(false)); + final Cache cache1 = cacheManager1.getCache("clustered-cache", Long.class, String.class); + final Cache cache2 = cacheManager2.getCache("clustered-cache", Long.class, String.class); - cacheManager2.close(); - cacheManager1.close(); + assertThat(cache2.get(1L), nullValue()); + cache1.put(1L, "value1"); + assertThat(cache1.containsKey(1L), is(true)); + timeSource.advanceTime(1L); + assertThat(cache1.containsKey(1L), is(false)); + assertThat(cache2.containsKey(1L), is(false)); + } + } } } diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/BasicClusteredCacheTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/BasicClusteredCacheTest.java similarity index 60% rename from clustered/client/src/test/java/org/ehcache/clustered/client/BasicClusteredCacheTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/BasicClusteredCacheTest.java index 5163527489..3f29a53628 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/BasicClusteredCacheTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/BasicClusteredCacheTest.java @@ -26,8 +26,7 @@ import org.ehcache.config.builders.ResourcePoolsBuilder; import org.ehcache.config.units.EntryUnit; import org.ehcache.config.units.MemoryUnit; -import org.ehcache.core.statistics.CacheStatistics; -import org.ehcache.impl.internal.statistics.DefaultStatisticsService; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -36,7 +35,6 @@ import java.math.BigInteger; import java.net.URI; import java.util.Random; -import java.util.concurrent.atomic.LongAdder; import static org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder.clusteredDedicated; import static org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder.cluster; @@ -44,9 +42,9 @@ import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder; import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; /** * Provides basic tests for creation of a cache using a {@link org.ehcache.clustered.client.internal.store.ClusteredStore ClusteredStore}. @@ -74,138 +72,131 @@ public void testClusteredCacheSingleClient() throws Exception { final CacheManagerBuilder clusteredCacheManagerBuilder = newCacheManagerBuilder() - .with(cluster(CLUSTER_URI).autoCreate()) + .with(cluster(CLUSTER_URI).autoCreate(c -> c)) .withCache("clustered-cache", newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .with(clusteredDedicated("primary-server-resource", 2, MemoryUnit.MB)))); - final PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(true); + try (PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(true)) { - final Cache cache = cacheManager.getCache("clustered-cache", Long.class, String.class); + final Cache cache = cacheManager.getCache("clustered-cache", Long.class, String.class); - cache.put(1L, "value"); - assertThat(cache.get(1L), is("value")); - - cacheManager.close(); + cache.put(1L, "value"); + assertThat(cache.get(1L), is("value")); + } } @Test public void testClusteredCacheTwoClients() throws Exception { final CacheManagerBuilder clusteredCacheManagerBuilder = newCacheManagerBuilder() - .with(cluster(CLUSTER_URI).autoCreate()) + .with(cluster(CLUSTER_URI).autoCreate(c -> c)) .withCache("clustered-cache", newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder().heap(100, EntryUnit.ENTRIES) .with(clusteredDedicated("primary-server-resource", 2, MemoryUnit.MB))) - .add(new ClusteredStoreConfiguration(Consistency.STRONG))) - ; + .withService(new ClusteredStoreConfiguration(Consistency.STRONG))); - final PersistentCacheManager cacheManager1 = clusteredCacheManagerBuilder.build(true); - final PersistentCacheManager cacheManager2 = clusteredCacheManagerBuilder.build(true); + try (PersistentCacheManager cacheManager1 = clusteredCacheManagerBuilder.build(true)) { + try (PersistentCacheManager cacheManager2 = clusteredCacheManagerBuilder.build(true)) { - final Cache cache1 = cacheManager1.getCache("clustered-cache", Long.class, String.class); - final Cache cache2 = cacheManager2.getCache("clustered-cache", Long.class, String.class); + final Cache cache1 = cacheManager1.getCache("clustered-cache", Long.class, String.class); + final Cache cache2 = cacheManager2.getCache("clustered-cache", Long.class, String.class); - assertThat(cache2.get(1L), nullValue()); - cache1.put(1L, "value1"); - assertThat(cache2.get(1L), is("value1")); - assertThat(cache1.get(1L), is("value1")); - cache1.put(1L, "value2"); - assertThat(cache2.get(1L), is("value2")); - assertThat(cache1.get(1L), is("value2")); - - cacheManager2.close(); - cacheManager1.close(); + assertThat(cache2.get(1L), nullValue()); + cache1.put(1L, "value1"); + assertThat(cache2.get(1L), is("value1")); + assertThat(cache1.get(1L), is("value1")); + cache1.put(1L, "value2"); + assertThat(cache2.get(1L), is("value2")); + assertThat(cache1.get(1L), is("value2")); + } + } } @Test public void testClustered3TierCacheTwoClients() throws Exception { final CacheManagerBuilder clusteredCacheManagerBuilder = newCacheManagerBuilder() - .with(cluster(CLUSTER_URI).autoCreate()) + .with(cluster(CLUSTER_URI).autoCreate(c -> c)) .withCache("clustered-cache", newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder().heap(1, EntryUnit.ENTRIES).offheap(1, MemoryUnit.MB) .with(clusteredDedicated("primary-server-resource", 2, MemoryUnit.MB))) - .add(new ClusteredStoreConfiguration(Consistency.STRONG))) - ; - - final PersistentCacheManager cacheManager1 = clusteredCacheManagerBuilder.build(true); - final PersistentCacheManager cacheManager2 = clusteredCacheManagerBuilder.build(true); - - final Cache cache1 = cacheManager1.getCache("clustered-cache", Long.class, String.class); - final Cache cache2 = cacheManager2.getCache("clustered-cache", Long.class, String.class); - - assertThat(cache2.get(1L), nullValue()); - cache1.put(1L, "value1"); - cache1.put(2L, "value2"); - cache1.put(3L, "value3"); - assertThat(cache2.get(1L), is("value1")); - assertThat(cache2.get(2L), is("value2")); - assertThat(cache2.get(3L), is("value3")); - assertThat(cache2.get(1L), is("value1")); - assertThat(cache2.get(2L), is("value2")); - assertThat(cache2.get(3L), is("value3")); - assertThat(cache1.get(1L), is("value1")); - assertThat(cache1.get(2L), is("value2")); - assertThat(cache1.get(3L), is("value3")); - assertThat(cache1.get(1L), is("value1")); - assertThat(cache1.get(2L), is("value2")); - assertThat(cache1.get(3L), is("value3")); - cache1.put(1L, "value11"); - cache1.put(2L, "value12"); - cache1.put(3L, "value13"); - assertThat(cache2.get(1L), is("value11")); - assertThat(cache2.get(2L), is("value12")); - assertThat(cache2.get(3L), is("value13")); - assertThat(cache2.get(1L), is("value11")); - assertThat(cache2.get(2L), is("value12")); - assertThat(cache2.get(3L), is("value13")); - assertThat(cache1.get(1L), is("value11")); - assertThat(cache1.get(2L), is("value12")); - assertThat(cache1.get(3L), is("value13")); - assertThat(cache1.get(1L), is("value11")); - assertThat(cache1.get(2L), is("value12")); - assertThat(cache1.get(3L), is("value13")); - - cacheManager2.close(); - cacheManager1.close(); + .withService(new ClusteredStoreConfiguration(Consistency.STRONG))); + + try (PersistentCacheManager cacheManager1 = clusteredCacheManagerBuilder.build(true)) { + try (PersistentCacheManager cacheManager2 = clusteredCacheManagerBuilder.build(true)) { + + final Cache cache1 = cacheManager1.getCache("clustered-cache", Long.class, String.class); + final Cache cache2 = cacheManager2.getCache("clustered-cache", Long.class, String.class); + + assertThat(cache2.get(1L), nullValue()); + cache1.put(1L, "value1"); + cache1.put(2L, "value2"); + cache1.put(3L, "value3"); + assertThat(cache2.get(1L), is("value1")); + assertThat(cache2.get(2L), is("value2")); + assertThat(cache2.get(3L), is("value3")); + assertThat(cache2.get(1L), is("value1")); + assertThat(cache2.get(2L), is("value2")); + assertThat(cache2.get(3L), is("value3")); + assertThat(cache1.get(1L), is("value1")); + assertThat(cache1.get(2L), is("value2")); + assertThat(cache1.get(3L), is("value3")); + assertThat(cache1.get(1L), is("value1")); + assertThat(cache1.get(2L), is("value2")); + assertThat(cache1.get(3L), is("value3")); + cache1.put(1L, "value11"); + cache1.put(2L, "value12"); + cache1.put(3L, "value13"); + assertThat(cache2.get(1L), is("value11")); + assertThat(cache2.get(2L), is("value12")); + assertThat(cache2.get(3L), is("value13")); + assertThat(cache2.get(1L), is("value11")); + assertThat(cache2.get(2L), is("value12")); + assertThat(cache2.get(3L), is("value13")); + assertThat(cache1.get(1L), is("value11")); + assertThat(cache1.get(2L), is("value12")); + assertThat(cache1.get(3L), is("value13")); + assertThat(cache1.get(1L), is("value11")); + assertThat(cache1.get(2L), is("value12")); + assertThat(cache1.get(3L), is("value13")); + } + } } @Test public void testTieredClusteredCache() throws Exception { final CacheManagerBuilder clusteredCacheManagerBuilder = newCacheManagerBuilder() - .with(cluster(CLUSTER_URI).autoCreate()) + .with(cluster(CLUSTER_URI).autoCreate(c -> c)) .withCache("clustered-cache", newCacheConfigurationBuilder(Long.class, String.class, heap(2) .with(clusteredDedicated("primary-server-resource", 2, MemoryUnit.MB)))); - final PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(true); + try (PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(true)) { - final Cache cache = cacheManager.getCache("clustered-cache", Long.class, String.class); + final Cache cache = cacheManager.getCache("clustered-cache", Long.class, String.class); - cache.put(1L, "value"); - assertThat(cache.get(1L), is("value")); - - cacheManager.close(); + cache.put(1L, "value"); + assertThat(cache.get(1L), is("value")); + } } @Test public void testClusteredCacheWithSerializableValue() throws Exception { final CacheManagerBuilder clusteredCacheManagerBuilder = - newCacheManagerBuilder().with(cluster(CLUSTER_URI).autoCreate()) + newCacheManagerBuilder().with(cluster(CLUSTER_URI).autoCreate(c -> c)) .withCache("clustered-cache", newCacheConfigurationBuilder(Long.class, Person.class, newResourcePoolsBuilder().with(clusteredDedicated("primary-server-resource", 2, MemoryUnit.MB)))); - PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(true); - - Cache cache = cacheManager.getCache("clustered-cache", Long.class, Person.class); + try (PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(true)) { + Cache cache = cacheManager.getCache("clustered-cache", Long.class, Person.class); - cache.put(38L, new Person("Clustered Joe", 28)); - - cacheManager.close(); + cache.put(38L, new Person("Clustered Joe", 28)); + } - cacheManager = clusteredCacheManagerBuilder.build(true); - cache = cacheManager.getCache("clustered-cache", Long.class, Person.class); + try (PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(true)) { + Cache cache = cacheManager.getCache("clustered-cache", Long.class, Person.class); - assertThat(cache.get(38L).name, is("Clustered Joe")); + assertThat(cache.get(38L).name, is("Clustered Joe")); + } } @Test @@ -214,7 +205,7 @@ public void testLargeValues() throws Exception { CacheManagerBuilder clusteredCacheManagerBuilder = newCacheManagerBuilder() .using(statisticsService) - .with(cluster(CLUSTER_URI).autoCreate()) + .with(cluster(CLUSTER_URI).autoCreate(c -> c)) .withCache("small-cache", newCacheConfigurationBuilder(Long.class, BigInteger.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .with(clusteredDedicated("secondary-server-resource", 4, MemoryUnit.MB)))); diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/CacheManagerDestroyTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/CacheManagerDestroyTest.java similarity index 70% rename from clustered/client/src/test/java/org/ehcache/clustered/client/CacheManagerDestroyTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/CacheManagerDestroyTest.java index 128712f18c..36b91c7d94 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/CacheManagerDestroyTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/CacheManagerDestroyTest.java @@ -23,7 +23,6 @@ import org.ehcache.Status; import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder; import org.ehcache.clustered.client.internal.UnitTestConnectionService; -import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.config.builders.CacheManagerBuilder; import org.ehcache.config.builders.ResourcePoolsBuilder; import org.ehcache.config.units.MemoryUnit; @@ -37,8 +36,8 @@ import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder; import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; public class CacheManagerDestroyTest { @@ -47,7 +46,7 @@ public class CacheManagerDestroyTest { private static final CacheManagerBuilder clusteredCacheManagerBuilder = newCacheManagerBuilder() - .with(cluster(CLUSTER_URI).autoCreate()); + .with(cluster(CLUSTER_URI).autoCreate(c -> c)); @Before public void definePassthroughServer() throws Exception { @@ -75,8 +74,8 @@ public void testDestroyCacheManagerWithSingleClient() throws CachePersistenceExc @Test public void testCreateDestroyCreate() throws Exception { - PersistentCacheManager cacheManager = newCacheManagerBuilder().with(cluster(CLUSTER_URI).autoCreate() - .defaultServerResource("primary-server-resource")) + PersistentCacheManager cacheManager = newCacheManagerBuilder().with(cluster(CLUSTER_URI) + .autoCreate(c -> c.defaultServerResource("primary-server-resource"))) .withCache("my-cache", newCacheConfigurationBuilder(Long.class, String.class, heap(10).with(ClusteredResourcePoolBuilder .clusteredDedicated(2, MemoryUnit.MB)))) .build(true); @@ -85,35 +84,36 @@ public void testCreateDestroyCreate() throws Exception { cacheManager.destroy(); cacheManager.init(); + + cacheManager.close(); } @Test public void testDestroyCacheManagerWithMultipleClients() throws CachePersistenceException { PersistentCacheManager persistentCacheManager1 = clusteredCacheManagerBuilder.build(true); - PersistentCacheManager persistentCacheManager2 = clusteredCacheManagerBuilder.build(true); + try (PersistentCacheManager persistentCacheManager2 = clusteredCacheManagerBuilder.build(true)) { - persistentCacheManager1.close(); + persistentCacheManager1.close(); - try { - persistentCacheManager1.destroy(); - fail("StateTransitionException expected"); - } catch (StateTransitionException e) { - assertThat(e.getMessage(), is("Couldn't acquire cluster-wide maintenance lease")); - } + try { + persistentCacheManager1.destroy(); + fail("StateTransitionException expected"); + } catch (StateTransitionException e) { + assertThat(e.getMessage(), is("Couldn't acquire cluster-wide maintenance lease")); + } - assertThat(persistentCacheManager1.getStatus(), is(Status.UNINITIALIZED)); + assertThat(persistentCacheManager1.getStatus(), is(Status.UNINITIALIZED)); - assertThat(persistentCacheManager2.getStatus(), is(Status.AVAILABLE)); + assertThat(persistentCacheManager2.getStatus(), is(Status.AVAILABLE)); - Cache cache = persistentCacheManager2.createCache("test", newCacheConfigurationBuilder(Long.class, String.class, + Cache cache = persistentCacheManager2.createCache("test", newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() - .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 2, MemoryUnit.MB)))); - - cache.put(1L, "One"); + .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 2, MemoryUnit.MB)))); - assertThat(cache.get(1L), is("One")); + cache.put(1L, "One"); - persistentCacheManager2.close(); + assertThat(cache.get(1L), is("One")); + } } @Test @@ -125,23 +125,22 @@ public void testDestroyCacheManagerDoesNotAffectsExistingCacheWithExistingClient .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 2, MemoryUnit.MB)))); PersistentCacheManager persistentCacheManager1 = cacheManagerBuilder.build(true); - PersistentCacheManager persistentCacheManager2 = cacheManagerBuilder.build(true); - - persistentCacheManager1.close(); - try { - persistentCacheManager1.destroy(); - fail("StateTransitionException expected"); - } catch (StateTransitionException e) { - assertThat(e.getMessage(), is("Couldn't acquire cluster-wide maintenance lease")); - } + try (PersistentCacheManager persistentCacheManager2 = cacheManagerBuilder.build(true)) { - Cache cache = persistentCacheManager2.getCache("test", Long.class, String.class); + persistentCacheManager1.close(); + try { + persistentCacheManager1.destroy(); + fail("StateTransitionException expected"); + } catch (StateTransitionException e) { + assertThat(e.getMessage(), is("Couldn't acquire cluster-wide maintenance lease")); + } - cache.put(1L, "One"); + Cache cache = persistentCacheManager2.getCache("test", Long.class, String.class); - assertThat(cache.get(1L), is("One")); + cache.put(1L, "One"); - persistentCacheManager2.close(); + assertThat(cache.get(1L), is("One")); + } } @Test @@ -173,21 +172,20 @@ public void testCloseCacheManagerMultipleClients() { .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 2, MemoryUnit.MB)))); PersistentCacheManager persistentCacheManager1 = cacheManagerBuilder.build(true); - PersistentCacheManager persistentCacheManager2 = cacheManagerBuilder.build(true); - - Cache cache = persistentCacheManager1.getCache("test", Long.class, String.class); - cache.put(1L, "One"); + try (PersistentCacheManager persistentCacheManager2 = cacheManagerBuilder.build(true)) { - assertThat(cache.get(1L), is("One")); + Cache cache = persistentCacheManager1.getCache("test", Long.class, String.class); + cache.put(1L, "One"); - persistentCacheManager1.close(); - assertThat(persistentCacheManager1.getStatus(), is(Status.UNINITIALIZED)); + assertThat(cache.get(1L), is("One")); - Cache cache2 = persistentCacheManager2.getCache("test", Long.class, String.class); + persistentCacheManager1.close(); + assertThat(persistentCacheManager1.getStatus(), is(Status.UNINITIALIZED)); - assertThat(cache2.get(1L), is("One")); + Cache cache2 = persistentCacheManager2.getCache("test", Long.class, String.class); - persistentCacheManager2.close(); + assertThat(cache2.get(1L), is("One")); + } } } diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/ClusteredCacheDestroyTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/ClusteredCacheDestroyTest.java similarity index 57% rename from clustered/client/src/test/java/org/ehcache/clustered/client/ClusteredCacheDestroyTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/ClusteredCacheDestroyTest.java index 833e26c035..587f315eae 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/ClusteredCacheDestroyTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/ClusteredCacheDestroyTest.java @@ -30,9 +30,7 @@ import org.ehcache.config.units.MemoryUnit; import org.junit.After; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import java.net.URI; @@ -41,10 +39,12 @@ import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.hasProperty; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.fail; public class ClusteredCacheDestroyTest { @@ -52,16 +52,13 @@ public class ClusteredCacheDestroyTest { private static final URI CLUSTER_URI = URI.create("terracotta://example.com:9540/my-application"); private static final String CLUSTERED_CACHE = "clustered-cache"; - @Rule - public ExpectedException expectedException = ExpectedException.none(); - private static final CacheManagerBuilder clusteredCacheManagerBuilder = newCacheManagerBuilder() - .with(cluster(CLUSTER_URI).autoCreate()) + .with(cluster(CLUSTER_URI).autoCreate(c -> c)) .withCache(CLUSTERED_CACHE, newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 8, MemoryUnit.MB))) - .add(ClusteredStoreConfigurationBuilder.withConsistency(Consistency.STRONG))); + .withService(ClusteredStoreConfigurationBuilder.withConsistency(Consistency.STRONG))); @Before public void definePassthroughServer() throws Exception { @@ -79,108 +76,97 @@ public void removePassthroughServer() throws Exception { @Test public void testDestroyCacheWhenSingleClientIsConnected() throws CachePersistenceException { - PersistentCacheManager persistentCacheManager = clusteredCacheManagerBuilder.build(true); + try (PersistentCacheManager persistentCacheManager = clusteredCacheManagerBuilder.build(true)) { - persistentCacheManager.destroyCache(CLUSTERED_CACHE); + persistentCacheManager.destroyCache(CLUSTERED_CACHE); - final Cache cache = persistentCacheManager.getCache(CLUSTERED_CACHE, Long.class, String.class); + final Cache cache = persistentCacheManager.getCache(CLUSTERED_CACHE, Long.class, String.class); - assertThat(cache, nullValue()); - - persistentCacheManager.close(); + assertThat(cache, nullValue()); + } } @Test public void testDestroyFreesUpTheAllocatedResource() throws CachePersistenceException { + try (PersistentCacheManager persistentCacheManager = clusteredCacheManagerBuilder.build(true)) { - PersistentCacheManager persistentCacheManager = clusteredCacheManagerBuilder.build(true); - - CacheConfigurationBuilder configBuilder = newCacheConfigurationBuilder(Long.class, String.class, + CacheConfigurationBuilder configBuilder = newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() - .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 10, MemoryUnit.MB))); + .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 10, MemoryUnit.MB))); - try { - Cache anotherCache = persistentCacheManager.createCache("another-cache", configBuilder); - fail(); - } catch (IllegalStateException e) { - assertThat(e.getMessage(), is("Cache 'another-cache' creation in EhcacheManager failed.")); - } - - persistentCacheManager.destroyCache(CLUSTERED_CACHE); + try { + Cache anotherCache = persistentCacheManager.createCache("another-cache", configBuilder); + fail(); + } catch (IllegalStateException e) { + assertThat(e.getMessage(), is("Cache 'another-cache' creation in EhcacheManager failed.")); + } - Cache anotherCache = persistentCacheManager.createCache("another-cache", configBuilder); + persistentCacheManager.destroyCache(CLUSTERED_CACHE); - anotherCache.put(1L, "One"); - assertThat(anotherCache.get(1L), is("One")); + Cache anotherCache = persistentCacheManager.createCache("another-cache", configBuilder); - persistentCacheManager.close(); + anotherCache.put(1L, "One"); + assertThat(anotherCache.get(1L), is("One")); + } } @Test public void testDestroyUnknownCacheAlias() throws Exception { clusteredCacheManagerBuilder.build(true).close(); - PersistentCacheManager cacheManager = newCacheManagerBuilder().with(cluster(CLUSTER_URI).expecting()).build(true); + try (PersistentCacheManager cacheManager = newCacheManagerBuilder().with(cluster(CLUSTER_URI).expecting(c -> c)).build(true)) { - cacheManager.destroyCache(CLUSTERED_CACHE); + cacheManager.destroyCache(CLUSTERED_CACHE); - try { - cacheManager.createCache(CLUSTERED_CACHE, newCacheConfigurationBuilder(Long.class, String.class, newResourcePoolsBuilder() + try { + cacheManager.createCache(CLUSTERED_CACHE, newCacheConfigurationBuilder(Long.class, String.class, newResourcePoolsBuilder() .with(clustered()))); - fail("Expected exception as clustered store no longer exists"); - } catch (IllegalStateException e) { - assertThat(e.getMessage(), containsString(CLUSTERED_CACHE)); + fail("Expected exception as clustered store no longer exists"); + } catch (IllegalStateException e) { + assertThat(e.getMessage(), containsString(CLUSTERED_CACHE)); + } } - cacheManager.close(); } @Test public void testDestroyNonExistentCache() throws CachePersistenceException { - PersistentCacheManager persistentCacheManager = clusteredCacheManagerBuilder.build(true); + try (PersistentCacheManager persistentCacheManager = clusteredCacheManagerBuilder.build(true)) { - String nonExistent = "this-is-not-the-cache-you-are-looking-for"; - assertThat(persistentCacheManager.getCache(nonExistent, Long.class, String.class), nullValue()); - persistentCacheManager.destroyCache(nonExistent); - persistentCacheManager.close(); + String nonExistent = "this-is-not-the-cache-you-are-looking-for"; + assertThat(persistentCacheManager.getCache(nonExistent, Long.class, String.class), nullValue()); + persistentCacheManager.destroyCache(nonExistent); + } } @Test public void testDestroyCacheWhenMultipleClientsConnected() { - PersistentCacheManager persistentCacheManager1 = clusteredCacheManagerBuilder.build(true); - PersistentCacheManager persistentCacheManager2 = clusteredCacheManagerBuilder.build(true); + try (PersistentCacheManager persistentCacheManager1 = clusteredCacheManagerBuilder.build(true)) { + try (PersistentCacheManager persistentCacheManager2 = clusteredCacheManagerBuilder.build(true)) { - final Cache cache1 = persistentCacheManager1.getCache(CLUSTERED_CACHE, Long.class, String.class); + final Cache cache1 = persistentCacheManager1.getCache(CLUSTERED_CACHE, Long.class, String.class); - final Cache cache2 = persistentCacheManager2.getCache(CLUSTERED_CACHE, Long.class, String.class); + final Cache cache2 = persistentCacheManager2.getCache(CLUSTERED_CACHE, Long.class, String.class); - try { - persistentCacheManager1.destroyCache(CLUSTERED_CACHE); - fail(); - } catch (CachePersistenceException e) { - assertThat(e.getMessage(), containsString("Cannot destroy cluster tier")); - } - - try { - cache1.put(1L, "One"); - } catch (IllegalStateException e) { - assertThat(e.getMessage(), is("State is UNINITIALIZED")); - } + try { + persistentCacheManager1.destroyCache(CLUSTERED_CACHE); + fail(); + } catch (CachePersistenceException e) { + assertThat(e.getMessage(), containsString("Cannot destroy cluster tier")); + } - assertThat(cache2.get(1L), nullValue()); + try { + cache1.put(1L, "One"); + } catch (IllegalStateException e) { + assertThat(e.getMessage(), is("State is UNINITIALIZED")); + } - cache2.put(1L, "One"); + assertThat(cache2.get(1L), nullValue()); - assertThat(cache2.get(1L), is("One")); + cache2.put(1L, "One"); - persistentCacheManager1.close(); - persistentCacheManager2.close(); - } - - private static Throwable getRootCause(Throwable t) { - if (t.getCause() == null || t.getCause() == t) { - return t; + assertThat(cache2.get(1L), is("One")); + } } - return getRootCause(t.getCause()); } @Test @@ -208,34 +194,35 @@ public void testDestroyCacheOnNonExistentCacheManager() throws CachePersistenceE persistentCacheManager.destroyCache("this-is-not-the-cache-you-are-looking-for"); assertThat(persistentCacheManager.getStatus(), is(Status.UNINITIALIZED)); } + @Test + @SuppressWarnings("try") public void testDestroyCacheWithTwoCacheManagerOnSameCache_forbiddenWhenInUse() throws CachePersistenceException { - PersistentCacheManager persistentCacheManager1 = clusteredCacheManagerBuilder.build(true); - PersistentCacheManager persistentCacheManager2 = clusteredCacheManagerBuilder.build(true); - - expectedException.expect(CachePersistenceException.class); - expectedException.expectMessage("Cannot destroy cluster tier 'clustered-cache': in use by other client(s)"); - persistentCacheManager1.destroyCache(CLUSTERED_CACHE); + try (PersistentCacheManager persistentCacheManager1 = clusteredCacheManagerBuilder.build(true)) { + try (PersistentCacheManager persistentCacheManager2 = clusteredCacheManagerBuilder.build(true)) { + CachePersistenceException thrown = assertThrows(CachePersistenceException.class, () -> persistentCacheManager1.destroyCache(CLUSTERED_CACHE)); + assertThat(thrown, hasProperty("message", is("Cannot destroy cluster tier 'clustered-cache': in use by other client(s)"))); + } + } } @Test public void testDestroyCacheWithTwoCacheManagerOnSameCache_firstRemovesSecondDestroy() throws CachePersistenceException { - PersistentCacheManager persistentCacheManager1 = clusteredCacheManagerBuilder.build(true); - PersistentCacheManager persistentCacheManager2 = clusteredCacheManagerBuilder.build(true); - - persistentCacheManager2.removeCache(CLUSTERED_CACHE); - - persistentCacheManager1.destroyCache(CLUSTERED_CACHE); + try (PersistentCacheManager persistentCacheManager1 = clusteredCacheManagerBuilder.build(true)) { + try (PersistentCacheManager persistentCacheManager2 = clusteredCacheManagerBuilder.build(true)) { + persistentCacheManager2.removeCache(CLUSTERED_CACHE); + persistentCacheManager1.destroyCache(CLUSTERED_CACHE); + } + } } @Test public void testDestroyCacheWithTwoCacheManagerOnSameCache_secondDoesntHaveTheCacheButPreventExclusiveAccessToCluster() throws CachePersistenceException { PersistentCacheManager persistentCacheManager1 = clusteredCacheManagerBuilder.build(false); - PersistentCacheManager persistentCacheManager2 = clusteredCacheManagerBuilder.build(true); - - persistentCacheManager2.removeCache(CLUSTERED_CACHE); - - persistentCacheManager1.destroyCache(CLUSTERED_CACHE); + try (PersistentCacheManager persistentCacheManager2 = clusteredCacheManagerBuilder.build(true)) { + persistentCacheManager2.removeCache(CLUSTERED_CACHE); + persistentCacheManager1.destroyCache(CLUSTERED_CACHE); + } } } diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/ClusteredCacheExpirationTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/ClusteredCacheExpirationTest.java similarity index 96% rename from clustered/client/src/test/java/org/ehcache/clustered/client/ClusteredCacheExpirationTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/ClusteredCacheExpirationTest.java index 4b02f488d3..a8a2a8164e 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/ClusteredCacheExpirationTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/ClusteredCacheExpirationTest.java @@ -32,7 +32,7 @@ import org.ehcache.core.statistics.TierStatistics; import org.ehcache.expiry.ExpiryPolicy; import org.ehcache.impl.internal.TimeSourceConfiguration; -import org.ehcache.impl.internal.statistics.DefaultStatisticsService; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -59,14 +59,14 @@ private CacheManagerBuilder cacheManagerBuilder(ExpiryPo return newCacheManagerBuilder() .using(statisticsService) .using(new TimeSourceConfiguration(timeSource)) - .with(cluster(CLUSTER_URI).autoCreate()) + .with(cluster(CLUSTER_URI).autoCreate(c -> c)) .withCache(CLUSTERED_CACHE, newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .heap(10, EntryUnit.ENTRIES) .offheap(6, MemoryUnit.MB) .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 8, MemoryUnit.MB))) .withExpiry(expiry) - .add(ClusteredStoreConfigurationBuilder.withConsistency(Consistency.STRONG))); + .withService(ClusteredStoreConfigurationBuilder.withConsistency(Consistency.STRONG))); } private ExpiryPolicy oneSecondExpiration() { diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/ClusteredConcurrencyTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/ClusteredConcurrencyTest.java similarity index 90% rename from clustered/client/src/test/java/org/ehcache/clustered/client/ClusteredConcurrencyTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/ClusteredConcurrencyTest.java index 17f770f66a..ea4d8952f3 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/ClusteredConcurrencyTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/ClusteredConcurrencyTest.java @@ -94,14 +94,14 @@ private Runnable content(final CountDownLatch latch) { return () -> { try { CacheManagerBuilder clusteredCacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder() - .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER_URI).autoCreate() - .defaultServerResource("primary-server-resource") - .resourcePool("resource-pool-a", 8, MemoryUnit.MB) - .resourcePool("resource-pool-b", 8, MemoryUnit.MB, "secondary-server-resource")) + .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER_URI) + .autoCreate(server -> server.defaultServerResource("primary-server-resource") + .resourcePool("resource-pool-a", 8, MemoryUnit.MB) + .resourcePool("resource-pool-b", 8, MemoryUnit.MB, "secondary-server-resource"))) .withCache(CACHE_NAME, CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 8, MemoryUnit.MB))) - .add(new ClusteredStoreConfiguration(Consistency.STRONG))); + .withService(new ClusteredStoreConfiguration(Consistency.STRONG))); latch.countDown(); try { @@ -110,7 +110,7 @@ private Runnable content(final CountDownLatch latch) { // continue } - clusteredCacheManagerBuilder.build(true); + clusteredCacheManagerBuilder.build(true).close(); } catch (Throwable t) { exception.compareAndSet(null, t); // only keep the first exception } diff --git a/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/ClusteredEventsTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/ClusteredEventsTest.java new file mode 100644 index 0000000000..34f75b4518 --- /dev/null +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/ClusteredEventsTest.java @@ -0,0 +1,205 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.clustered.client; + +import org.ehcache.Cache; +import org.ehcache.PersistentCacheManager; +import org.ehcache.clustered.client.internal.UnitTestConnectionService; +import org.ehcache.config.builders.CacheManagerBuilder; +import org.ehcache.config.units.MemoryUnit; +import org.ehcache.event.CacheEvent; +import org.ehcache.event.EventFiring; +import org.ehcache.event.EventOrdering; +import org.ehcache.event.EventType; +import org.ehcache.impl.internal.TimeSourceConfiguration; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; + +import java.net.URI; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.TimeoutException; + +import static java.util.EnumSet.allOf; +import static org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder.clusteredDedicated; +import static org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder.cluster; +import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; +import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder; +import static org.ehcache.config.builders.ExpiryPolicyBuilder.timeToLiveExpiration; +import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.collection.IsIterableContainingInOrder.contains; +import static org.hamcrest.core.IsNull.nullValue; +import static org.terracotta.utilities.test.matchers.Eventually.within; + +public class ClusteredEventsTest { + + private static final URI CLUSTER_URI = URI.create("terracotta://example.com:9540/my-application"); + + @Rule + public final TestName runningTest = new TestName(); + + @Before + public void definePassthroughServer() { + UnitTestConnectionService.add(CLUSTER_URI, + new UnitTestConnectionService.PassthroughServerBuilder() + .resource("primary-server-resource", 32, MemoryUnit.MB) + .build()); + } + + @After + public void removePassthroughServer() { + UnitTestConnectionService.remove(CLUSTER_URI); + } + + @Test + public void testNonExpiringEventSequence() throws TimeoutException { + CacheManagerBuilder clusteredCacheManagerBuilder = + newCacheManagerBuilder() + .with(cluster(CLUSTER_URI).autoCreate(s -> s.defaultServerResource("primary-server-resource"))) + .withCache(runningTest.getMethodName(), newCacheConfigurationBuilder(Long.class, String.class, + newResourcePoolsBuilder().with(clusteredDedicated(16, MemoryUnit.MB)))); + + try (PersistentCacheManager driver = clusteredCacheManagerBuilder.build(true)) { + Cache driverCache = driver.getCache(runningTest.getMethodName(), Long.class, String.class); + try (PersistentCacheManager observer = clusteredCacheManagerBuilder.build(true)) { + Cache observerCache = observer.getCache(runningTest.getMethodName(), Long.class, String.class); + + List> driverEvents = new ArrayList<>(); + driverCache.getRuntimeConfiguration().registerCacheEventListener(driverEvents::add, EventOrdering.ORDERED, EventFiring.ASYNCHRONOUS, allOf(EventType.class)); + + List> observerEvents = new ArrayList<>(); + observerCache.getRuntimeConfiguration().registerCacheEventListener(observerEvents::add, EventOrdering.ORDERED, EventFiring.ASYNCHRONOUS, allOf(EventType.class)); + + + driverCache.put(1L, "foo"); + driverCache.put(1L, "bar"); + driverCache.remove(1L); + driverCache.putIfAbsent(1L, "baz"); + driverCache.replace(1L, "bat"); + driverCache.replace(1L, "bat", "bag"); + driverCache.remove(1L, "bag"); + + @SuppressWarnings("unchecked") + Matcher>> expectedSequence = contains( + created(1L, "foo"), + updated(1L, "foo", "bar"), + removed(1L, "bar"), + created(1L, "baz"), + updated(1L, "baz", "bat"), + updated(1L, "bat", "bag"), + removed(1L, "bag")); + + within(Duration.ofSeconds(10)).runsCleanly(() -> { + assertThat(driverEvents, expectedSequence); + assertThat(observerEvents, expectedSequence); + }); + } + } + } + + @Test + public void testExpiringEventSequence() throws TimeoutException { + TestTimeSource timeSource = new TestTimeSource(); + + CacheManagerBuilder clusteredCacheManagerBuilder = + newCacheManagerBuilder() + .using(new TimeSourceConfiguration(timeSource)) + .with(cluster(CLUSTER_URI).autoCreate(s -> s.defaultServerResource("primary-server-resource"))) + .withCache(runningTest.getMethodName(), newCacheConfigurationBuilder(Long.class, String.class, + newResourcePoolsBuilder().with(clusteredDedicated(16, MemoryUnit.MB))) + .withExpiry(timeToLiveExpiration(Duration.ofMillis(1000)))); + + try (PersistentCacheManager driver = clusteredCacheManagerBuilder.build(true)) { + Cache driverCache = driver.getCache(runningTest.getMethodName(), Long.class, String.class); + try (PersistentCacheManager observer = clusteredCacheManagerBuilder.build(true)) { + Cache observerCache = observer.getCache(runningTest.getMethodName(), Long.class, String.class); + + List> driverEvents = new ArrayList<>(); + driverCache.getRuntimeConfiguration().registerCacheEventListener(driverEvents::add, EventOrdering.ORDERED, EventFiring.ASYNCHRONOUS, allOf(EventType.class)); + + List> observerEvents = new ArrayList<>(); + observerCache.getRuntimeConfiguration().registerCacheEventListener(observerEvents::add, EventOrdering.ORDERED, EventFiring.ASYNCHRONOUS, allOf(EventType.class)); + + + driverCache.put(1L, "foo"); + timeSource.advanceTime(1100); + driverCache.putIfAbsent(1L, "bar"); + timeSource.advanceTime(1100); + driverCache.remove(1L); + driverCache.put(1L, "baz"); + timeSource.advanceTime(1100); + assertThat(driverCache.get(1L), nullValue()); + + @SuppressWarnings("unchecked") + Matcher>> expectedSequence = contains( + created(1L, "foo"), + expired(1L, "foo"), + created(1L, "bar"), + expired(1L, "bar"), + created(1L, "baz"), + expired(1L, "baz")); + + within(Duration.ofSeconds(10)).runsCleanly(() -> { + assertThat(driverEvents, expectedSequence); + assertThat(observerEvents, expectedSequence); + }); + } + } + } + + private static Matcher> created(K key, V value) { + return event(EventType.CREATED, key, null, value); + } + + private static Matcher> updated(K key, V oldValue, V newValue) { + return event(EventType.UPDATED, key, oldValue, newValue); + } + + private static Matcher> removed(K key, V value) { + return event(EventType.REMOVED, key, value, null); + } + + private static Matcher> expired(K key, V value) { + return event(EventType.EXPIRED, key, value, null); + } + + private static Matcher> event(EventType type, K key, V oldValue, V newValue) { + return new TypeSafeMatcher>() { + @Override + protected boolean matchesSafely(CacheEvent item) { + return type.equals(item.getType()) && key.equals(item.getKey()) + && Objects.equals(oldValue, item.getOldValue()) + && Objects.equals(newValue, item.getNewValue()); + } + + @Override + public void describeTo(Description description) { + description.appendText(" on '").appendValue(key).appendText("' ").appendValue(type) + .appendText(" [").appendValue(oldValue).appendText(" => ").appendValue(newValue).appendText("]"); + } + }; + } +} diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/EntityServiceTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/EntityServiceTest.java similarity index 78% rename from clustered/client/src/test/java/org/ehcache/clustered/client/EntityServiceTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/EntityServiceTest.java index d220400d2d..1d9b027970 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/EntityServiceTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/EntityServiceTest.java @@ -20,7 +20,6 @@ import org.ehcache.clustered.client.internal.UnitTestConnectionService.PassthroughServerBuilder; import org.ehcache.clustered.client.internal.lock.VoltronReadWriteLockClient; import org.ehcache.clustered.client.service.ClientEntityFactory; -import org.ehcache.clustered.client.service.ClusteringService; import org.ehcache.clustered.client.service.EntityBusyException; import org.ehcache.clustered.client.service.EntityService; import org.ehcache.config.units.MemoryUnit; @@ -39,7 +38,7 @@ import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.fail; public class EntityServiceTest { @@ -59,41 +58,40 @@ public void removePassthroughServer() throws Exception { UnitTestConnectionService.remove(CLUSTER_URI); } - @Test + @Test @SuppressWarnings("try") public void test() throws Exception { ClusteredManagementService clusteredManagementService = new ClusteredManagementService(); - CacheManager cacheManager = newCacheManagerBuilder() + try (CacheManager cacheManager = newCacheManagerBuilder() .using(clusteredManagementService) - .with(cluster(CLUSTER_URI).autoCreate()) - .build(true); + .with(cluster(CLUSTER_URI).autoCreate(c -> c)) + .build(true)) { - assertThat(clusteredManagementService.clientEntityFactory, is(notNullValue())); + assertThat(clusteredManagementService.clientEntityFactory, is(notNullValue())); - clusteredManagementService.clientEntityFactory.create(); - - try { clusteredManagementService.clientEntityFactory.create(); - fail(); - } catch (Exception e) { - assertThat(e, instanceOf(EntityAlreadyExistsException.class)); - } - VoltronReadWriteLockClient entity = clusteredManagementService.clientEntityFactory.retrieve(); - assertThat(entity, is(notNullValue())); + try { + clusteredManagementService.clientEntityFactory.create(); + fail(); + } catch (Exception e) { + assertThat(e, instanceOf(EntityAlreadyExistsException.class)); + } - try { - clusteredManagementService.clientEntityFactory.destroy(); - fail(); - } catch (Exception e) { - assertThat(e, instanceOf(EntityBusyException.class)); - } + VoltronReadWriteLockClient entity = clusteredManagementService.clientEntityFactory.retrieve(); + assertThat(entity, is(notNullValue())); - entity.close(); + try { + clusteredManagementService.clientEntityFactory.destroy(); + fail(); + } catch (Exception e) { + assertThat(e, instanceOf(EntityBusyException.class)); + } - clusteredManagementService.clientEntityFactory.destroy(); + entity.close(); - cacheManager.close(); + clusteredManagementService.clientEntityFactory.destroy(); + } } @ServiceDependencies(EntityService.class) diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/NonClusteredCacheTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/NonClusteredCacheTest.java similarity index 67% rename from clustered/client/src/test/java/org/ehcache/clustered/client/NonClusteredCacheTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/NonClusteredCacheTest.java index 38ab3b9b77..51350722c3 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/NonClusteredCacheTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/NonClusteredCacheTest.java @@ -17,6 +17,7 @@ package org.ehcache.clustered.client; import org.ehcache.CacheManager; +import org.ehcache.clustered.client.internal.service.DefaultClusteringService; import org.ehcache.clustered.client.internal.store.ClusteredStore; import org.ehcache.clustered.client.service.ClusteringService; import org.ehcache.config.CacheConfiguration; @@ -25,16 +26,16 @@ import org.ehcache.config.builders.ResourcePoolsBuilder; import org.ehcache.config.units.EntryUnit; import org.ehcache.config.units.MemoryUnit; -import org.ehcache.core.internal.util.ClassLoading; import org.ehcache.core.spi.service.ServiceFactory; -import org.hamcrest.Matchers; +import org.ehcache.core.util.ClassLoading; import org.junit.Test; -import java.util.HashSet; -import java.util.Set; +import java.util.stream.Collectors; -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; +import static java.util.Spliterators.spliterator; +import static java.util.stream.StreamSupport.stream; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsCollectionContaining.hasItems; /** * Ensures that a non-clustered {@code CacheManager} can be created when clustered classes are @@ -48,17 +49,8 @@ public void testNonClustered() throws Exception { /* * Ensure the cluster provider classes are loadable through the ServiceLoader mechanism. */ - Set> targetProviders = new HashSet<>(); - targetProviders.add(ClusteredStore.Provider.class); - targetProviders.add(ClusteringService.class); - for (ServiceFactory factory : ClassLoading.libraryServiceLoaderFor(ServiceFactory.class)) { - if (targetProviders.remove(factory.getServiceType())) { - if (targetProviders.isEmpty()) { - break; - } - } - } - assertThat(targetProviders, is(Matchers.empty())); + assertThat(stream(spliterator(ClassLoading.servicesOfType(ServiceFactory.class).iterator(), Long.MAX_VALUE, 0), false).map(f -> f.getServiceType()).collect(Collectors.toList()), + hasItems(ClusteredStore.Provider.class, DefaultClusteringService.class)); CacheConfiguration cacheConfiguration = CacheConfigurationBuilder.newCacheConfigurationBuilder( String.class, @@ -70,11 +62,9 @@ public void testNonClustered() throws Exception { .build(); - CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().build(true); - - cacheManager.createCache("cache-1", cacheConfiguration); - cacheManager.createCache("cache-2", cacheConfiguration); - - cacheManager.close(); + try (CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().build(true)) { + cacheManager.createCache("cache-1", cacheConfiguration); + cacheManager.createCache("cache-2", cacheConfiguration); + } } } diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/SimpleClusteredCacheByXmlTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/SimpleClusteredCacheByXmlTest.java similarity index 97% rename from clustered/client/src/test/java/org/ehcache/clustered/client/SimpleClusteredCacheByXmlTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/SimpleClusteredCacheByXmlTest.java index ddc50e979b..bfd5bf1102 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/SimpleClusteredCacheByXmlTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/SimpleClusteredCacheByXmlTest.java @@ -27,11 +27,11 @@ import org.junit.After; import org.junit.Test; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; import org.junit.Before; /** diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/TerracottaUriXmlTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/TerracottaUriXmlTest.java similarity index 89% rename from clustered/client/src/test/java/org/ehcache/clustered/client/TerracottaUriXmlTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/TerracottaUriXmlTest.java index b360c25ee6..75ad3a942f 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/TerracottaUriXmlTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/TerracottaUriXmlTest.java @@ -22,8 +22,9 @@ import org.ehcache.xml.exceptions.XmlConfigurationException; import org.junit.Test; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.assertThat; /** * TerracottaUriXmlTest @@ -43,7 +44,7 @@ public void testFailsWithInvalidClusterUri() { try { new XmlConfiguration(getClass().getResource("/configs/cluster-invalid-uri.xml")); } catch (XmlConfigurationException e) { - assertThat(e.getCause().getMessage(), containsString("not facet-valid with respect to pattern")); + assertThat(e.getCause().getMessage(), allOf(containsString("facet"), containsString("pattern"))); } } } diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/TestTimeSource.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/TestTimeSource.java similarity index 100% rename from clustered/client/src/test/java/org/ehcache/clustered/client/TestTimeSource.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/TestTimeSource.java diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/UnSupportedCombinationsWIthClusteredCacheTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/UnSupportedCombinationsWithClusteredCacheTest.java similarity index 87% rename from clustered/client/src/test/java/org/ehcache/clustered/client/UnSupportedCombinationsWIthClusteredCacheTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/UnSupportedCombinationsWithClusteredCacheTest.java index 09c4352f61..b4a5460c1d 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/UnSupportedCombinationsWIthClusteredCacheTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/UnSupportedCombinationsWithClusteredCacheTest.java @@ -44,14 +44,14 @@ import java.net.URI; import java.util.Map; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; /** * This class should be removed as and when following features are done. */ -public class UnSupportedCombinationsWIthClusteredCacheTest { +public class UnSupportedCombinationsWithClusteredCacheTest { @Before public void resetPassthroughServer() throws Exception { @@ -68,31 +68,27 @@ public void removePassthroughServer() throws Exception { } @Test - public void testClusteredCacheWithEventListeners() { - + public void testClusteredCacheWithSynchronousEventListeners() { CacheEventListenerConfigurationBuilder cacheEventListenerConfiguration = CacheEventListenerConfigurationBuilder - .newEventListenerConfiguration(new TestEventListener(), EventType.CREATED, EventType.UPDATED) // <1> - .unordered().asynchronous(); // <2> + .newEventListenerConfiguration(new TestEventListener(), EventType.CREATED, EventType.UPDATED) + .unordered().synchronous(); final CacheManagerBuilder clusteredCacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder() .with(ClusteringServiceConfigurationBuilder.cluster(URI.create("terracotta://localhost/my-application")) - .autoCreate()); - final PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(true); - - try { + .autoCreate(c -> c)); + try (PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(true)) { CacheConfiguration config = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 8, MemoryUnit.MB))) - .add(cacheEventListenerConfiguration) + .withService(cacheEventListenerConfiguration) .build(); cacheManager.createCache("test", config); fail("IllegalStateException expected"); } catch (IllegalStateException e){ - assertThat(e.getCause().getMessage(), is("CacheEventListener is not supported with clustered tiers")); + assertThat(e.getCause().getMessage(), is("Synchronous CacheEventListener is not supported with clustered tiers")); } - cacheManager.close(); } @Test @@ -102,19 +98,19 @@ public void testClusteredCacheWithXA() throws Exception { BitronixTransactionManager transactionManager = TransactionManagerServices.getTransactionManager(); - PersistentCacheManager persistentCacheManager = null; try { CacheManagerBuilder.newCacheManagerBuilder() .using(new LookupTransactionManagerProviderConfiguration(BitronixTransactionManagerLookup.class)) - .with(ClusteringServiceConfigurationBuilder.cluster(URI.create("terracotta://localhost/my-application")).autoCreate()) + .with(ClusteringServiceConfigurationBuilder.cluster(URI.create("terracotta://localhost/my-application")).autoCreate(c -> c)) .withCache("xaCache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 8, MemoryUnit.MB)) ) - .add(new XAStoreConfiguration("xaCache")) + .withService(new XAStoreConfiguration("xaCache")) .build() ) - .build(true); + .build(true).close(); + fail("Expected StateTransitionException"); } catch (StateTransitionException e) { assertThat(e.getCause().getCause().getMessage(), is("Unsupported resource type : interface org.ehcache.clustered.client.config.DedicatedClusteredResourcePool")); } diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/XmlConsistencyTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/XmlConsistencyTest.java similarity index 98% rename from clustered/client/src/test/java/org/ehcache/clustered/client/XmlConsistencyTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/XmlConsistencyTest.java index 10bdf14cea..fdcd802b9c 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/XmlConsistencyTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/XmlConsistencyTest.java @@ -25,9 +25,9 @@ import org.junit.Before; import org.junit.Test; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; /** * XmlConsistencyTest diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/XmlUnknownCacheTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/XmlUnknownCacheTest.java similarity index 74% rename from clustered/client/src/test/java/org/ehcache/clustered/client/XmlUnknownCacheTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/XmlUnknownCacheTest.java index 4c90d5496a..0b449d0dc9 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/XmlUnknownCacheTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/XmlUnknownCacheTest.java @@ -16,16 +16,15 @@ package org.ehcache.clustered.client; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.endsWith; +import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.fail; import org.ehcache.xml.XmlConfiguration; import org.ehcache.xml.exceptions.XmlConfigurationException; -import org.junit.Assert; import org.junit.Test; -import static org.hamcrest.Matchers.contains; -import static org.junit.Assert.fail; /** * @@ -36,7 +35,7 @@ public class XmlUnknownCacheTest { @Test public void testGetUnknownCache() { XmlConfiguration xmlConfiguration = new XmlConfiguration(this.getClass().getResource("/configs/unknown-cluster-cache.xml")); - Assert.assertThat(xmlConfiguration.getCacheConfigurations().keySet(),contains("unknownCache")); + assertThat(xmlConfiguration.getCacheConfigurations().keySet(),contains("unknownCache")); } @Test @@ -45,7 +44,7 @@ public void testGetUnknownCacheInvalidAttribute() { new XmlConfiguration(this.getClass().getResource("/configs/unknown-cluster-cache-invalid-attribute.xml")); fail("Expected XmlConfigurationException"); } catch(XmlConfigurationException xce) { - Assert.assertThat(xce.getCause().getMessage(), endsWith("Attribute 'unit' is not allowed to appear in element 'tc:clustered'.")); + assertThat(xce.getCause().getMessage(), allOf(containsString("unit"), containsString("not allowed"), containsString("clustered"))); } } @@ -55,7 +54,7 @@ public void testGetUnknownCacheInvalidElement() { new XmlConfiguration(this.getClass().getResource("/configs/unknown-cluster-cache-invalid-element.xml")); fail("Expected XmlConfigurationException"); } catch(XmlConfigurationException xce) { - Assert.assertThat(xce.getCause().getMessage(), endsWith("Element 'tc:clustered' must have no character or element information item [children], because the type's content type is empty.")); + assertThat(xce.getCause().getMessage(), allOf(containsString("haracter"), containsString("clustered"), containsString("empty"))); } } diff --git a/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/config/ClusteredConfigurationDerivationTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/config/ClusteredConfigurationDerivationTest.java new file mode 100644 index 0000000000..4dbf576386 --- /dev/null +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/config/ClusteredConfigurationDerivationTest.java @@ -0,0 +1,50 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.clustered.client.config; + +import org.ehcache.clustered.common.Consistency; +import org.ehcache.config.Configuration; +import org.ehcache.xml.XmlConfiguration; +import org.junit.Test; + +import java.net.URI; + +import static org.ehcache.core.spi.service.ServiceUtils.findSingletonAmongst; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; + +public class ClusteredConfigurationDerivationTest { + private static final String SIMPLE_CLUSTER_XML = "/configs/simple-cluster.xml"; + private static final URI UPDATED_CLUSTER_URI = URI.create("terracotta://updated.example.com:9540/cachemanager"); + + @Test + public void testUpdateUri() throws Exception { + final XmlConfiguration configuration = new XmlConfiguration(this.getClass().getResource(SIMPLE_CLUSTER_XML)); + + Configuration newServer = configuration.derive().updateServices(ClusteringServiceConfiguration.class, existing -> + existing.usingUri(UPDATED_CLUSTER_URI)).build(); + assertThat(findSingletonAmongst(ClusteringServiceConfiguration.class, newServer.getServiceCreationConfigurations()).getClusterUri(), is(UPDATED_CLUSTER_URI)); + } + + @Test + public void testAddConsistency() { + final XmlConfiguration configuration = new XmlConfiguration(this.getClass().getResource(SIMPLE_CLUSTER_XML)); + + Configuration newConsistency = configuration.derive().updateCache("simple-cache", existing -> + existing.withService(new ClusteredStoreConfiguration(Consistency.STRONG))).build(); + assertThat(findSingletonAmongst(ClusteredStoreConfiguration.class, newConsistency.getCacheConfigurations().get("simple-cache").getServiceConfigurations()).getConsistency(), is(Consistency.STRONG)); + } +} diff --git a/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/config/ClusteredStoreConfigurationTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/config/ClusteredStoreConfigurationTest.java new file mode 100644 index 0000000000..fdb39f8f40 --- /dev/null +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/config/ClusteredStoreConfigurationTest.java @@ -0,0 +1,38 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.clustered.client.config; + +import org.ehcache.clustered.common.Consistency; +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNot.not; +import static org.hamcrest.core.IsSame.sameInstance; +import static org.junit.Assert.fail; + +public class ClusteredStoreConfigurationTest { + + @Test + public void testDeriveDetachesProperly() { + ClusteredStoreConfiguration configuration = new ClusteredStoreConfiguration(Consistency.EVENTUAL); + ClusteredStoreConfiguration derived = configuration.build(configuration.derive()); + + assertThat(derived, is(not(sameInstance(configuration)))); + assertThat(derived.getConsistency(), is(configuration.getConsistency())); + } +} diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/config/ClusteringServiceConfigurationTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/config/ClusteringServiceConfigurationTest.java similarity index 78% rename from clustered/client/src/test/java/org/ehcache/clustered/client/config/ClusteringServiceConfigurationTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/config/ClusteringServiceConfigurationTest.java index 31c4fbc91c..7e9b4803eb 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/config/ClusteringServiceConfigurationTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/config/ClusteringServiceConfigurationTest.java @@ -29,9 +29,14 @@ import java.net.URI; import java.util.Collections; import java.util.Iterator; +import java.util.Map; +import java.util.Properties; +import static java.time.Duration.ofSeconds; +import static java.util.Collections.singletonMap; import static org.assertj.core.api.Assertions.assertThat; +@SuppressWarnings("deprecation") public class ClusteringServiceConfigurationTest { private static final URI DEFAULT_URI = URI.create("terracotta://localhost:9450"); @@ -165,4 +170,40 @@ public void testBuilderWithServers() { assertThat(new ClusteringServiceConfiguration(SERVERS, CACHE_MANAGER) .builder(CacheManagerBuilder.newCacheManagerBuilder())).isExactlyInstanceOf(CacheManagerBuilder.class); } + + @Test + public void testReadableString() { + ClusteringServiceConfiguration cfg; + + cfg = new ClusteringServiceConfiguration(SERVERS, CACHE_MANAGER); + assertThat(cfg.readableString()).isNotNull(); + + cfg = new ClusteringServiceConfiguration(DEFAULT_URI); + assertThat(cfg.readableString()).isNotNull(); + + cfg = new ClusteringServiceConfiguration(DEFAULT_URI, TimeoutsBuilder.timeouts().build()); + assertThat(cfg.readableString()).isNotNull(); + } + + @Test + public void testDerivedConfiguration() { + URI uri = URI.create("blah-blah"); + Timeouts timeouts = new Timeouts(ofSeconds(1), ofSeconds(2), ofSeconds(3)); + Map pools = singletonMap("default", new ServerSideConfiguration.Pool(42L, "resource")); + ServerSideConfiguration serverSideConfiguration = new ServerSideConfiguration("default", pools); + Properties properties = new Properties(); + properties.setProperty("foo", "bar"); + + ClusteringServiceConfiguration configuration = new ClusteringServiceConfiguration(uri, timeouts, true, serverSideConfiguration, properties); + + + ClusteringServiceConfiguration derived = configuration.build(configuration.derive()); + + assertThat(derived).isNotSameAs(configuration); + assertThat(derived.getClusterUri()).isEqualTo(uri); + assertThat(derived.getTimeouts()).isEqualTo(timeouts); + assertThat(derived.getServerConfiguration().getDefaultServerResource()).isEqualTo("default"); + assertThat(derived.getServerConfiguration().getResourcePools()).isEqualTo(pools); + assertThat(derived.getProperties()).isEqualTo(properties); + } } diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/config/builders/ClusteredResourcePoolBuilderTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/config/builders/ClusteredResourcePoolBuilderTest.java similarity index 98% rename from clustered/client/src/test/java/org/ehcache/clustered/client/config/builders/ClusteredResourcePoolBuilderTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/config/builders/ClusteredResourcePoolBuilderTest.java index 84b4715f0b..5c0fd438fd 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/config/builders/ClusteredResourcePoolBuilderTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/config/builders/ClusteredResourcePoolBuilderTest.java @@ -23,11 +23,10 @@ import org.hamcrest.Matchers; import org.junit.Test; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; - import org.ehcache.clustered.client.config.DedicatedClusteredResourcePool; public class ClusteredResourcePoolBuilderTest { diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/config/builders/TimeoutsBuilderTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/config/builders/TimeoutsBuilderTest.java similarity index 100% rename from clustered/client/src/test/java/org/ehcache/clustered/client/config/builders/TimeoutsBuilderTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/config/builders/TimeoutsBuilderTest.java diff --git a/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/docs/ConfigurationDerivation.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/docs/ConfigurationDerivation.java new file mode 100644 index 0000000000..44a23f9d3b --- /dev/null +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/docs/ConfigurationDerivation.java @@ -0,0 +1,102 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.clustered.client.docs; + +import org.ehcache.clustered.client.config.ClusteredResourceType; +import org.ehcache.clustered.client.config.ClusteredStoreConfiguration; +import org.ehcache.clustered.client.config.ClusteringServiceConfiguration; +import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder; +import org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder; +import org.ehcache.clustered.common.Consistency; +import org.ehcache.config.Configuration; +import org.ehcache.config.FluentConfigurationBuilder; +import org.ehcache.config.ResourcePool; +import org.ehcache.config.builders.CacheConfigurationBuilder; +import org.ehcache.config.builders.ConfigurationBuilder; +import org.ehcache.config.builders.ResourcePoolsBuilder; +import org.ehcache.config.units.MemoryUnit; +import org.ehcache.core.internal.resilience.ThrowingResilienceStrategy; +import org.ehcache.core.spi.service.ServiceUtils; +import org.ehcache.impl.config.event.DefaultCacheEventListenerConfiguration; +import org.ehcache.impl.config.resilience.DefaultResilienceStrategyConfiguration; +import org.hamcrest.core.Is; +import org.hamcrest.core.IsCollectionContaining; +import org.hamcrest.core.IsInstanceOf; +import org.hamcrest.core.IsNot; +import org.junit.Test; + +import java.net.URI; +import java.util.List; +import java.util.stream.Collectors; + +import static org.hamcrest.MatcherAssert.assertThat; + +public class ConfigurationDerivation { + + @Test + public void removingServices() { + Configuration configuration = ConfigurationBuilder.newConfigurationBuilder() + .withService(ClusteringServiceConfigurationBuilder.cluster(URI.create("terracotta://example.com/myCacheManager"))) + .withCache("cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, + ResourcePoolsBuilder.heap(1000).with(ClusteredResourcePoolBuilder.clusteredDedicated("offheap", 128, MemoryUnit.MB)))) + .build(); + + //tag::removeService[] + Configuration withoutClustering = configuration.derive() + .updateCaches(cache -> cache // <1> + .withoutServices(ClusteredStoreConfiguration.class) // <2> + .updateResourcePools(existing -> { + ResourcePoolsBuilder poolsBuilder = ResourcePoolsBuilder.newResourcePoolsBuilder(); // <3> + for (ResourcePool pool : existing.getResourceTypeSet().stream() // <4> + .filter(p -> !(p instanceof ClusteredResourceType)) // <5> + .map(existing::getPoolForResource) + .toArray(ResourcePool[]::new)) { + poolsBuilder = poolsBuilder.with(pool); // <6> + } + return poolsBuilder.build(); + })) + .withoutServices(ClusteringServiceConfiguration.class) // <7> + .build(); + //end::removeService[] + + assertThat(withoutClustering.getServiceCreationConfigurations(), IsNot.not(IsCollectionContaining.hasItem( + IsInstanceOf.instanceOf(ClusteringServiceConfiguration.class)))); + } + + @Test + public void updateService() { + Configuration configuration = ConfigurationBuilder.newConfigurationBuilder() + .withCache("cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(10)) + .withService(new ClusteredStoreConfiguration(Consistency.STRONG))) + .build(); + + //tag::updateService[] + Configuration changedConsistency = configuration.derive() + .updateCache("cache", cache -> cache.updateServices( + ClusteredStoreConfiguration.class, + existing -> Consistency.EVENTUAL) + ) + .build(); + //end::updateService[] + + assertThat(ServiceUtils.findSingletonAmongst(ClusteredStoreConfiguration.class, + configuration.getCacheConfigurations().get("cache").getServiceConfigurations()).getConsistency(), Is.is(Consistency.STRONG)); + + assertThat(ServiceUtils.findSingletonAmongst(ClusteredStoreConfiguration.class, + changedConsistency.getCacheConfigurations().get("cache").getServiceConfigurations()).getConsistency(), Is.is(Consistency.EVENTUAL)); + } + +} diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/docs/GettingStarted.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/docs/GettingStarted.java similarity index 74% rename from clustered/client/src/test/java/org/ehcache/clustered/client/docs/GettingStarted.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/docs/GettingStarted.java index 5ea95fae52..7f7ef7b0ee 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/docs/GettingStarted.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/docs/GettingStarted.java @@ -61,7 +61,7 @@ public void clusteredCacheManagerExample() throws Exception { CacheManagerBuilder clusteredCacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder() // <1> .with(ClusteringServiceConfigurationBuilder.cluster(URI.create("terracotta://localhost/my-application")) // <2> - .autoCreate()); // <3> + .autoCreateOnReconnect(c -> c)); // <3> PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(true); // <4> cacheManager.close(); // <5> @@ -73,10 +73,10 @@ public void clusteredCacheManagerWithServerSideConfigExample() throws Exception // tag::clusteredCacheManagerWithServerSideConfigExample[] CacheManagerBuilder clusteredCacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder() - .with(ClusteringServiceConfigurationBuilder.cluster(URI.create("terracotta://localhost/my-application")).autoCreate() + .with(ClusteringServiceConfigurationBuilder.cluster(URI.create("terracotta://localhost/my-application")).autoCreateOnReconnect(server -> server .defaultServerResource("primary-server-resource") // <1> .resourcePool("resource-pool-a", 8, MemoryUnit.MB, "secondary-server-resource") // <2> - .resourcePool("resource-pool-b", 10, MemoryUnit.MB)) // <3> + .resourcePool("resource-pool-b", 10, MemoryUnit.MB))) // <3> .withCache("clustered-cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, // <4> ResourcePoolsBuilder.newResourcePoolsBuilder() .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 8, MemoryUnit.MB)))) // <5> @@ -98,9 +98,8 @@ public void clusteredCacheManagerWithDynamicallyAddedCacheExample() throws Excep CacheManagerBuilder clusteredCacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder() .with(ClusteringServiceConfigurationBuilder.cluster(URI.create("terracotta://localhost/my-application")) - .autoCreate() - .defaultServerResource("primary-server-resource") - .resourcePool("resource-pool-a", 8, MemoryUnit.MB)); + .autoCreateOnReconnect(server -> server.defaultServerResource("primary-server-resource") + .resourcePool("resource-pool-a", 8, MemoryUnit.MB))); PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(false); cacheManager.init(); @@ -122,9 +121,8 @@ public void explicitConsistencyConfiguration() throws Exception { CacheManagerBuilder clusteredCacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder() .with(ClusteringServiceConfigurationBuilder.cluster(URI.create("terracotta://localhost/my-application")) - .autoCreate() - .defaultServerResource("primary-server-resource") - .resourcePool("resource-pool-a", 8, MemoryUnit.MB)); + .autoCreateOnReconnect(server -> server.defaultServerResource("primary-server-resource") + .resourcePool("resource-pool-a", 8, MemoryUnit.MB))); PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(false); cacheManager.init(); @@ -133,7 +131,7 @@ public void explicitConsistencyConfiguration() throws Exception { CacheConfiguration config = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 2, MemoryUnit.MB))) - .add(ClusteredStoreConfigurationBuilder.withConsistency(Consistency.STRONG)) // <1> + .withService(ClusteredStoreConfigurationBuilder.withConsistency(Consistency.STRONG)) // <1> .build(); Cache cache = cacheManager.createCache("clustered-cache", config); @@ -150,9 +148,8 @@ public void clusteredCacheTieredExample() throws Exception { CacheManagerBuilder clusteredCacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder() .with(ClusteringServiceConfigurationBuilder.cluster(URI.create("terracotta://localhost/my-application")) - .autoCreate() - .defaultServerResource("primary-server-resource") - .resourcePool("resource-pool-a", 8, MemoryUnit.MB)); + .autoCreateOnReconnect(server -> server.defaultServerResource("primary-server-resource") + .resourcePool("resource-pool-a", 8, MemoryUnit.MB))); PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(false); cacheManager.init(); @@ -162,7 +159,7 @@ public void clusteredCacheTieredExample() throws Exception { ResourcePoolsBuilder.newResourcePoolsBuilder() .heap(2, MemoryUnit.MB) // <1> .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 8, MemoryUnit.MB))) // <2> - .add(ClusteredStoreConfigurationBuilder.withConsistency(Consistency.STRONG)) + .withService(ClusteredStoreConfigurationBuilder.withConsistency(Consistency.STRONG)) .build(); Cache cache = cacheManager.createCache("clustered-cache-tiered", config); @@ -179,29 +176,38 @@ public void clusteredCacheManagerLifecycleExamples() throws Exception { // tag::clusteredCacheManagerLifecycle[] CacheManagerBuilder autoCreate = CacheManagerBuilder.newCacheManagerBuilder() .with(ClusteringServiceConfigurationBuilder.cluster(URI.create("terracotta://localhost/my-application")) - .autoCreate() // <1> - .resourcePool("resource-pool", 8, MemoryUnit.MB, "primary-server-resource")) + .autoCreate(server -> server // <1> + .resourcePool("resource-pool", 8, MemoryUnit.MB, "primary-server-resource"))) + .withCache("clustered-cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, + ResourcePoolsBuilder.newResourcePoolsBuilder() + .with(ClusteredResourcePoolBuilder.clusteredShared("resource-pool")))); + + CacheManagerBuilder autoCreateOnReconnect = CacheManagerBuilder.newCacheManagerBuilder() + .with(ClusteringServiceConfigurationBuilder.cluster(URI.create("terracotta://localhost/my-application")) + .autoCreateOnReconnect(server -> server // <2> + .resourcePool("resource-pool", 8, MemoryUnit.MB, "primary-server-resource"))) .withCache("clustered-cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .with(ClusteredResourcePoolBuilder.clusteredShared("resource-pool")))); CacheManagerBuilder expecting = CacheManagerBuilder.newCacheManagerBuilder() .with(ClusteringServiceConfigurationBuilder.cluster(URI.create("terracotta://localhost/my-application")) - .expecting() // <2> - .resourcePool("resource-pool", 8, MemoryUnit.MB, "primary-server-resource")) + .expecting(server -> server // <3> + .resourcePool("resource-pool", 8, MemoryUnit.MB, "primary-server-resource"))) .withCache("clustered-cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .with(ClusteredResourcePoolBuilder.clusteredShared("resource-pool")))); CacheManagerBuilder configless = CacheManagerBuilder.newCacheManagerBuilder() .with(ClusteringServiceConfigurationBuilder.cluster(URI.create("terracotta://localhost/my-application"))) - // <3> + // <4> .withCache("clustered-cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .with(ClusteredResourcePoolBuilder.clusteredShared("resource-pool")))); // end::clusteredCacheManagerLifecycle[] autoCreate.build(true).close(); + autoCreateOnReconnect.build(true).close(); expecting.build(true).close(); configless.build(true).close(); } @@ -218,40 +224,42 @@ public void unknownClusteredCacheExample() CacheManagerBuilder cacheManagerBuilderAutoCreate = CacheManagerBuilder.newCacheManagerBuilder() .with(ClusteringServiceConfigurationBuilder.cluster(URI.create("terracotta://localhost/my-application")) - .autoCreate() // <1> - .resourcePool("resource-pool", 8, MemoryUnit.MB, "primary-server-resource")); + .autoCreateOnReconnect(server -> server // <1> + .resourcePool("resource-pool", 8, MemoryUnit.MB, "primary-server-resource"))); PersistentCacheManager cacheManager1 = cacheManagerBuilderAutoCreate.build(false); cacheManager1.init(); + try { + CacheConfiguration cacheConfigDedicated = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, + ResourcePoolsBuilder.newResourcePoolsBuilder() + .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 8, MemoryUnit.MB))) // <2> + .withService(ClusteredStoreConfigurationBuilder.withConsistency(Consistency.STRONG)) + .build(); - CacheConfiguration cacheConfigDedicated = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, - ResourcePoolsBuilder.newResourcePoolsBuilder() - .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 8, MemoryUnit.MB))) // <2> - .add(ClusteredStoreConfigurationBuilder.withConsistency(Consistency.STRONG)) - .build(); - - Cache cacheDedicated = cacheManager1.createCache("my-dedicated-cache", cacheConfigDedicated); // <3> - - CacheManagerBuilder cacheManagerBuilderExpecting = CacheManagerBuilder.newCacheManagerBuilder() - .with(ClusteringServiceConfigurationBuilder.cluster(URI.create("terracotta://localhost/my-application")) - .expecting() // <4> - .resourcePool("resource-pool", 8, MemoryUnit.MB, "primary-server-resource")); - - PersistentCacheManager cacheManager2 = cacheManagerBuilderExpecting.build(false); - cacheManager2.init(); + Cache cacheDedicated = cacheManager1.createCache("my-dedicated-cache", cacheConfigDedicated); // <3> - CacheConfiguration cacheConfigUnspecified = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, - ResourcePoolsBuilder.newResourcePoolsBuilder() - .with(ClusteredResourcePoolBuilder.clustered())) // <5> - .add(ClusteredStoreConfigurationBuilder.withConsistency(Consistency.STRONG)) - .build(); + CacheManagerBuilder cacheManagerBuilderExpecting = CacheManagerBuilder.newCacheManagerBuilder() + .with(ClusteringServiceConfigurationBuilder.cluster(URI.create("terracotta://localhost/my-application")) + .expecting(server -> server // <4> + .resourcePool("resource-pool", 8, MemoryUnit.MB, "primary-server-resource"))); - Cache cacheUnspecified = cacheManager2.createCache("my-dedicated-cache", cacheConfigUnspecified); // <6> + PersistentCacheManager cacheManager2 = cacheManagerBuilderExpecting.build(false); + cacheManager2.init(); + try { + CacheConfiguration cacheConfigUnspecified = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, + ResourcePoolsBuilder.newResourcePoolsBuilder() + .with(ClusteredResourcePoolBuilder.clustered())) // <5> + .withService(ClusteredStoreConfigurationBuilder.withConsistency(Consistency.STRONG)) + .build(); + Cache cacheUnspecified = cacheManager2.createCache("my-dedicated-cache", cacheConfigUnspecified); // <6> + } finally { + cacheManager2.close(); + } + } finally { + cacheManager1.close(); + } // end::unspecifiedClusteredCacheExample[] - - cacheManager1.close(); - cacheManager2.close(); - } + } } diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/docs/Resilience.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/docs/Resilience.java similarity index 98% rename from clustered/client/src/test/java/org/ehcache/clustered/client/docs/Resilience.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/docs/Resilience.java index 8e6e86dd3f..fd15cf8436 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/docs/Resilience.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/docs/Resilience.java @@ -56,7 +56,7 @@ public void clusteredCacheManagerExample() throws Exception { .read(Duration.ofSeconds(10)) // <2> .write(Timeouts.DEFAULT_OPERATION_TIMEOUT) // <3> .connection(Timeouts.INFINITE_TIMEOUT)) // <4> - .autoCreate()); + .autoCreate(c -> c)); // end::timeoutsExample[] } } diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/docs/Tiering.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/docs/Tiering.java similarity index 98% rename from clustered/client/src/test/java/org/ehcache/clustered/client/docs/Tiering.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/docs/Tiering.java index 561836d52f..7f90b671dc 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/docs/Tiering.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/docs/Tiering.java @@ -66,7 +66,7 @@ public void testSingleTier() { public void threeTiersCacheManager() throws Exception { // tag::threeTiersCacheManager[] PersistentCacheManager persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder() - .with(cluster(CLUSTER_URI).autoCreate()) // <1> + .with(cluster(CLUSTER_URI).autoCreate(c -> c)) // <1> .withCache("threeTierCache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/ClusterTierManagerClientEntityFactoryTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/ClusterTierManagerClientEntityFactoryTest.java similarity index 92% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/ClusterTierManagerClientEntityFactoryTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/ClusterTierManagerClientEntityFactoryTest.java index 89d4fc6b52..d82db588d0 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/ClusterTierManagerClientEntityFactoryTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/ClusterTierManagerClientEntityFactoryTest.java @@ -25,8 +25,8 @@ import org.mockito.MockitoAnnotations; import org.terracotta.connection.Connection; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.isNull; @@ -67,7 +67,7 @@ public void testCreate() throws Exception { addMockUnlockedLock(connection, "VoltronReadWriteLock-ClusterTierManagerClientEntityFactory-AccessLock-test"); - ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(connection); + ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(connection, Runnable::run); factory.create("test", null); verify(entityRef).create(isA(ClusterTierManagerConfiguration.class)); verifyNoMoreInteractions(entityRef); @@ -79,7 +79,7 @@ public void testCreateBadConfig() throws Exception { when(getEntityRef(ClusterTierManagerClientEntity.class)).thenReturn(entityRef); addMockUnlockedLock(connection, "VoltronReadWriteLock-ClusterTierManagerClientEntityFactory-AccessLock-test"); - ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(connection); + ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(connection, Runnable::run); try { factory.create("test", null); fail("Expecting ClusterTierManagerCreationException"); @@ -95,7 +95,7 @@ public void testCreateWhenExisting() throws Exception { addMockUnlockedLock(connection, "VoltronReadWriteLock-ClusterTierManagerClientEntityFactory-AccessLock-test"); - ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(connection); + ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(connection, Runnable::run); try { factory.create("test", null); fail("Expected EntityAlreadyExistsException"); @@ -111,7 +111,7 @@ public void testRetrieve() throws Exception { addMockUnlockedLock(connection, "VoltronReadWriteLock-ClusterTierManagerClientEntityFactory-AccessLock-test"); - ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(connection); + ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(connection, Runnable::run); assertThat(factory.retrieve("test", null), is(entity)); verify(entity).validate(isNull()); verify(entity, never()).close(); @@ -125,9 +125,9 @@ public void testRetrieveFailedValidate() throws Exception { addMockUnlockedLock(connection, "VoltronReadWriteLock-ClusterTierManagerClientEntityFactory-AccessLock-test"); - ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(connection); + ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(connection, Runnable::run); try { - factory.retrieve("test", null); + factory.retrieve("test", null).close(); fail("Expecting IllegalArgumentException"); } catch (IllegalArgumentException e) { // expected @@ -145,9 +145,9 @@ public void testRetrieveWhenNotExisting() throws Exception { addMockUnlockedLock(connection, "VoltronReadWriteLock-ClusterTierManagerClientEntityFactory-AccessLock-test"); - ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(connection); + ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(connection, Runnable::run); try { - factory.retrieve("test", null); + factory.retrieve("test", null).close(); fail("Expected EntityNotFoundException"); } catch (EntityNotFoundException e) { //expected @@ -163,7 +163,7 @@ public void testDestroy() throws Exception { addMockUnlockedLock(connection, "VoltronReadWriteLock-ClusterTierManagerClientEntityFactory-AccessLock-test"); - ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(connection); + ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(connection, Runnable::run); factory.destroy("test"); verify(entityRef).destroy(); } @@ -177,7 +177,7 @@ public void testDestroyWhenNotExisting() throws Exception { addMockUnlockedLock(connection, "VoltronReadWriteLock-ClusterTierManagerClientEntityFactory-AccessLock-test"); - ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(connection); + ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(connection, Runnable::run); factory.destroy("test"); verify(entityRef).destroy(); } diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/MockConnectionService.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/MockConnectionService.java similarity index 100% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/MockConnectionService.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/MockConnectionService.java diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/UnitTestConnectionService.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/UnitTestConnectionService.java similarity index 88% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/UnitTestConnectionService.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/UnitTestConnectionService.java index f9dc86734c..c6b66f3f6d 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/UnitTestConnectionService.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/UnitTestConnectionService.java @@ -35,6 +35,7 @@ import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.stream.Collectors; import org.ehcache.clustered.client.internal.lock.VoltronReadWriteLockEntityClientService; import org.ehcache.clustered.client.internal.store.ClusterTierClientEntityService; @@ -42,6 +43,7 @@ import org.ehcache.clustered.server.ClusterTierManagerServerEntityService; import org.ehcache.clustered.server.store.ClusterTierServerEntityService; +import org.ehcache.impl.internal.concurrent.ConcurrentHashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.terracotta.connection.Connection; @@ -68,6 +70,8 @@ import org.terracotta.passthrough.PassthroughServer; import org.terracotta.passthrough.PassthroughServerRegistry; +import static java.util.stream.Collectors.toCollection; +import static java.util.stream.Collectors.toList; import static org.mockito.Mockito.mock; @@ -127,8 +131,8 @@ public class UnitTestConnectionService implements ConnectionService { private static final Logger LOGGER = LoggerFactory.getLogger(UnitTestConnectionService.class); - private static final Map STRIPES = new HashMap<>(); - private static final Map SERVERS = new HashMap<>(); + private static final Map STRIPES = new ConcurrentHashMap<>(); + private static final Map SERVERS = new ConcurrentHashMap<>(); private static final String PASSTHROUGH = "passthrough"; @@ -141,27 +145,20 @@ public class UnitTestConnectionService implements ConnectionService { * @param server the {@code PassthroughServer} instance to use for connections to {@code uri} */ public static void add(URI uri, PassthroughServer server) { - URI keyURI = createKey(uri); - if (SERVERS.containsKey(keyURI)) { - throw new AssertionError("Server at " + uri + " already provided; use remove() to remove"); - } - - SERVERS.put(keyURI, new ServerDescriptor(server)); - // TODO rework that better - server.registerAsynchronousServerCrasher(mock(IAsynchronousServerCrasher.class)); - server.start(true, false); - server.addPermanentEntities(); - LOGGER.info("Started PassthroughServer at {}", keyURI); + SERVERS.compute(createKey(uri), (u, existing) -> { + if (existing == null) { + server.registerAsynchronousServerCrasher(mock(IAsynchronousServerCrasher.class)); + server.start(true, false); + LOGGER.info("Started PassthroughServer at {}", u); + return new ServerDescriptor(server); + } else { + throw new AssertionError("Server at " + u + " already provided; use remove() to remove"); + } + }); } public static void addServerToStripe(String stripeName, PassthroughServer server) { - - if (STRIPES.get(stripeName) == null) { - StripeDescriptor stripeDescriptor = new StripeDescriptor(); - STRIPES.put(stripeName, stripeDescriptor); - } - - STRIPES.get(stripeName).addServer(server); + STRIPES.computeIfAbsent(stripeName, k -> new StripeDescriptor()).addServer(server); } public static void removeStripe(String stripeName) { @@ -222,35 +219,34 @@ public static PassthroughServer remove(URI uri) { try { LOGGER.warn("Force close {}", formatConnectionId(connection)); connection.close(); - } catch (AssertionError | IOException e) { - // Ignored -- https://github.com/Terracotta-OSS/terracotta-apis/issues/102 + } catch (IOException e) { + throw new AssertionError(e); } } //open destroy connection. You need to make sure connection doesn't have any entities associated with it. - PassthroughConnection connection = serverDescriptor.server.connectNewClient("destroy-connection"); - - // destroy in reverse order of the creation to keep coherence - List> keys = new ArrayList<>(serverDescriptor.knownEntities.keySet()); - Collections.reverse(keys); - for(Class type : keys) { - Object[] args = serverDescriptor.knownEntities.get(type); - - Long version = (Long) args[0]; - String stringArg = (String) args[1]; - - try { - EntityRef entityRef = connection.getEntityRef(type, version, stringArg); - entityRef.destroy(); - } catch (EntityNotProvidedException ex) { - LOGGER.error("Entity destroy failed (not provided???): ", ex); - } catch (EntityNotFoundException ex) { - LOGGER.error("Entity destroy failed: ", ex); - } catch (PermanentEntityException ex) { - LOGGER.error("Entity destroy failed (permanent???): ", ex); + try (PassthroughConnection connection = serverDescriptor.server.connectNewClient("destroy-connection")) { + // destroy in reverse order of the creation to keep coherence + List> keys = new ArrayList<>(serverDescriptor.knownEntities.keySet()); + Collections.reverse(keys); + for (Class type : keys) { + Object[] args = serverDescriptor.knownEntities.get(type); + + Long version = (Long) args[0]; + String stringArg = (String) args[1]; + + try { + EntityRef entityRef = connection.getEntityRef(type, version, stringArg); + entityRef.destroy(); + } catch (EntityNotProvidedException ex) { + LOGGER.error("Entity destroy failed (not provided???): ", ex); + } catch (EntityNotFoundException ex) { + LOGGER.error("Entity destroy failed: ", ex); + } catch (PermanentEntityException ex) { + LOGGER.error("Entity destroy failed (permanent???): ", ex); + } } } - serverDescriptor.server.stop(); LOGGER.info("Stopped PassthroughServer at {}", keyURI); return serverDescriptor.server; @@ -366,9 +362,7 @@ public PassthroughServer build() { newServer.registerClientEntityService(service); } - if (!this.resources.getResource().isEmpty()) { - newServer.registerExtendedConfiguration(new OffHeapResourcesProvider(this.resources)); - } + newServer.registerExtendedConfiguration(new OffHeapResourcesProvider(this.resources)); for (Map.Entry entry : serviceProviders.entrySet()) { newServer.registerServiceProvider(entry.getKey(), entry.getValue()); @@ -380,25 +374,25 @@ public PassthroughServer build() { public static Collection getConnectionProperties(URI uri) { ServerDescriptor serverDescriptor = SERVERS.get(createKey(uri)); - if (serverDescriptor != null) { - return serverDescriptor.getConnections().values(); - } else { + if (serverDescriptor == null) { return Collections.emptyList(); + } else { + return serverDescriptor.getConnections().values(); } } public static Collection getConnections(URI uri) { ServerDescriptor serverDescriptor = SERVERS.get(createKey(uri)); - return serverDescriptor.getConnections().keySet(); + return serverDescriptor.getConnections().keySet().stream().map(c -> proxyConnection(serverDescriptor, c)).collect(toList()); } @Override public boolean handlesURI(URI uri) { if (PASSTHROUGH.equals(uri.getScheme())) { return STRIPES.containsKey(uri.getAuthority()); + } else { + return SERVERS.containsKey(checkURI(uri)); } - checkURI(uri); - return SERVERS.containsKey(uri); } @Override @@ -410,7 +404,10 @@ public boolean handlesConnectionType(String s) { public Connection connect(URI uri, Properties properties) throws ConnectionException { if (PASSTHROUGH.equals(uri.getScheme())) { - if(STRIPES.containsKey(uri.getAuthority())) { + StripeDescriptor stripeDescriptor = STRIPES.get(uri.getAuthority()); + if (stripeDescriptor == null) { + throw new IllegalArgumentException("UnitTestConnectionService failed to find stripe" + uri.getAuthority()); + } else { String serverName = uri.getHost(); PassthroughServer server = PassthroughServerRegistry.getSharedInstance().getServerForName(serverName); if(null != server) { @@ -423,8 +420,6 @@ public Connection connect(URI uri, Properties properties) throws ConnectionExcep STRIPES.get(uri.getAuthority()).add(connection); return connection; } - } else { - throw new IllegalArgumentException("UnitTestConnectionService failed to find stripe" + uri.getAuthority()); } } @@ -447,6 +442,10 @@ public Connection connect(URI uri, Properties properties) throws ConnectionExcep /* * Uses a Proxy around Connection so closed connections can be removed from the ServerDescriptor. */ + return proxyConnection(serverDescriptor, connection); + } + + private static Connection proxyConnection(ServerDescriptor serverDescriptor, Connection connection) { return (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), new Class[] { Connection.class }, new ConnectionInvocationHandler(serverDescriptor, connection)); @@ -467,8 +466,10 @@ public Connection connect(Iterable iterable, Properties prope * * @see #checkURI(URI) */ - private static void checkURI(URI requestURI) throws IllegalArgumentException { - if (!requestURI.equals(createKey(requestURI))) { + private static URI checkURI(URI requestURI) throws IllegalArgumentException { + if (requestURI.equals(createKey(requestURI))) { + return requestURI; + } else { throw new IllegalArgumentException("Connection URI contains user-info, path, query, and/or fragment"); } } diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/config/ClusteredResourcePoolImplTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/config/ClusteredResourcePoolImplTest.java similarity index 100% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/config/ClusteredResourcePoolImplTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/config/ClusteredResourcePoolImplTest.java diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/config/DedicatedClusteredResourcePoolImplTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/config/DedicatedClusteredResourcePoolImplTest.java similarity index 100% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/config/DedicatedClusteredResourcePoolImplTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/config/DedicatedClusteredResourcePoolImplTest.java diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/config/SharedClusteredResourcePoolImplTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/config/SharedClusteredResourcePoolImplTest.java similarity index 100% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/config/SharedClusteredResourcePoolImplTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/config/SharedClusteredResourcePoolImplTest.java diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/config/xml/ClusteredCacheConfigurationParserIT.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/config/xml/ClusteredCacheConfigurationParserIT.java similarity index 65% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/config/xml/ClusteredCacheConfigurationParserIT.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/config/xml/ClusteredCacheConfigurationParserIT.java index f501af949d..3b90be498b 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/config/xml/ClusteredCacheConfigurationParserIT.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/config/xml/ClusteredCacheConfigurationParserIT.java @@ -17,16 +17,12 @@ import org.ehcache.config.Configuration; import org.ehcache.xml.XmlConfiguration; -import org.ehcache.xml.XmlConfigurationTest; import org.junit.Test; -import org.xmlunit.builder.Input; -import org.xmlunit.diff.DefaultNodeMatcher; -import org.xmlunit.diff.ElementSelectors; import java.net.URL; -import static org.junit.Assert.assertThat; -import static org.xmlunit.matchers.CompareMatcher.isSimilarTo; +import static org.ehcache.xml.XmlConfigurationMatchers.isSameConfigurationAs; +import static org.hamcrest.MatcherAssert.assertThat; /** * ClusteredCacheConfigurationParserIT @@ -35,11 +31,9 @@ public class ClusteredCacheConfigurationParserIT { @Test public void testClusteredCacheXmlTranslationToString() { - URL resource = XmlConfigurationTest.class.getResource("/configs/clustered-cache.xml"); + URL resource = ClusteredCacheConfigurationParserIT.class.getResource("/configs/clustered-cache.xml"); Configuration config = new XmlConfiguration(resource); XmlConfiguration xmlConfig = new XmlConfiguration(config); - assertThat(xmlConfig.toString(), isSimilarTo(Input.from(resource)).ignoreComments() - .ignoreWhitespace() - .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndAllAttributes))); + assertThat(xmlConfig.toString(), isSameConfigurationAs(resource)); } } diff --git a/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/config/xml/ClusteredResourceConfigurationParserTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/config/xml/ClusteredResourceConfigurationParserTest.java new file mode 100644 index 0000000000..70a0e69e9a --- /dev/null +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/config/xml/ClusteredResourceConfigurationParserTest.java @@ -0,0 +1,122 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.clustered.client.internal.config.xml; + +import org.ehcache.clustered.client.config.DedicatedClusteredResourcePool; +import org.ehcache.clustered.client.config.SharedClusteredResourcePool; +import org.ehcache.clustered.client.internal.config.ClusteredResourcePoolImpl; +import org.ehcache.clustered.client.internal.config.DedicatedClusteredResourcePoolImpl; +import org.ehcache.clustered.client.internal.config.SharedClusteredResourcePoolImpl; +import org.ehcache.config.units.MemoryUnit; +import org.junit.Test; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; +import java.io.StringReader; + +import static org.ehcache.xml.XmlConfigurationMatchers.isSameConfigurationAs; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +/** + * ClusteredResourceConfigurationParserTest + */ +public class ClusteredResourceConfigurationParserTest { + + @Test + public void testClusteredSharedUsingProperties() throws ParserConfigurationException, IOException, SAXException { + String property = ClusteredResourceConfigurationParserTest.class.getName() + ":sharing"; + String inputString = ""; + + ClusteredResourceConfigurationParser parser = new ClusteredResourceConfigurationParser(); + + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + documentBuilderFactory.setNamespaceAware(true); + Element node = documentBuilderFactory.newDocumentBuilder() + .parse(new InputSource(new StringReader(inputString))).getDocumentElement(); + + System.setProperty(property, "foobar"); + try { + SharedClusteredResourcePool configuration = (SharedClusteredResourcePool) parser.parseResourceConfig(node); + + assertThat(configuration.getSharedResourcePool(), is("foobar")); + } finally { + System.clearProperty(property); + } + } + + @Test + public void testClusteredDedicatedUsingProperties() throws ParserConfigurationException, IOException, SAXException { + String fromProperty = ClusteredResourceConfigurationParserTest.class.getName() + ":from"; + String sizeProperty = ClusteredResourceConfigurationParserTest.class.getName() + ":size"; + String inputString = "" + + "${" + sizeProperty + "}"; + + ClusteredResourceConfigurationParser parser = new ClusteredResourceConfigurationParser(); + + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + documentBuilderFactory.setNamespaceAware(true); + Element node = documentBuilderFactory.newDocumentBuilder() + .parse(new InputSource(new StringReader(inputString))).getDocumentElement(); + + System.setProperty(fromProperty, "foobar"); + System.setProperty(sizeProperty, "1024"); + try { + DedicatedClusteredResourcePool configuration = (DedicatedClusteredResourcePool) parser.parseResourceConfig(node); + + assertThat(configuration.getFromResource(), is("foobar")); + assertThat(configuration.getSize(), is(1024L)); + } finally { + System.clearProperty(fromProperty); + System.clearProperty(sizeProperty); + } + } + + @Test + public void testTranslateClusteredResourcePoolConfiguration() { + ClusteredResourceConfigurationParser configTranslator = new ClusteredResourceConfigurationParser(); + ClusteredResourcePoolImpl clusteredResourcePool = new ClusteredResourcePoolImpl(); + Node retElement = configTranslator.unparseResourcePool(clusteredResourcePool); + String inputString = ""; + assertThat(retElement, isSameConfigurationAs(inputString)); + } + + @Test + public void testTranslateDedicatedResourcePoolConfiguration() { + ClusteredResourceConfigurationParser configTranslator = new ClusteredResourceConfigurationParser(); + DedicatedClusteredResourcePoolImpl dedicatedClusteredResourcePool = new DedicatedClusteredResourcePoolImpl("my-from", 12, MemoryUnit.GB); + Node retElement = configTranslator.unparseResourcePool(dedicatedClusteredResourcePool); + String inputString = "12"; + assertThat(retElement, isSameConfigurationAs(inputString)); + } + + @Test + public void testTranslateSharedResourcePoolConfiguration() { + ClusteredResourceConfigurationParser configTranslator = new ClusteredResourceConfigurationParser(); + SharedClusteredResourcePoolImpl sharedResourcePool = new SharedClusteredResourcePoolImpl("shared-pool"); + Node retElement = configTranslator.unparseResourcePool(sharedResourcePool); + String inputString = ""; + assertThat(retElement, isSameConfigurationAs(inputString)); + } + +} diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/config/xml/ClusteringCacheManagerServiceConfigurationParserTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/config/xml/ClusteringCacheManagerServiceConfigurationParserTest.java similarity index 55% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/config/xml/ClusteringCacheManagerServiceConfigurationParserTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/config/xml/ClusteringCacheManagerServiceConfigurationParserTest.java index 1bc0247e7f..6598cca839 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/config/xml/ClusteringCacheManagerServiceConfigurationParserTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/config/xml/ClusteringCacheManagerServiceConfigurationParserTest.java @@ -20,16 +20,15 @@ import org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder; import org.ehcache.clustered.client.config.builders.TimeoutsBuilder; import org.ehcache.clustered.client.internal.ConnectionSource; +import org.ehcache.clustered.common.ServerSideConfiguration; import org.ehcache.config.Configuration; import org.ehcache.config.units.MemoryUnit; -import org.ehcache.core.internal.util.ClassLoading; -import org.ehcache.core.spi.service.ServiceUtils; -import org.ehcache.spi.service.Service; +import org.ehcache.core.util.ClassLoading; import org.ehcache.spi.service.ServiceCreationConfiguration; import org.ehcache.xml.CacheManagerServiceConfigurationParser; import org.ehcache.xml.XmlConfiguration; import org.ehcache.xml.exceptions.XmlConfigurationException; -import org.ehcache.xml.model.TimeType; +import org.ehcache.xml.model.TimeTypeWithPropSubst; import org.hamcrest.Matchers; import org.junit.ClassRule; import org.junit.Rule; @@ -47,27 +46,36 @@ import java.net.InetSocketAddress; import java.net.URI; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.time.Duration; import java.time.temporal.TemporalUnit; import java.util.Arrays; import java.util.Collection; +import java.util.HashMap; import java.util.List; -import java.util.ServiceLoader; +import java.util.Map; +import java.util.stream.Collectors; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.stream.StreamSource; import static java.time.temporal.ChronoUnit.MINUTES; -import static org.ehcache.xml.ConfigurationParserTestHelper.assertElement; +import static java.util.Spliterators.spliterator; +import static java.util.stream.StreamSupport.stream; +import static org.ehcache.core.spi.service.ServiceUtils.findSingletonAmongst; +import static org.ehcache.xml.XmlConfigurationMatchers.isSameConfigurationAs; import static org.ehcache.xml.XmlModel.convertToJavaTimeUnit; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; +import static org.hamcrest.core.IsCollectionContaining.hasItem; import static org.junit.Assert.fail; public class ClusteringCacheManagerServiceConfigurationParserTest { @@ -78,23 +86,16 @@ public class ClusteringCacheManagerServiceConfigurationParserTest { @Rule public final TestName testName = new TestName(); + private static final String PROPERTY_PREFIX = ClusteringCacheManagerServiceConfigurationParserTest.class.getName() + ":"; + /** * Ensures the {@link ClusteringCacheManagerServiceConfigurationParser} is locatable as a * {@link CacheManagerServiceConfigurationParser} instance. */ @Test public void testServiceLocator() throws Exception { - String expectedParser = ClusteringCacheManagerServiceConfigurationParser.class.getName(); - @SuppressWarnings({"unchecked", "rawtypes"}) - ServiceLoader> parsers = (ServiceLoader) - ClassLoading.libraryServiceLoaderFor(CacheManagerServiceConfigurationParser.class); - - for (CacheManagerServiceConfigurationParser parser : parsers) { - if (parser.getClass().getName().equals(expectedParser)) { - return; - } - } - fail("Expected parser not found"); + assertThat(stream(spliterator(ClassLoading.servicesOfType(CacheManagerServiceConfigurationParser.class).iterator(), Long.MAX_VALUE, 0), false).map(Object::getClass).collect(Collectors.toList()), + hasItem(ClusteringCacheManagerServiceConfigurationParser.class)); } /** @@ -141,12 +142,12 @@ public void testGetTimeout() throws Exception { final Configuration configuration = new XmlConfiguration(makeConfig(config)); - Collection> serviceCreationConfigurations = + Collection> serviceCreationConfigurations = configuration.getServiceCreationConfigurations(); assertThat(serviceCreationConfigurations, is(not(Matchers.empty()))); ClusteringServiceConfiguration clusteringServiceConfiguration = - ServiceUtils.findSingletonAmongst(ClusteringServiceConfiguration.class, serviceCreationConfigurations); + findSingletonAmongst(ClusteringServiceConfiguration.class, serviceCreationConfigurations); assertThat(clusteringServiceConfiguration, is(notNullValue())); Timeouts timeouts = clusteringServiceConfiguration.getTimeouts(); @@ -175,12 +176,12 @@ public void testGetTimeoutNone() throws Exception { final Configuration configuration = new XmlConfiguration(makeConfig(config)); - Collection> serviceCreationConfigurations = + Collection> serviceCreationConfigurations = configuration.getServiceCreationConfigurations(); assertThat(serviceCreationConfigurations, is(not(Matchers.empty()))); ClusteringServiceConfiguration clusteringServiceConfiguration = - ServiceUtils.findSingletonAmongst(ClusteringServiceConfiguration.class, serviceCreationConfigurations); + findSingletonAmongst(ClusteringServiceConfiguration.class, serviceCreationConfigurations); assertThat(clusteringServiceConfiguration, is(notNullValue())); assertThat(clusteringServiceConfiguration.getTimeouts(), is(TimeoutsBuilder.timeouts().build())); @@ -207,15 +208,15 @@ public void testGetTimeoutUnitDefault() throws Exception { final Configuration configuration = new XmlConfiguration(makeConfig(config)); - Collection> serviceCreationConfigurations = + Collection> serviceCreationConfigurations = configuration.getServiceCreationConfigurations(); assertThat(serviceCreationConfigurations, is(not(Matchers.empty()))); ClusteringServiceConfiguration clusteringServiceConfiguration = - ServiceUtils.findSingletonAmongst(ClusteringServiceConfiguration.class, serviceCreationConfigurations); + findSingletonAmongst(ClusteringServiceConfiguration.class, serviceCreationConfigurations); assertThat(clusteringServiceConfiguration, is(notNullValue())); - TemporalUnit defaultUnit = convertToJavaTimeUnit(new TimeType().getUnit()); + TemporalUnit defaultUnit = convertToJavaTimeUnit(new TimeTypeWithPropSubst().getUnit()); assertThat(clusteringServiceConfiguration.getTimeouts().getReadOperationTimeout(), is(equalTo(Duration.of(5, defaultUnit)))); } @@ -244,7 +245,7 @@ public void testGetTimeoutUnitBad() throws Exception { fail("Expecting XmlConfigurationException"); } catch (XmlConfigurationException e) { assertThat(e.getMessage(), containsString("Error parsing XML configuration ")); - assertThat(e.getCause().getMessage(), containsString("Value 'femtos' is not facet-valid with respect to enumeration ")); + assertThat(e.getCause().getMessage(), allOf(containsString("facet"), containsString("enumeration"), containsString("femtos"))); } } @@ -301,7 +302,90 @@ public void testGetTimeoutValueOmitted() throws Exception { fail("Expecting XmlConfigurationException"); } catch (XmlConfigurationException e) { assertThat(e.getMessage(), containsString("Error parsing XML configuration ")); - assertThat(e.getCause().getMessage(), containsString("'' is not a valid value for 'integer'")); + assertThat(e.getCause().getMessage(), allOf(containsString("propertyOrPositiveInteger"), containsString("valid"), containsString("not"))); + } + } + + @Test + public void testGetTimeoutAsProperty() throws Exception { + String readTimeoutProperty = PROPERTY_PREFIX + testName.getMethodName() + ":read"; + String writeTimeoutProperty = PROPERTY_PREFIX + testName.getMethodName() + ":write"; + String connectTimeoutProperty = PROPERTY_PREFIX + testName.getMethodName() + ":connect"; + Map properties = new HashMap<>(); + properties.put(readTimeoutProperty, "5"); + properties.put(writeTimeoutProperty, "10"); + properties.put(connectTimeoutProperty, "15"); + + final String[] config = new String[] + { + "", + "", + " ", + " ", + " ", + " ${" + readTimeoutProperty + "}", + " ${" + writeTimeoutProperty + "}", + " ${" + connectTimeoutProperty + "}", + " ", + " ", + "", + "" + }; + + properties.forEach(System::setProperty); + try { + final Configuration configuration = new XmlConfiguration(makeConfig(config)); + + Collection> serviceCreationConfigurations = + configuration.getServiceCreationConfigurations(); + assertThat(serviceCreationConfigurations, is(not(Matchers.empty()))); + + ClusteringServiceConfiguration clusteringServiceConfiguration = + findSingletonAmongst(ClusteringServiceConfiguration.class, serviceCreationConfigurations); + assertThat(clusteringServiceConfiguration, is(notNullValue())); + + Timeouts timeouts = clusteringServiceConfiguration.getTimeouts(); + assertThat(timeouts.getReadOperationTimeout(), is(Duration.of(5, MINUTES))); + assertThat(timeouts.getWriteOperationTimeout(), is(Duration.of(10, MINUTES))); + assertThat(timeouts.getConnectionTimeout(), is(Duration.of(15, MINUTES))); + } finally { + properties.keySet().forEach(System::clearProperty); + } + } + + @Test + public void testUrlWithProperty() throws Exception { + String serverProperty = PROPERTY_PREFIX + testName.getMethodName() + ":server"; + String portProperty = PROPERTY_PREFIX + testName.getMethodName() + ":port"; + Map properties = new HashMap<>(); + properties.put(serverProperty, "example.com"); + properties.put(portProperty, "9540"); + + final String[] config = new String[] + { + "", + "", + " ", + " ", + " ", + " ", + " ", + "", + "" + }; + + properties.forEach(System::setProperty); + try { + XmlConfiguration configuration = new XmlConfiguration(makeConfig(config)); + ClusteringServiceConfiguration clusteringConfig = findSingletonAmongst(ClusteringServiceConfiguration.class, configuration.getServiceCreationConfigurations()); + ConnectionSource.ClusterUri connectionSource = (ConnectionSource.ClusterUri) clusteringConfig.getConnectionSource(); + assertThat(connectionSource.getClusterUri(), is(URI.create("terracotta://example.com:9540/cachemanager"))); + } finally { + properties.keySet().forEach(System::clearProperty); } } @@ -371,9 +455,9 @@ public void testServersWithClusterTierManager() throws Exception { }; final Configuration configuration = new XmlConfiguration(makeConfig(config)); - Collection> serviceCreationConfigurations = configuration.getServiceCreationConfigurations(); + Collection> serviceCreationConfigurations = configuration.getServiceCreationConfigurations(); ClusteringServiceConfiguration clusteringServiceConfiguration = - ServiceUtils.findSingletonAmongst(ClusteringServiceConfiguration.class, serviceCreationConfigurations); + findSingletonAmongst(ClusteringServiceConfiguration.class, serviceCreationConfigurations); ConnectionSource.ServerList connectionSource = (ConnectionSource.ServerList) clusteringServiceConfiguration.getConnectionSource(); Iterable servers = connectionSource.getServers(); @@ -408,9 +492,9 @@ public void testServersWithClusterTierManagerAndOptionalPorts() throws Exception }; final Configuration configuration = new XmlConfiguration(makeConfig(config)); - Collection> serviceCreationConfigurations = configuration.getServiceCreationConfigurations(); + Collection> serviceCreationConfigurations = configuration.getServiceCreationConfigurations(); ClusteringServiceConfiguration clusteringServiceConfiguration = - ServiceUtils.findSingletonAmongst(ClusteringServiceConfiguration.class, serviceCreationConfigurations); + findSingletonAmongst(ClusteringServiceConfiguration.class, serviceCreationConfigurations); ConnectionSource.ServerList connectionSource = (ConnectionSource.ServerList)clusteringServiceConfiguration.getConnectionSource(); Iterable servers = connectionSource.getServers(); @@ -424,15 +508,227 @@ public void testServersWithClusterTierManagerAndOptionalPorts() throws Exception assertThat(servers, is(expectedServers)); } + @Test + public void testServersWithClusterTierManagerAndOptionalPortsUsingProperties() throws Exception { + String hostProperty = PROPERTY_PREFIX + testName.getMethodName() + ":host"; + String portProperty = PROPERTY_PREFIX + testName.getMethodName() + ":port"; + String tierManagerProperty = PROPERTY_PREFIX + testName.getMethodName() + ":tierManager"; + Map properties = new HashMap<>(); + properties.put(hostProperty, "100.100.100.100"); + properties.put(portProperty, "9510"); + properties.put(tierManagerProperty, "george"); + + final String[] config = new String[] + { + "", + "", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + "", + "" + }; + + properties.forEach(System::setProperty); + try { + final Configuration configuration = new XmlConfiguration(makeConfig(config)); + Collection> serviceCreationConfigurations = configuration.getServiceCreationConfigurations(); + ClusteringServiceConfiguration clusteringServiceConfiguration = + findSingletonAmongst(ClusteringServiceConfiguration.class, serviceCreationConfigurations); + ConnectionSource.ServerList connectionSource = (ConnectionSource.ServerList) clusteringServiceConfiguration.getConnectionSource(); + Iterable servers = connectionSource.getServers(); + + assertThat(connectionSource.getClusterTierManager(), is("george")); + assertThat(servers, contains(InetSocketAddress.createUnresolved("100.100.100.100", 9510))); + } finally { + properties.keySet().forEach(System::clearProperty); + } + } + + @Test @SuppressWarnings("deprecation") + public void testAutoCreateFalseMapsToExpecting() throws IOException { + final String[] config = new String[] + { + "", + " ", + " ", + " ", + " ", + " ", + " ", + "" + }; + + XmlConfiguration configuration = new XmlConfiguration(makeConfig(config)); + ClusteringServiceConfiguration clusterConfig = findSingletonAmongst(ClusteringServiceConfiguration.class, configuration.getServiceCreationConfigurations()); + + assertThat(clusterConfig.isAutoCreate(), is(false)); + assertThat(clusterConfig.getClientMode(), is(ClusteringServiceConfiguration.ClientMode.EXPECTING)); + } + + @Test @SuppressWarnings("deprecation") + public void testAutoCreateTrueMapsToAutoCreate() throws IOException { + final String[] config = new String[] + { + "", + " ", + " ", + " ", + " ", + " ", + " ", + "" + }; + + XmlConfiguration configuration = new XmlConfiguration(makeConfig(config)); + ClusteringServiceConfiguration clusterConfig = findSingletonAmongst(ClusteringServiceConfiguration.class, configuration.getServiceCreationConfigurations()); + + assertThat(clusterConfig.isAutoCreate(), is(true)); + assertThat(clusterConfig.getClientMode(), is(ClusteringServiceConfiguration.ClientMode.AUTO_CREATE)); + } + + @Test + public void testBothAutoCreateAndClientModeIsDisallowed() throws IOException { + final String[] config = new String[] + { + "", + " ", + " ", + " ", + " ", + " ", + " ", + "" + }; + + try { + new XmlConfiguration(makeConfig(config)); + } catch (XmlConfigurationException e) { + assertThat(e.getMessage(), is("Cannot define both 'auto-create' and 'client-mode' attributes")); + } + } + + @Test + public void testClientModeAsAProperty() throws IOException { + String clientModeProperty = PROPERTY_PREFIX + testName.getMethodName() + ":client-mode"; + Map properties = new HashMap<>(); + properties.put(clientModeProperty, "auto-create"); + + final String[] config = new String[] + { + "", + " ", + " ", + " ", + " ", + " ", + " ", + "" + }; + + properties.forEach(System::setProperty); + try { + XmlConfiguration configuration = new XmlConfiguration(makeConfig(config)); + ClusteringServiceConfiguration clusterConfig = findSingletonAmongst(ClusteringServiceConfiguration.class, configuration.getServiceCreationConfigurations()); + assertThat(clusterConfig.getClientMode(), is(ClusteringServiceConfiguration.ClientMode.AUTO_CREATE)); + } finally { + properties.keySet().forEach(System::clearProperty); + } + } + + @Test + public void testSharedPoolUsingProperties() throws IOException { + String poolSizeProperty = PROPERTY_PREFIX + testName.getMethodName() + ":pool-size"; + String fromProperty = PROPERTY_PREFIX + testName.getMethodName() + ":from"; + Map properties = new HashMap<>(); + properties.put(poolSizeProperty, "1024"); + properties.put(fromProperty, "source"); + + final String[] config = new String[] + { + "", + " ", + " ", + " ", + " ", + " ", + " ${" + poolSizeProperty + "}", + " ", + " ", + " ", + " ", + "" + }; + + properties.forEach(System::setProperty); + try { + XmlConfiguration configuration = new XmlConfiguration(makeConfig(config)); + ClusteringServiceConfiguration clusterConfig = findSingletonAmongst(ClusteringServiceConfiguration.class, configuration.getServiceCreationConfigurations()); + ServerSideConfiguration.Pool pool = clusterConfig.getServerConfiguration().getResourcePools().get("pool"); + assertThat(pool.getSize(), is(1024L)); + assertThat(pool.getServerResource(), is("source")); + } finally { + properties.keySet().forEach(System::clearProperty); + } + } + + @Test + public void testDefaultResourceAsAProperty() throws IOException { + String fromProperty = PROPERTY_PREFIX + testName.getMethodName() + ":from"; + Map properties = new HashMap<>(); + properties.put(fromProperty, "source"); + + final String[] config = new String[] + { + "", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + "" + }; + + properties.forEach(System::setProperty); + try { + XmlConfiguration configuration = new XmlConfiguration(makeConfig(config)); + ClusteringServiceConfiguration clusterConfig = findSingletonAmongst(ClusteringServiceConfiguration.class, configuration.getServiceCreationConfigurations()); + assertThat(clusterConfig.getServerConfiguration().getDefaultServerResource(), is("source")); + } finally { + properties.keySet().forEach(System::clearProperty); + } + } + @Test public void testTranslateServiceCreationConfiguration() throws Exception { URI connectionUri = new URI("terracotta://localhost:9510/my-application"); ClusteringServiceConfiguration serviceConfig = ClusteringServiceConfigurationBuilder.cluster(connectionUri) .timeouts(Timeouts.DEFAULT) - .autoCreate() - .defaultServerResource("main") - .resourcePool("primaryresource", 5, MemoryUnit.GB) - .resourcePool("secondaryresource", 10, MemoryUnit.GB, "optional") + .autoCreate(server -> server + .defaultServerResource("main") + .resourcePool("primaryresource", 5, MemoryUnit.GB) + .resourcePool("secondaryresource", 10, MemoryUnit.GB, "optional")) .build(); ClusteringCacheManagerServiceConfigurationParser parser = new ClusteringCacheManagerServiceConfigurationParser(); @@ -443,13 +739,12 @@ public void testTranslateServiceCreationConfiguration() throws Exception { "5" + "5" + "150" + - "" + + "" + "" + "5368709120" + "10737418240" + ""; - assertElement(inputString, returnElement); - + assertThat(returnElement, isSameConfigurationAs(inputString)); } @Test @@ -457,8 +752,7 @@ public void testTranslateServiceCreationConfigurationWithNoResourcePoolAndAutoCr URI connectionUri = new URI("terracotta://localhost:9510/my-application"); ClusteringServiceConfiguration serviceConfig = ClusteringServiceConfigurationBuilder.cluster(connectionUri) .timeouts(Timeouts.DEFAULT) - .expecting() - .defaultServerResource("main") + .expecting(server -> server.defaultServerResource("main")) .build(); @@ -470,10 +764,10 @@ public void testTranslateServiceCreationConfigurationWithNoResourcePoolAndAutoCr "5" + "5" + "150" + - "" + + "" + "" + ""; - assertElement(inputString, returnElement); + assertThat(returnElement, isSameConfigurationAs(inputString)); } @Test @@ -491,9 +785,8 @@ public void testTranslateServiceCreationConfigurationWithNoServerSideConfig() th "5" + "5" + "150" + - "" + - ""; - assertElement(inputString, returnElement); + ""; + assertThat(returnElement, isSameConfigurationAs(inputString)); } @Test @@ -522,8 +815,8 @@ public void testTranslateServiceCreationConfigurationWithInetSocketAddress() { "5" + "5" + "150" + - ""; - assertElement(inputString, returnElement); + ""; + assertThat(returnElement, isSameConfigurationAs(inputString)); } /** @@ -533,24 +826,13 @@ public void testTranslateServiceCreationConfigurationWithInetSocketAddress() { * @return a {@code URL} pointing to the XML configuration file * @throws IOException if an error is raised while creating or writing the XML configuration file */ - @SuppressWarnings("ThrowFromFinallyBlock") private URL makeConfig(final String[] lines) throws IOException { final File configFile = folder.newFile(testName.getMethodName() + "_config.xml"); - OutputStreamWriter out = null; - try { - out = new OutputStreamWriter(new FileOutputStream(configFile), "UTF-8"); + try (FileOutputStream fout = new FileOutputStream(configFile); OutputStreamWriter out = new OutputStreamWriter(fout, StandardCharsets.UTF_8)) { for (final String line : lines) { out.write(line); } - } finally { - if (out != null) { - try { - out.close(); - } catch (IOException e) { - throw new AssertionError(e); - } - } } return configFile.toURI().toURL(); diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/config/xml/ClusteringCacheServiceConfigurationParserTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/config/xml/ClusteringCacheServiceConfigurationParserTest.java similarity index 88% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/config/xml/ClusteringCacheServiceConfigurationParserTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/config/xml/ClusteringCacheServiceConfigurationParserTest.java index 50f0335940..a1829d7f7d 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/config/xml/ClusteringCacheServiceConfigurationParserTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/config/xml/ClusteringCacheServiceConfigurationParserTest.java @@ -20,7 +20,8 @@ import org.junit.Test; import org.w3c.dom.Node; -import static org.ehcache.xml.ConfigurationParserTestHelper.assertElement; +import static org.ehcache.xml.XmlConfigurationMatchers.isSameConfigurationAs; +import static org.hamcrest.MatcherAssert.assertThat; public class ClusteringCacheServiceConfigurationParserTest { @@ -33,6 +34,6 @@ public void testTranslateServiceStoreConfiguration() { String inputString = ""; - assertElement(inputString, retNode); + assertThat(retNode, isSameConfigurationAs(inputString)); } } diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/loaderwriter/ClusteredLoaderWriterStoreProviderTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/loaderwriter/ClusteredLoaderWriterStoreProviderTest.java similarity index 91% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/loaderwriter/ClusteredLoaderWriterStoreProviderTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/loaderwriter/ClusteredLoaderWriterStoreProviderTest.java index 681ccb0a97..163cbfd8fd 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/loaderwriter/ClusteredLoaderWriterStoreProviderTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/loaderwriter/ClusteredLoaderWriterStoreProviderTest.java @@ -18,7 +18,7 @@ import org.ehcache.clustered.client.config.ClusteredResourceType; import org.ehcache.clustered.client.internal.store.ClusteredStoreProviderTest; import org.ehcache.clustered.client.service.ClusteringService; -import org.ehcache.core.internal.service.ServiceLocator; +import org.ehcache.core.spi.ServiceLocator; import org.ehcache.core.spi.service.DiskResourceService; import org.ehcache.impl.internal.store.disk.OffHeapDiskStore; import org.ehcache.impl.internal.store.heap.OnHeapStore; @@ -30,14 +30,14 @@ import java.util.Collections; import java.util.HashSet; -import static org.ehcache.core.internal.service.ServiceLocator.dependencySet; +import static org.ehcache.core.spi.ServiceLocator.dependencySet; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; public class ClusteredLoaderWriterStoreProviderTest { - private final CacheLoaderWriterConfiguration cacheLoaderWriterConfiguration = mock(CacheLoaderWriterConfiguration.class); + private final CacheLoaderWriterConfiguration cacheLoaderWriterConfiguration = mock(CacheLoaderWriterConfiguration.class); @Test public void testRank() { diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/loaderwriter/ClusteredLoaderWriterStoreTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/loaderwriter/ClusteredLoaderWriterStoreTest.java similarity index 67% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/loaderwriter/ClusteredLoaderWriterStoreTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/loaderwriter/ClusteredLoaderWriterStoreTest.java index 0f29847eab..b7ee9e0c2d 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/loaderwriter/ClusteredLoaderWriterStoreTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/loaderwriter/ClusteredLoaderWriterStoreTest.java @@ -16,15 +16,16 @@ package org.ehcache.clustered.client.internal.loaderwriter; import org.ehcache.clustered.client.internal.store.ServerStoreProxy; -import org.ehcache.clustered.client.internal.store.lock.LockingServerStoreProxy; +import org.ehcache.clustered.client.internal.store.lock.LockingServerStoreProxyImpl; import org.ehcache.clustered.client.internal.store.operations.EternalChainResolver; +import org.ehcache.clustered.common.internal.store.Element; import org.ehcache.clustered.common.internal.store.operations.PutOperation; import org.ehcache.clustered.common.internal.store.operations.codecs.OperationsCodec; import org.ehcache.clustered.common.internal.store.Chain; -import org.ehcache.clustered.common.internal.store.Util; import org.ehcache.clustered.loaderWriter.TestCacheLoaderWriter; import org.ehcache.core.spi.store.Store; import org.ehcache.core.spi.time.TimeSource; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; import org.ehcache.impl.serialization.LongSerializer; import org.ehcache.impl.serialization.StringSerializer; import org.ehcache.spi.loaderwriter.CacheLoaderWriter; @@ -32,16 +33,19 @@ import org.mockito.ArgumentMatchers; import java.nio.ByteBuffer; +import java.util.Iterator; +import java.util.concurrent.TimeoutException; -import static org.ehcache.clustered.common.internal.store.Util.EMPTY_CHAIN; +import static org.ehcache.clustered.ChainUtils.chainOf; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; @@ -55,37 +59,63 @@ public class ClusteredLoaderWriterStoreTest { private EternalChainResolver resolver = new EternalChainResolver<>(codec); private TimeSource timeSource = mock(TimeSource.class); + @Test + public void testContainsKeyValueAbsentInCache() throws Exception { + ServerStoreProxy storeProxy = mock(LockingServerStoreProxyImpl.class); + @SuppressWarnings("unchecked") + CacheLoaderWriter loaderWriter = mock(CacheLoaderWriter.class); + when(storeProxy.get(eq(1L))).thenReturn(entryOf()); + ClusteredLoaderWriterStore store = new ClusteredLoaderWriterStore<>(configuration, codec, resolver, storeProxy, + timeSource, loaderWriter, new DefaultStatisticsService()); + assertThat(store.containsKey(1L), is(false)); + verify(loaderWriter, never()).load(anyLong()); + } + + @Test + public void testContainsKeyValuePresentInCache() throws Exception { + ServerStoreProxy storeProxy = mock(LockingServerStoreProxyImpl.class); + @SuppressWarnings("unchecked") + CacheLoaderWriter loaderWriter = mock(CacheLoaderWriter.class); + PutOperation operation = new PutOperation<>(1L, "one", System.currentTimeMillis()); + ServerStoreProxy.ChainEntry toReturn = entryOf(codec.encode(operation)); + when(storeProxy.get(anyLong())).thenReturn(toReturn); + ClusteredLoaderWriterStore store = new ClusteredLoaderWriterStore<>(configuration, codec, resolver, storeProxy, + timeSource, loaderWriter, new DefaultStatisticsService()); + assertThat(store.containsKey(1L), is(true)); + verify(loaderWriter, never()).load(anyLong()); + } + @Test public void testGetValueAbsentInSOR() throws Exception { - ServerStoreProxy storeProxy = mock(LockingServerStoreProxy.class); + ServerStoreProxy storeProxy = mock(LockingServerStoreProxyImpl.class); CacheLoaderWriter loaderWriter = new TestCacheLoaderWriter(); - when(storeProxy.get(eq(1L))).thenReturn(EMPTY_CHAIN); + when(storeProxy.get(eq(1L))).thenReturn(entryOf()); ClusteredLoaderWriterStore store = new ClusteredLoaderWriterStore<>(configuration, codec, resolver, storeProxy, - timeSource, loaderWriter); + timeSource, loaderWriter, new DefaultStatisticsService()); assertThat(store.get(1L), is(nullValue())); } @Test public void testGetValuePresentInSOR() throws Exception { - ServerStoreProxy storeProxy = mock(LockingServerStoreProxy.class); + ServerStoreProxy storeProxy = mock(LockingServerStoreProxyImpl.class); TestCacheLoaderWriter loaderWriter = new TestCacheLoaderWriter(); loaderWriter.storeMap.put(1L, "one"); - when(storeProxy.get(eq(1L))).thenReturn(EMPTY_CHAIN); + when(storeProxy.get(eq(1L))).thenReturn(entryOf()); ClusteredLoaderWriterStore store = new ClusteredLoaderWriterStore<>(configuration, codec, resolver, storeProxy, - timeSource, loaderWriter); + timeSource, loaderWriter, new DefaultStatisticsService()); assertThat(store.get(1L).get(), equalTo("one")); } @Test public void testGetValuePresentInCache() throws Exception { - ServerStoreProxy storeProxy = mock(LockingServerStoreProxy.class); + ServerStoreProxy storeProxy = mock(LockingServerStoreProxyImpl.class); @SuppressWarnings("unchecked") CacheLoaderWriter loaderWriter = mock(CacheLoaderWriter.class); PutOperation operation = new PutOperation<>(1L, "one", System.currentTimeMillis()); - Chain toReturn = Util.getChain(false, codec.encode(operation)); + ServerStoreProxy.ChainEntry toReturn = entryOf(codec.encode(operation)); when(storeProxy.get(anyLong())).thenReturn(toReturn); ClusteredLoaderWriterStore store = new ClusteredLoaderWriterStore<>(configuration, codec, resolver, storeProxy, - timeSource, loaderWriter); + timeSource, loaderWriter, new DefaultStatisticsService()); assertThat(store.get(1L).get(), equalTo("one")); verify(loaderWriter, times(0)).load(anyLong()); verifyZeroInteractions(loaderWriter); @@ -93,10 +123,10 @@ public void testGetValuePresentInCache() throws Exception { @Test public void testPut() throws Exception { - ServerStoreProxy storeProxy = mock(LockingServerStoreProxy.class); + ServerStoreProxy storeProxy = mock(LockingServerStoreProxyImpl.class); TestCacheLoaderWriter loaderWriter = new TestCacheLoaderWriter(); ClusteredLoaderWriterStore store = new ClusteredLoaderWriterStore<>(configuration, codec, resolver, storeProxy, - timeSource, loaderWriter); + timeSource, loaderWriter, new DefaultStatisticsService()); assertThat(loaderWriter.storeMap.containsKey(1L), is(false)); assertThat(store.put(1L, "one"), is(Store.PutStatus.PUT)); assertThat(loaderWriter.storeMap.containsKey(1L), is(true)); @@ -104,11 +134,11 @@ public void testPut() throws Exception { @Test public void testRemoveValueAbsentInCachePresentInSOR() throws Exception { - LockingServerStoreProxy storeProxy = mock(LockingServerStoreProxy.class); + LockingServerStoreProxyImpl storeProxy = mock(LockingServerStoreProxyImpl.class); TestCacheLoaderWriter loaderWriter = new TestCacheLoaderWriter(); - when(storeProxy.lock(anyLong())).thenReturn(EMPTY_CHAIN); + when(storeProxy.lock(anyLong())).thenReturn(entryOf()); ClusteredLoaderWriterStore store = new ClusteredLoaderWriterStore<>(configuration, codec, resolver, storeProxy, - timeSource, loaderWriter); + timeSource, loaderWriter, new DefaultStatisticsService()); loaderWriter.storeMap.put(1L, "one"); assertThat(store.remove(1L), is(false)); assertThat(loaderWriter.storeMap.containsKey(1L), is(false)); @@ -116,14 +146,14 @@ public void testRemoveValueAbsentInCachePresentInSOR() throws Exception { @Test public void testRemoveValuePresentInCachePresentInSOR() throws Exception { - LockingServerStoreProxy storeProxy = mock(LockingServerStoreProxy.class); + LockingServerStoreProxyImpl storeProxy = mock(LockingServerStoreProxyImpl.class); TestCacheLoaderWriter loaderWriter = new TestCacheLoaderWriter(); PutOperation operation = new PutOperation<>(1L, "one", System.currentTimeMillis()); - Chain toReturn = Util.getChain(false, codec.encode(operation)); + ServerStoreProxy.ChainEntry toReturn = entryOf(codec.encode(operation)); when(storeProxy.lock(anyLong())).thenReturn(toReturn); when(storeProxy.get(anyLong())).thenReturn(toReturn); ClusteredLoaderWriterStore store = new ClusteredLoaderWriterStore<>(configuration, codec, resolver, storeProxy, - timeSource, loaderWriter); + timeSource, loaderWriter, new DefaultStatisticsService()); loaderWriter.storeMap.put(1L, "one"); assertThat(store.get(1L).get(), equalTo("one")); assertThat(store.remove(1L), is(true)); @@ -132,23 +162,23 @@ public void testRemoveValuePresentInCachePresentInSOR() throws Exception { @Test public void testRemoveValueAbsentInCacheAbsentInSOR() throws Exception { - LockingServerStoreProxy storeProxy = mock(LockingServerStoreProxy.class); + LockingServerStoreProxyImpl storeProxy = mock(LockingServerStoreProxyImpl.class); @SuppressWarnings("unchecked") CacheLoaderWriter loaderWriter = mock(CacheLoaderWriter.class); - when(storeProxy.lock(anyLong())).thenReturn(EMPTY_CHAIN); + when(storeProxy.lock(anyLong())).thenReturn(entryOf()); ClusteredLoaderWriterStore store = new ClusteredLoaderWriterStore<>(configuration, codec, resolver, storeProxy, - timeSource, loaderWriter); + timeSource, loaderWriter, new DefaultStatisticsService()); assertThat(store.remove(1L), is(false)); verify(loaderWriter, times(1)).delete(anyLong()); } @Test public void testPufIfAbsentValueAbsentInCacheAbsentInSOR() throws Exception { - LockingServerStoreProxy storeProxy = mock(LockingServerStoreProxy.class); + LockingServerStoreProxyImpl storeProxy = mock(LockingServerStoreProxyImpl.class); TestCacheLoaderWriter loaderWriter = new TestCacheLoaderWriter(); - when(storeProxy.lock(anyLong())).thenReturn(EMPTY_CHAIN); + when(storeProxy.lock(anyLong())).thenReturn(entryOf()); ClusteredLoaderWriterStore store = new ClusteredLoaderWriterStore<>(configuration, codec, resolver, storeProxy, - timeSource, loaderWriter); + timeSource, loaderWriter, new DefaultStatisticsService()); assertThat(loaderWriter.storeMap.isEmpty(), is(true)); assertThat(store.putIfAbsent(1L, "one", null), is(nullValue())); assertThat(loaderWriter.storeMap.get(1L), equalTo("one")); @@ -156,11 +186,11 @@ public void testPufIfAbsentValueAbsentInCacheAbsentInSOR() throws Exception { @Test public void testPufIfAbsentValueAbsentInCachePresentInSOR() throws Exception { - LockingServerStoreProxy storeProxy = mock(LockingServerStoreProxy.class); + LockingServerStoreProxyImpl storeProxy = mock(LockingServerStoreProxyImpl.class); TestCacheLoaderWriter loaderWriter = new TestCacheLoaderWriter(); - when(storeProxy.lock(anyLong())).thenReturn(EMPTY_CHAIN); + when(storeProxy.lock(anyLong())).thenReturn(entryOf()); ClusteredLoaderWriterStore store = new ClusteredLoaderWriterStore<>(configuration, codec, resolver, storeProxy, - timeSource, loaderWriter); + timeSource, loaderWriter, new DefaultStatisticsService()); loaderWriter.storeMap.put(1L, "one"); assertThat(store.putIfAbsent(1L, "Again", null).get(), equalTo("one")); verify(storeProxy, times(0)).append(anyLong(), ArgumentMatchers.any(ByteBuffer.class)); @@ -169,13 +199,13 @@ public void testPufIfAbsentValueAbsentInCachePresentInSOR() throws Exception { @Test public void testPufIfAbsentValuePresentInCachePresentInSOR() throws Exception { - LockingServerStoreProxy storeProxy = mock(LockingServerStoreProxy.class); + LockingServerStoreProxyImpl storeProxy = mock(LockingServerStoreProxyImpl.class); TestCacheLoaderWriter loaderWriter = new TestCacheLoaderWriter(); PutOperation operation = new PutOperation<>(1L, "one", System.currentTimeMillis()); - Chain toReturn = Util.getChain(false, codec.encode(operation)); + ServerStoreProxy.ChainEntry toReturn = entryOf(codec.encode(operation)); when(storeProxy.lock(anyLong())).thenReturn(toReturn); ClusteredLoaderWriterStore store = new ClusteredLoaderWriterStore<>(configuration, codec, resolver, storeProxy, - timeSource, loaderWriter); + timeSource, loaderWriter, new DefaultStatisticsService()); loaderWriter.storeMap.put(1L, "one"); assertThat(store.putIfAbsent(1L, "Again", null).get(), equalTo("one")); verify(storeProxy, times(0)).append(anyLong(), ArgumentMatchers.any(ByteBuffer.class)); @@ -184,11 +214,11 @@ public void testPufIfAbsentValuePresentInCachePresentInSOR() throws Exception { @Test public void testReplaceValueAbsentInCacheAbsentInSOR() throws Exception { - LockingServerStoreProxy storeProxy = mock(LockingServerStoreProxy.class); + LockingServerStoreProxyImpl storeProxy = mock(LockingServerStoreProxyImpl.class); TestCacheLoaderWriter loaderWriter = new TestCacheLoaderWriter(); - when(storeProxy.lock(anyLong())).thenReturn(EMPTY_CHAIN); + when(storeProxy.lock(anyLong())).thenReturn(entryOf()); ClusteredLoaderWriterStore store = new ClusteredLoaderWriterStore<>(configuration, codec, resolver, storeProxy, - timeSource, loaderWriter); + timeSource, loaderWriter, new DefaultStatisticsService()); assertThat(loaderWriter.storeMap.isEmpty(), is(true)); assertThat(store.replace(1L, "one"), is(nullValue())); assertThat(loaderWriter.storeMap.isEmpty(), is(true)); @@ -197,11 +227,11 @@ public void testReplaceValueAbsentInCacheAbsentInSOR() throws Exception { @Test public void testReplaceValueAbsentInCachePresentInSOR() throws Exception { - LockingServerStoreProxy storeProxy = mock(LockingServerStoreProxy.class); + LockingServerStoreProxyImpl storeProxy = mock(LockingServerStoreProxyImpl.class); TestCacheLoaderWriter loaderWriter = new TestCacheLoaderWriter(); - when(storeProxy.lock(anyLong())).thenReturn(EMPTY_CHAIN); + when(storeProxy.lock(anyLong())).thenReturn(entryOf()); ClusteredLoaderWriterStore store = new ClusteredLoaderWriterStore<>(configuration, codec, resolver, storeProxy, - timeSource, loaderWriter); + timeSource, loaderWriter, new DefaultStatisticsService()); loaderWriter.storeMap.put(1L, "one"); assertThat(store.replace(1L, "Again").get(), equalTo("one")); verify(storeProxy, times(1)).append(anyLong(), ArgumentMatchers.any(ByteBuffer.class)); @@ -210,13 +240,13 @@ public void testReplaceValueAbsentInCachePresentInSOR() throws Exception { @Test public void testReplaceValuePresentInCachePresentInSOR() throws Exception { - LockingServerStoreProxy storeProxy = mock(LockingServerStoreProxy.class); + LockingServerStoreProxyImpl storeProxy = mock(LockingServerStoreProxyImpl.class); TestCacheLoaderWriter loaderWriter = new TestCacheLoaderWriter(); PutOperation operation = new PutOperation<>(1L, "one", System.currentTimeMillis()); - Chain toReturn = Util.getChain(false, codec.encode(operation)); + ServerStoreProxy.ChainEntry toReturn = entryOf(codec.encode(operation)); when(storeProxy.lock(anyLong())).thenReturn(toReturn); ClusteredLoaderWriterStore store = new ClusteredLoaderWriterStore<>(configuration, codec, resolver, storeProxy, - timeSource, loaderWriter); + timeSource, loaderWriter, new DefaultStatisticsService()); loaderWriter.storeMap.put(1L, "one"); assertThat(store.replace(1L, "Again").get(), equalTo("one")); verify(storeProxy, times(1)).append(anyLong(), ArgumentMatchers.any(ByteBuffer.class)); @@ -225,23 +255,23 @@ public void testReplaceValuePresentInCachePresentInSOR() throws Exception { @Test public void testRemove2ArgsValueAbsentInCacheAbsentInSOR() throws Exception { - LockingServerStoreProxy storeProxy = mock(LockingServerStoreProxy.class); + LockingServerStoreProxyImpl storeProxy = mock(LockingServerStoreProxyImpl.class); @SuppressWarnings("unchecked") CacheLoaderWriter loaderWriter = mock(CacheLoaderWriter.class); - when(storeProxy.lock(anyLong())).thenReturn(EMPTY_CHAIN); + when(storeProxy.lock(anyLong())).thenReturn(entryOf()); ClusteredLoaderWriterStore store = new ClusteredLoaderWriterStore<>(configuration, codec, resolver, storeProxy, - timeSource, loaderWriter); + timeSource, loaderWriter, new DefaultStatisticsService()); assertThat(store.remove(1L, "one"), is(Store.RemoveStatus.KEY_MISSING)); verify(storeProxy, times(0)).append(anyLong(), ArgumentMatchers.any(ByteBuffer.class)); } @Test public void testRemove2ArgsValueAbsentInCachePresentInSOR() throws Exception { - LockingServerStoreProxy storeProxy = mock(LockingServerStoreProxy.class); + LockingServerStoreProxyImpl storeProxy = mock(LockingServerStoreProxyImpl.class); TestCacheLoaderWriter loaderWriter = new TestCacheLoaderWriter(); - when(storeProxy.lock(anyLong())).thenReturn(EMPTY_CHAIN); + when(storeProxy.lock(anyLong())).thenReturn(entryOf()); ClusteredLoaderWriterStore store = new ClusteredLoaderWriterStore<>(configuration, codec, resolver, storeProxy, - timeSource, loaderWriter); + timeSource, loaderWriter, new DefaultStatisticsService()); loaderWriter.storeMap.put(1L, "one"); assertThat(store.remove(1L, "one"), is(Store.RemoveStatus.REMOVED)); verify(storeProxy, times(1)).append(anyLong(), ArgumentMatchers.any(ByteBuffer.class)); @@ -250,14 +280,14 @@ public void testRemove2ArgsValueAbsentInCachePresentInSOR() throws Exception { @Test public void testRemove2ArgsValuePresentInCachePresentInSOR() throws Exception { - LockingServerStoreProxy storeProxy = mock(LockingServerStoreProxy.class); + LockingServerStoreProxyImpl storeProxy = mock(LockingServerStoreProxyImpl.class); @SuppressWarnings("unchecked") CacheLoaderWriter loaderWriter = mock(CacheLoaderWriter.class); PutOperation operation = new PutOperation<>(1L, "one", System.currentTimeMillis()); - Chain toReturn = Util.getChain(false, codec.encode(operation)); + ServerStoreProxy.ChainEntry toReturn = entryOf(codec.encode(operation)); when(storeProxy.lock(anyLong())).thenReturn(toReturn); ClusteredLoaderWriterStore store = new ClusteredLoaderWriterStore<>(configuration, codec, resolver, storeProxy, - timeSource, loaderWriter); + timeSource, loaderWriter, new DefaultStatisticsService()); assertThat(store.remove(1L, "one"), is(Store.RemoveStatus.REMOVED)); verify(storeProxy, times(1)).append(anyLong(), ArgumentMatchers.any(ByteBuffer.class)); verify(loaderWriter, times(0)).load(anyLong()); @@ -266,11 +296,11 @@ public void testRemove2ArgsValuePresentInCachePresentInSOR() throws Exception { @Test public void testRemove2ArgsValueAbsentInCacheDiffValuePresentInSOR() throws Exception { - LockingServerStoreProxy storeProxy = mock(LockingServerStoreProxy.class); + LockingServerStoreProxyImpl storeProxy = mock(LockingServerStoreProxyImpl.class); TestCacheLoaderWriter loaderWriter = new TestCacheLoaderWriter(); - when(storeProxy.lock(anyLong())).thenReturn(EMPTY_CHAIN); + when(storeProxy.lock(anyLong())).thenReturn(entryOf()); ClusteredLoaderWriterStore store = new ClusteredLoaderWriterStore<>(configuration, codec, resolver, storeProxy, - timeSource, loaderWriter); + timeSource, loaderWriter, new DefaultStatisticsService()); loaderWriter.storeMap.put(1L, "one"); assertThat(store.remove(1L, "Again"), is(Store.RemoveStatus.KEY_PRESENT)); verify(storeProxy, times(0)).append(anyLong(), ArgumentMatchers.any(ByteBuffer.class)); @@ -279,12 +309,12 @@ public void testRemove2ArgsValueAbsentInCacheDiffValuePresentInSOR() throws Exce @Test public void testReplace2ArgsValueAbsentInCacheAbsentInSOR() throws Exception { - LockingServerStoreProxy storeProxy = mock(LockingServerStoreProxy.class); + LockingServerStoreProxyImpl storeProxy = mock(LockingServerStoreProxyImpl.class); @SuppressWarnings("unchecked") CacheLoaderWriter loaderWriter = mock(CacheLoaderWriter.class); - when(storeProxy.lock(anyLong())).thenReturn(EMPTY_CHAIN); + when(storeProxy.lock(anyLong())).thenReturn(entryOf()); ClusteredLoaderWriterStore store = new ClusteredLoaderWriterStore<>(configuration, codec, resolver, storeProxy, - timeSource, loaderWriter); + timeSource, loaderWriter, new DefaultStatisticsService()); assertThat(store.replace(1L, "one", "Again"), is(Store.ReplaceStatus.MISS_NOT_PRESENT)); verify(storeProxy, times(0)).append(anyLong(), ArgumentMatchers.any(ByteBuffer.class)); verify(loaderWriter, times(1)).load(anyLong()); @@ -293,11 +323,11 @@ public void testReplace2ArgsValueAbsentInCacheAbsentInSOR() throws Exception { @Test public void testReplace2ArgsValueAbsentInCachePresentInSOR() throws Exception { - LockingServerStoreProxy storeProxy = mock(LockingServerStoreProxy.class); + LockingServerStoreProxyImpl storeProxy = mock(LockingServerStoreProxyImpl.class); TestCacheLoaderWriter loaderWriter = new TestCacheLoaderWriter(); - when(storeProxy.lock(anyLong())).thenReturn(EMPTY_CHAIN); + when(storeProxy.lock(anyLong())).thenReturn(entryOf()); ClusteredLoaderWriterStore store = new ClusteredLoaderWriterStore<>(configuration, codec, resolver, storeProxy, - timeSource, loaderWriter); + timeSource, loaderWriter, new DefaultStatisticsService()); loaderWriter.storeMap.put(1L, "one"); assertThat(store.replace(1L, "one", "Again"), is(Store.ReplaceStatus.HIT)); verify(storeProxy, times(1)).append(anyLong(), ArgumentMatchers.any(ByteBuffer.class)); @@ -306,14 +336,14 @@ public void testReplace2ArgsValueAbsentInCachePresentInSOR() throws Exception { @Test public void testReplace2ArgsValuePresentInCachePresentInSOR() throws Exception { - LockingServerStoreProxy storeProxy = mock(LockingServerStoreProxy.class); + LockingServerStoreProxyImpl storeProxy = mock(LockingServerStoreProxyImpl.class); @SuppressWarnings("unchecked") CacheLoaderWriter loaderWriter = mock(CacheLoaderWriter.class); PutOperation operation = new PutOperation<>(1L, "one", System.currentTimeMillis()); - Chain toReturn = Util.getChain(false, codec.encode(operation)); + ServerStoreProxy.ChainEntry toReturn = entryOf(codec.encode(operation)); when(storeProxy.lock(anyLong())).thenReturn(toReturn); ClusteredLoaderWriterStore store = new ClusteredLoaderWriterStore<>(configuration, codec, resolver, storeProxy, - timeSource, loaderWriter); + timeSource, loaderWriter, new DefaultStatisticsService()); assertThat(store.replace(1L, "one", "Again"), is(Store.ReplaceStatus.HIT)); verify(storeProxy, times(1)).append(anyLong(), ArgumentMatchers.any(ByteBuffer.class)); verify(loaderWriter, times(0)).load(anyLong()); @@ -322,14 +352,44 @@ public void testReplace2ArgsValuePresentInCachePresentInSOR() throws Exception { @Test public void testReplace2ArgsValueAbsentInCacheDiffValueInSOR() throws Exception { - LockingServerStoreProxy storeProxy = mock(LockingServerStoreProxy.class); + LockingServerStoreProxyImpl storeProxy = mock(LockingServerStoreProxyImpl.class); TestCacheLoaderWriter loaderWriter = new TestCacheLoaderWriter(); - when(storeProxy.lock(anyLong())).thenReturn(EMPTY_CHAIN); + when(storeProxy.lock(anyLong())).thenReturn(entryOf()); ClusteredLoaderWriterStore store = new ClusteredLoaderWriterStore<>(configuration, codec, resolver, storeProxy, - timeSource, loaderWriter); + timeSource, loaderWriter, new DefaultStatisticsService()); loaderWriter.storeMap.put(1L, "one"); assertThat(store.replace(1L, "Again", "one"), is(Store.ReplaceStatus.MISS_PRESENT)); verify(storeProxy, times(0)).append(anyLong(), ArgumentMatchers.any(ByteBuffer.class)); assertThat(loaderWriter.storeMap.get(1L), equalTo("one")); } + + private static ServerStoreProxy.ChainEntry entryOf(ByteBuffer ... elements) { + Chain chain = chainOf(elements); + return new ServerStoreProxy.ChainEntry() { + @Override + public void append(ByteBuffer payLoad) throws TimeoutException { + throw new AssertionError(); + } + + @Override + public void replaceAtHead(Chain equivalent) { + throw new AssertionError(); + } + + @Override + public boolean isEmpty() { + return chain.isEmpty(); + } + + @Override + public int length() { + return chain.length(); + } + + @Override + public Iterator iterator() { + return chain.iterator(); + } + }; + } } diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/loaderwriter/writebehind/ClusteredWriteBehindStoreProviderTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/loaderwriter/writebehind/ClusteredWriteBehindStoreProviderTest.java similarity index 90% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/loaderwriter/writebehind/ClusteredWriteBehindStoreProviderTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/loaderwriter/writebehind/ClusteredWriteBehindStoreProviderTest.java index 332d008fc3..673381fd61 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/loaderwriter/writebehind/ClusteredWriteBehindStoreProviderTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/loaderwriter/writebehind/ClusteredWriteBehindStoreProviderTest.java @@ -18,7 +18,7 @@ import org.ehcache.clustered.client.config.ClusteredResourceType; import org.ehcache.clustered.client.internal.store.ClusteredStoreProviderTest; import org.ehcache.clustered.client.service.ClusteringService; -import org.ehcache.core.internal.service.ServiceLocator; +import org.ehcache.core.spi.ServiceLocator; import org.ehcache.core.spi.service.DiskResourceService; import org.ehcache.impl.internal.store.disk.OffHeapDiskStore; import org.ehcache.impl.internal.store.heap.OnHeapStore; @@ -32,15 +32,15 @@ import java.util.Collections; import java.util.HashSet; -import static org.ehcache.core.internal.service.ServiceLocator.dependencySet; +import static org.ehcache.core.spi.ServiceLocator.dependencySet; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; public class ClusteredWriteBehindStoreProviderTest { - private final CacheLoaderWriterConfiguration cacheLoaderWriterConfiguration = mock(CacheLoaderWriterConfiguration.class); - private final WriteBehindConfiguration writeBehindConfiguration = mock(WriteBehindConfiguration.class); + private final CacheLoaderWriterConfiguration cacheLoaderWriterConfiguration = mock(CacheLoaderWriterConfiguration.class); + private final WriteBehindConfiguration writeBehindConfiguration = mock(WriteBehindConfiguration.class); @Test public void testRank() { diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/loaderwriter/writebehind/ClusteredWriteBehindTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/loaderwriter/writebehind/ClusteredWriteBehindTest.java similarity index 91% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/loaderwriter/writebehind/ClusteredWriteBehindTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/loaderwriter/writebehind/ClusteredWriteBehindTest.java index 036b08c8c0..ff0563bf6e 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/loaderwriter/writebehind/ClusteredWriteBehindTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/loaderwriter/writebehind/ClusteredWriteBehindTest.java @@ -15,6 +15,8 @@ */ package org.ehcache.clustered.client.internal.loaderwriter.writebehind; +import org.ehcache.clustered.client.internal.store.ServerStoreProxy; +import org.ehcache.clustered.common.internal.util.ChainBuilder; import org.ehcache.clustered.client.internal.store.operations.ChainResolver; import org.ehcache.clustered.client.internal.store.operations.ExpiryChainResolver; import org.ehcache.clustered.common.internal.store.operations.ConditionalRemoveOperation; @@ -26,7 +28,6 @@ import org.ehcache.clustered.common.internal.store.operations.codecs.OperationsCodec; import org.ehcache.clustered.common.internal.store.Chain; import org.ehcache.clustered.common.internal.store.Element; -import org.ehcache.clustered.common.internal.store.Util; import org.ehcache.clustered.loaderWriter.writebehind.RecordingLoaderWriter; import org.ehcache.core.spi.time.SystemTimeSource; import org.ehcache.core.spi.time.TimeSource; @@ -48,8 +49,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.mock; @@ -157,13 +158,37 @@ private void verifyEvents(List expected, Map expectedCh ClusteredWriteBehind clusteredWriteBehind = new ClusteredWriteBehind<>(clusteredWriteBehindStore, executorService, - TIME_SOURCE, resolver, cacheLoaderWriter, operationCodec); Chain elements = makeChain(expected, operationCodec); - when(clusteredWriteBehindStore.lock(1L)).thenReturn(elements); + when(clusteredWriteBehindStore.lock(1L)).thenReturn(new ServerStoreProxy.ChainEntry() { + @Override + public void append(ByteBuffer payLoad) throws TimeoutException { + + } + + @Override + public void replaceAtHead(Chain equivalent) { + + } + + @Override + public boolean isEmpty() { + return elements.isEmpty(); + } + + @Override + public int length() { + return elements.length(); + } + + @Override + public Iterator iterator() { + return elements.iterator(); + } + }); ArgumentCaptor chainArgumentCaptor = ArgumentCaptor.forClass(Chain.class); @@ -188,7 +213,7 @@ private void verifyEvents(List expected, Map expectedCh assertThat(entry.getValue(), is(expectedChainContents.get(entry.getKey()))); } - verify(clusteredWriteBehindStore).unlock(1L); + verify(clusteredWriteBehindStore).unlock(1L, false); } private Map convert(Chain chain, OperationsCodec codec, @@ -200,19 +225,18 @@ private Map convert(Chain chain, OperationsCodec cod Long key = operation.getKey(); PutOperation opResult = resolver.applyOperation(key, null, - operation, - timeSource.getTimeMillis()); + operation); result.put(key, opResult.getValue()); } return result; } private Chain makeChain(List expected, OperationsCodec operationsCodec) { - ByteBuffer[] byteBuffers = new ByteBuffer[expected.size()]; - for (int i = 0; i < byteBuffers.length; i++) { - byteBuffers[i] = operationsCodec.encode(expected.get(i).operation); + ChainBuilder builder = new ChainBuilder(); + for (EventInfo eventInfo : expected) { + builder.add(operationsCodec.encode(eventInfo.operation)); } - return chain(byteBuffers); + return builder.build(); } @@ -249,38 +273,6 @@ public void execute(Runnable command) { } } - public static Chain chain(ByteBuffer... buffers) { - final List list = new ArrayList<>(); - for (ByteBuffer b : buffers) { - list.add(b::asReadOnlyBuffer); - } - - return new Chain() { - - final List elements = Collections.unmodifiableList(list); - - @Override - public Iterator iterator() { - return elements.iterator(); - } - - @Override - public Iterator reverseIterator() { - return Util.reverseIterator(elements); - } - - @Override - public boolean isEmpty() { - return elements.isEmpty(); - } - - @Override - public int length() { - return elements.size(); - } - }; - } - private class EventInfo { private final Long key; private final Operation operation; diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/lock/VoltronReadWriteLockClientTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/lock/VoltronReadWriteLockClientTest.java similarity index 99% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/lock/VoltronReadWriteLockClientTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/lock/VoltronReadWriteLockClientTest.java index 86ee2b9724..f3af9e3595 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/lock/VoltronReadWriteLockClientTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/lock/VoltronReadWriteLockClientTest.java @@ -36,8 +36,8 @@ import static org.ehcache.clustered.common.internal.lock.LockMessaging.HoldType.READ; import static org.ehcache.clustered.common.internal.lock.LockMessaging.HoldType.WRITE; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import org.junit.Before; import org.terracotta.exception.EntityNotProvidedException; diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/lock/VoltronReadWriteLockTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/lock/VoltronReadWriteLockTest.java similarity index 99% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/lock/VoltronReadWriteLockTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/lock/VoltronReadWriteLockTest.java index a7f2f56a06..41de1e0c4f 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/lock/VoltronReadWriteLockTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/lock/VoltronReadWriteLockTest.java @@ -26,9 +26,9 @@ import static org.ehcache.clustered.common.internal.lock.LockMessaging.HoldType.READ; import static org.ehcache.clustered.common.internal.lock.LockMessaging.HoldType.WRITE; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.IsNull.notNullValue; import static org.hamcrest.core.IsNull.nullValue; -import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doThrow; diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/ClusterStateRepositoryReplicationTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/service/ClusterStateRepositoryReplicationTest.java similarity index 64% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/ClusterStateRepositoryReplicationTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/service/ClusterStateRepositoryReplicationTest.java index 19469217ef..86429fd73f 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/ClusterStateRepositoryReplicationTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/service/ClusterStateRepositoryReplicationTest.java @@ -30,8 +30,8 @@ import org.ehcache.clustered.lock.server.VoltronReadWriteLockServerEntityService; import org.ehcache.clustered.server.ClusterTierManagerServerEntityService; import org.ehcache.clustered.server.store.ClusterTierServerEntityService; -import org.ehcache.core.config.BaseCacheConfiguration; -import org.ehcache.core.internal.store.StoreConfigurationImpl; +import org.ehcache.impl.config.BaseCacheConfiguration; +import org.ehcache.core.store.StoreConfigurationImpl; import org.ehcache.spi.persistence.StateHolder; import org.junit.After; import org.junit.Before; @@ -50,9 +50,9 @@ import static org.ehcache.config.Eviction.noAdvice; import static org.ehcache.config.builders.ExpiryPolicyBuilder.noExpiration; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; public class ClusterStateRepositoryReplicationTest { @@ -91,82 +91,84 @@ public void tearDown() throws Exception { public void testClusteredStateRepositoryReplication() throws Exception { ClusteringServiceConfiguration configuration = ClusteringServiceConfigurationBuilder.cluster(URI.create(STRIPE_URI)) - .autoCreate() + .autoCreate(c -> c) .build(); ClusteringService service = new ClusteringServiceFactory().create(configuration); service.start(null); + try { + BaseCacheConfiguration config = new BaseCacheConfiguration<>(Long.class, String.class, noAdvice(), null, noExpiration(), + newResourcePoolsBuilder().with(clusteredDedicated("test", 2, org.ehcache.config.units.MemoryUnit.MB)).build()); + ClusteringService.ClusteredCacheIdentifier spaceIdentifier = (ClusteringService.ClusteredCacheIdentifier) service.getPersistenceSpaceIdentifier("test", + config); - BaseCacheConfiguration config = new BaseCacheConfiguration<>(Long.class, String.class, noAdvice(), null, noExpiration(), - newResourcePoolsBuilder().with(clusteredDedicated("test", 2, org.ehcache.config.units.MemoryUnit.MB)).build()); - ClusteringService.ClusteredCacheIdentifier spaceIdentifier = (ClusteringService.ClusteredCacheIdentifier) service.getPersistenceSpaceIdentifier("test", - config); + ServerStoreProxy serverStoreProxy = service.getServerStoreProxy(spaceIdentifier, new StoreConfigurationImpl<>(config, 1, null, null), Consistency.STRONG, mock(ServerCallback.class)); - ServerStoreProxy serverStoreProxy = service.getServerStoreProxy(spaceIdentifier, new StoreConfigurationImpl<>(config, 1, null, null), Consistency.STRONG, mock(ServerCallback.class)); + SimpleClusterTierClientEntity clientEntity = getEntity(serverStoreProxy); - SimpleClusterTierClientEntity clientEntity = getEntity(serverStoreProxy); + ClusterStateRepository stateRepository = new ClusterStateRepository(spaceIdentifier, "test", clientEntity); - ClusterStateRepository stateRepository = new ClusterStateRepository(spaceIdentifier, "test", clientEntity); + StateHolder testHolder = stateRepository.getPersistentStateHolder("testHolder", String.class, String.class, c -> true, null); + testHolder.putIfAbsent("One", "One"); + testHolder.putIfAbsent("Two", "Two"); - StateHolder testHolder = stateRepository.getPersistentStateHolder("testHolder", String.class, String.class, c -> true, null); - testHolder.putIfAbsent("One", "One"); - testHolder.putIfAbsent("Two", "Two"); + clusterControl.terminateActive(); + clusterControl.waitForActive(); - clusterControl.terminateActive(); - clusterControl.waitForActive(); - - assertThat(testHolder.get("One"), is("One")); - assertThat(testHolder.get("Two"), is("Two")); - - service.stop(); + assertThat(testHolder.get("One"), is("One")); + assertThat(testHolder.get("Two"), is("Two")); + } finally { + service.stop(); + } } @Test public void testClusteredStateRepositoryReplicationWithSerializableKV() throws Exception { ClusteringServiceConfiguration configuration = ClusteringServiceConfigurationBuilder.cluster(URI.create(STRIPE_URI)) - .autoCreate() + .autoCreate(c -> c) .build(); ClusteringService service = new ClusteringServiceFactory().create(configuration); service.start(null); + try { + BaseCacheConfiguration config = new BaseCacheConfiguration<>(Long.class, String.class, noAdvice(), null, noExpiration(), + newResourcePoolsBuilder().with(clusteredDedicated("test", 2, org.ehcache.config.units.MemoryUnit.MB)).build()); + ClusteringService.ClusteredCacheIdentifier spaceIdentifier = (ClusteringService.ClusteredCacheIdentifier) service.getPersistenceSpaceIdentifier("test", + config); - BaseCacheConfiguration config = new BaseCacheConfiguration<>(Long.class, String.class, noAdvice(), null, noExpiration(), - newResourcePoolsBuilder().with(clusteredDedicated("test", 2, org.ehcache.config.units.MemoryUnit.MB)).build()); - ClusteringService.ClusteredCacheIdentifier spaceIdentifier = (ClusteringService.ClusteredCacheIdentifier) service.getPersistenceSpaceIdentifier("test", - config); - - ServerStoreProxy serverStoreProxy = service.getServerStoreProxy(spaceIdentifier, new StoreConfigurationImpl<>(config, 1, null, null), Consistency.STRONG, mock(ServerCallback.class)); + ServerStoreProxy serverStoreProxy = service.getServerStoreProxy(spaceIdentifier, new StoreConfigurationImpl<>(config, 1, null, null), Consistency.STRONG, mock(ServerCallback.class)); - SimpleClusterTierClientEntity clientEntity = getEntity(serverStoreProxy); + SimpleClusterTierClientEntity clientEntity = getEntity(serverStoreProxy); - ClusterStateRepository stateRepository = new ClusterStateRepository(new ClusteringService.ClusteredCacheIdentifier() { - @Override - public String getId() { - return "testStateRepo"; - } + ClusterStateRepository stateRepository = new ClusterStateRepository(new ClusteringService.ClusteredCacheIdentifier() { + @Override + public String getId() { + return "testStateRepo"; + } - @Override - public Class getServiceType() { - return ClusteringService.class; - } - }, "test", clientEntity); + @Override + public Class getServiceType() { + return ClusteringService.class; + } + }, "test", clientEntity); - StateHolder testMap = stateRepository.getPersistentStateHolder("testMap", TestVal.class, TestVal.class, c -> true, null); - testMap.putIfAbsent(new TestVal("One"), new TestVal("One")); - testMap.putIfAbsent(new TestVal("Two"), new TestVal("Two")); + StateHolder testMap = stateRepository.getPersistentStateHolder("testMap", TestVal.class, TestVal.class, c -> true, null); + testMap.putIfAbsent(new TestVal("One"), new TestVal("One")); + testMap.putIfAbsent(new TestVal("Two"), new TestVal("Two")); - clusterControl.terminateActive(); - clusterControl.waitForActive(); + clusterControl.terminateActive(); + clusterControl.waitForActive(); - assertThat(testMap.get(new TestVal("One")), is(new TestVal("One"))); - assertThat(testMap.get(new TestVal("Two")), is(new TestVal("Two"))); + assertThat(testMap.get(new TestVal("One")), is(new TestVal("One"))); + assertThat(testMap.get(new TestVal("Two")), is(new TestVal("Two"))); - assertThat(testMap.entrySet(), hasSize(2)); - - service.stop(); + assertThat(testMap.entrySet(), hasSize(2)); + } finally { + service.stop(); + } } private static SimpleClusterTierClientEntity getEntity(ServerStoreProxy clusteringService) throws NoSuchFieldException, IllegalAccessException { diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/ClusterTierManagerClientEntityExceptionTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/service/ClusterTierManagerClientEntityExceptionTest.java similarity index 93% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/ClusterTierManagerClientEntityExceptionTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/service/ClusterTierManagerClientEntityExceptionTest.java index 9d9e6986f3..82ecc988d2 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/ClusterTierManagerClientEntityExceptionTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/service/ClusterTierManagerClientEntityExceptionTest.java @@ -30,8 +30,8 @@ import org.ehcache.config.builders.ResourcePoolsBuilder; import org.ehcache.config.units.MemoryUnit; import org.ehcache.core.config.store.StoreEventSourceConfiguration; -import org.ehcache.core.internal.store.StoreConfigurationImpl; import org.ehcache.core.spi.store.Store; +import org.ehcache.core.store.StoreConfigurationImpl; import org.ehcache.impl.internal.spi.serialization.DefaultSerializationProvider; import org.junit.After; import org.junit.Before; @@ -39,9 +39,10 @@ import java.net.URI; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.*; +import static org.junit.Assert.fail; /** * This class includes tests to ensure server-side exceptions returned as responses to @@ -75,21 +76,21 @@ public void removePassThroughServer() throws Exception { public void testServerExceptionPassThrough() throws Exception { ClusteringServiceConfiguration creationConfig = ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + .autoCreate(server -> server .defaultServerResource("defaultResource") .resourcePool("sharedPrimary", 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService creationService = new DefaultClusteringService(creationConfig); creationService.start(null); creationService.stop(); ClusteringServiceConfiguration accessConfig = ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .expecting() - .defaultServerResource("different") - .build(); + .expecting(server -> server + .defaultServerResource("different")) + .build(); DefaultClusteringService accessService = new DefaultClusteringService(accessConfig); /* * Induce an "InvalidStoreException: cluster tier 'cacheAlias' does not exist" on the server. @@ -98,7 +99,8 @@ public void testServerExceptionPassThrough() throws Exception { accessService.start(null); fail("Expecting ClusterTierManagerValidationException"); - } catch (ClusterTierManagerValidationException e) { + } catch (RuntimeException e) { + assertThat(e.getCause(), is(instanceOf(ClusterTierManagerValidationException.class))); /* * Find the last ClusterTierManagerClientEntity involved exception in the causal chain. This diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/ClusteringServiceFactoryTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/service/ClusteringServiceFactoryTest.java similarity index 61% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/ClusteringServiceFactoryTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/service/ClusteringServiceFactoryTest.java index c25cf95aff..25ba158741 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/ClusteringServiceFactoryTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/service/ClusteringServiceFactoryTest.java @@ -17,12 +17,15 @@ package org.ehcache.clustered.client.internal.service; import org.ehcache.core.spi.service.ServiceFactory; -import org.ehcache.core.internal.util.ClassLoading; +import org.ehcache.core.util.ClassLoading; import org.junit.Test; -import java.util.ServiceLoader; +import java.util.stream.Collectors; -import static org.junit.Assert.fail; +import static java.util.Spliterators.spliterator; +import static java.util.stream.StreamSupport.stream; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsCollectionContaining.hasItem; /** * @author Clifford W. Johnson @@ -31,17 +34,7 @@ public class ClusteringServiceFactoryTest { @Test public void testServiceLocator() throws Exception { - String expectedFactory = ClusteringServiceFactory.class.getName(); - @SuppressWarnings({"unchecked", "rawtypes"}) - ServiceLoader> factories = (ServiceLoader) ClassLoading.libraryServiceLoaderFor(ServiceFactory.class); - - for (ServiceFactory factory : factories) { - if (factory.getClass().getName().equals(expectedFactory)) { - return; - } - } - - fail("Expected factory not found"); + assertThat(stream(spliterator(ClassLoading.servicesOfType(ServiceFactory.class).iterator(), Long.MAX_VALUE, 0), false).map(Object::getClass).collect(Collectors.toList()), + hasItem(ClusteringServiceFactory.class)); } - } diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/ConnectionClosedTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/service/ConnectionClosedTest.java similarity index 65% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/ConnectionClosedTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/service/ConnectionClosedTest.java index ea417720cf..03dadd4f10 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/ConnectionClosedTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/service/ConnectionClosedTest.java @@ -23,6 +23,7 @@ import org.ehcache.config.builders.CacheManagerBuilder; import org.ehcache.config.builders.ResourcePoolsBuilder; import org.ehcache.config.units.MemoryUnit; +import org.ehcache.core.internal.resilience.ThrowingResilienceStrategy; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -33,23 +34,21 @@ import java.time.Duration; import java.util.Collection; import java.util.Properties; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; import static org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder.clusteredDedicated; import static org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder.cluster; import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; +import static org.terracotta.utilities.test.matchers.Eventually.within; public class ConnectionClosedTest { private static final URI CLUSTER_URI = URI.create("terracotta://connection.com:9540/timeout"); @Before - public void definePassthroughServer() throws Exception { + public void definePassthroughServer() { UnitTestConnectionService.add(CLUSTER_URI, new UnitTestConnectionService.PassthroughServerBuilder() .resource("primary-server-resource", 64, MemoryUnit.MB) @@ -57,7 +56,7 @@ public void definePassthroughServer() throws Exception { } @After - public void removePassthroughServer() throws Exception { + public void removePassthroughServer() { try { UnitTestConnectionService.remove(CLUSTER_URI); } catch (IllegalStateException e) { @@ -78,47 +77,31 @@ public void testCacheOperationThrowsAfterConnectionClosed() throws Exception { .timeouts() .connection(Duration.ofSeconds(20)) .build()) - .autoCreate()) + .autoCreate(c -> c)) .withCache("clustered-cache", newCacheConfigurationBuilder(Long.class, String.class, - resourcePoolsBuilder)); - PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(true); + resourcePoolsBuilder).withResilienceStrategy(new ThrowingResilienceStrategy<>())); + try (PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(true)) { - Cache cache = cacheManager.getCache("clustered-cache", Long.class, String.class); + Cache cache = cacheManager.getCache("clustered-cache", Long.class, String.class); - Collection connectionProperties = UnitTestConnectionService.getConnectionProperties(CLUSTER_URI); + Collection connectionProperties = UnitTestConnectionService.getConnectionProperties(CLUSTER_URI); - assertThat(connectionProperties.size(), is(1)); - Properties properties = connectionProperties.iterator().next(); + assertThat(connectionProperties.size(), is(1)); + Properties properties = connectionProperties.iterator().next(); - assertThat(properties.getProperty(ConnectionPropertyNames.CONNECTION_TIMEOUT), is("20000")); + assertThat(properties.getProperty(ConnectionPropertyNames.CONNECTION_TIMEOUT), is("20000")); - cache.put(1L, "value"); - assertThat(cache.get(1L), is("value")); + cache.put(1L, "value"); + assertThat(cache.get(1L), is("value")); - Collection connections = UnitTestConnectionService.getConnections(CLUSTER_URI); + Collection connections = UnitTestConnectionService.getConnections(CLUSTER_URI); - assertThat(connections.size(), is(1)); + assertThat(connections.size(), is(1)); - Connection connection = connections.iterator().next(); - - connection.close(); - - CompletableFuture future = CompletableFuture.supplyAsync(() -> { - while (true) { - try { - Thread.sleep(200); - } catch (InterruptedException e) { - // - } - String result; - if ((result = cache.get(1L)) != null) { - return result; - } - } - }); - - assertThat(future.get(5, TimeUnit.SECONDS), is("value")); + connections.iterator().next().close(); + assertThat(() -> cache.get(1L), within(Duration.ofSeconds(60)).is("value")); + } } } diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/ConnectionStateTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/service/ConnectionStateTest.java similarity index 84% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/ConnectionStateTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/service/ConnectionStateTest.java index e2a12e8793..daa021c4e9 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/ConnectionStateTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/service/ConnectionStateTest.java @@ -28,9 +28,7 @@ import org.ehcache.impl.serialization.StringSerializer; import org.junit.After; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import org.terracotta.connection.Connection; import java.io.IOException; @@ -38,22 +36,21 @@ import java.util.Collection; import java.util.Properties; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; -import static org.junit.Assert.assertThat; +import static org.terracotta.utilities.test.matchers.ThrowsMatcher.threw; public class ConnectionStateTest { - private static URI CLUSTER_URI = URI.create("terracotta://localhost:9510"); + private static final URI CLUSTER_URI = URI.create("terracotta://localhost:9510"); private final ClusteringServiceConfiguration serviceConfiguration = ClusteringServiceConfigurationBuilder .cluster(CLUSTER_URI) - .autoCreate() + .autoCreate(c -> c) .build(); - @Rule - public ExpectedException expectedException = ExpectedException.none(); - @Before public void definePassthroughServer() { UnitTestConnectionService.add(CLUSTER_URI, @@ -65,7 +62,6 @@ public void definePassthroughServer() { @After public void removePassthrough() { - expectedException.expect(IllegalStateException.class); UnitTestConnectionService.remove(CLUSTER_URI); } @@ -73,17 +69,18 @@ public void removePassthrough() { public void testInitializeStateAfterConnectionCloses() throws Exception { ConnectionState connectionState = new ConnectionState(Timeouts.DEFAULT, new Properties(), serviceConfiguration); - connectionState.initClusterConnection(); + connectionState.initClusterConnection(Runnable::run); closeConnection(); - expectedException.expect(IllegalStateException.class); - connectionState.getConnection().close(); + Connection connection = connectionState.getConnection(); + assertThat(connection::close, threw(instanceOf(IllegalStateException.class))); connectionState.initializeState(); assertThat(connectionState.getConnection(), notNullValue()); assertThat(connectionState.getEntityFactory(), notNullValue()); + assertThat(connectionState.getEntity(), notNullValue()); connectionState.getConnection().close(); @@ -93,7 +90,7 @@ public void testInitializeStateAfterConnectionCloses() throws Exception { public void testCreateClusterTierEntityAfterConnectionCloses() throws Exception { ConnectionState connectionState = new ConnectionState(Timeouts.DEFAULT, new Properties(), serviceConfiguration); - connectionState.initClusterConnection(); + connectionState.initClusterConnection(Runnable::run); connectionState.initializeState(); closeConnection(); @@ -105,7 +102,7 @@ public void testCreateClusterTierEntityAfterConnectionCloses() throws Exception ClusterTierClientEntity clientEntity = connectionState.createClusterTierClientEntity("cache1", serverStoreConfiguration, false); assertThat(clientEntity, notNullValue()); - + assertThat(connectionState.getEntity(), notNullValue()); } //For test to simulate connection close as result of lease expiry @@ -114,9 +111,7 @@ private void closeConnection() throws IOException { assertThat(connections.size(), is(1)); - Connection connection = connections.iterator().next(); - - connection.close(); + connections.iterator().next().close(); } } diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/DefaultClusteringServiceDestroyTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/service/DefaultClusteringServiceDestroyTest.java similarity index 91% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/DefaultClusteringServiceDestroyTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/service/DefaultClusteringServiceDestroyTest.java index e61074d825..157f7cbdb5 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/DefaultClusteringServiceDestroyTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/service/DefaultClusteringServiceDestroyTest.java @@ -16,12 +16,10 @@ package org.ehcache.clustered.client.internal.service; -import org.ehcache.clustered.client.config.ClusteringServiceConfiguration; import org.ehcache.clustered.client.internal.ClusterTierManagerClientEntity; import org.ehcache.clustered.client.internal.MockConnectionService; import org.ehcache.clustered.client.internal.lock.VoltronReadWriteLockClient; import org.ehcache.clustered.client.internal.store.InternalClusterTierClientEntity; -import org.ehcache.clustered.common.ServerSideConfiguration; import org.ehcache.clustered.common.internal.ClusterTierManagerConfiguration; import org.ehcache.clustered.common.internal.exceptions.DestroyInProgressException; import org.ehcache.clustered.common.internal.lock.LockMessaging; @@ -39,10 +37,10 @@ import java.util.HashSet; import java.util.Set; -import static java.util.Collections.emptyMap; +import static org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder.cluster; import static org.ehcache.clustered.common.EhcacheEntityVersion.ENTITY_VERSION; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -91,8 +89,8 @@ public void testDestroyAllFullyMocked() throws Exception { when(tierEntityRef.destroy()).thenReturn(true); when(managerEntityRef.destroy()).thenReturn(true); - DefaultClusteringService service = new DefaultClusteringService(new ClusteringServiceConfiguration(URI - .create("mock://localhost/whatever"))); + DefaultClusteringService service = new DefaultClusteringService(cluster((URI + .create("mock://localhost/whatever"))).build()); service.startForMaintenance(null, MaintainableService.MaintenanceScope.CACHE_MANAGER); service.destroyAll(); @@ -103,8 +101,6 @@ public void testDestroyAllFullyMocked() throws Exception { @Test public void testAutoCreateOnPartialDestroyState() throws Exception { - ServerSideConfiguration serverConfig = new ServerSideConfiguration("default", emptyMap()); - mockLockForWriteLockSuccess(); when(getEntityRef(ClusterTierManagerClientEntity.class)).thenReturn(managerEntityRef); @@ -114,14 +110,14 @@ public void testAutoCreateOnPartialDestroyState() throws Exception { doThrow(new EntityAlreadyExistsException("className", "entityName")) // Next time simulate creation .doNothing() - .when(managerEntityRef).create(new ClusterTierManagerConfiguration("whatever", serverConfig)); + .when(managerEntityRef).create(new ClusterTierManagerConfiguration("whatever", any())); // And can be fetch when(managerEntityRef.fetchEntity(null)).thenReturn(managerEntity); // However validate indicates destroy in progress doThrow(new DestroyInProgressException("destroying")) // Next time validation succeeds .doNothing() - .when(managerEntity).validate(serverConfig); + .when(managerEntity).validate(any()); Set stores = new HashSet<>(); stores.add("store1"); @@ -133,8 +129,8 @@ public void testAutoCreateOnPartialDestroyState() throws Exception { when(tierEntityRef.destroy()).thenReturn(true); when(managerEntityRef.destroy()).thenReturn(true); - DefaultClusteringService service = new DefaultClusteringService(new ClusteringServiceConfiguration(URI - .create("mock://localhost/whatever"), true, serverConfig)); + DefaultClusteringService service = new DefaultClusteringService(cluster(URI + .create("mock://localhost/whatever")).autoCreate(s -> s).build()); service.start(null); verify(managerEntity).prepareForDestroy(); @@ -154,8 +150,8 @@ public void testFetchOnPartialDestroyState() throws Exception { // However validate indicates destroy in progress doThrow(new DestroyInProgressException("destroying")).when(managerEntity).validate(null); - DefaultClusteringService service = new DefaultClusteringService(new ClusteringServiceConfiguration(URI - .create("mock://localhost/whatever"))); + DefaultClusteringService service = new DefaultClusteringService(cluster(URI + .create("mock://localhost/whatever")).build()); try { service.start(null); fail("IllegalStateException expected"); @@ -183,8 +179,8 @@ public void testDestroyOnPartialDestroyState() throws Exception { when(tierEntityRef.destroy()).thenReturn(true); when(managerEntityRef.destroy()).thenReturn(true); - DefaultClusteringService service = new DefaultClusteringService(new ClusteringServiceConfiguration(URI - .create("mock://localhost/whatever"))); + DefaultClusteringService service = new DefaultClusteringService(cluster(URI + .create("mock://localhost/whatever")).build()); service.startForMaintenance(null, MaintainableService.MaintenanceScope.CACHE_MANAGER); service.destroyAll(); diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/DefaultClusteringServiceTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/service/DefaultClusteringServiceTest.java similarity index 90% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/DefaultClusteringServiceTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/service/DefaultClusteringServiceTest.java index fc76c80768..8100a13bb4 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/DefaultClusteringServiceTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/service/DefaultClusteringServiceTest.java @@ -22,6 +22,7 @@ import org.ehcache.clustered.client.config.ClusteringServiceConfiguration; import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder; import org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder; +import org.ehcache.clustered.client.internal.ClusterTierManagerClientEntityFactory; import org.ehcache.clustered.client.internal.ClusterTierManagerClientEntityService; import org.ehcache.clustered.client.internal.UnitTestConnectionService; import org.ehcache.clustered.client.internal.UnitTestConnectionService.PassthroughServerBuilder; @@ -35,8 +36,6 @@ import org.ehcache.clustered.client.service.ClusteringService; import org.ehcache.clustered.client.service.ClusteringService.ClusteredCacheIdentifier; import org.ehcache.clustered.common.Consistency; -import org.ehcache.clustered.common.ServerSideConfiguration; -import org.ehcache.clustered.common.ServerSideConfiguration.Pool; import org.ehcache.clustered.common.internal.exceptions.InvalidServerStoreConfigurationException; import org.ehcache.clustered.lock.server.VoltronReadWriteLockServerEntityService; import org.ehcache.clustered.server.ObservableEhcacheServerEntityService; @@ -48,8 +47,8 @@ import org.ehcache.config.builders.ResourcePoolsBuilder; import org.ehcache.config.units.MemoryUnit; import org.ehcache.core.config.store.StoreEventSourceConfiguration; -import org.ehcache.core.internal.store.StoreConfigurationImpl; import org.ehcache.core.spi.store.Store; +import org.ehcache.core.store.StoreConfigurationImpl; import org.ehcache.impl.internal.spi.serialization.DefaultSerializationProvider; import org.ehcache.spi.persistence.PersistableResourceService; import org.ehcache.spi.persistence.StateRepository; @@ -57,9 +56,7 @@ import org.hamcrest.Matchers; import org.junit.After; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import org.terracotta.connection.ConnectionPropertyNames; import org.terracotta.exception.EntityNotFoundException; @@ -77,21 +74,25 @@ import static org.ehcache.clustered.client.config.ClusteredResourceType.Types.DEDICATED; import static org.ehcache.clustered.client.config.ClusteredResourceType.Types.SHARED; +import static org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder.cluster; import static org.ehcache.clustered.client.internal.service.TestServiceProvider.providerContaining; import static org.ehcache.config.ResourceType.Core.DISK; import static org.ehcache.config.ResourceType.Core.HEAP; import static org.ehcache.config.ResourceType.Core.OFFHEAP; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.endsWith; +import static org.hamcrest.Matchers.hasProperty; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.sameInstance; +import static org.hamcrest.Matchers.startsWith; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; @@ -104,9 +105,6 @@ public class DefaultClusteringServiceTest { private ObservableEhcacheServerEntityService observableEhcacheServerEntityService; private ObservableClusterTierServerEntityService observableClusterTierServerEntityService; - @Rule - public ExpectedException expectedException = ExpectedException.none(); - @Before public void definePassthroughServer() throws Exception { observableEhcacheServerEntityService = new ObservableEhcacheServerEntityService(); @@ -134,8 +132,8 @@ public void removePassthroughServer() throws Exception { @Test public void testHandlesResourceType() throws Exception { ClusteringServiceConfiguration configuration = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(c -> c) .build(); DefaultClusteringService service = new DefaultClusteringService(configuration); @@ -170,8 +168,8 @@ public int getTierHeight() { @Test public void testGetPersistenceSpaceIdentifier() throws Exception { ClusteringServiceConfiguration configuration = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(c -> c) .build(); DefaultClusteringService service = new DefaultClusteringService(configuration); @@ -189,7 +187,7 @@ public void testCreate() throws Exception { .with(ClusteredResourcePoolBuilder.clusteredShared("primary"))); ClusteringServiceConfiguration configuration = ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + .autoCreate(c -> c) .build(); DefaultClusteringService service = new DefaultClusteringService(configuration); @@ -203,9 +201,9 @@ public void testCreate() throws Exception { public void testConnectionName() throws Exception { String entityIdentifier = "my-application"; ClusteringServiceConfiguration configuration = - new ClusteringServiceConfiguration( - URI.create(CLUSTER_URI_BASE + entityIdentifier), - true, new ServerSideConfiguration(Collections.emptyMap())); + ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + entityIdentifier)) + .autoCreate(s -> s) + .build(); DefaultClusteringService service = new DefaultClusteringService(configuration); service.start(null); @@ -223,7 +221,7 @@ public void testStartStopAutoCreate() throws Exception { URI clusterUri = URI.create(CLUSTER_URI_BASE + "my-application"); ClusteringServiceConfiguration configuration = ClusteringServiceConfigurationBuilder.cluster(clusterUri) - .autoCreate() + .autoCreate(c -> c) .build(); DefaultClusteringService service = new DefaultClusteringService(configuration); assertThat(service.isConnected(), is(false)); @@ -249,7 +247,7 @@ public void testStartStopAutoCreate() throws Exception { public void testStartStopNoAutoCreate() throws Exception { URI clusterUri = URI.create(CLUSTER_URI_BASE + "my-application"); ClusteringServiceConfiguration configuration = - ClusteringServiceConfigurationBuilder.cluster(clusterUri) + cluster(clusterUri) .build(); DefaultClusteringService service = new DefaultClusteringService(configuration); try { @@ -274,8 +272,8 @@ public void testStartStopNoAutoCreate() throws Exception { @Test public void testStartStopAutoCreateTwiceA() throws Exception { ClusteringServiceConfiguration configuration = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(c -> c) .build(); DefaultClusteringService firstService = new DefaultClusteringService(configuration); firstService.start(null); @@ -305,8 +303,8 @@ public void testStartStopAutoCreateTwiceA() throws Exception { @Test public void testStartStopAutoCreateTwiceB() throws Exception { ClusteringServiceConfiguration configuration = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(c -> c) .build(); DefaultClusteringService firstService = new DefaultClusteringService(configuration); firstService.start(null); @@ -334,8 +332,8 @@ public void testStartStopAutoCreateTwiceB() throws Exception { public void testStartForMaintenanceAutoStart() throws Exception { URI clusterUri = URI.create(CLUSTER_URI_BASE + "my-application"); ClusteringServiceConfiguration configuration = - ClusteringServiceConfigurationBuilder.cluster(clusterUri) - .autoCreate() + cluster(clusterUri) + .autoCreate(c -> c) .build(); DefaultClusteringService service = new DefaultClusteringService(configuration); assertThat(service.isConnected(), is(false)); @@ -356,8 +354,8 @@ public void testStartForMaintenanceAutoStart() throws Exception { @Test public void testStartForMaintenanceOtherAutoCreate() throws Exception { ClusteringServiceConfiguration configuration = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(c -> c) .build(); DefaultClusteringService createService = new DefaultClusteringService(configuration); createService.start(null); @@ -388,8 +386,8 @@ public void testStartForMaintenanceOtherAutoCreate() throws Exception { @Test public void testStartForMaintenanceOtherCreated() throws Exception { ClusteringServiceConfiguration configuration = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(c -> c) .build(); DefaultClusteringService createService = new DefaultClusteringService(configuration); createService.start(null); @@ -421,8 +419,8 @@ public void testStartForMaintenanceOtherCreated() throws Exception { @Test public void testMultipleAutoCreateClientsRunConcurrently() throws InterruptedException, ExecutionException { final ClusteringServiceConfiguration configuration = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(c -> c) .build(); Callable task = () -> { @@ -448,8 +446,8 @@ public void testMultipleAutoCreateClientsRunConcurrently() throws InterruptedExc @Test public void testStartForMaintenanceInterlock() throws Exception { ClusteringServiceConfiguration configuration = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(c -> c) .build(); DefaultClusteringService maintenanceService1 = new DefaultClusteringService(configuration); maintenanceService1.startForMaintenance(null, MaintainableService.MaintenanceScope.CACHE_MANAGER); @@ -471,8 +469,8 @@ public void testStartForMaintenanceInterlock() throws Exception { @Test public void testStartForMaintenanceSequence() throws Exception { ClusteringServiceConfiguration configuration = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(c -> c) .build(); DefaultClusteringService maintenanceService1 = new DefaultClusteringService(configuration); maintenanceService1.startForMaintenance(null, MaintainableService.MaintenanceScope.CACHE_MANAGER); @@ -492,13 +490,13 @@ public void testStartForMaintenanceSequence() throws Exception { @Test public void testBasicConfiguration() throws Exception { ClusteringServiceConfiguration configuration = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(server -> server .defaultServerResource("defaultResource") .resourcePool("sharedPrimary", 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService createService = new DefaultClusteringService(configuration); createService.start(null); @@ -528,13 +526,13 @@ public void testGetServerStoreProxySharedAutoCreate() throws Exception { String cacheAlias = "cacheAlias"; String targetPool = "sharedPrimary"; ClusteringServiceConfiguration configuration = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(server -> server .defaultServerResource("defaultResource") .resourcePool(targetPool, 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService service = new DefaultClusteringService(configuration); service.start(null); @@ -577,25 +575,25 @@ public void testGetServerStoreProxySharedNoAutoCreateNonExistent() throws Except String cacheAlias = "cacheAlias"; String targetPool = "sharedPrimary"; ClusteringServiceConfiguration creationConfig = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(server -> server .defaultServerResource("defaultResource") .resourcePool(targetPool, 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService creationService = new DefaultClusteringService(creationConfig); creationService.start(null); creationService.stop(); ClusteringServiceConfiguration accessConfig = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .expecting() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .expecting(server -> server .defaultServerResource("defaultResource") .resourcePool(targetPool, 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService accessService = new DefaultClusteringService(accessConfig); accessService.start(null); @@ -635,13 +633,13 @@ public void testGetServerStoreProxySharedNoAutoCreateExists() throws Exception { String cacheAlias = "cacheAlias"; String targetPool = "sharedPrimary"; ClusteringServiceConfiguration creationConfig = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(server -> server .defaultServerResource("defaultResource") .resourcePool(targetPool, 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService creationService = new DefaultClusteringService(creationConfig); creationService.start(null); @@ -672,13 +670,13 @@ public void testGetServerStoreProxySharedNoAutoCreateExists() throws Exception { ClusteringServiceConfiguration accessConfig = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .expecting() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .expecting(server -> server .defaultServerResource("defaultResource") .resourcePool(targetPool, 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService accessService = new DefaultClusteringService(accessConfig); accessService.start(null); @@ -716,13 +714,13 @@ public void testGetServerStoreProxySharedAutoCreateTwice() throws Exception { String cacheAlias = "cacheAlias"; String targetPool = "sharedPrimary"; ClusteringServiceConfiguration configuration = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(server -> server .defaultServerResource("defaultResource") .resourcePool(targetPool, 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService firstService = new DefaultClusteringService(configuration); firstService.start(null); @@ -778,13 +776,13 @@ public void testReleaseServerStoreProxyShared() throws Exception { String cacheAlias = "cacheAlias"; String targetPool = "sharedPrimary"; ClusteringServiceConfiguration configuration = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(server -> server .defaultServerResource("defaultResource") .resourcePool(targetPool, 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService creationService = new DefaultClusteringService(configuration); creationService.start(null); @@ -829,13 +827,13 @@ public void testGetServerStoreProxyDedicatedAutoCreate() throws Exception { String cacheAlias = "cacheAlias"; String targetResource = "serverResource2"; ClusteringServiceConfiguration configuration = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(server -> server .defaultServerResource("defaultResource") .resourcePool("sharedPrimary", 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService service = new DefaultClusteringService(configuration); service.start(null); @@ -880,25 +878,25 @@ public void testGetServerStoreProxyDedicatedNoAutoCreateNonExistent() throws Exc String cacheAlias = "cacheAlias"; String targetResource = "serverResource2"; ClusteringServiceConfiguration creationConfig = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(server -> server .defaultServerResource("defaultResource") .resourcePool("sharedPrimary", 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService creationService = new DefaultClusteringService(creationConfig); creationService.start(null); creationService.stop(); ClusteringServiceConfiguration accessConfig = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .expecting() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .expecting(server -> server .defaultServerResource("defaultResource") .resourcePool("sharedPrimary", 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService accessService = new DefaultClusteringService(accessConfig); accessService.start(null); @@ -940,13 +938,13 @@ public void testGetServerStoreProxyDedicatedNoAutoCreateExists() throws Exceptio String cacheAlias = "cacheAlias"; String targetResource = "serverResource2"; ClusteringServiceConfiguration creationConfig = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(server -> server .defaultServerResource("defaultResource") .resourcePool("sharedPrimary", 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService creationService = new DefaultClusteringService(creationConfig); creationService.start(null); @@ -977,13 +975,13 @@ public void testGetServerStoreProxyDedicatedNoAutoCreateExists() throws Exceptio assertThat(clusterTierActiveEntity.getConnectedClients(), empty()); ClusteringServiceConfiguration accessConfig = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .expecting() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .expecting(server -> server .defaultServerResource("defaultResource") .resourcePool("sharedPrimary", 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService accessService = new DefaultClusteringService(accessConfig); accessService.start(null); @@ -1023,13 +1021,13 @@ public void testGetServerStoreProxyDedicatedAutoCreateTwice() throws Exception { String cacheAlias = "cacheAlias"; String targetResource = "serverResource2"; ClusteringServiceConfiguration configuration = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(server -> server .defaultServerResource("defaultResource") .resourcePool("sharedPrimary", 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService firstService = new DefaultClusteringService(configuration); firstService.start(null); @@ -1090,13 +1088,13 @@ public void testReleaseServerStoreProxyDedicated() throws Exception { String cacheAlias = "cacheAlias"; String targetResource = "serverResource2"; ClusteringServiceConfiguration configuration = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(server -> server .defaultServerResource("defaultResource") .resourcePool("sharedPrimary", 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService creationService = new DefaultClusteringService(configuration); creationService.start(null); @@ -1142,13 +1140,13 @@ public void testGetServerStoreProxySharedDestroy() throws Exception { String cacheAlias = "cacheAlias"; String targetPool = "sharedPrimary"; ClusteringServiceConfiguration configuration = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(server -> server .defaultServerResource("defaultResource") .resourcePool(targetPool, 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService creationService = new DefaultClusteringService(configuration); creationService.start(null); @@ -1195,13 +1193,13 @@ public void testGetServerStoreProxyDedicatedDestroy() throws Exception { String cacheAlias = "cacheAlias"; String targetResource = "serverResource2"; ClusteringServiceConfiguration configuration = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(server -> server .defaultServerResource("defaultResource") .resourcePool("sharedPrimary", 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService creationService = new DefaultClusteringService(configuration); creationService.start(null); @@ -1250,28 +1248,26 @@ public void testDestroyCantBeCalledIfStopped() throws Exception { String cacheAlias = "cacheAlias"; String targetResource = "serverResource2"; ClusteringServiceConfiguration configuration = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() - .defaultServerResource("defaultResource") + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(server -> server + .defaultServerResource("defaultResource")) .build(); DefaultClusteringService creationService = new DefaultClusteringService(configuration); - expectedException.expect(IllegalStateException.class); - expectedException.expectMessage(endsWith(" should be started to call destroy")); - - creationService.destroy(cacheAlias); + IllegalStateException thrown = assertThrows(IllegalStateException.class, () -> creationService.destroy(cacheAlias)); + assertThat(thrown, hasProperty("message", endsWith(" should be started to call destroy"))); } @Test public void testDestroyAllNoStores() throws Exception { ClusteringServiceConfiguration configuration = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(server -> server .defaultServerResource("defaultResource") .resourcePool("sharedPrimary", 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService createService = new DefaultClusteringService(configuration); createService.start(null); createService.stop(); @@ -1309,13 +1305,13 @@ public void testDestroyAllNoStores() throws Exception { @Test public void testDestroyAllWithStores() throws Exception { ClusteringServiceConfiguration configuration = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(server -> server .defaultServerResource("defaultResource") .resourcePool("sharedPrimary", 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService createService = new DefaultClusteringService(configuration); createService.start(null); @@ -1371,13 +1367,13 @@ public void testDestroyAllWithStores() throws Exception { @Test public void testStartNoAutoCreateThenAutoCreate() throws Exception { ClusteringServiceConfiguration creationConfigBad = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .expecting() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .expecting(server -> server .defaultServerResource("defaultResource") .resourcePool("sharedPrimary", 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService creationServiceBad = new DefaultClusteringService(creationConfigBad); try { @@ -1391,13 +1387,13 @@ public void testStartNoAutoCreateThenAutoCreate() throws Exception { assertThat(activeEntitiesBad.size(), is(0)); ClusteringServiceConfiguration creationConfigGood = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(server -> server .defaultServerResource("defaultResource") .resourcePool("sharedPrimary", 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService creationServiceGood = new DefaultClusteringService(creationConfigGood); creationServiceGood.start(null); @@ -1407,13 +1403,13 @@ public void testStartNoAutoCreateThenAutoCreate() throws Exception { public void testStoreValidation_autoCreateConfigGood_autoCreateConfigBad() throws Exception { String cacheAlias = "cacheAlias"; ClusteringServiceConfiguration config = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(server -> server .defaultServerResource("defaultResource") .resourcePool("sharedPrimary", 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService creationService = new DefaultClusteringService(config); creationService.start(null); @@ -1469,13 +1465,13 @@ public void testStoreValidation_autoCreateConfigGood_autoCreateConfigBad() throw public void testStoreValidation_autoCreateConfigGood_autoCreateConfigGood() throws Exception { String cacheAlias = "cacheAlias"; ClusteringServiceConfiguration config = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(server -> server .defaultServerResource("defaultResource") .resourcePool("sharedPrimary", 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService creationService = new DefaultClusteringService(config); creationService.start(null); @@ -1521,13 +1517,13 @@ public void testStoreValidation_autoCreateConfigGood_autoCreateConfigGood() thro public void testStoreValidation_autoCreateConfigBad() throws Exception { String cacheAlias = "cacheAlias"; ClusteringServiceConfiguration config = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(server -> server .defaultServerResource("defaultResource") .resourcePool("sharedPrimary", 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService creationService = new DefaultClusteringService(config); creationService.start(null); @@ -1568,13 +1564,13 @@ public void testStoreValidation_autoCreateConfigBad() throws Exception { public void testStoreValidation_autoCreateConfigGood_noAutoCreateConfigBad() throws Exception { String cacheAlias = "cacheAlias"; ClusteringServiceConfiguration autoConfig = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(server -> server .defaultServerResource("defaultResource") .resourcePool("sharedPrimary", 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService creationService = new DefaultClusteringService(autoConfig); creationService.start(null); @@ -1587,13 +1583,13 @@ public void testStoreValidation_autoCreateConfigGood_noAutoCreateConfigBad() thr creationService.getServerStoreProxy(getClusteredCacheIdentifier(creationService, cacheAlias), creationStoreConfig, Consistency.EVENTUAL, mock(ServerCallback.class)); ClusteringServiceConfiguration noAutoConfig = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .expecting() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .expecting(server -> server .defaultServerResource("defaultResource") .resourcePool("sharedPrimary", 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService accessService = new DefaultClusteringService(noAutoConfig); accessService.start(null); @@ -1637,13 +1633,13 @@ public void testStoreValidation_autoCreateConfigGood_noAutoCreateConfigBad() thr public void testStoreValidation_autoCreateConfigGood_noAutoCreateConfigGood() throws Exception { String cacheAlias = "cacheAlias"; ClusteringServiceConfiguration autoConfig = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(server -> server .defaultServerResource("defaultResource") .resourcePool("sharedPrimary", 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService creationService = new DefaultClusteringService(autoConfig); creationService.start(null); @@ -1657,13 +1653,12 @@ public void testStoreValidation_autoCreateConfigGood_noAutoCreateConfigGood() th ClusteringServiceConfiguration noAutoConfig = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .expecting() - .defaultServerResource("defaultResource") + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .expecting(server -> server.defaultServerResource("defaultResource") .resourcePool("sharedPrimary", 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService accessService = new DefaultClusteringService(noAutoConfig); accessService.start(null); @@ -1699,13 +1694,13 @@ public void testStoreValidation_autoCreateConfigGood_noAutoCreateConfigGood() th public void testStoreValidation_MismatchedPoolTypes_ConfiguredDedicatedValidateShared() throws Exception { String cacheAlias = "cacheAlias"; ClusteringServiceConfiguration creationConfig = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(server -> server .defaultServerResource("defaultResource") .resourcePool("sharedPrimary", 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService creationService = new DefaultClusteringService(creationConfig); creationService.start(null); @@ -1720,13 +1715,13 @@ public void testStoreValidation_MismatchedPoolTypes_ConfiguredDedicatedValidateS creationService.stop(); ClusteringServiceConfiguration accessConfig = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(server -> server .defaultServerResource("defaultResource") .resourcePool("sharedPrimary", 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService accessService = new DefaultClusteringService(accessConfig); accessService.start(null); @@ -1767,13 +1762,13 @@ public void testStoreValidation_MismatchedPoolTypes_ConfiguredDedicatedValidateS public void testStoreValidation_MismatchedPoolTypes_ConfiguredSharedValidateDedicated() throws Exception { String cacheAlias = "cacheAlias"; ClusteringServiceConfiguration creationConfig = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(server -> server .defaultServerResource("defaultResource") .resourcePool("sharedPrimary", 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService creationService = new DefaultClusteringService(creationConfig); creationService.start(null); @@ -1788,13 +1783,13 @@ public void testStoreValidation_MismatchedPoolTypes_ConfiguredSharedValidateDedi creationService.stop(); ClusteringServiceConfiguration accessConfig = - ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + "my-application")) - .autoCreate() + cluster(URI.create(CLUSTER_URI_BASE + "my-application")) + .autoCreate(server -> server .defaultServerResource("defaultResource") .resourcePool("sharedPrimary", 2, MemoryUnit.MB, "serverResource1") .resourcePool("sharedSecondary", 2, MemoryUnit.MB, "serverResource2") - .resourcePool("sharedTertiary", 4, MemoryUnit.MB) - .build(); + .resourcePool("sharedTertiary", 4, MemoryUnit.MB)) + .build(); DefaultClusteringService accessService = new DefaultClusteringService(accessConfig); accessService.start(null); @@ -1872,9 +1867,9 @@ private ClusteredCacheIdentifier getClusteredCacheIdentifier( public void testGetServerStoreProxyReturnsEventualStore() throws Exception { String entityIdentifier = "my-application"; ClusteringServiceConfiguration configuration = - new ClusteringServiceConfiguration( - URI.create(CLUSTER_URI_BASE + entityIdentifier), - true, new ServerSideConfiguration(Collections.emptyMap())); + ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + entityIdentifier)) + .autoCreate(s -> s) + .build(); DefaultClusteringService service = new DefaultClusteringService(configuration); service.start(null); @@ -1896,9 +1891,9 @@ public void testGetServerStoreProxyReturnsEventualStore() throws Exception { public void testGetServerStoreProxyReturnsStrongStore() throws Exception { String entityIdentifier = "my-application"; ClusteringServiceConfiguration configuration = - new ClusteringServiceConfiguration( - URI.create(CLUSTER_URI_BASE + entityIdentifier), - true, new ServerSideConfiguration(Collections.emptyMap())); + ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + entityIdentifier)) + .autoCreate(s -> s) + .build(); DefaultClusteringService service = new DefaultClusteringService(configuration); service.start(null); @@ -1921,9 +1916,9 @@ public void testGetServerStoreProxyFailureClearsEntityListeners() throws Excepti // Initial setup begin String entityIdentifier = "my-application"; ClusteringServiceConfiguration configuration = - new ClusteringServiceConfiguration( - URI.create(CLUSTER_URI_BASE + entityIdentifier), - true, new ServerSideConfiguration(Collections.emptyMap())); + ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + entityIdentifier)) + .autoCreate(s -> s) + .build(); DefaultClusteringService service = new DefaultClusteringService(configuration); service.start(null); @@ -1942,7 +1937,7 @@ public void testGetServerStoreProxyFailureClearsEntityListeners() throws Excepti // Initial setup end service.start(null); - when(resourcePools.getPoolForResource(eq(DEDICATED))).thenReturn(new DedicatedClusteredResourcePoolImpl("serverResource1", 2L, MemoryUnit.MB)); + when(resourcePools.getPoolForResource(eq(DEDICATED))).thenReturn(new DedicatedClusteredResourcePoolImpl("serverResource2", 1L, MemoryUnit.MB)); try { service.getServerStoreProxy(cacheIdentifier, storeConfig, Consistency.STRONG, mock(ServerCallback.class)); fail("Server store proxy creation should have failed"); @@ -1957,9 +1952,9 @@ public void testGetServerStoreProxyFailureDoesNotClearOtherStoreEntityListeners( // Initial setup begin String entityIdentifier = "my-application"; ClusteringServiceConfiguration configuration = - new ClusteringServiceConfiguration( - URI.create(CLUSTER_URI_BASE + entityIdentifier), - true, new ServerSideConfiguration(Collections.emptyMap())); + ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE + entityIdentifier)) + .autoCreate(s -> s) + .build(); DefaultClusteringService service = new DefaultClusteringService(configuration); service.start(null); @@ -1981,7 +1976,7 @@ public void testGetServerStoreProxyFailureDoesNotClearOtherStoreEntityListeners( ClusteringService.ClusteredCacheIdentifier otherCacheIdentifier = (ClusteredCacheIdentifier) service.getPersistenceSpaceIdentifier("my-other-cache", null); service.getServerStoreProxy(otherCacheIdentifier, storeConfig, Consistency.STRONG, mock(ServerCallback.class)); // Creates one more store - when(resourcePools.getPoolForResource(eq(DEDICATED))).thenReturn(new DedicatedClusteredResourcePoolImpl("serverResource1", 2L, MemoryUnit.MB)); + when(resourcePools.getPoolForResource(eq(DEDICATED))).thenReturn(new DedicatedClusteredResourcePoolImpl("serverResource2", 1L, MemoryUnit.MB)); try { service.getServerStoreProxy(cacheIdentifier, storeConfig, Consistency.STRONG, mock(ServerCallback.class)); fail("Server store proxy creation should have failed"); @@ -1994,7 +1989,9 @@ public void testGetServerStoreProxyFailureDoesNotClearOtherStoreEntityListeners( @Test public void testGetStateRepositoryWithinTwiceWithSameName() throws Exception { ClusteringServiceConfiguration configuration = - new ClusteringServiceConfiguration(URI.create(CLUSTER_URI_BASE), true, new ServerSideConfiguration(Collections.emptyMap())); + ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE)) + .autoCreate(s -> s) + .build(); DefaultClusteringService service = new DefaultClusteringService(configuration); PersistableResourceService.PersistenceSpaceIdentifier cacheIdentifier = service.getPersistenceSpaceIdentifier("myCache", null); StateRepository repository1 = service.getStateRepositoryWithin(cacheIdentifier, "myRepo"); @@ -2005,7 +2002,9 @@ public void testGetStateRepositoryWithinTwiceWithSameName() throws Exception { @Test public void testGetStateRepositoryWithinTwiceWithSameNameDifferentPersistenceSpaceIdentifier() throws Exception { ClusteringServiceConfiguration configuration = - new ClusteringServiceConfiguration(URI.create(CLUSTER_URI_BASE), true, new ServerSideConfiguration(Collections.emptyMap())); + ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE)) + .autoCreate(s -> s) + .build(); DefaultClusteringService service = new DefaultClusteringService(configuration); PersistableResourceService.PersistenceSpaceIdentifier cacheIdentifier1 = service.getPersistenceSpaceIdentifier("myCache1", null); PersistableResourceService.PersistenceSpaceIdentifier cacheIdentifier2 = service.getPersistenceSpaceIdentifier("myCache2", null); @@ -2016,34 +2015,38 @@ public void testGetStateRepositoryWithinTwiceWithSameNameDifferentPersistenceSpa @Test public void testGetStateRepositoryWithinWithNonExistentPersistenceSpaceIdentifier() throws Exception { - expectedException.expect(CachePersistenceException.class); - expectedException.expectMessage("Clustered space not found for identifier"); ClusteringServiceConfiguration configuration = - new ClusteringServiceConfiguration(URI.create(CLUSTER_URI_BASE), true, new ServerSideConfiguration(Collections.emptyMap())); + ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE)) + .autoCreate(s -> s) + .build(); DefaultClusteringService service = new DefaultClusteringService(configuration); ClusteredCacheIdentifier cacheIdentifier = mock(ClusteredCacheIdentifier.class); doReturn("foo").when(cacheIdentifier).getId(); - service.getStateRepositoryWithin(cacheIdentifier, "myRepo"); + + CachePersistenceException thrown = assertThrows(CachePersistenceException.class, () -> service.getStateRepositoryWithin(cacheIdentifier, "myRepo")); + assertThat(thrown, hasProperty("message", startsWith("Clustered space not found for identifier"))); } @Test public void testReleaseNonExistentPersistenceSpaceIdentifierTwice() throws Exception { - expectedException.expect(CachePersistenceException.class); - expectedException.expectMessage("Unknown identifier"); ClusteringServiceConfiguration configuration = - new ClusteringServiceConfiguration(URI.create(CLUSTER_URI_BASE), true, new ServerSideConfiguration(Collections.emptyMap())); + ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE)) + .autoCreate(s -> s) + .build(); DefaultClusteringService service = new DefaultClusteringService(configuration); ClusteredCacheIdentifier cacheIdentifier = mock(ClusteredCacheIdentifier.class); doReturn("foo").when(cacheIdentifier).getId(); - service.releasePersistenceSpaceIdentifier(cacheIdentifier); + + CachePersistenceException thrown = assertThrows(CachePersistenceException.class, () -> service.releasePersistenceSpaceIdentifier(cacheIdentifier)); + assertThat(thrown, hasProperty("message", startsWith("Unknown identifier"))); } @Test public void testReleasePersistenceSpaceIdentifierTwice() throws Exception { - expectedException.expect(CachePersistenceException.class); - expectedException.expectMessage("Unknown identifier"); ClusteringServiceConfiguration configuration = - new ClusteringServiceConfiguration(URI.create(CLUSTER_URI_BASE), true, new ServerSideConfiguration(Collections.emptyMap())); + ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE)) + .autoCreate(s -> s) + .build(); DefaultClusteringService service = new DefaultClusteringService(configuration); PersistableResourceService.PersistenceSpaceIdentifier cacheIdentifier = service.getPersistenceSpaceIdentifier("myCache", null); try { @@ -2051,7 +2054,27 @@ public void testReleasePersistenceSpaceIdentifierTwice() throws Exception { } catch (CachePersistenceException e) { fail("First invocation of releasePersistenceSpaceIdentifier should not have failed"); } - service.releasePersistenceSpaceIdentifier(cacheIdentifier); + CachePersistenceException thrown = assertThrows(CachePersistenceException.class, () -> service.releasePersistenceSpaceIdentifier(cacheIdentifier)); + assertThat(thrown, hasProperty("message", startsWith("Unknown identifier"))); + } + + @Test + public void releaseMaintenanceHoldsWhenConnectionClosedDuringDestruction() { + ClusteringServiceConfiguration configuration = + ClusteringServiceConfigurationBuilder.cluster(URI.create(CLUSTER_URI_BASE)) + .autoCreate(s -> s) + .build(); + DefaultClusteringService service = new DefaultClusteringService(configuration); + assertThat(service.isConnected(), is(false)); + service.startForMaintenance(null, MaintainableService.MaintenanceScope.CACHE_MANAGER); + assertThat(service.isConnected(), is(true)); + + ConnectionState connectionState = service.getConnectionState(); + ClusterTierManagerClientEntityFactory clusterTierManagerClientEntityFactory = connectionState.getEntityFactory(); + assertEquals(clusterTierManagerClientEntityFactory.getMaintenanceHolds().size(), 1); + connectionState.destroyState(false); + assertEquals(clusterTierManagerClientEntityFactory.getMaintenanceHolds().size(), 0); + service.stop(); } private static Throwable getRootCause(Throwable t) { diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/ReconnectTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/service/ReconnectTest.java similarity index 82% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/ReconnectTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/service/ReconnectTest.java index c2607aaf9b..dbcf495614 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/ReconnectTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/service/ReconnectTest.java @@ -18,9 +18,9 @@ import org.ehcache.clustered.client.config.ClusteringServiceConfiguration; import org.ehcache.clustered.client.config.Timeouts; import org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder; +import org.ehcache.clustered.client.internal.ClusterTierManagerValidationException; import org.ehcache.clustered.client.internal.MockConnectionService; import org.hamcrest.Matchers; -import org.junit.Assert; import org.junit.Test; import org.mockito.Mockito; import org.terracotta.connection.Connection; @@ -31,13 +31,15 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +import static org.hamcrest.MatcherAssert.assertThat; + public class ReconnectTest { private static URI CLUSTER_URI = URI.create("mock://localhost:9510"); private final ClusteringServiceConfiguration serviceConfiguration = ClusteringServiceConfigurationBuilder .cluster(CLUSTER_URI) - .autoCreate() + .autoCreate(c -> c) .build(); @Test(expected = RuntimeException.class) @@ -45,7 +47,7 @@ public void testInitialConnectDoesNotRetryAfterConnectionException() { MockConnectionService.mockConnection = null; ConnectionState connectionState = new ConnectionState(Timeouts.DEFAULT, new Properties(), serviceConfiguration); - connectionState.initClusterConnection(); + connectionState.initClusterConnection(Runnable::run); } @Test @@ -59,9 +61,15 @@ public void testAfterConnectionReconnectHappensEvenAfterConnectionException() th ConnectionState connectionState = new ConnectionState(Timeouts.DEFAULT, new Properties(), serviceConfiguration); - connectionState.initClusterConnection(); + connectionState.initClusterConnection(Runnable::run); - CompletableFuture future = CompletableFuture.runAsync(() -> connectionState.initializeState()); + CompletableFuture future = CompletableFuture.runAsync(() -> { + try { + connectionState.initializeState(); + } catch (ClusterTierManagerValidationException e) { + throw new AssertionError(e); + } + }); MockConnectionService.mockConnection = null; @@ -79,10 +87,10 @@ public void testAfterConnectionReconnectHappensEvenAfterConnectionException() th try { future.get(); } catch (ExecutionException e) { - Assert.assertThat(e.getCause().getMessage(), Matchers.is("Stop reconnecting")); + assertThat(e.getCause().getMessage(), Matchers.is("Stop reconnecting")); } - Assert.assertThat(connectionState.getReconnectCount(), Matchers.is(1)); + assertThat(connectionState.getReconnectCount(), Matchers.is(1)); } diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/StateRepositoryWhitelistingTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/service/StateRepositoryWhitelistingTest.java similarity index 97% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/StateRepositoryWhitelistingTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/service/StateRepositoryWhitelistingTest.java index e6d5cc2e21..ffe57b376c 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/StateRepositoryWhitelistingTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/service/StateRepositoryWhitelistingTest.java @@ -29,8 +29,8 @@ import org.ehcache.clustered.lock.server.VoltronReadWriteLockServerEntityService; import org.ehcache.clustered.server.ClusterTierManagerServerEntityService; import org.ehcache.clustered.server.store.ClusterTierServerEntityService; -import org.ehcache.core.config.BaseCacheConfiguration; -import org.ehcache.core.internal.store.StoreConfigurationImpl; +import org.ehcache.impl.config.BaseCacheConfiguration; +import org.ehcache.core.store.StoreConfigurationImpl; import org.ehcache.spi.persistence.StateHolder; import org.junit.After; import org.junit.Before; @@ -50,9 +50,9 @@ import static org.ehcache.config.Eviction.noAdvice; import static org.ehcache.config.builders.ExpiryPolicyBuilder.noExpiration; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; @@ -84,7 +84,7 @@ public void setUp() throws Exception { ClusteringServiceConfiguration configuration = ClusteringServiceConfigurationBuilder.cluster(URI.create(STRIPE_URI)) - .autoCreate() + .autoCreate(c -> c) .build(); service = new ClusteringServiceFactory().create(configuration); diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/TestServiceProvider.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/service/TestServiceProvider.java similarity index 100% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/TestServiceProvider.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/service/TestServiceProvider.java diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/AbstractServerStoreProxyTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/AbstractServerStoreProxyTest.java similarity index 79% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/AbstractServerStoreProxyTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/AbstractServerStoreProxyTest.java index 7d93bf16d3..1b4bb4438c 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/AbstractServerStoreProxyTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/AbstractServerStoreProxyTest.java @@ -15,18 +15,23 @@ */ package org.ehcache.clustered.client.internal.store; +import org.ehcache.clustered.client.config.ClusteredResourcePool; +import org.ehcache.clustered.client.config.ClusteringServiceConfiguration; +import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder; import org.ehcache.clustered.client.config.builders.TimeoutsBuilder; import org.ehcache.clustered.client.internal.ClusterTierManagerClientEntityFactory; import org.ehcache.clustered.client.internal.ClusterTierManagerClientEntityService; import org.ehcache.clustered.client.internal.UnitTestConnectionService; import org.ehcache.clustered.client.internal.UnitTestConnectionService.PassthroughServerBuilder; import org.ehcache.clustered.client.internal.lock.VoltronReadWriteLockEntityClientService; +import org.ehcache.clustered.common.Consistency; import org.ehcache.clustered.common.ServerSideConfiguration; import org.ehcache.clustered.common.internal.ServerStoreConfiguration; import org.ehcache.clustered.lock.server.VoltronReadWriteLockServerEntityService; import org.ehcache.clustered.server.ClusterTierManagerServerEntityService; import org.ehcache.clustered.server.store.ObservableClusterTierServerEntityService; import org.ehcache.config.units.MemoryUnit; +import org.ehcache.impl.serialization.LongSerializer; import org.junit.AfterClass; import org.junit.BeforeClass; import org.terracotta.connection.Connection; @@ -75,17 +80,28 @@ protected static SimpleClusterTierClientEntity createClientEntity(String name, // Create ClusterTierManagerClientEntity if needed ClusterTierManagerClientEntityFactory entityFactory = new ClusterTierManagerClientEntityFactory( - connection, + connection, Runnable::run, TimeoutsBuilder.timeouts().write(Duration.ofSeconds(30)).build()); if (create) { entityFactory.create(name, new ServerSideConfiguration("defaultResource", Collections.emptyMap())); } // Create or fetch the ClusterTierClientEntity - SimpleClusterTierClientEntity clientEntity = (SimpleClusterTierClientEntity) entityFactory.fetchOrCreateClusteredStoreEntity(name, name, configuration, create); + SimpleClusterTierClientEntity clientEntity = (SimpleClusterTierClientEntity) entityFactory.fetchOrCreateClusteredStoreEntity(name, name, configuration, create ? ClusteringServiceConfiguration.ClientMode.AUTO_CREATE : ClusteringServiceConfiguration.ClientMode.CONNECT, false); if (validate) { clientEntity.validate(configuration); } return clientEntity; } + protected static SimpleClusterTierClientEntity createClientEntity(String name, Consistency consistency, boolean create) throws Exception { + ClusteredResourcePool resourcePool = ClusteredResourcePoolBuilder.clusteredDedicated(8L, MemoryUnit.MB); + + ServerStoreConfiguration serverStoreConfiguration = new ServerStoreConfiguration(resourcePool.getPoolAllocation(), Long.class + .getName(), + Long.class.getName(), LongSerializer.class.getName(), LongSerializer.class + .getName(), consistency, false); + + return createClientEntity(name, serverStoreConfiguration, create); + } + } diff --git a/clustered/common/src/test/java/org/ehcache/clustered/common/internal/messages/Util.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/ChainBuilderTest.java similarity index 53% rename from clustered/common/src/test/java/org/ehcache/clustered/common/internal/messages/Util.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/ChainBuilderTest.java index 94a4b350c5..56abb981a2 100644 --- a/clustered/common/src/test/java/org/ehcache/clustered/common/internal/messages/Util.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/ChainBuilderTest.java @@ -13,31 +13,29 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package org.ehcache.clustered.common.internal.messages; +package org.ehcache.clustered.client.internal.store; import org.ehcache.clustered.common.internal.store.Chain; -import org.ehcache.clustered.common.internal.store.Element; - -import java.util.Iterator; +import org.ehcache.clustered.common.internal.util.ChainBuilder; +import org.junit.Test; +import static org.ehcache.clustered.ChainUtils.createPayload; +import static org.ehcache.clustered.Matchers.hasPayloads; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; -import static org.ehcache.clustered.common.internal.store.Util.readPayLoad; /** - * */ -public final class Util { +public class ChainBuilderTest { - private Util() { - } + @Test + public void testChainBuilder() { + Chain chain = new ChainBuilder() + .add(createPayload(1L)) + .add(createPayload(3L)) + .add(createPayload(4L)) + .add(createPayload(2L)).build(); - public static void assertChainHas(Chain chain, long... payLoads) { - Iterator elements = chain.iterator(); - for (long payLoad : payLoads) { - assertThat(readPayLoad(elements.next().getPayload()), is(Long.valueOf(payLoad))); - } - assertThat(elements.hasNext(), is(false)); + assertThat(chain, hasPayloads(1L, 3L, 4L, 2L)); } } diff --git a/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/ClusteredStoreEventsTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/ClusteredStoreEventsTest.java new file mode 100644 index 0000000000..9751829dc9 --- /dev/null +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/ClusteredStoreEventsTest.java @@ -0,0 +1,378 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.clustered.client.internal.store; + +import org.ehcache.clustered.ChainUtils; +import org.ehcache.clustered.client.TestTimeSource; +import org.ehcache.clustered.client.config.ClusteredResourcePool; +import org.ehcache.clustered.client.config.ClusteringServiceConfiguration; +import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder; +import org.ehcache.clustered.client.internal.ClusterTierManagerClientEntityFactory; +import org.ehcache.clustered.client.internal.UnitTestConnectionService; +import org.ehcache.clustered.client.internal.store.ServerStoreProxy.ServerCallback; +import org.ehcache.clustered.client.internal.store.operations.ChainResolver; +import org.ehcache.clustered.client.internal.store.operations.ExpiryChainResolver; +import org.ehcache.clustered.common.ServerSideConfiguration; +import org.ehcache.clustered.common.internal.ServerStoreConfiguration; +import org.ehcache.clustered.common.internal.store.Chain; +import org.ehcache.clustered.common.internal.store.operations.ConditionalRemoveOperation; +import org.ehcache.clustered.common.internal.store.operations.ConditionalReplaceOperation; +import org.ehcache.clustered.common.internal.store.operations.Operation; +import org.ehcache.clustered.common.internal.store.operations.PutIfAbsentOperation; +import org.ehcache.clustered.common.internal.store.operations.PutOperation; +import org.ehcache.clustered.common.internal.store.operations.RemoveOperation; +import org.ehcache.clustered.common.internal.store.operations.ReplaceOperation; +import org.ehcache.clustered.common.internal.store.operations.TimestampOperation; +import org.ehcache.clustered.common.internal.store.operations.codecs.OperationsCodec; +import org.ehcache.config.EvictionAdvisor; +import org.ehcache.config.ResourcePools; +import org.ehcache.config.builders.ExpiryPolicyBuilder; +import org.ehcache.config.units.MemoryUnit; +import org.ehcache.core.events.StoreEventDispatcher; +import org.ehcache.core.events.StoreEventSink; +import org.ehcache.core.spi.store.Store; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; +import org.ehcache.expiry.ExpiryPolicy; +import org.ehcache.impl.serialization.LongSerializer; +import org.ehcache.impl.serialization.StringSerializer; +import org.ehcache.spi.loaderwriter.CacheLoaderWriter; +import org.ehcache.spi.serialization.Serializer; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; +import org.terracotta.connection.Connection; + +import java.net.URI; +import java.nio.ByteBuffer; +import java.time.Duration; +import java.util.Collections; +import java.util.Properties; +import java.util.function.Supplier; + +import static org.hamcrest.core.IsEqual.equalTo; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; +import static org.mockito.hamcrest.MockitoHamcrest.argThat; + +public class ClusteredStoreEventsTest { + + private static final String CACHE_IDENTIFIER = "testCache"; + private static final URI CLUSTER_URI = URI.create("terracotta://localhost"); + + private final Store.Configuration config = new Store.Configuration() { + + @Override + public Class getKeyType() { + return Long.class; + } + + @Override + public Class getValueType() { + return String.class; + } + + @Override + public EvictionAdvisor getEvictionAdvisor() { + return null; + } + + @Override + public ClassLoader getClassLoader() { + return null; + } + + @Override + public ExpiryPolicy getExpiry() { + return null; + } + + @Override + public ResourcePools getResourcePools() { + return null; + } + + @Override + public Serializer getKeySerializer() { + return null; + } + + @Override + public Serializer getValueSerializer() { + return null; + } + + @Override + public int getDispatcherConcurrency() { + return 0; + } + + @Override + public CacheLoaderWriter getCacheLoaderWriter() { + return null; + } + }; + private StoreEventSink storeEventSink; + private ServerCallback serverCallback; + private OperationsCodec codec; + private TestTimeSource testTimeSource; + + @SuppressWarnings("unchecked") + @Before + public void setup() throws Exception { + UnitTestConnectionService.add( + CLUSTER_URI, + new UnitTestConnectionService.PassthroughServerBuilder().resource("defaultResource", 8, MemoryUnit.MB).build() + ); + + Connection connection = new UnitTestConnectionService().connect(CLUSTER_URI, new Properties()); + ClusterTierManagerClientEntityFactory entityFactory = new ClusterTierManagerClientEntityFactory(connection, Runnable::run); + + ServerSideConfiguration serverConfig = + new ServerSideConfiguration("defaultResource", Collections.emptyMap()); + entityFactory.create("TestCacheManager", serverConfig); + + ClusteredResourcePool resourcePool = ClusteredResourcePoolBuilder.clusteredDedicated(4, MemoryUnit.MB); + ServerStoreConfiguration serverStoreConfiguration = new ServerStoreConfiguration(resourcePool.getPoolAllocation(), + Long.class.getName(), String.class.getName(), LongSerializer.class.getName(), StringSerializer.class.getName(), null, false); + ClusterTierClientEntity clientEntity = entityFactory.fetchOrCreateClusteredStoreEntity("TestCacheManager", CACHE_IDENTIFIER, serverStoreConfiguration, ClusteringServiceConfiguration.ClientMode.AUTO_CREATE, false); + clientEntity.validate(serverStoreConfiguration); + ServerStoreProxy serverStoreProxy = new CommonServerStoreProxy(CACHE_IDENTIFIER, clientEntity, mock(ServerCallback.class)); + + testTimeSource = new TestTimeSource(); + + codec = new OperationsCodec<>(new LongSerializer(), new StringSerializer()); + ChainResolver resolver = new ExpiryChainResolver<>(codec, ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMillis(1000))); + + StoreEventDispatcher storeEventDispatcher = mock(StoreEventDispatcher.class); + storeEventSink = mock(StoreEventSink.class); + when(storeEventDispatcher.eventSink()).thenReturn(storeEventSink); + + ClusteredStore store = new ClusteredStore<>(config, codec, resolver, serverStoreProxy, testTimeSource, storeEventDispatcher, new DefaultStatisticsService()); + serverCallback = new ClusteredStore.Provider().getServerCallback(store); + } + + @After + public void tearDown() throws Exception { + UnitTestConnectionService.remove(CLUSTER_URI); + } + + private ByteBuffer op(Operation operation) { + return codec.encode(operation); + } + + + @Test + public void testOnAppend_PutAfterNothingFiresCreatedEvent() { + Chain beforeAppend = ChainUtils.chainOf(); + serverCallback.onAppend(beforeAppend, op(new PutOperation<>(1L, "one", testTimeSource.getTimeMillis()))); + + verify(storeEventSink).created(eq(1L), eq("one")); + verifyNoMoreInteractions(storeEventSink); + } + + @Test + public void testOnAppend_PutAfterPutFiresUpdatedEvent() { + Chain beforeAppend = ChainUtils.chainOf(op(new PutOperation<>(1L, "one", testTimeSource.getTimeMillis()))); + serverCallback.onAppend(beforeAppend, op(new PutOperation<>(1L, "one-bis", testTimeSource.getTimeMillis()))); + + verify(storeEventSink).updated(eq(1L), argThat(supplies("one")), eq("one-bis")); + verifyNoMoreInteractions(storeEventSink); + } + + @Test + public void testOnAppend_RemoveAfterPutFiresRemovedEvent() { + Chain beforeAppend = ChainUtils.chainOf(op(new PutOperation<>(1L, "one", testTimeSource.getTimeMillis()))); + serverCallback.onAppend(beforeAppend, op(new RemoveOperation<>(1L, testTimeSource.getTimeMillis()))); + + verify(storeEventSink).removed(eq(1L), argThat(supplies("one"))); + verifyNoMoreInteractions(storeEventSink); + } + + @Test + public void testOnAppend_RemoveAfterNothingFiresNoEvent() { + Chain beforeAppend = ChainUtils.chainOf(); + serverCallback.onAppend(beforeAppend, op(new RemoveOperation<>(1L, testTimeSource.getTimeMillis()))); + + verifyNoMoreInteractions(storeEventSink); + } + + @Test + public void testOnAppend_ReplaceAfterPutFiresUpdatedEvent() { + Chain beforeAppend = ChainUtils.chainOf(op(new PutOperation<>(1L, "one", testTimeSource.getTimeMillis()))); + serverCallback.onAppend(beforeAppend, op(new ReplaceOperation<>(1L, "one-bis", testTimeSource.getTimeMillis()))); + + verify(storeEventSink).updated(eq(1L), argThat(supplies("one")), eq("one-bis")); + verifyNoMoreInteractions(storeEventSink); + } + + @Test + public void testOnAppend_ReplaceAfterNothingFiresNoEvent() { + Chain beforeAppend = ChainUtils.chainOf(); + serverCallback.onAppend(beforeAppend, op(new ReplaceOperation<>(1L, "one", testTimeSource.getTimeMillis()))); + + verifyNoMoreInteractions(storeEventSink); + } + + @Test + public void testOnAppend_PutIfAbsentAfterNothingFiresCreatedEvent() { + Chain beforeAppend = ChainUtils.chainOf(); + serverCallback.onAppend(beforeAppend, op(new PutIfAbsentOperation<>(1L, "one", testTimeSource.getTimeMillis()))); + + verify(storeEventSink).created(eq(1L), eq("one")); + verifyNoMoreInteractions(storeEventSink); + } + + @Test + public void testOnAppend_PutIfAbsentAfterPutFiresNoEvent() { + Chain beforeAppend = ChainUtils.chainOf(op(new PutOperation<>(1L, "one", testTimeSource.getTimeMillis()))); + serverCallback.onAppend(beforeAppend, op(new PutIfAbsentOperation<>(1L, "one-bis", testTimeSource.getTimeMillis()))); + + verifyNoMoreInteractions(storeEventSink); + } + + @Test + public void testOnAppend_SuccessfulReplaceConditionalAfterPutFiresUpdatedEvent() { + Chain beforeAppend = ChainUtils.chainOf(op(new PutOperation<>(1L, "one", testTimeSource.getTimeMillis()))); + serverCallback.onAppend(beforeAppend, op(new ConditionalReplaceOperation<>(1L, "one", "one-bis", testTimeSource.getTimeMillis()))); + + verify(storeEventSink).updated(eq(1L), argThat(supplies("one")), eq("one-bis")); + verifyNoMoreInteractions(storeEventSink); + } + + @Test + public void testOnAppend_FailingReplaceConditionalAfterPutFiresNoEvent() { + Chain beforeAppend = ChainUtils.chainOf(op(new PutOperation<>(1L, "one", testTimeSource.getTimeMillis()))); + serverCallback.onAppend(beforeAppend, op(new ConditionalReplaceOperation<>(1L, "un", "one-bis", testTimeSource.getTimeMillis()))); + + verifyNoMoreInteractions(storeEventSink); + } + + @Test + public void testOnAppend_ReplaceConditionalAfterNothingFiresNoEvent() { + Chain beforeAppend = ChainUtils.chainOf(); + serverCallback.onAppend(beforeAppend, op(new ConditionalReplaceOperation<>(1L, "one", "one-bis", testTimeSource.getTimeMillis()))); + + verifyNoMoreInteractions(storeEventSink); + } + + @Test + public void testOnAppend_SuccessfulRemoveConditionalAfterPutFiresUpdatedEvent() { + Chain beforeAppend = ChainUtils.chainOf(op(new PutOperation<>(1L, "one", testTimeSource.getTimeMillis()))); + serverCallback.onAppend(beforeAppend, op(new ConditionalRemoveOperation<>(1L, "one", testTimeSource.getTimeMillis()))); + + verify(storeEventSink).removed(eq(1L), argThat(supplies("one"))); + verifyNoMoreInteractions(storeEventSink); + } + + @Test + public void testOnAppend_FailingRemoveConditionalAfterPutFiresNoEvent() { + Chain beforeAppend = ChainUtils.chainOf(op(new PutOperation<>(1L, "one", testTimeSource.getTimeMillis()))); + serverCallback.onAppend(beforeAppend, op(new ConditionalRemoveOperation<>(1L, "un", testTimeSource.getTimeMillis()))); + + verifyNoMoreInteractions(storeEventSink); + } + + @Test + public void testOnAppend_RemoveConditionalAfterNothingFiresNoEvent() { + Chain beforeAppend = ChainUtils.chainOf(); + serverCallback.onAppend(beforeAppend, op(new ConditionalRemoveOperation<>(1L, "one", testTimeSource.getTimeMillis()))); + + verifyNoMoreInteractions(storeEventSink); + } + + @Test + public void testOnAppend_timestampAfterExpiryFiresExpiredEvent() { + Chain beforeAppend = ChainUtils.chainOf(op(new PutOperation<>(1L, "wrong-one", testTimeSource.getTimeMillis())), op(new PutOperation<>(1L, "one", testTimeSource.getTimeMillis()))); + testTimeSource.advanceTime(1100L); + serverCallback.onAppend(beforeAppend, op(new TimestampOperation<>(1L, testTimeSource.getTimeMillis()))); + + verify(storeEventSink).expired(eq(1L), argThat(supplies("one"))); + verifyNoMoreInteractions(storeEventSink); + } + + @Test + public void testOnAppend_timestampAfterNothingFiresNoEvent() { + Chain beforeAppend = ChainUtils.chainOf(); + serverCallback.onAppend(beforeAppend, op(new TimestampOperation<>(1L, testTimeSource.getTimeMillis()))); + + verifyNoMoreInteractions(storeEventSink); + } + + @Test + public void testOnAppend_timestampAfterNoExpiryFiresNoEvent() { + Chain beforeAppend = ChainUtils.chainOf(op(new PutOperation<>(1L, "one", testTimeSource.getTimeMillis()))); + testTimeSource.advanceTime(100L); + serverCallback.onAppend(beforeAppend, op(new TimestampOperation<>(1L, testTimeSource.getTimeMillis()))); + + verifyNoMoreInteractions(storeEventSink); + } + + @Test + public void testOnAppend_putIfAbsentAfterExpiredPutFiresCorrectly() { + Chain beforeAppend = ChainUtils.chainOf(op(new PutOperation<>(1L, "one", testTimeSource.getTimeMillis()))); + testTimeSource.advanceTime(1100L); + serverCallback.onAppend(beforeAppend, op(new PutIfAbsentOperation<>(1L, "one-bis", testTimeSource.getTimeMillis()))); + + InOrder inOrder = inOrder(storeEventSink); + inOrder.verify(storeEventSink).expired(eq(1L), argThat(supplies("one"))); + inOrder.verify(storeEventSink).created(1L, "one-bis"); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testOnInvalidateHash_chainFiresEvictedEvents() { + Chain evictedChain = ChainUtils.chainOf(op(new PutOperation<>(1L, "one", testTimeSource.getTimeMillis())), op(new PutOperation<>(2L, "two", testTimeSource.getTimeMillis()))); + serverCallback.onInvalidateHash(1L, evictedChain); + + verify(storeEventSink).evicted(eq(1L), argThat(supplies("one"))); + verify(storeEventSink).evicted(eq(2L), argThat(supplies("two"))); + verifyNoMoreInteractions(storeEventSink); + } + + @Test + public void testOnInvalidateHash_noChainFiresNoEvent() { + serverCallback.onInvalidateHash(1L, null); + + verifyNoMoreInteractions(storeEventSink); + } + + private static Matcher> supplies(T value) { + return supplies(equalTo(value)); + } + + private static Matcher> supplies(Matcher matcher) { + return new TypeSafeMatcher>() { + @Override + protected boolean matchesSafely(Supplier item) { + return matcher.matches(item.get()); + } + + @Override + public void describeTo(Description description) { + description.appendValue(" supplier of ").appendDescriptionOf(matcher); + } + }; + } +} diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/ClusteredStoreProviderTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/ClusteredStoreProviderTest.java similarity index 92% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/ClusteredStoreProviderTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/ClusteredStoreProviderTest.java index e0c76ef807..814b8886ff 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/ClusteredStoreProviderTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/ClusteredStoreProviderTest.java @@ -27,8 +27,9 @@ import org.ehcache.config.ResourceType; import org.ehcache.config.builders.ExpiryPolicyBuilder; import org.ehcache.config.units.MemoryUnit; -import org.ehcache.core.config.ResourcePoolsImpl; -import org.ehcache.core.internal.service.ServiceLocator; +import org.ehcache.core.spi.service.CacheManagerProviderService; +import org.ehcache.impl.config.ResourcePoolsImpl; +import org.ehcache.core.spi.ServiceLocator; import org.ehcache.core.spi.service.DiskResourceService; import org.ehcache.core.spi.store.Store; import org.ehcache.expiry.ExpiryPolicy; @@ -42,6 +43,8 @@ import org.ehcache.spi.serialization.Serializer; import org.ehcache.spi.service.ServiceConfiguration; import org.junit.Test; +import org.mockito.Answers; +import org.mockito.Mockito; import java.util.Arrays; import java.util.Collections; @@ -49,10 +52,11 @@ import java.util.List; import java.util.Map; -import static org.ehcache.core.internal.service.ServiceLocator.dependencySet; +import static org.ehcache.core.spi.ServiceLocator.dependencySet; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.startsWith; -import static org.junit.Assert.*; +import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; /** @@ -89,6 +93,7 @@ public void testRankTiered() throws Exception { .with(new OffHeapStore.Provider()) .with(new OffHeapDiskStore.Provider()) .with(mock(DiskResourceService.class)) + .with(Mockito.mock(CacheManagerProviderService.class, Answers.RETURNS_DEEP_STUBS)) .with(mock(ClusteringService.class)).build(); serviceLocator.startAllServices(); @@ -124,14 +129,14 @@ public void testAuthoritativeRank() throws Exception { ServiceLocator serviceLocator = dependencySet().with(mock(ClusteringService.class)).build(); provider.start(serviceLocator); - assertThat(provider.rankAuthority(ClusteredResourceType.Types.DEDICATED, Collections.>emptyList()), is(1)); - assertThat(provider.rankAuthority(ClusteredResourceType.Types.SHARED, Collections.>emptyList()), is(1)); - assertThat(provider.rankAuthority(new UnmatchedResourceType(), Collections.>emptyList()), is(0)); + assertThat(provider.rankAuthority(ClusteredResourceType.Types.DEDICATED, Collections.>emptyList()), is(1)); + assertThat(provider.rankAuthority(ClusteredResourceType.Types.SHARED, Collections.>emptyList()), is(1)); + assertThat(provider.rankAuthority(new UnmatchedResourceType(), Collections.>emptyList()), is(0)); } private void assertRank(final Store.Provider provider, final int expectedRank, final ResourceType... resources) { - final List> serviceConfigs = Collections.emptyList(); + final List> serviceConfigs = Collections.emptyList(); if (expectedRank == -1) { try { provider.rank(new HashSet<>(Arrays.asList(resources)), diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/ClusteredStoreTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/ClusteredStoreTest.java similarity index 55% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/ClusteredStoreTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/ClusteredStoreTest.java index beebc0dc29..0ce631c4a9 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/ClusteredStoreTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/ClusteredStoreTest.java @@ -16,25 +16,28 @@ package org.ehcache.clustered.client.internal.store; +import com.google.common.base.Objects; import org.assertj.core.api.ThrowableAssert; +import org.ehcache.Cache; import org.ehcache.clustered.client.TestTimeSource; import org.ehcache.clustered.client.config.ClusteredResourcePool; +import org.ehcache.clustered.client.config.ClusteringServiceConfiguration; import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder; import org.ehcache.clustered.client.internal.ClusterTierManagerClientEntityFactory; import org.ehcache.clustered.client.internal.UnitTestConnectionService; import org.ehcache.clustered.client.internal.store.ServerStoreProxy.ServerCallback; import org.ehcache.clustered.client.internal.store.operations.EternalChainResolver; -import org.ehcache.clustered.common.internal.store.operations.Result; import org.ehcache.clustered.common.internal.store.operations.codecs.OperationsCodec; import org.ehcache.clustered.common.ServerSideConfiguration; import org.ehcache.clustered.common.internal.ServerStoreConfiguration; -import org.ehcache.clustered.common.internal.store.Chain; import org.ehcache.config.EvictionAdvisor; import org.ehcache.config.ResourcePools; import org.ehcache.config.units.MemoryUnit; import org.ehcache.core.Ehcache; import org.ehcache.core.spi.store.Store; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; import org.ehcache.expiry.ExpiryPolicy; +import org.ehcache.impl.store.DefaultStoreEventDispatcher; import org.ehcache.spi.loaderwriter.CacheLoaderWriter; import org.ehcache.spi.resilience.StoreAccessException; import org.ehcache.core.spi.time.TimeSource; @@ -43,42 +46,43 @@ import org.ehcache.impl.serialization.LongSerializer; import org.ehcache.impl.serialization.StringSerializer; import org.ehcache.spi.serialization.Serializer; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.terracotta.connection.Connection; import java.net.URI; -import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Properties; -import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.Function; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.ehcache.clustered.client.internal.store.ClusteredStore.DEFAULT_CHAIN_COMPACTION_THRESHOLD; -import static org.ehcache.clustered.client.internal.store.ClusteredStore.CHAIN_COMPACTION_THRESHOLD_PROP; import static org.ehcache.clustered.util.StatisticsTestUtils.validateStat; import static org.ehcache.clustered.util.StatisticsTestUtils.validateStats; import static org.ehcache.core.spi.store.Store.ValueHolder.NO_EXPIRE; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; -import static org.junit.Assert.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.CombinableMatcher.either; +import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.RETURNS_MOCKS; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; public class ClusteredStoreTest { @@ -148,7 +152,7 @@ public void setup() throws Exception { ); Connection connection = new UnitTestConnectionService().connect(CLUSTER_URI, new Properties()); - ClusterTierManagerClientEntityFactory entityFactory = new ClusterTierManagerClientEntityFactory(connection); + ClusterTierManagerClientEntityFactory entityFactory = new ClusterTierManagerClientEntityFactory(connection, Runnable::run); ServerSideConfiguration serverConfig = new ServerSideConfiguration("defaultResource", Collections.emptyMap()); @@ -157,7 +161,7 @@ public void setup() throws Exception { ClusteredResourcePool resourcePool = ClusteredResourcePoolBuilder.clusteredDedicated(4, MemoryUnit.MB); ServerStoreConfiguration serverStoreConfiguration = new ServerStoreConfiguration(resourcePool.getPoolAllocation(), Long.class.getName(), String.class.getName(), LongSerializer.class.getName(), StringSerializer.class.getName(), null, false); - ClusterTierClientEntity clientEntity = entityFactory.fetchOrCreateClusteredStoreEntity("TestCacheManager", CACHE_IDENTIFIER, serverStoreConfiguration, true); + ClusterTierClientEntity clientEntity = entityFactory.fetchOrCreateClusteredStoreEntity("TestCacheManager", CACHE_IDENTIFIER, serverStoreConfiguration, ClusteringServiceConfiguration.ClientMode.AUTO_CREATE, false); clientEntity.validate(serverStoreConfiguration); ServerStoreProxy serverStoreProxy = new CommonServerStoreProxy(CACHE_IDENTIFIER, clientEntity, mock(ServerCallback.class)); @@ -165,12 +169,12 @@ public void setup() throws Exception { OperationsCodec codec = new OperationsCodec<>(new LongSerializer(), new StringSerializer()); EternalChainResolver resolver = new EternalChainResolver<>(codec); - store = new ClusteredStore<>(config, codec, resolver, serverStoreProxy, testTimeSource); + store = new ClusteredStore<>(config, codec, resolver, serverStoreProxy, testTimeSource, new DefaultStoreEventDispatcher<>(8), new DefaultStatisticsService()); } @After public void tearDown() throws Exception { - UnitTestConnectionService.remove("terracotta://localhost/my-application"); + UnitTestConnectionService.remove(CLUSTER_URI); } private void assertTimeoutOccurred(ThrowableAssert.ThrowingCallable throwingCallable) { @@ -195,7 +199,7 @@ public void testPutTimeout() throws Exception { OperationsCodec codec = mock(OperationsCodec.class); TimeSource timeSource = mock(TimeSource.class); doThrow(TimeoutException.class).when(proxy).append(anyLong(), isNull()); - ClusteredStore store = new ClusteredStore<>(config, codec, null, proxy, timeSource); + ClusteredStore store = new ClusteredStore<>(config, codec, null, proxy, timeSource, null, new DefaultStatisticsService()); assertTimeoutOccurred(() -> store.put(1L, "one")); } @@ -218,7 +222,7 @@ public void testGetThrowsOnlySAE() throws Exception { ServerStoreProxy serverStoreProxy = mock(ServerStoreProxy.class); when(serverStoreProxy.get(anyLong())).thenThrow(new RuntimeException()); TestTimeSource testTimeSource = mock(TestTimeSource.class); - ClusteredStore store = new ClusteredStore<>(config, codec, chainResolver, serverStoreProxy, testTimeSource); + ClusteredStore store = new ClusteredStore<>(config, codec, chainResolver, serverStoreProxy, testTimeSource, null, new DefaultStatisticsService()); store.get(1L); } @@ -228,60 +232,11 @@ public void testGetTimeout() throws Exception { ServerStoreProxy proxy = mock(ServerStoreProxy.class); long longKey = HashUtils.intHashToLong(new Long(1L).hashCode()); when(proxy.get(longKey)).thenThrow(TimeoutException.class); - ClusteredStore store = new ClusteredStore<>(config,null, null, proxy, null); + ClusteredStore store = new ClusteredStore<>(config,null, null, proxy, null, null, new DefaultStatisticsService()); assertThat(store.get(1L), nullValue()); validateStats(store, EnumSet.of(StoreOperationOutcomes.GetOutcome.TIMEOUT)); } - @Test - public void testGetThatCompactsInvokesReplace() throws Exception { - TestTimeSource timeSource = new TestTimeSource(); - timeSource.advanceTime(134556L); - long now = timeSource.getTimeMillis(); - @SuppressWarnings("unchecked") - OperationsCodec operationsCodec = new OperationsCodec<>(new LongSerializer(), new StringSerializer()); - @SuppressWarnings("unchecked") - EternalChainResolver chainResolver = mock(EternalChainResolver.class); - @SuppressWarnings("unchecked") - ResolvedChain resolvedChain = mock(ResolvedChain.class); - when(resolvedChain.isCompacted()).thenReturn(true); - when(chainResolver.resolve(any(Chain.class), eq(42L), eq(now))).thenReturn(resolvedChain); - ServerStoreProxy serverStoreProxy = mock(ServerStoreProxy.class); - Chain chain = mock(Chain.class); - when(chain.isEmpty()).thenReturn(false); - long longKey = HashUtils.intHashToLong(new Long(42L).hashCode()); - when(serverStoreProxy.get(longKey)).thenReturn(chain); - - ClusteredStore clusteredStore = new ClusteredStore<>(config, operationsCodec, chainResolver, - serverStoreProxy, timeSource); - clusteredStore.get(42L); - verify(serverStoreProxy).replaceAtHead(eq(longKey), eq(chain), isNull()); - } - - @Test - public void testGetThatDoesNotCompactsInvokesReplace() throws Exception { - TestTimeSource timeSource = new TestTimeSource(); - timeSource.advanceTime(134556L); - long now = timeSource.getTimeMillis(); - OperationsCodec operationsCodec = new OperationsCodec<>(new LongSerializer(), new StringSerializer()); - @SuppressWarnings("unchecked") - EternalChainResolver chainResolver = mock(EternalChainResolver.class); - @SuppressWarnings("unchecked") - ResolvedChain resolvedChain = mock(ResolvedChain.class); - when(resolvedChain.isCompacted()).thenReturn(false); - when(chainResolver.resolve(any(Chain.class), eq(42L), eq(now))).thenReturn(resolvedChain); - ServerStoreProxy serverStoreProxy = mock(ServerStoreProxy.class); - Chain chain = mock(Chain.class); - when(chain.isEmpty()).thenReturn(false); - long longKey = HashUtils.intHashToLong(new Long(42L).hashCode()); - when(serverStoreProxy.get(longKey)).thenReturn(chain); - - ClusteredStore clusteredStore = new ClusteredStore<>(config, operationsCodec, chainResolver, - serverStoreProxy, timeSource); - clusteredStore.get(42L); - verify(serverStoreProxy, never()).replaceAtHead(eq(longKey), eq(chain), any(Chain.class)); - } - @Test public void testContainsKey() throws Exception { assertThat(store.containsKey(1L), is(false)); @@ -300,7 +255,7 @@ public void testContainsKeyThrowsOnlySAE() throws Exception { ServerStoreProxy serverStoreProxy = mock(ServerStoreProxy.class); when(serverStoreProxy.get(anyLong())).thenThrow(new RuntimeException()); TestTimeSource testTimeSource = mock(TestTimeSource.class); - ClusteredStore store = new ClusteredStore<>(config, codec, chainResolver, serverStoreProxy, testTimeSource); + ClusteredStore store = new ClusteredStore<>(config, codec, chainResolver, serverStoreProxy, testTimeSource, null, new DefaultStatisticsService()); store.containsKey(1L); } @@ -325,7 +280,7 @@ public void testRemoveThrowsOnlySAE() throws Exception { when(serverStoreProxy.getAndAppend(anyLong(), any())).thenThrow(theException); TestTimeSource testTimeSource = new TestTimeSource(); - ClusteredStore store = new ClusteredStore<>(config, codec, chainResolver, serverStoreProxy, testTimeSource); + ClusteredStore store = new ClusteredStore<>(config, codec, chainResolver, serverStoreProxy, testTimeSource, null, new DefaultStatisticsService()); assertThatExceptionOfType(StoreAccessException.class) .isThrownBy(() -> store.remove(1L)) .withCause(theException); @@ -338,7 +293,7 @@ public void testRemoveTimeout() throws Exception { OperationsCodec codec = mock(OperationsCodec.class); TimeSource timeSource = mock(TimeSource.class); when(proxy.getAndAppend(anyLong(), isNull())).thenThrow(TimeoutException.class); - ClusteredStore store = new ClusteredStore<>(config, codec, null, proxy, timeSource); + ClusteredStore store = new ClusteredStore<>(config, codec, null, proxy, timeSource, null, new DefaultStatisticsService()); assertTimeoutOccurred(() -> store.remove(1L)); } @@ -370,7 +325,7 @@ public void testClearThrowsOnlySAE() throws Exception { ServerStoreProxy serverStoreProxy = mock(ServerStoreProxy.class); doThrow(new RuntimeException()).when(serverStoreProxy).clear(); TestTimeSource testTimeSource = mock(TestTimeSource.class); - ClusteredStore store = new ClusteredStore<>(config, codec, chainResolver, serverStoreProxy, testTimeSource); + ClusteredStore store = new ClusteredStore<>(config, codec, chainResolver, serverStoreProxy, testTimeSource, null, new DefaultStatisticsService()); store.clear(); } @@ -381,7 +336,7 @@ public void testClearTimeout() throws Exception { OperationsCodec codec = mock(OperationsCodec.class); TimeSource timeSource = mock(TimeSource.class); doThrow(TimeoutException.class).when(proxy).clear(); - ClusteredStore store = new ClusteredStore<>(config, codec, null, proxy, timeSource); + ClusteredStore store = new ClusteredStore<>(config, codec, null, proxy, timeSource, null, new DefaultStatisticsService()); assertTimeoutOccurred(() -> store.clear()); } @@ -401,9 +356,9 @@ public void testPutIfAbsentThrowsOnlySAE() throws Exception { @SuppressWarnings("unchecked") EternalChainResolver chainResolver = mock(EternalChainResolver.class); ServerStoreProxy serverStoreProxy = mock(ServerStoreProxy.class); - when(serverStoreProxy.get(anyLong())).thenThrow(new RuntimeException()); + when(serverStoreProxy.getAndAppend(anyLong(), any())).thenThrow(new RuntimeException()); TestTimeSource testTimeSource = mock(TestTimeSource.class); - ClusteredStore store = new ClusteredStore<>(config, codec, chainResolver, serverStoreProxy, testTimeSource); + ClusteredStore store = new ClusteredStore<>(config, codec, chainResolver, serverStoreProxy, testTimeSource, null, new DefaultStatisticsService()); store.putIfAbsent(1L, "one", b -> {}); } @@ -414,7 +369,7 @@ public void testPutIfAbsentTimeout() throws Exception { OperationsCodec codec = mock(OperationsCodec.class); TimeSource timeSource = mock(TimeSource.class); when(proxy.getAndAppend(anyLong(), isNull())).thenThrow(TimeoutException.class); - ClusteredStore store = new ClusteredStore<>(config, codec, null, proxy, timeSource); + ClusteredStore store = new ClusteredStore<>(config, codec, null, proxy, timeSource, null, new DefaultStatisticsService()); assertTimeoutOccurred(() -> store.putIfAbsent(1L, "one", b -> {})); } @@ -438,9 +393,9 @@ public void testConditionalRemoveThrowsOnlySAE() throws Exception { @SuppressWarnings("unchecked") EternalChainResolver chainResolver = mock(EternalChainResolver.class); ServerStoreProxy serverStoreProxy = mock(ServerStoreProxy.class); - when(serverStoreProxy.get(anyLong())).thenThrow(new RuntimeException()); + when(serverStoreProxy.getAndAppend(anyLong(), any())).thenThrow(new RuntimeException()); TestTimeSource testTimeSource = mock(TestTimeSource.class); - ClusteredStore store = new ClusteredStore<>(config, codec, chainResolver, serverStoreProxy, testTimeSource); + ClusteredStore store = new ClusteredStore<>(config, codec, chainResolver, serverStoreProxy, testTimeSource, null, new DefaultStatisticsService()); store.remove(1L, "one"); } @@ -451,7 +406,7 @@ public void testConditionalRemoveTimeout() throws Exception { OperationsCodec codec = mock(OperationsCodec.class); TimeSource timeSource = mock(TimeSource.class); when(proxy.getAndAppend(anyLong(), isNull())).thenThrow(TimeoutException.class); - ClusteredStore store = new ClusteredStore<>(config, codec, null, proxy, timeSource); + ClusteredStore store = new ClusteredStore<>(config, codec, null, proxy, timeSource, null, new DefaultStatisticsService()); assertTimeoutOccurred(() -> store.remove(1L, "one")); } @@ -472,9 +427,9 @@ public void testReplaceThrowsOnlySAE() throws Exception { @SuppressWarnings("unchecked") EternalChainResolver chainResolver = mock(EternalChainResolver.class); ServerStoreProxy serverStoreProxy = mock(ServerStoreProxy.class); - when(serverStoreProxy.get(anyLong())).thenThrow(new RuntimeException()); + when(serverStoreProxy.getAndAppend(anyLong(), any())).thenThrow(new RuntimeException()); TestTimeSource testTimeSource = mock(TestTimeSource.class); - ClusteredStore store = new ClusteredStore<>(config, codec, chainResolver, serverStoreProxy, testTimeSource); + ClusteredStore store = new ClusteredStore<>(config, codec, chainResolver, serverStoreProxy, testTimeSource, null, new DefaultStatisticsService()); store.replace(1L, "one"); } @@ -485,7 +440,7 @@ public void testReplaceTimeout() throws Exception { OperationsCodec codec = mock(OperationsCodec.class); TimeSource timeSource = mock(TimeSource.class); when(proxy.getAndAppend(anyLong(), isNull())).thenThrow(TimeoutException.class); - ClusteredStore store = new ClusteredStore<>(config, codec, null, proxy, timeSource); + ClusteredStore store = new ClusteredStore<>(config, codec, null, proxy, timeSource, null, new DefaultStatisticsService()); assertTimeoutOccurred(() -> store.replace(1L, "one")); } @@ -510,9 +465,9 @@ public void testConditionalReplaceThrowsOnlySAE() throws Exception { @SuppressWarnings("unchecked") EternalChainResolver chainResolver = mock(EternalChainResolver.class); ServerStoreProxy serverStoreProxy = mock(ServerStoreProxy.class); - when(serverStoreProxy.get(anyLong())).thenThrow(new RuntimeException()); + when(serverStoreProxy.getAndAppend(anyLong(), any())).thenThrow(new RuntimeException()); TestTimeSource testTimeSource = mock(TestTimeSource.class); - ClusteredStore store = new ClusteredStore<>(config, codec, chainResolver, serverStoreProxy, testTimeSource); + ClusteredStore store = new ClusteredStore<>(config, codec, chainResolver, serverStoreProxy, testTimeSource, null, new DefaultStatisticsService()); store.replace(1L, "one", "another one"); } @@ -523,7 +478,7 @@ public void testConditionalReplaceTimeout() throws Exception { OperationsCodec codec = mock(OperationsCodec.class); TimeSource timeSource = mock(TimeSource.class); when(proxy.getAndAppend(anyLong(), isNull())).thenThrow(TimeoutException.class); - ClusteredStore store = new ClusteredStore<>(config, codec, null, proxy, timeSource); + ClusteredStore store = new ClusteredStore<>(config, codec, null, proxy, timeSource, null, new DefaultStatisticsService()); assertTimeoutOccurred(() -> store.replace(1L, "one", "another one")); } @@ -592,323 +547,186 @@ public void testBulkComputeIfAbsentThrowsForGenericFunction() throws Exception { } @Test - @SuppressWarnings("unchecked") - public void testPutIfAbsentReplacesChainOnlyOnCompressionThreshold() throws Exception { - Result result = mock(Result.class); - when(result.getValue()).thenReturn("one"); - - ResolvedChain resolvedChain = mock(ResolvedChain.class); - when(resolvedChain.getResolvedResult(anyLong())).thenReturn(result); - when(resolvedChain.getCompactedChain()).thenReturn(mock(Chain.class)); - - ServerStoreProxy proxy = mock(ServerStoreProxy.class); - when(proxy.getAndAppend(anyLong(), any(ByteBuffer.class))).thenReturn(mock(Chain.class)); + public void testExpirationIsSentToHigherTiers() throws Exception { + @SuppressWarnings("unchecked") + Store.ValueHolder valueHolder = mock(Store.ValueHolder.class, withSettings().defaultAnswer(RETURNS_MOCKS)); + when(valueHolder.get()).thenReturn("bar"); + when(valueHolder.expirationTime()).thenReturn(1000L); + @SuppressWarnings("unchecked") EternalChainResolver resolver = mock(EternalChainResolver.class); - when(resolver.resolve(any(Chain.class), anyLong(), anyLong())).thenReturn(resolvedChain); - - OperationsCodec codec = mock(OperationsCodec.class); - when(codec.encode(any())).thenReturn(ByteBuffer.allocate(0)); - TimeSource timeSource = mock(TimeSource.class); - - ClusteredStore store = new ClusteredStore<>(config, codec, resolver, proxy, timeSource); - - when(resolvedChain.getCompactionCount()).thenReturn(DEFAULT_CHAIN_COMPACTION_THRESHOLD - 1); // less than the default threshold - store.putIfAbsent(1L, "one", b -> {}); - verify(proxy, never()).replaceAtHead(anyLong(), any(Chain.class), any(Chain.class)); - - when(resolvedChain.getCompactionCount()).thenReturn(DEFAULT_CHAIN_COMPACTION_THRESHOLD); // equal to the default threshold - store.putIfAbsent(1L, "one", b -> {}); - verify(proxy, never()).replaceAtHead(anyLong(), any(Chain.class), any(Chain.class)); - - when(resolvedChain.getCompactionCount()).thenReturn(DEFAULT_CHAIN_COMPACTION_THRESHOLD + 1); // greater than the default threshold - store.putIfAbsent(1L, "one", b -> {}); - verify(proxy).replaceAtHead(anyLong(), any(Chain.class), any(Chain.class)); - } + when(resolver.resolve(any(ServerStoreProxy.ChainEntry.class), anyLong(), anyLong())).thenReturn(valueHolder); - @Test - @SuppressWarnings("unchecked") - public void testReplaceReplacesChainOnlyOnCompressionThreshold() throws Exception { - Result result = mock(Result.class); - when(result.getValue()).thenReturn("one"); - - ResolvedChain resolvedChain = mock(ResolvedChain.class); - when(resolvedChain.getResolvedResult(anyLong())).thenReturn(result); - when(resolvedChain.getCompactedChain()).thenReturn(mock(Chain.class)); - - ServerStoreProxy proxy = mock(ServerStoreProxy.class); - when(proxy.getAndAppend(anyLong(), any(ByteBuffer.class))).thenReturn(mock(Chain.class)); - - EternalChainResolver resolver = mock(EternalChainResolver.class); - when(resolver.resolve(any(Chain.class), anyLong(), anyLong())).thenReturn(resolvedChain); + ServerStoreProxy proxy = mock(ServerStoreProxy.class, withSettings().defaultAnswer(RETURNS_MOCKS)); + @SuppressWarnings("unchecked") OperationsCodec codec = mock(OperationsCodec.class); - when(codec.encode(any())).thenReturn(ByteBuffer.allocate(0)); TimeSource timeSource = mock(TimeSource.class); - ClusteredStore store = new ClusteredStore<>(config, codec, resolver, proxy, timeSource); - - when(resolvedChain.getCompactionCount()).thenReturn(DEFAULT_CHAIN_COMPACTION_THRESHOLD - 1); // less than the default threshold - store.replace(1L, "one"); - verify(proxy, never()).replaceAtHead(anyLong(), any(Chain.class), any(Chain.class)); + ClusteredStore store = new ClusteredStore<>(config, codec, resolver, proxy, timeSource, null, new DefaultStatisticsService()); - when(resolvedChain.getCompactionCount()).thenReturn(DEFAULT_CHAIN_COMPACTION_THRESHOLD); // equal to the default threshold - store.replace(1L, "one"); - verify(proxy, never()).replaceAtHead(anyLong(), any(Chain.class), any(Chain.class)); + Store.ValueHolder vh = store.get(1L); - when(resolvedChain.getCompactionCount()).thenReturn(DEFAULT_CHAIN_COMPACTION_THRESHOLD + 1); // greater than the default threshold - store.replace(1L, "one"); - verify(proxy).replaceAtHead(anyLong(), any(Chain.class), any(Chain.class)); + long expirationTime = vh.expirationTime(); + assertThat(expirationTime, is(1000L)); } @Test - @SuppressWarnings("unchecked") - public void testConditionalReplaceReplacesChainOnlyOnCompressionThreshold() throws Exception { - ResolvedChain resolvedChain = mock(ResolvedChain.class); - when(resolvedChain.getResolvedResult(anyLong())).thenReturn(mock(Result.class)); - when(resolvedChain.getCompactedChain()).thenReturn(mock(Chain.class)); - - ServerStoreProxy proxy = mock(ServerStoreProxy.class); - when(proxy.getAndAppend(anyLong(), any(ByteBuffer.class))).thenReturn(mock(Chain.class)); + public void testNoExpireIsSentToHigherTiers() throws Exception { + @SuppressWarnings("unchecked") + Store.ValueHolder valueHolder = mock(Store.ValueHolder.class, withSettings().defaultAnswer(RETURNS_MOCKS)); + when(valueHolder.get()).thenReturn("bar"); + when(valueHolder.expirationTime()).thenReturn(NO_EXPIRE); + @SuppressWarnings("unchecked") EternalChainResolver resolver = mock(EternalChainResolver.class); - when(resolver.resolve(any(Chain.class), anyLong(), anyLong())).thenReturn(resolvedChain); + when(resolver.resolve(any(ServerStoreProxy.ChainEntry.class), anyLong(), anyLong())).thenReturn(valueHolder); + ServerStoreProxy proxy = mock(ServerStoreProxy.class, withSettings().defaultAnswer(RETURNS_MOCKS)); + + @SuppressWarnings("unchecked") OperationsCodec codec = mock(OperationsCodec.class); - when(codec.encode(any())).thenReturn(ByteBuffer.allocate(0)); TimeSource timeSource = mock(TimeSource.class); - ClusteredStore store = new ClusteredStore<>(config, codec, resolver, proxy, timeSource); - - when(resolvedChain.getCompactionCount()).thenReturn(DEFAULT_CHAIN_COMPACTION_THRESHOLD - 1); // less than the default threshold - store.replace(1L, "one", "anotherOne"); - verify(proxy, never()).replaceAtHead(anyLong(), any(Chain.class), any(Chain.class)); + ClusteredStore store = new ClusteredStore<>(config, codec, resolver, proxy, timeSource, null, new DefaultStatisticsService()); - when(resolvedChain.getCompactionCount()).thenReturn(DEFAULT_CHAIN_COMPACTION_THRESHOLD); // equal to the default threshold - store.replace(1L, "one", "anotherOne"); - verify(proxy, never()).replaceAtHead(anyLong(), any(Chain.class), any(Chain.class)); + Store.ValueHolder vh = store.get(1L); - when(resolvedChain.getCompactionCount()).thenReturn(DEFAULT_CHAIN_COMPACTION_THRESHOLD + 1); // greater than the default threshold - store.replace(1L, "one", "anotherOne"); - verify(proxy).replaceAtHead(anyLong(), any(Chain.class), any(Chain.class)); + long expirationTime = vh.expirationTime(); + assertThat(expirationTime, is(NO_EXPIRE)); } @Test - @SuppressWarnings("unchecked") - public void testCustomCompressionThreshold() throws Exception { - int customThreshold = 4; - try { - System.setProperty(CHAIN_COMPACTION_THRESHOLD_PROP, String.valueOf(customThreshold)); - - Result result = mock(Result.class); - when(result.getValue()).thenReturn("one"); - - ResolvedChain resolvedChain = mock(ResolvedChain.class); - when(resolvedChain.getResolvedResult(anyLong())).thenReturn(result); - when(resolvedChain.getCompactedChain()).thenReturn(mock(Chain.class)); - - ServerStoreProxy proxy = mock(ServerStoreProxy.class); - when(proxy.getAndAppend(anyLong(), any(ByteBuffer.class))).thenReturn(mock(Chain.class)); + public void testEmptyChainIteratorIsEmpty() throws StoreAccessException { - EternalChainResolver resolver = mock(EternalChainResolver.class); - when(resolver.resolve(any(Chain.class), anyLong(), anyLong())).thenReturn(resolvedChain); + Store.Iterator>> iterator = store.iterator(); - OperationsCodec codec = mock(OperationsCodec.class); - when(codec.encode(any())).thenReturn(ByteBuffer.allocate(0)); - TimeSource timeSource = mock(TimeSource.class); - - ClusteredStore store = new ClusteredStore<>(config, codec, resolver, proxy, timeSource); - - when(resolvedChain.getCompactionCount()).thenReturn(customThreshold - 1); // less than the custom threshold - store.putIfAbsent(1L, "one", b -> {}); - verify(proxy, never()).replaceAtHead(anyLong(), any(Chain.class), any(Chain.class)); - - when(resolvedChain.getCompactionCount()).thenReturn(customThreshold); // equal to the custom threshold - store.replace(1L, "one"); - verify(proxy, never()).replaceAtHead(anyLong(), any(Chain.class), any(Chain.class)); - - when(resolvedChain.getCompactionCount()).thenReturn(customThreshold + 1); // greater than the custom threshold - store.replace(1L, "one"); - verify(proxy).replaceAtHead(anyLong(), any(Chain.class), any(Chain.class)); - } finally { - System.clearProperty(CHAIN_COMPACTION_THRESHOLD_PROP); + assertThat(iterator.hasNext(), is(false)); + try { + iterator.next(); + fail("Expected NoSuchElementException"); + } catch (NoSuchElementException e) { + //expected } } @Test - @SuppressWarnings("unchecked") - public void testRemoveReplacesChainOnHits() throws Exception { - ResolvedChain resolvedChain = mock(ResolvedChain.class); - when(resolvedChain.getCompactedChain()).thenReturn(mock(Chain.class)); - when(resolvedChain.getResolvedResult(anyLong())).thenReturn(mock(Result.class)); //simulate a key hit on chain resolution - when(resolvedChain.getCompactionCount()).thenReturn(1); - - ServerStoreProxy proxy = mock(ServerStoreProxy.class); - when(proxy.getAndAppend(anyLong(), any(ByteBuffer.class))).thenReturn(mock(Chain.class)); - - EternalChainResolver resolver = mock(EternalChainResolver.class); - when(resolver.resolve(any(Chain.class), anyLong(), anyLong())).thenReturn(resolvedChain); - - OperationsCodec codec = mock(OperationsCodec.class); - when(codec.encode(any())).thenReturn(ByteBuffer.allocate(0)); - TimeSource timeSource = mock(TimeSource.class); - - ClusteredStore store = new ClusteredStore<>(config, codec, resolver, proxy, timeSource); - - store.remove(1L); - verify(proxy).replaceAtHead(anyLong(), any(Chain.class), any(Chain.class)); - } - - @Test - @SuppressWarnings("unchecked") - public void testRemoveDoesNotReplaceChainOnMisses() throws Exception { - ResolvedChain resolvedChain = mock(ResolvedChain.class); - when(resolvedChain.getResolvedResult(anyLong())).thenReturn(null); //simulate a key miss on chain resolution + public void testSingleChainSingleValue() throws StoreAccessException { + store.put(1L, "foo"); - EternalChainResolver resolver = mock(EternalChainResolver.class); - when(resolver.resolve(any(Chain.class), anyLong(), anyLong())).thenReturn(resolvedChain); - - OperationsCodec codec = mock(OperationsCodec.class); - when(codec.encode(any())).thenReturn(ByteBuffer.allocate(0)); - ServerStoreProxy proxy = mock(ServerStoreProxy.class); - when(proxy.getAndAppend(anyLong(), any(ByteBuffer.class))).thenReturn(mock(Chain.class)); - TimeSource timeSource = mock(TimeSource.class); + Store.Iterator>> iterator = store.iterator(); - ClusteredStore store = new ClusteredStore<>(config, codec, resolver, proxy, timeSource); - - store.remove(1L); - verify(proxy, never()).replaceAtHead(anyLong(), any(Chain.class), any(Chain.class)); + assertThat(iterator.hasNext(), is(true)); + assertThat(iterator.next(), isEntry(1L, "foo")); + assertThat(iterator.hasNext(), is(false)); + try { + iterator.next(); + fail("Expected NoSuchElementException"); + } catch (NoSuchElementException e) { + //expected + } } @Test - @SuppressWarnings("unchecked") - public void testConditionalRemoveReplacesChainOnHits() throws Exception { - Result result = mock(Result.class); - when(result.getValue()).thenReturn("foo"); + public void testSingleChainMultipleValues() throws StoreAccessException { + assertThat(Long.hashCode(1L), is(Long.hashCode(~1L))); - ResolvedChain resolvedChain = mock(ResolvedChain.class); - when(resolvedChain.getCompactedChain()).thenReturn(mock(Chain.class)); - when(resolvedChain.getResolvedResult(anyLong())).thenReturn(result); //simulate a key hit on chain resolution - when(resolvedChain.getCompactionCount()).thenReturn(1); + store.put(1L, "foo"); + store.put(~1L, "bar"); - ServerStoreProxy proxy = mock(ServerStoreProxy.class); - when(proxy.getAndAppend(anyLong(), any(ByteBuffer.class))).thenReturn(mock(Chain.class)); + Store.Iterator>> iterator = store.iterator(); - EternalChainResolver resolver = mock(EternalChainResolver.class); - when(resolver.resolve(any(Chain.class), anyLong(), anyLong())).thenReturn(resolvedChain); - - OperationsCodec codec = mock(OperationsCodec.class); - when(codec.encode(any())).thenReturn(ByteBuffer.allocate(0)); - TimeSource timeSource = mock(TimeSource.class); - - ClusteredStore store = new ClusteredStore<>(config, codec, resolver, proxy, timeSource); + Matcher>> entryOne = isEntry(1L, "foo"); + Matcher>> entryTwo = isEntry(~1L, "bar"); - store.remove(1L, "foo"); - verify(proxy).replaceAtHead(anyLong(), any(Chain.class), any(Chain.class)); - } + assertThat(iterator.hasNext(), is(true)); - @Test - @SuppressWarnings("unchecked") - public void testConditionalRemoveDoesNotReplaceChainOnKeyMiss() throws Exception { - ResolvedChain resolvedChain = mock(ResolvedChain.class); - when(resolvedChain.getResolvedResult(anyLong())).thenReturn(null); //simulate a key miss on chain resolution - - EternalChainResolver resolver = mock(EternalChainResolver.class); - when(resolver.resolve(any(Chain.class), anyLong(), anyLong())).thenReturn(resolvedChain); + Cache.Entry> next = iterator.next(); + assertThat(next, either(entryOne).or(entryTwo)); - OperationsCodec codec = mock(OperationsCodec.class); - when(codec.encode(any())).thenReturn(ByteBuffer.allocate(0)); - ServerStoreProxy proxy = mock(ServerStoreProxy.class); - when(proxy.getAndAppend(anyLong(), any(ByteBuffer.class))).thenReturn(mock(Chain.class)); - TimeSource timeSource = mock(TimeSource.class); - - ClusteredStore store = new ClusteredStore<>(config, codec, resolver, proxy, timeSource); + if (entryOne.matches(next)) { + assertThat(iterator.hasNext(), is(true)); + assertThat(iterator.next(), is(entryTwo)); + assertThat(iterator.hasNext(), is(false)); + } else { + assertThat(iterator.hasNext(), is(true)); + assertThat(iterator.next(), is(entryOne)); + assertThat(iterator.hasNext(), is(false)); + } - store.remove(1L, "foo"); - verify(proxy, never()).replaceAtHead(anyLong(), any(Chain.class), any(Chain.class)); + try { + iterator.next(); + fail("Expected NoSuchElementException"); + } catch (NoSuchElementException e) { + //expected + } } @Test - @SuppressWarnings("unchecked") - public void testConditionalRemoveDoesNotReplaceChainOnKeyHitValueMiss() throws Exception { - Result result = mock(Result.class); - ResolvedChain resolvedChain = mock(ResolvedChain.class); - when(resolvedChain.getResolvedResult(anyLong())).thenReturn(result); //simulate a key kit - when(result.getValue()).thenReturn("bar"); //but a value miss - + public void testSingleChainRequiresResolution() throws StoreAccessException { - EternalChainResolver resolver = mock(EternalChainResolver.class); - when(resolver.resolve(any(Chain.class), anyLong(), anyLong())).thenReturn(resolvedChain); - - OperationsCodec codec = mock(OperationsCodec.class); - when(codec.encode(any())).thenReturn(ByteBuffer.allocate(0)); - ServerStoreProxy proxy = mock(ServerStoreProxy.class); - when(proxy.getAndAppend(anyLong(), any(ByteBuffer.class))).thenReturn(mock(Chain.class)); - TimeSource timeSource = mock(TimeSource.class); + store.put(~1L, "bar"); + store.put(1L, "foo"); + store.remove(~1L); - ClusteredStore store = new ClusteredStore<>(config, codec, resolver, proxy, timeSource); + Store.Iterator>> iterator = store.iterator(); - store.remove(1L, "foo"); - verify(proxy, never()).replaceAtHead(anyLong(), any(Chain.class), any(Chain.class)); + assertThat(iterator.hasNext(), is(true)); + assertThat(iterator.next(), isEntry(1L, "foo")); + assertThat(iterator.hasNext(), is(false)); + try { + iterator.next(); + fail("Expected NoSuchElementException"); + } catch (NoSuchElementException e) { + //expected + } } @Test - public void testExpirationIsSentToHigherTiers() throws Exception { - @SuppressWarnings("unchecked") - Result result = mock(Result.class); - when(result.getValue()).thenReturn("bar"); + public void testMultipleChains() throws StoreAccessException { - @SuppressWarnings("unchecked") - ResolvedChain resolvedChain = mock(ResolvedChain.class); - when(resolvedChain.getResolvedResult(anyLong())).thenReturn(result); - when(resolvedChain.getExpirationTime()).thenReturn(1000L); + store.put(1L, "foo"); + store.put(2L, "bar"); - @SuppressWarnings("unchecked") - EternalChainResolver resolver = mock(EternalChainResolver.class); - when(resolver.resolve(any(Chain.class), anyLong(), anyLong())).thenReturn(resolvedChain); + Store.Iterator>> iterator = store.iterator(); - ServerStoreProxy proxy = mock(ServerStoreProxy.class); - when(proxy.get(anyLong())).thenReturn(mock(Chain.class)); + Matcher>> entryOne = isEntry(1L, "foo"); + Matcher>> entryTwo = isEntry(2L, "bar"); - @SuppressWarnings("unchecked") - OperationsCodec codec = mock(OperationsCodec.class); - TimeSource timeSource = mock(TimeSource.class); + assertThat(iterator.hasNext(), is(true)); - ClusteredStore store = new ClusteredStore<>(config, codec, resolver, proxy, timeSource); + Cache.Entry> next = iterator.next(); + assertThat(next, either(entryOne).or(entryTwo)); - Store.ValueHolder vh = store.get(1L); + if (entryOne.matches(next)) { + assertThat(iterator.hasNext(), is(true)); + assertThat(iterator.next(), is(entryTwo)); + assertThat(iterator.hasNext(), is(false)); + } else { + assertThat(iterator.hasNext(), is(true)); + assertThat(iterator.next(), is(entryOne)); + assertThat(iterator.hasNext(), is(false)); + } - long expirationTime = vh.expirationTime(TimeUnit.MILLISECONDS); - assertThat(expirationTime, is(1000L)); + try { + iterator.next(); + fail("Expected NoSuchElementException"); + } catch (NoSuchElementException e) { + //expected + } } - @Test - public void testNoExpireIsSentToHigherTiers() throws Exception { - @SuppressWarnings("unchecked") - Result result = mock(Result.class); - when(result.getValue()).thenReturn("bar"); - - @SuppressWarnings("unchecked") - ResolvedChain resolvedChain = mock(ResolvedChain.class); - when(resolvedChain.getResolvedResult(anyLong())).thenReturn(result); - when(resolvedChain.getExpirationTime()).thenReturn(Long.MAX_VALUE); // no expire + private Matcher>> isEntry(K key, V value) { + return new TypeSafeMatcher>>() { + @Override + public void describeTo(Description description) { + description.appendText(" the cache entry { ").appendValue(key).appendText(": ").appendValue(value).appendText(" }"); + } - @SuppressWarnings("unchecked") - EternalChainResolver resolver = mock(EternalChainResolver.class); - when(resolver.resolve(any(Chain.class), anyLong(), anyLong())).thenReturn(resolvedChain); - - ServerStoreProxy proxy = mock(ServerStoreProxy.class); - when(proxy.get(anyLong())).thenReturn(mock(Chain.class)); - - @SuppressWarnings("unchecked") - OperationsCodec codec = mock(OperationsCodec.class); - TimeSource timeSource = mock(TimeSource.class); - - ClusteredStore store = new ClusteredStore<>(config, codec, resolver, proxy, timeSource); - - Store.ValueHolder vh = store.get(1L); - - long expirationTime = vh.expirationTime(TimeUnit.MILLISECONDS); - assertThat(expirationTime, is(NO_EXPIRE)); + @Override + protected boolean matchesSafely(Cache.Entry> item) { + return Objects.equal(key, item.getKey()) && Objects.equal(value, item.getValue().get()); + } + }; } } diff --git a/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/CommonServerStoreProxyTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/CommonServerStoreProxyTest.java new file mode 100644 index 0000000000..3677e8781c --- /dev/null +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/CommonServerStoreProxyTest.java @@ -0,0 +1,493 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.clustered.client.internal.store; + +import org.ehcache.clustered.Matchers; +import org.ehcache.clustered.client.internal.store.ServerStoreProxy.ServerCallback; +import org.ehcache.clustered.common.Consistency; +import org.ehcache.clustered.common.internal.store.Chain; +import org.ehcache.clustered.common.internal.store.Element; +import org.ehcache.clustered.server.store.ObservableClusterTierServerEntityService; +import org.hamcrest.CoreMatchers; +import org.hamcrest.Matcher; +import org.hamcrest.core.Is; +import org.junit.Test; + +import java.nio.ByteBuffer; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicBoolean; + +import static org.ehcache.clustered.ChainUtils.chainOf; +import static org.ehcache.clustered.ChainUtils.createPayload; +import static org.ehcache.clustered.Matchers.hasPayloads; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.core.CombinableMatcher.either; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +public class CommonServerStoreProxyTest extends AbstractServerStoreProxyTest { + + @Test + public void testInvalidationsContainChains() throws Exception { + SimpleClusterTierClientEntity clientEntity1 = createClientEntity("testInvalidationsContainChains", Consistency.EVENTUAL, true); + SimpleClusterTierClientEntity clientEntity2 = createClientEntity("testInvalidationsContainChains", Consistency.EVENTUAL, false); + + final List store1InvalidatedHashes = new CopyOnWriteArrayList<>(); + final List store1InvalidatedChains = new CopyOnWriteArrayList<>(); + final AtomicBoolean store1InvalidatedAll = new AtomicBoolean(); + final List store2InvalidatedHashes = new CopyOnWriteArrayList<>(); + final List store2InvalidatedChains = new CopyOnWriteArrayList<>(); + final AtomicBoolean store2InvalidatedAll = new AtomicBoolean(); + + EventualServerStoreProxy serverStoreProxy1 = new EventualServerStoreProxy("testInvalidationsContainChains", clientEntity1, new ServerCallback() { + @Override + public void onInvalidateHash(long hash, Chain evictedChain) { + store1InvalidatedHashes.add(hash); + if (evictedChain != null) { + // make sure the chain's elements' buffers are correctly sized + for (Element element : evictedChain) { + assertThat(element.getPayload().limit(), is(512 * 1024)); + } + store1InvalidatedChains.add(evictedChain); + } + } + + @Override + public void onInvalidateAll() { + store1InvalidatedAll.set(true); + } + + @Override + public void onAppend(Chain beforeAppend, ByteBuffer appended) { + // make sure the appended buffer is correctly sized + assertThat(appended.limit(), is(512 * 1024)); + // make sure the chain's elements' buffers are correctly sized + for (Element element : beforeAppend) { + assertThat(element.getPayload().limit(), is(512 * 1024)); + } + } + + @Override + public void compact(ServerStoreProxy.ChainEntry chain) { + fail("should not be called"); + } + }); + serverStoreProxy1.enableEvents(true); + EventualServerStoreProxy serverStoreProxy2 = new EventualServerStoreProxy("testInvalidationsContainChains", clientEntity2, new ServerCallback() { + @Override + public void onInvalidateHash(long hash, Chain evictedChain) { + store2InvalidatedHashes.add(hash); + if (evictedChain != null) { + // make sure the chain's elements' buffers are correctly sized + for (Element element : evictedChain) { + assertThat(element.getPayload().limit(), is(512 * 1024)); + } + store2InvalidatedChains.add(evictedChain); + } + } + + @Override + public void onInvalidateAll() { + store2InvalidatedAll.set(true); + } + + @Override + public void onAppend(Chain beforeAppend, ByteBuffer appended) { + // make sure the appended buffer is correctly sized + assertThat(appended.limit(), is(512 * 1024)); + // make sure the chain's elements' buffers are correctly sized + for (Element element : beforeAppend) { + assertThat(element.getPayload().limit(), is(512 * 1024)); + } + } + + @Override + public void compact(ServerStoreProxy.ChainEntry chain) { + fail("should not be called"); + } + }); + serverStoreProxy2.enableEvents(true); + + final int ITERATIONS = 40; + for (int i = 0; i < ITERATIONS; i++) { + serverStoreProxy1.append(i, createPayload(i, 512 * 1024)); + } + + int evictionCount = 0; + int entryCount = 0; + for (int i = 0; i < ITERATIONS; i++) { + Chain elements1 = serverStoreProxy1.get(i); + Chain elements2 = serverStoreProxy2.get(i); + assertThat(elements1, Matchers.matchesChain(elements2)); + if (!elements1.isEmpty()) { + entryCount++; + } else { + evictionCount++; + } + } + + // there has to be server-side evictions, otherwise this test is useless + assertThat(store1InvalidatedHashes.size(), greaterThan(0)); + // test that each time the server evicted, the originating client got notified + assertThat(store1InvalidatedHashes.size(), Is.is(ITERATIONS - entryCount)); + // test that each time the server evicted, the other client got notified on top of normal invalidations + assertThat(store2InvalidatedHashes.size(), Is.is(ITERATIONS + evictionCount)); + // test that we got evicted chains + assertThat(store1InvalidatedChains.size(), greaterThan(0)); + assertThat(store2InvalidatedChains.size(), is(store1InvalidatedChains.size())); + + assertThatClientsWaitingForInvalidationIsEmpty("testInvalidationsContainChains"); + assertThat(store1InvalidatedAll.get(), is(false)); + assertThat(store2InvalidatedAll.get(), is(false)); + } + + @Test + public void testAppendFireEvents() throws Exception { + SimpleClusterTierClientEntity clientEntity1 = createClientEntity("testAppendFireEvents", Consistency.EVENTUAL, true); + SimpleClusterTierClientEntity clientEntity2 = createClientEntity("testAppendFireEvents", Consistency.EVENTUAL, false); + + final List store1AppendedBuffers = new CopyOnWriteArrayList<>(); + final List store1Chains = new CopyOnWriteArrayList<>(); + final AtomicBoolean store1InvalidatedAll = new AtomicBoolean(); + final List store2AppendedBuffers = new CopyOnWriteArrayList<>(); + final List store2Chains = new CopyOnWriteArrayList<>(); + final AtomicBoolean store2InvalidatedAll = new AtomicBoolean(); + + EventualServerStoreProxy serverStoreProxy1 = new EventualServerStoreProxy("testAppendFireEvents", clientEntity1, new ServerCallback() { + @Override + public void onInvalidateHash(long hash, Chain evictedChain) { + fail("should not be called"); + } + + @Override + public void onInvalidateAll() { + store1InvalidatedAll.set(true); + } + + @Override + public void onAppend(Chain beforeAppend, ByteBuffer appended) { + // make sure the appended buffer is correctly sized + assertThat(appended.limit(), is(512)); + // make sure the chain's elements' buffers are correctly sized + for (Element element : beforeAppend) { + assertThat(element.getPayload().limit(), is(512)); + } + store1AppendedBuffers.add(appended); + store1Chains.add(beforeAppend); + } + + @Override + public void compact(ServerStoreProxy.ChainEntry chain) { + fail("should not be called"); + } + }); + serverStoreProxy1.enableEvents(true); + EventualServerStoreProxy serverStoreProxy2 = new EventualServerStoreProxy("testAppendFireEvents", clientEntity2, new ServerCallback() { + @Override + public void onInvalidateHash(long hash, Chain evictedChain) { + // make sure those only are cross-client invalidations and not server evictions + assertThat(evictedChain, is(nullValue())); + } + + @Override + public void onInvalidateAll() { + store2InvalidatedAll.set(true); + } + + @Override + public void onAppend(Chain beforeAppend, ByteBuffer appended) { + // make sure the appended buffer is correctly sized + assertThat(appended.limit(), is(512)); + // make sure the chain's elements' buffers are correctly sized + for (Element element : beforeAppend) { + assertThat(element.getPayload().limit(), is(512)); + } + store2AppendedBuffers.add(appended); + store2Chains.add(beforeAppend); + } + + @Override + public void compact(ServerStoreProxy.ChainEntry chain) { + fail("should not be called"); + } + }); + serverStoreProxy2.enableEvents(true); + + serverStoreProxy1.append(1L, createPayload(1L, 512)); + Chain c = serverStoreProxy1.getAndAppend(1L, createPayload(2L, 512)); + assertThat(c.length(), is(1)); + + assertThatClientsWaitingForInvalidationIsEmpty("testAppendFireEvents"); + + assertThat(store1AppendedBuffers.size(), is(2)); + assertThat(store1AppendedBuffers.get(0).asLongBuffer().get(), is(1L)); + assertThat(store1AppendedBuffers.get(1).asLongBuffer().get(), is(2L)); + assertThat(store1Chains.size(), is(2)); + assertThat(store1Chains.get(0).length(), is(0)); + assertThat(store1Chains.get(1).length(), is(1)); + assertThat(store1InvalidatedAll.get(), is(false)); + assertThat(store2AppendedBuffers.size(), is(2)); + assertThat(store2AppendedBuffers.get(0).asLongBuffer().get(), is(1L)); + assertThat(store2AppendedBuffers.get(1).asLongBuffer().get(), is(2L)); + assertThat(store2Chains.size(), is(2)); + assertThat(store2Chains.get(0).length(), is(0)); + assertThat(store2Chains.get(1).length(), is(1)); + assertThat(store2InvalidatedAll.get(), is(false)); + } + + private static void assertThatClientsWaitingForInvalidationIsEmpty(String name) throws Exception { + ObservableClusterTierServerEntityService.ObservableClusterTierActiveEntity activeEntity = observableClusterTierService.getServedActiveEntitiesFor(name).get(0); + long now = System.currentTimeMillis(); + while (System.currentTimeMillis() < now + 5000 && activeEntity.getClientsWaitingForInvalidation().size() != 0); + assertThat(activeEntity.getClientsWaitingForInvalidation().size(), Is.is(0)); + } + + @Test + public void testGetKeyNotPresent() throws Exception { + ClusterTierClientEntity clientEntity = createClientEntity("testGetKeyNotPresent", Consistency.EVENTUAL, true); + CommonServerStoreProxy serverStoreProxy = new CommonServerStoreProxy("testGetKeyNotPresent", clientEntity, mock(ServerCallback.class)); + + Chain chain = serverStoreProxy.get(1); + + assertThat(chain.isEmpty(), is(true)); + } + + @Test + public void testAppendKeyNotPresent() throws Exception { + ClusterTierClientEntity clientEntity = createClientEntity("testAppendKeyNotPresent", Consistency.EVENTUAL, true); + CommonServerStoreProxy serverStoreProxy = new CommonServerStoreProxy("testAppendKeyNotPresent", clientEntity, mock(ServerCallback.class)); + + serverStoreProxy.append(2, createPayload(2)); + + Chain chain = serverStoreProxy.get(2); + assertThat(chain.isEmpty(), is(false)); + assertThat(chain, hasPayloads(2L)); + } + + @Test + public void testGetAfterMultipleAppendsOnSameKey() throws Exception { + ClusterTierClientEntity clientEntity = createClientEntity("testGetAfterMultipleAppendsOnSameKey", Consistency.EVENTUAL, true); + CommonServerStoreProxy serverStoreProxy = new CommonServerStoreProxy("testGetAfterMultipleAppendsOnSameKey", clientEntity, mock(ServerCallback.class)); + + serverStoreProxy.append(3L, createPayload(3L)); + serverStoreProxy.append(3L, createPayload(33L)); + serverStoreProxy.append(3L, createPayload(333L)); + + Chain chain = serverStoreProxy.get(3L); + + assertThat(chain.isEmpty(), is(false)); + + assertThat(chain, hasPayloads(3L, 33L, 333l)); + } + + @Test + public void testGetAndAppendKeyNotPresent() throws Exception { + ClusterTierClientEntity clientEntity = createClientEntity("testGetAndAppendKeyNotPresent", Consistency.EVENTUAL, true); + CommonServerStoreProxy serverStoreProxy = new CommonServerStoreProxy("testGetAndAppendKeyNotPresent", clientEntity, mock(ServerCallback.class)); + Chain chain = serverStoreProxy.getAndAppend(4L, createPayload(4L)); + + assertThat(chain.isEmpty(), is(true)); + + chain = serverStoreProxy.get(4L); + + assertThat(chain.isEmpty(), is(false)); + assertThat(chain, hasPayloads(4L)); + } + + @Test + public void testGetAndAppendMultipleTimesOnSameKey() throws Exception { + ClusterTierClientEntity clientEntity = createClientEntity("testGetAndAppendMultipleTimesOnSameKey", Consistency.EVENTUAL, true); + CommonServerStoreProxy serverStoreProxy = new CommonServerStoreProxy("testGetAndAppendMultipleTimesOnSameKey", clientEntity, mock(ServerCallback.class)); + serverStoreProxy.getAndAppend(5L, createPayload(5L)); + serverStoreProxy.getAndAppend(5L, createPayload(55L)); + serverStoreProxy.getAndAppend(5L, createPayload(555L)); + Chain chain = serverStoreProxy.getAndAppend(5l, createPayload(5555L)); + + assertThat(chain.isEmpty(), is(false)); + assertThat(chain, hasPayloads(5L, 55L, 555L)); + } + + @Test + public void testReplaceAtHeadSuccessFull() throws Exception { + ClusterTierClientEntity clientEntity = createClientEntity("testReplaceAtHeadSuccessFull", Consistency.EVENTUAL, true); + CommonServerStoreProxy serverStoreProxy = new CommonServerStoreProxy("testReplaceAtHeadSuccessFull", clientEntity, mock(ServerCallback.class)); + serverStoreProxy.append(20L, createPayload(200L)); + serverStoreProxy.append(20L, createPayload(2000L)); + serverStoreProxy.append(20L, createPayload(20000L)); + + Chain expect = serverStoreProxy.get(20L); + Chain update = chainOf(createPayload(400L)); + + serverStoreProxy.replaceAtHead(20l, expect, update); + + Chain afterReplace = serverStoreProxy.get(20L); + assertThat(afterReplace, hasPayloads(400L)); + + serverStoreProxy.append(20L, createPayload(4000L)); + serverStoreProxy.append(20L, createPayload(40000L)); + + serverStoreProxy.replaceAtHead(20L, afterReplace, chainOf(createPayload(800L))); + + Chain anotherReplace = serverStoreProxy.get(20L); + + assertThat(anotherReplace, hasPayloads(800L, 4000L, 40000L)); + } + + @Test + public void testClear() throws Exception { + ClusterTierClientEntity clientEntity = createClientEntity("testClear", Consistency.EVENTUAL, true); + CommonServerStoreProxy serverStoreProxy = new CommonServerStoreProxy("testClear", clientEntity, mock(ServerCallback.class)); + serverStoreProxy.append(1L, createPayload(100L)); + + serverStoreProxy.clear(); + Chain chain = serverStoreProxy.get(1); + assertThat(chain.isEmpty(), is(true)); + } + + @Test + public void testResolveRequestIsProcessedAtThreshold() throws Exception { + ByteBuffer buffer = createPayload(42L); + + ClusterTierClientEntity clientEntity = createClientEntity("testResolveRequestIsProcessed", Consistency.EVENTUAL, true); + ServerCallback serverCallback = mock(ServerCallback.class); + doAnswer(inv -> { + ServerStoreProxy.ChainEntry entry = inv.getArgument(0); + entry.replaceAtHead(chainOf(buffer.duplicate())); + return null; + }).when(serverCallback).compact(any(ServerStoreProxy.ChainEntry.class), any(long.class)); + + CommonServerStoreProxy serverStoreProxy = new CommonServerStoreProxy("testResolveRequestIsProcessed", clientEntity, serverCallback); + + for (int i = 0; i < 8; i++) { + serverStoreProxy.append(1L, buffer.duplicate()); + } + verify(serverCallback, never()).compact(any(ServerStoreProxy.ChainEntry.class)); + assertThat(serverStoreProxy.get(1L), hasPayloads(42L, 42L, 42L, 42L, 42L, 42L, 42L, 42L)); + + //trigger compaction at > 8 entries + serverStoreProxy.append(1L, buffer.duplicate()); + verify(serverCallback).compact(any(ServerStoreProxy.ChainEntry.class), any(long.class)); + assertThat(serverStoreProxy.get(1L), hasPayloads(42L)); + } + + @Test + public void testEmptyStoreIteratorIsEmpty() throws Exception { + ClusterTierClientEntity clientEntity = createClientEntity("testEmptyStoreIteratorIsEmpty", Consistency.EVENTUAL, true); + CommonServerStoreProxy serverStoreProxy = new CommonServerStoreProxy("testEmptyStoreIteratorIsEmpty", clientEntity, mock(ServerCallback.class)); + + Iterator> iterator = serverStoreProxy.iterator(); + + assertThat(iterator.hasNext(), is(false)); + try { + iterator.next(); + fail("Expected NoSuchElementException"); + } catch (NoSuchElementException e) { + //expected + } + } + + @Test + public void testSingleChainIterator() throws Exception { + ClusterTierClientEntity clientEntity = createClientEntity("testSingleChainIterator", Consistency.EVENTUAL, true); + CommonServerStoreProxy serverStoreProxy = new CommonServerStoreProxy("testSingleChainIterator", clientEntity, mock(ServerCallback.class)); + + serverStoreProxy.append(1L, createPayload(42L)); + + Iterator> iterator = serverStoreProxy.iterator(); + + assertThat(iterator.hasNext(), is(true)); + Map.Entry next = iterator.next(); + assertThat(next.getKey(), is(1L)); + assertThat(next.getValue(), hasPayloads(42L)); + assertThat(iterator.hasNext(), is(false)); + try { + iterator.next(); + fail("Expected NoSuchElementException"); + } catch (NoSuchElementException e) { + //expected + } + } + + @Test + public void testSingleChainMultipleElements() throws Exception { + ClusterTierClientEntity clientEntity = createClientEntity("testSingleChainMultipleElements", Consistency.EVENTUAL, true); + CommonServerStoreProxy serverStoreProxy = new CommonServerStoreProxy("testSingleChainMultipleElements", clientEntity, mock(ServerCallback.class)); + + serverStoreProxy.append(1L, createPayload(42L)); + serverStoreProxy.append(1L, createPayload(43L)); + + Iterator> iterator = serverStoreProxy.iterator(); + + assertThat(iterator.hasNext(), is(true)); + Map.Entry next = iterator.next(); + assertThat(next.getKey(), is(1L)); + assertThat(next.getValue(), hasPayloads(42L, 43L)); + assertThat(iterator.hasNext(), CoreMatchers.is(false)); + try { + iterator.next(); + fail("Expected NoSuchElementException"); + } catch (NoSuchElementException e) { + //expected + } + } + + @Test + public void testMultipleChains() throws Exception { + ClusterTierClientEntity clientEntity = createClientEntity("testMultipleChains", Consistency.EVENTUAL, true); + CommonServerStoreProxy serverStoreProxy = new CommonServerStoreProxy("testMultipleChains", clientEntity, mock(ServerCallback.class)); + + serverStoreProxy.append(1L, createPayload(42L)); + serverStoreProxy.append(2L, createPayload(43L)); + + Iterator> iterator = serverStoreProxy.iterator(); + + Matcher chainOne = hasPayloads(42L); + Matcher chainTwo = hasPayloads(43L); + + assertThat(iterator.hasNext(), CoreMatchers.is(true)); + + Chain next = iterator.next().getValue(); + assertThat(next, either(chainOne).or(chainTwo)); + + if (chainOne.matches(next)) { + assertThat(iterator.hasNext(), is(true)); + assertThat(iterator.next().getValue(), is(chainTwo)); + assertThat(iterator.hasNext(), is(false)); + } else { + assertThat(iterator.hasNext(), is(true)); + assertThat(iterator.next().getValue(), is(chainOne)); + assertThat(iterator.hasNext(), is(false)); + } + + try { + iterator.next(); + fail("Expected NoSuchElementException"); + } catch (NoSuchElementException e) { + //expected + } + } +} diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/EventualServerStoreProxyTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/EventualServerStoreProxyTest.java similarity index 74% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/EventualServerStoreProxyTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/EventualServerStoreProxyTest.java index 7bed2fef30..776fe2c93a 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/EventualServerStoreProxyTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/EventualServerStoreProxyTest.java @@ -15,17 +15,14 @@ */ package org.ehcache.clustered.client.internal.store; -import org.ehcache.clustered.client.config.ClusteredResourcePool; -import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder; +import org.ehcache.clustered.Matchers; import org.ehcache.clustered.client.internal.store.ServerStoreProxy.ServerCallback; import org.ehcache.clustered.common.Consistency; -import org.ehcache.clustered.common.internal.ServerStoreConfiguration; import org.ehcache.clustered.common.internal.store.Chain; import org.ehcache.clustered.server.store.ObservableClusterTierServerEntityService.ObservableClusterTierActiveEntity; -import org.ehcache.config.units.MemoryUnit; -import org.ehcache.impl.serialization.LongSerializer; import org.junit.Test; +import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; @@ -33,8 +30,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; -import static org.ehcache.clustered.common.internal.store.Util.chainsEqual; -import static org.ehcache.clustered.common.internal.store.Util.createPayload; +import static org.ehcache.clustered.ChainUtils.createPayload; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.core.Is.is; @@ -43,54 +39,55 @@ public class EventualServerStoreProxyTest extends AbstractServerStoreProxyTest { - private static SimpleClusterTierClientEntity createClientEntity(String name, boolean create) throws Exception { - ClusteredResourcePool resourcePool = ClusteredResourcePoolBuilder.clusteredDedicated(8L, MemoryUnit.MB); - - ServerStoreConfiguration serverStoreConfiguration = new ServerStoreConfiguration(resourcePool.getPoolAllocation(), Long.class.getName(), - Long.class.getName(), LongSerializer.class.getName(), LongSerializer.class - .getName(), Consistency.EVENTUAL, false); - - return createClientEntity(name, serverStoreConfiguration, create); - } - @Test public void testServerSideEvictionFiresInvalidations() throws Exception { - SimpleClusterTierClientEntity clientEntity1 = createClientEntity("testServerSideEvictionFiresInvalidations", true); - SimpleClusterTierClientEntity clientEntity2 = createClientEntity("testServerSideEvictionFiresInvalidations", false); + SimpleClusterTierClientEntity clientEntity1 = createClientEntity("testServerSideEvictionFiresInvalidations", Consistency.EVENTUAL, true); + SimpleClusterTierClientEntity clientEntity2 = createClientEntity("testServerSideEvictionFiresInvalidations", Consistency.EVENTUAL, false); final List store1InvalidatedHashes = new CopyOnWriteArrayList<>(); + final AtomicBoolean store1InvalidatedAll = new AtomicBoolean(); final List store2InvalidatedHashes = new CopyOnWriteArrayList<>(); + final AtomicBoolean store2InvalidatedAll = new AtomicBoolean(); EventualServerStoreProxy serverStoreProxy1 = new EventualServerStoreProxy("testServerSideEvictionFiresInvalidations", clientEntity1, new ServerCallback() { @Override - public void onInvalidateHash(long hash) { + public void onInvalidateHash(long hash, Chain evictedChain) { store1InvalidatedHashes.add(hash); } @Override public void onInvalidateAll() { + store1InvalidatedAll.set(true); + } + + @Override + public void onAppend(Chain beforeAppend, ByteBuffer appended) { fail("should not be called"); } @Override - public Chain compact(Chain chain) { + public void compact(ServerStoreProxy.ChainEntry chain) { throw new AssertionError(); } }); EventualServerStoreProxy serverStoreProxy2 = new EventualServerStoreProxy("testServerSideEvictionFiresInvalidations", clientEntity2, new ServerCallback() { @Override - public void onInvalidateHash(long hash) { + public void onInvalidateHash(long hash, Chain evictedChain) { store2InvalidatedHashes.add(hash); } @Override public void onInvalidateAll() { + store2InvalidatedAll.set(true); + } + + @Override + public void onAppend(Chain beforeAppend, ByteBuffer appended) { fail("should not be called"); } @Override - public Chain compact(Chain chain) { - return chain; + public void compact(ServerStoreProxy.ChainEntry chain) { } }); @@ -104,7 +101,7 @@ public Chain compact(Chain chain) { for (int i = 0; i < ITERATIONS; i++) { Chain elements1 = serverStoreProxy1.get(i); Chain elements2 = serverStoreProxy2.get(i); - assertThat(chainsEqual(elements1, elements2), is(true)); + assertThat(elements1, Matchers.matchesChain(elements2)); if (!elements1.isEmpty()) { entryCount++; } else { @@ -120,31 +117,40 @@ public Chain compact(Chain chain) { assertThat(store2InvalidatedHashes.size(), is(ITERATIONS + evictionCount)); assertThatClientsWaitingForInvalidationIsEmpty("testServerSideEvictionFiresInvalidations"); + + assertThat(store1InvalidatedAll.get(), is(false)); + assertThat(store2InvalidatedAll.get(), is(false)); } @Test public void testHashInvalidationListenerWithAppend() throws Exception { - SimpleClusterTierClientEntity clientEntity1 = createClientEntity("testHashInvalidationListenerWithAppend", true); - SimpleClusterTierClientEntity clientEntity2 = createClientEntity("testHashInvalidationListenerWithAppend", false); + SimpleClusterTierClientEntity clientEntity1 = createClientEntity("testHashInvalidationListenerWithAppend", Consistency.EVENTUAL, true); + SimpleClusterTierClientEntity clientEntity2 = createClientEntity("testHashInvalidationListenerWithAppend", Consistency.EVENTUAL, false); final CountDownLatch latch = new CountDownLatch(1); final AtomicReference invalidatedHash = new AtomicReference<>(); + final AtomicBoolean invalidatedAll = new AtomicBoolean(); EventualServerStoreProxy serverStoreProxy1 = new EventualServerStoreProxy("testHashInvalidationListenerWithAppend", clientEntity1, new ServerCallback() { @Override - public void onInvalidateHash(long hash) { + public void onInvalidateHash(long hash, Chain evictedChain) { invalidatedHash.set(hash); latch.countDown(); } @Override public void onInvalidateAll() { - throw new AssertionError("Should not be called"); + invalidatedAll.set(true); } @Override - public Chain compact(Chain chain) { + public void onAppend(Chain beforeAppend, ByteBuffer appended) { + fail("should not be called"); + } + + @Override + public void compact(ServerStoreProxy.ChainEntry chain) { throw new AssertionError(); } }); @@ -155,31 +161,38 @@ public Chain compact(Chain chain) { latch.await(5, TimeUnit.SECONDS); assertThat(invalidatedHash.get(), is(1L)); assertThatClientsWaitingForInvalidationIsEmpty("testHashInvalidationListenerWithAppend"); + assertThat(invalidatedAll.get(), is(false)); } @Test public void testHashInvalidationListenerWithGetAndAppend() throws Exception { - SimpleClusterTierClientEntity clientEntity1 = createClientEntity("testHashInvalidationListenerWithGetAndAppend", true); - SimpleClusterTierClientEntity clientEntity2 = createClientEntity("testHashInvalidationListenerWithGetAndAppend", false); + SimpleClusterTierClientEntity clientEntity1 = createClientEntity("testHashInvalidationListenerWithGetAndAppend", Consistency.EVENTUAL, true); + SimpleClusterTierClientEntity clientEntity2 = createClientEntity("testHashInvalidationListenerWithGetAndAppend", Consistency.EVENTUAL, false); final CountDownLatch latch = new CountDownLatch(1); final AtomicReference invalidatedHash = new AtomicReference<>(); + final AtomicBoolean invalidatedAll = new AtomicBoolean(); EventualServerStoreProxy serverStoreProxy1 = new EventualServerStoreProxy("testHashInvalidationListenerWithGetAndAppend", clientEntity1, new ServerCallback() { @Override - public void onInvalidateHash(long hash) { + public void onInvalidateHash(long hash, Chain evictedChain) { invalidatedHash.set(hash); latch.countDown(); } @Override public void onInvalidateAll() { - throw new AssertionError("Should not be called"); + invalidatedAll.set(true); + } + + @Override + public void onAppend(Chain beforeAppend, ByteBuffer appended) { + fail("should not be called"); } @Override - public Chain compact(Chain chain) { + public void compact(ServerStoreProxy.ChainEntry chain) { throw new AssertionError(); } }); @@ -190,19 +203,20 @@ public Chain compact(Chain chain) { latch.await(5, TimeUnit.SECONDS); assertThat(invalidatedHash.get(), is(1L)); assertThatClientsWaitingForInvalidationIsEmpty("testHashInvalidationListenerWithGetAndAppend"); + assertThat(invalidatedAll.get(), is(false)); } @Test public void testAllInvalidationListener() throws Exception { - SimpleClusterTierClientEntity clientEntity1 = createClientEntity("testAllInvalidationListener", true); - SimpleClusterTierClientEntity clientEntity2 = createClientEntity("testAllInvalidationListener", false); + SimpleClusterTierClientEntity clientEntity1 = createClientEntity("testAllInvalidationListener", Consistency.EVENTUAL, true); + SimpleClusterTierClientEntity clientEntity2 = createClientEntity("testAllInvalidationListener", Consistency.EVENTUAL, false); final CountDownLatch latch = new CountDownLatch(1); final AtomicBoolean invalidatedAll = new AtomicBoolean(); EventualServerStoreProxy serverStoreProxy1 = new EventualServerStoreProxy("testAllInvalidationListener", clientEntity1, new ServerCallback() { @Override - public void onInvalidateHash(long hash) { + public void onInvalidateHash(long hash, Chain evictedChain) { throw new AssertionError("Should not be called"); } @@ -213,7 +227,12 @@ public void onInvalidateAll() { } @Override - public Chain compact(Chain chain) { + public void onAppend(Chain beforeAppend, ByteBuffer appended) { + fail("should not be called"); + } + + @Override + public void compact(ServerStoreProxy.ChainEntry chain) { throw new AssertionError(); } }); diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/MultiThreadedStrongServerStoreProxyTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/MultiThreadedStrongServerStoreProxyTest.java similarity index 89% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/MultiThreadedStrongServerStoreProxyTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/MultiThreadedStrongServerStoreProxyTest.java index 2dc34d4c2c..344567685c 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/MultiThreadedStrongServerStoreProxyTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/MultiThreadedStrongServerStoreProxyTest.java @@ -26,13 +26,15 @@ import org.junit.Assert; import org.junit.Test; +import java.nio.ByteBuffer; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; -import static org.ehcache.clustered.common.internal.store.Util.createPayload; +import static org.ehcache.clustered.ChainUtils.createPayload; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertNotNull; @@ -58,6 +60,7 @@ public void testConcurrentHashInvalidationListenerWithAppend() throws Exception final AtomicReference invalidatedHash = new AtomicReference<>(); SimpleClusterTierClientEntity clientEntity1 = createClientEntity(ENTITY_NAME, getServerStoreConfiguration(), true, true); StrongServerStoreProxy serverStoreProxy1 = new StrongServerStoreProxy(ENTITY_NAME, clientEntity1, mock(ServerCallback.class)); + AtomicBoolean invalidatedAll = new AtomicBoolean(); ExecutorService executor = Executors.newSingleThreadExecutor(); CountDownLatch beforeValidationLatch = new CountDownLatch(1); @@ -67,17 +70,22 @@ public void testConcurrentHashInvalidationListenerWithAppend() throws Exception SimpleClusterTierClientEntity clientEntity2 = createClientEntity(ENTITY_NAME, getServerStoreConfiguration(), false, false); StrongServerStoreProxy serverStoreProxy2 = new StrongServerStoreProxy(ENTITY_NAME, clientEntity2, new ServerCallback() { @Override - public void onInvalidateHash(long hash) { + public void onInvalidateHash(long hash, Chain evictedChain) { invalidatedHash.set(hash); } @Override public void onInvalidateAll() { + invalidatedAll.set(true); + } + + @Override + public void onAppend(Chain beforeAppend, ByteBuffer appended) { throw new AssertionError("Should not be called"); } @Override - public Chain compact(Chain chain) { + public void compact(ServerStoreProxy.ChainEntry chain) { throw new AssertionError(); } }); @@ -97,7 +105,7 @@ public Chain compact(Chain chain) { assertTrue(afterValidationLatch.await(MAX_WAIT_TIME_SECONDS, TimeUnit.SECONDS)); serverStoreProxy1.append(1L, createPayload(1L)); assertThat(invalidatedHash.get(), is(1L)); - + assertThat(invalidatedAll.get(), is(false)); executor.shutdownNow(); } } diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/ReconnectingServerStoreProxyTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/ReconnectingServerStoreProxyTest.java similarity index 76% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/ReconnectingServerStoreProxyTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/ReconnectingServerStoreProxyTest.java index 16cfe1768e..7ab175942c 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/ReconnectingServerStoreProxyTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/ReconnectingServerStoreProxyTest.java @@ -17,7 +17,6 @@ import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; @@ -26,6 +25,7 @@ import java.nio.ByteBuffer; +import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.doThrow; @@ -41,9 +41,6 @@ public class ReconnectingServerStoreProxyTest { @Mock Runnable runnable; - @Rule - public ExpectedException exception = ExpectedException.none(); - private final ServerStoreProxyException storeProxyException = new ServerStoreProxyException(new ConnectionClosedException("Connection Closed")); @InjectMocks @@ -51,20 +48,16 @@ public class ReconnectingServerStoreProxyTest { @Test public void testAppend() throws Exception { - doThrow(storeProxyException).when(proxy).append(anyLong(), any(ByteBuffer.class)); - exception.expect(ReconnectInProgressException.class); - serverStoreProxy.append(0, ByteBuffer.allocate(2)); + assertThrows(ReconnectInProgressException.class, () -> serverStoreProxy.append(0, ByteBuffer.allocate(2))); } @Test public void testGetAndAppend() throws Exception { - doThrow(storeProxyException).when(proxy).getAndAppend(anyLong(), any(ByteBuffer.class)); - exception.expect(ReconnectInProgressException.class); - serverStoreProxy.getAndAppend(0, ByteBuffer.allocate(2)); + assertThrows(ReconnectInProgressException.class, () -> serverStoreProxy.getAndAppend(0, ByteBuffer.allocate(2))); } @Test @@ -72,8 +65,13 @@ public void testGet() throws Exception { doThrow(storeProxyException).when(proxy).get(anyLong()); - exception.expect(ReconnectInProgressException.class); - serverStoreProxy.get(0); + assertThrows(ReconnectInProgressException.class, () -> serverStoreProxy.get(0)); } -} \ No newline at end of file + @Test + public void testIterator() throws Exception { + doThrow(storeProxyException).when(proxy).iterator(); + + assertThrows(ReconnectInProgressException.class, () -> serverStoreProxy.iterator()); + } +} diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/StrongServerStoreProxyTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/StrongServerStoreProxyTest.java similarity index 62% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/StrongServerStoreProxyTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/StrongServerStoreProxyTest.java index fab62756a8..feb10da5b5 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/StrongServerStoreProxyTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/StrongServerStoreProxyTest.java @@ -15,16 +15,14 @@ */ package org.ehcache.clustered.client.internal.store; -import org.ehcache.clustered.client.config.ClusteredResourcePool; -import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder; +import org.ehcache.clustered.client.config.Timeouts; import org.ehcache.clustered.client.internal.store.ServerStoreProxy.ServerCallback; import org.ehcache.clustered.common.Consistency; -import org.ehcache.clustered.common.internal.ServerStoreConfiguration; import org.ehcache.clustered.common.internal.store.Chain; -import org.ehcache.config.units.MemoryUnit; -import org.ehcache.impl.serialization.LongSerializer; import org.junit.Test; +import org.terracotta.exception.ConnectionClosedException; +import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; @@ -34,65 +32,70 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; -import static org.ehcache.clustered.common.internal.store.Util.chainsEqual; -import static org.ehcache.clustered.common.internal.store.Util.createPayload; +import static org.ehcache.clustered.ChainUtils.createPayload; +import static org.ehcache.clustered.Matchers.matchesChain; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsInstanceOf.instanceOf; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class StrongServerStoreProxyTest extends AbstractServerStoreProxyTest { - private static SimpleClusterTierClientEntity createClientEntity(String name, boolean create) throws Exception { - ClusteredResourcePool resourcePool = ClusteredResourcePoolBuilder.clusteredDedicated(4L, MemoryUnit.MB); - - ServerStoreConfiguration serverStoreConfiguration = new ServerStoreConfiguration(resourcePool.getPoolAllocation(), Long.class.getName(), - Long.class.getName(), LongSerializer.class.getName(), LongSerializer.class - .getName(), Consistency.STRONG, false); - - return createClientEntity(name, serverStoreConfiguration, create); - } - @Test public void testServerSideEvictionFiresInvalidations() throws Exception { - SimpleClusterTierClientEntity clientEntity1 = createClientEntity("testServerSideEvictionFiresInvalidations", true); - SimpleClusterTierClientEntity clientEntity2 = createClientEntity("testServerSideEvictionFiresInvalidations", false); + SimpleClusterTierClientEntity clientEntity1 = createClientEntity("testServerSideEvictionFiresInvalidations", Consistency.STRONG, true); + SimpleClusterTierClientEntity clientEntity2 = createClientEntity("testServerSideEvictionFiresInvalidations", Consistency.STRONG, false); final List store1InvalidatedHashes = new CopyOnWriteArrayList<>(); + final AtomicBoolean store1InvalidatedAll = new AtomicBoolean(); final List store2InvalidatedHashes = new CopyOnWriteArrayList<>(); + final AtomicBoolean store2InvalidatedAll = new AtomicBoolean(); StrongServerStoreProxy serverStoreProxy1 = new StrongServerStoreProxy("testServerSideEvictionFiresInvalidations", clientEntity1, new ServerCallback() { @Override - public void onInvalidateHash(long hash) { + public void onInvalidateHash(long hash, Chain evictedChain) { store1InvalidatedHashes.add(hash); } @Override public void onInvalidateAll() { + store1InvalidatedAll.set(true); + } + + @Override + public void onAppend(Chain beforeAppend, ByteBuffer appended) { fail("should not be called"); } @Override - public Chain compact(Chain chain) { + public void compact(ServerStoreProxy.ChainEntry chain) { throw new AssertionError(); } }); StrongServerStoreProxy serverStoreProxy2 = new StrongServerStoreProxy("testServerSideEvictionFiresInvalidations", clientEntity2, new ServerCallback() { @Override - public void onInvalidateHash(long hash) { + public void onInvalidateHash(long hash, Chain evictedChain) { store2InvalidatedHashes.add(hash); } @Override public void onInvalidateAll() { + store2InvalidatedAll.set(true); + } + + @Override + public void onAppend(Chain beforeAppend, ByteBuffer appended) { fail("should not be called"); } @Override - public Chain compact(Chain chain) { - return chain; + public void compact(ServerStoreProxy.ChainEntry chain) { } }); @@ -106,7 +109,7 @@ public Chain compact(Chain chain) { for (int i = 0; i < ITERATIONS; i++) { Chain elements1 = serverStoreProxy1.get(i); Chain elements2 = serverStoreProxy2.get(i); - assertThat(chainsEqual(elements1, elements2), is(true)); + assertThat(elements2, matchesChain(elements2)); if (!elements1.isEmpty()) { entryCount++; } else { @@ -120,29 +123,37 @@ public Chain compact(Chain chain) { assertThat(store1InvalidatedHashes.size(), is(ITERATIONS - entryCount)); // test that each time the server evicted, the other client got notified on top of normal invalidations assertThat(store2InvalidatedHashes.size(), is(ITERATIONS + evictionCount)); + assertThat(store1InvalidatedAll.get(), is(false)); + assertThat(store2InvalidatedAll.get(), is(false)); } @Test public void testHashInvalidationListenerWithAppend() throws Exception { - SimpleClusterTierClientEntity clientEntity1 = createClientEntity("testHashInvalidationListenerWithAppend", true); - SimpleClusterTierClientEntity clientEntity2 = createClientEntity("testHashInvalidationListenerWithAppend", false); + SimpleClusterTierClientEntity clientEntity1 = createClientEntity("testHashInvalidationListenerWithAppend", Consistency.STRONG, true); + SimpleClusterTierClientEntity clientEntity2 = createClientEntity("testHashInvalidationListenerWithAppend", Consistency.STRONG, false); final AtomicReference invalidatedHash = new AtomicReference<>(); + final AtomicBoolean invalidatedAll = new AtomicBoolean(); StrongServerStoreProxy serverStoreProxy1 = new StrongServerStoreProxy("testHashInvalidationListenerWithAppend", clientEntity1, mock(ServerCallback.class)); StrongServerStoreProxy serverStoreProxy2 = new StrongServerStoreProxy("testHashInvalidationListenerWithAppend", clientEntity2, new ServerCallback() { @Override - public void onInvalidateHash(long hash) { + public void onInvalidateHash(long hash, Chain evictedChain) { invalidatedHash.set(hash); } @Override public void onInvalidateAll() { - throw new AssertionError("Should not be called"); + invalidatedAll.set(true); + } + + @Override + public void onAppend(Chain beforeAppend, ByteBuffer appended) { + fail("should not be called"); } @Override - public Chain compact(Chain chain) { + public void compact(ServerStoreProxy.ChainEntry chain) { throw new AssertionError(); } }); @@ -150,19 +161,21 @@ public Chain compact(Chain chain) { serverStoreProxy1.append(1L, createPayload(1L)); assertThat(invalidatedHash.get(), is(1L)); + assertThat(invalidatedAll.get(), is(false)); } @Test public void testConcurrentHashInvalidationListenerWithAppend() throws Exception { - SimpleClusterTierClientEntity clientEntity1 = createClientEntity("testConcurrentHashInvalidationListenerWithAppend", true); - SimpleClusterTierClientEntity clientEntity2 = createClientEntity("testConcurrentHashInvalidationListenerWithAppend", false); + SimpleClusterTierClientEntity clientEntity1 = createClientEntity("testConcurrentHashInvalidationListenerWithAppend", Consistency.STRONG, true); + SimpleClusterTierClientEntity clientEntity2 = createClientEntity("testConcurrentHashInvalidationListenerWithAppend", Consistency.STRONG, false); final AtomicBoolean invalidating = new AtomicBoolean(); + final AtomicBoolean invalidatedAll = new AtomicBoolean(); final CountDownLatch latch = new CountDownLatch(2); StrongServerStoreProxy serverStoreProxy1 = new StrongServerStoreProxy("testConcurrentHashInvalidationListenerWithAppend", clientEntity1, mock(ServerCallback.class)); StrongServerStoreProxy serverStoreProxy2 = new StrongServerStoreProxy("testConcurrentHashInvalidationListenerWithAppend", clientEntity2, new ServerCallback() { @Override - public void onInvalidateHash(long hash) { + public void onInvalidateHash(long hash, Chain evictedChain) { if (!invalidating.compareAndSet(false, true)) { fail("Both threads entered the listener concurrently"); } @@ -177,11 +190,16 @@ public void onInvalidateHash(long hash) { @Override public void onInvalidateAll() { - throw new AssertionError("Should not be called"); + invalidatedAll.set(true); + } + + @Override + public void onAppend(Chain beforeAppend, ByteBuffer appended) { + fail("should not be called"); } @Override - public Chain compact(Chain chain) { + public void compact(ServerStoreProxy.ChainEntry chain) { throw new AssertionError(); } }); @@ -203,29 +221,35 @@ public Chain compact(Chain chain) { } finally { executor.shutdown(); } + assertThat(invalidatedAll.get(), is(false)); } @Test public void testHashInvalidationListenerWithGetAndAppend() throws Exception { - SimpleClusterTierClientEntity clientEntity1 = createClientEntity("testHashInvalidationListenerWithGetAndAppend", true); - SimpleClusterTierClientEntity clientEntity2 = createClientEntity("testHashInvalidationListenerWithGetAndAppend", false); + SimpleClusterTierClientEntity clientEntity1 = createClientEntity("testHashInvalidationListenerWithGetAndAppend", Consistency.STRONG, true); + SimpleClusterTierClientEntity clientEntity2 = createClientEntity("testHashInvalidationListenerWithGetAndAppend", Consistency.STRONG, false); final AtomicReference invalidatedHash = new AtomicReference<>(); - + final AtomicBoolean invalidatedAll = new AtomicBoolean(); StrongServerStoreProxy serverStoreProxy1 = new StrongServerStoreProxy("testHashInvalidationListenerWithGetAndAppend", clientEntity1, mock(ServerCallback.class)); StrongServerStoreProxy serverStoreProxy2 = new StrongServerStoreProxy("testHashInvalidationListenerWithGetAndAppend", clientEntity2, new ServerCallback() { @Override - public void onInvalidateHash(long hash) { + public void onInvalidateHash(long hash, Chain evictedChain) { invalidatedHash.set(hash); } @Override public void onInvalidateAll() { - throw new AssertionError("Should not be called"); + invalidatedAll.set(true); + } + + @Override + public void onAppend(Chain beforeAppend, ByteBuffer appended) { + fail("should not be called"); } @Override - public Chain compact(Chain chain) { + public void compact(ServerStoreProxy.ChainEntry chain) { throw new AssertionError(); } }); @@ -233,19 +257,20 @@ public Chain compact(Chain chain) { serverStoreProxy1.getAndAppend(1L, createPayload(1L)); assertThat(invalidatedHash.get(), is(1L)); + assertThat(invalidatedAll.get(), is(false)); } @Test public void testAllInvalidationListener() throws Exception { - SimpleClusterTierClientEntity clientEntity1 = createClientEntity("testAllInvalidationListener", true); - SimpleClusterTierClientEntity clientEntity2 = createClientEntity("testAllInvalidationListener", false); + SimpleClusterTierClientEntity clientEntity1 = createClientEntity("testAllInvalidationListener", Consistency.STRONG, true); + SimpleClusterTierClientEntity clientEntity2 = createClientEntity("testAllInvalidationListener", Consistency.STRONG, false); final AtomicBoolean invalidatedAll = new AtomicBoolean(); StrongServerStoreProxy serverStoreProxy1 = new StrongServerStoreProxy("testAllInvalidationListener", clientEntity1, mock(ServerCallback.class)); StrongServerStoreProxy serverStoreProxy2 = new StrongServerStoreProxy("testAllInvalidationListener", clientEntity2, new ServerCallback() { @Override - public void onInvalidateHash(long hash) { + public void onInvalidateHash(long hash, Chain evictedChain) { throw new AssertionError("Should not be called"); } @@ -255,7 +280,12 @@ public void onInvalidateAll() { } @Override - public Chain compact(Chain chain) { + public void onAppend(Chain beforeAppend, ByteBuffer appended) { + fail("should not be called"); + } + + @Override + public void compact(ServerStoreProxy.ChainEntry chain) { throw new AssertionError(); } }); @@ -267,8 +297,8 @@ public Chain compact(Chain chain) { @Test public void testConcurrentAllInvalidationListener() throws Exception { - SimpleClusterTierClientEntity clientEntity1 = createClientEntity("testConcurrentAllInvalidationListener", true); - SimpleClusterTierClientEntity clientEntity2 = createClientEntity("testConcurrentAllInvalidationListener", false); + SimpleClusterTierClientEntity clientEntity1 = createClientEntity("testConcurrentAllInvalidationListener", Consistency.STRONG, true); + SimpleClusterTierClientEntity clientEntity2 = createClientEntity("testConcurrentAllInvalidationListener", Consistency.STRONG, false); final AtomicBoolean invalidating = new AtomicBoolean(); final CountDownLatch latch = new CountDownLatch(2); @@ -276,7 +306,7 @@ public void testConcurrentAllInvalidationListener() throws Exception { StrongServerStoreProxy serverStoreProxy1 = new StrongServerStoreProxy("testConcurrentAllInvalidationListener", clientEntity1, mock(ServerCallback.class)); StrongServerStoreProxy serverStoreProxy2 = new StrongServerStoreProxy("testConcurrentAllInvalidationListener", clientEntity2, new ServerCallback() { @Override - public void onInvalidateHash(long hash) { + public void onInvalidateHash(long hash, Chain evictedChain) { throw new AssertionError("Should not be called"); } @@ -295,7 +325,12 @@ public void onInvalidateAll() { } @Override - public Chain compact(Chain chain) { + public void onAppend(Chain beforeAppend, ByteBuffer appended) { + fail("should not be called"); + } + + @Override + public void compact(ServerStoreProxy.ChainEntry chain) { throw new AssertionError(); } }); @@ -321,23 +356,30 @@ public Chain compact(Chain chain) { @Test public void testAppendInvalidationUnblockedByDisconnection() throws Exception { - SimpleClusterTierClientEntity clientEntity1 = createClientEntity("testAppendInvalidationUnblockedByDisconnection", true); - SimpleClusterTierClientEntity clientEntity2 = createClientEntity("testAppendInvalidationUnblockedByDisconnection", false); + SimpleClusterTierClientEntity clientEntity1 = createClientEntity("testAppendInvalidationUnblockedByDisconnection", Consistency.STRONG, true); + SimpleClusterTierClientEntity clientEntity2 = createClientEntity("testAppendInvalidationUnblockedByDisconnection", Consistency.STRONG, false); + + final AtomicBoolean invalidatedAll = new AtomicBoolean(); StrongServerStoreProxy serverStoreProxy1 = new StrongServerStoreProxy("testAppendInvalidationUnblockedByDisconnection", clientEntity1, mock(ServerCallback.class)); StrongServerStoreProxy serverStoreProxy2 = new StrongServerStoreProxy("testAppendInvalidationUnblockedByDisconnection", clientEntity2, new ServerCallback() { @Override - public void onInvalidateHash(long hash) { + public void onInvalidateHash(long hash, Chain evictedChain) { clientEntity1.fireDisconnectionEvent(); } @Override public void onInvalidateAll() { - throw new AssertionError("Should not be called"); + invalidatedAll.set(true); + } + + @Override + public void onAppend(Chain beforeAppend, ByteBuffer appended) { + fail("should not be called"); } @Override - public Chain compact(Chain chain) { + public void compact(ServerStoreProxy.ChainEntry chain) { throw new AssertionError(); } }); @@ -348,17 +390,18 @@ public Chain compact(Chain chain) { } catch (RuntimeException re) { assertThat(re.getCause(), instanceOf(IllegalStateException.class)); } + assertThat(invalidatedAll.get(), is(false)); } @Test public void testClearInvalidationUnblockedByDisconnection() throws Exception { - SimpleClusterTierClientEntity clientEntity1 = createClientEntity("testClearInvalidationUnblockedByDisconnection", true); - SimpleClusterTierClientEntity clientEntity2 = createClientEntity("testClearInvalidationUnblockedByDisconnection", false); + SimpleClusterTierClientEntity clientEntity1 = createClientEntity("testClearInvalidationUnblockedByDisconnection", Consistency.STRONG, true); + SimpleClusterTierClientEntity clientEntity2 = createClientEntity("testClearInvalidationUnblockedByDisconnection", Consistency.STRONG, false); StrongServerStoreProxy serverStoreProxy1 = new StrongServerStoreProxy("testClearInvalidationUnblockedByDisconnection", clientEntity1, mock(ServerCallback.class)); StrongServerStoreProxy serverStoreProxy2 = new StrongServerStoreProxy("testClearInvalidationUnblockedByDisconnection", clientEntity2, new ServerCallback() { @Override - public void onInvalidateHash(long hash) { + public void onInvalidateHash(long hash, Chain evictedChain) { throw new AssertionError("Should not be called"); } @@ -368,7 +411,12 @@ public void onInvalidateAll() { } @Override - public Chain compact(Chain chain) { + public void onAppend(Chain beforeAppend, ByteBuffer appended) { + fail("should not be called"); + } + + @Override + public void compact(ServerStoreProxy.ChainEntry chain) { throw new AssertionError(); } }); @@ -380,4 +428,39 @@ public Chain compact(Chain chain) { assertThat(re.getCause(), instanceOf(IllegalStateException.class)); } } + + @Test + public void testAppendThrowsConnectionClosedExceptionDuringHashInvalidation() throws Exception { + SimpleClusterTierClientEntity clientEntity1 = mock(SimpleClusterTierClientEntity.class); + StrongServerStoreProxy serverStoreProxy1 = new StrongServerStoreProxy("testAppendThrowsConnectionClosedExceptionDuringHashInvalidation", clientEntity1, mock(ServerCallback.class)); + doThrow(new ConnectionClosedException("Test")).when(clientEntity1).invokeAndWaitForReceive(any(), anyBoolean()); + when(clientEntity1.getTimeouts()).thenReturn(Timeouts.DEFAULT); + when(clientEntity1.isConnected()).thenReturn(true); + try { + serverStoreProxy1.append(1L, createPayload(1L)); + fail("Expected ServerStoreProxyException"); + } catch (ServerStoreProxyException e) { + assertThat(e.getCause(), instanceOf(ConnectionClosedException.class)); + } catch (RuntimeException e) { + fail("Expected ServerStoreProxyException"); + } + } + + @Test + public void testClearThrowsConnectionClosedExceptionDuringAllInvaildation() throws Exception { + SimpleClusterTierClientEntity clientEntity1 = mock(SimpleClusterTierClientEntity.class); + StrongServerStoreProxy serverStoreProxy1 = new StrongServerStoreProxy("testClearThrowsConnectionClosedExceptionDuringAllInvaildation", clientEntity1, mock(ServerCallback.class)); + doThrow(new ConnectionClosedException("Test")).when(clientEntity1).invokeAndWaitForRetired(any(), anyBoolean()); + when(clientEntity1.getTimeouts()).thenReturn(Timeouts.DEFAULT); + when(clientEntity1.isConnected()).thenReturn(true); + try { + serverStoreProxy1.clear(); + fail("Expected ServerStoreProxyException"); + } catch (ServerStoreProxyException e) { + assertThat(e.getCause(), instanceOf(ConnectionClosedException.class)); + } catch (RuntimeException e) { + fail("Expected ServerStoreProxyException"); + } + } + } diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/lock/LockManagerImplTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/lock/LockManagerTest.java similarity index 68% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/lock/LockManagerImplTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/lock/LockManagerTest.java index d3c7ec4759..de51c9f1e3 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/lock/LockManagerImplTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/lock/LockManagerTest.java @@ -18,34 +18,37 @@ import org.ehcache.clustered.client.internal.store.ClusterTierClientEntity; import org.ehcache.clustered.client.internal.store.ServerStoreProxyException; import org.ehcache.clustered.common.internal.exceptions.UnknownClusterException; +import org.ehcache.clustered.common.internal.messages.ClusterTierReconnectMessage; import org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse; import org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.LockSuccess; import org.ehcache.clustered.common.internal.messages.ServerStoreOpMessage.LockMessage; import org.ehcache.clustered.common.internal.store.Chain; -import org.ehcache.clustered.common.internal.store.Util; import org.junit.Test; +import org.mockito.ArgumentCaptor; import java.nio.ByteBuffer; +import java.util.Set; import java.util.concurrent.TimeoutException; +import static org.ehcache.clustered.ChainUtils.chainOf; +import static org.ehcache.clustered.ChainUtils.createPayload; import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.lockFailure; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; -public class LockManagerImplTest { +public class LockManagerTest { @Test public void testLock() throws Exception { ClusterTierClientEntity clusterTierClientEntity = mock(ClusterTierClientEntity.class); - LockManagerImpl lockManager = new LockManagerImpl(clusterTierClientEntity); + LockManager lockManager = new LockManager(clusterTierClientEntity); LockSuccess lockSuccess = getLockSuccessResponse(); @@ -63,7 +66,7 @@ public void testLock() throws Exception { public void testLockWhenException() throws Exception { ClusterTierClientEntity clusterTierClientEntity = mock(ClusterTierClientEntity.class); - LockManagerImpl lockManager = new LockManagerImpl(clusterTierClientEntity); + LockManager lockManager = new LockManager(clusterTierClientEntity); when(clusterTierClientEntity.invokeAndWaitForComplete(any(LockMessage.class), anyBoolean())) .thenThrow(new UnknownClusterException(""), new TimeoutException("timed out test")); @@ -88,7 +91,7 @@ public void testLockWhenException() throws Exception { public void testLockWhenFailure() throws Exception { ClusterTierClientEntity clusterTierClientEntity = mock(ClusterTierClientEntity.class); - LockManagerImpl lockManager = new LockManagerImpl(clusterTierClientEntity); + LockManager lockManager = new LockManager(clusterTierClientEntity); LockSuccess lockSuccess = getLockSuccessResponse(); @@ -101,15 +104,36 @@ public void testLockWhenFailure() throws Exception { assertThat(lock.length(), is(3)); } + @SuppressWarnings("unchecked") + @Test + public void testUnlockClearsLocksHeldState() throws Exception { + ClusterTierClientEntity clusterTierClientEntity = mock(ClusterTierClientEntity.class); + LockManager lockManager = new LockManager(clusterTierClientEntity); + + LockSuccess lockSuccess = getLockSuccessResponse(); + when(clusterTierClientEntity.invokeAndWaitForComplete(any(LockMessage.class), anyBoolean())) + .thenReturn(lockSuccess); + + Chain lock = lockManager.lock(2L); + lockManager.unlock(2L, false); + + ClusterTierReconnectMessage reconnectMessage = mock(ClusterTierReconnectMessage.class); + ArgumentCaptor> locks = ArgumentCaptor.forClass(Set.class); + doNothing().when(reconnectMessage).addLocksHeld(locks.capture()); + lockManager.reconnectListener(reconnectMessage); + assertThat(locks.getValue().size(), is(0)); + + } + private LockSuccess getLockSuccessResponse() { ByteBuffer[] buffers = new ByteBuffer[3]; - for (int i = 1; i <= 3; i++) { - buffers[i-1] = Util.createPayload(i); + for (int i = 0; i < 3; i++) { + buffers[i] = createPayload(i + 1); } - Chain chain = Util.getChain(false, buffers); + Chain chain = chainOf(buffers); return EhcacheEntityResponse.lockSuccess(chain); } -} \ No newline at end of file +} diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/lock/LockRetentionDuringFailoverTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/lock/LockRetentionDuringFailoverTest.java similarity index 93% rename from clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/lock/LockRetentionDuringFailoverTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/lock/LockRetentionDuringFailoverTest.java index 3f26f4d520..c443c8776f 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/store/lock/LockRetentionDuringFailoverTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/internal/store/lock/LockRetentionDuringFailoverTest.java @@ -27,7 +27,6 @@ import org.ehcache.config.CacheConfiguration; import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.config.builders.CacheManagerBuilder; -import org.ehcache.impl.internal.concurrent.ConcurrentHashMap; import org.ehcache.spi.loaderwriter.CacheLoaderWriter; import org.junit.After; import org.junit.Before; @@ -38,6 +37,7 @@ import org.terracotta.passthrough.PassthroughTestHelpers; import java.net.URI; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -47,8 +47,8 @@ import static org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder.cluster; import static org.ehcache.clustered.client.internal.UnitTestConnectionService.getOffheapResourcesType; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; public class LockRetentionDuringFailoverTest { @@ -59,6 +59,7 @@ public class LockRetentionDuringFailoverTest { private CountDownLatch latch; private LatchedLoaderWriter loaderWriter; + private CacheManager cacheManager; private Cache cache; @Before @@ -90,7 +91,7 @@ public void setUp() throws Exception { .withLoaderWriter(loaderWriter) .build(); - CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().with(cluster(URI.create(STRIPE_URI)).autoCreate()) + cacheManager = CacheManagerBuilder.newCacheManagerBuilder().with(cluster(URI.create(STRIPE_URI)).autoCreate(c -> c)) .withCache("cache-1", config) .build(true); @@ -100,8 +101,12 @@ public void setUp() throws Exception { @After public void tearDown() throws Exception { - UnitTestConnectionService.removeStripe(STRIPENAME); - clusterControl.tearDown(); + try { + cacheManager.close(); + } finally { + UnitTestConnectionService.removeStripe(STRIPENAME); + clusterControl.tearDown(); + } } @Test diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/replication/ActivePassiveClientIdTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/replication/ActivePassiveClientIdTest.java similarity index 89% rename from clustered/client/src/test/java/org/ehcache/clustered/client/replication/ActivePassiveClientIdTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/replication/ActivePassiveClientIdTest.java index 20b4d1249d..4094626ddd 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/client/replication/ActivePassiveClientIdTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/replication/ActivePassiveClientIdTest.java @@ -29,13 +29,12 @@ import org.ehcache.clustered.common.Consistency; import org.ehcache.clustered.common.internal.messages.EhcacheEntityMessage; import org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse; -import org.ehcache.clustered.common.internal.store.Element; import org.ehcache.clustered.lock.server.VoltronReadWriteLockServerEntityService; import org.ehcache.clustered.server.ObservableEhcacheServerEntityService; import org.ehcache.clustered.server.store.ObservableClusterTierServerEntityService; import org.ehcache.config.CacheConfiguration; import org.ehcache.config.builders.CacheConfigurationBuilder; -import org.ehcache.core.internal.store.StoreConfigurationImpl; +import org.ehcache.core.store.StoreConfigurationImpl; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -46,18 +45,16 @@ import org.terracotta.passthrough.PassthroughTestHelpers; import java.net.URI; -import java.util.ArrayList; -import java.util.List; import java.util.Map; import java.util.function.Predicate; +import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; +import static org.ehcache.clustered.ChainUtils.chainOf; +import static org.ehcache.clustered.ChainUtils.createPayload; import static org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder.clusteredDedicated; import static org.ehcache.clustered.client.internal.UnitTestConnectionService.getOffheapResourcesType; -import static org.ehcache.clustered.common.internal.store.Util.createPayload; -import static org.ehcache.clustered.common.internal.store.Util.getChain; -import static org.ehcache.clustered.common.internal.store.Util.getElement; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; import static org.mockito.Mockito.mock; @@ -105,7 +102,7 @@ public void setUp() throws Exception { ClusteringServiceConfiguration configuration = ClusteringServiceConfigurationBuilder.cluster(URI.create(STRIPE_URI)) - .autoCreate() + .autoCreate(c -> c) .build(); service = new ClusteringServiceFactory().create(configuration); @@ -142,8 +139,7 @@ public void messageTrackedAndRemovedWhenClientLeaves() throws Exception { storeProxy.getAndAppend(42L, createPayload(42L)); - Map responses = activeMessageHandler.getTrackedResponsesForSegment(KEY_ENDS_UP_IN_SEGMENT_11, activeMessageHandler.getTrackedClients().findFirst().get()); - assertThat(responses).hasSize(1); // should now track one message + assertThat(activeMessageHandler.getRecordedMessages().collect(Collectors.toList())).hasSize(1); // should now track one message assertThat(activeEntity.getConnectedClients()).hasSize(1); // make sure we currently have one client attached @@ -161,11 +157,8 @@ public void untrackedMessageAreNotStored() throws Exception { // Nothing tracked assertThat(activeMessageHandler.getTrackedClients().count()).isZero(); - List elements = new ArrayList<>(1); - elements.add(getElement(createPayload(44L))); - // Send a replace message, those are not tracked - storeProxy.replaceAtHead(44L, getChain(elements), getChain(new ArrayList<>(0))); + storeProxy.replaceAtHead(44L, chainOf(createPayload(44L)), chainOf()); // Not tracked as well storeProxy.get(42L); @@ -188,7 +181,8 @@ public void trackedMessagesReplicatedToPassive() throws Exception { assertThat(passiveMessageHandler.getTrackedClients().count()).isEqualTo(1L); // one client tracked - Map responses = passiveMessageHandler.getTrackedResponsesForSegment(KEY_ENDS_UP_IN_SEGMENT_11, passiveMessageHandler.getTrackedClients().findFirst().get()); + Map responses = activeMessageHandler.getRecordedMessages().filter(r->r.getClientSourceId().toLong() == activeMessageHandler.getTrackedClients().findFirst().get().toLong()) + .collect(Collectors.toMap(r->r.getTransactionId(), r->r.getResponse())); assertThat(responses).hasSize(1); // one message should have sync } @@ -198,7 +192,8 @@ public void messageTrackedAndRemovedByPassiveWhenClientLeaves() throws Exception storeProxy.getAndAppend(42L, createPayload(42L)); - Map responses = passiveMessageHandler.getTrackedResponsesForSegment(KEY_ENDS_UP_IN_SEGMENT_11, passiveMessageHandler.getTrackedClients().findFirst().get()); + Map responses = activeMessageHandler.getRecordedMessages().filter(r->r.getClientSourceId().toLong() == activeMessageHandler.getTrackedClients().findFirst().get().toLong()) + .collect(Collectors.toMap(r->r.getTransactionId(), r->r.getResponse())); assertThat(responses).hasSize(1); // should now track one message service.stop(); // stop the service. It will remove the client diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/replication/ReplicationUtil.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/replication/ReplicationUtil.java similarity index 100% rename from clustered/client/src/test/java/org/ehcache/clustered/client/replication/ReplicationUtil.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/replication/ReplicationUtil.java diff --git a/clustered/ehcache-client/src/test/java/org/ehcache/clustered/common/internal/store/operations/AbstractChainResolverTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/common/internal/store/operations/AbstractChainResolverTest.java new file mode 100644 index 0000000000..b9f1d55306 --- /dev/null +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/common/internal/store/operations/AbstractChainResolverTest.java @@ -0,0 +1,791 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.clustered.common.internal.store.operations; + +import org.ehcache.clustered.client.internal.store.ServerStoreProxy; +import org.ehcache.clustered.common.internal.util.ChainBuilder; +import org.ehcache.clustered.client.internal.store.operations.ChainResolver; +import org.ehcache.clustered.common.internal.store.operations.codecs.OperationsCodec; +import org.ehcache.clustered.common.internal.store.Chain; +import org.ehcache.clustered.common.internal.store.Element; +import org.ehcache.config.builders.ExpiryPolicyBuilder; +import org.ehcache.core.spi.store.Store; +import org.ehcache.expiry.ExpiryPolicy; +import org.ehcache.impl.serialization.LongSerializer; +import org.ehcache.impl.serialization.StringSerializer; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; +import org.junit.Test; +import org.mockito.ArgumentMatcher; +import org.mockito.ArgumentMatchers; + +import java.nio.ByteBuffer; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static java.util.Collections.emptyMap; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.emptyIterable; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder; +import static org.hamcrest.collection.IsIterableContainingInOrder.contains; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +public abstract class AbstractChainResolverTest { + + private static OperationsCodec codec = new OperationsCodec<>(new LongSerializer(), new StringSerializer()); + + protected abstract ChainResolver createChainResolver(ExpiryPolicy expiryPolicy, OperationsCodec codec); + + @Test + @SuppressWarnings("unchecked") + public void testResolveMaintainsOtherKeysInOrder() { + PutOperation expected = new PutOperation<>(1L, "Suresh", 0L); + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new PutOperation<>(1L, "Albin", 0L), + new PutOperation<>(2L, "Albin", 0L), + expected, + new PutOperation<>(2L, "Suresh", 0L), + new PutOperation<>(2L, "Matthew", 0L)); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + Store.ValueHolder valueHolder = resolver.resolve(chain, 1L, 0L); + assertThat(valueHolder.get(), is(expected.getValue())); + verify(chain).replaceAtHead(argThat(contains( + operation(new PutOperation<>(2L, "Albin", 0L)), + operation(new PutOperation<>(2L, "Suresh", 0L)), + operation(new PutOperation<>(2L, "Matthew", 0L)), + operation(new PutOperation<>(1L, "Suresh", 0L))))); + } + + @Test + public void testResolveEmptyChain() { + ServerStoreProxy.ChainEntry chain = getEntryFromOperations(); + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + Store.ValueHolder valueHolder = resolver.resolve(chain, 1L, 0L); + assertThat(valueHolder, nullValue()); + verify(chain, never()).replaceAtHead(any()); + } + + @Test + public void testResolveChainWithNonExistentKey() { + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new PutOperation<>(1L, "Albin", 0L), + new PutOperation<>(2L, "Suresh", 0L), + new PutOperation<>(2L, "Matthew", 0L)); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + Store.ValueHolder valueHolder = resolver.resolve(chain, 3L, 0L); + assertThat(valueHolder, nullValue()); + verify(chain, never()).replaceAtHead(any()); + } + + @Test + public void testResolveSinglePut() { + PutOperation expected = new PutOperation<>(1L, "Albin", 0L); + ServerStoreProxy.ChainEntry chain = getEntryFromOperations(expected); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + Store.ValueHolder valueHolder = resolver.resolve(chain, 1L, 0L); + assertThat(valueHolder.get(), is(expected.getValue())); + verify(chain, never()).replaceAtHead(any()); + } + + @Test + public void testResolvePutsOnly() { + PutOperation expected = new PutOperation<>(1L, "Matthew", 0L); + + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new PutOperation<>(1L, "Albin", 0L), + new PutOperation<>(1L, "Suresh", 0L), + expected); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + Store.ValueHolder valueHolder = resolver.resolve(chain, 1L, 0L); + assertThat(valueHolder.get(), is(expected.getValue())); + verify(chain).replaceAtHead(argThat(contains(operation(expected)))); + } + + @Test + public void testResolveSingleRemove() { + ServerStoreProxy.ChainEntry chain = getEntryFromOperations(new RemoveOperation<>(1L, 0L)); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + Store.ValueHolder valueHolder = resolver.resolve(chain, 1L, 0L); + assertThat(valueHolder, nullValue()); + verify(chain).replaceAtHead(argThat(emptyIterable())); + } + + @Test + public void testResolveRemovesOnly() { + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new RemoveOperation<>(1L, 0L), + new RemoveOperation<>(1L, 0L)); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + Store.ValueHolder valueHolder = resolver.resolve(chain, 1L, 0L); + assertThat(valueHolder, nullValue()); + verify(chain).replaceAtHead(argThat(emptyIterable())); + } + + @Test + public void testPutAndRemove() { + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new PutOperation<>(1L, "Albin", 0L), + new RemoveOperation<>(1L, 0L)); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + Store.ValueHolder valueHolder = resolver.resolve(chain, 1L, 0L); + assertThat(valueHolder, nullValue()); + verify(chain).replaceAtHead(argThat(emptyIterable())); + } + + @Test + public void testResolvePutIfAbsentOnly() { + PutOperation expected = new PutOperation<>(1L, "Matthew", 0L); + ServerStoreProxy.ChainEntry chain = getEntryFromOperations(new PutIfAbsentOperation<>(1L, "Matthew", 0L)); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + Store.ValueHolder valueHolder = resolver.resolve(chain, 1L, 0L); + assertThat(valueHolder.get(), is(expected.getValue())); + verify(chain, never()).replaceAtHead(any()); + } + + @Test + public void testResolvePutIfAbsentsOnly() { + PutOperation expected = new PutOperation<>(1L, "Albin", 0L); + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new PutIfAbsentOperation<>(1L, "Albin", 0L), + new PutIfAbsentOperation<>(1L, "Suresh", 0L), + new PutIfAbsentOperation<>(1L, "Matthew", 0L)); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + Store.ValueHolder valueHolder = resolver.resolve(chain, 1L, 0L); + assertThat(valueHolder.get(), is(expected.getValue())); + verify(chain).replaceAtHead(argThat(contains(operation(expected)))); + } + + @Test + public void testResolvePutIfAbsentSucceeds() { + PutOperation expected = new PutOperation<>(1L, "Matthew", 0L); + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new PutOperation<>(1L, "Albin", 0L), + new RemoveOperation<>(1L, 0L), + new PutIfAbsentOperation<>(1L, "Matthew", 0L)); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + Store.ValueHolder valueHolder = resolver.resolve(chain, 1L, 0L); + assertThat(valueHolder.get(), is(expected.getValue())); + verify(chain).replaceAtHead(argThat(contains(operation(expected)))); + } + + @Test + public void testResolveForSingleOperationDoesNotCompact() { + ServerStoreProxy.ChainEntry chain = getEntryFromOperations(new PutOperation<>(1L, "Albin", 0L)); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + Store.ValueHolder valueHolder = resolver.resolve(chain, 1L, 0L); + assertThat(valueHolder.get(), is("Albin")); + verify(chain, never()).replaceAtHead(any()); + } + + @Test + @SuppressWarnings("unchecked") + public void testResolveForMultiplesOperationsAlwaysCompact() { + //create a random mix of operations + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new PutIfAbsentOperation<>(1L, "Albin", 0L), + new PutOperation<>(1L, "Suresh", 0L), + new PutOperation<>(1L, "Matthew", 0L), + new PutOperation<>(2L, "Melvin", 0L), + new ReplaceOperation<>(1L, "Joseph", 0L), + new RemoveOperation<>(2L, 0L), + new ConditionalRemoveOperation<>(1L, "Albin", 0L), + new PutOperation<>(1L, "Gregory", 0L), + new ConditionalReplaceOperation<>(1L, "Albin", "Abraham", 0L), + new RemoveOperation<>(1L, 0L), + new PutIfAbsentOperation<>(2L, "Albin", 0L)); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + Store.ValueHolder valueHolder = resolver.resolve(chain, 1L, 0L); + assertThat(valueHolder, nullValue()); + verify(chain).replaceAtHead(argThat(contains( + operation(new PutOperation<>(2L, "Melvin", 0L)), + operation(new RemoveOperation<>(2L, 0L)), + operation(new PutIfAbsentOperation<>(2L, "Albin", 0L)) + ))); + } + + @Test + public void testResolveDoesNotDecodeOtherKeyOperationValues() { + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new PutOperation<>(2L, "Albin", 0L), + new PutOperation<>(2L, "Suresh", 0L), + new PutOperation<>(2L, "Matthew", 0L)); + + CountingLongSerializer keySerializer = new CountingLongSerializer(); + CountingStringSerializer valueSerializer = new CountingStringSerializer(); + OperationsCodec customCodec = new OperationsCodec<>(keySerializer, valueSerializer); + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration(), customCodec); + resolver.resolve(chain, 1L, 0L); + + assertThat(keySerializer.decodeCount, is(3)); + assertThat(valueSerializer.decodeCount, is(0)); + assertThat(keySerializer.encodeCount, is(0)); + assertThat(valueSerializer.encodeCount, is(0)); //No operation to resolve + } + + @Test + public void testResolveDecodesOperationValueOnlyOnDemand() { + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new PutOperation<>(1L, "Albin", 1), + new PutOperation<>(1L, "Suresh", 2), + new PutOperation<>(1L, "Matthew", 3)); + + CountingLongSerializer keySerializer = new CountingLongSerializer(); + CountingStringSerializer valueSerializer = new CountingStringSerializer(); + OperationsCodec customCodec = new OperationsCodec<>(keySerializer, valueSerializer); + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration(), customCodec); + resolver.resolve(chain, 1L, 0L); + + assertThat(keySerializer.decodeCount, is(3)); + assertThat(valueSerializer.decodeCount, is(1)); + assertThat(valueSerializer.encodeCount, is(0)); + assertThat(keySerializer.encodeCount, is(1)); //One encode from encoding the resolved operation's key + } + + @Test + @SuppressWarnings("unchecked") + public void testCompactingTwoKeys() { + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new PutOperation<>(1L, "Albin", 0L), + new PutOperation<>(2L, "Albin", 0L), + new PutOperation<>(1L, "Suresh", 0L), + new PutOperation<>(2L, "Suresh", 0L), + new PutOperation<>(2L, "Matthew", 0L)); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + + resolver.compact(chain); + + verify(chain).replaceAtHead(argThat(containsInAnyOrder( //@SuppressWarnings("unchecked") + operation(new PutOperation<>(2L, "Matthew", 0L)), + operation(new PutOperation<>(1L, "Suresh", 0L)) + ))); + } + + @Test + public void testCompactEmptyChain() { + ServerStoreProxy.ChainEntry chain = getEntryFromOperations(); + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + resolver.compact(chain); + verify(chain, never()).replaceAtHead(any()); + } + + @Test + public void testCompactSinglePut() { + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new PutOperation<>(1L, "Albin", 0L) + ); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + resolver.compact(chain); + + verify(chain, never()).replaceAtHead(any()); + } + + @Test + public void testCompactMultiplePuts() { + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new PutOperation<>(1L, "Albin", 0L), + new PutOperation<>(1L, "Suresh", 0L), + new PutOperation<>(1L, "Matthew", 0L)); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + resolver.compact(chain); + verify(chain).replaceAtHead(argThat(contains(operation(new PutOperation<>(1L, "Matthew", 0L))))); + } + + @Test + public void testCompactSingleRemove() { + ServerStoreProxy.ChainEntry chain = getEntryFromOperations(new RemoveOperation<>(1L, 0L)); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + resolver.compact(chain); + verify(chain).replaceAtHead(argThat(emptyIterable())); + } + + @Test + public void testCompactMultipleRemoves() { + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new RemoveOperation<>(1L, 0L), + new RemoveOperation<>(1L, 0L)); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + resolver.compact(chain); + verify(chain).replaceAtHead(argThat(emptyIterable())); + } + + @Test + public void testCompactPutAndRemove() { + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new PutOperation<>(1L, "Albin", 0L), + new RemoveOperation<>(1L, 0L)); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + resolver.compact(chain); + verify(chain).replaceAtHead(argThat(emptyIterable())); + } + + @Test + public void testCompactSinglePutIfAbsent() { + ServerStoreProxy.ChainEntry chain = getEntryFromOperations(new PutIfAbsentOperation<>(1L, "Matthew", 0L)); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + resolver.compact(chain); + verify(chain, never()).replaceAtHead(any()); + } + + @Test + public void testCompactMultiplePutIfAbsents() { + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new PutIfAbsentOperation<>(1L, "Albin", 0L), + new PutIfAbsentOperation<>(1L, "Suresh", 0L), + new PutIfAbsentOperation<>(1L, "Matthew", 0L)); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + resolver.compact(chain); + verify(chain).replaceAtHead(argThat(contains(operation(new PutOperation<>(1L, "Albin", 0L))))); + } + + @Test + public void testCompactPutIfAbsentAfterRemove() { + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new PutOperation<>(1L, "Albin", 0L), + new RemoveOperation<>(1L, 0L), + new PutIfAbsentOperation<>(1L, "Matthew", 0L)); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + resolver.compact(chain); + verify(chain).replaceAtHead(argThat(contains(operation(new PutOperation<>(1L, "Matthew", 0L))))); + } + + @Test + public void testCompactForMultipleKeysAndOperations() { + //create a random mix of operations + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new PutIfAbsentOperation<>(1L, "Albin", 0L), + new PutOperation<>(1L, "Suresh", 0L), + new PutOperation<>(1L, "Matthew", 0L), + new PutOperation<>(2L, "Melvin", 0L), + new ReplaceOperation<>(1L, "Joseph", 0L), + new RemoveOperation<>(2L, 0L), + new ConditionalRemoveOperation<>(1L, "Albin", 0L), + new PutOperation<>(1L, "Gregory", 0L), + new ConditionalReplaceOperation<>(1L, "Albin", "Abraham", 0L), + new RemoveOperation<>(1L, 0L), + new PutIfAbsentOperation<>(2L, "Albin", 0L)); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + resolver.compact(chain); + verify(chain).replaceAtHead(argThat(contains(operation(new PutOperation<>(2L, "Albin", 0L))))); + } + + @Test + public void testCompactHasCorrectTimeStamp() { + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new PutOperation<>(1L, "Albin", 0), + new PutOperation<>(1L, "Albin", 1), + new RemoveOperation<>(1L, 2), + new PutOperation<>(1L, "Albin", 3)); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + resolver.compact(chain); + + verify(chain).replaceAtHead(argThat(contains(operation(new PutOperation<>(1L, "Albin", 3))))); + } + + @Test + public void testCompactDecodesOperationValueOnlyOnDemand() { + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new PutOperation<>(1L, "Albin", 1), + new PutOperation<>(1L, "Suresh", 2), + new PutOperation<>(1L, "Matthew", 3)); + + CountingLongSerializer keySerializer = new CountingLongSerializer(); + CountingStringSerializer valueSerializer = new CountingStringSerializer(); + OperationsCodec customCodec = new OperationsCodec<>(keySerializer, valueSerializer); + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration(), customCodec); + resolver.compact(chain); + + assertThat(keySerializer.decodeCount, is(3)); //Three decodes: one for each operation + assertThat(keySerializer.encodeCount, is(1)); //One encode from encoding the resolved operation's key + + assertThat(valueSerializer.decodeCount, is(0)); + assertThat(valueSerializer.encodeCount, is(0)); + } + + @Test + @SuppressWarnings("unchecked") + public void testResolvingTwoKeys() { + Chain chain = getEntryFromOperations( + new PutOperation<>(1L, "Albin", 0L), + new PutOperation<>(2L, "Albin", 0L), + new PutOperation<>(1L, "Suresh", 0L), + new PutOperation<>(2L, "Suresh", 0L), + new PutOperation<>(2L, "Matthew", 0L)); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + + Map> resolved = resolver.resolveAll(chain, 0L); + + assertThat(resolved.get(1L).get(), is("Suresh")); + assertThat(resolved.get(2L).get(), is("Matthew")); + } + + @Test + public void testFullResolveEmptyChain() { + Chain chain = (new ChainBuilder()).build(); + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + Map> resolved = resolver.resolveAll(chain, 0L); + assertThat(resolved, is(emptyMap())); + } + + @Test + public void testFullResolveSinglePut() { + Chain chain = getEntryFromOperations( + new PutOperation<>(1L, "Albin", 0L) + ); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + Map> resolved = resolver.resolveAll(chain, 0L); + + assertThat(resolved.get(1L).get(), is("Albin")); + } + + @Test + public void testFullResolveMultiplePuts() { + Chain chain = getEntryFromOperations( + new PutOperation<>(1L, "Albin", 0L), + new PutOperation<>(1L, "Suresh", 0L), + new PutOperation<>(1L, "Matthew", 0L)); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + Map> resolved = resolver.resolveAll(chain, 0L); + assertThat(resolved.get(1L).get(), is("Matthew")); + } + + @Test + public void testFullResolveSingleRemove() { + Chain chain = getEntryFromOperations(new RemoveOperation<>(1L, 0L)); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + Map> resolved = resolver.resolveAll(chain, 0L); + assertThat(resolved, is(emptyMap())); + } + + @Test + public void testFullResolveMultipleRemoves() { + Chain chain = getEntryFromOperations( + new RemoveOperation<>(1L, 0L), + new RemoveOperation<>(1L, 0L)); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + Map> resolved = resolver.resolveAll(chain, 0L); + assertThat(resolved, is(emptyMap())); + } + + @Test + public void testFullResolvePutAndRemove() { + Chain chain = getEntryFromOperations( + new PutOperation<>(1L, "Albin", 0L), + new RemoveOperation<>(1L, 0L)); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + Map> resolved = resolver.resolveAll(chain, 0L); + assertThat(resolved, is(emptyMap())); + } + + @Test + public void testFullResolveSinglePutIfAbsent() { + Chain chain = getEntryFromOperations(new PutIfAbsentOperation<>(1L, "Matthew", 0L)); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + Map> resolved = resolver.resolveAll(chain, 0L); + assertThat(resolved.get(1L).get(), is("Matthew")); + } + + @Test + public void testFullResolveMultiplePutIfAbsents() { + Chain chain = getEntryFromOperations( + new PutIfAbsentOperation<>(1L, "Albin", 0L), + new PutIfAbsentOperation<>(1L, "Suresh", 0L), + new PutIfAbsentOperation<>(1L, "Matthew", 0L)); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + Map> resolved = resolver.resolveAll(chain, 0L); + assertThat(resolved.get(1L).get(), is("Albin")); + } + + @Test + public void testFullResolvePutIfAbsentAfterRemove() { + Chain chain = getEntryFromOperations( + new PutOperation<>(1L, "Albin", 0L), + new RemoveOperation<>(1L, 0L), + new PutIfAbsentOperation<>(1L, "Matthew", 0L)); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + Map> resolved = resolver.resolveAll(chain, 0L); + assertThat(resolved.get(1L).get(), is("Matthew")); + } + + @Test + public void testFullResolveForMultipleKeysAndOperations() { + //create a random mix of operations + Chain chain = getEntryFromOperations( + new PutIfAbsentOperation<>(1L, "Albin", 0L), + new PutOperation<>(1L, "Suresh", 0L), + new PutOperation<>(1L, "Matthew", 0L), + new PutOperation<>(2L, "Melvin", 0L), + new ReplaceOperation<>(1L, "Joseph", 0L), + new RemoveOperation<>(2L, 0L), + new ConditionalRemoveOperation<>(1L, "Albin", 0L), + new PutOperation<>(1L, "Gregory", 0L), + new ConditionalReplaceOperation<>(1L, "Albin", "Abraham", 0L), + new RemoveOperation<>(1L, 0L), + new PutIfAbsentOperation<>(2L, "Albin", 0L)); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + Map> resolved = resolver.resolveAll(chain, 0L); + assertThat(resolved.get(2L).get(), is("Albin")); + } + + @Test + public void testFullResolveHasCorrectTimeStamp() { + Chain chain = getEntryFromOperations( + new PutOperation<>(1L, "Albin", 0), + new PutOperation<>(1L, "Albin", 1), + new RemoveOperation<>(1L, 2), + new PutOperation<>(1L, "Albin", 3)); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + Map> resolved = resolver.resolveAll(chain, 3); + + assertThat(resolved.get(1L).get(), is("Albin")); + } + + @Test + public void testResolveForMultipleOperationHasCorrectIsFirstAndTimeStamp() { + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new PutOperation<>(1L, "Albin1", 0), + new PutOperation<>(1L, "Albin2", 1), + new RemoveOperation<>(1L, 2), + new PutOperation<>(1L, "AlbinAfterRemove", 3)); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofHours(1))); + Store.ValueHolder valueHolder = resolver.resolve(chain, 1L, 3); + assertThat(valueHolder.get(), is("AlbinAfterRemove")); + verify(chain).replaceAtHead(argThat(contains(operation(new PutOperation<>(1L, "AlbinAfterRemove", TimeUnit.HOURS.toMillis(1) + 3))))); + } + + @Test + public void testResolveForMultipleOperationHasCorrectIsFirstAndTimeStampWithExpiry() { + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new PutOperation<>(1L, "Albin1", 0L), + new PutOperation<>(1L, "Albin2", 1L), + new PutOperation<>(1L, "Albin3", 2L), + new PutOperation<>(1L, "Albin4", 3L) + ); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMillis(1L))); + Store.ValueHolder valueHolder = resolver.resolve(chain, 1L, 3L); + + assertThat(valueHolder.get(), is("Albin4")); + verify(chain).replaceAtHead(argThat(contains(operation(new PutOperation<>(1L, "Albin4", 4L))))); + } + + @Test + public void testCompactHasCorrectWithExpiry() { + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new PutOperation<>(1L, "Albin1", 0L), + new PutOperation<>(1L, "Albin2", 1L), + new PutOperation<>(1L, "Albin3", 2L), + new PutOperation<>(1L, "Albin4", 3L) + ); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMillis(1L))); + resolver.compact(chain); + + verify(chain).replaceAtHead(argThat(contains(operation(new PutOperation<>(1L, "Albin4", 3L))))); + } + + protected ChainResolver createChainResolver(ExpiryPolicy expiryPolicy) { + return createChainResolver(expiryPolicy, codec); + } + + @Test + public void testNonExpiringTimestampIsCleared() throws TimeoutException { + PutOperation expected = new PutOperation<>(1L, "Albin", 0L); + ServerStoreProxy.ChainEntry chain = getEntryFromOperations(expected, + new TimestampOperation<>(1L, 1L) + ); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration()); + + assertThat(resolver.resolve(chain, 1L, 2L).get(), is("Albin")); + verify(chain).replaceAtHead(argThat(contains(operation(expected)))); + } + + + @SafeVarargs + protected final ServerStoreProxy.ChainEntry getEntryFromOperations(Operation ... operations) { + ChainBuilder chainBuilder = new ChainBuilder(); + for(Operation operation: operations) { + chainBuilder = chainBuilder.add(codec.encode(operation)); + } + Chain chain = chainBuilder.build(); + return spy(new ServerStoreProxy.ChainEntry(){ + + @Override + public Iterator iterator() { + return chain.iterator(); + } + + @Override + public void append(ByteBuffer payLoad) throws TimeoutException { + //nothing + } + + @Override + public void replaceAtHead(Chain equivalent) { + //nothing + } + + @Override + public boolean isEmpty() { + return chain.isEmpty(); + } + + @Override + public int length() { + return chain.length(); + } + }); + } + + protected List> getOperationsListFromChain(Chain chain) { + List> list = new ArrayList<>(); + for (Element element : chain) { + Operation operation = codec.decode(element.getPayload()); + list.add(operation); + } + return list; + } + + protected Matcher operation(Operation operation) { + return new TypeSafeMatcher() { + @Override + protected boolean matchesSafely(Element item) { + return operation.equals(codec.decode(item.getPayload())); + } + + @Override + public void describeTo(Description description) { + description.appendText("is ").appendValue(operation); + } + }; + } + + protected Matcher binaryOperation(Operation operation) { + return new TypeSafeMatcher() { + @Override + protected boolean matchesSafely(ByteBuffer item) { + return operation.equals(codec.decode(item.duplicate())); + } + + @Override + public void describeTo(Description description) { + description.appendText("is ").appendValue(operation); + } + }; + } + + protected static class CountingLongSerializer extends LongSerializer { + + protected int encodeCount = 0; + protected int decodeCount = 0; + + @Override + public ByteBuffer serialize(final Long object) { + encodeCount++; + return super.serialize(object); + } + + @Override + public Long read(final ByteBuffer binary) throws ClassNotFoundException { + decodeCount++; + return super.read(binary); + } + + @Override + public boolean equals(final Long object, final ByteBuffer binary) throws ClassNotFoundException { + return super.equals(object, binary); + } + } + + protected static class CountingStringSerializer extends StringSerializer { + + protected int encodeCount = 0; + protected int decodeCount = 0; + + @Override + public ByteBuffer serialize(final String object) { + encodeCount++; + return super.serialize(object); + } + + @Override + public String read(final ByteBuffer binary) throws ClassNotFoundException { + decodeCount++; + return super.read(binary); + } + + @Override + public boolean equals(final String object, final ByteBuffer binary) throws ClassNotFoundException { + return super.equals(object, binary); + } + } + + T argThat(Matcher matches) { + return ArgumentMatchers.argThat(new ArgumentMatcher() { + @Override + public boolean matches(T argument) { + return matches.matches(argument); + } + }); + } +} diff --git a/clustered/client/src/test/java/org/ehcache/clustered/common/internal/store/operations/BaseKeyValueOperationTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/common/internal/store/operations/BaseKeyValueOperationTest.java similarity index 100% rename from clustered/client/src/test/java/org/ehcache/clustered/common/internal/store/operations/BaseKeyValueOperationTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/common/internal/store/operations/BaseKeyValueOperationTest.java diff --git a/clustered/client/src/test/java/org/ehcache/clustered/common/internal/store/operations/ConditionalRemoveOperationTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/common/internal/store/operations/ConditionalRemoveOperationTest.java similarity index 100% rename from clustered/client/src/test/java/org/ehcache/clustered/common/internal/store/operations/ConditionalRemoveOperationTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/common/internal/store/operations/ConditionalRemoveOperationTest.java diff --git a/clustered/client/src/test/java/org/ehcache/clustered/common/internal/store/operations/ConditionalReplaceOperationTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/common/internal/store/operations/ConditionalReplaceOperationTest.java similarity index 95% rename from clustered/client/src/test/java/org/ehcache/clustered/common/internal/store/operations/ConditionalReplaceOperationTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/common/internal/store/operations/ConditionalReplaceOperationTest.java index d512e4838f..c87b1ebd6f 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/common/internal/store/operations/ConditionalReplaceOperationTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/common/internal/store/operations/ConditionalReplaceOperationTest.java @@ -27,8 +27,12 @@ import static org.ehcache.clustered.common.internal.store.operations.Operation.BYTE_SIZE_BYTES; import static org.ehcache.clustered.common.internal.store.operations.Operation.INT_SIZE_BYTES; import static org.ehcache.clustered.common.internal.store.operations.Operation.LONG_SIZE_BYTES; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.*; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; public class ConditionalReplaceOperationTest { diff --git a/clustered/ehcache-client/src/test/java/org/ehcache/clustered/common/internal/store/operations/EternalChainResolverTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/common/internal/store/operations/EternalChainResolverTest.java new file mode 100644 index 0000000000..e3ddfb8717 --- /dev/null +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/common/internal/store/operations/EternalChainResolverTest.java @@ -0,0 +1,35 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.clustered.common.internal.store.operations; + +import org.ehcache.clustered.client.internal.store.operations.ChainResolver; +import org.ehcache.clustered.client.internal.store.operations.EternalChainResolver; +import org.ehcache.clustered.common.internal.store.operations.codecs.OperationsCodec; +import org.ehcache.expiry.ExpiryPolicy; + +import static org.ehcache.config.builders.ExpiryPolicyBuilder.noExpiration; +import static org.hamcrest.Matchers.is; +import static org.junit.Assume.assumeThat; + +public class EternalChainResolverTest extends AbstractChainResolverTest { + + @Override + protected ChainResolver createChainResolver(ExpiryPolicy expiryPolicy, OperationsCodec codec) { + assumeThat(expiryPolicy, is(noExpiration())); + return new EternalChainResolver<>(codec); + } +} diff --git a/clustered/ehcache-client/src/test/java/org/ehcache/clustered/common/internal/store/operations/ExpiryChainResolverTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/common/internal/store/operations/ExpiryChainResolverTest.java new file mode 100644 index 0000000000..e17bc673ef --- /dev/null +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/common/internal/store/operations/ExpiryChainResolverTest.java @@ -0,0 +1,364 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.clustered.common.internal.store.operations; + +import org.ehcache.clustered.client.TestTimeSource; +import org.ehcache.clustered.client.internal.store.ServerStoreProxy; +import org.ehcache.clustered.client.internal.store.operations.ChainResolver; +import org.ehcache.clustered.client.internal.store.operations.ExpiryChainResolver; +import org.ehcache.clustered.common.internal.store.operations.codecs.OperationsCodec; +import org.ehcache.config.builders.ExpiryPolicyBuilder; +import org.ehcache.core.spi.store.Store; +import org.ehcache.core.spi.time.TimeSource; +import org.ehcache.expiry.ExpiryPolicy; +import org.junit.Test; +import org.mockito.InOrder; + +import java.util.Map; +import java.util.concurrent.TimeoutException; + +import static java.time.Duration.ofMillis; +import static java.util.Collections.emptyMap; +import static org.ehcache.config.builders.ExpiryPolicyBuilder.timeToLiveExpiration; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.emptyIterable; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.core.IsNull.nullValue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class ExpiryChainResolverTest extends AbstractChainResolverTest { + + @Override + protected ChainResolver createChainResolver(ExpiryPolicy expiryPolicy, OperationsCodec codec) { + return new ExpiryChainResolver<>(codec, expiryPolicy); + } + + @Test @Override + public void testCompactDecodesOperationValueOnlyOnDemand() { + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new PutOperation<>(1L, "Albin", 1), + new PutOperation<>(1L, "Suresh", 2), + new PutOperation<>(1L, "Matthew", 3)); + + CountingLongSerializer keySerializer = new CountingLongSerializer(); + CountingStringSerializer valueSerializer = new CountingStringSerializer(); + OperationsCodec customCodec = new OperationsCodec<>(keySerializer, valueSerializer); + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration(), customCodec); + resolver.compact(chain); + + assertThat(keySerializer.decodeCount, is(3)); + assertThat(valueSerializer.decodeCount, is(3)); + assertThat(valueSerializer.encodeCount, is(0)); + assertThat(keySerializer.encodeCount, is(1)); //One encode from encoding the resolved operation's key + } + + @Test @Override + public void testResolveDecodesOperationValueOnlyOnDemand() { + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new PutOperation<>(1L, "Albin", 1), + new PutOperation<>(1L, "Suresh", 2), + new PutOperation<>(1L, "Matthew", 3)); + + CountingLongSerializer keySerializer = new CountingLongSerializer(); + CountingStringSerializer valueSerializer = new CountingStringSerializer(); + OperationsCodec customCodec = new OperationsCodec<>(keySerializer, valueSerializer); + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.noExpiration(), customCodec); + resolver.resolve(chain, 1L, 0L); + + assertThat(keySerializer.decodeCount, is(3)); + assertThat(valueSerializer.decodeCount, is(3)); + assertThat(valueSerializer.encodeCount, is(0)); + assertThat(keySerializer.encodeCount, is(1)); //One encode from encoding the resolved operation's key + } + + @Test + @SuppressWarnings("unchecked") + public void testGetExpiryForAccessIsIgnored() { + TimeSource timeSource = new TestTimeSource(); + ExpiryPolicy expiry = mock(ExpiryPolicy.class); + ChainResolver chainResolver = createChainResolver(expiry); + + when(expiry.getExpiryForCreation(anyLong(), anyString())).thenReturn(ExpiryPolicy.INFINITE); + + + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new PutOperation<>(1L, "One", timeSource.getTimeMillis()), + new PutOperation<>(1L, "Second", timeSource.getTimeMillis()) + ); + + Store.ValueHolder valueHolder = chainResolver.resolve(chain, 1L, timeSource.getTimeMillis()); + + verify(expiry, times(0)).getExpiryForAccess(anyLong(), any()); + verify(expiry, times(1)).getExpiryForCreation(anyLong(), anyString()); + verify(expiry, times(1)).getExpiryForUpdate(anyLong(), any(), anyString()); + + verify(chain).replaceAtHead(any()); + } + + @Test + @SuppressWarnings("unchecked") + public void testGetExpiryForCreationIsInvokedOnlyOnce() { + TimeSource timeSource = new TestTimeSource(); + ExpiryPolicy expiry = mock(ExpiryPolicy.class); + ChainResolver chainResolver = createChainResolver(expiry); + + when(expiry.getExpiryForCreation(anyLong(), anyString())).thenReturn(ExpiryPolicy.INFINITE); + + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new PutOperation<>(1L, "One", timeSource.getTimeMillis()), + new PutOperation<>(1L, "Second", timeSource.getTimeMillis()), + new PutOperation<>(1L, "Three", timeSource.getTimeMillis()), + new PutOperation<>(1L, "Four", timeSource.getTimeMillis()) + ); + + Store.ValueHolder valueHolder = chainResolver.resolve(chain, 1L, timeSource.getTimeMillis()); + + InOrder inOrder = inOrder(expiry); + + inOrder.verify(expiry, times(1)).getExpiryForCreation(anyLong(), anyString()); + inOrder.verify(expiry, times(3)).getExpiryForUpdate(anyLong(), any(), anyString()); + + verify(chain).replaceAtHead(any()); + } + + @Test + @SuppressWarnings("unchecked") + public void testGetExpiryForCreationIsNotInvokedForReplacedChains() { + TimeSource timeSource = new TestTimeSource(); + ExpiryPolicy expiry = mock(ExpiryPolicy.class); + ChainResolver chainResolver = createChainResolver(expiry); + + when(expiry.getExpiryForCreation(anyLong(), anyString())).thenReturn(ExpiryPolicy.INFINITE); + + + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new PutOperation<>(1L, "Replaced", -10L), + new PutOperation<>(1L, "SecondAfterReplace", timeSource.getTimeMillis()), + new PutOperation<>(1L, "ThirdAfterReplace", timeSource.getTimeMillis()), + new PutOperation<>(1L, "FourthAfterReplace", timeSource.getTimeMillis()) + ); + + Store.ValueHolder valueHolder = chainResolver.resolve(chain, 1L, timeSource.getTimeMillis()); + verify(expiry, times(0)).getExpiryForCreation(anyLong(), anyString()); + verify(expiry, times(3)).getExpiryForUpdate(anyLong(), any(), anyString()); + + verify(chain).replaceAtHead(any()); + } + + @Test + @SuppressWarnings("unchecked") + public void testGetExpiryForCreationIsInvokedAfterRemoveOperations() { + TimeSource timeSource = new TestTimeSource(); + ExpiryPolicy expiry = mock(ExpiryPolicy.class); + ChainResolver chainResolver = createChainResolver(expiry); + + when(expiry.getExpiryForCreation(anyLong(), anyString())).thenReturn(ExpiryPolicy.INFINITE); + + + ServerStoreProxy.ChainEntry chainA = getEntryFromOperations( + new PutOperation<>(1L, "Replaced", 10L), + new PutOperation<>(1L, "SecondAfterReplace", 3L), + new RemoveOperation<>(1L, 4L), + new PutOperation<>(1L, "FourthAfterReplace", 5L) + ); + + Store.ValueHolder valueHolder = chainResolver.resolve(chainA, 1L, timeSource.getTimeMillis()); + + InOrder inOrder = inOrder(expiry); + + verify(expiry, times(0)).getExpiryForAccess(anyLong(), any()); + inOrder.verify(expiry, times(1)).getExpiryForCreation(anyLong(), anyString()); + inOrder.verify(expiry, times(1)).getExpiryForUpdate(anyLong(), any(), anyString()); + inOrder.verify(expiry, times(1)).getExpiryForCreation(anyLong(), anyString()); + + verify(chainA).replaceAtHead(any()); + + reset(expiry); + + when(expiry.getExpiryForCreation(anyLong(), anyString())).thenReturn(ExpiryPolicy.INFINITE); + + + ServerStoreProxy.ChainEntry chainB = getEntryFromOperations( + new PutOperation<>(1L, "One", timeSource.getTimeMillis()), + new PutOperation<>(1L, "Second", timeSource.getTimeMillis()), + new RemoveOperation<>(1L, timeSource.getTimeMillis()), + new PutOperation<>(1L, "Four", timeSource.getTimeMillis()) + ); + + chainResolver.resolve(chainB, 1L, timeSource.getTimeMillis()); + + inOrder = inOrder(expiry); + + verify(expiry, times(0)).getExpiryForAccess(anyLong(), any()); + inOrder.verify(expiry, times(1)).getExpiryForCreation(anyLong(), anyString()); + inOrder.verify(expiry, times(1)).getExpiryForUpdate(anyLong(), any(), anyString()); + inOrder.verify(expiry, times(1)).getExpiryForCreation(anyLong(), anyString()); + + verify(chainB).replaceAtHead(any()); + } + + @Test + @SuppressWarnings("unchecked") + public void testNullGetExpiryForCreation() { + TimeSource timeSource = new TestTimeSource(); + ExpiryPolicy expiry = mock(ExpiryPolicy.class); + ChainResolver chainResolver = createChainResolver(expiry); + + when(expiry.getExpiryForCreation(anyLong(), anyString())).thenReturn(null); + + ServerStoreProxy.ChainEntry chain = getEntryFromOperations(new PutOperation<>(1L, "Replaced", 10L)); + + Store.ValueHolder valueHolder = chainResolver.resolve(chain, 1L, timeSource.getTimeMillis()); + + assertThat(valueHolder, nullValue()); + verify(chain, never()).replaceAtHead(any()); + } + + @Test + @SuppressWarnings("unchecked") + public void testNullGetExpiryForUpdate() { + TimeSource timeSource = new TestTimeSource(); + ExpiryPolicy expiry = mock(ExpiryPolicy.class); + ChainResolver chainResolver = createChainResolver(expiry); + + when(expiry.getExpiryForUpdate(anyLong(), any(), anyString())).thenReturn(null); + + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new PutOperation<>(1L, "Replaced", -10L), + new PutOperation<>(1L, "New", timeSource.getTimeMillis()) + ); + + Store.ValueHolder resolvedChain = chainResolver.resolve(chain, 1L, timeSource.getTimeMillis()); + + assertThat(resolvedChain.get(), is("New")); + + verify(chain).replaceAtHead(argThat(contains(operation(new PutOperation<>(1L, "New", -10L))))); + } + + @Test + @SuppressWarnings("unchecked") + public void testGetExpiryForUpdateUpdatesExpirationTimeStamp() { + TimeSource timeSource = new TestTimeSource(); + ExpiryPolicy expiry = mock(ExpiryPolicy.class); + ChainResolver chainResolver = createChainResolver(expiry); + + when(expiry.getExpiryForUpdate(anyLong(), any(), anyString())).thenReturn(ofMillis(2L)); + + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new PutOperation<>(1L, "Replaced", -10L), + new PutOperation<>(1L, "New", timeSource.getTimeMillis()) + ); + + Store.ValueHolder valueHolder = chainResolver.resolve(chain, 1L, timeSource.getTimeMillis()); + + assertThat(valueHolder.get(), is("New")); + verify(chain).replaceAtHead(argThat(contains(operation(new PutOperation<>(1L, "New", -2L))))); + } + + @Test + @SuppressWarnings("unchecked") + public void testExpiryThrowsException() { + TimeSource timeSource = new TestTimeSource(); + ExpiryPolicy expiry = mock(ExpiryPolicy.class); + ChainResolver chainResolver = createChainResolver(expiry); + + when(expiry.getExpiryForUpdate(anyLong(), any(), anyString())).thenThrow(new RuntimeException("Test Update Expiry")); + when(expiry.getExpiryForCreation(anyLong(), anyString())).thenThrow(new RuntimeException("Test Create Expiry")); + + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new PutOperation<>(1L, "One", -10L), + new PutOperation<>(1L, "Two", timeSource.getTimeMillis()) + ); + + Store.ValueHolder valueHolder = chainResolver.resolve(chain, 1L, timeSource.getTimeMillis()); + + assertThat(valueHolder, nullValue()); + + chain = getEntryFromOperations( + new PutOperation<>(1L, "One", timeSource.getTimeMillis()), + new PutOperation<>(1L, "Two", timeSource.getTimeMillis()) + ); + + valueHolder = chainResolver.resolve(chain, 1L, timeSource.getTimeMillis()); + + assertThat(valueHolder, nullValue()); + + verify(chain).replaceAtHead(any()); + } + + @Test + public void testResolveExpiresUsingOperationTime() { + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new PutOperation(1L, "Albin", 0), + new PutIfAbsentOperation(1L, "Chris", 900) + ); + + ChainResolver resolver = createChainResolver(timeToLiveExpiration(ofMillis(1000))); + + Store.ValueHolder result = resolver.resolve(chain, 1L, 1500); + assertThat(result, nullValue()); + } + + @Test + public void testResolveAllExpiresUsingOperationTime() { + ServerStoreProxy.ChainEntry chain = getEntryFromOperations( + new PutOperation<>(1L, "Albin", 0), + new PutIfAbsentOperation<>(1L, "Chris", 900) + ); + + ChainResolver resolver = createChainResolver(timeToLiveExpiration(ofMillis(1000))); + + Map> result = resolver.resolveAll(chain, 1500); + + assertThat(result, is(emptyMap())); + } + + @Test + public void testExpiredResolvedValueAddsTimestamp() throws TimeoutException { + ServerStoreProxy.ChainEntry chain = getEntryFromOperations(new PutOperation<>(1L, "Albin", 0L)); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.timeToLiveExpiration(ofMillis(1000))); + + assertThat(resolver.resolve(chain, 1L, 1001L), nullValue()); + verify(chain).append(argThat(binaryOperation(new TimestampOperation<>(1L, 1001L)))); + verify(chain, never()).replaceAtHead(any()); + + } + + @Test + public void testExpiredTimestampClearsChain() { + PutOperation expected = new PutOperation<>(1L, "Albin", 0L); + ServerStoreProxy.ChainEntry chain = getEntryFromOperations(expected, + new TimestampOperation<>(1L, 1000L) + ); + + ChainResolver resolver = createChainResolver(ExpiryPolicyBuilder.timeToLiveExpiration(ofMillis(1000))); + + assertThat(resolver.resolve(chain, 1L, 999L), nullValue()); + verify(chain).replaceAtHead(argThat(emptyIterable())); + } +} diff --git a/clustered/client/src/test/java/org/ehcache/clustered/common/internal/store/operations/LazyValueHolderTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/common/internal/store/operations/LazyValueHolderTest.java similarity index 98% rename from clustered/client/src/test/java/org/ehcache/clustered/common/internal/store/operations/LazyValueHolderTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/common/internal/store/operations/LazyValueHolderTest.java index 00ae6f6122..d9d0a9f163 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/common/internal/store/operations/LazyValueHolderTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/common/internal/store/operations/LazyValueHolderTest.java @@ -25,8 +25,8 @@ import java.nio.ByteBuffer; import java.util.Date; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.sameInstance; -import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; diff --git a/clustered/client/src/test/java/org/ehcache/clustered/common/internal/store/operations/PutIfAbsentOperationTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/common/internal/store/operations/PutIfAbsentOperationTest.java similarity index 100% rename from clustered/client/src/test/java/org/ehcache/clustered/common/internal/store/operations/PutIfAbsentOperationTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/common/internal/store/operations/PutIfAbsentOperationTest.java diff --git a/clustered/client/src/test/java/org/ehcache/clustered/common/internal/store/operations/PutOperationTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/common/internal/store/operations/PutOperationTest.java similarity index 100% rename from clustered/client/src/test/java/org/ehcache/clustered/common/internal/store/operations/PutOperationTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/common/internal/store/operations/PutOperationTest.java diff --git a/clustered/client/src/test/java/org/ehcache/clustered/common/internal/store/operations/PutWithWriterOperationTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/common/internal/store/operations/PutWithWriterOperationTest.java similarity index 100% rename from clustered/client/src/test/java/org/ehcache/clustered/common/internal/store/operations/PutWithWriterOperationTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/common/internal/store/operations/PutWithWriterOperationTest.java diff --git a/clustered/client/src/test/java/org/ehcache/clustered/common/internal/store/operations/RemoveOperationTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/common/internal/store/operations/RemoveOperationTest.java similarity index 100% rename from clustered/client/src/test/java/org/ehcache/clustered/common/internal/store/operations/RemoveOperationTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/common/internal/store/operations/RemoveOperationTest.java diff --git a/clustered/client/src/test/java/org/ehcache/clustered/common/internal/store/operations/ReplaceOperationTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/common/internal/store/operations/ReplaceOperationTest.java similarity index 100% rename from clustered/client/src/test/java/org/ehcache/clustered/common/internal/store/operations/ReplaceOperationTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/common/internal/store/operations/ReplaceOperationTest.java diff --git a/clustered/ehcache-client/src/test/java/org/ehcache/clustered/common/internal/store/operations/TimestampOperationTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/common/internal/store/operations/TimestampOperationTest.java new file mode 100644 index 0000000000..80a990ade7 --- /dev/null +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/common/internal/store/operations/TimestampOperationTest.java @@ -0,0 +1,96 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.clustered.common.internal.store.operations; + +import org.ehcache.clustered.client.TestTimeSource; +import org.ehcache.impl.serialization.LongSerializer; +import org.ehcache.impl.serialization.StringSerializer; +import org.ehcache.spi.serialization.Serializer; +import org.junit.Test; + +import java.nio.ByteBuffer; + +import static org.ehcache.clustered.common.internal.store.operations.Operation.BYTE_SIZE_BYTES; +import static org.ehcache.clustered.common.internal.store.operations.Operation.LONG_SIZE_BYTES; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsNull.nullValue; +import static org.hamcrest.core.IsSame.sameInstance; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +public class TimestampOperationTest { + + private static final Serializer keySerializer = new LongSerializer(); + private static final Serializer valueSerializer = new StringSerializer(); + + private static final TestTimeSource TIME_SOURCE = new TestTimeSource(); + + @Test + public void testEncode() throws Exception { + Long key = 12L; + TimestampOperation operation = new TimestampOperation<>(key, TIME_SOURCE.getTimeMillis()); + ByteBuffer byteBuffer = operation.encode(keySerializer, valueSerializer); + + ByteBuffer expected = ByteBuffer.allocate(BYTE_SIZE_BYTES + 2 * LONG_SIZE_BYTES); + expected.put(OperationCode.TIMESTAMP.getValue()); + expected.putLong(TIME_SOURCE.getTimeMillis()); + expected.putLong(key); + expected.flip(); + assertArrayEquals(expected.array(), byteBuffer.array()); + } + + @Test + public void testDecode() throws Exception { + Long key = 12L; + ByteBuffer blob = ByteBuffer.allocate(BYTE_SIZE_BYTES + 2 * LONG_SIZE_BYTES); + blob.put(OperationCode.TIMESTAMP.getValue()); + blob.putLong(TIME_SOURCE.getTimeMillis()); + blob.putLong(key); + blob.flip(); + + TimestampOperation operation = new TimestampOperation<>(blob, keySerializer); + assertEquals(key, operation.getKey()); + } + + @Test + public void testEncodeDecodeInvariant() throws Exception { + Long key = 12L; + TimestampOperation operation = new TimestampOperation<>(key, System.currentTimeMillis()); + + TimestampOperation decodedOperation = + new TimestampOperation<>(operation.encode(keySerializer, valueSerializer), keySerializer); + assertEquals(key, decodedOperation.getKey()); + } + + @Test(expected = IllegalArgumentException.class) + public void testDecodeThrowsOnInvalidType() throws Exception { + ByteBuffer buffer = ByteBuffer.wrap(new byte[] {10}); + new TimestampOperation(buffer, keySerializer); + } + + @Test + public void testApply() throws Exception { + TimestampOperation operation = new TimestampOperation<>(1L, System.currentTimeMillis()); + + Result result = operation.apply(null); + assertThat(result, nullValue()); + + PutOperation anotherOperation = new PutOperation<>(1L, "another one", System.currentTimeMillis()); + result = operation.apply(anotherOperation); + assertThat(result, sameInstance(anotherOperation)); + } +} diff --git a/clustered/ehcache-client/src/test/java/org/ehcache/clustered/loaderWriter/BasicClusteredLoaderWriterTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/loaderWriter/BasicClusteredLoaderWriterTest.java new file mode 100644 index 0000000000..83baff2609 --- /dev/null +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/loaderWriter/BasicClusteredLoaderWriterTest.java @@ -0,0 +1,285 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.clustered.loaderWriter; + +import org.ehcache.Cache; +import org.ehcache.CacheManager; +import org.ehcache.CachePersistenceException; +import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder; +import org.ehcache.clustered.client.internal.UnitTestConnectionService; +import org.ehcache.clustered.client.internal.service.ClusterTierValidationException; +import org.ehcache.config.CacheConfiguration; +import org.ehcache.config.builders.CacheManagerBuilder; +import org.ehcache.config.builders.ResourcePoolsBuilder; +import org.ehcache.config.units.EntryUnit; +import org.ehcache.config.units.MemoryUnit; +import org.ehcache.core.internal.resilience.ThrowingResilienceStrategy; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.net.URI; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ThreadLocalRandom; + +import static org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder.cluster; +import static org.ehcache.config.builders.CacheConfigurationBuilder.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; + +public class BasicClusteredLoaderWriterTest { + + private static final URI CLUSTER_URI = URI.create("terracotta://example.com:9540/clustered-loader-writer"); + + @Before + public void definePassthroughServer() throws Exception { + UnitTestConnectionService.add(CLUSTER_URI, + new UnitTestConnectionService.PassthroughServerBuilder() + .resource("primary-server-resource", 4, MemoryUnit.MB) + .build()); + } + + @After + public void removePassthroughServer() throws Exception { + UnitTestConnectionService.remove(CLUSTER_URI); + } + + @Test @SuppressWarnings("try") + public void testAllClientsNeedToHaveLoaderWriterConfigured() { + TestCacheLoaderWriter loaderWriter = new TestCacheLoaderWriter(); + CacheConfiguration cacheConfiguration = getCacheConfiguration(loaderWriter); + + try (CacheManager cacheManager = CacheManagerBuilder + .newCacheManagerBuilder() + .with(cluster(CLUSTER_URI).autoCreate(c -> c)) + .withCache("cache-1", cacheConfiguration) + .build(true)) { + + CacheConfiguration withoutLoaderWriter = newCacheConfigurationBuilder(Long.class, String.class, + ResourcePoolsBuilder + .newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES).offheap(1, MemoryUnit.MB) + .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 2, MemoryUnit.MB))) + .withResilienceStrategy(new ThrowingResilienceStrategy<>()) + .build(); + + try (CacheManager anotherManager = CacheManagerBuilder + .newCacheManagerBuilder() + .with(cluster(CLUSTER_URI).autoCreate(c -> c)) + .withCache("cache-1", withoutLoaderWriter) + .build(true)) { + } catch (RuntimeException e) { + assertThat(e.getCause().getCause().getCause().getCause(), instanceOf(CachePersistenceException.class)); + assertThat(e.getCause().getCause().getCause().getCause().getCause(), instanceOf(ClusterTierValidationException.class)); + } + } + } + + @Test + public void testBasicClusteredCacheLoaderWriter() { + + TestCacheLoaderWriter loaderWriter = new TestCacheLoaderWriter(); + CacheConfiguration cacheConfiguration = getCacheConfiguration(loaderWriter); + + try (CacheManager cacheManager = CacheManagerBuilder + .newCacheManagerBuilder() + .with(cluster(CLUSTER_URI).autoCreate(c -> c)) + .withCache("cache-1", cacheConfiguration) + .build(true)) { + + Cache cache = cacheManager.getCache("cache-1", Long.class, String.class); + + cache.put(1L, "1"); + + assertThat(cache.get(1L), is("1")); + + assertThat(loaderWriter.storeMap.get(1L), is("1")); + } + } + + @Test + public void testLoaderWriterMultipleClients() { + + TestCacheLoaderWriter loaderWriter = new TestCacheLoaderWriter(); + + CacheConfiguration cacheConfiguration = getCacheConfiguration(loaderWriter); + + try (CacheManager cacheManager1 = CacheManagerBuilder + .newCacheManagerBuilder() + .with(cluster(CLUSTER_URI).autoCreate(c -> c)) + .withCache("cache-1", cacheConfiguration) + .build(true)) { + + try (CacheManager cacheManager2 = CacheManagerBuilder + .newCacheManagerBuilder() + .with(cluster(CLUSTER_URI).autoCreate(c -> c)) + .withCache("cache-1", cacheConfiguration) + .build(true)) { + + Cache client1 = cacheManager1.getCache("cache-1", Long.class, String.class); + Cache client2 = cacheManager2.getCache("cache-1", Long.class, String.class); + + client1.put(1L, "1"); + client2.put(1L, "2"); + + assertThat(client1.get(1L), is("2")); + assertThat(loaderWriter.storeMap.get(1L), is("2")); + + client1.remove(1L); + + assertThat(client2.get(1L), nullValue()); + assertThat(loaderWriter.storeMap.get(1L), nullValue()); + } + } + } + + @Test + public void testCASOpsMultipleClients() { + TestCacheLoaderWriter loaderWriter = new TestCacheLoaderWriter(); + + CacheConfiguration cacheConfiguration = getCacheConfiguration(loaderWriter); + + try (CacheManager cacheManager1 = CacheManagerBuilder + .newCacheManagerBuilder() + .with(cluster(CLUSTER_URI).autoCreate(c -> c)) + .withCache("cache-1", cacheConfiguration) + .build(true)) { + + try (CacheManager cacheManager2 = CacheManagerBuilder + .newCacheManagerBuilder() + .with(cluster(CLUSTER_URI).autoCreate(c -> c)) + .withCache("cache-1", cacheConfiguration) + .build(true)) { + + Cache client1 = cacheManager1.getCache("cache-1", Long.class, String.class); + Cache client2 = cacheManager2.getCache("cache-1", Long.class, String.class); + + assertThat(client1.putIfAbsent(1L, "1"), nullValue()); + assertThat(client2.putIfAbsent(1L, "2"), is("1")); + + assertThat(client1.get(1L), is("1")); + assertThat(loaderWriter.storeMap.get(1L), is("1")); + + assertThat(client1.replace(1L, "2"), is("1")); + assertThat(client2.replace(1L, "3"), is("2")); + + assertThat(client1.get(1L), is("3")); + assertThat(loaderWriter.storeMap.get(1L), is("3")); + + assertThat(client1.replace(1L, "2", "4"), is(false)); + assertThat(client2.replace(1L, "3", "4"), is(true)); + + assertThat(client1.get(1L), is("4")); + assertThat(loaderWriter.storeMap.get(1L), is("4")); + + assertThat(client1.remove(1L, "5"), is(false)); + assertThat(client2.remove(1L, "4"), is(true)); + } + } + } + + @Test + public void testBulkOps() { + TestCacheLoaderWriter loaderWriter = new TestCacheLoaderWriter(); + CacheConfiguration cacheConfiguration = getCacheConfiguration(loaderWriter); + + try (CacheManager cacheManager = CacheManagerBuilder + .newCacheManagerBuilder() + .with(cluster(CLUSTER_URI).autoCreate(c -> c)) + .withCache("cache-1", cacheConfiguration) + .build(true)) { + + Cache cache = cacheManager.getCache("cache-1", Long.class, String.class); + + Map mappings = new HashMap<>(); + + for (int i = 1; i <= 5; i++) { + mappings.put((long) i, "" + i); + } + + cache.putAll(mappings); + + assertThat(loaderWriter.storeMap.keySet(), containsInAnyOrder(mappings.keySet().toArray())); + + cache.clear(); + + Map loadedData = cache.getAll(mappings.keySet()); + + assertThat(mappings.keySet(), containsInAnyOrder(loadedData.keySet().toArray())); + + cache.removeAll(mappings.keySet()); + + assertThat(loaderWriter.storeMap.isEmpty(), is(true)); + } + } + + @Test + public void testCASOps() { + TestCacheLoaderWriter loaderWriter = new TestCacheLoaderWriter(); + + CacheConfiguration cacheConfiguration = getCacheConfiguration(loaderWriter); + + try (CacheManager cacheManager1 = CacheManagerBuilder + .newCacheManagerBuilder() + .with(cluster(CLUSTER_URI).autoCreate(c -> c)) + .withCache("cache-1", cacheConfiguration) + .build(true)) { + + try (CacheManager cacheManager2 = CacheManagerBuilder + .newCacheManagerBuilder() + .with(cluster(CLUSTER_URI).autoCreate(c -> c)) + .withCache("cache-1", cacheConfiguration) + .build(true)) { + + Cache client1 = cacheManager1.getCache("cache-1", Long.class, String.class); + Cache client2 = cacheManager2.getCache("cache-1", Long.class, String.class); + + assertThat(loaderWriter.storeMap.isEmpty(), is(true)); + + Set keys = new HashSet<>(); + ThreadLocalRandom.current().longs(10).forEach(x -> { + keys.add(x); + client1.put(x, Long.toString(x)); + }); + assertThat(loaderWriter.storeMap.size(), is(10)); + + + keys.forEach(x -> assertThat(client2.putIfAbsent(x, "Again" + x), is(Long.toString(x)))); + + keys.stream().limit(5).forEach(x -> + assertThat(client2.replace(x, "Replaced" + x), is(Long.toString(x)))); + + keys.forEach(x -> client1.remove(x, Long.toString(x))); + + assertThat(loaderWriter.storeMap.size(), is(5)); + } + } + } + + private CacheConfiguration getCacheConfiguration(TestCacheLoaderWriter loaderWriter) { + return newCacheConfigurationBuilder(Long.class, String.class, + ResourcePoolsBuilder + .newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES).offheap(1, MemoryUnit.MB) + .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 2, MemoryUnit.MB))) + .withLoaderWriter(loaderWriter) + .withResilienceStrategy(new ThrowingResilienceStrategy<>()) + .build(); + } + +} diff --git a/clustered/client/src/test/java/org/ehcache/clustered/loaderWriter/TestCacheLoaderWriter.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/loaderWriter/TestCacheLoaderWriter.java similarity index 94% rename from clustered/client/src/test/java/org/ehcache/clustered/loaderWriter/TestCacheLoaderWriter.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/loaderWriter/TestCacheLoaderWriter.java index 4dd229b209..36d1f10d7a 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/loaderWriter/TestCacheLoaderWriter.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/loaderWriter/TestCacheLoaderWriter.java @@ -16,10 +16,10 @@ package org.ehcache.clustered.loaderWriter; -import org.ehcache.impl.internal.concurrent.ConcurrentHashMap; import org.ehcache.spi.loaderwriter.CacheLoaderWriter; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; public class TestCacheLoaderWriter implements CacheLoaderWriter { diff --git a/clustered/client/src/test/java/org/ehcache/clustered/loaderWriter/writebehind/BasicClusteredWriteBehindPassthroughTest.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/loaderWriter/writebehind/BasicClusteredWriteBehindPassthroughTest.java similarity index 95% rename from clustered/client/src/test/java/org/ehcache/clustered/loaderWriter/writebehind/BasicClusteredWriteBehindPassthroughTest.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/loaderWriter/writebehind/BasicClusteredWriteBehindPassthroughTest.java index 214fde795e..9175f25a83 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/loaderWriter/writebehind/BasicClusteredWriteBehindPassthroughTest.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/loaderWriter/writebehind/BasicClusteredWriteBehindPassthroughTest.java @@ -41,9 +41,9 @@ import static org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder.cluster; import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; -import static org.junit.Assert.assertThat; public class BasicClusteredWriteBehindPassthroughTest { @@ -78,11 +78,10 @@ public void testBasicClusteredWriteBehind() { try (PersistentCacheManager cacheManager = createCacheManager()) { Cache cache = cacheManager.getCache(CACHE_NAME, Long.class, String.class); - for (int i = 0; i < 10; i++) { - put(cache, String.valueOf(i)); - } + put(cache, String.valueOf(0)); + put(cache, String.valueOf(1)); - assertValue(cache, String.valueOf(9)); + assertValue(cache, String.valueOf(1)); verifyRecords(cache); cache.clear(); @@ -249,13 +248,13 @@ private PersistentCacheManager createCacheManager() { .offheap(1, MemoryUnit.MB) .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 2, MemoryUnit.MB))) .withLoaderWriter(loaderWriter) - .add(WriteBehindConfigurationBuilder.newUnBatchedWriteBehindConfiguration()) - .add(new ClusteredStoreConfiguration(Consistency.STRONG)) + .withService(WriteBehindConfigurationBuilder.newUnBatchedWriteBehindConfiguration()) + .withService(new ClusteredStoreConfiguration(Consistency.STRONG)) .build(); return CacheManagerBuilder .newCacheManagerBuilder() - .with(cluster(CLUSTER_URI).autoCreate()) + .with(cluster(CLUSTER_URI).autoCreate(c -> c)) .withCache(CACHE_NAME, cacheConfiguration) .build(true); } diff --git a/clustered/client/src/test/java/org/ehcache/clustered/loaderWriter/writebehind/RecordingLoaderWriter.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/loaderWriter/writebehind/RecordingLoaderWriter.java similarity index 100% rename from clustered/client/src/test/java/org/ehcache/clustered/loaderWriter/writebehind/RecordingLoaderWriter.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/loaderWriter/writebehind/RecordingLoaderWriter.java diff --git a/clustered/client/src/test/java/org/ehcache/clustered/server/ObservableEhcacheServerEntityService.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/server/ObservableEhcacheServerEntityService.java similarity index 98% rename from clustered/client/src/test/java/org/ehcache/clustered/server/ObservableEhcacheServerEntityService.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/server/ObservableEhcacheServerEntityService.java index 2dba617610..ea6957ebcf 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/server/ObservableEhcacheServerEntityService.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/server/ObservableEhcacheServerEntityService.java @@ -18,7 +18,6 @@ import org.ehcache.clustered.common.internal.messages.EhcacheEntityMessage; import org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse; -import org.ehcache.impl.internal.concurrent.ConcurrentHashMap; import org.terracotta.entity.ActiveInvokeContext; import org.terracotta.entity.ClientDescriptor; import org.terracotta.entity.ClientSourceId; @@ -33,6 +32,7 @@ import java.util.Collections; import java.util.List; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; /** * Provides an alternative to {@link ClusterTierManagerServerEntityService} for unit tests to enable observing diff --git a/clustered/client/src/test/java/org/ehcache/clustered/server/package-info.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/server/package-info.java similarity index 100% rename from clustered/client/src/test/java/org/ehcache/clustered/server/package-info.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/server/package-info.java diff --git a/clustered/client/src/test/java/org/ehcache/clustered/server/store/ObservableClusterTierServerEntityService.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/server/store/ObservableClusterTierServerEntityService.java similarity index 100% rename from clustered/client/src/test/java/org/ehcache/clustered/server/store/ObservableClusterTierServerEntityService.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/server/store/ObservableClusterTierServerEntityService.java diff --git a/clustered/client/src/test/java/org/ehcache/clustered/util/StatisticsTestUtils.java b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/util/StatisticsTestUtils.java similarity index 94% rename from clustered/client/src/test/java/org/ehcache/clustered/util/StatisticsTestUtils.java rename to clustered/ehcache-client/src/test/java/org/ehcache/clustered/util/StatisticsTestUtils.java index e7cb278e0f..e17f9f66fb 100644 --- a/clustered/client/src/test/java/org/ehcache/clustered/util/StatisticsTestUtils.java +++ b/clustered/ehcache-client/src/test/java/org/ehcache/clustered/util/StatisticsTestUtils.java @@ -21,7 +21,6 @@ import org.hamcrest.Factory; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; -import org.junit.Assert; import org.terracotta.context.ContextManager; import org.terracotta.context.TreeNode; import org.terracotta.statistics.OperationStatistic; @@ -31,6 +30,8 @@ import java.util.EnumSet; import java.util.List; +import static org.hamcrest.MatcherAssert.assertThat; + /** * StatisticsTestUtils */ @@ -62,18 +63,18 @@ public static > void validateStats(final Store store, fi final OperationStatistic operationStatistic = getOperationStatistic(store, statsClass); for (final E statId : changed) { - Assert.assertThat(String.format("Value for %s.%s", statId.getDeclaringClass().getName(), statId.name()), + assertThat(String.format("Value for %s.%s", statId.getDeclaringClass().getName(), statId.name()), getStatistic(operationStatistic, statId), StatisticMatcher.equalTo(1L)); } for (final E statId : unchanged) { - Assert.assertThat(String.format("Value for %s.%s", statId.getDeclaringClass().getName(), statId.name()), + assertThat(String.format("Value for %s.%s", statId.getDeclaringClass().getName(), statId.name()), getStatistic(operationStatistic, statId), StatisticMatcher.equalTo(0L)); } } public static > void validateStat(final Store store, E outcome, long count) { OperationStatistic operationStatistic = getOperationStatistic(store, outcome.getDeclaringClass()); - Assert.assertThat(getStatistic(operationStatistic, outcome), StatisticMatcher.equalTo(count)); + assertThat(getStatistic(operationStatistic, outcome), StatisticMatcher.equalTo(count)); } /** diff --git a/clustered/client/src/test/resources/META-INF/services/org.terracotta.connection.ConnectionService b/clustered/ehcache-client/src/test/resources/META-INF/services/org.terracotta.connection.ConnectionService similarity index 100% rename from clustered/client/src/test/resources/META-INF/services/org.terracotta.connection.ConnectionService rename to clustered/ehcache-client/src/test/resources/META-INF/services/org.terracotta.connection.ConnectionService diff --git a/clustered/client/src/test/resources/configs/cluster-ha.xml b/clustered/ehcache-client/src/test/resources/configs/cluster-ha.xml similarity index 96% rename from clustered/client/src/test/resources/configs/cluster-ha.xml rename to clustered/ehcache-client/src/test/resources/configs/cluster-ha.xml index f3e8ba0fdb..1e5ef784b0 100644 --- a/clustered/client/src/test/resources/configs/cluster-ha.xml +++ b/clustered/ehcache-client/src/test/resources/configs/cluster-ha.xml @@ -23,7 +23,7 @@ 5 - + 8 diff --git a/clustered/client/src/test/resources/configs/cluster-invalid-uri.xml b/clustered/ehcache-client/src/test/resources/configs/cluster-invalid-uri.xml similarity index 100% rename from clustered/client/src/test/resources/configs/cluster-invalid-uri.xml rename to clustered/ehcache-client/src/test/resources/configs/cluster-invalid-uri.xml diff --git a/clustered/client/src/test/resources/configs/clustered-cache.xml b/clustered/ehcache-client/src/test/resources/configs/clustered-cache.xml similarity index 97% rename from clustered/client/src/test/resources/configs/clustered-cache.xml rename to clustered/ehcache-client/src/test/resources/configs/clustered-cache.xml index 4b065bd150..33e84b2194 100644 --- a/clustered/client/src/test/resources/configs/clustered-cache.xml +++ b/clustered/ehcache-client/src/test/resources/configs/clustered-cache.xml @@ -23,7 +23,7 @@ 5 5 150 - + 8388608 diff --git a/clustered/client/src/test/resources/configs/consistency.xml b/clustered/ehcache-client/src/test/resources/configs/consistency.xml similarity index 86% rename from clustered/client/src/test/resources/configs/consistency.xml rename to clustered/ehcache-client/src/test/resources/configs/consistency.xml index a32851c490..4f69a9afbb 100644 --- a/clustered/client/src/test/resources/configs/consistency.xml +++ b/clustered/ehcache-client/src/test/resources/configs/consistency.xml @@ -16,12 +16,8 @@ --> + xmlns:tc='http://www.ehcache.org/v3/clustered'> diff --git a/clustered/client/src/test/resources/configs/docs/ehcache-clustered.xml b/clustered/ehcache-client/src/test/resources/configs/docs/ehcache-clustered.xml similarity index 82% rename from clustered/client/src/test/resources/configs/docs/ehcache-clustered.xml rename to clustered/ehcache-client/src/test/resources/configs/docs/ehcache-clustered.xml index fc34ea80f5..84f263a0a7 100644 --- a/clustered/client/src/test/resources/configs/docs/ehcache-clustered.xml +++ b/clustered/ehcache-client/src/test/resources/configs/docs/ehcache-clustered.xml @@ -15,12 +15,8 @@ --> + xmlns:tc='http://www.ehcache.org/v3/clustered'> diff --git a/clustered/client/src/test/resources/configs/offheap-resource.xml b/clustered/ehcache-client/src/test/resources/configs/offheap-resource.xml similarity index 94% rename from clustered/client/src/test/resources/configs/offheap-resource.xml rename to clustered/ehcache-client/src/test/resources/configs/offheap-resource.xml index 5018f2226c..b805e5dca2 100644 --- a/clustered/client/src/test/resources/configs/offheap-resource.xml +++ b/clustered/ehcache-client/src/test/resources/configs/offheap-resource.xml @@ -17,7 +17,6 @@ --> 64 diff --git a/clustered/client/src/test/resources/configs/simple-cluster.xml b/clustered/ehcache-client/src/test/resources/configs/simple-cluster.xml similarity index 97% rename from clustered/client/src/test/resources/configs/simple-cluster.xml rename to clustered/ehcache-client/src/test/resources/configs/simple-cluster.xml index 429e80b9fe..e8016a8f54 100644 --- a/clustered/client/src/test/resources/configs/simple-cluster.xml +++ b/clustered/ehcache-client/src/test/resources/configs/simple-cluster.xml @@ -23,7 +23,7 @@ 5 - + 8 diff --git a/clustered/client/src/test/resources/configs/unknown-cluster-cache-invalid-attribute.xml b/clustered/ehcache-client/src/test/resources/configs/unknown-cluster-cache-invalid-attribute.xml similarity index 90% rename from clustered/client/src/test/resources/configs/unknown-cluster-cache-invalid-attribute.xml rename to clustered/ehcache-client/src/test/resources/configs/unknown-cluster-cache-invalid-attribute.xml index 89295bc1bb..5b39fa2d8a 100644 --- a/clustered/client/src/test/resources/configs/unknown-cluster-cache-invalid-attribute.xml +++ b/clustered/ehcache-client/src/test/resources/configs/unknown-cluster-cache-invalid-attribute.xml @@ -16,14 +16,13 @@ --> - + diff --git a/clustered/client/src/test/resources/configs/unknown-cluster-cache-invalid-element.xml b/clustered/ehcache-client/src/test/resources/configs/unknown-cluster-cache-invalid-element.xml similarity index 90% rename from clustered/client/src/test/resources/configs/unknown-cluster-cache-invalid-element.xml rename to clustered/ehcache-client/src/test/resources/configs/unknown-cluster-cache-invalid-element.xml index ed0af06f8b..642b8a3d1f 100644 --- a/clustered/client/src/test/resources/configs/unknown-cluster-cache-invalid-element.xml +++ b/clustered/ehcache-client/src/test/resources/configs/unknown-cluster-cache-invalid-element.xml @@ -16,14 +16,13 @@ --> - + diff --git a/clustered/client/src/test/resources/configs/unknown-cluster-cache.xml b/clustered/ehcache-client/src/test/resources/configs/unknown-cluster-cache.xml similarity index 90% rename from clustered/client/src/test/resources/configs/unknown-cluster-cache.xml rename to clustered/ehcache-client/src/test/resources/configs/unknown-cluster-cache.xml index 63f9c7a278..0ee2f72f42 100644 --- a/clustered/client/src/test/resources/configs/unknown-cluster-cache.xml +++ b/clustered/ehcache-client/src/test/resources/configs/unknown-cluster-cache.xml @@ -16,14 +16,13 @@ --> - + diff --git a/clustered/ehcache-clustered/build.gradle b/clustered/ehcache-clustered/build.gradle new file mode 100644 index 0000000000..92ddb8616e --- /dev/null +++ b/clustered/ehcache-clustered/build.gradle @@ -0,0 +1,180 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import aQute.bnd.osgi.Constants + +/** + * NOTE: this directory had to be named clustered-dist instead of just dist + * because gradle creatively substitutes :dist for :clustered:dist or vice versa + * if groups are the same + * https://discuss.gradle.org/t/dependency-substitution-wrong-with-more-than-one-sub-project-with-same-name/7253/6 + */ + +plugins { + id 'org.ehcache.build.package' + id 'distribution' +} + +publishing.publications.withType(MavenPublication) { + pom { + name = 'Ehcache 3 Clustered Module' + description = 'Ehcache 3 Clustered: Defines the client jar and the kit containing the Terracotta server' + } +} + +ext { + docsFolder = "$buildDir/docs/asciidoc" +} + +configurations { + [apiElements, runtimeElements].each { + it.outgoing { + variants.removeIf { it.name == 'classes' || it.name == 'resources' } + capability "org.ehcache:ehcache-clustered:$version" + capability "org.ehcache.modules.clustered:ehcache-client:$version" + } + } + + contents { + exclude group:'org.ehcache.modules' + exclude group:'org.slf4j' + } +} + +dependencies { + contents project(':clustered:ehcache-client') + contents "org.terracotta.internal:client-runtime:$terracottaCoreVersion" + + implementation "org.slf4j:slf4j-api:$parent.slf4jVersion" + implementation project(':ehcache') +} + +def kitProvides = { Configuration c -> + c.exclude group:'org.slf4j', module:'slf4j-api' + c.exclude group:'org.terracotta', module:'entity-server-api' + c.exclude group:'org.terracotta', module:'entity-common-api' + c.exclude group:'org.terracotta', module:'packaging-support' + c.exclude group:'org.terracotta', module:'standard-cluster-services' + c.exclude group:'org.terracotta', module:'statistics' + c.exclude group:'org.terracotta', module:'runnel' + c.exclude group:'org.terracotta', module:'client-message-tracker' + c.exclude group:'org.terracotta.management', module:'monitoring-service-api' + c.exclude group:'org.terracotta.management', module:'management-model' + c.exclude group:'org.terracotta.management', module:'management-registry' + c.exclude group:'org.terracotta.management', module:'cluster-topology' + c.exclude group:'org.terracotta.management', module:'sequence-generator' +} + +configurations { + kit + serverApis(kitProvides) + serverLibs(kitProvides) +} + +dependencies { + serverApis project(':clustered:server:ehcache-service-api') + serverLibs project(':clustered:server:ehcache-entity') + serverLibs project(':clustered:server:ehcache-service') + + kit "org.terracotta:platform-kit:$terracottaPlatformVersion@tar.gz" +} + +task copyDocs(type: Sync) { + dependsOn asciidocZip + from zipTree(asciidocZip.archivePath) + into docsFolder +} + +javadoc { + exclude '**/core/**', '**/impl/**', '**/xml/**', '**/jsr107/**', '**/transactions/**', '**/management/**', '**/tck/**' +} + +tasks.named('jar') { + osgi { + instruction Constants.BUNDLE_SYMBOLICNAME, 'org.ehcache.clustered' + instruction Constants.EXPORT_PACKAGE, '!com.tc.*, !com.terracotta.*, !org.terracotta.*, !org.ehcache.*.internal.*, !sun.misc, org.ehcache.clustered.client.*, org.ehcache.clustered.common.*' + instruction Constants.IMPORT_PACKAGE, '!sun.misc.*, org.ehcache.xml.*;resolution:=optional, jdk.jfr.*;resolution:=optional, !com.fasterxml.jackson.*, !org.terracotta.json, javax.xml.bind*;version="[2.2,3)", *' + } +} + +distributions { + main { + distributionBaseName = archivesBaseName + contents { + filesMatching('**/*.jar') { + // We can safely exclude JAR duplicates as our dependency strategy is fail on conflict + duplicatesStrategy DuplicatesStrategy.EXCLUDE + } + //tc kit + into ('') { + from configurations.kit.elements.map { + files -> files.collect { tarTree(it) } + } + eachFile { f -> + // remove top level directory from the kit + f.path = f.path.replace("platform-kit-$terracottaPlatformVersion/", "") + } + exclude { f -> + // Exclude tc's README.txt - Issue 1273 + f.path.contains('README.txt') || f.path.contains('server/conf') + } + includeEmptyDirs = false + } + into ("server/plugins/api") { + from configurations.serverApis + } + into ("server/plugins/lib") { + from (configurations.serverLibs - configurations.serverApis) + } + into ('client/ehcache') { + from jar + from project(':ehcache').jar + exclude { f -> + !f.path.contains('ehcache') // do not add any transitives in this directory + } + } + into ('client/ehcache/documentation') { + from "$docsFolder/user" + } + into ('client/ehcache/javadoc') { + from tasks.named('javadocJar') + from project(':ehcache').javadocJar + } + into ('client/lib') { + from configurations.runtimeClasspath + } + into ('') { + from 'src/assemble' + } + } + } +} + +distTar { + archiveClassifier = 'kit' + compression = Compression.GZIP +} + +distZip { + archiveClassifier = 'kit' +} + +publishing.publications.withType(MavenPublication) { + artifact distZip + artifact distTar +} + +[distTar, distZip, installDist]*.dependsOn copyDocs, javadocJar, project(':ehcache').jar, project(':ehcache').javadocJar diff --git a/clustered/clustered-dist/src/assemble/README.txt b/clustered/ehcache-clustered/src/assemble/README.txt similarity index 100% rename from clustered/clustered-dist/src/assemble/README.txt rename to clustered/ehcache-clustered/src/assemble/README.txt diff --git a/clustered/clustered-dist/src/assemble/legal/APACHE_PUBLIC_LICENSE.txt b/clustered/ehcache-clustered/src/assemble/legal/APACHE_PUBLIC_LICENSE.txt similarity index 100% rename from clustered/clustered-dist/src/assemble/legal/APACHE_PUBLIC_LICENSE.txt rename to clustered/ehcache-clustered/src/assemble/legal/APACHE_PUBLIC_LICENSE.txt diff --git a/clustered/clustered-dist/src/assemble/legal/LICENSE b/clustered/ehcache-clustered/src/assemble/legal/LICENSE similarity index 100% rename from clustered/clustered-dist/src/assemble/legal/LICENSE rename to clustered/ehcache-clustered/src/assemble/legal/LICENSE diff --git a/clustered/ehcache-clustered/src/assemble/server/conf/cluster.cfg b/clustered/ehcache-clustered/src/assemble/server/conf/cluster.cfg new file mode 100644 index 0000000000..230113261a --- /dev/null +++ b/clustered/ehcache-clustered/src/assemble/server/conf/cluster.cfg @@ -0,0 +1,13 @@ +client-lease-duration=150s +client-reconnect-window=120s +cluster-name=default-cluster +failover-priority=availability +offheap-resources=main:512MB +stripe-names=default-stripe +default-stripe:node-names=default-node +default-node:bind-address=0.0.0.0 +default-node:group-bind-address=0.0.0.0 +default-node:group-port=9430 +default-node:hostname=localhost +default-node:log-dir=%H/terracotta/logs +default-node:port=9410 diff --git a/dist/build.gradle b/clustered/ehcache-common-api/build.gradle similarity index 66% rename from dist/build.gradle rename to clustered/ehcache-common-api/build.gradle index fec683687b..e748bfe3be 100644 --- a/dist/build.gradle +++ b/clustered/ehcache-common-api/build.gradle @@ -14,19 +14,17 @@ * limitations under the License. */ -group = 'org.ehcache' -archivesBaseName = 'ehcache' - -dependencies { - compileOnly project(':api') - compileOnly project(':core') - compileOnly project(':impl') - compileOnly project(':107') - compileOnly project(':xml') +plugins { + id 'org.ehcache.build.clustered-module' } -apply plugin: EhDistribute +publishing.publications.withType(MavenPublication) { + pom { + name = 'Ehcache 3 Common Clustering API module' + description = 'The Common Clustering API module of Ehcache 3' + } +} dependencies { - shadowCompile "org.slf4j:slf4j-api:$parent.slf4jVersion" + api "org.terracotta:entity-common-api:$terracottaApisVersion" } diff --git a/clustered/common/config/checkstyle-suppressions.xml b/clustered/ehcache-common-api/config/checkstyle-suppressions.xml similarity index 100% rename from clustered/common/config/checkstyle-suppressions.xml rename to clustered/ehcache-common-api/config/checkstyle-suppressions.xml diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/Consistency.java b/clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/Consistency.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/Consistency.java rename to clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/Consistency.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/PoolAllocation.java b/clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/PoolAllocation.java similarity index 96% rename from clustered/common/src/main/java/org/ehcache/clustered/common/PoolAllocation.java rename to clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/PoolAllocation.java index 6917c5b099..c8250fd31a 100644 --- a/clustered/common/src/main/java/org/ehcache/clustered/common/PoolAllocation.java +++ b/clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/PoolAllocation.java @@ -17,6 +17,7 @@ package org.ehcache.clustered.common; import java.io.Serializable; +import java.util.Objects; /** * PoolAllocation @@ -86,8 +87,7 @@ public boolean isCompatible(PoolAllocation other) { final Dedicated dedicated = (Dedicated)other; - if (size != dedicated.size) return false; - return resourceName != null ? resourceName.equals(dedicated.resourceName) : dedicated.resourceName == null; + return Objects.equals(resourceName, dedicated.resourceName); } @Override diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/ServerSideConfiguration.java b/clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/ServerSideConfiguration.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/ServerSideConfiguration.java rename to clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/ServerSideConfiguration.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/ClusterTierManagerConfiguration.java b/clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/ClusterTierManagerConfiguration.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/ClusterTierManagerConfiguration.java rename to clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/ClusterTierManagerConfiguration.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/ServerStoreConfiguration.java b/clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/ServerStoreConfiguration.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/ServerStoreConfiguration.java rename to clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/ServerStoreConfiguration.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/exceptions/ClusterException.java b/clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/exceptions/ClusterException.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/exceptions/ClusterException.java rename to clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/exceptions/ClusterException.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/exceptions/DestroyInProgressException.java b/clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/exceptions/DestroyInProgressException.java similarity index 86% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/exceptions/DestroyInProgressException.java rename to clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/exceptions/DestroyInProgressException.java index afe0e5281a..ae6c63c4f4 100644 --- a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/exceptions/DestroyInProgressException.java +++ b/clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/exceptions/DestroyInProgressException.java @@ -26,4 +26,9 @@ public class DestroyInProgressException extends LifecycleException { public DestroyInProgressException(String message) { super(message); } + + @Override + public DestroyInProgressException withClientStackTrace() { + return new DestroyInProgressException(this.getMessage()); + } } diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/exceptions/InvalidServerSideConfigurationException.java b/clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/exceptions/InvalidServerSideConfigurationException.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/exceptions/InvalidServerSideConfigurationException.java rename to clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/exceptions/InvalidServerSideConfigurationException.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/exceptions/InvalidStoreException.java b/clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/exceptions/InvalidStoreException.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/exceptions/InvalidStoreException.java rename to clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/exceptions/InvalidStoreException.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/exceptions/LifecycleException.java b/clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/exceptions/LifecycleException.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/exceptions/LifecycleException.java rename to clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/exceptions/LifecycleException.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/EhcacheEntityMessage.java b/clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/messages/EhcacheEntityMessage.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/EhcacheEntityMessage.java rename to clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/messages/EhcacheEntityMessage.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/EhcacheEntityResponse.java b/clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/messages/EhcacheEntityResponse.java similarity index 77% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/EhcacheEntityResponse.java rename to clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/messages/EhcacheEntityResponse.java index 99da5edee5..61dbfcb597 100644 --- a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/EhcacheEntityResponse.java +++ b/clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/messages/EhcacheEntityResponse.java @@ -21,7 +21,11 @@ import org.ehcache.clustered.common.internal.store.Chain; import org.terracotta.entity.EntityResponse; +import java.nio.ByteBuffer; +import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.UUID; public abstract class EhcacheEntityResponse implements EntityResponse { @@ -125,21 +129,55 @@ public final EhcacheResponseType getResponseType() { } } - public static ServerInvalidateHash serverInvalidateHash(long key) { - return new ServerInvalidateHash(key); + public static ServerAppend serverAppend(ByteBuffer appended, Chain beforeAppend) { + return new ServerAppend(appended, beforeAppend); } + public static class ServerAppend extends EhcacheEntityResponse { + private final ByteBuffer appended; + private final Chain beforeAppend; + + ServerAppend(ByteBuffer appended, Chain beforeAppend) { + this.appended = appended; + this.beforeAppend = beforeAppend; + } + + public ByteBuffer getAppended() { + return appended; + } + + public Chain getBeforeAppend() { + return beforeAppend; + } + + @Override + public EhcacheResponseType getResponseType() { + return EhcacheResponseType.SERVER_APPEND; + } + } + + public static ServerInvalidateHash serverInvalidateHash(long key, Chain evictedChain) { + return new ServerInvalidateHash(key, evictedChain); + } + + // this is fired when the server evicts a chain public static class ServerInvalidateHash extends EhcacheEntityResponse { private final long key; + private final Chain evictedChain; - private ServerInvalidateHash(long key) { + private ServerInvalidateHash(long key, Chain evictedChain) { this.key = key; + this.evictedChain = evictedChain; } public long getKey() { return key; } + public Chain getEvictedChain() { + return evictedChain; + } + @Override public final EhcacheResponseType getResponseType() { return EhcacheResponseType.SERVER_INVALIDATE_HASH; @@ -150,6 +188,7 @@ public static ClientInvalidateHash clientInvalidateHash(long key, int invalidati return new ClientInvalidateHash(key, invalidationId); } + // this is fired when a client modifies a chain (i.e.: on append) public static class ClientInvalidateHash extends EhcacheEntityResponse { private final long key; private final int invalidationId; @@ -303,4 +342,38 @@ public EhcacheResponseType getResponseType() { return EhcacheResponseType.LOCK_FAILURE; } } + + public static IteratorBatch iteratorBatchResponse(UUID id, List> chains, boolean last) { + return new IteratorBatch(id, chains, last); + } + + public static class IteratorBatch extends EhcacheEntityResponse { + + private final UUID id; + private final List> chains; + private final boolean last; + + private IteratorBatch(UUID id, List> chains, boolean last) { + this.id = id; + this.chains = chains; + this.last = last; + } + + @Override + public EhcacheResponseType getResponseType() { + return EhcacheResponseType.ITERATOR_BATCH; + } + + public boolean isLast() { + return last; + } + + public List> getChains() { + return chains; + } + + public UUID getIdentity() { + return id; + } + } } diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/EhcacheMessageType.java b/clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/messages/EhcacheMessageType.java similarity index 77% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/EhcacheMessageType.java rename to clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/messages/EhcacheMessageType.java index f05d542dc3..06963dd68b 100644 --- a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/EhcacheMessageType.java +++ b/clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/messages/EhcacheMessageType.java @@ -16,12 +16,9 @@ package org.ehcache.clustered.common.internal.messages; -import org.terracotta.runnel.EnumMapping; - import java.util.EnumSet; import static java.util.EnumSet.of; -import static org.terracotta.runnel.EnumMappingBuilder.newEnumMappingBuilder; /** * EhcacheMessageType @@ -44,6 +41,10 @@ public enum EhcacheMessageType { GET_STORE, LOCK, UNLOCK, + ITERATOR_OPEN, + ITERATOR_CLOSE, + ITERATOR_ADVANCE, + ENABLE_EVENT_LISTENER, // StateRepository operation messages GET_STATE_REPO, @@ -53,33 +54,8 @@ public enum EhcacheMessageType { // Passive replication messages CHAIN_REPLICATION_OP, CLEAR_INVALIDATION_COMPLETE, - INVALIDATION_COMPLETE; - - public static final String MESSAGE_TYPE_FIELD_NAME = "opCode"; - public static final int MESSAGE_TYPE_FIELD_INDEX = 10; - public static final EnumMapping EHCACHE_MESSAGE_TYPES_ENUM_MAPPING = newEnumMappingBuilder(EhcacheMessageType.class) - .mapping(VALIDATE, 1) - .mapping(VALIDATE_SERVER_STORE, 2) - .mapping(PREPARE_FOR_DESTROY, 3) - - .mapping(GET_AND_APPEND, 21) - .mapping(APPEND, 22) - .mapping(REPLACE, 23) - .mapping(CLIENT_INVALIDATION_ACK, 24) - .mapping(CLIENT_INVALIDATION_ALL_ACK, 25) - .mapping(CLEAR, 26) - .mapping(GET_STORE, 27) - .mapping(LOCK, 28) - .mapping(UNLOCK, 29) - - .mapping(GET_STATE_REPO, 41) - .mapping(PUT_IF_ABSENT, 42) - .mapping(ENTRY_SET, 43) - - .mapping(CHAIN_REPLICATION_OP, 61) - .mapping(CLEAR_INVALIDATION_COMPLETE, 63) - .mapping(INVALIDATION_COMPLETE, 64) - .build(); + INVALIDATION_COMPLETE, + MESSAGE_CATCHUP; public static final EnumSet LIFECYCLE_MESSAGES = of(VALIDATE, VALIDATE_SERVER_STORE, PREPARE_FOR_DESTROY); public static boolean isLifecycleMessage(EhcacheMessageType value) { @@ -87,7 +63,7 @@ public static boolean isLifecycleMessage(EhcacheMessageType value) { } public static final EnumSet STORE_OPERATION_MESSAGES = of(GET_AND_APPEND, APPEND, - REPLACE, CLIENT_INVALIDATION_ACK, CLIENT_INVALIDATION_ALL_ACK, CLEAR, GET_STORE, LOCK, UNLOCK); + REPLACE, CLIENT_INVALIDATION_ACK, CLIENT_INVALIDATION_ALL_ACK, CLEAR, GET_STORE, LOCK, UNLOCK, ITERATOR_OPEN, ITERATOR_CLOSE, ITERATOR_ADVANCE, ENABLE_EVENT_LISTENER); public static boolean isStoreOperationMessage(EhcacheMessageType value) { return STORE_OPERATION_MESSAGES.contains(value); } diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/EhcacheOperationMessage.java b/clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/messages/EhcacheOperationMessage.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/EhcacheOperationMessage.java rename to clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/messages/EhcacheOperationMessage.java diff --git a/api/src/main/java/org/ehcache/spi/service/ServiceConfiguration.java b/clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/messages/EhcacheResponseType.java similarity index 59% rename from api/src/main/java/org/ehcache/spi/service/ServiceConfiguration.java rename to clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/messages/EhcacheResponseType.java index b7766dc7cf..90b683b994 100644 --- a/api/src/main/java/org/ehcache/spi/service/ServiceConfiguration.java +++ b/clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/messages/EhcacheResponseType.java @@ -14,20 +14,26 @@ * limitations under the License. */ -package org.ehcache.spi.service; +package org.ehcache.clustered.common.internal.messages; /** - * A configuration type to be used when interacting with a {@link Service}. - * - * @param the service type this configuration works with - * + * EhcacheResponseType */ -public interface ServiceConfiguration { - - /** - * Indicates which service this configuration works with. - * - * @return the service type - */ - Class getServiceType(); +public enum EhcacheResponseType { + SUCCESS, + FAILURE, + GET_RESPONSE, + HASH_INVALIDATION_DONE, + CLIENT_INVALIDATE_HASH, + CLIENT_INVALIDATE_ALL, + SERVER_INVALIDATE_HASH, + MAP_VALUE, + ALL_INVALIDATION_DONE, + PREPARE_FOR_DESTROY, + RESOLVE_REQUEST, + LOCK_SUCCESS, + LOCK_FAILURE, + ITERATOR_BATCH, + SERVER_APPEND, + ; } diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/StateRepositoryOpMessage.java b/clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/messages/StateRepositoryOpMessage.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/StateRepositoryOpMessage.java rename to clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/messages/StateRepositoryOpMessage.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/Chain.java b/clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/store/Chain.java similarity index 87% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/Chain.java rename to clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/store/Chain.java index 2919628a38..836d7193cd 100644 --- a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/Chain.java +++ b/clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/store/Chain.java @@ -34,15 +34,6 @@ */ public interface Chain extends Iterable { - /** - * Returns the iterator to iterate the {@link Chain} of - * {@link Element}s in backwards direction i.e. starting - * from last one. - * - * @return an Iterator. - */ - Iterator reverseIterator(); - /** * Returns true if Chain is empty else false. * diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/Element.java b/clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/store/Element.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/Element.java rename to clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/store/Element.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/ServerStore.java b/clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/store/ServerStore.java similarity index 95% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/ServerStore.java rename to clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/store/ServerStore.java index e59bea92ac..ab62818b89 100644 --- a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/ServerStore.java +++ b/clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/store/ServerStore.java @@ -17,6 +17,8 @@ package org.ehcache.clustered.common.internal.store; import java.nio.ByteBuffer; +import java.util.Iterator; +import java.util.Map; import java.util.concurrent.TimeoutException; /** @@ -128,4 +130,11 @@ public interface ServerStore { * @throws TimeoutException if the get exceeds the timeout configured for write operations */ void clear() throws TimeoutException; + + /** + * Returns an iterator over the chains. + * + * @return an chain iterator. + */ + Iterator> iterator() throws TimeoutException; } diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/ValueWrapper.java b/clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/store/ValueWrapper.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/ValueWrapper.java rename to clustered/ehcache-common-api/src/main/java/org/ehcache/clustered/common/internal/store/ValueWrapper.java diff --git a/clustered/common/src/test/java/org/ehcache/clustered/common/ServerSideConfigurationTest.java b/clustered/ehcache-common-api/src/test/java/org/ehcache/clustered/common/ServerSideConfigurationTest.java similarity index 100% rename from clustered/common/src/test/java/org/ehcache/clustered/common/ServerSideConfigurationTest.java rename to clustered/ehcache-common-api/src/test/java/org/ehcache/clustered/common/ServerSideConfigurationTest.java diff --git a/clustered/ehcache-common/build.gradle b/clustered/ehcache-common/build.gradle new file mode 100644 index 0000000000..0a5f41937d --- /dev/null +++ b/clustered/ehcache-common/build.gradle @@ -0,0 +1,36 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id 'org.ehcache.build.clustered-module' +} + +publishing.publications.withType(MavenPublication) { + pom { + name = 'Ehcache 3 Common Clustering module' + description = 'The Common Clustering module of Ehcache 3' + } +} + +dependencies { + api project(':ehcache-api') + api project(':clustered:ehcache-common-api') + + implementation "org.terracotta:entity-common-api:$terracottaApisVersion" + implementation "org.terracotta:runnel:$terracottaPlatformVersion" + + testImplementation project(':clustered:test-utils') +} diff --git a/clustered/server/config/checkstyle-suppressions.xml b/clustered/ehcache-common/config/checkstyle-suppressions.xml similarity index 100% rename from clustered/server/config/checkstyle-suppressions.xml rename to clustered/ehcache-common/config/checkstyle-suppressions.xml diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/EhcacheEntityVersion.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/EhcacheEntityVersion.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/EhcacheEntityVersion.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/EhcacheEntityVersion.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/exceptions/IllegalMessageException.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/exceptions/IllegalMessageException.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/exceptions/IllegalMessageException.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/exceptions/IllegalMessageException.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/exceptions/InvalidOperationException.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/exceptions/InvalidOperationException.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/exceptions/InvalidOperationException.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/exceptions/InvalidOperationException.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/exceptions/InvalidServerStoreConfigurationException.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/exceptions/InvalidServerStoreConfigurationException.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/exceptions/InvalidServerStoreConfigurationException.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/exceptions/InvalidServerStoreConfigurationException.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/exceptions/UnknownClusterException.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/exceptions/UnknownClusterException.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/exceptions/UnknownClusterException.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/exceptions/UnknownClusterException.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/lock/LockMessaging.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/lock/LockMessaging.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/lock/LockMessaging.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/lock/LockMessaging.java diff --git a/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/BaseCodec.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/BaseCodec.java new file mode 100644 index 0000000000..b85360cb2e --- /dev/null +++ b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/BaseCodec.java @@ -0,0 +1,113 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.clustered.common.internal.messages; + +import org.terracotta.runnel.EnumMapping; + +import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.APPEND; +import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.CHAIN_REPLICATION_OP; +import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.CLEAR; +import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.CLEAR_INVALIDATION_COMPLETE; +import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.CLIENT_INVALIDATION_ACK; +import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.CLIENT_INVALIDATION_ALL_ACK; +import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.ENABLE_EVENT_LISTENER; +import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.ENTRY_SET; +import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.GET_AND_APPEND; +import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.GET_STATE_REPO; +import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.GET_STORE; +import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.INVALIDATION_COMPLETE; +import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.ITERATOR_ADVANCE; +import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.ITERATOR_CLOSE; +import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.ITERATOR_OPEN; +import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.LOCK; +import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.MESSAGE_CATCHUP; +import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.PUT_IF_ABSENT; +import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.REPLACE; +import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.UNLOCK; +import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.VALIDATE; +import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.VALIDATE_SERVER_STORE; +import static org.ehcache.clustered.common.internal.messages.EhcacheResponseType.ALL_INVALIDATION_DONE; +import static org.ehcache.clustered.common.internal.messages.EhcacheResponseType.CLIENT_INVALIDATE_ALL; +import static org.ehcache.clustered.common.internal.messages.EhcacheResponseType.CLIENT_INVALIDATE_HASH; +import static org.ehcache.clustered.common.internal.messages.EhcacheResponseType.FAILURE; +import static org.ehcache.clustered.common.internal.messages.EhcacheResponseType.GET_RESPONSE; +import static org.ehcache.clustered.common.internal.messages.EhcacheResponseType.HASH_INVALIDATION_DONE; +import static org.ehcache.clustered.common.internal.messages.EhcacheResponseType.ITERATOR_BATCH; +import static org.ehcache.clustered.common.internal.messages.EhcacheResponseType.LOCK_FAILURE; +import static org.ehcache.clustered.common.internal.messages.EhcacheResponseType.LOCK_SUCCESS; +import static org.ehcache.clustered.common.internal.messages.EhcacheResponseType.MAP_VALUE; +import static org.ehcache.clustered.common.internal.messages.EhcacheResponseType.RESOLVE_REQUEST; +import static org.ehcache.clustered.common.internal.messages.EhcacheResponseType.SERVER_APPEND; +import static org.ehcache.clustered.common.internal.messages.EhcacheResponseType.SERVER_INVALIDATE_HASH; +import static org.ehcache.clustered.common.internal.messages.EhcacheResponseType.SUCCESS; +import static org.terracotta.runnel.EnumMappingBuilder.newEnumMappingBuilder; + +public class BaseCodec { + + public static final String MESSAGE_TYPE_FIELD_NAME = "opCode"; + public static final int MESSAGE_TYPE_FIELD_INDEX = 10; + public static final EnumMapping EHCACHE_MESSAGE_TYPES_ENUM_MAPPING = newEnumMappingBuilder(EhcacheMessageType.class) + .mapping(VALIDATE, 1) + .mapping(VALIDATE_SERVER_STORE, 2) + .mapping(EhcacheMessageType.PREPARE_FOR_DESTROY, 3) + + .mapping(GET_AND_APPEND, 21) + .mapping(APPEND, 22) + .mapping(REPLACE, 23) + .mapping(CLIENT_INVALIDATION_ACK, 24) + .mapping(CLIENT_INVALIDATION_ALL_ACK, 25) + .mapping(CLEAR, 26) + .mapping(GET_STORE, 27) + .mapping(LOCK, 28) + .mapping(UNLOCK, 29) + .mapping(ITERATOR_OPEN, 30) + .mapping(ITERATOR_CLOSE, 31) + .mapping(ITERATOR_ADVANCE, 32) + .mapping(ENABLE_EVENT_LISTENER, 33) + + .mapping(GET_STATE_REPO, 41) + .mapping(PUT_IF_ABSENT, 42) + .mapping(ENTRY_SET, 43) + + .mapping(CHAIN_REPLICATION_OP, 61) + .mapping(CLEAR_INVALIDATION_COMPLETE, 63) + .mapping(INVALIDATION_COMPLETE, 64) + + .mapping(MESSAGE_CATCHUP, 71) + .build(); + + public static final String RESPONSE_TYPE_FIELD_NAME = "opCode"; + public static final int RESPONSE_TYPE_FIELD_INDEX = 10; + public static final EnumMapping EHCACHE_RESPONSE_TYPES_ENUM_MAPPING = newEnumMappingBuilder(EhcacheResponseType.class) + .mapping(SUCCESS, 80) + .mapping(FAILURE, 81) + .mapping(GET_RESPONSE, 82) + .mapping(HASH_INVALIDATION_DONE, 83) + .mapping(ALL_INVALIDATION_DONE, 84) + .mapping(CLIENT_INVALIDATE_HASH, 85) + .mapping(CLIENT_INVALIDATE_ALL, 86) + .mapping(SERVER_INVALIDATE_HASH, 87) + .mapping(MAP_VALUE, 88) + .mapping(EhcacheResponseType.PREPARE_FOR_DESTROY, 89) + .mapping(RESOLVE_REQUEST, 90) + .mapping(LOCK_SUCCESS, 91) + .mapping(LOCK_FAILURE, 92) + .mapping(ITERATOR_BATCH, 93) + .mapping(SERVER_APPEND, 94) + .build(); + +} diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/ChainCodec.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/ChainCodec.java similarity index 64% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/ChainCodec.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/ChainCodec.java index 77f6b62852..b66ba41de7 100644 --- a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/ChainCodec.java +++ b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/ChainCodec.java @@ -19,7 +19,6 @@ import org.ehcache.clustered.common.internal.store.Chain; import org.ehcache.clustered.common.internal.store.Element; import org.ehcache.clustered.common.internal.store.SequencedElement; -import org.ehcache.clustered.common.internal.store.Util; import org.terracotta.runnel.Struct; import org.terracotta.runnel.StructBuilder; import org.terracotta.runnel.decoding.StructArrayDecoder; @@ -28,8 +27,12 @@ import org.terracotta.runnel.encoding.StructEncoder; import java.nio.ByteBuffer; +import java.util.AbstractMap; import java.util.ArrayList; import java.util.List; +import java.util.Map; + +import static org.ehcache.clustered.common.internal.util.ChainBuilder.chainFromList; public final class ChainCodec { @@ -46,16 +49,21 @@ private ChainCodec() { .structs("elements", 10, ELEMENT_STRUCT) .build(); - public static byte[] encode(Chain chain) { + public static final Struct CHAIN_ENTRY_STRUCT = StructBuilder.newStructBuilder() + .int64("key", 5) + .structs("elements", 10, ELEMENT_STRUCT) + .build(); + + public static byte[] encodeChain(Chain chain) { StructEncoder encoder = CHAIN_STRUCT.encoder(); - encode(encoder, chain); + encodeChain(encoder, chain); ByteBuffer byteBuffer = encoder.encode(); return byteBuffer.array(); } - public static void encode(StructEncoder encoder, Chain chain) { + public static void encodeChain(StructEncoder encoder, Chain chain) { StructArrayEncoder> elementsEncoder = encoder.structs("elements"); for (Element element : chain) { StructEncoder elementEncoder = elementsEncoder.add(); @@ -68,12 +76,17 @@ public static void encode(StructEncoder encoder, Chain chain) { elementsEncoder.end(); } - public static Chain decode(byte[] payload) { + public static void encodeChainEntry(StructEncoder encoder, Map.Entry chain) { + encoder.int64("key", chain.getKey()); + encodeChain(encoder, chain.getValue()); + } + + public static Chain decodeChain(byte[] payload) { StructDecoder decoder = CHAIN_STRUCT.decoder(ByteBuffer.wrap(payload)); - return decode(decoder); + return decodeChain(decoder); } - public static Chain decode(StructDecoder decoder) { + public static Chain decodeChain(StructDecoder decoder) { StructArrayDecoder> elementsDecoder = decoder.structs("elements"); final List elements = new ArrayList<>(); @@ -84,14 +97,35 @@ public static Chain decode(StructDecoder decoder) { elementDecoder.end(); if (sequence == null) { - elements.add(Util.getElement(byteBuffer)); + elements.add(byteBuffer::asReadOnlyBuffer); } else { - elements.add(Util.getElement(sequence, byteBuffer)); + elements.add(new SequencedElement() { + @Override + public long getSequenceNumber() { + return sequence; + } + + @Override + public ByteBuffer getPayload() { + return byteBuffer.asReadOnlyBuffer(); + } + + @Override + public String toString() { + return "SequencedElement{sequence=" + sequence + " size=" + byteBuffer.capacity() + "}"; + } + }); } } elementsDecoder.end(); - return Util.getChain(elements); + return chainFromList(elements); + } + + public static Map.Entry decodeChainEntry(StructDecoder decoder) { + Long key = decoder.int64("key"); + Chain elements = decodeChain(decoder); + return new AbstractMap.SimpleImmutableEntry<>(key, elements); } } diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/ClusterTierReconnectMessage.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/ClusterTierReconnectMessage.java similarity index 84% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/ClusterTierReconnectMessage.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/ClusterTierReconnectMessage.java index 49315a99d4..129cca97ea 100644 --- a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/ClusterTierReconnectMessage.java +++ b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/ClusterTierReconnectMessage.java @@ -24,16 +24,19 @@ public class ClusterTierReconnectMessage { private final Set hashInvalidationsInProgress; private boolean clearInProgress = false; private final Set locksHeld; + private boolean eventsEnabled; - public ClusterTierReconnectMessage() { + public ClusterTierReconnectMessage(boolean eventsEnabled) { + this.eventsEnabled = eventsEnabled; hashInvalidationsInProgress = new HashSet<>(); locksHeld = new HashSet<>(); } - public ClusterTierReconnectMessage(Set hashInvalidationsInProgress, Set locksHeld, boolean clearInProgress) { + public ClusterTierReconnectMessage(Set hashInvalidationsInProgress, Set locksHeld, boolean clearInProgress, boolean eventsEnabled) { this.hashInvalidationsInProgress = hashInvalidationsInProgress; this.locksHeld = locksHeld; this.clearInProgress = clearInProgress; + this.eventsEnabled = eventsEnabled; } public void addInvalidationsInProgress(Set hashInvalidationsInProgress) { @@ -59,4 +62,8 @@ public boolean isClearInProgress() { public Set getLocksHeld() { return locksHeld; } + + public boolean isEventsEnabled() { + return eventsEnabled; + } } diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/CodecUtil.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/CodecUtil.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/CodecUtil.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/CodecUtil.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/CommonConfigCodec.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/CommonConfigCodec.java similarity index 97% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/CommonConfigCodec.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/CommonConfigCodec.java index 0edc019597..1175b5b38d 100644 --- a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/CommonConfigCodec.java +++ b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/CommonConfigCodec.java @@ -32,6 +32,7 @@ import java.util.HashMap; import java.util.Map; +import java.util.Optional; import static org.terracotta.runnel.EnumMappingBuilder.newEnumMappingBuilder; import static org.terracotta.runnel.StructBuilder.newStructBuilder; @@ -164,7 +165,11 @@ public ServerStoreConfiguration decodeServerStoreConfiguration(PrimitiveDecoding } return new ServerStoreConfiguration(poolAllocation, keyType, valueType, keySerializer, valueSerializer, consistency, - loaderWriterConfigured, writeBehindConfigured); + getNonNullBoolean(loaderWriterConfigured), getNonNullBoolean(writeBehindConfigured)); + } + + private static Boolean getNonNullBoolean(Boolean loaderWriterConfigured) { + return Optional.ofNullable(loaderWriterConfigured).orElse(false); } @Override diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/ConcurrentEntityMessage.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/ConcurrentEntityMessage.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/ConcurrentEntityMessage.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/ConcurrentEntityMessage.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/ConfigCodec.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/ConfigCodec.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/ConfigCodec.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/ConfigCodec.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/EhcacheCodec.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/EhcacheCodec.java similarity index 93% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/EhcacheCodec.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/EhcacheCodec.java index 69c2c7c879..932735aee1 100644 --- a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/EhcacheCodec.java +++ b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/EhcacheCodec.java @@ -26,9 +26,9 @@ import java.nio.ByteBuffer; import static java.nio.ByteBuffer.wrap; -import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.EHCACHE_MESSAGE_TYPES_ENUM_MAPPING; -import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.MESSAGE_TYPE_FIELD_INDEX; -import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.MESSAGE_TYPE_FIELD_NAME; +import static org.ehcache.clustered.common.internal.messages.BaseCodec.EHCACHE_MESSAGE_TYPES_ENUM_MAPPING; +import static org.ehcache.clustered.common.internal.messages.BaseCodec.MESSAGE_TYPE_FIELD_INDEX; +import static org.ehcache.clustered.common.internal.messages.BaseCodec.MESSAGE_TYPE_FIELD_NAME; import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.isLifecycleMessage; import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.isStateRepoOperationMessage; import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.isStoreOperationMessage; diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/EntityConfigurationCodec.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/EntityConfigurationCodec.java similarity index 95% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/EntityConfigurationCodec.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/EntityConfigurationCodec.java index b5e6f970d3..e9edfc9478 100644 --- a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/EntityConfigurationCodec.java +++ b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/EntityConfigurationCodec.java @@ -25,6 +25,8 @@ import org.terracotta.runnel.decoding.StructDecoder; import org.terracotta.runnel.encoding.StructEncoder; +import java.nio.ByteBuffer; + import static java.nio.ByteBuffer.wrap; import static org.ehcache.clustered.common.internal.messages.MessageCodecUtils.SERVER_STORE_NAME_FIELD; import static org.terracotta.runnel.StructBuilder.newStructBuilder; @@ -68,7 +70,11 @@ public byte[] encode(ClusterTierEntityConfiguration configuration) { } public ClusterTierEntityConfiguration decodeClusteredStoreConfiguration(byte[] configuration) { - StructDecoder decoder = clusteredStoreConfigurationStruct.decoder(wrap(configuration)); + return decodeClusteredStoreConfiguration(wrap(configuration)); + } + + public ClusterTierEntityConfiguration decodeClusteredStoreConfiguration(ByteBuffer buffer) { + StructDecoder decoder = clusteredStoreConfigurationStruct.decoder(buffer); String managerIdentifier = decoder.string(IDENTIFIER); if (managerIdentifier == null) { throw new IllegalArgumentException("Payload is an invalid content"); diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/ExceptionCodec.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/ExceptionCodec.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/ExceptionCodec.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/ExceptionCodec.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/LifeCycleMessageCodec.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/LifeCycleMessageCodec.java similarity index 86% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/LifeCycleMessageCodec.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/LifeCycleMessageCodec.java index af1c5b5ba9..0841cfb534 100644 --- a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/LifeCycleMessageCodec.java +++ b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/LifeCycleMessageCodec.java @@ -25,10 +25,11 @@ import java.nio.ByteBuffer; -import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.EHCACHE_MESSAGE_TYPES_ENUM_MAPPING; -import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.MESSAGE_TYPE_FIELD_INDEX; -import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.MESSAGE_TYPE_FIELD_NAME; +import static org.ehcache.clustered.common.internal.messages.BaseCodec.EHCACHE_MESSAGE_TYPES_ENUM_MAPPING; +import static org.ehcache.clustered.common.internal.messages.BaseCodec.MESSAGE_TYPE_FIELD_INDEX; +import static org.ehcache.clustered.common.internal.messages.BaseCodec.MESSAGE_TYPE_FIELD_NAME; import static org.ehcache.clustered.common.internal.messages.MessageCodecUtils.SERVER_STORE_NAME_FIELD; +import static org.ehcache.clustered.common.internal.messages.MessageCodecUtils.encodeMandatoryFields; import static org.terracotta.runnel.StructBuilder.newStructBuilder; public class LifeCycleMessageCodec { @@ -45,11 +46,9 @@ public class LifeCycleMessageCodec { private final Struct validateMessageStruct; private final Struct validateStoreMessageStruct; - private final MessageCodecUtils messageCodecUtils; private final ConfigCodec configCodec; public LifeCycleMessageCodec(ConfigCodec configCodec) { - this.messageCodecUtils = new MessageCodecUtils(); this.configCodec = configCodec; StructBuilder validateMessageStructBuilderPrefix = newStructBuilder() @@ -81,14 +80,11 @@ public byte[] encode(LifecycleMessage message) { } private byte[] encodePrepareForDestroyMessage(LifecycleMessage message) { - return PREPARE_FOR_DESTROY_STRUCT.encoder() - .enm(MESSAGE_TYPE_FIELD_NAME, message.getMessageType()) - .encode().array(); + return encodeMandatoryFields(PREPARE_FOR_DESTROY_STRUCT, message).encode().array(); } private byte[] encodeValidateStoreMessage(LifecycleMessage.ValidateServerStore message) { - StructEncoder encoder = validateStoreMessageStruct.encoder(); - messageCodecUtils.encodeMandatoryFields(encoder, message); + StructEncoder encoder = encodeMandatoryFields(validateStoreMessageStruct, message); encoder.string(SERVER_STORE_NAME_FIELD, message.getName()); configCodec.encodeServerStoreConfiguration(encoder, message.getStoreConfiguration()); @@ -96,9 +92,8 @@ private byte[] encodeValidateStoreMessage(LifecycleMessage.ValidateServerStore m } private byte[] encodeTierManagerValidateMessage(LifecycleMessage.ValidateStoreManager message) { - StructEncoder encoder = validateMessageStruct.encoder(); + StructEncoder encoder = encodeMandatoryFields(validateMessageStruct, message); ServerSideConfiguration config = message.getConfiguration(); - messageCodecUtils.encodeMandatoryFields(encoder, message); if (config == null) { encoder.bool(CONFIG_PRESENT_FIELD, false); } else { diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/LifeCycleMessageFactory.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/LifeCycleMessageFactory.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/LifeCycleMessageFactory.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/LifeCycleMessageFactory.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/LifecycleMessage.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/LifecycleMessage.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/LifecycleMessage.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/LifecycleMessage.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/MessageCodecUtils.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/MessageCodecUtils.java similarity index 68% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/MessageCodecUtils.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/MessageCodecUtils.java index 0472f0f89c..4b37ece7e1 100644 --- a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/MessageCodecUtils.java +++ b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/MessageCodecUtils.java @@ -16,17 +16,22 @@ package org.ehcache.clustered.common.internal.messages; +import org.terracotta.runnel.Struct; import org.terracotta.runnel.encoding.StructEncoder; +import static org.ehcache.clustered.common.internal.messages.BaseCodec.MESSAGE_TYPE_FIELD_NAME; + /** * MessageCodecUtils */ -public class MessageCodecUtils { +public final class MessageCodecUtils { public static final String SERVER_STORE_NAME_FIELD = "serverStoreName"; public static final String KEY_FIELD = "key"; - public void encodeMandatoryFields(StructEncoder encoder, EhcacheOperationMessage message) { - encoder.enm(EhcacheMessageType.MESSAGE_TYPE_FIELD_NAME, message.getMessageType()); + private MessageCodecUtils() {} + + public static StructEncoder encodeMandatoryFields(Struct struct, EhcacheOperationMessage message) { + return struct.encoder().enm(MESSAGE_TYPE_FIELD_NAME, message.getMessageType()); } } diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/ReconnectMessageCodec.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/ReconnectMessageCodec.java similarity index 88% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/ReconnectMessageCodec.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/ReconnectMessageCodec.java index b68a4ef661..d21fbb6ce6 100644 --- a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/ReconnectMessageCodec.java +++ b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/ReconnectMessageCodec.java @@ -33,11 +33,13 @@ public class ReconnectMessageCodec { private static final String HASH_INVALIDATION_IN_PROGRESS_FIELD = "hashInvalidationInProgress"; private static final String CLEAR_IN_PROGRESS_FIELD = "clearInProgress"; private static final String LOCKS_HELD_FIELD = "locksHeld"; + private static final String EVENTS_ENABLED_FIELD = "eventsEnabled"; private static final Struct CLUSTER_TIER_RECONNECT_MESSAGE_STRUCT = newStructBuilder() .int64s(HASH_INVALIDATION_IN_PROGRESS_FIELD, 20) .bool(CLEAR_IN_PROGRESS_FIELD, 30) .int64s(LOCKS_HELD_FIELD, 40) + .bool(EVENTS_ENABLED_FIELD, 50) // added in 10.5.0 .build(); public byte[] encode(ClusterTierReconnectMessage reconnectMessage) { @@ -47,6 +49,7 @@ public byte[] encode(ClusterTierReconnectMessage reconnectMessage) { encoder.bool(CLEAR_IN_PROGRESS_FIELD, reconnectMessage.isClearInProgress()); ArrayEncoder> locksHeldEncoder = encoder.int64s(LOCKS_HELD_FIELD); reconnectMessage.getLocksHeld().forEach(locksHeldEncoder::value); + encoder.bool(EVENTS_ENABLED_FIELD, reconnectMessage.isEventsEnabled()); return encoder.encode().array(); } @@ -61,11 +64,9 @@ public ClusterTierReconnectMessage decode(byte[] payload) { ArrayDecoder> locksHeldDecoder = decoder.int64s(LOCKS_HELD_FIELD); Set locks = decodeLongs(locksHeldDecoder); - ClusterTierReconnectMessage message = new ClusterTierReconnectMessage(hashes, locks, clearInProgress != null ? clearInProgress : false); + Boolean eventsEnabled = decoder.bool(EVENTS_ENABLED_FIELD); - - - return message; + return new ClusterTierReconnectMessage(hashes, locks, clearInProgress != null ? clearInProgress : false, eventsEnabled != null ? eventsEnabled : false); } private static Set decodeLongs(ArrayDecoder> decoder) { diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/ResponseCodec.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/ResponseCodec.java similarity index 67% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/ResponseCodec.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/ResponseCodec.java index 2230519a53..d082a095c6 100644 --- a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/ResponseCodec.java +++ b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/ResponseCodec.java @@ -25,25 +25,38 @@ import org.terracotta.runnel.StructBuilder; import org.terracotta.runnel.decoding.ArrayDecoder; import org.terracotta.runnel.decoding.Enm; +import org.terracotta.runnel.decoding.StructArrayDecoder; import org.terracotta.runnel.decoding.StructDecoder; import org.terracotta.runnel.encoding.ArrayEncoder; import org.terracotta.runnel.encoding.StructEncoder; import java.nio.ByteBuffer; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.UUID; import static java.nio.ByteBuffer.wrap; +import static org.ehcache.clustered.common.internal.messages.BaseCodec.EHCACHE_RESPONSE_TYPES_ENUM_MAPPING; +import static org.ehcache.clustered.common.internal.messages.BaseCodec.RESPONSE_TYPE_FIELD_INDEX; +import static org.ehcache.clustered.common.internal.messages.BaseCodec.RESPONSE_TYPE_FIELD_NAME; +import static org.ehcache.clustered.common.internal.messages.ChainCodec.CHAIN_ENTRY_STRUCT; import static org.ehcache.clustered.common.internal.messages.ChainCodec.CHAIN_STRUCT; -import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.AllInvalidationDone; -import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.ClientInvalidateAll; -import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.ClientInvalidateHash; -import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.HashInvalidationDone; -import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.ServerInvalidateHash; -import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.MapValue; -import static org.ehcache.clustered.common.internal.messages.EhcacheResponseType.EHCACHE_RESPONSE_TYPES_ENUM_MAPPING; -import static org.ehcache.clustered.common.internal.messages.EhcacheResponseType.RESPONSE_TYPE_FIELD_INDEX; -import static org.ehcache.clustered.common.internal.messages.EhcacheResponseType.RESPONSE_TYPE_FIELD_NAME; +import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.allInvalidationDone; +import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.clientInvalidateAll; +import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.clientInvalidateHash; +import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.failure; +import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.getResponse; +import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.hashInvalidationDone; +import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.lockFailure; +import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.mapValue; +import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.prepareForDestroy; +import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.resolveRequest; +import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.serverAppend; +import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.serverInvalidateHash; +import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.success; import static org.ehcache.clustered.common.internal.messages.MessageCodecUtils.KEY_FIELD; import static org.ehcache.clustered.common.internal.messages.StateRepositoryOpCodec.WHITELIST_PREDICATE; import static org.terracotta.runnel.StructBuilder.newStructBuilder; @@ -53,6 +66,7 @@ public class ResponseCodec { private static final String EXCEPTION_FIELD = "exception"; private static final String INVALIDATION_ID_FIELD = "invalidationId"; private static final String CHAIN_FIELD = "chain"; + private static final String APPENDED_FIELD = "appended"; private static final String MAP_VALUE_FIELD = "mapValue"; private static final String STORES_FIELD = "stores"; @@ -86,6 +100,12 @@ public class ResponseCodec { private static final Struct SERVER_INVALIDATE_HASH_RESPONSE_STRUCT = StructBuilder.newStructBuilder() .enm(RESPONSE_TYPE_FIELD_NAME, RESPONSE_TYPE_FIELD_INDEX, EHCACHE_RESPONSE_TYPES_ENUM_MAPPING) .int64(KEY_FIELD, 20) + .struct(CHAIN_FIELD, 30, CHAIN_STRUCT) // added in version 10.5.0 + .build(); + private static final Struct SERVER_APPEND_RESPONSE_STRUCT = StructBuilder.newStructBuilder() + .enm(RESPONSE_TYPE_FIELD_NAME, RESPONSE_TYPE_FIELD_INDEX, EHCACHE_RESPONSE_TYPES_ENUM_MAPPING) + .byteBuffer(APPENDED_FIELD, 20) + .struct(CHAIN_FIELD, 30, CHAIN_STRUCT) .build(); private static final Struct MAP_VALUE_RESPONSE_STRUCT = StructBuilder.newStructBuilder() .enm(RESPONSE_TYPE_FIELD_NAME, RESPONSE_TYPE_FIELD_INDEX, EHCACHE_RESPONSE_TYPES_ENUM_MAPPING) @@ -104,6 +124,12 @@ public class ResponseCodec { .enm(RESPONSE_TYPE_FIELD_NAME, RESPONSE_TYPE_FIELD_INDEX, EHCACHE_RESPONSE_TYPES_ENUM_MAPPING) .struct(CHAIN_FIELD, 20, CHAIN_STRUCT) .build(); + private static final Struct ITERATOR_BATCH_STRUCT = newStructBuilder() + .enm(RESPONSE_TYPE_FIELD_NAME, RESPONSE_TYPE_FIELD_INDEX, EHCACHE_RESPONSE_TYPES_ENUM_MAPPING) + .string("id", 20) + .structs("chains", 30, CHAIN_ENTRY_STRUCT) + .bool("last", 40) + .build(); public byte[] encode(EhcacheEntityResponse response) { switch (response.getResponseType()) { @@ -121,23 +147,23 @@ public byte[] encode(EhcacheEntityResponse response) { final EhcacheEntityResponse.GetResponse getResponse = (EhcacheEntityResponse.GetResponse)response; return GET_RESPONSE_STRUCT.encoder() .enm(RESPONSE_TYPE_FIELD_NAME, getResponse.getResponseType()) - .struct(CHAIN_FIELD, getResponse.getChain(), ChainCodec::encode) + .struct(CHAIN_FIELD, getResponse.getChain(), ChainCodec::encodeChain) .encode().array(); case HASH_INVALIDATION_DONE: { - HashInvalidationDone hashInvalidationDone = (HashInvalidationDone) response; + EhcacheEntityResponse.HashInvalidationDone hashInvalidationDone = (EhcacheEntityResponse.HashInvalidationDone) response; return HASH_INVALIDATION_DONE_RESPONSE_STRUCT.encoder() .enm(RESPONSE_TYPE_FIELD_NAME, hashInvalidationDone.getResponseType()) .int64(KEY_FIELD, hashInvalidationDone.getKey()) .encode().array(); } case ALL_INVALIDATION_DONE: { - AllInvalidationDone allInvalidationDone = (AllInvalidationDone) response; + EhcacheEntityResponse.AllInvalidationDone allInvalidationDone = (EhcacheEntityResponse.AllInvalidationDone) response; return ALL_INVALIDATION_DONE_RESPONSE_STRUCT.encoder() .enm(RESPONSE_TYPE_FIELD_NAME, allInvalidationDone.getResponseType()) .encode().array(); } case CLIENT_INVALIDATE_HASH: { - ClientInvalidateHash clientInvalidateHash = (ClientInvalidateHash) response; + EhcacheEntityResponse.ClientInvalidateHash clientInvalidateHash = (EhcacheEntityResponse.ClientInvalidateHash) response; return CLIENT_INVALIDATE_HASH_RESPONSE_STRUCT.encoder() .enm(RESPONSE_TYPE_FIELD_NAME, clientInvalidateHash.getResponseType()) .int64(KEY_FIELD, clientInvalidateHash.getKey()) @@ -145,21 +171,32 @@ public byte[] encode(EhcacheEntityResponse response) { .encode().array(); } case CLIENT_INVALIDATE_ALL: { - ClientInvalidateAll clientInvalidateAll = (ClientInvalidateAll) response; + EhcacheEntityResponse.ClientInvalidateAll clientInvalidateAll = (EhcacheEntityResponse.ClientInvalidateAll) response; return CLIENT_INVALIDATE_ALL_RESPONSE_STRUCT.encoder() .enm(RESPONSE_TYPE_FIELD_NAME, clientInvalidateAll.getResponseType()) .int32(INVALIDATION_ID_FIELD, clientInvalidateAll.getInvalidationId()) .encode().array(); } + case SERVER_APPEND: { + EhcacheEntityResponse.ServerAppend serverAppend = (EhcacheEntityResponse.ServerAppend) response; + return SERVER_APPEND_RESPONSE_STRUCT.encoder() + .enm(RESPONSE_TYPE_FIELD_NAME, serverAppend.getResponseType()) + .byteBuffer(APPENDED_FIELD, serverAppend.getAppended()) + .struct(CHAIN_FIELD, serverAppend.getBeforeAppend(), ChainCodec::encodeChain) + .encode().array(); + } case SERVER_INVALIDATE_HASH: { - ServerInvalidateHash serverInvalidateHash = (ServerInvalidateHash) response; - return SERVER_INVALIDATE_HASH_RESPONSE_STRUCT.encoder() + EhcacheEntityResponse.ServerInvalidateHash serverInvalidateHash = (EhcacheEntityResponse.ServerInvalidateHash) response; + StructEncoder encoder = SERVER_INVALIDATE_HASH_RESPONSE_STRUCT.encoder() .enm(RESPONSE_TYPE_FIELD_NAME, serverInvalidateHash.getResponseType()) - .int64(KEY_FIELD, serverInvalidateHash.getKey()) - .encode().array(); + .int64(KEY_FIELD, serverInvalidateHash.getKey()); + if (serverInvalidateHash.getEvictedChain() != null) { + encoder.struct(CHAIN_FIELD, serverInvalidateHash.getEvictedChain(), ChainCodec::encodeChain); + } + return encoder.encode().array(); } case MAP_VALUE: { - MapValue mapValue = (MapValue) response; + EhcacheEntityResponse.MapValue mapValue = (EhcacheEntityResponse.MapValue) response; byte[] encodedMapValue = Util.marshall(mapValue.getValue()); return MAP_VALUE_RESPONSE_STRUCT.encoder() .enm(RESPONSE_TYPE_FIELD_NAME, mapValue.getResponseType()) @@ -178,18 +215,18 @@ public byte[] encode(EhcacheEntityResponse response) { .encode().array(); } case RESOLVE_REQUEST: { - EhcacheEntityResponse.ResolveRequest resolve = (ResolveRequest) response; + ResolveRequest resolve = (ResolveRequest) response; return RESOLVE_REQUEST_RESPONSE_STRUCT.encoder() .enm(RESPONSE_TYPE_FIELD_NAME, resolve.getResponseType()) .int64(KEY_FIELD, resolve.getKey()) - .struct(CHAIN_FIELD, resolve.getChain(), ChainCodec::encode) + .struct(CHAIN_FIELD, resolve.getChain(), ChainCodec::encodeChain) .encode().array(); } case LOCK_SUCCESS: { EhcacheEntityResponse.LockSuccess lockSuccess = (EhcacheEntityResponse.LockSuccess) response; return LOCK_RESPONSE_STRUCT.encoder() .enm(RESPONSE_TYPE_FIELD_NAME, lockSuccess.getResponseType()) - .struct(CHAIN_FIELD, lockSuccess.getChain(), ChainCodec::encode) + .struct(CHAIN_FIELD, lockSuccess.getChain(), ChainCodec::encodeChain) .encode().array(); } case LOCK_FAILURE: { @@ -198,6 +235,15 @@ public byte[] encode(EhcacheEntityResponse response) { .enm(RESPONSE_TYPE_FIELD_NAME, lockFailure.getResponseType()) .encode().array(); } + case ITERATOR_BATCH: { + EhcacheEntityResponse.IteratorBatch iteratorBatch = (EhcacheEntityResponse.IteratorBatch) response; + return ITERATOR_BATCH_STRUCT.encoder() + .enm(RESPONSE_TYPE_FIELD_NAME, iteratorBatch.getResponseType()) + .string("id", iteratorBatch.getIdentity().toString()) + .structs("chains", iteratorBatch.getChains(), ChainCodec::encodeChainEntry) + .bool("last", iteratorBatch.isLast()) + .encode().array(); + } default: throw new UnsupportedOperationException("The operation is not supported : " + response.getResponseType()); } @@ -220,41 +266,50 @@ public EhcacheEntityResponse decode(byte[] payload) { buffer.rewind(); switch (opCode) { case SUCCESS: - return EhcacheEntityResponse.success(); + return success(); case FAILURE: decoder = FAILURE_RESPONSE_STRUCT.decoder(buffer); ClusterException exception = ExceptionCodec.decode(decoder.struct(EXCEPTION_FIELD)); - return EhcacheEntityResponse.failure(exception.withClientStackTrace()); + return failure(exception.withClientStackTrace()); case GET_RESPONSE: decoder = GET_RESPONSE_STRUCT.decoder(buffer); - return EhcacheEntityResponse.getResponse(ChainCodec.decode(decoder.struct(CHAIN_FIELD))); + return getResponse(ChainCodec.decodeChain(decoder.struct(CHAIN_FIELD))); case HASH_INVALIDATION_DONE: { decoder = HASH_INVALIDATION_DONE_RESPONSE_STRUCT.decoder(buffer); long key = decoder.int64(KEY_FIELD); - return EhcacheEntityResponse.hashInvalidationDone(key); + return hashInvalidationDone(key); } case ALL_INVALIDATION_DONE: { - return EhcacheEntityResponse.allInvalidationDone(); + return allInvalidationDone(); + } + case SERVER_APPEND: { + decoder = SERVER_APPEND_RESPONSE_STRUCT.decoder(buffer); + ByteBuffer appended = decoder.byteBuffer(APPENDED_FIELD); + StructDecoder> chainDecoder = decoder.struct(CHAIN_FIELD); + Chain chain = chainDecoder == null ? null : ChainCodec.decodeChain(chainDecoder); + return serverAppend(appended, chain); } case CLIENT_INVALIDATE_HASH: { decoder = CLIENT_INVALIDATE_HASH_RESPONSE_STRUCT.decoder(buffer); long key = decoder.int64(KEY_FIELD); int invalidationId = decoder.int32(INVALIDATION_ID_FIELD); - return EhcacheEntityResponse.clientInvalidateHash(key, invalidationId); + return clientInvalidateHash(key, invalidationId); } case CLIENT_INVALIDATE_ALL: { decoder = CLIENT_INVALIDATE_ALL_RESPONSE_STRUCT.decoder(buffer); int invalidationId = decoder.int32(INVALIDATION_ID_FIELD); - return EhcacheEntityResponse.clientInvalidateAll(invalidationId); + return clientInvalidateAll(invalidationId); } case SERVER_INVALIDATE_HASH: { decoder = SERVER_INVALIDATE_HASH_RESPONSE_STRUCT.decoder(buffer); long key = decoder.int64(KEY_FIELD); - return EhcacheEntityResponse.serverInvalidateHash(key); + StructDecoder> chainDecoder = decoder.struct(CHAIN_FIELD); + Chain evictedChain = chainDecoder == null ? null : ChainCodec.decodeChain(chainDecoder); + return serverInvalidateHash(key, evictedChain); } case MAP_VALUE: { decoder = MAP_VALUE_RESPONSE_STRUCT.decoder(buffer); - return EhcacheEntityResponse.mapValue( + return mapValue( Util.unmarshall(decoder.byteBuffer(MAP_VALUE_FIELD), WHITELIST_PREDICATE)); } case PREPARE_FOR_DESTROY: { @@ -264,22 +319,34 @@ public EhcacheEntityResponse decode(byte[] payload) { for (int i = 0; i < storesDecoder.length(); i++) { stores.add(storesDecoder.value()); } - return EhcacheEntityResponse.prepareForDestroy(stores); + return prepareForDestroy(stores); } case RESOLVE_REQUEST: { decoder = RESOLVE_REQUEST_RESPONSE_STRUCT.decoder(buffer); long key = decoder.int64(KEY_FIELD); - Chain chain = ChainCodec.decode(decoder.struct(CHAIN_FIELD)); - return EhcacheEntityResponse.resolveRequest(key, chain); + Chain chain = ChainCodec.decodeChain(decoder.struct(CHAIN_FIELD)); + return resolveRequest(key, chain); } case LOCK_SUCCESS: { decoder = LOCK_RESPONSE_STRUCT.decoder(buffer); - Chain chain = ChainCodec.decode(decoder.struct(CHAIN_FIELD)); + Chain chain = ChainCodec.decodeChain(decoder.struct(CHAIN_FIELD)); return new EhcacheEntityResponse.LockSuccess(chain); } case LOCK_FAILURE: { - return EhcacheEntityResponse.lockFailure(); + return lockFailure(); } + case ITERATOR_BATCH: { + decoder = ITERATOR_BATCH_STRUCT.decoder(buffer); + UUID id = UUID.fromString(decoder.string("id")); + StructArrayDecoder> chainsDecoder = decoder.structs("chains"); + List> chains = new ArrayList<>(chainsDecoder.length()); + while (chainsDecoder.hasNext()) { + chains.add(ChainCodec.decodeChainEntry(chainsDecoder.next())); + } + boolean last = decoder.bool("last"); + return EhcacheEntityResponse.iteratorBatchResponse(id, chains, last); + } + default: throw new UnsupportedOperationException("The operation is not supported with opCode : " + opCode); } diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/ServerStoreOpCodec.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/ServerStoreOpCodec.java similarity index 55% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/ServerStoreOpCodec.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/ServerStoreOpCodec.java index 89cdffb5fa..cd38dc0310 100644 --- a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/ServerStoreOpCodec.java +++ b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/ServerStoreOpCodec.java @@ -26,15 +26,16 @@ import org.ehcache.clustered.common.internal.store.Chain; import org.terracotta.runnel.Struct; import org.terracotta.runnel.decoding.StructDecoder; -import org.terracotta.runnel.encoding.StructEncoder; import java.nio.ByteBuffer; +import java.util.UUID; +import static org.ehcache.clustered.common.internal.messages.BaseCodec.EHCACHE_MESSAGE_TYPES_ENUM_MAPPING; +import static org.ehcache.clustered.common.internal.messages.BaseCodec.MESSAGE_TYPE_FIELD_INDEX; +import static org.ehcache.clustered.common.internal.messages.BaseCodec.MESSAGE_TYPE_FIELD_NAME; import static org.ehcache.clustered.common.internal.messages.ChainCodec.CHAIN_STRUCT; -import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.EHCACHE_MESSAGE_TYPES_ENUM_MAPPING; -import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.MESSAGE_TYPE_FIELD_INDEX; -import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.MESSAGE_TYPE_FIELD_NAME; import static org.ehcache.clustered.common.internal.messages.MessageCodecUtils.KEY_FIELD; +import static org.ehcache.clustered.common.internal.messages.MessageCodecUtils.encodeMandatoryFields; import static org.terracotta.runnel.StructBuilder.newStructBuilder; public class ServerStoreOpCodec { @@ -83,123 +84,133 @@ public class ServerStoreOpCodec { .int64("hash", 30) .build(); - private final MessageCodecUtils messageCodecUtils = new MessageCodecUtils(); + private static final Struct ITERATOR_OPEN_STRUCT = newStructBuilder() + .enm(MESSAGE_TYPE_FIELD_NAME, MESSAGE_TYPE_FIELD_INDEX, EHCACHE_MESSAGE_TYPES_ENUM_MAPPING) + .int32("batchSize", 20) + .build(); - public byte[] encode(ServerStoreOpMessage message) { - StructEncoder encoder; + private static final Struct ITERATOR_CLOSE_STRUCT = newStructBuilder() + .enm(MESSAGE_TYPE_FIELD_NAME, MESSAGE_TYPE_FIELD_INDEX, EHCACHE_MESSAGE_TYPES_ENUM_MAPPING) + .string("id", 20) + .build(); + + private static final Struct ITERATOR_ADVANCE_STRUCT = newStructBuilder() + .enm(MESSAGE_TYPE_FIELD_NAME, MESSAGE_TYPE_FIELD_INDEX, EHCACHE_MESSAGE_TYPES_ENUM_MAPPING) + .string("id", 20) + .int32("batchSize", 30) + .build(); + + private static final Struct ENABLE_EVENT_LISTENER_STRUCT = newStructBuilder() + .enm(MESSAGE_TYPE_FIELD_NAME, MESSAGE_TYPE_FIELD_INDEX, EHCACHE_MESSAGE_TYPES_ENUM_MAPPING) + .bool("enable", 20) + .build(); + public byte[] encode(ServerStoreOpMessage message) { switch (message.getMessageType()) { case GET_STORE: GetMessage getMessage = (GetMessage) message; - encoder = GET_MESSAGE_STRUCT.encoder(); - return encoder - .enm(MESSAGE_TYPE_FIELD_NAME, message.getMessageType()) + return encodeMandatoryFields(GET_MESSAGE_STRUCT, message) .int64(KEY_FIELD, getMessage.getKey()) - .encode() - .array(); + .encode().array(); case APPEND: AppendMessage appendMessage = (AppendMessage) message; - encoder = APPEND_MESSAGE_STRUCT.encoder(); - messageCodecUtils.encodeMandatoryFields(encoder, message); - return encoder + return encodeMandatoryFields(APPEND_MESSAGE_STRUCT, message) .int64(KEY_FIELD, appendMessage.getKey()) .byteBuffer("payload", appendMessage.getPayload()) - .encode() - .array(); + .encode().array(); case GET_AND_APPEND: GetAndAppendMessage getAndAppendMessage = (GetAndAppendMessage) message; - encoder = GET_AND_APPEND_MESSAGE_STRUCT.encoder(); - messageCodecUtils.encodeMandatoryFields(encoder, message); - return encoder + return encodeMandatoryFields(GET_AND_APPEND_MESSAGE_STRUCT, message) .int64(KEY_FIELD, getAndAppendMessage.getKey()) .byteBuffer("payload", getAndAppendMessage.getPayload()) - .encode() - .array(); + .encode().array(); case REPLACE: final ReplaceAtHeadMessage replaceAtHeadMessage = (ReplaceAtHeadMessage) message; - encoder = REPLACE_MESSAGE_STRUCT.encoder(); - messageCodecUtils.encodeMandatoryFields(encoder, message); - return encoder + return encodeMandatoryFields(REPLACE_MESSAGE_STRUCT, message) .int64(KEY_FIELD, replaceAtHeadMessage.getKey()) - .struct("expect", replaceAtHeadMessage.getExpect(), ChainCodec::encode) - .struct("update", replaceAtHeadMessage.getUpdate(), ChainCodec::encode) - .encode() - .array(); + .struct("expect", replaceAtHeadMessage.getExpect(), ChainCodec::encodeChain) + .struct("update", replaceAtHeadMessage.getUpdate(), ChainCodec::encodeChain) + .encode().array(); case CLIENT_INVALIDATION_ACK: ClientInvalidationAck clientInvalidationAckMessage = (ClientInvalidationAck) message; - encoder = CLIENT_INVALIDATION_ACK_MESSAGE_STRUCT.encoder(); - return encoder - .enm(MESSAGE_TYPE_FIELD_NAME, message.getMessageType()) + return encodeMandatoryFields(CLIENT_INVALIDATION_ACK_MESSAGE_STRUCT, message) .int64(KEY_FIELD, clientInvalidationAckMessage.getKey()) .int32("invalidationId", clientInvalidationAckMessage.getInvalidationId()) - .encode() - .array(); + .encode().array(); case CLIENT_INVALIDATION_ALL_ACK: ClientInvalidationAllAck clientInvalidationAllAckMessage = (ClientInvalidationAllAck) message; - encoder = CLIENT_INVALIDATION_ALL_ACK_MESSAGE_STRUCT.encoder(); - return encoder - .enm(MESSAGE_TYPE_FIELD_NAME, message.getMessageType()) + return encodeMandatoryFields(CLIENT_INVALIDATION_ALL_ACK_MESSAGE_STRUCT, message) .int32("invalidationId", clientInvalidationAllAckMessage.getInvalidationId()) .encode().array(); case CLEAR: - encoder = CLEAR_MESSAGE_STRUCT.encoder(); - messageCodecUtils.encodeMandatoryFields(encoder, message); - return encoder - .encode() - .array(); + return encodeMandatoryFields(CLEAR_MESSAGE_STRUCT, message) + .encode().array(); case LOCK: - encoder = LOCK_STRUCT.encoder(); - return encoder - .enm(MESSAGE_TYPE_FIELD_NAME, message.getMessageType()) - .int64("hash", ((ServerStoreOpMessage.LockMessage) message).getHash()) - .encode().array(); + ServerStoreOpMessage.LockMessage lockMessage = (ServerStoreOpMessage.LockMessage) message; + return encodeMandatoryFields(LOCK_STRUCT, message) + .int64("hash", lockMessage.getHash()) + .encode().array(); case UNLOCK: - encoder = LOCK_STRUCT.encoder(); - return encoder - .enm(MESSAGE_TYPE_FIELD_NAME, message.getMessageType()) - .int64("hash", ((ServerStoreOpMessage.UnlockMessage) message).getHash()) - .encode().array(); + ServerStoreOpMessage.UnlockMessage unlockMessage = (ServerStoreOpMessage.UnlockMessage) message; + return encodeMandatoryFields(LOCK_STRUCT, message) + .int64("hash", unlockMessage.getHash()) + .encode().array(); + case ITERATOR_OPEN: + return encodeMandatoryFields(ITERATOR_OPEN_STRUCT, message) + .int32("batchSize", ((ServerStoreOpMessage.IteratorOpenMessage) message).getBatchSize()) + .encode().array(); + case ITERATOR_CLOSE: + return encodeMandatoryFields(ITERATOR_CLOSE_STRUCT, message) + .string("id", ((ServerStoreOpMessage.IteratorCloseMessage) message).getIdentity().toString()) + .encode().array(); + case ITERATOR_ADVANCE: + return encodeMandatoryFields(ITERATOR_ADVANCE_STRUCT, message) + .string("id", ((ServerStoreOpMessage.IteratorAdvanceMessage) message).getIdentity().toString()) + .int32("batchSize", ((ServerStoreOpMessage.IteratorAdvanceMessage) message).getBatchSize()) + .encode().array(); + case ENABLE_EVENT_LISTENER: + return encodeMandatoryFields(ENABLE_EVENT_LISTENER_STRUCT, message) + .bool("enable", ((ServerStoreOpMessage.EnableEventListenerMessage) message).isEnable()) + .encode().array(); default: throw new RuntimeException("Unhandled message operation : " + message.getMessageType()); } } public EhcacheEntityMessage decode(EhcacheMessageType opCode, ByteBuffer messageBuffer) { - StructDecoder decoder; switch (opCode) { case GET_STORE: { - decoder = GET_MESSAGE_STRUCT.decoder(messageBuffer); + StructDecoder decoder = GET_MESSAGE_STRUCT.decoder(messageBuffer); Long key = decoder.int64(KEY_FIELD); return new GetMessage(key); } case GET_AND_APPEND: { - decoder = GET_AND_APPEND_MESSAGE_STRUCT.decoder(messageBuffer); + StructDecoder decoder = GET_AND_APPEND_MESSAGE_STRUCT.decoder(messageBuffer); Long key = decoder.int64(KEY_FIELD); ByteBuffer payload = decoder.byteBuffer("payload"); return new GetAndAppendMessage(key, payload); } case APPEND: { - decoder = APPEND_MESSAGE_STRUCT.decoder(messageBuffer); + StructDecoder decoder = APPEND_MESSAGE_STRUCT.decoder(messageBuffer); Long key = decoder.int64(KEY_FIELD); ByteBuffer payload = decoder.byteBuffer("payload"); return new AppendMessage(key, payload); } case REPLACE: { - decoder = REPLACE_MESSAGE_STRUCT.decoder(messageBuffer); + StructDecoder decoder = REPLACE_MESSAGE_STRUCT.decoder(messageBuffer); Long key = decoder.int64(KEY_FIELD); - Chain expect = ChainCodec.decode(decoder.struct("expect")); - Chain update = ChainCodec.decode(decoder.struct("update")); + Chain expect = ChainCodec.decodeChain(decoder.struct("expect")); + Chain update = ChainCodec.decodeChain(decoder.struct("update")); return new ReplaceAtHeadMessage(key, expect, update); } case CLIENT_INVALIDATION_ACK: { - decoder = CLIENT_INVALIDATION_ACK_MESSAGE_STRUCT.decoder(messageBuffer); + StructDecoder decoder = CLIENT_INVALIDATION_ACK_MESSAGE_STRUCT.decoder(messageBuffer); Long key = decoder.int64(KEY_FIELD); Integer invalidationId = decoder.int32("invalidationId"); return new ClientInvalidationAck(key, invalidationId); } case CLIENT_INVALIDATION_ALL_ACK: { - decoder = CLIENT_INVALIDATION_ALL_ACK_MESSAGE_STRUCT - .decoder(messageBuffer); + StructDecoder decoder = CLIENT_INVALIDATION_ALL_ACK_MESSAGE_STRUCT.decoder(messageBuffer); Integer invalidationId = decoder.int32("invalidationId"); return new ClientInvalidationAllAck(invalidationId); } @@ -207,15 +218,36 @@ public EhcacheEntityMessage decode(EhcacheMessageType opCode, ByteBuffer message return new ClearMessage(); } case LOCK: { - decoder = LOCK_STRUCT.decoder(messageBuffer); + StructDecoder decoder = LOCK_STRUCT.decoder(messageBuffer); long hash = decoder.int64("hash"); return new ServerStoreOpMessage.LockMessage(hash); } case UNLOCK: { - decoder = LOCK_STRUCT.decoder(messageBuffer); + StructDecoder decoder = LOCK_STRUCT.decoder(messageBuffer); long hash = decoder.int64("hash"); return new ServerStoreOpMessage.UnlockMessage(hash); } + case ITERATOR_OPEN: { + StructDecoder decoder = ITERATOR_OPEN_STRUCT.decoder(messageBuffer); + int batchSize = decoder.int32("batchSize"); + return new ServerStoreOpMessage.IteratorOpenMessage(batchSize); + } + case ITERATOR_CLOSE: { + StructDecoder decoder = ITERATOR_CLOSE_STRUCT.decoder(messageBuffer); + UUID identity = UUID.fromString(decoder.string("id")); + return new ServerStoreOpMessage.IteratorCloseMessage(identity); + } + case ITERATOR_ADVANCE: { + StructDecoder decoder = ITERATOR_ADVANCE_STRUCT.decoder(messageBuffer); + UUID identity = UUID.fromString(decoder.string("id")); + int batchSize = decoder.int32("batchSize"); + return new ServerStoreOpMessage.IteratorAdvanceMessage(identity, batchSize); + } + case ENABLE_EVENT_LISTENER: { + StructDecoder decoder = ENABLE_EVENT_LISTENER_STRUCT.decoder(messageBuffer); + Boolean enable = decoder.bool("enable"); + return new ServerStoreOpMessage.EnableEventListenerMessage(enable); + } default: throw new RuntimeException("Unhandled message operation : " + opCode); } diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/ServerStoreOpMessage.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/ServerStoreOpMessage.java similarity index 73% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/ServerStoreOpMessage.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/ServerStoreOpMessage.java index 50c52a8f20..a0b459d590 100644 --- a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/ServerStoreOpMessage.java +++ b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/ServerStoreOpMessage.java @@ -19,6 +19,7 @@ import org.ehcache.clustered.common.internal.store.Chain; import java.nio.ByteBuffer; +import java.util.UUID; public abstract class ServerStoreOpMessage extends EhcacheOperationMessage { @@ -202,5 +203,83 @@ public EhcacheMessageType getMessageType() { } } + public static class IteratorOpenMessage extends ServerStoreOpMessage { + + private final int batchSize; + + public IteratorOpenMessage(int batchSize) { + this.batchSize = batchSize; + } + + public int getBatchSize() { + return batchSize; + } + + @Override + public EhcacheMessageType getMessageType() { + return EhcacheMessageType.ITERATOR_OPEN; + } + } + + public static class IteratorCloseMessage extends ServerStoreOpMessage { + + private final UUID id; + + public IteratorCloseMessage(UUID id) { + this.id = id; + } + + public UUID getIdentity() { + return id; + } + + @Override + public EhcacheMessageType getMessageType() { + return EhcacheMessageType.ITERATOR_CLOSE; + } + } + + public static class IteratorAdvanceMessage extends ServerStoreOpMessage { + + private final UUID id; + private final int batchSize; + + public IteratorAdvanceMessage(UUID id, int batchSize) { + this.id = id; + this.batchSize = batchSize; + + } + + public UUID getIdentity() { + return id; + } + + public int getBatchSize() { + return batchSize; + } + + @Override + public EhcacheMessageType getMessageType() { + return EhcacheMessageType.ITERATOR_ADVANCE; + } + } + + public static class EnableEventListenerMessage extends ServerStoreOpMessage { + private final boolean enable; + + public EnableEventListenerMessage(boolean enable) { + this.enable = enable; + } + + public boolean isEnable() { + return enable; + } + + @Override + public EhcacheMessageType getMessageType() { + return EhcacheMessageType.ENABLE_EVENT_LISTENER; + } + } + } diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/StateRepositoryMessageFactory.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/StateRepositoryMessageFactory.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/StateRepositoryMessageFactory.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/StateRepositoryMessageFactory.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/StateRepositoryOpCodec.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/StateRepositoryOpCodec.java similarity index 75% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/StateRepositoryOpCodec.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/StateRepositoryOpCodec.java index df669e6213..546e0e12aa 100644 --- a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/messages/StateRepositoryOpCodec.java +++ b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/messages/StateRepositoryOpCodec.java @@ -19,7 +19,6 @@ import org.ehcache.clustered.common.internal.store.Util; import org.terracotta.runnel.Struct; import org.terracotta.runnel.decoding.StructDecoder; -import org.terracotta.runnel.encoding.StructEncoder; import java.nio.ByteBuffer; import java.util.Arrays; @@ -27,11 +26,12 @@ import java.util.function.Predicate; import static java.nio.ByteBuffer.wrap; -import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.EHCACHE_MESSAGE_TYPES_ENUM_MAPPING; -import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.MESSAGE_TYPE_FIELD_INDEX; -import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.MESSAGE_TYPE_FIELD_NAME; +import static org.ehcache.clustered.common.internal.messages.BaseCodec.EHCACHE_MESSAGE_TYPES_ENUM_MAPPING; +import static org.ehcache.clustered.common.internal.messages.BaseCodec.MESSAGE_TYPE_FIELD_INDEX; +import static org.ehcache.clustered.common.internal.messages.BaseCodec.MESSAGE_TYPE_FIELD_NAME; import static org.ehcache.clustered.common.internal.messages.MessageCodecUtils.KEY_FIELD; import static org.ehcache.clustered.common.internal.messages.MessageCodecUtils.SERVER_STORE_NAME_FIELD; +import static org.ehcache.clustered.common.internal.messages.MessageCodecUtils.encodeMandatoryFields; import static org.terracotta.runnel.StructBuilder.newStructBuilder; public class StateRepositoryOpCodec { @@ -60,8 +60,6 @@ public class StateRepositoryOpCodec { .string(MAP_ID_FIELD, 35) .build(); - private final MessageCodecUtils messageCodecUtils = new MessageCodecUtils(); - public byte[] encode(StateRepositoryOpMessage message) { switch (message.getMessageType()) { @@ -77,38 +75,29 @@ public byte[] encode(StateRepositoryOpMessage message) { } private byte[] encodeEntrySetMessage(StateRepositoryOpMessage.EntrySetMessage message) { - StructEncoder encoder = ENTRY_SET_MESSAGE_STRUCT.encoder(); - - messageCodecUtils.encodeMandatoryFields(encoder, message); - encoder.string(SERVER_STORE_NAME_FIELD, message.getCacheId()); - encoder.string(MAP_ID_FIELD, message.getCacheId()); - - return encoder.encode().array(); + return encodeMandatoryFields(ENTRY_SET_MESSAGE_STRUCT, message) + .string(SERVER_STORE_NAME_FIELD, message.getCacheId()) + .string(MAP_ID_FIELD, message.getCacheId()) + .encode().array(); } private byte[] encodePutIfAbsentMessage(StateRepositoryOpMessage.PutIfAbsentMessage message) { - StructEncoder encoder = PUT_IF_ABSENT_MESSAGE_STRUCT.encoder(); - - messageCodecUtils.encodeMandatoryFields(encoder, message); - encoder.string(SERVER_STORE_NAME_FIELD, message.getCacheId()); - encoder.string(MAP_ID_FIELD, message.getCacheId()); - // TODO this needs to change - serialization needs to happen in the StateRepo not here, though we need the hashcode for server side comparison. - encoder.byteBuffer(KEY_FIELD, wrap(Util.marshall(message.getKey()))); - encoder.byteBuffer(VALUE_FIELD, wrap(Util.marshall(message.getValue()))); - - return encoder.encode().array(); + return encodeMandatoryFields(PUT_IF_ABSENT_MESSAGE_STRUCT, message) + .string(SERVER_STORE_NAME_FIELD, message.getCacheId()) + .string(MAP_ID_FIELD, message.getCacheId()) + // TODO this needs to change - serialization needs to happen in the StateRepo not here, though we need the hashcode for server side comparison. + .byteBuffer(KEY_FIELD, wrap(Util.marshall(message.getKey()))) + .byteBuffer(VALUE_FIELD, wrap(Util.marshall(message.getValue()))) + .encode().array(); } private byte[] encodeGetMessage(StateRepositoryOpMessage.GetMessage message) { - StructEncoder encoder = GET_MESSAGE_STRUCT.encoder(); - - messageCodecUtils.encodeMandatoryFields(encoder, message); - encoder.string(SERVER_STORE_NAME_FIELD, message.getCacheId()); - encoder.string(MAP_ID_FIELD, message.getCacheId()); - // TODO this needs to change - serialization needs to happen in the StateRepo not here, though we need the hashcode for server side comparison. - encoder.byteBuffer(KEY_FIELD, wrap(Util.marshall(message.getKey()))); - - return encoder.encode().array(); + return encodeMandatoryFields(GET_MESSAGE_STRUCT, message) + .string(SERVER_STORE_NAME_FIELD, message.getCacheId()) + .string(MAP_ID_FIELD, message.getCacheId()) + // TODO this needs to change - serialization needs to happen in the StateRepo not here, though we need the hashcode for server side comparison. + .byteBuffer(KEY_FIELD, wrap(Util.marshall(message.getKey()))) + .encode().array(); } public StateRepositoryOpMessage decode(EhcacheMessageType messageType, ByteBuffer messageBuffer) { diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/ClusterTierEntityConfiguration.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/ClusterTierEntityConfiguration.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/ClusterTierEntityConfiguration.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/ClusterTierEntityConfiguration.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/CustomLoaderBasedObjectInputStream.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/CustomLoaderBasedObjectInputStream.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/CustomLoaderBasedObjectInputStream.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/CustomLoaderBasedObjectInputStream.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/FilteredObjectInputStream.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/FilteredObjectInputStream.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/FilteredObjectInputStream.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/FilteredObjectInputStream.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/SequencedElement.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/SequencedElement.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/SequencedElement.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/SequencedElement.java diff --git a/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/Util.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/Util.java new file mode 100644 index 0000000000..a1ee21c176 --- /dev/null +++ b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/Util.java @@ -0,0 +1,53 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.clustered.common.internal.store; + +import org.ehcache.clustered.common.internal.util.ByteBufferInputStream; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.function.Predicate; + +public class Util { + + public static Object unmarshall(ByteBuffer payload, Predicate> isClassPermitted) { + try (ObjectInputStream objectInputStream = + new FilteredObjectInputStream(new ByteBufferInputStream(payload), isClassPermitted, null)) { + return objectInputStream.readObject(); + } catch (IOException | ClassNotFoundException ex) { + throw new IllegalArgumentException(ex); + } + } + + public static byte[] marshall(Object message) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try(ObjectOutputStream oout = new ObjectOutputStream(out)) { + oout.writeObject(message); + } catch (IOException e) { + throw new IllegalArgumentException(e); + } + return out.toByteArray(); + } +} diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/operations/BaseKeyValueOperation.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/BaseKeyValueOperation.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/operations/BaseKeyValueOperation.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/BaseKeyValueOperation.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/operations/ConditionalRemoveOperation.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/ConditionalRemoveOperation.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/operations/ConditionalRemoveOperation.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/ConditionalRemoveOperation.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/operations/ConditionalReplaceOperation.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/ConditionalReplaceOperation.java similarity index 99% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/operations/ConditionalReplaceOperation.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/ConditionalReplaceOperation.java index e38cf833b0..caac548cca 100644 --- a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/operations/ConditionalReplaceOperation.java +++ b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/ConditionalReplaceOperation.java @@ -80,7 +80,7 @@ public K getKey() { return key; } - V getOldValue() { + public V getOldValue() { return this.oldValueHolder.getValue(); } diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/operations/LazyValueHolder.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/LazyValueHolder.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/operations/LazyValueHolder.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/LazyValueHolder.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/operations/Operation.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/Operation.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/operations/Operation.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/Operation.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/operations/OperationCode.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/OperationCode.java similarity index 92% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/operations/OperationCode.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/OperationCode.java index 4ee3277644..7515a387b7 100644 --- a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/operations/OperationCode.java +++ b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/OperationCode.java @@ -68,6 +68,12 @@ public Operation decode(final ByteBuffer buffer, final Serializer Operation decode(ByteBuffer buffer, Serializer keySerializer, Serializer valueSerializer) { return new PutWithWriterOperation<>(buffer, keySerializer, valueSerializer); } + }, + TIMESTAMP((byte)8) { + @Override + public Operation decode(ByteBuffer buffer, Serializer keySerializer, Serializer valueSerializer) { + return new TimestampOperation<>(buffer, keySerializer); + } }; private final byte value; @@ -102,6 +108,8 @@ public static OperationCode valueOf(byte value) { return REPLACE_CONDITIONAL; case 7: return PUT_WITH_WRITER; + case 8: + return TIMESTAMP; default: throw new IllegalArgumentException("Operation undefined for the value " + value); } diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/operations/PutIfAbsentOperation.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/PutIfAbsentOperation.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/operations/PutIfAbsentOperation.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/PutIfAbsentOperation.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/operations/PutOperation.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/PutOperation.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/operations/PutOperation.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/PutOperation.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/operations/PutWithWriterOperation.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/PutWithWriterOperation.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/operations/PutWithWriterOperation.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/PutWithWriterOperation.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/operations/RemoveOperation.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/RemoveOperation.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/operations/RemoveOperation.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/RemoveOperation.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/operations/ReplaceOperation.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/ReplaceOperation.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/operations/ReplaceOperation.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/ReplaceOperation.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/operations/Result.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/Result.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/operations/Result.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/Result.java diff --git a/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/TimestampOperation.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/TimestampOperation.java new file mode 100644 index 0000000000..ad34af91b0 --- /dev/null +++ b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/TimestampOperation.java @@ -0,0 +1,138 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.clustered.common.internal.store.operations; + +import org.ehcache.clustered.common.internal.store.operations.codecs.CodecException; +import org.ehcache.spi.serialization.Serializer; + +import java.nio.ByteBuffer; + +import static java.util.Objects.requireNonNull; + +public class TimestampOperation implements Operation { + + private final K key; + private final long timeStamp; + + public TimestampOperation(final K key, final long timeStamp) { + this.key = requireNonNull(key); + this.timeStamp = timeStamp; + } + + TimestampOperation(final ByteBuffer buffer, final Serializer keySerializer) { + OperationCode opCode = OperationCode.valueOf(buffer.get()); + if (opCode != getOpCode()) { + throw new IllegalArgumentException("Invalid operation: " + opCode); + } + this.timeStamp = buffer.getLong(); + ByteBuffer keyBlob = buffer.slice(); + try { + this.key = keySerializer.read(keyBlob); + } catch (ClassNotFoundException e) { + throw new CodecException(e); + } + } + + public K getKey() { + return key; + } + + @Override + public OperationCode getOpCode() { + return OperationCode.TIMESTAMP; + } + + /** + * Timestamp operation is a no-op - it only exists to establish a wall-time in the chain. + */ + @Override + public Result apply(final Result previousOperation) { + return previousOperation; + } + + @Override + public ByteBuffer encode(final Serializer keySerializer, final Serializer valueSerializer) { + ByteBuffer keyBuf = keySerializer.serialize(key); + + int size = BYTE_SIZE_BYTES + // Operation type + LONG_SIZE_BYTES + // Size of expiration time stamp + keyBuf.remaining(); // the key payload itself + + ByteBuffer buffer = ByteBuffer.allocate(size); + buffer.put(getOpCode().getValue()); + buffer.putLong(this.timeStamp); + buffer.put(keyBuf); + buffer.flip(); + return buffer; + } + + @Override + public String toString() { + return "{" + getOpCode() + "# key: " + key + "}"; + } + + @Override + public boolean equals(final Object obj) { + if(obj == null) { + return false; + } + if(!(obj instanceof TimestampOperation)) { + return false; + } + + @SuppressWarnings("unchecked") + TimestampOperation other = (TimestampOperation) obj; + if(this.getOpCode() != other.getOpCode()) { + return false; + } + if(!this.getKey().equals(other.getKey())) { + return false; + } + return true; + } + + @Override + public int hashCode() { + int hash = getOpCode().hashCode(); + hash = hash * 31 + key.hashCode(); + return hash; + } + + @Override + public long timeStamp() { + if (!isExpiryAvailable()) { + return this.timeStamp; + } else { + throw new RuntimeException("Timestamp not available"); + } + } + + @Override + public boolean isExpiryAvailable() { + return timeStamp < 0; + } + + @Override + public long expirationTime() { + if (isExpiryAvailable()) { + return - this.timeStamp; + } else { + throw new RuntimeException("Expiry not available"); + } + } + +} diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/operations/codecs/CodecException.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/codecs/CodecException.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/operations/codecs/CodecException.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/codecs/CodecException.java diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/operations/codecs/OperationsCodec.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/codecs/OperationsCodec.java similarity index 96% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/operations/codecs/OperationsCodec.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/codecs/OperationsCodec.java index 4ce754ae55..bc4a48868f 100644 --- a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/store/operations/codecs/OperationsCodec.java +++ b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/store/operations/codecs/OperationsCodec.java @@ -38,12 +38,13 @@ public ByteBuffer encode(Operation operation) { } public static OperationCode getOperationCode(ByteBuffer buffer) { - return OperationCode.valueOf(buffer.duplicate().get()); + OperationCode opCode = OperationCode.valueOf(buffer.get()); + buffer.rewind(); + return opCode; } public Operation decode(ByteBuffer buffer) { - OperationCode opCode = OperationCode.valueOf(buffer.get()); - buffer.rewind(); + OperationCode opCode = getOperationCode(buffer); return opCode.decode(buffer, keySerializer, valueSerializer); } diff --git a/clustered/common/src/main/java/org/ehcache/clustered/common/internal/util/ByteBufferInputStream.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/util/ByteBufferInputStream.java similarity index 100% rename from clustered/common/src/main/java/org/ehcache/clustered/common/internal/util/ByteBufferInputStream.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/util/ByteBufferInputStream.java diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ChainBuilder.java b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/util/ChainBuilder.java similarity index 58% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ChainBuilder.java rename to clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/util/ChainBuilder.java index 00aff2d0c6..0dd9ff8f0a 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ChainBuilder.java +++ b/clustered/ehcache-common/src/main/java/org/ehcache/clustered/common/internal/util/ChainBuilder.java @@ -13,15 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.ehcache.clustered.client.internal.store; +package org.ehcache.clustered.common.internal.util; import org.ehcache.clustered.common.internal.store.Chain; import org.ehcache.clustered.common.internal.store.Element; -import org.ehcache.clustered.common.internal.store.Util; import java.nio.ByteBuffer; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Iterator; import java.util.List; /** @@ -31,25 +30,39 @@ public class ChainBuilder { private List buffers = new ArrayList<>(); - public ChainBuilder() { + public ChainBuilder add(final ByteBuffer payload) { + buffers.add(payload); + return this; } - private ChainBuilder(List buffers) { - this.buffers = buffers; + public Chain build() { + List elements = new ArrayList<>(); + for (final ByteBuffer buffer : buffers) { + elements.add(buffer::asReadOnlyBuffer); + } + return chainFromList(elements); } - //TODO: optimize this & make this mutable - public ChainBuilder add(final ByteBuffer payload) { - List newList = new ArrayList<>(buffers.size() + 1); - newList.addAll(buffers); - newList.add(payload); - return new ChainBuilder(newList); + public int length() { + return buffers.size(); } - public Chain build() { - ByteBuffer[] elements = new ByteBuffer[buffers.size()]; - buffers.toArray(elements); - return Util.getChain(false, elements); - } + public static Chain chainFromList(List elements) { + return new Chain() { + @Override + public boolean isEmpty() { + return elements.isEmpty(); + } + + @Override + public int length() { + return elements.size(); + } + @Override + public Iterator iterator() { + return elements.iterator(); + } + }; + } } diff --git a/clustered/common/src/test/java/org/ehcache/clustered/common/internal/exceptions/BaseClusteredEhcacheExceptionTest.java b/clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/exceptions/BaseClusteredEhcacheExceptionTest.java similarity index 99% rename from clustered/common/src/test/java/org/ehcache/clustered/common/internal/exceptions/BaseClusteredEhcacheExceptionTest.java rename to clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/exceptions/BaseClusteredEhcacheExceptionTest.java index 8c5b68f8e1..408a265d5f 100644 --- a/clustered/common/src/test/java/org/ehcache/clustered/common/internal/exceptions/BaseClusteredEhcacheExceptionTest.java +++ b/clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/exceptions/BaseClusteredEhcacheExceptionTest.java @@ -22,12 +22,12 @@ import java.lang.reflect.Constructor; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.typeCompatibleWith; -import static org.junit.Assert.assertThat; /** * Foundation for tests on {@link ClusterException} subclasses. diff --git a/clustered/common/src/test/java/org/ehcache/clustered/common/internal/exceptions/IllegalMessageExceptionTest.java b/clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/exceptions/IllegalMessageExceptionTest.java similarity index 100% rename from clustered/common/src/test/java/org/ehcache/clustered/common/internal/exceptions/IllegalMessageExceptionTest.java rename to clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/exceptions/IllegalMessageExceptionTest.java diff --git a/clustered/common/src/test/java/org/ehcache/clustered/common/internal/exceptions/InvalidOperationExceptionTest.java b/clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/exceptions/InvalidOperationExceptionTest.java similarity index 100% rename from clustered/common/src/test/java/org/ehcache/clustered/common/internal/exceptions/InvalidOperationExceptionTest.java rename to clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/exceptions/InvalidOperationExceptionTest.java diff --git a/clustered/common/src/test/java/org/ehcache/clustered/common/internal/exceptions/InvalidServerSideConfigurationExceptionTest.java b/clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/exceptions/InvalidServerSideConfigurationExceptionTest.java similarity index 100% rename from clustered/common/src/test/java/org/ehcache/clustered/common/internal/exceptions/InvalidServerSideConfigurationExceptionTest.java rename to clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/exceptions/InvalidServerSideConfigurationExceptionTest.java diff --git a/clustered/common/src/test/java/org/ehcache/clustered/common/internal/exceptions/InvalidServerStoreConfigurationExceptionTest.java b/clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/exceptions/InvalidServerStoreConfigurationExceptionTest.java similarity index 100% rename from clustered/common/src/test/java/org/ehcache/clustered/common/internal/exceptions/InvalidServerStoreConfigurationExceptionTest.java rename to clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/exceptions/InvalidServerStoreConfigurationExceptionTest.java diff --git a/clustered/common/src/test/java/org/ehcache/clustered/common/internal/exceptions/InvalidStoreExceptionTest.java b/clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/exceptions/InvalidStoreExceptionTest.java similarity index 100% rename from clustered/common/src/test/java/org/ehcache/clustered/common/internal/exceptions/InvalidStoreExceptionTest.java rename to clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/exceptions/InvalidStoreExceptionTest.java diff --git a/clustered/common/src/test/java/org/ehcache/clustered/common/internal/exceptions/LifecycleExceptionTest.java b/clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/exceptions/LifecycleExceptionTest.java similarity index 100% rename from clustered/common/src/test/java/org/ehcache/clustered/common/internal/exceptions/LifecycleExceptionTest.java rename to clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/exceptions/LifecycleExceptionTest.java diff --git a/clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/messages/ChainCodecTest.java b/clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/messages/ChainCodecTest.java new file mode 100644 index 0000000000..7fa0a7b0ba --- /dev/null +++ b/clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/messages/ChainCodecTest.java @@ -0,0 +1,187 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.clustered.common.internal.messages; + +import org.ehcache.clustered.common.internal.store.Chain; +import org.ehcache.clustered.common.internal.store.Element; +import org.junit.Test; +import org.terracotta.runnel.encoding.StructEncoder; + +import java.nio.ByteBuffer; +import java.util.AbstractMap.SimpleImmutableEntry; +import java.util.Iterator; +import java.util.Map; + +import static org.ehcache.clustered.ChainUtils.chainOf; +import static org.ehcache.clustered.ChainUtils.createPayload; +import static org.ehcache.clustered.ChainUtils.readPayload; +import static org.ehcache.clustered.ChainUtils.sequencedChainOf; +import static org.ehcache.clustered.Matchers.hasPayloads; +import static org.ehcache.clustered.Matchers.sameSequenceAs; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +public class ChainCodecTest { + + @Test + public void testChainWithSingleElement() { + Chain chain = chainOf(createPayload(1L)); + + assertThat(chain.isEmpty(), is(false)); + Iterator chainIterator = chain.iterator(); + assertThat(readPayload(chainIterator.next().getPayload()), is(1L)); + assertThat(chainIterator.hasNext(), is(false)); + + Chain decoded = ChainCodec.decodeChain(ChainCodec.encodeChain(chain)); + + assertThat(decoded.isEmpty(), is(false)); + chainIterator = decoded.iterator(); + assertThat(readPayload(chainIterator.next().getPayload()), is(1L)); + assertThat(chainIterator.hasNext(), is(false)); + } + + @Test + public void testChainWithSingleSequencedElement() { + Chain chain = sequencedChainOf(createPayload(1L)); + + assertThat(chain.isEmpty(), is(false)); + Iterator chainIterator = chain.iterator(); + assertThat(readPayload(chainIterator.next().getPayload()), is(1L)); + assertThat(chainIterator.hasNext(), is(false)); + + Chain decoded = ChainCodec.decodeChain(ChainCodec.encodeChain(chain)); + + assertThat(decoded.isEmpty(), is(false)); + chainIterator = decoded.iterator(); + assertThat(readPayload(chainIterator.next().getPayload()), is(1L)); + assertThat(chainIterator.hasNext(), is(false)); + + assertThat(decoded, sameSequenceAs(chain)); + } + + @Test + public void testChainWithMultipleElements() { + Chain chain = chainOf(createPayload(1L), createPayload(2L), createPayload(3L)); + + assertThat(chain.isEmpty(), is(false)); + assertThat(chain, hasPayloads(1L, 2L, 3L)); + + Chain decoded = ChainCodec.decodeChain(ChainCodec.encodeChain(chain)); + + assertThat(decoded.isEmpty(), is(false)); + assertThat(decoded, hasPayloads(1L, 2L, 3L)); + } + + @Test + public void testChainWithMultipleSequencedElements() { + Chain chain = sequencedChainOf(createPayload(1L), createPayload(2L), createPayload(3L)); + + assertThat(chain.isEmpty(), is(false)); + assertThat(chain, hasPayloads(1L, 2L, 3L)); + + Chain decoded = ChainCodec.decodeChain(ChainCodec.encodeChain(chain)); + + assertThat(decoded.isEmpty(), is(false)); + assertThat(decoded, hasPayloads(1L, 2L, 3L)); + + assertThat(decoded, sameSequenceAs(chain)); + } + + @Test + public void testEmptyChain() { + Chain decoded = ChainCodec.decodeChain(ChainCodec.encodeChain(chainOf())); + + assertThat(decoded.isEmpty(), is(true)); + } + + @Test + public void testChainEntryWithSingleElement() { + SimpleImmutableEntry entry = new SimpleImmutableEntry<>(42L, chainOf(createPayload(1L))); + StructEncoder encoder = ChainCodec.CHAIN_ENTRY_STRUCT.encoder(); + ChainCodec.encodeChainEntry(encoder, entry); + + Map.Entry decoded = ChainCodec.decodeChainEntry(ChainCodec.CHAIN_ENTRY_STRUCT.decoder((ByteBuffer) encoder.encode().flip())); + + + assertThat(decoded.getKey(), is(42L)); + assertThat(decoded.getValue().isEmpty(), is(false)); + Iterator chainIterator = decoded.getValue().iterator(); + assertThat(readPayload(chainIterator.next().getPayload()), is(1L)); + assertThat(chainIterator.hasNext(), is(false)); + } + + @Test + public void testChainEntryWithSingleSequencedElement() { + Chain chain = sequencedChainOf(createPayload(1L)); + SimpleImmutableEntry entry = new SimpleImmutableEntry<>(43L, chain); + StructEncoder encoder = ChainCodec.CHAIN_ENTRY_STRUCT.encoder(); + ChainCodec.encodeChainEntry(encoder, entry); + + Map.Entry decoded = ChainCodec.decodeChainEntry(ChainCodec.CHAIN_ENTRY_STRUCT.decoder((ByteBuffer) encoder.encode().flip())); + + assertThat(decoded.getKey(), is(43L)); + assertThat(decoded.getValue().isEmpty(), is(false)); + Iterator chainIterator = decoded.getValue().iterator(); + assertThat(readPayload(chainIterator.next().getPayload()), is(1L)); + assertThat(chainIterator.hasNext(), is(false)); + + assertThat(decoded.getValue(), sameSequenceAs(chain)); + } + + @Test + public void testChainEntryWithMultipleElements() { + Chain chain = chainOf(createPayload(1L), createPayload(2L), createPayload(3L)); + SimpleImmutableEntry entry = new SimpleImmutableEntry<>(44L, chain); + StructEncoder encoder = ChainCodec.CHAIN_ENTRY_STRUCT.encoder(); + ChainCodec.encodeChainEntry(encoder, entry); + + Map.Entry decoded = ChainCodec.decodeChainEntry(ChainCodec.CHAIN_ENTRY_STRUCT.decoder((ByteBuffer) encoder.encode().flip())); + + assertThat(decoded.getKey(), is(44L)); + assertThat(decoded.getValue().isEmpty(), is(false)); + assertThat(decoded.getValue(), hasPayloads(1L, 2L, 3L)); + } + + @Test + public void testChainEntryWithMultipleSequencedElements() { + Chain chain = sequencedChainOf(createPayload(1L), createPayload(2L), createPayload(3L)); + SimpleImmutableEntry entry = new SimpleImmutableEntry<>(45L, chain); + StructEncoder encoder = ChainCodec.CHAIN_ENTRY_STRUCT.encoder(); + ChainCodec.encodeChainEntry(encoder, entry); + + Map.Entry decoded = ChainCodec.decodeChainEntry(ChainCodec.CHAIN_ENTRY_STRUCT.decoder((ByteBuffer) encoder.encode().flip())); + + assertThat(decoded.getKey(), is(45L)); + assertThat(decoded.getValue().isEmpty(), is(false)); + assertThat(decoded.getValue(), hasPayloads(1L, 2L, 3L)); + + assertThat(decoded.getValue(), sameSequenceAs(chain)); + } + + @Test + public void testEmptyChainEntry() { + Chain chain = chainOf(); + SimpleImmutableEntry entry = new SimpleImmutableEntry<>(46L, chain); + StructEncoder encoder = ChainCodec.CHAIN_ENTRY_STRUCT.encoder(); + ChainCodec.encodeChainEntry(encoder, entry); + + Map.Entry decoded = ChainCodec.decodeChainEntry(ChainCodec.CHAIN_ENTRY_STRUCT.decoder((ByteBuffer) encoder.encode().flip())); + + assertThat(decoded.getKey(), is(46L)); + assertThat(decoded.getValue().isEmpty(), is(true)); + } +} diff --git a/clustered/common/src/test/java/org/ehcache/clustered/common/internal/messages/CommonConfigCodecTest.java b/clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/messages/CommonConfigCodecTest.java similarity index 59% rename from clustered/common/src/test/java/org/ehcache/clustered/common/internal/messages/CommonConfigCodecTest.java rename to clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/messages/CommonConfigCodecTest.java index 879960b2af..5732cda349 100644 --- a/clustered/common/src/test/java/org/ehcache/clustered/common/internal/messages/CommonConfigCodecTest.java +++ b/clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/messages/CommonConfigCodecTest.java @@ -22,6 +22,7 @@ import org.ehcache.clustered.common.internal.ServerStoreConfiguration; import org.junit.Test; import org.mockito.Mockito; +import org.terracotta.runnel.EnumMapping; import org.terracotta.runnel.Struct; import org.terracotta.runnel.StructBuilder; import org.terracotta.runnel.encoding.StructEncoder; @@ -29,10 +30,12 @@ import java.nio.ByteBuffer; import java.util.Collections; +import static org.ehcache.clustered.common.internal.messages.MessageCodecUtils.SERVER_STORE_NAME_FIELD; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.*; import static org.mockito.Mockito.mock; +import static org.terracotta.runnel.EnumMappingBuilder.newEnumMappingBuilder; import static org.terracotta.runnel.StructBuilder.newStructBuilder; public class CommonConfigCodecTest { @@ -72,4 +75,49 @@ public void testInjectServerStoreConfiguration() { } + @Test + public void testDecodeNonLoaderWriterServerStoreConfiguration() { + EnumMapping consistencyEnumMapping = newEnumMappingBuilder(Consistency.class) + .mapping(Consistency.EVENTUAL, 1) + .mapping(Consistency.STRONG, 2) + .build(); + int index = 30; + StructBuilder builder = newStructBuilder() + .string("identifier", 10) + .string(SERVER_STORE_NAME_FIELD, 20) + .string("keyType", index) + .string("keySerializerType", index + 10) + .string("valueType", index + 11) + .string("valueSerializerType", index + 15) + .enm("consistency", index + 16, consistencyEnumMapping) + .int64("poolSize", index + 20) + .string("resourceName", index + 30); + + Struct struct = builder.build(); + + ByteBuffer encodedStoreConfig = struct.encoder() + .string("identifier", "test") + .string(SERVER_STORE_NAME_FIELD, "testStore") + .string("keyType", "Long") + .string("keySerializerType", "Long") + .string("valueType", "Long") + .string("valueSerializerType", "Long") + .enm("consistency", Consistency.STRONG) + .int64("poolSize", 20) + .string("resourceName", "primary").encode(); + + Struct newStruct = CODEC.injectServerStoreConfiguration(newStructBuilder() + .string("identifier", 10) + .string(SERVER_STORE_NAME_FIELD, 20), index) + .getUpdatedBuilder() + .build(); + encodedStoreConfig.flip(); + ServerStoreConfiguration serverStoreConfiguration = + CODEC.decodeServerStoreConfiguration(newStruct.decoder(encodedStoreConfig)); + + assertThat(serverStoreConfiguration.isLoaderWriterConfigured(), is(false)); + assertThat(serverStoreConfiguration.isWriteBehindConfigured(), is(false)); + + } + } diff --git a/clustered/common/src/test/java/org/ehcache/clustered/common/internal/messages/EhcacheCodecTest.java b/clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/messages/EhcacheCodecTest.java similarity index 100% rename from clustered/common/src/test/java/org/ehcache/clustered/common/internal/messages/EhcacheCodecTest.java rename to clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/messages/EhcacheCodecTest.java diff --git a/clustered/common/src/test/java/org/ehcache/clustered/common/internal/messages/LifeCycleMessageCodecTest.java b/clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/messages/LifeCycleMessageCodecTest.java similarity index 99% rename from clustered/common/src/test/java/org/ehcache/clustered/common/internal/messages/LifeCycleMessageCodecTest.java rename to clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/messages/LifeCycleMessageCodecTest.java index f031e0a896..7c1e2a98bb 100644 --- a/clustered/common/src/test/java/org/ehcache/clustered/common/internal/messages/LifeCycleMessageCodecTest.java +++ b/clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/messages/LifeCycleMessageCodecTest.java @@ -26,9 +26,9 @@ import java.util.Collections; import static java.nio.ByteBuffer.wrap; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; /** * LifeCycleMessageCodecTest diff --git a/clustered/common/src/test/java/org/ehcache/clustered/common/internal/messages/ReconnectMessageCodecTest.java b/clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/messages/ReconnectMessageCodecTest.java similarity index 96% rename from clustered/common/src/test/java/org/ehcache/clustered/common/internal/messages/ReconnectMessageCodecTest.java rename to clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/messages/ReconnectMessageCodecTest.java index d1b336fd53..26955fe38a 100644 --- a/clustered/common/src/test/java/org/ehcache/clustered/common/internal/messages/ReconnectMessageCodecTest.java +++ b/clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/messages/ReconnectMessageCodecTest.java @@ -22,10 +22,10 @@ import java.util.HashSet; import java.util.Set; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; -import static org.junit.Assert.assertThat; public class ReconnectMessageCodecTest { @@ -39,7 +39,7 @@ public void setUp() { @Test public void testClusterTierReconnectCodec() { - ClusterTierReconnectMessage reconnectMessage = new ClusterTierReconnectMessage(); + ClusterTierReconnectMessage reconnectMessage = new ClusterTierReconnectMessage(false); Set setToInvalidate = new HashSet<>(); setToInvalidate.add(1L); diff --git a/clustered/common/src/test/java/org/ehcache/clustered/common/internal/messages/ResponseCodecTest.java b/clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/messages/ResponseCodecTest.java similarity index 69% rename from clustered/common/src/test/java/org/ehcache/clustered/common/internal/messages/ResponseCodecTest.java rename to clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/messages/ResponseCodecTest.java index 32182d3bb3..967a263def 100644 --- a/clustered/common/src/test/java/org/ehcache/clustered/common/internal/messages/ResponseCodecTest.java +++ b/clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/messages/ResponseCodecTest.java @@ -21,10 +21,17 @@ import org.hamcrest.Matchers; import org.junit.Test; -import java.util.Date; +import java.util.AbstractMap; import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.UUID; +import static java.util.Arrays.asList; +import static org.ehcache.clustered.ChainUtils.chainOf; +import static org.ehcache.clustered.ChainUtils.createPayload; +import static org.ehcache.clustered.Matchers.hasPayloads; import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.allInvalidationDone; import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.clientInvalidateAll; import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.clientInvalidateHash; @@ -35,11 +42,10 @@ import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.prepareForDestroy; import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.serverInvalidateHash; import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.success; -import static org.ehcache.clustered.common.internal.store.Util.createPayload; -import static org.ehcache.clustered.common.internal.store.Util.getChain; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.Matchers.nullValue; public class ResponseCodecTest { @@ -58,14 +64,13 @@ public void testFailureResponseCodec() { @Test public void testGetResponseCodec() { - EhcacheEntityResponse getResponse = getResponse(getChain(false, - createPayload(1L), createPayload(11L), createPayload(111L))); + EhcacheEntityResponse getResponse = getResponse(chainOf(createPayload(1L), createPayload(11L), createPayload(111L))); EhcacheEntityResponse decoded = RESPONSE_CODEC.decode(RESPONSE_CODEC.encode(getResponse)); Chain decodedChain = ((EhcacheEntityResponse.GetResponse) decoded).getChain(); - Util.assertChainHas(decodedChain, 1L, 11L, 111L); + assertThat(decodedChain, hasPayloads(1L, 11L, 111L)); } @Test @@ -126,14 +131,27 @@ public void testClientInvalidateAll() throws Exception { } @Test - public void testServerInvalidateHash() throws Exception { - EhcacheEntityResponse.ServerInvalidateHash response = serverInvalidateHash(KEY); + public void testServerInvalidateHash_withEvictedChain() { + EhcacheEntityResponse.ServerInvalidateHash response = serverInvalidateHash(KEY, chainOf(createPayload(1L), createPayload(11L), createPayload(111L))); byte[] encoded = RESPONSE_CODEC.encode(response); EhcacheEntityResponse.ServerInvalidateHash decodedResponse = (EhcacheEntityResponse.ServerInvalidateHash) RESPONSE_CODEC.decode(encoded); assertThat(decodedResponse.getResponseType(), is(EhcacheResponseType.SERVER_INVALIDATE_HASH)); assertThat(decodedResponse.getKey(), is(KEY)); + assertThat(decodedResponse.getEvictedChain(), hasPayloads(1L, 11L, 111L)); + } + + @Test + public void testServerInvalidateHash_withoutEvictedChain() { + EhcacheEntityResponse.ServerInvalidateHash response = serverInvalidateHash(KEY, null); + + byte[] encoded = RESPONSE_CODEC.encode(response); + EhcacheEntityResponse.ServerInvalidateHash decodedResponse = (EhcacheEntityResponse.ServerInvalidateHash) RESPONSE_CODEC.decode(encoded); + + assertThat(decodedResponse.getResponseType(), is(EhcacheResponseType.SERVER_INVALIDATE_HASH)); + assertThat(decodedResponse.getKey(), is(KEY)); + assertThat(decodedResponse.getEvictedChain(), is(nullValue())); } @Test @@ -153,26 +171,25 @@ public void testPrepareForDestroy() throws Exception { @Test public void testResolveRequest() throws Exception { long hash = 42L; - EhcacheEntityResponse.ResolveRequest response = new EhcacheEntityResponse.ResolveRequest(hash, getChain(false, - createPayload(1L), createPayload(11L), createPayload(111L))); + EhcacheEntityResponse.ResolveRequest response = new EhcacheEntityResponse.ResolveRequest(hash, chainOf(createPayload(1L), createPayload(11L), createPayload(111L))); byte[] encoded = RESPONSE_CODEC.encode(response); EhcacheEntityResponse.ResolveRequest decodedResponse = (EhcacheEntityResponse.ResolveRequest) RESPONSE_CODEC.decode(encoded); assertThat(decodedResponse.getResponseType(), is(EhcacheResponseType.RESOLVE_REQUEST)); assertThat(decodedResponse.getKey(), is(42L)); - Util.assertChainHas(decodedResponse.getChain(), 1L, 11L, 111L); + assertThat(decodedResponse.getChain(), hasPayloads(1L, 11L, 111L)); } @Test public void testLockResponse() { - EhcacheEntityResponse.LockSuccess lockSuccess = new EhcacheEntityResponse.LockSuccess(getChain(false, createPayload(1L), createPayload(10L))); + EhcacheEntityResponse.LockSuccess lockSuccess = new EhcacheEntityResponse.LockSuccess(chainOf(createPayload(1L), createPayload(10L))); byte[] sucessEncoded = RESPONSE_CODEC.encode(lockSuccess); EhcacheEntityResponse.LockSuccess successDecoded = (EhcacheEntityResponse.LockSuccess) RESPONSE_CODEC.decode(sucessEncoded); assertThat(successDecoded.getResponseType(), is(EhcacheResponseType.LOCK_SUCCESS)); - Util.assertChainHas(successDecoded.getChain(), 1L, 10L); + assertThat(successDecoded.getChain(), hasPayloads(1L, 10L)); EhcacheEntityResponse.LockFailure lockFailure = EhcacheEntityResponse.lockFailure(); byte[] failureEncoded = RESPONSE_CODEC.encode(lockFailure); @@ -180,4 +197,34 @@ public void testLockResponse() { assertThat(failureDecoded.getResponseType(), is(EhcacheResponseType.LOCK_FAILURE)); } + + @Test + public void testIteratorBatchResponse() { + UUID uuid = UUID.randomUUID(); + List> chains = asList( + new AbstractMap.SimpleImmutableEntry<>(1L, chainOf(createPayload(1L), createPayload(10L))), + new AbstractMap.SimpleImmutableEntry<>(2L, chainOf(createPayload(2L), createPayload(20L)))); + EhcacheEntityResponse.IteratorBatch iteratorBatch = EhcacheEntityResponse.iteratorBatchResponse(uuid, chains, true); + + byte[] encoded = RESPONSE_CODEC.encode(iteratorBatch); + EhcacheEntityResponse.IteratorBatch batchDecoded = (EhcacheEntityResponse.IteratorBatch) RESPONSE_CODEC.decode(encoded); + + assertThat(batchDecoded.getResponseType(), is(EhcacheResponseType.ITERATOR_BATCH)); + assertThat(batchDecoded.getIdentity(), is(uuid)); + assertThat(batchDecoded.getChains().get(0).getValue(), hasPayloads(1L, 10L)); + assertThat(batchDecoded.getChains().get(1).getValue(), hasPayloads(2L, 20L)); + assertThat(batchDecoded.isLast(), is(true)); + } + + @Test + public void testServerAppendResponse() { + EhcacheEntityResponse.ServerAppend serverAppend = new EhcacheEntityResponse.ServerAppend(createPayload(3L), chainOf(createPayload(1L), createPayload(2L))); + + byte[] encoded = RESPONSE_CODEC.encode(serverAppend); + EhcacheEntityResponse.ServerAppend appendDecoded = (EhcacheEntityResponse.ServerAppend) RESPONSE_CODEC.decode(encoded); + + assertThat(appendDecoded.getResponseType(), is(EhcacheResponseType.SERVER_APPEND)); + assertThat(appendDecoded.getAppended().asLongBuffer().get(), is(3L)); + assertThat(appendDecoded.getBeforeAppend(), hasPayloads(1L, 2L)); + } } diff --git a/clustered/common/src/test/java/org/ehcache/clustered/common/internal/messages/ServerStoreOpCodecTest.java b/clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/messages/ServerStoreOpCodecTest.java similarity index 64% rename from clustered/common/src/test/java/org/ehcache/clustered/common/internal/messages/ServerStoreOpCodecTest.java rename to clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/messages/ServerStoreOpCodecTest.java index e6c6c1d3bd..2b25c1495f 100644 --- a/clustered/common/src/test/java/org/ehcache/clustered/common/internal/messages/ServerStoreOpCodecTest.java +++ b/clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/messages/ServerStoreOpCodecTest.java @@ -18,12 +18,16 @@ import org.junit.Test; +import java.util.UUID; + import static java.nio.ByteBuffer.wrap; -import static org.ehcache.clustered.common.internal.store.Util.createPayload; -import static org.ehcache.clustered.common.internal.store.Util.getChain; -import static org.ehcache.clustered.common.internal.store.Util.readPayLoad; +import static org.ehcache.clustered.ChainUtils.chainOf; +import static org.ehcache.clustered.ChainUtils.createPayload; +import static org.ehcache.clustered.ChainUtils.readPayload; +import static org.ehcache.clustered.ChainUtils.sequencedChainOf; +import static org.ehcache.clustered.Matchers.hasPayloads; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; public class ServerStoreOpCodecTest { @@ -39,7 +43,7 @@ public void testAppendMessageCodec() { ServerStoreOpMessage.AppendMessage decodedAppendMessage = (ServerStoreOpMessage.AppendMessage) decodedMsg; assertThat(decodedAppendMessage.getKey(), is(1L)); - assertThat(readPayLoad(decodedAppendMessage.getPayload()), is(1L)); + assertThat(readPayload(decodedAppendMessage.getPayload()), is(1L)); assertThat(decodedAppendMessage.getMessageType(), is(EhcacheMessageType.APPEND)); } @@ -64,23 +68,23 @@ public void testGetAndAppendMessageCodec() { ServerStoreOpMessage.GetAndAppendMessage decodedGetAndAppendMessage = (ServerStoreOpMessage.GetAndAppendMessage) decodedMsg; assertThat(decodedGetAndAppendMessage.getKey(), is(10L)); - assertThat(readPayLoad(decodedGetAndAppendMessage.getPayload()), is(10L)); + assertThat(readPayload(decodedGetAndAppendMessage.getPayload()), is(10L)); assertThat(decodedGetAndAppendMessage.getMessageType(), is(EhcacheMessageType.GET_AND_APPEND)); } @Test public void testReplaceAtHeadMessageCodec() { ServerStoreOpMessage replaceAtHeadMessage = new ServerStoreOpMessage.ReplaceAtHeadMessage(10L, - getChain(true, createPayload(10L), createPayload(100L), createPayload(1000L)), - getChain(false, createPayload(2000L))); + sequencedChainOf(createPayload(10L), createPayload(100L), createPayload(1000L)), + chainOf(createPayload(2000L))); byte[] encoded = STORE_OP_CODEC.encode(replaceAtHeadMessage); EhcacheEntityMessage decodedMsg = STORE_OP_CODEC.decode(replaceAtHeadMessage.getMessageType(), wrap(encoded)); ServerStoreOpMessage.ReplaceAtHeadMessage decodedReplaceAtHeadMessage = (ServerStoreOpMessage.ReplaceAtHeadMessage) decodedMsg; assertThat(decodedReplaceAtHeadMessage.getKey(), is(10L)); - Util.assertChainHas(decodedReplaceAtHeadMessage.getExpect(), 10L, 100L, 1000L); - Util.assertChainHas(decodedReplaceAtHeadMessage.getUpdate(), 2000L); + assertThat(decodedReplaceAtHeadMessage.getExpect(), hasPayloads(10L, 100L, 1000L)); + assertThat(decodedReplaceAtHeadMessage.getUpdate(), hasPayloads(2000L)); assertThat(decodedReplaceAtHeadMessage.getMessageType(), is(EhcacheMessageType.REPLACE)); } @@ -133,4 +137,50 @@ public void testUnlockMessage() throws Exception { assertThat(decodedLockMessage.getMessageType(), is(EhcacheMessageType.UNLOCK)); } + @Test + public void testIteratorOpenMessage() { + ServerStoreOpMessage iteratorOpenMessage = new ServerStoreOpMessage.IteratorOpenMessage(42); + + byte[] encoded = STORE_OP_CODEC.encode(iteratorOpenMessage); + ServerStoreOpMessage.IteratorOpenMessage decoded = (ServerStoreOpMessage.IteratorOpenMessage) STORE_OP_CODEC.decode(iteratorOpenMessage.getMessageType(), wrap(encoded)); + + assertThat(decoded.getMessageType(), is(EhcacheMessageType.ITERATOR_OPEN)); + assertThat(decoded.getBatchSize(), is(42)); + } + + @Test + public void testIteratorCloseMessage() { + UUID uuid = UUID.randomUUID(); + ServerStoreOpMessage iteratorCloseMessage = new ServerStoreOpMessage.IteratorCloseMessage(uuid); + + byte[] encoded = STORE_OP_CODEC.encode(iteratorCloseMessage); + ServerStoreOpMessage.IteratorCloseMessage decoded = (ServerStoreOpMessage.IteratorCloseMessage) STORE_OP_CODEC.decode(iteratorCloseMessage.getMessageType(), wrap(encoded)); + + assertThat(decoded.getMessageType(), is(EhcacheMessageType.ITERATOR_CLOSE)); + assertThat(decoded.getIdentity(), is(uuid)); + } + + @Test + public void testIteratorAdvanceMessage() { + UUID uuid = UUID.randomUUID(); + ServerStoreOpMessage iteratorAdvanceMessage = new ServerStoreOpMessage.IteratorAdvanceMessage(uuid, 42); + + byte[] encoded = STORE_OP_CODEC.encode(iteratorAdvanceMessage); + ServerStoreOpMessage.IteratorAdvanceMessage decoded = (ServerStoreOpMessage.IteratorAdvanceMessage) STORE_OP_CODEC.decode(iteratorAdvanceMessage.getMessageType(), wrap(encoded)); + + assertThat(decoded.getMessageType(), is(EhcacheMessageType.ITERATOR_ADVANCE)); + assertThat(decoded.getIdentity(), is(uuid)); + assertThat(decoded.getBatchSize(), is(42)); + } + + @Test + public void testEnableEventListenerMessage() { + ServerStoreOpMessage enableEventListenerMessage = new ServerStoreOpMessage.EnableEventListenerMessage(true); + + byte[] encoded = STORE_OP_CODEC.encode(enableEventListenerMessage); + ServerStoreOpMessage.EnableEventListenerMessage decoded = (ServerStoreOpMessage.EnableEventListenerMessage) STORE_OP_CODEC.decode(enableEventListenerMessage.getMessageType(), wrap(encoded)); + + assertThat(decoded.getMessageType(), is(EhcacheMessageType.ENABLE_EVENT_LISTENER)); + assertThat(decoded.isEnable(), is(true)); + } } diff --git a/clustered/common/src/test/java/org/ehcache/clustered/common/internal/messages/ServerStoreOpMessageTest.java b/clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/messages/ServerStoreOpMessageTest.java similarity index 81% rename from clustered/common/src/test/java/org/ehcache/clustered/common/internal/messages/ServerStoreOpMessageTest.java rename to clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/messages/ServerStoreOpMessageTest.java index 332b0c6b40..ce0c886ed3 100644 --- a/clustered/common/src/test/java/org/ehcache/clustered/common/internal/messages/ServerStoreOpMessageTest.java +++ b/clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/messages/ServerStoreOpMessageTest.java @@ -16,13 +16,10 @@ package org.ehcache.clustered.common.internal.messages; -import org.ehcache.clustered.common.internal.store.Element; import org.junit.Test; -import java.util.Collections; - -import static org.ehcache.clustered.common.internal.store.Util.createPayload; -import static org.ehcache.clustered.common.internal.store.Util.getChain; +import static org.ehcache.clustered.ChainUtils.chainOf; +import static org.ehcache.clustered.ChainUtils.createPayload; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsNot.not; @@ -30,17 +27,17 @@ public class ServerStoreOpMessageTest { @Test - public void testConcurrencyKeysEqualForSameCacheAndKey() throws Exception { + public void testConcurrencyKeysEqualForSameCacheAndKey() { ConcurrentEntityMessage m1 = new ServerStoreOpMessage.AppendMessage(1L, createPayload(1L)); ConcurrentEntityMessage m2 = new ServerStoreOpMessage.GetAndAppendMessage(1L, createPayload(1L)); - ConcurrentEntityMessage m3 = new ServerStoreOpMessage.ReplaceAtHeadMessage(1L, getChain(Collections.emptyList()), getChain(Collections.emptyList())); + ConcurrentEntityMessage m3 = new ServerStoreOpMessage.ReplaceAtHeadMessage(1L, chainOf(), chainOf()); assertThat(m1.concurrencyKey(), is(m2.concurrencyKey())); assertThat(m2.concurrencyKey(), is(m3.concurrencyKey())); } @Test - public void testConcurrencyKeysEqualForDifferentCachesSameKey() throws Exception { + public void testConcurrencyKeysEqualForDifferentCachesSameKey() { ConcurrentEntityMessage m1 = new ServerStoreOpMessage.AppendMessage(1L, createPayload(1L)); ConcurrentEntityMessage m2 = new ServerStoreOpMessage.GetAndAppendMessage(1L, createPayload(1L)); @@ -48,7 +45,7 @@ public void testConcurrencyKeysEqualForDifferentCachesSameKey() throws Exception } @Test - public void testConcurrencyKeysNotEqualForDifferentCachesAndKeys() throws Exception { + public void testConcurrencyKeysNotEqualForDifferentCachesAndKeys() { ConcurrentEntityMessage m1 = new ServerStoreOpMessage.AppendMessage(1L, createPayload(1L)); ConcurrentEntityMessage m2 = new ServerStoreOpMessage.GetAndAppendMessage(2L, createPayload(1L)); ConcurrentEntityMessage m3 = new ServerStoreOpMessage.AppendMessage(3L, createPayload(1L)); diff --git a/clustered/common/src/test/java/org/ehcache/clustered/common/internal/store/WhitelistedUnmarshallingTest.java b/clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/store/WhitelistedUnmarshallingTest.java similarity index 95% rename from clustered/common/src/test/java/org/ehcache/clustered/common/internal/store/WhitelistedUnmarshallingTest.java rename to clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/store/WhitelistedUnmarshallingTest.java index 920ac6a627..77862c7fe5 100644 --- a/clustered/common/src/test/java/org/ehcache/clustered/common/internal/store/WhitelistedUnmarshallingTest.java +++ b/clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/store/WhitelistedUnmarshallingTest.java @@ -14,12 +14,11 @@ * limitations under the License. */ -package org.ehcache.clustered.common.internal.Store; +package org.ehcache.clustered.common.internal.store; import org.ehcache.clustered.common.internal.store.Util; import org.ehcache.clustered.common.internal.store.ValueWrapper; import org.hamcrest.Matchers; -import org.junit.Assert; import org.junit.Test; import java.io.ObjectStreamClass; @@ -29,6 +28,7 @@ import java.util.function.Predicate; import static org.ehcache.clustered.common.internal.messages.StateRepositoryOpCodec.WHITELIST_PREDICATE; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -52,7 +52,7 @@ public void unmarshallingNonWhitelistedClassTest() { private void unmarshallingCheck(T t, Predicate> isClassPermitted) { @SuppressWarnings("unchecked") T unmarshalled = (T) Util.unmarshall(ByteBuffer.wrap(Util.marshall(t)), isClassPermitted); - Assert.assertThat(unmarshalled, Matchers.is(t)); + assertThat(unmarshalled, Matchers.is(t)); } private void unmarshallingStateRepoMessagesCheck(T t) { diff --git a/clustered/common/src/test/java/org/ehcache/clustered/common/internal/store/operations/OperationCodeTest.java b/clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/store/operations/OperationCodeTest.java similarity index 74% rename from clustered/common/src/test/java/org/ehcache/clustered/common/internal/store/operations/OperationCodeTest.java rename to clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/store/operations/OperationCodeTest.java index 9ae6559a11..521edd149a 100644 --- a/clustered/common/src/test/java/org/ehcache/clustered/common/internal/store/operations/OperationCodeTest.java +++ b/clustered/ehcache-common/src/test/java/org/ehcache/clustered/common/internal/store/operations/OperationCodeTest.java @@ -18,8 +18,10 @@ import org.ehcache.clustered.common.internal.store.operations.OperationCode; import org.junit.Test; +import java.util.Arrays; + +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.*; public class OperationCodeTest { @@ -27,10 +29,8 @@ public class OperationCodeTest { public void testPinning() { assertThat(OperationCode.PUT.shouldBePinned(), is(false)); - for (OperationCode operationCode : OperationCode.values()) { - if (OperationCode.PUT != operationCode) { - assertThat(operationCode.shouldBePinned(), is(true)); - } - } + Arrays.stream(OperationCode.values()) + .filter(operationCode -> operationCode != OperationCode.PUT) + .forEach((operationCode -> assertThat(operationCode + " must be pinned", operationCode.shouldBePinned(), is(true)))); } } diff --git a/clustered/integration-test/build.gradle b/clustered/integration-test/build.gradle index ae1c06ee55..56aab2750d 100644 --- a/clustered/integration-test/build.gradle +++ b/clustered/integration-test/build.gradle @@ -14,61 +14,67 @@ * limitations under the License. */ +plugins { + id 'org.ehcache.build.conventions.java' +} + configurations { serverLibs } dependencies { - // not required by gradle but required by the IDE because 'dist' do not has any transitive dependencies - testCompileOnly project(':clustered:client') - testCompileOnly project(':clustered:common') - testCompileOnly project(':impl') - testCompileOnly project(':xml') - testCompileOnly "org.terracotta.internal:client-runtime:$terracottaCoreVersion" - testCompileOnly "org.terracotta:runnel:$terracottaPlatformVersion" - testCompileOnly "org.terracotta:lease-api:$terracottaPlatformVersion" - - testImplementation project(':management') - testImplementation "org.terracotta.management.dist:mnm-nms:$terracottaPlatformVersion" - testImplementation "org.terracotta.management.dist:mnm-nms-agent:$terracottaPlatformVersion" - testImplementation "com.fasterxml.jackson.core:jackson-databind:2.8.0" - testRuntimeOnly project(':clustered:clustered-dist') - testRuntimeOnly project(':dist') - - testImplementation (group:'org.terracotta.internal', name:'galvan-support', version: terracottaCoreVersion) - testImplementation (group:'com.google.code.tempus-fugit', name:'tempus-fugit', version:'1.1') { + testImplementation project(':clustered:ehcache-client') + testImplementation project(':clustered:ehcache-common') + testImplementation project(':ehcache-impl') + testImplementation project(':ehcache-xml') + testImplementation project(':ehcache-107') + testImplementation "org.terracotta.internal:client-runtime:$terracottaCoreVersion" + testImplementation "org.terracotta:runnel:$terracottaPlatformVersion" + testImplementation "org.terracotta:lease-api:$terracottaPlatformVersion" + testImplementation("javax.cache:cache-tests:$jcacheTckVersion") { exclude group:'junit', module:'junit' - exclude group:'org.hamcrest', module:'hamcrest-core' } - testImplementation group: 'javax.cache', name: 'cache-api', version: jcacheVersion - - serverLibs ("org.terracotta.management.dist:mnm-server:$terracottaPlatformVersion") { - exclude group:'org.terracotta.management.dist', module:'mnm-common' + testImplementation("javax.cache:cache-tests:$jcacheTckVersion:tests") { + exclude group:'junit', module:'junit' } + + testImplementation project(':ehcache-management') + testImplementation "org.terracotta.management:nms-entity-client:$terracottaPlatformVersion" + testImplementation "org.terracotta.management:nms-agent-entity-client:$terracottaPlatformVersion" + testImplementation "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion" + testImplementation "org.terracotta:galvan-platform-support:$terracottaPlatformVersion" + testImplementation "javax.cache:cache-api:$jcacheVersion" } -task unzipKit(type: Copy) { - dependsOn project(':clustered:clustered-dist').distZip - from zipTree(project(':clustered:clustered-dist').distZip.archivePath) +task unzipKit(type: Sync) { + dependsOn project(':clustered:ehcache-clustered').distZip + from zipTree(project(':clustered:ehcache-clustered').distZip.archivePath) into 'build/ehcache-kit' } -task copyServerLibs(type: Copy) { +task copyServerLibs(type: Sync) { dependsOn unzipKit from project.configurations.serverLibs - into "$unzipKit.destinationDir/${project(':clustered:clustered-dist').archivesBaseName}-$project.version-kit/server/plugins/lib" + into "$unzipKit.destinationDir/${project(':clustered:ehcache-clustered').archivesBaseName}-$project.version-kit/server/plugins/lib" } test { + maxHeapSize = '512m' + maxParallelForks = 8 dependsOn copyServerLibs environment 'JAVA_HOME', testJava.javaHome - if (testJava.javaVersion.isJava9Compatible()) { - jvmArgs += ['--add-modules', 'java.xml.bind'] - environment 'JAVA_OPTS', '--add-modules java.xml.bind' - } //If this directory does not exist, tests will fail with a cryptic assert failure - systemProperty 'kitInstallationPath', "$unzipKit.destinationDir/${project(':clustered:clustered-dist').archivesBaseName}-$project.version-kit" + systemProperty 'kitInstallationPath', "$unzipKit.destinationDir/${project(':clustered:ehcache-clustered').archivesBaseName}-$project.version-kit" // Uncomment to include client logging in console output // testLogging.showStandardStreams = true } +configurations.all { + resolutionStrategy { + dependencySubstitution { + substitute(module('junit:junit:4.12')) + .because('CVE-2020-15250') + .with(module('junit:junit:4.13.1')) + } + } +} diff --git a/clustered/integration-test/gradle.properties b/clustered/integration-test/gradle.properties deleted file mode 100644 index bdb75fe1c9..0000000000 --- a/clustered/integration-test/gradle.properties +++ /dev/null @@ -1,18 +0,0 @@ -# -# Copyright Terracotta, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -subPomName = Ehcache 3 Clustered Integration Test module -subPomDesc = The Clustering Integration Test module of Ehcache 3 diff --git a/clustered/integration-test/src/test/java/org/ehcache/Diagnostics.java b/clustered/integration-test/src/test/java/org/ehcache/Diagnostics.java deleted file mode 100644 index 1891c1fbb3..0000000000 --- a/clustered/integration-test/src/test/java/org/ehcache/Diagnostics.java +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ehcache; - -import com.sun.management.HotSpotDiagnosticMXBean; - -import java.io.File; -import java.io.IOException; -import java.io.PrintStream; -import java.lang.management.LockInfo; -import java.lang.management.ManagementFactory; -import java.lang.management.MonitorInfo; -import java.lang.management.ThreadInfo; -import java.lang.management.ThreadMXBean; -import java.util.Calendar; -import java.util.Date; - -import javax.management.MBeanServer; - -/** - * Provides methods to produce diagnostic output. - */ -@SuppressWarnings({ "UnusedDeclaration", "WeakerAccess" }) -public final class Diagnostics { - - private static final String HOTSPOT_DIAGNOSTIC_MXBEAN_NAME = "com.sun.management:type=HotSpotDiagnostic"; - private static final String HEAP_DUMP_FILENAME_TEMPLATE = "java_%1$04d_%2$tFT%2$tH%2$tM%2$tS.%2$tL.hprof"; - private static final File WORKING_DIRECTORY = new File(System.getProperty("user.dir")); - - /** - * Private niladic constructor to prevent instantiation. - */ - private Diagnostics() { - } - - /** - * Writes a complete thread dump to {@code System.err}. - */ - public static void threadDump() { - threadDump(System.err); - } - - /** - * Writes a complete thread dump to the designated {@code PrintStream}. - * - * @param out the {@code PrintStream} to which the thread dump is written - */ - public static void threadDump(final PrintStream out) { - if (out == null) { - throw new NullPointerException("out"); - } - - final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); - - final Calendar when = Calendar.getInstance(); - final ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads( - threadMXBean.isObjectMonitorUsageSupported(), threadMXBean.isSynchronizerUsageSupported()); - - out.format("%nFull thread dump %1$tF %1$tT.%1$tL %1$tz%n", when); - for (final ThreadInfo threadInfo : threadInfos) { - out.print(format(threadInfo)); - } - } - - /** - * Format a {@code ThreadInfo} instance without a stack depth limitation. This method reproduces the - * formatting performed in {@code java.lang.management.ThreadInfo.toString()} without the stack depth limit. - * - * @param threadInfo the {@code ThreadInfo} instance to foramt - * - * @return a {@code CharSequence} instance containing the formatted {@code ThreadInfo} - */ - private static CharSequence format(final ThreadInfo threadInfo) { - StringBuilder sb = new StringBuilder(4096); - - Thread.State threadState = threadInfo.getThreadState(); - sb.append('"') - .append(threadInfo.getThreadName()) - .append('"') - .append(" Id=") - .append(threadInfo.getThreadId()) - .append(' ') - .append(threadState); - - if (threadInfo.getLockName() != null) { - sb.append(" on ").append(threadInfo.getLockName()); - } - if (threadInfo.getLockOwnerName() != null) { - sb.append(" owned by ").append('"').append(threadInfo.getLockOwnerName()).append('"') - .append(" Id=").append(threadInfo.getLockOwnerId()); - } - - if (threadInfo.isSuspended()) { - sb.append(" (suspended)"); - } - if (threadInfo.isInNative()) { - sb.append(" (in native)"); - } - sb.append('\n'); - - StackTraceElement[] stackTrace = threadInfo.getStackTrace(); - for (int i = 0; i < stackTrace.length; i++) { - StackTraceElement element = stackTrace[i]; - sb.append("\tat ").append(element); - sb.append('\n'); - if (i == 0) { - if (threadInfo.getLockInfo() != null) { - switch (threadState) { - case BLOCKED: - sb.append("\t- blocked on ").append(threadInfo.getLockInfo()); - sb.append('\n'); - break; - case WAITING: - sb.append("\t- waiting on ").append(threadInfo.getLockInfo()); - sb.append('\n'); - break; - case TIMED_WAITING: - sb.append("\t- waiting on ").append(threadInfo.getLockInfo()); - sb.append('\n'); - break; - default: - } - } - } - - for (MonitorInfo monitorInfo : threadInfo.getLockedMonitors()) { - if (monitorInfo.getLockedStackDepth() == i) { - sb.append("\t- locked ").append(monitorInfo); - sb.append('\n'); - } - } - } - - LockInfo[] lockedSynchronizers = threadInfo.getLockedSynchronizers(); - if (lockedSynchronizers.length > 0) { - sb.append("\n\tNumber of locked synchronizers = ").append(lockedSynchronizers.length); - sb.append('\n'); - for (LockInfo lockedSynchronizer : lockedSynchronizers) { - sb.append("\t- ").append(lockedSynchronizer); - sb.append('\n'); - } - } - - sb.append('\n'); - return sb; - } - - /** - * Take a Java heap dump into a file whose name is produced from the template - * {@value #HEAP_DUMP_FILENAME_TEMPLATE} where {@code 1$} is the PID of - * the current process obtained from {@link #getPid()}. - * - * @param dumpLiveObjects if {@code true}, only "live" (reachable) objects are dumped; - * if {@code false}, all objects in the heap are dumped - * - * @return the name of the dump file; the file is written to the current directory (generally {@code user.dir}) - */ - public static String dumpHeap(final boolean dumpLiveObjects) { - - String dumpName; - final int pid = getPid(); - final Date currentTime = new Date(); - if (pid > 0) { - dumpName = String.format(HEAP_DUMP_FILENAME_TEMPLATE, pid, currentTime); - } else { - dumpName = String.format(HEAP_DUMP_FILENAME_TEMPLATE, 0, currentTime); - } - - dumpName = new File(WORKING_DIRECTORY, dumpName).getAbsolutePath(); - - try { - dumpHeap(dumpLiveObjects, dumpName); - } catch (IOException e) { - System.err.printf("Unable to write heap dump to %s: %s%n", dumpName, e); - e.printStackTrace(System.err); - return null; - } - - return dumpName; - } - - /** - * Write a Java heap dump to the named file. If the dump file exists, this method will - * fail. - * - * @param dumpLiveObjects if {@code true}, only "live" (reachable) objects are dumped; - * if {@code false}, all objects in the heap are dumped - * @param dumpName the name of the file to which the heap dump is written; relative names - * are relative to the current directory ({@code user.dir}). If the value - * of {@code dumpName} does not end in {@code .hprof}, it is appended. - * - * @throws IOException if thrown while loading the HotSpot Diagnostic MXBean or writing the heap dump - * - * @see - * com.sun.management.HotSpotDiagnosticMXBean - */ - public static void dumpHeap(final boolean dumpLiveObjects, String dumpName) throws IOException { - if (dumpName == null) { - throw new NullPointerException("dumpName"); - } - - if (!dumpName.endsWith(".hprof")) { - dumpName += ".hprof"; - } - - final MBeanServer server = ManagementFactory.getPlatformMBeanServer(); - final HotSpotDiagnosticMXBean hotSpotDiagnosticMXBean = - ManagementFactory.newPlatformMXBeanProxy(server, HOTSPOT_DIAGNOSTIC_MXBEAN_NAME, HotSpotDiagnosticMXBean.class); - hotSpotDiagnosticMXBean.dumpHeap(dumpName, dumpLiveObjects); - } - - /** - * Gets the PID of the current process. This method is dependent upon "common" - * operation of the {@code java.lang.management.RuntimeMXBean#getName()} method. - * - * @return the PID of the current process or {@code -1} if the PID can not be determined - */ - public static int getPid() { - // Expected to be of the form "@" - final String jvmProcessName = ManagementFactory.getRuntimeMXBean().getName(); - try { - return Integer.valueOf(jvmProcessName.substring(0, jvmProcessName.indexOf('@'))); - } catch (NumberFormatException | IndexOutOfBoundsException e) { - return -1; - } - } -} diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/BasicCacheOpsMultiThreadedTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/BasicCacheOpsMultiThreadedTest.java index ba74b1872d..8d57c13c93 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/BasicCacheOpsMultiThreadedTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/BasicCacheOpsMultiThreadedTest.java @@ -19,60 +19,50 @@ import org.ehcache.Cache; import org.ehcache.PersistentCacheManager; import org.ehcache.clustered.client.config.ClusteredStoreConfiguration; -import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder; -import org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder; -import org.ehcache.clustered.client.config.builders.ServerSideConfigurationBuilder; -import org.ehcache.clustered.client.config.builders.TimeoutsBuilder; import org.ehcache.clustered.common.Consistency; -import org.ehcache.config.ResourcePool; -import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.config.builders.CacheManagerBuilder; -import org.ehcache.config.builders.ResourcePoolsBuilder; import org.ehcache.config.units.MemoryUnit; -import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; import org.terracotta.testing.rules.Cluster; -import com.tc.util.Assert; - -import java.io.File; import java.net.URI; import java.time.Duration; -import java.util.ArrayList; import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; +import java.util.Optional; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.atomic.AtomicReference; +import static java.util.Collections.nCopies; +import static java.util.stream.Collectors.toList; +import static org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder.clusteredDedicated; +import static org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder.cluster; +import static org.ehcache.clustered.client.config.builders.TimeoutsBuilder.timeouts; +import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; +import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder; +import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.ehcache.testing.StandardCluster.offheapResource; +import static org.ehcache.testing.StandardTimeouts.eventually; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.terracotta.testing.rules.BasicExternalClusterBuilder.newCluster; +import static org.hamcrest.Matchers.notNullValue; + /** * Simulate multiple clients starting up the same cache manager simultaneously and ensure that puts and gets works just * fine and nothing get lost or hung, just because multiple cache manager instances of the same cache manager are coming up * simultaneously. */ -public class BasicCacheOpsMultiThreadedTest extends ClusteredTests { - - private static final String RESOURCE_CONFIG = - "" - + "" - + "64" - + "" + - "\n"; +public class BasicCacheOpsMultiThreadedTest { @ClassRule public static Cluster CLUSTER = - newCluster().in(new File("build/cluster")).withServiceFragment(RESOURCE_CONFIG).build(); - - @BeforeClass - public static void waitForActive() throws Exception { - CLUSTER.getClusterControl().waitForActive(); - } + newCluster().in(clusterPath()).withServiceFragment(offheapResource("primary-server-resource", 64)).build(); private static final String CLUSTERED_CACHE_NAME = "clustered-cache"; private static final String SYN_CACHE_NAME = "syn-cache"; @@ -82,103 +72,65 @@ public static void waitForActive() throws Exception { private static final int NUM_THREADS = 8; private static final int MAX_WAIT_TIME_SECONDS = 30; - private final AtomicReference exception = new AtomicReference<>(); private final AtomicLong idGenerator = new AtomicLong(2L); @Test - public void testMulipleClients() throws Throwable { - CountDownLatch latch = new CountDownLatch(NUM_THREADS + 1); - - List threads = new ArrayList<>(NUM_THREADS); - for (int i = 0; i < NUM_THREADS; i++) { - Thread t1 = new Thread(content(latch)); - t1.start(); - threads.add(t1); - } + public void testMultipleClients() { + ExecutorService executorService = Executors.newFixedThreadPool(NUM_THREADS); + try { + List> results = nCopies(NUM_THREADS, content()).stream().map(executorService::submit).collect(toList()); - latch.countDown(); - assertTrue(latch.await(MAX_WAIT_TIME_SECONDS, TimeUnit.SECONDS)); - - for (Thread t : threads) { - t.join(); - } - - Throwable throwable = exception.get(); - if (throwable != null) { - throw throwable; + results.stream().map(f -> { + try { + f.get(); + return Optional.empty(); + } catch (Exception e) { + return Optional.of(e); + } + }).filter(Optional::isPresent).map(Optional::get).reduce((a, b) -> { + a.addSuppressed(b); + return a; + } + ).ifPresent(t -> { + throw new AssertionError(t); + }); + } finally { + executorService.shutdownNow(); } } - private Runnable content(CountDownLatch latch) { + private Callable content() { return () -> { try (PersistentCacheManager cacheManager = createCacheManager(CLUSTER.getConnectionURI())) { - latch.countDown(); - try { - assertTrue(latch.await(MAX_WAIT_TIME_SECONDS, TimeUnit.SECONDS)); - } catch (InterruptedException e) { - // continue - } - - cacheManager.init(); - doSyncAndPut(cacheManager); - } catch (Throwable t) { - if (!exception.compareAndSet(null, t)) { - exception.get().addSuppressed(t); + Cache synCache = cacheManager.getCache(SYN_CACHE_NAME, String.class, Boolean.class); + Cache customValueCache = cacheManager.getCache(CLUSTERED_CACHE_NAME, Long.class, String.class); + parallelPuts(customValueCache); + String firstClientStartKey = "first_client_start", firstClientEndKey = "first_client_end"; + if (synCache.putIfAbsent(firstClientStartKey, true) == null) { + customValueCache.put(1L, "value"); + assertThat(customValueCache.get(1L), is("value")); + synCache.put(firstClientEndKey, true); + } else { + assertThat(() -> synCache.get(firstClientEndKey), eventually().matches(notNullValue())); + assertThat(customValueCache.get(1L), is("value")); } + return null; } }; } - private void doSyncAndPut(PersistentCacheManager cacheManager) throws InterruptedException { - String customValue = "value"; - Cache synCache = cacheManager.getCache(SYN_CACHE_NAME, String.class, Boolean.class); - Cache customValueCache = cacheManager.getCache(CLUSTERED_CACHE_NAME, Long.class, String.class); - parallelPuts(customValueCache); - String firstClientStartKey = "first_client_start", firstClientEndKey = "first_client_end"; - if (synCache.putIfAbsent(firstClientStartKey, true) == null) { - customValueCache.put(1L, customValue); - assertThat(customValueCache.get(1L), is(customValue)); - synCache.put(firstClientEndKey, true); - } else { - int retry = 0, maxRetryCount = 30; - while (++retry <= maxRetryCount && synCache.get(firstClientEndKey) == null) { - Thread.sleep(1000L); - } - - if (retry > maxRetryCount) { - Assert.fail("Couldn't find " + firstClientEndKey + " in synCache after " + maxRetryCount + " retries!"); - } - - assertThat(customValueCache.get(1L), is(customValue)); - } - } - private static PersistentCacheManager createCacheManager(URI clusterURI) { - ServerSideConfigurationBuilder serverSideConfigBuilder = ClusteringServiceConfigurationBuilder - .cluster(clusterURI.resolve(CACHE_MANAGER_NAME)) - .timeouts(TimeoutsBuilder.timeouts().read(Duration.ofSeconds(20)).write(Duration.ofSeconds(30))) - .autoCreate() - .defaultServerResource(PRIMARY_SERVER_RESOURCE_NAME); - - ResourcePool resourcePool = ClusteredResourcePoolBuilder - .clusteredDedicated(PRIMARY_SERVER_RESOURCE_NAME, PRIMARY_SERVER_RESOURCE_SIZE, MemoryUnit.MB); - - CacheManagerBuilder clusteredCacheManagerBuilder = CacheManagerBuilder - .newCacheManagerBuilder() - .with(serverSideConfigBuilder) - .withCache(CLUSTERED_CACHE_NAME, - CacheConfigurationBuilder - .newCacheConfigurationBuilder(Long.class, String.class, - ResourcePoolsBuilder.newResourcePoolsBuilder() - .with(resourcePool)) - .add(new ClusteredStoreConfiguration(Consistency.STRONG))) - .withCache(SYN_CACHE_NAME, - CacheConfigurationBuilder - .newCacheConfigurationBuilder(String.class, Boolean.class, - ResourcePoolsBuilder.newResourcePoolsBuilder() - .with(resourcePool)) - .add(new ClusteredStoreConfiguration(Consistency.STRONG))); - return clusteredCacheManagerBuilder.build(false); + CacheManagerBuilder clusteredCacheManagerBuilder = newCacheManagerBuilder() + .with(cluster(clusterURI.resolve(CACHE_MANAGER_NAME)) + .timeouts(timeouts().read(Duration.ofSeconds(MAX_WAIT_TIME_SECONDS)).write(Duration.ofSeconds(MAX_WAIT_TIME_SECONDS))) + .autoCreate(server -> server.defaultServerResource(PRIMARY_SERVER_RESOURCE_NAME))) + .withCache(CLUSTERED_CACHE_NAME, newCacheConfigurationBuilder(Long.class, String.class, newResourcePoolsBuilder() + .with(clusteredDedicated(PRIMARY_SERVER_RESOURCE_SIZE, MemoryUnit.MB))) + .withService(new ClusteredStoreConfiguration(Consistency.STRONG))) + .withCache(SYN_CACHE_NAME, newCacheConfigurationBuilder(String.class, Boolean.class, newResourcePoolsBuilder() + .with(clusteredDedicated(PRIMARY_SERVER_RESOURCE_SIZE, MemoryUnit.MB))) + .withService(new ClusteredStoreConfiguration(Consistency.STRONG))); + return clusteredCacheManagerBuilder.build(true); } private void parallelPuts(Cache customValueCache) { diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/BasicClusteredCacheOpsTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/BasicClusteredCacheOpsTest.java index b21174d0ea..e9c9cf5e4c 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/BasicClusteredCacheOpsTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/BasicClusteredCacheOpsTest.java @@ -33,7 +33,6 @@ import org.junit.Test; import org.terracotta.testing.rules.Cluster; -import java.io.File; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; @@ -43,37 +42,28 @@ import static org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder.cluster; import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder; +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.ehcache.testing.StandardCluster.offheapResource; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; -import static org.terracotta.testing.rules.BasicExternalClusterBuilder.newCluster; +import static org.hamcrest.collection.IsIterableWithSize.iterableWithSize; -public class BasicClusteredCacheOpsTest extends ClusteredTests { - - private static final String RESOURCE_CONFIG = - "" - + "" - + "64" - + "" + - "\n"; +public class BasicClusteredCacheOpsTest { @ClassRule - public static Cluster CLUSTER = - newCluster().in(new File("build/cluster")).withServiceFragment(RESOURCE_CONFIG).build(); - - @BeforeClass - public static void waitForActive() throws Exception { - CLUSTER.getClusterControl().waitForActive(); - } + public static Cluster CLUSTER = newCluster().in(clusterPath()) + .withServiceFragment(offheapResource("primary-server-resource", 64)).build(); @Test public void basicCacheCRUD() throws Exception { final CacheManagerBuilder clusteredCacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder() .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER.getConnectionURI().resolve("/crud-cm")) - .autoCreate() - .defaultServerResource("primary-server-resource")); + .autoCreate(server -> server.defaultServerResource("primary-server-resource"))); final PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(false); cacheManager.init(); @@ -108,11 +98,11 @@ public void basicCacheCRUD() throws Exception { public void basicCacheCAS() throws Exception { final CacheManagerBuilder clusteredCacheManagerBuilder = newCacheManagerBuilder() - .with(cluster(CLUSTER.getConnectionURI().resolve("/cas-cm")).autoCreate()) + .with(cluster(CLUSTER.getConnectionURI().resolve("/cas-cm")).autoCreate(c -> c)) .withCache("clustered-cache", newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder().heap(100, EntryUnit.ENTRIES) .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 2, MemoryUnit.MB))) - .add(new ClusteredStoreConfiguration(Consistency.STRONG))); + .withService(new ClusteredStoreConfiguration(Consistency.STRONG))); try (PersistentCacheManager cacheManager1 = clusteredCacheManagerBuilder.build(true)) { @@ -136,11 +126,11 @@ public void basicCacheCAS() throws Exception { public void basicClusteredBulk() throws Exception { final CacheManagerBuilder clusteredCacheManagerBuilder = newCacheManagerBuilder() - .with(cluster(CLUSTER.getConnectionURI().resolve("/bulk-cm")).autoCreate()) + .with(cluster(CLUSTER.getConnectionURI().resolve("/bulk-cm")).autoCreate(c -> c)) .withCache("clustered-cache", newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 2, MemoryUnit.MB))) - .add(new ClusteredStoreConfiguration(Consistency.STRONG))); + .withService(new ClusteredStoreConfiguration(Consistency.STRONG))); try (PersistentCacheManager cacheManager1 = clusteredCacheManagerBuilder.build(true)) { @@ -160,6 +150,19 @@ public void basicClusteredBulk() throws Exception { assertThat(all.get(2L), is("two")); assertThat(all.get(3L), is("three")); + Map entries1 = new HashMap<>(); + assertThat(cache1, iterableWithSize(3)); + cache1.forEach(e -> entries1.putIfAbsent(e.getKey(), e.getValue())); + assertThat(entries1, hasEntry(1L, "one")); + assertThat(entries1, hasEntry(2L, "two")); + assertThat(entries1, hasEntry(3L, "three")); + + Map entries2 = new HashMap<>(); + assertThat(cache2, iterableWithSize(3)); + cache2.forEach(e -> entries2.putIfAbsent(e.getKey(), e.getValue())); + assertThat(entries2, hasEntry(1L, "one")); + assertThat(entries2, hasEntry(2L, "two")); + assertThat(entries2, hasEntry(3L, "three")); cache2.removeAll(keySet); all = cache1.getAll(keySet); diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/BasicEntityInteractionTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/BasicEntityInteractionTest.java index 8f3227cee3..eb5d6d9bcd 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/BasicEntityInteractionTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/BasicEntityInteractionTest.java @@ -15,13 +15,22 @@ */ package org.ehcache.clustered; -import java.io.File; +import java.net.URI; + +import org.ehcache.Cache; +import org.ehcache.CacheManager; +import org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder; import org.ehcache.clustered.client.internal.ClusterTierManagerClientEntity; import org.ehcache.clustered.common.EhcacheEntityVersion; import org.ehcache.clustered.common.ServerSideConfiguration; import org.ehcache.clustered.common.internal.ClusterTierManagerConfiguration; +import org.ehcache.config.builders.CacheConfigurationBuilder; +import org.ehcache.config.builders.CacheManagerBuilder; +import org.ehcache.config.builders.ResourcePoolsBuilder; +import org.ehcache.config.units.MemoryUnit; +import org.ehcache.management.cluster.DefaultClusteringManagementService; +import org.ehcache.management.statistics.DefaultExtendedStatisticsService; import org.hamcrest.Matchers; -import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Ignore; import org.junit.Rule; @@ -34,33 +43,91 @@ import org.terracotta.testing.rules.Cluster; import static java.util.Collections.emptyMap; +import static org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder.clusteredDedicated; +import static org.ehcache.config.units.EntryUnit.ENTRIES; +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.ehcache.testing.StandardCluster.offheapResource; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; -import static org.terracotta.testing.rules.BasicExternalClusterBuilder.newCluster; - -public class BasicEntityInteractionTest extends ClusteredTests { - private static final String RESOURCE_CONFIG = - "" - + "" - + "4" - + "" + - "\n"; +public class BasicEntityInteractionTest { @ClassRule - public static Cluster CLUSTER = newCluster().in(new File("build/cluster")).withServiceFragment(RESOURCE_CONFIG).build(); + public static Cluster CLUSTER = newCluster().in(clusterPath()) + .withServiceFragment(offheapResource("primary-server-resource", 4)).build(); private ClusterTierManagerConfiguration blankConfiguration = new ClusterTierManagerConfiguration("identifier", new ServerSideConfiguration(emptyMap())); - @BeforeClass - public static void waitForActive() throws Exception { - CLUSTER.getClusterControl().waitForActive(); - } - @Rule public TestName testName= new TestName(); + @Test + public void testClusteringServiceConfigurationBuilderThrowsNPE() throws Exception { + String cacheName = "myCACHE"; + String offheap = "primary-server-resource"; + URI tsaUri = CLUSTER.getConnectionURI(); + + try (CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() + .withCache(cacheName, CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() + .heap(100, ENTRIES) + .with(clusteredDedicated(offheap, 2, MemoryUnit.MB))) + ).with(ClusteringServiceConfigurationBuilder.cluster(tsaUri) + .autoCreate(server -> server.defaultServerResource(offheap)) + ).build(true)) { + Cache cache = cacheManager.getCache(cacheName, Long.class, String.class); + cache.put(1L, "one"); + } + + try (CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() + .withCache(cacheName, CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() + .heap(100, ENTRIES) + .with(clusteredDedicated(offheap, 2, MemoryUnit.MB)) + ) + ).with(ClusteringServiceConfigurationBuilder.cluster(tsaUri) + ).using(new DefaultExtendedStatisticsService() + ).using(new DefaultClusteringManagementService() + ).build(true)) { + Cache cache = cacheManager.getCache(cacheName, Long.class, String.class); + cache.get(1L); + } + + } + + @Test + public void testServicesStoppedTwice() throws Exception { + String cacheName = "myCACHE"; + String offheap = "primary-server-resource"; + URI tsaUri = CLUSTER.getConnectionURI(); + + try (CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() + .withCache(cacheName, CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() + .heap(100, ENTRIES) + .with(clusteredDedicated(offheap, 2, MemoryUnit.MB))) + ).with(ClusteringServiceConfigurationBuilder.cluster(tsaUri) + .autoCreate(server -> server.defaultServerResource(offheap)) + // manually adding the following two services should work + ).using(new DefaultExtendedStatisticsService() + ).using(new DefaultClusteringManagementService() + ).build(true)) { + Cache cache = cacheManager.getCache(cacheName, Long.class, String.class); + cache.put(1L, "one"); + } + + try (CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() + .withCache(cacheName, CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() + .heap(100, ENTRIES) + .with(clusteredDedicated(offheap, 2, MemoryUnit.MB)) + ) + ).with(ClusteringServiceConfigurationBuilder.cluster(tsaUri) + ).build(true)) { + Cache cache = cacheManager.getCache(cacheName, Long.class, String.class); + cache.get(1L); + } + + } + @Test public void testAbsentEntityRetrievalFails() throws Throwable { try (Connection client = CLUSTER.newConnection()) { diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/CacheManagerLifecycleEhcacheIntegrationTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/CacheManagerLifecycleEhcacheIntegrationTest.java index 689d51a7a6..f828722f90 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/CacheManagerLifecycleEhcacheIntegrationTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/CacheManagerLifecycleEhcacheIntegrationTest.java @@ -53,27 +53,22 @@ import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder; import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManager; +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.ehcache.testing.StandardCluster.offheapResource; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; -import static org.terracotta.testing.rules.BasicExternalClusterBuilder.newCluster; -public class CacheManagerLifecycleEhcacheIntegrationTest extends ClusteredTests { - - private static final String RESOURCE_CONFIG = - "" - + "" - + "64" - + "" + - "\n"; +public class CacheManagerLifecycleEhcacheIntegrationTest { @ClassRule - public static Cluster CLUSTER = newCluster().in(new File("build/cluster")).withServiceFragment(RESOURCE_CONFIG).build(); + public static Cluster CLUSTER = newCluster().in(clusterPath()) + .withServiceFragment(offheapResource("primary-server-resource", 64)).build(); private static Connection ASSERTION_CONNECTION; @BeforeClass public static void waitForActive() throws Exception { - CLUSTER.getClusterControl().waitForActive(); ASSERTION_CONNECTION = CLUSTER.newConnection(); } @@ -81,7 +76,7 @@ public static void waitForActive() throws Exception { public void testAutoCreatedCacheManager() throws Exception { assertEntityNotExists(ClusterTierManagerClientEntity.class, "testAutoCreatedCacheManager"); PersistentCacheManager manager = newCacheManagerBuilder() - .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER.getConnectionURI().resolve("/testAutoCreatedCacheManager")).autoCreate().build()) + .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER.getConnectionURI().resolve("/testAutoCreatedCacheManager")).autoCreate(c -> c).build()) .build(); assertEntityNotExists(ClusterTierManagerClientEntity.class, "testAutoCreatedCacheManager"); manager.init(); @@ -112,7 +107,7 @@ public void testMultipleClientsAutoCreatingCacheManager() throws Exception { assertEntityNotExists(ClusterTierManagerClientEntity.class, "testMultipleClientsAutoCreatingCacheManager"); final CacheManagerBuilder managerBuilder = newCacheManagerBuilder() - .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER.getConnectionURI().resolve("/testMultipleClientsAutoCreatingCacheManager")).autoCreate().build()); + .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER.getConnectionURI().resolve("/testMultipleClientsAutoCreatingCacheManager")).autoCreate(c -> c).build()); Callable task = () -> { PersistentCacheManager manager = managerBuilder.build(); @@ -186,6 +181,10 @@ public EntityRef getEntityRef(Class cls, lo public void close() throws IOException { //no-op } + + public boolean isValid() { + return true; + } }; } diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/ClusterTierManagerClientEntityFactoryIntegrationTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/ClusterTierManagerClientEntityFactoryIntegrationTest.java index 8678e0b8e0..ab470bbfd4 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/ClusterTierManagerClientEntityFactoryIntegrationTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/ClusterTierManagerClientEntityFactoryIntegrationTest.java @@ -15,7 +15,6 @@ */ package org.ehcache.clustered; -import java.io.File; import java.io.IOException; import java.util.Collections; import java.util.Map; @@ -34,33 +33,28 @@ import org.terracotta.exception.EntityNotFoundException; import org.terracotta.testing.rules.Cluster; +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.ehcache.testing.StandardCluster.offheapResource; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsNull.notNullValue; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import static org.terracotta.testing.rules.BasicExternalClusterBuilder.newCluster; -public class ClusterTierManagerClientEntityFactoryIntegrationTest extends ClusteredTests { - private static final Map EMPTY_RESOURCE_MAP = Collections.emptyMap(); +public class ClusterTierManagerClientEntityFactoryIntegrationTest { - private static final String RESOURCE_CONFIG = - "" - + "" - + "64" - + "" + - "\n"; + private static final Map EMPTY_RESOURCE_MAP = Collections.emptyMap(); @ClassRule - public static Cluster CLUSTER = - newCluster().in(new File("build/cluster")).withServiceFragment(RESOURCE_CONFIG).build(); + public static Cluster CLUSTER = newCluster().in(clusterPath()) + .withServiceFragment(offheapResource("primary", 64)).build(); private static Connection CONNECTION; @BeforeClass - public static void waitForActive() throws Exception { - CLUSTER.getClusterControl().waitForActive(); + public static void initConnection() throws Exception { CONNECTION = CLUSTER.newConnection(); } @@ -71,14 +65,14 @@ public static void closeConnection() throws IOException { @Test public void testCreate() throws Exception { - ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(CONNECTION); + ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(CONNECTION, Runnable::run); factory.create("testCreate", new ServerSideConfiguration(EMPTY_RESOURCE_MAP)); } @Test public void testCreateWhenExisting() throws Exception { - ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(CONNECTION); + ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(CONNECTION, Runnable::run); factory.create("testCreateWhenExisting", new ServerSideConfiguration(EMPTY_RESOURCE_MAP)); try { factory.create("testCreateWhenExisting", @@ -91,7 +85,7 @@ public void testCreateWhenExisting() throws Exception { @Test public void testCreateWithBadConfigCleansUp() throws Exception { - ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(CONNECTION); + ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(CONNECTION, Runnable::run); try { factory.create("testCreateWithBadConfigCleansUp", new ServerSideConfiguration("flargle", EMPTY_RESOURCE_MAP)); @@ -108,7 +102,7 @@ public void testCreateWithBadConfigCleansUp() throws Exception { @Test public void testRetrieveWithGoodConfig() throws Exception { - ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(CONNECTION); + ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(CONNECTION, Runnable::run); factory.create("testRetrieveWithGoodConfig", new ServerSideConfiguration(Collections.singletonMap("foo", new Pool(43L, "primary")))); assertThat(factory.retrieve("testRetrieveWithGoodConfig", @@ -117,7 +111,7 @@ public void testRetrieveWithGoodConfig() throws Exception { @Test public void testRetrieveWithBadConfig() throws Exception { - ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(CONNECTION); + ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(CONNECTION, Runnable::run); factory.create("testRetrieveWithBadConfig", new ServerSideConfiguration(Collections.singletonMap("foo", new Pool(42L, "primary")))); try { @@ -131,7 +125,7 @@ public void testRetrieveWithBadConfig() throws Exception { @Test public void testRetrieveWhenNotExisting() throws Exception { - ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(CONNECTION); + ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(CONNECTION, Runnable::run); try { factory.retrieve("testRetrieveWhenNotExisting", null); fail("Expected EntityNotFoundException"); @@ -142,48 +136,48 @@ public void testRetrieveWhenNotExisting() throws Exception { @Test public void testDestroy() throws Exception { - ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(CONNECTION); + ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(CONNECTION, Runnable::run); factory.create("testDestroy", new ServerSideConfiguration(Collections.emptyMap())); factory.destroy("testDestroy"); } @Test public void testDestroyWhenNotExisting() throws Exception { - ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(CONNECTION); + ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(CONNECTION, Runnable::run); factory.destroy("testDestroyWhenNotExisting"); } @Test public void testAbandonLeadershipWhenNotOwning() throws Exception { - ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(CONNECTION); - assertFalse(factory.abandonLeadership("testAbandonLeadershipWhenNotOwning")); + ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(CONNECTION, Runnable::run); + assertFalse(factory.abandonLeadership("testAbandonLeadershipWhenNotOwning", true)); } @Test public void testAcquireLeadershipWhenAlone() throws Exception { - ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(CONNECTION); + ClusterTierManagerClientEntityFactory factory = new ClusterTierManagerClientEntityFactory(CONNECTION, Runnable::run); assertThat(factory.acquireLeadership("testAcquireLeadershipWhenAlone"), is(true)); } @Test public void testAcquireLeadershipWhenTaken() throws Exception { - ClusterTierManagerClientEntityFactory factoryA = new ClusterTierManagerClientEntityFactory(CONNECTION); + ClusterTierManagerClientEntityFactory factoryA = new ClusterTierManagerClientEntityFactory(CONNECTION, Runnable::run); assertThat(factoryA.acquireLeadership("testAcquireLeadershipWhenTaken"), is(true)); try (Connection clientB = CLUSTER.newConnection()) { - ClusterTierManagerClientEntityFactory factoryB = new ClusterTierManagerClientEntityFactory(clientB); + ClusterTierManagerClientEntityFactory factoryB = new ClusterTierManagerClientEntityFactory(clientB, Runnable::run); assertThat(factoryB.acquireLeadership("testAcquireLeadershipWhenTaken"), is(false)); } } @Test public void testAcquireLeadershipAfterAbandoned() throws Exception { - ClusterTierManagerClientEntityFactory factoryA = new ClusterTierManagerClientEntityFactory(CONNECTION); + ClusterTierManagerClientEntityFactory factoryA = new ClusterTierManagerClientEntityFactory(CONNECTION, Runnable::run); factoryA.acquireLeadership("testAcquireLeadershipAfterAbandoned"); - assertTrue(factoryA.abandonLeadership("testAcquireLeadershipAfterAbandoned")); + assertTrue(factoryA.abandonLeadership("testAcquireLeadershipAfterAbandoned", true)); try (Connection clientB = CLUSTER.newConnection()) { - ClusterTierManagerClientEntityFactory factoryB = new ClusterTierManagerClientEntityFactory(clientB); + ClusterTierManagerClientEntityFactory factoryB = new ClusterTierManagerClientEntityFactory(clientB, Runnable::run); assertThat(factoryB.acquireLeadership("testAcquireLeadershipAfterAbandoned"), is(true)); } } diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/ClusteredIterationTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/ClusteredIterationTest.java new file mode 100644 index 0000000000..704d87f9cc --- /dev/null +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/ClusteredIterationTest.java @@ -0,0 +1,148 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.clustered; + +import org.ehcache.Cache; +import org.ehcache.CacheManager; +import org.ehcache.config.units.MemoryUnit; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; +import org.terracotta.testing.rules.Cluster; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Set; + +import static java.util.stream.LongStream.range; +import static org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder.clusteredDedicated; +import static org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder.cluster; +import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; +import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder; +import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.ehcache.testing.StandardCluster.offheapResource; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.collection.IsIn.isIn; +import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsInstanceOf.any; +import static org.hamcrest.core.IsNull.notNullValue; +import static org.junit.Assert.fail; + +public class ClusteredIterationTest { + + @ClassRule + public static Cluster CLUSTER = newCluster().in(clusterPath()) + .withServiceFragment(offheapResource("primary-server-resource", 64)).build(); + + @Rule + public final TestName testName = new TestName(); + + @Test + public void testIterationTerminatedWithException() { + try (CacheManager cacheManager = createTestCacheManager()) { + Cache cache = cacheManager.getCache(testName.getMethodName(), Long.class, byte[].class); + + byte[] data = new byte[101 * 1024]; + cache.put(1L, data); + cache.put(2L, data); + + Iterator> iterator = cache.iterator(); + + assertThat(iterator.next(), notNullValue()); + assertThat(iterator.next(), notNullValue()); + + try { + iterator.next(); + fail("Expected NoSuchElementException"); + } catch (NoSuchElementException e) { + //expected + } + } + } + + @Test @SuppressWarnings("unchecked") + public void testIterationWithSingleLastBatchIsBroken() { + try (CacheManager cacheManager = createTestCacheManager()) { + Cache cache = cacheManager.getCache(testName.getMethodName(), Long.class, byte[].class); + + byte[] data = new byte[101 * 1024]; + cache.put(1L, data); + cache.put(2L, data); + + assertThat(cache, containsInAnyOrder( + isEntry(is(1L), any(byte[].class)), + isEntry(is(2L), any(byte[].class)) + )); + } + } + + @Test + public void testIterationWithConcurrentClearedCacheException() { + try (CacheManager cacheManager = createTestCacheManager()) { + Cache cache = cacheManager.getCache(testName.getMethodName(), Long.class, byte[].class); + + byte[] data = new byte[10 * 1024]; + Set initialKeySet = new HashSet<>(); + range(0, 20).forEach(k -> { + cache.put(k, data); + initialKeySet.add(k); + }); + + Iterator> iterator = cache.iterator(); + + cache.clear(); + + HashSet foundKeys = new HashSet<>(); + try { + while (true) { + assertThat(foundKeys.add(iterator.next().getKey()), is(true)); + } + } catch (NoSuchElementException e) { + //expected + } + foundKeys.forEach(k -> assertThat(k, isIn(initialKeySet))); + } + } + + private CacheManager createTestCacheManager() { + return newCacheManagerBuilder().with(cluster(CLUSTER.getConnectionURI().resolve("/iteration-cm")) + .autoCreate(server -> server.defaultServerResource("primary-server-resource"))) + .withCache(testName.getMethodName(), newCacheConfigurationBuilder(Long.class, byte[].class, newResourcePoolsBuilder() + .with(clusteredDedicated("primary-server-resource", 1, MemoryUnit.MB)))).build(true); + } + + private static Matcher> isEntry(Matcher keyMatcher, Matcher valueMatcher) { + return new TypeSafeMatcher>() { + @Override + public void describeTo(Description description) { + description.appendText(" a cache entry { key ").appendDescriptionOf(keyMatcher).appendText(": value ").appendDescriptionOf(valueMatcher).appendText(" }"); + } + + @Override + protected boolean matchesSafely(Cache.Entry item) { + return keyMatcher.matches(item.getKey()) && valueMatcher.matches(item.getValue()); + } + }; + } +} diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/ClusteredLoaderWriterTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/ClusteredLoaderWriterTest.java index a59acfd07a..74b954a42d 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/ClusteredLoaderWriterTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/ClusteredLoaderWriterTest.java @@ -20,6 +20,7 @@ import org.ehcache.PersistentCacheManager; import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder; import org.ehcache.clustered.client.config.builders.ClusteredStoreConfigurationBuilder; +import org.ehcache.clustered.client.config.builders.TimeoutsBuilder; import org.ehcache.clustered.common.Consistency; import org.ehcache.clustered.util.TestCacheLoaderWriter; import org.ehcache.config.CacheConfiguration; @@ -27,6 +28,10 @@ import org.ehcache.config.builders.CacheManagerBuilder; import org.ehcache.config.builders.ResourcePoolsBuilder; import org.ehcache.config.units.MemoryUnit; +import org.ehcache.core.internal.resilience.ThrowingResilienceStrategy; +import org.ehcache.management.ManagementRegistryService; +import org.ehcache.management.registry.DefaultManagementRegistryConfiguration; +import org.ehcache.management.registry.DefaultManagementRegistryService; import org.junit.Before; import org.junit.BeforeClass; import org.junit.ClassRule; @@ -35,7 +40,7 @@ import org.junit.runners.Parameterized; import org.terracotta.testing.rules.Cluster; -import java.io.File; +import java.time.Duration; import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -45,20 +50,16 @@ import java.util.concurrent.atomic.AtomicInteger; import static org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder.cluster; +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.ehcache.testing.StandardCluster.offheapResource; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; -import static org.terracotta.testing.rules.BasicExternalClusterBuilder.newCluster; -@RunWith(Parameterized.class) -public class ClusteredLoaderWriterTest extends ClusteredTests { - private static final String RESOURCE_CONFIG = - "" - + "" - + "64" - + "" + - "\n"; +@RunWith(Parameterized.class) +public class ClusteredLoaderWriterTest { @Parameterized.Parameters(name = "consistency={0}") public static Consistency[] data() { @@ -75,20 +76,25 @@ public static Consistency[] data() { private ConcurrentMap sor; @ClassRule - public static Cluster CLUSTER = - newCluster().in(new File("build/cluster")).withServiceFragment(RESOURCE_CONFIG).build(); + public static Cluster CLUSTER = newCluster().in(clusterPath()) + .withServiceFragment(offheapResource("primary-server-resource", 64)).build(); @BeforeClass - public static void waitForActive() throws Exception { - CLUSTER.getClusterControl().waitForActive(); + public static void initCacheManager() throws Exception { cacheManager = newCacheManager(); } private static PersistentCacheManager newCacheManager() { + DefaultManagementRegistryConfiguration registryConfiguration = new DefaultManagementRegistryConfiguration().setCacheManagerAlias("myCacheManager1"); + ManagementRegistryService managementRegistry = new DefaultManagementRegistryService(registryConfiguration); return CacheManagerBuilder.newCacheManagerBuilder() .with(cluster(CLUSTER.getConnectionURI()) - .autoCreate() + .timeouts(TimeoutsBuilder.timeouts() + .read(Duration.ofSeconds(30)) + .write(Duration.ofSeconds(30))) + .autoCreate(c -> c) .build()) + .using(managementRegistry) .build(true); } @@ -106,7 +112,8 @@ private CacheConfiguration getCacheConfig() { .heap(20) .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 2, MemoryUnit.MB))) .withLoaderWriter(new TestCacheLoaderWriter(sor)) - .add(ClusteredStoreConfigurationBuilder.withConsistency(cacheConsistency)) + .withService(ClusteredStoreConfigurationBuilder.withConsistency(cacheConsistency)) + .withResilienceStrategy(new ThrowingResilienceStrategy<>()) .build(); } diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/ClusteredTests.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/ClusteredTests.java deleted file mode 100644 index 63e573d6c8..0000000000 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/ClusteredTests.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ehcache.clustered; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Comparator; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; - -/** - * Base class for all clustered tests. It makes sure the environment is correctly configured to launch the servers. Especially - * in the IDE. - */ -public abstract class ClusteredTests { - - private static final boolean FORCE_KIT_REFRESH = false; - - static { - initInstallationPath(); - } - - private static void initInstallationPath() { - if(System.getProperty("kitInstallationPath") != null) { - return; // nothing to do, all set - } - - String currentDir = System.getProperty("user.dir"); - - // We might have the root of ehcache or in the integration-test directory - // as current working directory - String diskPrefix; - if(Paths.get(currentDir).getFileName().toString().equals("integration-test")) { - diskPrefix = ""; - } - else { - diskPrefix = "clustered/integration-test/"; - } - - String kitInstallationPath = getKitInstallationPath(diskPrefix); - - if (kitInstallationPath == null || FORCE_KIT_REFRESH) { - installKit(diskPrefix); - kitInstallationPath = getKitInstallationPath(diskPrefix); - } - - System.setProperty("kitInstallationPath", kitInstallationPath); - } - - private static void installKit(String diskPrefix) { - try { - Process process = new ProcessBuilder(diskPrefix + "../../gradlew", "copyServerLibs") - .redirectError(ProcessBuilder.Redirect.INHERIT) - .redirectOutput(ProcessBuilder.Redirect.INHERIT) - .start(); - int status = process.waitFor(); - assertThat(status).isZero(); - } catch (IOException e) { - fail("Failed to start gradle to install kit", e); - } catch (InterruptedException e) { - fail("Interrupted while installing kit", e); - } - } - - private static String getKitInstallationPath(String diskPrefix) { - String basedir = diskPrefix + "build/ehcache-kit"; - if(!new File(basedir).exists()) { - return null; - } - try { - return Files.list(Paths.get(basedir)) - .sorted(Comparator.naturalOrder().reversed()) // the last one should be the one with the highest version - .findFirst() - .map(path -> path.toAbsolutePath().normalize().toString()) - .orElse(null); - } catch (IOException e) { - fail("Failed to set kitInstallationPath from " + basedir, e); - return null; - } - } -} diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/DestroyLoopTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/DestroyLoopTest.java index 444631d2d0..8e13c95a3f 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/DestroyLoopTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/DestroyLoopTest.java @@ -27,12 +27,10 @@ import org.ehcache.config.builders.ResourcePoolsBuilder; import org.ehcache.config.units.EntryUnit; import org.ehcache.config.units.MemoryUnit; -import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; import org.terracotta.testing.rules.Cluster; -import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -41,29 +39,20 @@ import static org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder.cluster; import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder; +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.ehcache.testing.StandardCluster.offheapResource; import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; -import static org.terracotta.testing.rules.BasicExternalClusterBuilder.newCluster; +import static org.hamcrest.MatcherAssert.assertThat; -public class DestroyLoopTest extends ClusteredTests { - - private static final String RESOURCE_CONFIG = - "" - + "" - + "64" - + "" + - "\n"; +public class DestroyLoopTest { private static final String CACHE_MANAGER_NAME = "/destroy-cm"; private static final String CACHE_NAME = "clustered-cache"; @ClassRule - public static Cluster CLUSTER = newCluster().in(new File("build/cluster")).withServiceFragment(RESOURCE_CONFIG).build(); - - @BeforeClass - public static void waitForActive() throws Exception { - CLUSTER.getClusterControl().waitForActive(); - } + public static Cluster CLUSTER = newCluster().in(clusterPath()) + .withServiceFragment(offheapResource("primary-server-resource", 64)).build(); @Test public void testDestroyLoop() throws Exception { @@ -81,18 +70,18 @@ public void testDestroyLoop() throws Exception { private void destroyCacheManager() throws CachePersistenceException { PersistentCacheManager cacheManager = newCacheManagerBuilder().with( ClusteringServiceConfigurationBuilder.cluster(CLUSTER.getConnectionURI().resolve(CACHE_MANAGER_NAME)) - .expecting()).build(false); + .expecting(c -> c)).build(false); cacheManager.destroy(); } private PersistentCacheManager createCacheManager() { CacheManagerBuilder clusteredCacheManagerBuilder = newCacheManagerBuilder() - .with(cluster(CLUSTER.getConnectionURI().resolve(CACHE_MANAGER_NAME)).autoCreate()) + .with(cluster(CLUSTER.getConnectionURI().resolve(CACHE_MANAGER_NAME)).autoCreate(c -> c)) .withCache(CACHE_NAME, newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder().heap(100, EntryUnit.ENTRIES) .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 2, MemoryUnit.MB))) - .add(new ClusteredStoreConfiguration(Consistency.STRONG))); + .withService(new ClusteredStoreConfiguration(Consistency.STRONG))); return clusteredCacheManagerBuilder.build(true); } diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/EventsFailureBehaviorTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/EventsFailureBehaviorTest.java new file mode 100644 index 0000000000..29e55f0ba9 --- /dev/null +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/EventsFailureBehaviorTest.java @@ -0,0 +1,275 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.clustered; + +import org.ehcache.Cache; +import org.ehcache.CacheManager; +import org.ehcache.PersistentCacheManager; +import org.ehcache.StateTransitionException; +import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder; +import org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder; +import org.ehcache.clustered.reconnect.ThrowingResiliencyStrategy; +import org.ehcache.clustered.util.ParallelTestCluster; +import org.ehcache.clustered.util.runners.Parallel; +import org.ehcache.config.builders.CacheConfigurationBuilder; +import org.ehcache.config.builders.CacheEventListenerConfigurationBuilder; +import org.ehcache.config.builders.CacheManagerBuilder; +import org.ehcache.config.builders.ExpiryPolicyBuilder; +import org.ehcache.config.builders.ResourcePoolsBuilder; +import org.ehcache.config.units.MemoryUnit; +import org.ehcache.event.CacheEvent; +import org.ehcache.event.CacheEventListener; +import org.ehcache.event.EventType; +import org.ehcache.expiry.ExpiryPolicy; +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; +import org.junit.runner.RunWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.time.Duration; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; + +import static java.util.stream.LongStream.range; +import static org.ehcache.clustered.client.config.builders.TimeoutsBuilder.timeouts; +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.ehcache.testing.StandardCluster.offheapResource; +import static org.ehcache.testing.StandardTimeouts.eventually; +import static org.ehcache.event.EventType.CREATED; +import static org.ehcache.event.EventType.EVICTED; +import static org.ehcache.event.EventType.EXPIRED; +import static org.ehcache.event.EventType.REMOVED; +import static org.ehcache.event.EventType.UPDATED; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.either; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.isOneOf; +import static org.hamcrest.Matchers.nullValue; + + +/* + * Eventing behavior is broken across a failover due to actives and passives + * evicting independently. Until this behavior is fixed or at least detectable + * this test cannot reliably assert anything. + */ +@Ignore("Eventing is broken across failover") +@RunWith(Parallel.class) +public class EventsFailureBehaviorTest { + + private static final Logger LOGGER = LoggerFactory.getLogger(EventsFailureBehaviorTest.class); + + private static final int KEYS = 500; + private static final Duration TIMEOUT = Duration.ofSeconds(5); + private static final Duration FAILOVER_TIMEOUT = Duration.ofMinutes(1); + + @ClassRule @Rule + public static final ParallelTestCluster CLUSTER = new ParallelTestCluster(newCluster(2).in(clusterPath()) + .withServiceFragment(offheapResource("primary-server-resource", 64)).build()); + @Rule + public final TestName testName = new TestName(); + + private PersistentCacheManager cacheManager1; + private PersistentCacheManager cacheManager2; + + @Before + public void waitForActive() throws Exception { + CLUSTER.getClusterControl().startAllServers(); + CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); + + cacheManager1 = CacheManagerBuilder.newCacheManagerBuilder() + .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER.getConnectionURI().resolve(testName.getMethodName())) + .timeouts(timeouts().read(Duration.ofSeconds(20)).write(Duration.ofSeconds(20))) + .autoCreate(s -> s.defaultServerResource("primary-server-resource"))).build(true); + + cacheManager2 = CacheManagerBuilder.newCacheManagerBuilder() + .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER.getConnectionURI().resolve(testName.getMethodName())) + .timeouts(timeouts().read(Duration.ofSeconds(20)).write(Duration.ofSeconds(20))) + .autoCreate(s -> s.defaultServerResource("primary-server-resource"))).build(true); + } + + @After + public void tearDown() { + try { + try { + cacheManager1.close(); + } catch (StateTransitionException e) { + LOGGER.warn("Failed to shutdown cache manager", e); + } + } finally { + try { + cacheManager2.close(); + } catch (StateTransitionException e) { + LOGGER.warn("Failed to shutdown cache manager", e); + } + } + } + + private static Cache createCache(CacheManager cacheManager, CacheEventListener cacheEventListener, ExpiryPolicy expiryPolicy) { + return cacheManager.createCache("cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, byte[].class, + ResourcePoolsBuilder.newResourcePoolsBuilder() + .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 4, MemoryUnit.MB))) + .withResilienceStrategy(new ThrowingResiliencyStrategy<>()) + .withService(CacheEventListenerConfigurationBuilder + .newEventListenerConfiguration(cacheEventListener, EnumSet.allOf(EventType.class)) + .unordered().asynchronous()) + .withExpiry(expiryPolicy) + .build()); + } + + private void failover(Cache cache1, Cache cache2) throws Exception { + // failover passive -> active + CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); + CLUSTER.getClusterControl().terminateActive(); + + // wait for clients to be back in business + assertThat(() -> { + try { + cache1.replace(1L, new byte[0], new byte[0]); + cache2.replace(1L, new byte[0], new byte[0]); + return true; + } catch (Exception e) { + return false; + } + }, eventually().is(true)); + } + + @Test @SuppressWarnings("unchecked") + public void testEventsFailover() throws Exception { + AccountingCacheEventListener accountingCacheEventListener1 = new AccountingCacheEventListener<>(); + Cache cache1 = createCache(cacheManager1, accountingCacheEventListener1, ExpiryPolicyBuilder.noExpiration()); + AccountingCacheEventListener accountingCacheEventListener2 = new AccountingCacheEventListener<>(); + Cache cache2 = createCache(cacheManager2, accountingCacheEventListener2, ExpiryPolicyBuilder.noExpiration()); + + + byte[] value = new byte[10 * 1024]; + + range(0, KEYS).forEach(k -> { + cache1.put(k, value); + }); + eventually().runsCleanly(() -> range(0, KEYS).forEach(k -> { + if (cache1.containsKey(k)) { + assertThat(accountingCacheEventListener1.events, hasEntry(is(k), containsInAnyOrder(CREATED))); + assertThat(accountingCacheEventListener2.events, hasEntry(is(k), containsInAnyOrder(accountingCacheEventListener1.events.get(k).toArray()))); + } else { + assertThat(accountingCacheEventListener1.events, hasEntry(is(k), containsInAnyOrder(CREATED, EVICTED))); + assertThat(accountingCacheEventListener2.events, hasEntry(is(k), containsInAnyOrder(accountingCacheEventListener1.events.get(k).toArray()))); + } + })); + + // failover passive -> active + failover(cache1, cache2); + + range(0, KEYS).forEach(k -> { + cache1.put(k, value); + }); + eventually().runsCleanly(() -> range(0, KEYS).forEach(k -> { + if (cache1.containsKey(k)) { + assertThat(accountingCacheEventListener1.events, hasEntry(is(k), + either(containsInAnyOrder(CREATED, UPDATED)) + .or(containsInAnyOrder(CREATED, EVICTED, CREATED)))); + assertThat(accountingCacheEventListener2.events, hasEntry(is(k), containsInAnyOrder(accountingCacheEventListener1.events.get(k).toArray()))); + } else { + assertThat(accountingCacheEventListener1.events, hasEntry(is(k), + either(containsInAnyOrder(CREATED, UPDATED, EVICTED)) + .or(containsInAnyOrder(CREATED, EVICTED, CREATED, EVICTED)))); + assertThat(accountingCacheEventListener2.events, hasEntry(is(k), containsInAnyOrder(accountingCacheEventListener1.events.get(k).toArray()))); + } + })); + + range(0, KEYS).forEach(cache1::remove); + eventually().runsCleanly(() -> range(0, KEYS).forEach(k -> { + assertThat(accountingCacheEventListener1.events, hasEntry(is(k), + either(containsInAnyOrder(CREATED, UPDATED, REMOVED)) + .or(containsInAnyOrder(CREATED, EVICTED, CREATED, REMOVED)) + .or(containsInAnyOrder(CREATED, UPDATED, EVICTED)) + .or(containsInAnyOrder(CREATED, EVICTED, CREATED, EVICTED)))); + assertThat(accountingCacheEventListener2.events, hasEntry(is(k), containsInAnyOrder(accountingCacheEventListener1.events.get(k).toArray()))); + })); + + range(KEYS, KEYS * 2).forEach(k -> { + cache1.put(k, value); + }); + eventually().runsCleanly(() -> range(KEYS, KEYS * 2).forEach(k -> { + if (cache1.containsKey(k)) { + assertThat(accountingCacheEventListener1.events, hasEntry(is(k), containsInAnyOrder(CREATED))); + assertThat(accountingCacheEventListener2.events, hasEntry(is(k), containsInAnyOrder(accountingCacheEventListener1.events.get(k).toArray()))); + } else { + assertThat(accountingCacheEventListener1.events, hasEntry(is(k), containsInAnyOrder(CREATED, EVICTED))); + assertThat(accountingCacheEventListener2.events, hasEntry(is(k), containsInAnyOrder(accountingCacheEventListener1.events.get(k).toArray()))); + } + })); + } + + @Test @SuppressWarnings("unchecked") + public void testExpirationFailover() throws Exception { + AccountingCacheEventListener accountingCacheEventListener1 = new AccountingCacheEventListener<>(); + Cache cache1 = createCache(cacheManager1, accountingCacheEventListener1, ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(1))); + AccountingCacheEventListener accountingCacheEventListener2 = new AccountingCacheEventListener<>(); + Cache cache2 = createCache(cacheManager2, accountingCacheEventListener2, ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(1))); + + + byte[] value = new byte[10 * 1024]; + + range(0, KEYS).forEach(k -> cache1.put(k, value)); + + eventually().runsCleanly(() -> range(0, KEYS).forEach(k -> { + if (cache1.containsKey(k)) { + assertThat(accountingCacheEventListener1.events, hasEntry(is(k), containsInAnyOrder(CREATED))); + assertThat(accountingCacheEventListener2.events, hasEntry(is(k), containsInAnyOrder(accountingCacheEventListener1.events.get(k).toArray()))); + } else { + assertThat(accountingCacheEventListener1.events, hasEntry(is(k), containsInAnyOrder(is(CREATED), isOneOf(EVICTED, EXPIRED)))); + //assertThat(accountingCacheEventListener2.events, hasEntry(is(k), containsInAnyOrder(accountingCacheEventListener1.events.get(k).toArray()))); + assertThat(accountingCacheEventListener2.events, hasEntry(is(k), containsInAnyOrder(is(CREATED), isOneOf(EVICTED, EXPIRED)))); + } + })); + + // failover passive -> active + failover(cache1, cache2); + + range(0, KEYS).forEach(k -> { + assertThat(cache1.get(k), is(nullValue())); + }); + + eventually().runsCleanly(() -> range(0, KEYS).forEach(k -> { + assertThat(accountingCacheEventListener1.events, hasEntry(is(k), containsInAnyOrder(is(CREATED), isOneOf(EVICTED, EXPIRED)))); + //assertThat(accountingCacheEventListener2.events, hasEntry(is(k), containsInAnyOrder(accountingCacheEventListener1.events.get(k).toArray()))); + assertThat(accountingCacheEventListener2.events, hasEntry(is(k), containsInAnyOrder(is(CREATED), isOneOf(EVICTED, EXPIRED)))); + })); + } + + + + static class AccountingCacheEventListener implements CacheEventListener { + private final Map> events = new ConcurrentHashMap<>(); + + @Override + public void onEvent(CacheEvent event) { + events.computeIfAbsent(event.getKey(), key -> new CopyOnWriteArrayList<>()).add(event.getType()); + } + } +} diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/IterationFailureBehaviorTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/IterationFailureBehaviorTest.java new file mode 100644 index 0000000000..5386e8432e --- /dev/null +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/IterationFailureBehaviorTest.java @@ -0,0 +1,191 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.clustered; + +import org.ehcache.Cache; +import org.ehcache.CacheIterationException; +import org.ehcache.PersistentCacheManager; +import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder; +import org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder; +import org.ehcache.clustered.client.internal.store.ServerStoreProxyException; +import org.ehcache.clustered.common.internal.exceptions.InvalidOperationException; +import org.ehcache.config.CacheConfiguration; +import org.ehcache.config.builders.CacheConfigurationBuilder; +import org.ehcache.config.builders.CacheManagerBuilder; +import org.ehcache.config.builders.ResourcePoolsBuilder; +import org.ehcache.config.units.MemoryUnit; +import org.ehcache.spi.resilience.StoreAccessException; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.terracotta.exception.ConnectionClosedException; +import org.terracotta.testing.rules.Cluster; +import org.terracotta.utilities.test.rules.TestRetryer; + +import java.time.Duration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import static java.time.Duration.ofSeconds; +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.toMap; +import static java.util.stream.LongStream.range; +import static org.ehcache.clustered.client.config.builders.TimeoutsBuilder.timeouts; +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.leaseLength; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.ehcache.testing.StandardCluster.offheapResource; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.either; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.core.IsNull.notNullValue; +import static org.junit.Assert.fail; + +import static org.terracotta.utilities.test.rules.TestRetryer.OutputIs.CLASS_RULE; + +public class IterationFailureBehaviorTest { + + private static final int KEYS = 100; + + @ClassRule @Rule + public static final TestRetryer CLUSTER = TestRetryer.tryValues(ofSeconds(1), ofSeconds(10), ofSeconds(30)) + .map(leaseLength -> newCluster(2).in(clusterPath()).withServiceFragment( + offheapResource("primary-server-resource", 64) + leaseLength(leaseLength)).build()) + .outputIs(CLASS_RULE); + + @Before + public void startAllServers() throws Exception { + CLUSTER.get().getClusterControl().startAllServers(); + } + + @Test + public void testIteratorFailover() throws Exception { + final CacheManagerBuilder clusteredCacheManagerBuilder + = CacheManagerBuilder.newCacheManagerBuilder() + .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER.get().getConnectionURI().resolve("/iterator-cm")) + .autoCreate(server -> server.defaultServerResource("primary-server-resource")) + .timeouts(timeouts().read(ofSeconds(10)))); + try (PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(true)) { + CacheConfiguration smallConfig = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, + ResourcePoolsBuilder.newResourcePoolsBuilder() + .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 1, MemoryUnit.MB))).build(); + + Cache smallCache = cacheManager.createCache("small-cache", smallConfig); + range(0, KEYS).forEach(k -> smallCache.put(k, Long.toString(k))); + + CacheConfiguration largeConfig = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, byte[].class, + ResourcePoolsBuilder.newResourcePoolsBuilder() + .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 4, MemoryUnit.MB))).build(); + + Cache largeCache = cacheManager.createCache("large-cache", largeConfig); + byte[] value = new byte[10 * 1024]; + range(0, KEYS).forEach(k -> { + largeCache.put(k, value); + }); + + Map smallMap = new HashMap<>(); + + Iterator> smallIterator = smallCache.iterator(); + Cache.Entry smallNext = smallIterator.next(); + smallMap.put(smallNext.getKey(), smallNext.getValue()); + + Iterator> largeIterator = largeCache.iterator(); + Cache.Entry largeNext = largeIterator.next(); + assertThat(largeCache.get(largeNext.getKey()), notNullValue()); + + CLUSTER.get().getClusterControl().waitForRunningPassivesInStandby(); + CLUSTER.get().getClusterControl().terminateActive(); + + //large iterator fails + try { + largeIterator.forEachRemaining(k -> {}); + fail("Expected CacheIterationException"); + } catch (CacheIterationException e) { + assertThat(e.getCause(), instanceOf(StoreAccessException.class)); + assertThat(e.getCause().getCause(), instanceOf(ServerStoreProxyException.class)); + assertThat(e.getCause().getCause().getCause(), + either(instanceOf(ConnectionClosedException.class)) //lost in the space between active and passive + .or(instanceOf(InvalidOperationException.class))); //picked up by the passive - it doesn't have our iterator + } + + //small iterator completes... it fetched the entire batch in one shot + smallIterator.forEachRemaining(k -> smallMap.put(k.getKey(), k.getValue())); + + assertThat(smallMap, is(range(0, KEYS).boxed().collect(toMap(identity(), k -> Long.toString(k))))); + } + } + + @Test + public void testIteratorReconnect() throws Exception { + final CacheManagerBuilder clusteredCacheManagerBuilder + = CacheManagerBuilder.newCacheManagerBuilder() + .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER.get().getConnectionURI().resolve("/iterator-cm")) + .autoCreate(server -> server.defaultServerResource("primary-server-resource"))); + try (PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(true)) { + CacheConfiguration smallConfig = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, + ResourcePoolsBuilder.newResourcePoolsBuilder() + .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 1, MemoryUnit.MB))).build(); + + Cache smallCache = cacheManager.createCache("small-cache", smallConfig); + range(0, KEYS).forEach(k -> smallCache.put(k, Long.toString(k))); + + CacheConfiguration largeConfig = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, byte[].class, + ResourcePoolsBuilder.newResourcePoolsBuilder() + .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 4, MemoryUnit.MB))).build(); + + Cache largeCache = cacheManager.createCache("large-cache", largeConfig); + byte[] value = new byte[10 * 1024]; + range(0, KEYS).forEach(k -> { + largeCache.put(k, value); + }); + + Map smallMap = new HashMap<>(); + + Iterator> smallIterator = smallCache.iterator(); + Cache.Entry smallNext = smallIterator.next(); + smallMap.put(smallNext.getKey(), smallNext.getValue()); + + Iterator> largeIterator = largeCache.iterator(); + Cache.Entry largeNext = largeIterator.next(); + assertThat(largeCache.get(largeNext.getKey()), notNullValue()); + + CLUSTER.get().getClusterControl().waitForRunningPassivesInStandby(); + CLUSTER.get().getClusterControl().terminateAllServers(); + Thread.sleep(CLUSTER.input().multipliedBy(2L).toMillis()); + CLUSTER.get().getClusterControl().startAllServers(); + + //large iterator fails + try { + largeIterator.forEachRemaining(k -> {}); + fail("Expected CacheIterationException"); + } catch (CacheIterationException e) { + assertThat(e.getCause(), instanceOf(StoreAccessException.class)); + assertThat(e.getCause().getCause(), instanceOf(ServerStoreProxyException.class)); + assertThat(e.getCause().getCause().getCause(), + either(instanceOf(ConnectionClosedException.class)) //lost in the space between the two cluster executions + .or(instanceOf(InvalidOperationException.class))); //picked up by the new cluster - it doesn't have our iterator + } + + //small iterator completes... it fetched the entire batch in one shot + smallIterator.forEachRemaining(k -> smallMap.put(k.getKey(), k.getValue())); + + assertThat(smallMap, is(range(0, KEYS).boxed().collect(toMap(identity(), k -> Long.toString(k))))); + } + } +} diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/JCacheClusteredTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/JCacheClusteredTest.java index 0d8474405f..60959d9c5a 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/JCacheClusteredTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/JCacheClusteredTest.java @@ -16,45 +16,64 @@ package org.ehcache.clustered; +import org.ehcache.testing.ExternalTests; +import org.jsr107.tck.event.CacheListenerTest; +import org.jsr107.tck.spi.CachingProviderTest; +import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.ClassRule; -import org.junit.Test; +import org.junit.runner.RunWith; import org.terracotta.testing.rules.Cluster; -import java.io.File; -import java.net.URL; - import javax.cache.Caching; import javax.cache.spi.CachingProvider; +import java.net.URL; +import java.util.Properties; import static org.ehcache.clustered.CacheManagerLifecycleEhcacheIntegrationTest.substitute; -import static org.terracotta.testing.rules.BasicExternalClusterBuilder.newCluster; +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.ehcache.testing.StandardCluster.offheapResource; + /** - * JCacheClusteredTest + * JCacheClusteredTest - runs the TCK test suite using clustered caches */ -public class JCacheClusteredTest extends ClusteredTests { - - private static final String RESOURCE_CONFIG = - "" - + "" - + "64" - + "" + - "\n"; +@RunWith(ExternalTests.class) +@ExternalTests.From(javax.cache.CachingTest.class) +@ExternalTests.Ignore(value=CachingProviderTest.class, method="getCacheManagerUsingDefaultURI") +@ExternalTests.Ignore(value= CacheListenerTest.class) +public class JCacheClusteredTest { + private static final Properties TCK_PROPERTIES = new Properties(); + static { + TCK_PROPERTIES.setProperty("java.net.preferIPv4Stack", "true"); + TCK_PROPERTIES.setProperty("javax.management.builder.initial", "org.ehcache.jsr107.internal.tck.Eh107MBeanServerBuilder"); + TCK_PROPERTIES.setProperty("org.jsr107.tck.management.agentId", "Eh107MBeanServer"); + TCK_PROPERTIES.setProperty("javax.cache.CacheManager", "org.ehcache.CacheManager"); + TCK_PROPERTIES.setProperty("javax.cache.Cache", "org.ehcache.Cache"); + TCK_PROPERTIES.setProperty("javax.cache.Cache.Entry", "org.ehcache.Cache$Entry"); + TCK_PROPERTIES.setProperty("javax.cache.annotation.CacheInvocationContext", "javax.cache.annotation.impl.cdi.CdiCacheKeyInvocationContextImpl"); + } @ClassRule - public static Cluster CLUSTER = newCluster().in(new File("build/cluster")).withServiceFragment(RESOURCE_CONFIG).build(); + public static Cluster CLUSTER = newCluster().in(clusterPath()) + .withServiceFragment(offheapResource("primary", 256)).build(); @BeforeClass - public static void waitForActive() throws Exception { - CLUSTER.getClusterControl().waitForActive(); - } - - @Test - public void testJCacheClustered() throws Exception { + public static void configureEnvironment() throws Exception { URL xml = CacheManagerLifecycleEhcacheIntegrationTest.class.getResource("/configs/jcache-clustered.xml"); URL substitutedXml = substitute(xml, "cluster-uri", CLUSTER.getConnectionURI().toString()); - CachingProvider cachingProvider = Caching.getCachingProvider(); - cachingProvider.getCacheManager(substitutedXml.toURI(), getClass().getClassLoader()); + System.setProperty("ehcache.jsr107.config.default", substitutedXml.toURI().toString()); + TCK_PROPERTIES.forEach((k, v) -> System.setProperty(k.toString(), v.toString())); + } + + @AfterClass + public static void cleanup() { + try { + Caching.getCachingProviders().forEach(CachingProvider::close); + } finally { + System.clearProperty("ehcache.jsr107.config.default"); + TCK_PROPERTIES.forEach((k, v) -> System.clearProperty(k.toString())); + } } } diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/LeaseTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/LeaseTest.java index c5025a2b74..1e2c1e541e 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/LeaseTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/LeaseTest.java @@ -15,81 +15,60 @@ */ package org.ehcache.clustered; -import com.tc.net.proxy.TCPProxy; import org.ehcache.Cache; import org.ehcache.PersistentCacheManager; import org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder; import org.ehcache.clustered.client.config.builders.TimeoutsBuilder; -import org.ehcache.clustered.util.TCPProxyUtil; +import org.ehcache.clustered.util.TCPProxyManager; import org.ehcache.config.CacheConfiguration; import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.config.builders.CacheManagerBuilder; import org.ehcache.config.builders.ResourcePoolsBuilder; import org.ehcache.config.units.EntryUnit; import org.ehcache.config.units.MemoryUnit; -import org.junit.After; -import org.junit.BeforeClass; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.terracotta.testing.rules.Cluster; +import org.terracotta.utilities.test.rules.TestRetryer; -import java.io.File; import java.net.URI; import java.time.Duration; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; +import static java.time.Duration.ofSeconds; import static org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder.clusteredDedicated; -import static org.ehcache.clustered.util.TCPProxyUtil.setDelay; +import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder; +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.leaseLength; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.ehcache.testing.StandardCluster.offheapResource; +import static org.ehcache.testing.StandardTimeouts.eventually; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; -import static org.terracotta.testing.rules.BasicExternalClusterBuilder.newCluster; -@RunWith(Parameterized.class) -public class LeaseTest extends ClusteredTests { +import static org.terracotta.utilities.test.rules.TestRetryer.OutputIs.CLASS_RULE; +import static org.terracotta.utilities.test.rules.TestRetryer.tryValues; - private static final String RESOURCE_CONFIG = - "" - + "" - + "64" - + "" - + "\n" - + "" - + "" - + "5" - + "" - + ""; +@RunWith(Parameterized.class) +public class LeaseTest { @ClassRule - public static Cluster CLUSTER = - newCluster().in(new File("build/cluster")).withServiceFragment(RESOURCE_CONFIG).build(); - - private final List proxies = new ArrayList<>(); - - @BeforeClass - public static void waitForActive() throws Exception { - CLUSTER.getClusterControl().waitForActive(); - } - - @After - public void after() { - proxies.forEach(TCPProxy::stop); - } + @Rule + public static final TestRetryer CLUSTER = tryValues(ofSeconds(1), ofSeconds(10), ofSeconds(30)) + .map(leaseLength -> newCluster().in(clusterPath()).withServiceFragment( + offheapResource("primary-server-resource", 64) + leaseLength(leaseLength)).build()) + .outputIs(CLASS_RULE); @Parameterized.Parameters public static ResourcePoolsBuilder[] data() { return new ResourcePoolsBuilder[]{ - ResourcePoolsBuilder.newResourcePoolsBuilder() - .with(clusteredDedicated("primary-server-resource", 1, MemoryUnit.MB)), - ResourcePoolsBuilder.newResourcePoolsBuilder() - .heap(10, EntryUnit.ENTRIES) - .with(clusteredDedicated("primary-server-resource", 1, MemoryUnit.MB)) + ResourcePoolsBuilder.newResourcePoolsBuilder() + .with(clusteredDedicated("primary-server-resource", 1, MemoryUnit.MB)), + ResourcePoolsBuilder.newResourcePoolsBuilder() + .heap(10, EntryUnit.ENTRIES) + .with(clusteredDedicated("primary-server-resource", 1, MemoryUnit.MB)) }; } @@ -98,59 +77,40 @@ public static ResourcePoolsBuilder[] data() { @Test public void leaseExpiry() throws Exception { - URI connectionURI = TCPProxyUtil.getProxyURI(CLUSTER.getConnectionURI(), proxies); - - CacheManagerBuilder clusteredCacheManagerBuilder - = CacheManagerBuilder.newCacheManagerBuilder() - .with(ClusteringServiceConfigurationBuilder.cluster(connectionURI.resolve("/crud-cm")) - .timeouts(TimeoutsBuilder.timeouts() - .connection(Duration.ofSeconds(20))) - .autoCreate() - .defaultServerResource("primary-server-resource")); - PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(false); - cacheManager.init(); - - CacheConfiguration config = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, - resourcePoolsBuilder).build(); - - Cache cache = cacheManager.createCache("clustered-cache", config); - cache.put(1L, "The one"); - cache.put(2L, "The two"); - cache.put(3L, "The three"); - assertThat(cache.get(1L), equalTo("The one")); - assertThat(cache.get(2L), equalTo("The two")); - assertThat(cache.get(3L), equalTo("The three")); - - setDelay(6000, proxies); - Thread.sleep(6000); - // We will now have lost the lease - - setDelay(0L, proxies); - - AtomicBoolean timedout = new AtomicBoolean(false); - - CompletableFuture future = CompletableFuture.supplyAsync(() -> { - while (!timedout.get()) { - try { - Thread.sleep(200); - } catch (InterruptedException e) { - throw new AssertionError(e); - } - String result = cache.get(1L); - if (result != null) { - return result; - } + try (TCPProxyManager proxyManager = TCPProxyManager.create(CLUSTER.get().getConnectionURI())) { + URI connectionURI = proxyManager.getURI(); + + CacheManagerBuilder clusteredCacheManagerBuilder = newCacheManagerBuilder() + .with(ClusteringServiceConfigurationBuilder.cluster(connectionURI.resolve("/crud-cm")) + .timeouts(TimeoutsBuilder.timeouts().connection(Duration.ofSeconds(20))) + .autoCreate(server -> server.defaultServerResource("primary-server-resource"))); + PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(false); + cacheManager.init(); + + CacheConfiguration config = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, + resourcePoolsBuilder).build(); + + Cache cache = cacheManager.createCache("clustered-cache", config); + cache.put(1L, "The one"); + cache.put(2L, "The two"); + cache.put(3L, "The three"); + assertThat(cache.get(1L), equalTo("The one")); + assertThat(cache.get(2L), equalTo("The two")); + assertThat(cache.get(3L), equalTo("The three")); + + long delay = CLUSTER.input().plusSeconds(1L).toMillis(); + proxyManager.setDelay(delay); + try { + Thread.sleep(delay); + } finally { + proxyManager.setDelay(0); } - return null; - }); - - assertThat(future.get(30, TimeUnit.SECONDS), is("The one")); - - timedout.set(true); - - assertThat(cache.get(2L), equalTo("The two")); - assertThat(cache.get(3L), equalTo("The three")); + eventually().runsCleanly(() -> { + assertThat(cache.get(1L), equalTo("The one")); + assertThat(cache.get(2L), equalTo("The two")); + assertThat(cache.get(3L), equalTo("The three")); + }); + } } - } diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/NoOffheapTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/NoOffheapTest.java new file mode 100644 index 0000000000..9821243cb8 --- /dev/null +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/NoOffheapTest.java @@ -0,0 +1,55 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.clustered; + +import org.ehcache.StateTransitionException; +import org.ehcache.config.units.MemoryUnit; +import org.junit.ClassRule; +import org.junit.Test; +import org.terracotta.testing.rules.Cluster; + +import static java.util.function.UnaryOperator.identity; +import static org.assertj.core.api.Assertions.assertThat; +import static org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder.clusteredDedicated; +import static org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder.cluster; +import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; +import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder; +import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.junit.Assert.fail; + + +public class NoOffheapTest { + + @ClassRule + public static Cluster CLUSTER = newCluster().in(clusterPath()).build(); + + @Test + public void testNoOffheap() throws InterruptedException { + try { + newCacheManagerBuilder().with(cluster(CLUSTER.getConnectionURI().resolve("/no-offheap-cm")) + .autoCreate(identity())) + .withCache("testNoOffheap", newCacheConfigurationBuilder(Long.class, byte[].class, newResourcePoolsBuilder() + .with(clusteredDedicated("primary-server-resource", 1, MemoryUnit.MB)) + )).build(true).close(); + fail(); + } catch (StateTransitionException e) { + assertThat(e).hasMessage("Could not create the cluster tier manager 'no-offheap-cm'."); + } + } +} diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/OversizedCacheOpsTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/OversizedCacheOpsTest.java index 210a781176..9ceb14c2a3 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/OversizedCacheOpsTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/OversizedCacheOpsTest.java @@ -25,40 +25,32 @@ import org.ehcache.config.builders.CacheManagerBuilder; import org.ehcache.config.builders.ResourcePoolsBuilder; import org.ehcache.config.units.MemoryUnit; -import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; import org.terracotta.testing.rules.Cluster; -import java.io.File; import java.util.Arrays; +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.ehcache.testing.StandardCluster.offheapResource; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; -import static org.terracotta.testing.rules.BasicExternalClusterBuilder.newCluster; -public class OversizedCacheOpsTest extends ClusteredTests { - - private static final String RESOURCE_CONFIG = - "" - + "" - + "2" - + "" + - "\n"; +public class OversizedCacheOpsTest { @ClassRule - public static Cluster CLUSTER = - newCluster().in(new File("build/cluster")).withServiceFragment(RESOURCE_CONFIG).build(); + public static Cluster CLUSTER = newCluster().in(clusterPath()) + .withServiceFragment(offheapResource("primary-server-resource", 2)).build(); @Test public void overSizedCacheOps() throws Exception { CacheManagerBuilder clusteredCacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder() .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER.getConnectionURI().resolve("/crud-cm")) - .autoCreate() - .defaultServerResource("primary-server-resource")); + .autoCreate(server -> server.defaultServerResource("primary-server-resource"))); try (PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(true)) { CacheConfiguration config = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/ReconnectDuringDestroyTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/ReconnectDuringDestroyTest.java new file mode 100644 index 0000000000..12867958d1 --- /dev/null +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/ReconnectDuringDestroyTest.java @@ -0,0 +1,203 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.clustered; + +import org.ehcache.Cache; +import org.ehcache.PersistentCacheManager; +import org.ehcache.clustered.client.config.ClusteringServiceConfiguration; +import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder; +import org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder; +import org.ehcache.clustered.client.internal.ClusterTierManagerClientEntity; +import org.ehcache.clustered.client.internal.lock.VoltronReadWriteLock; +import org.ehcache.clustered.client.service.EntityBusyException; +import org.ehcache.clustered.common.internal.ClusterTierManagerConfiguration; +import org.ehcache.clustered.reconnect.ThrowingResiliencyStrategy; +import org.ehcache.clustered.util.TCPProxyManager; +import org.ehcache.config.CacheConfiguration; +import org.ehcache.config.builders.CacheConfigurationBuilder; +import org.ehcache.config.builders.CacheManagerBuilder; +import org.ehcache.config.builders.ResourcePoolsBuilder; +import org.ehcache.config.units.MemoryUnit; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.terracotta.connection.Connection; +import org.terracotta.connection.entity.EntityRef; +import org.terracotta.exception.EntityNotFoundException; +import org.terracotta.lease.connection.LeasedConnectionFactory; +import org.terracotta.testing.rules.Cluster; + +import org.terracotta.utilities.test.rules.TestRetryer; + +import java.time.Duration; +import java.util.Properties; + +import static java.time.Duration.ofSeconds; +import static org.ehcache.clustered.common.EhcacheEntityVersion.ENTITY_VERSION; +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.leaseLength; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.ehcache.testing.StandardCluster.offheapResource; +import static org.ehcache.testing.StandardTimeouts.eventually; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; + +import static org.terracotta.utilities.test.rules.TestRetryer.OutputIs.CLASS_RULE; +import static org.terracotta.utilities.test.rules.TestRetryer.tryValues; + +/** + * ReconnectDuringDestroyTest + */ +public class ReconnectDuringDestroyTest { + + private static TCPProxyManager proxyManager; + PersistentCacheManager cacheManager; + + @ClassRule @Rule + public static final TestRetryer CLUSTER = tryValues(ofSeconds(1), ofSeconds(10), ofSeconds(3)) + .map(leaseLength -> newCluster().in(clusterPath()).withServiceFragment( + offheapResource("primary-server-resource", 64) + leaseLength(leaseLength)).build()) + .outputIs(CLASS_RULE); + + @BeforeClass + public static void initializeProxy() throws Exception { + proxyManager = TCPProxyManager.create(CLUSTER.get().getConnectionURI()); + } + + @AfterClass + public static void closeProxy() { + proxyManager.close(); + } + + @Before + public void initializeCacheManager() { + ClusteringServiceConfiguration clusteringConfiguration = + ClusteringServiceConfigurationBuilder.cluster(proxyManager.getURI().resolve("/crud-cm")) + .autoCreate(server -> server.defaultServerResource("primary-server-resource")).build(); + + CacheManagerBuilder clusteredCacheManagerBuilder + = CacheManagerBuilder.newCacheManagerBuilder().with(clusteringConfiguration); + cacheManager = clusteredCacheManagerBuilder.build(false); + cacheManager.init(); + } + + /* + This is to test the scenario in which reconnect happens while cache manager + destruction is in progress. This test checks whether the cache manager + gets destructed properly in the reconnect path once the connection is closed + after the prepareForDestroy() call. + */ + @Test + public void reconnectDuringDestroyTest() throws Exception { + cacheManager.close(); + Connection client = null; + try { + client = LeasedConnectionFactory.connect(proxyManager.getURI(), new Properties()); + VoltronReadWriteLock voltronReadWriteLock = new VoltronReadWriteLock(client, "crud-cm"); + try (VoltronReadWriteLock.Hold localMaintenance = voltronReadWriteLock.tryWriteLock()) { + if (localMaintenance == null) { + throw new EntityBusyException("Unable to obtain maintenance lease for " + "crud-cm"); + } + EntityRef ref = getEntityRef(client); + try { + ClusterTierManagerClientEntity entity = ref.fetchEntity(null); + entity.prepareForDestroy(); + entity.close(); + } catch (EntityNotFoundException e) { + Assert.fail(); + } + } + // For reconnection. + long delay = CLUSTER.input().plusSeconds(1).toMillis(); + proxyManager.setDelay(delay); + try { + Thread.sleep(delay); + } finally { + proxyManager.setDelay(0); + } + client = LeasedConnectionFactory.connect(proxyManager.getURI(), new Properties()); + + // For mimicking the cacheManager.destroy() in the reconnect path. + voltronReadWriteLock = new VoltronReadWriteLock(client, "crud-cm"); + try (VoltronReadWriteLock.Hold localMaintenance = voltronReadWriteLock.tryWriteLock()) { + if (localMaintenance == null) { + throw new EntityBusyException("Unable to obtain maintenance lease for " + "crud-cm"); + } + EntityRef ref = getEntityRef(client); + try { + ClusterTierManagerClientEntity entity = ref.fetchEntity(null); + entity.prepareForDestroy(); + entity.close(); + } catch (EntityNotFoundException e) { + Assert.fail("Unexpected exception " + e.getMessage()); + } + if (!ref.destroy()) { + Assert.fail("Unexpected exception while trying to destroy cache manager"); + } + } + } finally { + if (client != null) { + client.close(); + } + } + } + + @Test + public void reconnectAfterDestroyOneOfTheCache() throws Exception { + try { + CacheConfiguration config = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, + ResourcePoolsBuilder.newResourcePoolsBuilder() + .with(ClusteredResourcePoolBuilder. + clusteredDedicated("primary-server-resource", 1, MemoryUnit.MB))) + .withResilienceStrategy(new ThrowingResiliencyStrategy<>()) + .build(); + Cache cache1 = cacheManager.createCache("clustered-cache-1", config); + Cache cache2 = cacheManager.createCache("clustered-cache-2", config); + cache1.put(1L, "The one"); + cache1.put(2L, "The two"); + cache2.put(1L, "The one"); + cache2.put(2L, "The two"); + cacheManager.destroyCache("clustered-cache-1"); + + // For reconnection. + long delay = CLUSTER.input().plusSeconds(1L).toMillis(); + proxyManager.setDelay(delay); + try { + Thread.sleep(delay); + } finally { + proxyManager.setDelay(0); + } + + Cache cache2Again = cacheManager.getCache("clustered-cache-2", Long.class, String.class); + eventually().runsCleanly(() -> { + assertThat(cache2Again.get(1L), equalTo("The one")); + assertThat(cache2Again.get(2L), equalTo("The two")); + }); + cache2Again.put(3L, "The three"); + assertThat(cache2Again.get(3L), equalTo("The three")); + } finally { + cacheManager.close(); + } + } + + private EntityRef getEntityRef(Connection client) throws org.terracotta.exception.EntityNotProvidedException { + return client.getEntityRef(ClusterTierManagerClientEntity.class, ENTITY_VERSION, "crud-cm"); + } +} diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/ResourcePoolAllocationFailureTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/ResourcePoolAllocationFailureTest.java index d067541f17..8799fc36eb 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/ResourcePoolAllocationFailureTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/ResourcePoolAllocationFailureTest.java @@ -16,48 +16,35 @@ package org.ehcache.clustered; -import org.ehcache.CachePersistenceException; +import org.ehcache.clustered.client.internal.PerpetualCachePersistenceException; import org.ehcache.PersistentCacheManager; import org.ehcache.clustered.client.config.ClusteredStoreConfiguration; +import org.ehcache.clustered.client.config.ClusteringServiceConfiguration; import org.ehcache.clustered.client.config.DedicatedClusteredResourcePool; import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder; import org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder; -import org.ehcache.clustered.client.config.builders.ServerSideConfigurationBuilder; import org.ehcache.clustered.common.Consistency; import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.config.builders.CacheManagerBuilder; import org.ehcache.config.builders.ResourcePoolsBuilder; import org.ehcache.config.units.MemoryUnit; -import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; import org.terracotta.testing.rules.Cluster; -import java.io.File; - +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.ehcache.testing.StandardCluster.offheapResource; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.startsWith; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; -import static org.terracotta.testing.rules.BasicExternalClusterBuilder.newCluster; - -public class ResourcePoolAllocationFailureTest extends ClusteredTests { - private static final String RESOURCE_CONFIG = - "" - + "" - + "64" - + "" + - "\n"; +public class ResourcePoolAllocationFailureTest { @ClassRule - public static Cluster CLUSTER = - newCluster().in(new File("build/cluster")).withServiceFragment(RESOURCE_CONFIG).build(); - - @BeforeClass - public static void waitForActive() throws Exception { - CLUSTER.getClusterControl().waitForActive(); - } + public static Cluster CLUSTER = newCluster().in(clusterPath()) + .withServiceFragment(offheapResource("primary-server-resource", 64)).build(); @Test public void testTooLowResourceException() throws InterruptedException { @@ -69,7 +56,7 @@ public void testTooLowResourceException() throws InterruptedException { cacheManagerBuilder.build(true); fail("InvalidServerStoreConfigurationException expected"); } catch (Exception e) { - Throwable cause = getCause(e, CachePersistenceException.class); + Throwable cause = getCause(e, PerpetualCachePersistenceException.class); assertThat(cause, notNullValue()); assertThat(cause.getMessage(), startsWith("Unable to create")); } @@ -85,15 +72,14 @@ public void testTooLowResourceException() throws InterruptedException { private CacheManagerBuilder getPersistentCacheManagerCacheManagerBuilder(DedicatedClusteredResourcePool resourcePool) { ClusteringServiceConfigurationBuilder clusteringServiceConfigurationBuilder = ClusteringServiceConfigurationBuilder.cluster(CLUSTER.getConnectionURI().resolve("/crud-cm")); - ServerSideConfigurationBuilder serverSideConfigurationBuilder = clusteringServiceConfigurationBuilder.autoCreate() - .defaultServerResource("primary-server-resource"); + ClusteringServiceConfiguration clusteringConfiguration = clusteringServiceConfigurationBuilder.autoCreate(server -> server.defaultServerResource("primary-server-resource")).build(); return CacheManagerBuilder.newCacheManagerBuilder() - .with(serverSideConfigurationBuilder) + .with(clusteringConfiguration) .withCache("test-cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .with(resourcePool) - ).add(new ClusteredStoreConfiguration(Consistency.EVENTUAL))); + ).withService(new ClusteredStoreConfiguration(Consistency.EVENTUAL))); } private static Throwable getCause(Throwable e, Class causeClass) { diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/TerminatedServerTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/TerminatedServerTest.java index d3856aade0..793d03afcf 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/TerminatedServerTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/TerminatedServerTest.java @@ -16,24 +16,23 @@ package org.ehcache.clustered; -import com.google.code.tempusfugit.concurrency.ConcurrentTestRunner; - import org.assertj.core.api.ThrowableAssertAlternative; import org.ehcache.Cache; import org.ehcache.CacheManager; import org.ehcache.CachePersistenceException; -import org.ehcache.Diagnostics; import org.ehcache.PersistentCacheManager; import org.ehcache.StateTransitionException; import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder; import org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder; import org.ehcache.clustered.client.config.builders.TimeoutsBuilder; +import org.ehcache.clustered.util.ParallelTestCluster; +import org.ehcache.clustered.util.runners.Parallel; import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.config.builders.CacheManagerBuilder; import org.ehcache.config.builders.ResourcePoolsBuilder; import org.ehcache.config.units.MemoryUnit; import org.ehcache.core.spi.service.StatisticsService; -import org.ehcache.impl.internal.statistics.DefaultStatisticsService; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; @@ -41,21 +40,16 @@ import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExternalResource; -import org.junit.rules.RuleChain; import org.junit.rules.TestName; -import org.junit.rules.TestRule; -import org.junit.runner.Description; import org.junit.runner.RunWith; -import org.junit.runners.model.Statement; -import org.terracotta.testing.rules.Cluster; +import org.terracotta.utilities.test.Diagnostics; import com.tc.net.protocol.transport.ClientMessageTransport; import com.tc.properties.TCProperties; import com.tc.properties.TCPropertiesConsts; import com.tc.properties.TCPropertiesImpl; +import org.terracotta.utilities.test.rules.TestRetryer; -import java.io.File; import java.time.Duration; import java.time.temporal.ChronoUnit; import java.util.HashMap; @@ -63,14 +57,20 @@ import java.util.Map; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; -import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import static java.time.Duration.ofSeconds; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.junit.Assume.assumeNoException; -import static org.terracotta.testing.rules.BasicExternalClusterBuilder.newCluster; + +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.leaseLength; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.ehcache.testing.StandardCluster.offheapResource; +import static org.terracotta.utilities.test.rules.TestRetryer.OutputIs.CLASS_RULE; +import static org.terracotta.utilities.test.rules.TestRetryer.OutputIs.RULE; +import static org.terracotta.utilities.test.rules.TestRetryer.tryValues; /** * Provides integration tests in which the server is terminated before the Ehcache operation completes. @@ -84,52 +84,11 @@ // and stopping a server for each test. Each test and the environment supporting it must have // no side effects which can affect another test. // ============================================================================================= -@RunWith(ConcurrentTestRunner.class) -public class TerminatedServerTest extends ClusteredTests { +@RunWith(Parallel.class) +public class TerminatedServerTest { private static final int CLIENT_MAX_PENDING_REQUESTS = 5; - /** - * Determines the level of test concurrency. The number of allowed concurrent tests - * is set in {@link #setConcurrency()}. - */ - private static final Semaphore TEST_PERMITS = new Semaphore(0); - - @ClassRule - public static final TestCounter TEST_COUNTER = new TestCounter(); - - @BeforeClass - public static void setConcurrency() { - int availableProcessors = Runtime.getRuntime().availableProcessors(); - int testCount = TEST_COUNTER.getTestCount(); - /* - * Some build environments can't reliably handle running tests in this class concurrently. - * If the 'disable.concurrent.tests' system property is 'true', restrict the tests to - * single operation using a single test permit. - */ - boolean disableConcurrentTests = Boolean.getBoolean("disable.concurrent.tests"); - if (disableConcurrentTests) { - TEST_PERMITS.release(1); - } else { - TEST_PERMITS.release(Math.min(Math.max(1, testCount / 2), availableProcessors)); - } - System.out.format("TerminatedServerTest:" + - " disableConcurrentTests=%b, testCount=%d, availableProcessors=%d, TEST_PERMITS.availablePermits()=%d%n", - disableConcurrentTests, testCount, availableProcessors, TEST_PERMITS.availablePermits()); - } - - private static final String RESOURCE_CONFIG = - "" - + "" - + "64" - + "" - + "" - + "" - + "" - + "5" - + "" - + "\n"; - private static Map OLD_PROPERTIES; @BeforeClass @@ -169,34 +128,19 @@ private ThrowableAssertAlternative assertExceptionOccur .isThrownBy(() -> task.run()); } - private static Cluster createCluster() { - try { - return newCluster().in(new File("build/cluster")).withServiceFragment(RESOURCE_CONFIG).build(); - } catch (IllegalArgumentException e) { - assumeNoException(e); - return null; - } - } + @ClassRule @Rule + public static final TestRetryer CLUSTER = tryValues(ofSeconds(2), ofSeconds(10), ofSeconds(30)) + .map(leaseLength -> new ParallelTestCluster( + newCluster().in(clusterPath()).withServiceFragment( + offheapResource("primary-server-resource", 64) + leaseLength(leaseLength)).build())) + .outputIs(CLASS_RULE, RULE); @Rule public final TestName testName = new TestName(); - // Included in 'ruleChain' below. - private final Cluster cluster = createCluster(); - - - // The TestRule.apply method is called on the inner-most Rule first with the result being passed to each - // successively outer rule until the outer-most rule is reached. For ExternalResource rules, the before - // method of each rule is called from outer-most rule to inner-most rule; the after method is called from - // inner-most to outer-most. - @Rule - public final RuleChain ruleChain = RuleChain - .outerRule(new TestConcurrencyLimiter()) - .around(cluster); - @Before - public void waitForActive() throws Exception { - cluster.getClusterControl().waitForActive(); + public void startAllServers() throws Exception { + CLUSTER.get().getClusterControl().startAllServers(); } /** @@ -206,15 +150,14 @@ public void waitForActive() throws Exception { public void testTerminationBeforeCacheManagerClose() throws Exception { CacheManagerBuilder clusteredCacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder() - .with(ClusteringServiceConfigurationBuilder.cluster(cluster.getConnectionURI().resolve("/MyCacheManagerName")) - .autoCreate() - .defaultServerResource("primary-server-resource")); + .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER.get().getConnectionURI().resolve("/").resolve(testName.getMethodName())) + .autoCreate(server -> server.defaultServerResource("primary-server-resource"))); PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(false); cacheManager.init(); - cluster.getClusterControl().terminateAllServers(); + CLUSTER.get().getClusterControl().terminateAllServers(); - new TimeLimitedTask(10, TimeUnit.SECONDS) { + new TimeLimitedTask(CLUSTER.input().plusSeconds(10)) { @Override Void runTask() throws Exception { cacheManager.close(); @@ -229,9 +172,8 @@ Void runTask() throws Exception { public void testTerminationBeforeCacheManagerCloseWithCaches() throws Exception { CacheManagerBuilder clusteredCacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder() - .with(ClusteringServiceConfigurationBuilder.cluster(cluster.getConnectionURI().resolve("/MyCacheManagerName")) - .autoCreate() - .defaultServerResource("primary-server-resource")) + .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER.get().getConnectionURI().resolve("/").resolve(testName.getMethodName())) + .autoCreate(server -> server.defaultServerResource("primary-server-resource"))) .withCache("simple-cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() @@ -239,7 +181,7 @@ public void testTerminationBeforeCacheManagerCloseWithCaches() throws Exception PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(false); cacheManager.init(); - cluster.getClusterControl().terminateAllServers(); + CLUSTER.get().getClusterControl().terminateAllServers(); cacheManager.close(); @@ -248,22 +190,21 @@ public void testTerminationBeforeCacheManagerCloseWithCaches() throws Exception @Test public void testTerminationBeforeCacheManagerRetrieve() throws Exception { // Close all servers - cluster.getClusterControl().terminateAllServers(); + CLUSTER.get().getClusterControl().terminateAllServers(); // Try to retrieve an entity (that doesn't exist but I don't care... the server is not running anyway CacheManagerBuilder clusteredCacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder() - .with(ClusteringServiceConfigurationBuilder.cluster(cluster.getConnectionURI().resolve("/MyCacheManagerName")) + .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER.get().getConnectionURI().resolve("/").resolve(testName.getMethodName())) .timeouts(TimeoutsBuilder.timeouts().connection(Duration.ofSeconds(1))) // Need a connection timeout shorter than the TimeLimitedTask timeout - .expecting() - .defaultServerResource("primary-server-resource")); + .expecting(server -> server.defaultServerResource("primary-server-resource"))); PersistentCacheManager cacheManagerExisting = clusteredCacheManagerBuilder.build(false); // Base test time limit on observed TRANSPORT_HANDSHAKE_SYNACK_TIMEOUT; might not have been set in time to be effective long synackTimeout = TimeUnit.MILLISECONDS.toSeconds(ClientMessageTransport.TRANSPORT_HANDSHAKE_SYNACK_TIMEOUT); assertExceptionOccurred(StateTransitionException.class, - new TimeLimitedTask(3 + synackTimeout, TimeUnit.SECONDS) { + new TimeLimitedTask(ofSeconds(3 + synackTimeout)) { @Override Void runTask() { cacheManagerExisting.init(); @@ -278,9 +219,8 @@ Void runTask() { public void testTerminationBeforeCacheManagerDestroyCache() throws Exception { CacheManagerBuilder clusteredCacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder() - .with(ClusteringServiceConfigurationBuilder.cluster(cluster.getConnectionURI().resolve("/MyCacheManagerName")) - .autoCreate() - .defaultServerResource("primary-server-resource")) + .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER.get().getConnectionURI().resolve("/").resolve(testName.getMethodName())) + .autoCreate(server -> server.defaultServerResource("primary-server-resource"))) .withCache("simple-cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() @@ -295,10 +235,10 @@ public void testTerminationBeforeCacheManagerDestroyCache() throws Exception { cacheManager.removeCache("simple-cache"); - cluster.getClusterControl().terminateAllServers(); + CLUSTER.get().getClusterControl().terminateAllServers(); assertExceptionOccurred(CachePersistenceException.class, - new TimeLimitedTask(10, TimeUnit.SECONDS) { + new TimeLimitedTask(ofSeconds(10)) { @Override Void runTask() throws Exception { cacheManager.destroyCache("simple-cache"); @@ -312,16 +252,15 @@ Void runTask() throws Exception { public void testTerminationBeforeCacheCreate() throws Exception { CacheManagerBuilder clusteredCacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder() - .with(ClusteringServiceConfigurationBuilder.cluster(cluster.getConnectionURI().resolve("/MyCacheManagerName")) - .autoCreate() - .defaultServerResource("primary-server-resource")); + .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER.get().getConnectionURI().resolve("/").resolve(testName.getMethodName())) + .autoCreate(server -> server.defaultServerResource("primary-server-resource"))); PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(false); cacheManager.init(); - cluster.getClusterControl().terminateAllServers(); + CLUSTER.get().getClusterControl().terminateAllServers(); assertExceptionOccurred(IllegalStateException.class, - new TimeLimitedTask>(10, TimeUnit.SECONDS) { + new TimeLimitedTask>(ofSeconds(10)) { @Override Cache runTask() throws Exception { return cacheManager.createCache("simple-cache", @@ -337,9 +276,8 @@ Cache runTask() throws Exception { public void testTerminationBeforeCacheRemove() throws Exception { CacheManagerBuilder clusteredCacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder() - .with(ClusteringServiceConfigurationBuilder.cluster(cluster.getConnectionURI().resolve("/MyCacheManagerName")) - .autoCreate() - .defaultServerResource("primary-server-resource")) + .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER.get().getConnectionURI().resolve("/").resolve(testName.getMethodName())) + .autoCreate(server -> server.defaultServerResource("primary-server-resource"))) .withCache("simple-cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() @@ -347,7 +285,7 @@ public void testTerminationBeforeCacheRemove() throws Exception { PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(false); cacheManager.init(); - cluster.getClusterControl().terminateAllServers(); + CLUSTER.get().getClusterControl().terminateAllServers(); cacheManager.removeCache("simple-cache"); } @@ -356,10 +294,9 @@ public void testTerminationBeforeCacheRemove() throws Exception { public void testTerminationThenGet() throws Exception { CacheManagerBuilder clusteredCacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder() - .with(ClusteringServiceConfigurationBuilder.cluster(cluster.getConnectionURI().resolve("/MyCacheManagerName")) + .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER.get().getConnectionURI().resolve("/").resolve(testName.getMethodName())) .timeouts(TimeoutsBuilder.timeouts().read(Duration.of(1, ChronoUnit.SECONDS)).build()) - .autoCreate() - .defaultServerResource("primary-server-resource")) + .autoCreate(server -> server.defaultServerResource("primary-server-resource"))) .withCache("simple-cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() @@ -374,9 +311,9 @@ public void testTerminationThenGet() throws Exception { assertThat(cache.get(2L)).isNotNull(); - cluster.getClusterControl().terminateAllServers(); + CLUSTER.get().getClusterControl().terminateAllServers(); - String value = new TimeLimitedTask(5, TimeUnit.SECONDS) { + String value = new TimeLimitedTask(ofSeconds(5)) { @Override String runTask() throws Exception { return cache.get(2L); @@ -390,10 +327,9 @@ String runTask() throws Exception { public void testTerminationThenContainsKey() throws Exception { CacheManagerBuilder clusteredCacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder() - .with(ClusteringServiceConfigurationBuilder.cluster(cluster.getConnectionURI().resolve("/MyCacheManagerName")) + .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER.get().getConnectionURI().resolve("/").resolve(testName.getMethodName())) .timeouts(TimeoutsBuilder.timeouts().read(Duration.of(1, ChronoUnit.SECONDS)).build()) - .autoCreate() - .defaultServerResource("primary-server-resource")) + .autoCreate(server -> server.defaultServerResource("primary-server-resource"))) .withCache("simple-cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() @@ -408,9 +344,9 @@ public void testTerminationThenContainsKey() throws Exception { assertThat(cache.containsKey(2L)).isTrue(); - cluster.getClusterControl().terminateAllServers(); + CLUSTER.get().getClusterControl().terminateAllServers(); - boolean value = new TimeLimitedTask(5, TimeUnit.SECONDS) { + boolean value = new TimeLimitedTask(ofSeconds(5)) { @Override Boolean runTask() throws Exception { return cache.containsKey(2L); @@ -425,10 +361,9 @@ Boolean runTask() throws Exception { public void testTerminationThenIterator() throws Exception { CacheManagerBuilder clusteredCacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder() - .with(ClusteringServiceConfigurationBuilder.cluster(cluster.getConnectionURI().resolve("/MyCacheManagerName")) + .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER.get().getConnectionURI().resolve("/").resolve(testName.getMethodName())) .timeouts(TimeoutsBuilder.timeouts().read(Duration.of(1, ChronoUnit.SECONDS)).build()) - .autoCreate() - .defaultServerResource("primary-server-resource")) + .autoCreate(server -> server.defaultServerResource("primary-server-resource"))) .withCache("simple-cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() @@ -441,11 +376,11 @@ public void testTerminationThenIterator() throws Exception { cache.put(2L, "deux"); cache.put(3L, "trois"); - cluster.getClusterControl().terminateAllServers(); + CLUSTER.get().getClusterControl().terminateAllServers(); - Iterator> value = new TimeLimitedTask>>(5, TimeUnit.SECONDS) { + Iterator> value = new TimeLimitedTask>>(ofSeconds(5)) { @Override - Iterator> runTask() throws Exception { + Iterator> runTask() { return cache.iterator(); } }.run(); @@ -457,10 +392,9 @@ Iterator> runTask() throws Exception { public void testTerminationThenPut() throws Exception { CacheManagerBuilder clusteredCacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder() - .with(ClusteringServiceConfigurationBuilder.cluster(cluster.getConnectionURI().resolve("/MyCacheManagerName")) + .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER.get().getConnectionURI().resolve("/").resolve(testName.getMethodName())) .timeouts(TimeoutsBuilder.timeouts().write(Duration.of(1, ChronoUnit.SECONDS)).build()) - .autoCreate() - .defaultServerResource("primary-server-resource")) + .autoCreate(server -> server.defaultServerResource("primary-server-resource"))) .withCache("simple-cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() @@ -473,10 +407,10 @@ public void testTerminationThenPut() throws Exception { cache.put(2L, "deux"); cache.put(3L, "trois"); - cluster.getClusterControl().terminateAllServers(); + CLUSTER.get().getClusterControl().terminateAllServers(); // The resilience strategy will pick it up and not exception is thrown - new TimeLimitedTask(10, TimeUnit.SECONDS) { + new TimeLimitedTask(ofSeconds(10)) { @Override Void runTask() throws Exception { cache.put(2L, "dos"); @@ -489,10 +423,9 @@ Void runTask() throws Exception { public void testTerminationThenPutIfAbsent() throws Exception { CacheManagerBuilder clusteredCacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder() - .with(ClusteringServiceConfigurationBuilder.cluster(cluster.getConnectionURI().resolve("/MyCacheManagerName")) + .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER.get().getConnectionURI().resolve("/").resolve(testName.getMethodName())) .timeouts(TimeoutsBuilder.timeouts().write(Duration.of(1, ChronoUnit.SECONDS)).build()) - .autoCreate() - .defaultServerResource("primary-server-resource")) + .autoCreate(server -> server.defaultServerResource("primary-server-resource"))) .withCache("simple-cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() @@ -505,10 +438,10 @@ public void testTerminationThenPutIfAbsent() throws Exception { cache.put(2L, "deux"); cache.put(3L, "trois"); - cluster.getClusterControl().terminateAllServers(); + CLUSTER.get().getClusterControl().terminateAllServers(); // The resilience strategy will pick it up and not exception is thrown - new TimeLimitedTask(10, TimeUnit.SECONDS) { + new TimeLimitedTask(ofSeconds(10)) { @Override String runTask() throws Exception { return cache.putIfAbsent(2L, "dos"); @@ -520,10 +453,9 @@ String runTask() throws Exception { public void testTerminationThenRemove() throws Exception { CacheManagerBuilder clusteredCacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder() - .with(ClusteringServiceConfigurationBuilder.cluster(cluster.getConnectionURI().resolve("/MyCacheManagerName")) + .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER.get().getConnectionURI().resolve("/").resolve(testName.getMethodName())) .timeouts(TimeoutsBuilder.timeouts().write(Duration.of(1, ChronoUnit.SECONDS)).build()) - .autoCreate() - .defaultServerResource("primary-server-resource")) + .autoCreate(server -> server.defaultServerResource("primary-server-resource"))) .withCache("simple-cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() @@ -536,9 +468,9 @@ public void testTerminationThenRemove() throws Exception { cache.put(2L, "deux"); cache.put(3L, "trois"); - cluster.getClusterControl().terminateAllServers(); + CLUSTER.get().getClusterControl().terminateAllServers(); - new TimeLimitedTask(10, TimeUnit.SECONDS) { + new TimeLimitedTask(ofSeconds(10)) { @Override Void runTask() throws Exception { cache.remove(2L); @@ -553,10 +485,9 @@ public void testTerminationThenClear() throws Exception { CacheManagerBuilder clusteredCacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder() .using(statisticsService) - .with(ClusteringServiceConfigurationBuilder.cluster(cluster.getConnectionURI().resolve("/MyCacheManagerName")) + .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER.get().getConnectionURI().resolve("/").resolve(testName.getMethodName())) .timeouts(TimeoutsBuilder.timeouts().write(Duration.of(1, ChronoUnit.SECONDS)).build()) - .autoCreate() - .defaultServerResource("primary-server-resource")) + .autoCreate(server -> server.defaultServerResource("primary-server-resource"))) .withCache("simple-cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() @@ -569,10 +500,10 @@ public void testTerminationThenClear() throws Exception { cache.put(2L, "deux"); cache.put(3L, "trois"); - cluster.getClusterControl().terminateAllServers(); + CLUSTER.get().getClusterControl().terminateAllServers(); // The resilience strategy will pick it up and not exception is thrown - new TimeLimitedTask(10, TimeUnit.SECONDS) { + new TimeLimitedTask(ofSeconds(10)) { @Override Void runTask() { cache.clear(); @@ -593,11 +524,10 @@ public void testTerminationFreezesTheClient() throws Exception { try(PersistentCacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() - .with(ClusteringServiceConfigurationBuilder.cluster(cluster.getConnectionURI().resolve("/MyCacheManagerName")) + .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER.get().getConnectionURI().resolve("/").resolve(testName.getMethodName())) .timeouts(TimeoutsBuilder.timeouts() .read(readOperationTimeout)) - .autoCreate() - .defaultServerResource("primary-server-resource")) + .autoCreate(server -> server.defaultServerResource("primary-server-resource"))) .withCache("simple-cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() @@ -607,7 +537,7 @@ public void testTerminationFreezesTheClient() throws Exception { Cache cache = cacheManager.getCache("simple-cache", Long.class, String.class); cache.put(1L, "un"); - cluster.getClusterControl().terminateAllServers(); + CLUSTER.get().getClusterControl().terminateAllServers(); // Fill the inflight queue and check that we wait no longer than the read timeout for (int i = 0; i < CLIENT_MAX_PENDING_REQUESTS; i++) { @@ -615,7 +545,7 @@ public void testTerminationFreezesTheClient() throws Exception { } // The resilience strategy will pick it up and not exception is thrown - new TimeLimitedTask(readOperationTimeout.toMillis() * 2, TimeUnit.MILLISECONDS) { // I multiply by 2 to let some room after the expected timeout + new TimeLimitedTask(readOperationTimeout.multipliedBy(2)) { // I multiply by 2 to let some room after the expected timeout @Override Void runTask() { cache.get(1L); // the call that could block @@ -634,52 +564,6 @@ private static void overrideProperty(Map oldProperties, String p tcProperties.setProperty(propertyName, propertyValue); } - /** - * Used as a {@link Rule @Rule} to limit the number of concurrently executing tests. - */ - private final class TestConcurrencyLimiter extends ExternalResource { - - @Override - protected void before() throws Throwable { - try { - TEST_PERMITS.acquire(); - } catch (InterruptedException e) { - throw new AssertionError(e); - } - } - - @Override - protected void after() { - TEST_PERMITS.release(); - } - } - - /** - * Used as a {@link org.junit.ClassRule @ClassRule} to determine the number of tests to - * be run from the class. - */ - private static final class TestCounter implements TestRule { - - private int testCount; - - @Override - public Statement apply(Statement base, Description description) { - int testCount = 0; - for (Description child : description.getChildren()) { - if (child.isTest()) { - testCount++; - } - } - this.testCount = testCount; - - return base; - } - - private int getTestCount() { - return testCount; - } - } - /** * Runs a method under control of a timeout. * @@ -693,14 +577,12 @@ private abstract class TimeLimitedTask { * and test task completion & thread interrupt clear. */ private final byte[] lock = new byte[0]; - private final long timeLimit; - private final TimeUnit unit; + private final Duration timeLimit; private volatile boolean isDone = false; private volatile boolean isExpired = false; - private TimeLimitedTask(long timeLimit, TimeUnit unit) { + private TimeLimitedTask(Duration timeLimit) { this.timeLimit = timeLimit; - this.unit = unit; } /** @@ -713,7 +595,7 @@ private TimeLimitedTask(long timeLimit, TimeUnit unit) { /** * Invokes {@link #runTask()} under a time limit. If {@code runTask} execution exceeds the amount of - * time specified in the {@link TimeLimitedTask#TimeLimitedTask(long, TimeUnit) constructor}, the task + * time specified in the {@link TimeLimitedTask#TimeLimitedTask(Duration) constructor}, the task * {@code Thread} is first interrupted and, if the thread remains alive for another duration of the time * limit, the thread is forcefully stopped using {@link Thread#stop()}. * @@ -726,7 +608,7 @@ private TimeLimitedTask(long timeLimit, TimeUnit unit) { V run() throws Exception { V result; - Future future = interruptAfter(timeLimit, unit); + Future future = interruptAfter(timeLimit); try { result = this.runTask(); } finally { @@ -735,7 +617,7 @@ V run() throws Exception { future.cancel(true); Thread.interrupted(); // Reset interrupted status } - assertThat(isExpired).describedAs( "%s test thread exceeded its time limit of %d %s", testName.getMethodName(), timeLimit, unit).isFalse(); + assertThat(isExpired).describedAs( "%s test thread exceeded its time limit of %s", testName.getMethodName(), timeLimit).isFalse(); } return result; @@ -746,15 +628,14 @@ V run() throws Exception { * If the timeout expires, a thread dump is taken and the current thread interrupted. * * @param interval the amount of time to wait - * @param unit the unit for {@code interval} * * @return a {@code Future} that may be used to cancel the timeout. */ - private Future interruptAfter(final long interval, final TimeUnit unit) { + private Future interruptAfter(Duration interval) { final Thread targetThread = Thread.currentThread(); FutureTask killer = new FutureTask<>(() -> { try { - unit.sleep(interval); + Thread.sleep(interval.toMillis()); if (!isDone && targetThread.isAlive()) { synchronized (lock) { if (isDone) { @@ -773,7 +654,7 @@ private Future interruptAfter(final long interval, final TimeUnit unit) { * looping wait where the interrupt status is recorded but ignored until the awaited event * occurs. */ - unit.timedJoin(targetThread, interval); + targetThread.join(interval.toMillis()); if (!isDone && targetThread.isAlive()) { System.out.format("%s test thread did not respond to Thread.interrupt; forcefully stopping %s%n", testName.getMethodName(), targetThread); diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/lock/VoltronReadWriteLockIntegrationTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/lock/VoltronReadWriteLockIntegrationTest.java index ef9ad659c4..889dd93c01 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/lock/VoltronReadWriteLockIntegrationTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/lock/VoltronReadWriteLockIntegrationTest.java @@ -15,7 +15,6 @@ */ package org.ehcache.clustered.lock; -import java.io.File; import java.util.Collections; import java.util.List; import java.util.concurrent.Callable; @@ -25,8 +24,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; - -import org.ehcache.clustered.ClusteredTests; import org.ehcache.clustered.client.internal.lock.VoltronReadWriteLock; import org.ehcache.clustered.client.internal.lock.VoltronReadWriteLock.Hold; import org.junit.BeforeClass; @@ -35,20 +32,17 @@ import org.terracotta.connection.Connection; import org.terracotta.testing.rules.Cluster; +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; -import static org.terracotta.testing.rules.BasicExternalClusterBuilder.newCluster; -public class VoltronReadWriteLockIntegrationTest extends ClusteredTests { - @ClassRule - public static Cluster CLUSTER = newCluster().in(new File("build/cluster")).build(); +public class VoltronReadWriteLockIntegrationTest { - @BeforeClass - public static void waitForActive() throws Exception { - CLUSTER.getClusterControl().waitForActive(); - } + @ClassRule + public static Cluster CLUSTER = newCluster().in(clusterPath()).build(); @Test public void testSingleThreadSingleClientInteraction() throws Throwable { diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/lock/VoltronReadWriteLockPassiveIntegrationTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/lock/VoltronReadWriteLockPassiveIntegrationTest.java index 619bfcf010..f728c96e95 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/lock/VoltronReadWriteLockPassiveIntegrationTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/lock/VoltronReadWriteLockPassiveIntegrationTest.java @@ -15,45 +15,49 @@ */ package org.ehcache.clustered.lock; -import java.io.File; -import java.util.concurrent.Callable; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; - -import org.ehcache.clustered.ClusteredTests; import org.ehcache.clustered.client.internal.lock.VoltronReadWriteLock; import org.ehcache.clustered.client.internal.lock.VoltronReadWriteLock.Hold; +import org.ehcache.clustered.util.ParallelTestCluster; +import org.ehcache.clustered.util.runners.Parallel; import org.junit.Before; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TestName; +import org.junit.runner.RunWith; import org.terracotta.connection.Connection; -import org.terracotta.testing.rules.Cluster; import static org.ehcache.clustered.lock.VoltronReadWriteLockIntegrationTest.async; +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.newCluster; import static org.junit.Assert.fail; -import static org.terracotta.testing.rules.BasicExternalClusterBuilder.newCluster; -public class VoltronReadWriteLockPassiveIntegrationTest extends ClusteredTests { - @ClassRule - public static Cluster CLUSTER = newCluster(2).in(new File("build/cluster")).build(); +@RunWith(Parallel.class) +public class VoltronReadWriteLockPassiveIntegrationTest { + + @ClassRule @Rule + public static final ParallelTestCluster CLUSTER = new ParallelTestCluster(newCluster(2).in(clusterPath()).build()); + @Rule + public final TestName testName = new TestName(); @Before - public void waitForActive() throws Exception { - CLUSTER.getClusterControl().waitForActive(); - CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); + public void startAllServers() throws Exception { + CLUSTER.getClusterControl().startAllServers(); } @Test public void testSingleThreadSingleClientInteraction() throws Throwable { try (Connection client = CLUSTER.newConnection()) { - VoltronReadWriteLock lock = new VoltronReadWriteLock(client, "test"); + VoltronReadWriteLock lock = new VoltronReadWriteLock(client, testName.getMethodName()); Hold hold = lock.writeLock(); + CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); CLUSTER.getClusterControl().terminateActive(); - CLUSTER.getClusterControl().startOneServer(); hold.unlock(); } @@ -62,7 +66,7 @@ public void testSingleThreadSingleClientInteraction() throws Throwable { @Test public void testMultipleThreadsSingleConnection() throws Throwable { try (Connection client = CLUSTER.newConnection()) { - final VoltronReadWriteLock lock = new VoltronReadWriteLock(client, "test"); + final VoltronReadWriteLock lock = new VoltronReadWriteLock(client, testName.getMethodName()); Hold hold = lock.writeLock(); @@ -78,8 +82,8 @@ public void testMultipleThreadsSingleConnection() throws Throwable { //expected } + CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); CLUSTER.getClusterControl().terminateActive(); - CLUSTER.getClusterControl().startOneServer(); try { waiter.get(100, TimeUnit.MILLISECONDS); @@ -98,12 +102,12 @@ public void testMultipleThreadsSingleConnection() throws Throwable { public void testMultipleClients() throws Throwable { try (Connection clientA = CLUSTER.newConnection(); Connection clientB = CLUSTER.newConnection()) { - VoltronReadWriteLock lockA = new VoltronReadWriteLock(clientA, "test"); + VoltronReadWriteLock lockA = new VoltronReadWriteLock(clientA, testName.getMethodName()); Hold hold = lockA.writeLock(); Future waiter = async(() -> { - new VoltronReadWriteLock(clientB, "test").writeLock().unlock(); + new VoltronReadWriteLock(clientB, testName.getMethodName()).writeLock().unlock(); return null; }); @@ -114,8 +118,8 @@ public void testMultipleClients() throws Throwable { //expected } + CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); CLUSTER.getClusterControl().terminateActive(); - CLUSTER.getClusterControl().startOneServer(); try { waiter.get(100, TimeUnit.MILLISECONDS); diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/management/AbstractClusteringManagementTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/management/AbstractClusteringManagementTest.java index b320f523ca..fef40076f7 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/management/AbstractClusteringManagementTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/management/AbstractClusteringManagementTest.java @@ -19,7 +19,6 @@ import com.fasterxml.jackson.databind.SerializationFeature; import org.ehcache.CacheManager; import org.ehcache.Status; -import org.ehcache.clustered.ClusteredTests; import org.ehcache.clustered.util.BeforeAll; import org.ehcache.clustered.util.BeforeAllRule; import org.ehcache.config.units.EntryUnit; @@ -31,13 +30,8 @@ import org.junit.Rule; import org.junit.rules.RuleChain; import org.junit.rules.Timeout; -import org.terracotta.connection.Connection; -import org.terracotta.connection.ConnectionException; -import org.terracotta.exception.EntityConfigurationException; -import org.terracotta.management.entity.nms.NmsConfig; -import org.terracotta.management.entity.nms.client.DefaultNmsService; -import org.terracotta.management.entity.nms.client.NmsEntity; -import org.terracotta.management.entity.nms.client.NmsEntityFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.terracotta.management.entity.nms.client.NmsService; import org.terracotta.management.model.cluster.AbstractManageableNode; import org.terracotta.management.model.cluster.Client; @@ -47,84 +41,67 @@ import org.terracotta.management.model.context.Context; import org.terracotta.management.model.notification.ContextualNotification; import org.terracotta.management.model.stats.ContextualStatistics; -import org.terracotta.testing.rules.Cluster; -import java.io.File; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Scanner; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import static java.lang.Thread.sleep; +import static java.util.Collections.unmodifiableMap; import static org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder.clusteredDedicated; import static org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder.clusteredShared; import static org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder.cluster; import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.ehcache.testing.StandardCluster.offheapResources; import static org.hamcrest.CoreMatchers.equalTo; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertTrue; -import static org.terracotta.testing.rules.BasicExternalClusterBuilder.newCluster; +import static org.junit.rules.RuleChain.outerRule; + @SuppressWarnings("rawtypes") // Need to suppress because of a Javac bug giving a rawtype on AbstractManageableNode::isManageable. -public abstract class AbstractClusteringManagementTest extends ClusteredTests { +public abstract class AbstractClusteringManagementTest { - private static final String RESOURCE_CONFIG = - "" - + "" - + "64" - + "64" - + "" + - "\n"; + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractClusteringManagementTest.class); + + private static final Map resources; + static { + HashMap map = new HashMap<>(); + map.put("primary-server-resource", 64L); + map.put("secondary-server-resource", 64L); + resources = unmodifiableMap(map); + } protected static CacheManager cacheManager; protected static ClientIdentifier ehcacheClientIdentifier; protected static ServerEntityIdentifier clusterTierManagerEntityIdentifier; - protected static ObjectMapper mapper = new ObjectMapper(); - - static NmsService nmsService; - protected static ServerEntityIdentifier tmsServerEntityIdentifier; - protected static Connection managementConnection; - - static { - mapper.configure(SerializationFeature.INDENT_OUTPUT, true); - } + protected static final ObjectMapper mapper = new ObjectMapper().configure(SerializationFeature.INDENT_OUTPUT, true); @ClassRule - public static Cluster CLUSTER = newCluster(2) - .in(new File("build/cluster")) - .withServiceFragment(RESOURCE_CONFIG) - .build(); + public static final ClusterWithManagement CLUSTER = new ClusterWithManagement(newCluster(2) + .in(clusterPath()).withServiceFragment(offheapResources(resources)).build()); @Rule - public final RuleChain rules = RuleChain.emptyRuleChain() - .around(Timeout.seconds(90)) - .around(new BeforeAllRule(this)); + public final RuleChain rules = outerRule(Timeout.seconds(90)).around(new BeforeAllRule(this)); @BeforeAll public void beforeAllTests() throws Exception { - CLUSTER.getClusterControl().waitForActive(); - CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); - - // simulate a TMS client - createNmsService(); - initCM(); - initIdentifiers(); - - sendManagementCallOnEntityToCollectStats(); } @Before public void init() { - if (nmsService != null) { - // this call clear the CURRENT arrived messages, but be aware that some other messages can arrive just after the drain - nmsService.readMessages(); - } + CLUSTER.getNmsService().readMessages(); } @AfterClass @@ -132,14 +109,14 @@ public static void afterClass() throws Exception { tearDownCacheManagerAndStatsCollector(); } - protected void initCM() throws InterruptedException { + private static void initCM() throws InterruptedException { cacheManager = newCacheManagerBuilder() // cluster config - .with(cluster(CLUSTER.getConnectionURI().resolve("/my-server-entity-1")) - .autoCreate() - .defaultServerResource("primary-server-resource") - .resourcePool("resource-pool-a", 10, MemoryUnit.MB, "secondary-server-resource") // <2> - .resourcePool("resource-pool-b", 8, MemoryUnit.MB)) // will take from primary-server-resource + .with(cluster(CLUSTER.getCluster().getConnectionURI().resolve("/my-server-entity-1")) + .autoCreate(server -> server + .defaultServerResource("primary-server-resource") + .resourcePool("resource-pool-a", 10, MemoryUnit.MB, "secondary-server-resource") // <2> + .resourcePool("resource-pool-b", 8, MemoryUnit.MB))) // will take from primary-server-resource // management config .using(new DefaultManagementRegistryConfiguration() .addTags("webapp-1", "server-node-1") @@ -174,6 +151,9 @@ protected void initCM() throws InterruptedException { // test_notifs_sent_at_CM_init waitForAllNotifications( "CLIENT_CONNECTED", + "CLIENT_PROPERTY_ADDED", + "CLIENT_PROPERTY_ADDED", + "CLIENT_PROPERTY_ADDED", "CLIENT_REGISTRY_AVAILABLE", "CLIENT_TAGS_UPDATED", "EHCACHE_RESOURCE_POOLS_CONFIGURED", @@ -181,7 +161,7 @@ protected void initCM() throws InterruptedException { "ENTITY_REGISTRY_AVAILABLE", "ENTITY_REGISTRY_AVAILABLE", "ENTITY_REGISTRY_AVAILABLE", "ENTITY_REGISTRY_AVAILABLE", "SERVER_ENTITY_CREATED", "SERVER_ENTITY_CREATED", "SERVER_ENTITY_CREATED", "SERVER_ENTITY_CREATED", "SERVER_ENTITY_CREATED", "SERVER_ENTITY_CREATED", "SERVER_ENTITY_DESTROYED", - "SERVER_ENTITY_FETCHED", "SERVER_ENTITY_FETCHED", "SERVER_ENTITY_FETCHED", "SERVER_ENTITY_FETCHED", "SERVER_ENTITY_FETCHED", "SERVER_ENTITY_FETCHED", "SERVER_ENTITY_FETCHED", "SERVER_ENTITY_FETCHED", + "SERVER_ENTITY_FETCHED", "SERVER_ENTITY_FETCHED", "SERVER_ENTITY_FETCHED", "SERVER_ENTITY_FETCHED", "SERVER_ENTITY_FETCHED", "SERVER_ENTITY_FETCHED", "SERVER_ENTITY_FETCHED", "SERVER_ENTITY_UNFETCHED", "EHCACHE_RESOURCE_POOLS_CONFIGURED", @@ -194,92 +174,46 @@ protected void initCM() throws InterruptedException { ); } - public static void initIdentifiers() throws Exception { - tmsServerEntityIdentifier = null; - ehcacheClientIdentifier = null; - clusterTierManagerEntityIdentifier = null; - - do { - tmsServerEntityIdentifier = readTopology() - .activeServerEntityStream() - .filter(serverEntity -> serverEntity.getType().equals(NmsConfig.ENTITY_TYPE)) - .filter(AbstractManageableNode::isManageable) - .map(ServerEntity::getServerEntityIdentifier) - .findFirst() - .orElse(null); - sleep(500); - } while (tmsServerEntityIdentifier == null && !Thread.currentThread().isInterrupted()); - - do { - ehcacheClientIdentifier = readTopology().getClients().values() + private static void initIdentifiers() throws Exception { + while ((ehcacheClientIdentifier = readTopology().getClients().values() .stream() .filter(client -> client.getName().equals("Ehcache:my-server-entity-1")) .filter(AbstractManageableNode::isManageable) .findFirst() .map(Client::getClientIdentifier) - .orElse(null); - sleep(500); - } while (ehcacheClientIdentifier == null && !Thread.currentThread().isInterrupted()); + .orElse(null)) == null) { + sleep(200); + } - do { - clusterTierManagerEntityIdentifier = readTopology() + while ((clusterTierManagerEntityIdentifier = readTopology() .activeServerEntityStream() .filter(serverEntity -> serverEntity.getName().equals("my-server-entity-1")) .filter(AbstractManageableNode::isManageable) .map(ServerEntity::getServerEntityIdentifier) .findFirst() - .orElse(null); - sleep(500); - } while (clusterTierManagerEntityIdentifier == null && !Thread.currentThread().isInterrupted()); + .orElse(null)) == null) { + sleep(200); + } } - public static void tearDownCacheManagerAndStatsCollector() throws Exception { + private static void tearDownCacheManagerAndStatsCollector() throws Exception { if (cacheManager != null && cacheManager.getStatus() == Status.AVAILABLE) { - if (nmsService != null) { - readTopology().getClient(ehcacheClientIdentifier) - .ifPresent(client -> { - try { - nmsService.stopStatisticCollector(client.getContext().with("cacheManagerName", "my-super-cache-manager")).waitForReturn(); - } catch (Exception e) { - throw new RuntimeException(e); - } - }); - } - - cacheManager.close(); - } - - if (nmsService != null) { - readTopology().getSingleStripe().getActiveServerEntity(tmsServerEntityIdentifier) + readTopology().getClient(ehcacheClientIdentifier) .ifPresent(client -> { try { - nmsService.stopStatisticCollector(client.getContext()); + CLUSTER.getNmsService().stopStatisticCollector(client.getContext().with("cacheManagerName", "my-super-cache-manager")).waitForReturn(); } catch (Exception e) { throw new RuntimeException(e); } }); - managementConnection.close(); + cacheManager.close(); } } - public static void createNmsService() throws ConnectionException, EntityConfigurationException { - createNmsService(CLUSTER); - } - - public static void createNmsService(Cluster cluster) throws ConnectionException, EntityConfigurationException { - managementConnection = cluster.newConnection(); - - NmsEntityFactory entityFactory = new NmsEntityFactory(managementConnection, AbstractClusteringManagementTest.class.getName()); - NmsEntity tmsAgentEntity = entityFactory.retrieveOrCreate(new NmsConfig()); - - nmsService = new DefaultNmsService(tmsAgentEntity); - nmsService.setOperationTimeout(5, TimeUnit.SECONDS); - } - public static org.terracotta.management.model.cluster.Cluster readTopology() throws Exception { - org.terracotta.management.model.cluster.Cluster cluster = nmsService.readTopology(); + org.terracotta.management.model.cluster.Cluster cluster = CLUSTER.getNmsService().readTopology(); //System.out.println(mapper.writeValueAsString(cluster.toMap())); return cluster; } @@ -287,14 +221,13 @@ public static org.terracotta.management.model.cluster.Cluster readTopology() thr public static void sendManagementCallOnClientToCollectStats() throws Exception { org.terracotta.management.model.cluster.Cluster topology = readTopology(); Client manageableClient = topology.getClient(ehcacheClientIdentifier).filter(AbstractManageableNode::isManageable).get(); - Context cmContext = manageableClient.getContext() - .with("cacheManagerName", "my-super-cache-manager"); - nmsService.startStatisticCollector(cmContext, 1, TimeUnit.SECONDS).waitForReturn(); + Context cmContext = manageableClient.getContext(); + CLUSTER.getNmsService().startStatisticCollector(cmContext, 1, TimeUnit.SECONDS).waitForReturn(); } public static List waitForNextStats() throws Exception { // uses the monitoring to get the content of the stat buffer when some stats are collected - return nmsService.waitForMessage(message -> message.getType().equals("STATISTICS")) + return CLUSTER.getNmsService().waitForMessage(message -> message.getType().equals("STATISTICS")) .stream() .filter(message -> message.getType().equals("STATISTICS")) .flatMap(message -> message.unwrap(ContextualStatistics.class).stream()) @@ -311,16 +244,14 @@ protected static String normalizeForLineEndings(String stringToNormalize) { return stringToNormalize.replace("\r\n", "\n").replace("\r", "\n"); } - public static void sendManagementCallOnEntityToCollectStats() throws Exception { - org.terracotta.management.model.cluster.Cluster topology = readTopology(); - ServerEntity manageableEntity = topology.getSingleStripe().getActiveServerEntity(tmsServerEntityIdentifier).filter(AbstractManageableNode::isManageable).get(); - Context context = manageableEntity.getContext(); - nmsService.startStatisticCollector(context, 1, TimeUnit.SECONDS).waitForReturn(); + public static void waitForAllNotifications(String... notificationTypes) throws InterruptedException { + waitForAllNotifications(CLUSTER.getNmsService(), notificationTypes); } - public static void waitForAllNotifications(String... notificationTypes) throws InterruptedException { + public static void waitForAllNotifications(NmsService nmsService, String... notificationTypes) throws InterruptedException { List waitingFor = new ArrayList<>(Arrays.asList(notificationTypes)); List missingOnes = new ArrayList<>(); + List existingOnes = new ArrayList<>(); // please keep these sout because it is really hard to troubleshoot blocking tests in the beforeClass method in the case we do not receive all notifs. // System.out.println("waitForAllNotifications: " + waitingFor); @@ -330,10 +261,14 @@ public static void waitForAllNotifications(String... notificationTypes) throws I nmsService.waitForMessage(message -> { if (message.getType().equals("NOTIFICATION")) { for (ContextualNotification notification : message.unwrap(ContextualNotification.class)) { - if (waitingFor.remove(notification.getType())) { -// System.out.println("Remove " + notification.getType()); -// System.out.println("Still waiting for: " + waitingFor); + if ("org.terracotta.management.entity.nms.client.NmsEntity".equals(notification.getContext().get("entityType"))) { + LOGGER.info("IGNORE:" + notification); // this is the passive NmsEntity, sometimes we catch it, sometimes not + } else if (waitingFor.remove(notification.getType())) { + existingOnes.add(notification); + LOGGER.debug("Remove " + notification); + LOGGER.debug("Still waiting for: " + waitingFor); } else { + LOGGER.debug("Extra: " + notification); missingOnes.add(notification); } } @@ -348,7 +283,7 @@ public static void waitForAllNotifications(String... notificationTypes) throws I t.join(30_000); // should be way enough to receive all messages t.interrupt(); // we interrupt the thread that is waiting on the message queue - assertTrue("Still waiting for: " + waitingFor, waitingFor.isEmpty()); - assertTrue("Unexpected notification: " + missingOnes, missingOnes.isEmpty()); + assertTrue("Still waiting for: " + waitingFor + ", only got: " + existingOnes, waitingFor.isEmpty()); + assertTrue("Unexpected notification: " + missingOnes + ", only got: " + existingOnes, missingOnes.isEmpty()); } } diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/management/AfterFailoverManagementServiceTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/management/AfterFailoverManagementServiceTest.java index 159eaace68..499bc0697a 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/management/AfterFailoverManagementServiceTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/management/AfterFailoverManagementServiceTest.java @@ -26,15 +26,8 @@ public class AfterFailoverManagementServiceTest extends ClusteringManagementServ @Override public void beforeAllTests() throws Exception { super.beforeAllTests(); - - CLUSTER.getClusterControl().terminateActive(); - CLUSTER.getClusterControl().waitForActive(); - - createNmsService(); - - initIdentifiers(); - - sendManagementCallOnEntityToCollectStats(); + CLUSTER.getCluster().getClusterControl().terminateActive(); + CLUSTER.getCluster().getClusterControl().waitForActive(); + CLUSTER.startCollectingServerEntityStats(); } - } diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/management/CMClosedEventSentTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/management/CMClosedEventSentTest.java index c444348b80..0687961e13 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/management/CMClosedEventSentTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/management/CMClosedEventSentTest.java @@ -24,49 +24,45 @@ import org.junit.Test; import org.terracotta.management.model.message.Message; import org.terracotta.management.model.notification.ContextualNotification; -import org.terracotta.testing.rules.Cluster; -import java.io.File; +import java.util.HashMap; +import java.util.Map; +import static java.util.Collections.unmodifiableMap; import static org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder.clusteredDedicated; import static org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder.cluster; -import static org.ehcache.clustered.management.AbstractClusteringManagementTest.createNmsService; -import static org.ehcache.clustered.management.AbstractClusteringManagementTest.nmsService; import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.ehcache.testing.StandardCluster.offheapResources; import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; -import static org.terracotta.testing.rules.BasicExternalClusterBuilder.newCluster; + public class CMClosedEventSentTest { - private static final String RESOURCE_CONFIG = - "" - + "" - + "64" - + "64" - + "" - + "\n" - + "" - + "" - + "5" - + "" - + ""; + private static final Map resources; + static { + HashMap map = new HashMap<>(); + map.put("primary-server-resource", 64L); + map.put("secondary-server-resource", 64L); + resources = unmodifiableMap(map); + } @ClassRule - public static Cluster CLUSTER = newCluster().in(new File("build/cluster")).withServiceFragment(RESOURCE_CONFIG).build(); + public static ClusterWithManagement CLUSTER = new ClusterWithManagement( + newCluster().in(clusterPath()).withServiceFragment(offheapResources(resources)).build()); @Test(timeout = 60_000) public void test_CACHE_MANAGER_CLOSED() throws Exception { - createNmsService(CLUSTER); - - try (CacheManager cacheManager = newCacheManagerBuilder().with(cluster(CLUSTER.getConnectionURI().resolve("/my-server-entity-1")) - .autoCreate() - .defaultServerResource("primary-server-resource") - .resourcePool("resource-pool-a", 10, MemoryUnit.MB, "secondary-server-resource") // <2> - .resourcePool("resource-pool-b", 10, MemoryUnit.MB)) // will take from primary-server-resource + try (CacheManager cacheManager = newCacheManagerBuilder().with(cluster(CLUSTER.getCluster().getConnectionURI().resolve("/my-server-entity-1")) + .autoCreate(server -> server + .defaultServerResource("primary-server-resource") + .resourcePool("resource-pool-a", 10, MemoryUnit.MB, "secondary-server-resource") // <2> + .resourcePool("resource-pool-b", 10, MemoryUnit.MB))) // will take from primary-server-resource // management config .using(new DefaultManagementRegistryConfiguration() .addTags("webapp-1", "server-node-1") @@ -90,7 +86,7 @@ public void test_CACHE_MANAGER_CLOSED() throws Exception { private void waitFor(String notifType) throws InterruptedException { while (!Thread.currentThread().isInterrupted()) { - Message message = nmsService.waitForMessage(); + Message message = CLUSTER.getNmsService().waitForMessage(); if (message.getType().equals("NOTIFICATION")) { ContextualNotification notification = message.unwrap(ContextualNotification.class).get(0); if (notification.getType().equals(notifType)) { diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/management/ClusterWithManagement.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/management/ClusterWithManagement.java new file mode 100644 index 0000000000..5603c629e5 --- /dev/null +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/management/ClusterWithManagement.java @@ -0,0 +1,130 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.clustered.management; + +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.MultipleFailureException; +import org.junit.runners.model.Statement; +import org.terracotta.connection.Connection; +import org.terracotta.connection.ConnectionException; +import org.terracotta.exception.EntityConfigurationException; +import org.terracotta.management.entity.nms.NmsConfig; +import org.terracotta.management.entity.nms.client.DefaultNmsService; +import org.terracotta.management.entity.nms.client.IllegalManagementCallException; +import org.terracotta.management.entity.nms.client.NmsEntity; +import org.terracotta.management.entity.nms.client.NmsEntityFactory; +import org.terracotta.management.entity.nms.client.NmsService; +import org.terracotta.management.model.cluster.ServerEntity; +import org.terracotta.management.model.cluster.ServerEntityIdentifier; +import org.terracotta.testing.rules.Cluster; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static java.lang.Thread.sleep; +import static java.util.Arrays.asList; + +public final class ClusterWithManagement implements TestRule { + + private final Cluster cluster; + + private Connection managementConnection; + private NmsService nmsService; + private ServerEntityIdentifier tmsServerEntityIdentifier; + + public ClusterWithManagement(Cluster cluster) { + this.cluster = cluster; + } + + protected void before() throws Throwable { + this.managementConnection = cluster.newConnection(); + this.nmsService = createNmsService(managementConnection); + while ((tmsServerEntityIdentifier = nmsService.readTopology() + .activeServerEntityStream() + .filter(serverEntity -> serverEntity.getType().equals(NmsConfig.ENTITY_TYPE)) + .filter(ServerEntity::isManageable) + .map(ServerEntity::getServerEntityIdentifier) + .findFirst() + .orElse(null)) == null) { + sleep(100); + } + startCollectingServerEntityStats(); + } + + protected void after() throws Exception { + try { + nmsService.readTopology().getSingleStripe().getActiveServerEntity(tmsServerEntityIdentifier) + .ifPresent(client -> { + try { + nmsService.stopStatisticCollector(client.getContext()); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } finally { + managementConnection.close(); + } + } + + @Override + public Statement apply(Statement base, Description description) { + return cluster.apply(new Statement() { + @Override + public void evaluate() throws Throwable { + before(); + try { + base.evaluate(); + after(); + } catch (Throwable t) { + try { + after(); + throw t; + } catch (Exception e) { + throw new MultipleFailureException(asList(t, e)); + } + } + } + }, description); + } + + public Cluster getCluster() { + return cluster; + } + + public NmsService getNmsService() { + return nmsService; + } + + public ServerEntityIdentifier getTmsServerEntityIdentifier() { + return tmsServerEntityIdentifier; + } + + private static NmsService createNmsService(Connection connection) throws ConnectionException, EntityConfigurationException { + NmsEntityFactory entityFactory = new NmsEntityFactory(connection, AbstractClusteringManagementTest.class.getName()); + NmsEntity tmsAgentEntity = entityFactory.retrieveOrCreate(new NmsConfig()); + + NmsService nmsService = new DefaultNmsService(tmsAgentEntity); + nmsService.setOperationTimeout(10, TimeUnit.SECONDS); + return nmsService; + } + + public void startCollectingServerEntityStats() throws InterruptedException, ExecutionException, TimeoutException, IllegalManagementCallException { + ServerEntity manageableEntity = nmsService.readTopology().getSingleStripe().getActiveServerEntity(tmsServerEntityIdentifier).filter(ServerEntity::isManageable).get(); + nmsService.startStatisticCollector(manageableEntity.getContext(), 1, TimeUnit.SECONDS).waitForReturn(); + } +} diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/management/ClusteredStatisticsCountTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/management/ClusteredStatisticsCountTest.java index f27143201e..9e59520677 100755 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/management/ClusteredStatisticsCountTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/management/ClusteredStatisticsCountTest.java @@ -16,13 +16,13 @@ package org.ehcache.clustered.management; import org.ehcache.Cache; -import org.junit.Assert; import org.junit.Test; import org.terracotta.management.model.stats.ContextualStatistics; import java.util.List; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; public class ClusteredStatisticsCountTest extends AbstractClusteringManagementTest { @@ -61,8 +61,11 @@ public void countTest() throws Exception { if (stat.getContext().contains("cacheName") && stat.getContext().get("cacheName").equals("dedicated-cache-1")) { // please leave it there - it's really useful to see what's coming - /*System.out.println("stats:"); - for (Map.Entry> entry : stat.getStatistics().entrySet()) { + /* + System.out.println("stats:"); + + Set>> entries = stat.getStatistics().entrySet(); + for (Map.Entry> entry : entries) { System.out.println(" - " + entry.getKey() + " : " + entry.getValue()); }*/ @@ -73,13 +76,14 @@ public void countTest() throws Exception { } } } while(!Thread.currentThread().isInterrupted() && - (cacheHitCount != CACHE_HIT_COUNT) && (clusteredHitCount != CLUSTERED_HIT_COUNT) && - (cacheMissCount != CACHE_MISS_COUNT) && (clusteredMissCount != CLUSTERED_MISS_COUNT)); + ((cacheHitCount != CACHE_HIT_COUNT) || (clusteredHitCount != CLUSTERED_HIT_COUNT) || + (cacheMissCount != CACHE_MISS_COUNT) || (clusteredMissCount != CLUSTERED_MISS_COUNT))); + - Assert.assertThat(cacheHitCount,is(CACHE_HIT_COUNT)); - Assert.assertThat(clusteredHitCount,is(CLUSTERED_HIT_COUNT)); - Assert.assertThat(cacheMissCount,is(CACHE_MISS_COUNT)); - Assert.assertThat(clusteredMissCount,is(CLUSTERED_MISS_COUNT)); + assertThat(cacheHitCount,is(CACHE_HIT_COUNT)); + assertThat(clusteredHitCount,is(CLUSTERED_HIT_COUNT)); + assertThat(cacheMissCount,is(CACHE_MISS_COUNT)); + assertThat(clusteredMissCount,is(CLUSTERED_MISS_COUNT)); } diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/management/ClusteringManagementServiceTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/management/ClusteringManagementServiceTest.java index 0a9420d5d2..91955e8130 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/management/ClusteringManagementServiceTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/management/ClusteringManagementServiceTest.java @@ -18,6 +18,7 @@ import org.ehcache.Cache; import org.ehcache.config.units.EntryUnit; import org.ehcache.config.units.MemoryUnit; +import org.hamcrest.MatcherAssert; import org.junit.FixMethodOrder; import org.junit.Ignore; import org.junit.Test; @@ -43,6 +44,8 @@ import static org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder.clusteredDedicated; import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; +import static org.ehcache.testing.StandardTimeouts.eventually; +import static org.hamcrest.Matchers.arrayContainingInAnyOrder; @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class ClusteringManagementServiceTest extends AbstractClusteringManagementTest { @@ -133,15 +136,20 @@ public class ClusteringManagementServiceTest extends AbstractClusteringManagemen @Test @Ignore("This is not a test, but something useful to show a json print of a cluster topology with all management metadata inside") public void test_A_topology() throws Exception { - Cluster cluster = nmsService.readTopology(); + Cluster cluster = CLUSTER.getNmsService().readTopology(); String json = mapper.writeValueAsString(cluster.toMap()); //System.out.println(json); } @Test public void test_A_client_tags_exposed() throws Exception { - String[] tags = readTopology().getClient(ehcacheClientIdentifier).get().getTags().toArray(new String[0]); - assertThat(tags).containsOnly("server-node-1", "webapp-1"); + MatcherAssert.assertThat(() -> { + try { + return readTopology().getClient(ehcacheClientIdentifier).get().getTags().toArray(new String[0]); + } catch (Exception e) { + throw new AssertionError(e); + } + }, eventually().matches(arrayContainingInAnyOrder("server-node-1", "webapp-1"))); } @Test @@ -262,7 +270,7 @@ public void test_D_server_capabilities_exposed() throws Exception { // tms entity - managerCapabilities = readTopology().activeServerEntityStream().filter(serverEntity -> serverEntity.is(tmsServerEntityIdentifier)).findFirst().get().getManagementRegistry().get().getCapabilities().toArray(new Capability[0]); + managerCapabilities = readTopology().activeServerEntityStream().filter(serverEntity -> serverEntity.is(CLUSTER.getTmsServerEntityIdentifier())).findFirst().get().getManagementRegistry().get().getCapabilities().toArray(new Capability[0]); assertThat(managerCapabilities.length).isEqualTo(3); assertThat(managerCapabilities[0].getName()).isEqualTo("OffHeapResourceSettings"); @@ -294,10 +302,10 @@ public void test_E_notifs_on_add_cache() throws Exception { if (cluster.serverStream().count() == 2) { waitForAllNotifications( "SERVER_ENTITY_CREATED", "ENTITY_REGISTRY_AVAILABLE", "EHCACHE_SERVER_STORE_CREATED", "SERVER_ENTITY_FETCHED", "CACHE_ADDED", - "SERVER_ENTITY_CREATED", "ENTITY_REGISTRY_AVAILABLE", "EHCACHE_SERVER_STORE_CREATED"); // passive server + "SERVER_ENTITY_CREATED", "ENTITY_REGISTRY_AVAILABLE", "EHCACHE_SERVER_STORE_CREATED", "CLIENT_REGISTRY_AVAILABLE"); // passive server } else { waitForAllNotifications( - "SERVER_ENTITY_CREATED", "ENTITY_REGISTRY_AVAILABLE", "EHCACHE_SERVER_STORE_CREATED", "SERVER_ENTITY_FETCHED", "CACHE_ADDED"); + "SERVER_ENTITY_CREATED", "ENTITY_REGISTRY_AVAILABLE", "EHCACHE_SERVER_STORE_CREATED", "SERVER_ENTITY_FETCHED", "CACHE_ADDED", "CLIENT_REGISTRY_AVAILABLE"); } } @@ -305,7 +313,7 @@ public void test_E_notifs_on_add_cache() throws Exception { public void test_F_notifs_on_remove_cache() throws Exception { cacheManager.removeCache("cache-2"); - waitForAllNotifications("CACHE_REMOVED", "SERVER_ENTITY_UNFETCHED"); + waitForAllNotifications("CACHE_REMOVED", "SERVER_ENTITY_UNFETCHED", "CLIENT_REGISTRY_AVAILABLE"); } @Test @@ -373,7 +381,7 @@ public void test_G_stats_collection() throws Exception { .map(ContextualStatistics::getCapability) .collect(Collectors.toCollection(TreeSet::new)); - assertThat(capabilities).containsOnly("PoolStatistics", "ServerStoreStatistics", "OffHeapResourceStatistics"); + assertThat(capabilities).contains("PoolStatistics", "ServerStoreStatistics", "OffHeapResourceStatistics"); // ensure we collect stats from all registered objects (pools and stores) diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/management/DiagnosticTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/management/DiagnosticTest.java index fb1fdd6114..7b94d69697 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/management/DiagnosticTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/management/DiagnosticTest.java @@ -51,9 +51,9 @@ public void test_CACHE_MANAGER_CLOSED() throws Exception { int activePort = readTopology().serverStream().filter(Server::isActive).findFirst().get().getBindPort(); Properties properties = new Properties(); - properties.setProperty(ConnectionPropertyNames.CONNECTION_TIMEOUT, String.valueOf("5000")); + properties.setProperty(ConnectionPropertyNames.CONNECTION_TIMEOUT, String.valueOf("10000")); properties.setProperty(ConnectionPropertyNames.CONNECTION_NAME, "diagnostic"); - properties.setProperty(PROP_REQUEST_TIMEOUT, "5000"); + properties.setProperty(PROP_REQUEST_TIMEOUT, "10000"); properties.setProperty(PROP_REQUEST_TIMEOUTMESSAGE, "timed out"); URI uri = URI.create("diagnostic://localhost:" + activePort); diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/management/EhcacheConfigWithManagementTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/management/EhcacheConfigWithManagementTest.java index 44d1dca2d0..5fdc428695 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/management/EhcacheConfigWithManagementTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/management/EhcacheConfigWithManagementTest.java @@ -16,7 +16,6 @@ package org.ehcache.clustered.management; import org.ehcache.CacheManager; -import org.ehcache.clustered.ClusteredTests; import org.ehcache.config.units.EntryUnit; import org.ehcache.config.units.MemoryUnit; import org.ehcache.management.registry.DefaultManagementRegistryConfiguration; @@ -25,44 +24,44 @@ import org.junit.Test; import org.terracotta.testing.rules.Cluster; -import java.io.File; +import java.util.HashMap; +import java.util.Map; +import static java.util.Collections.unmodifiableMap; import static org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder.clusteredDedicated; import static org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder.clusteredShared; import static org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder.cluster; import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; -import static org.terracotta.testing.rules.BasicExternalClusterBuilder.newCluster; +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.ehcache.testing.StandardCluster.offheapResources; -public class EhcacheConfigWithManagementTest extends ClusteredTests { - private static final String RESOURCE_CONFIG = - "" - + "" - + "64" - + "64" - + "" + - "\n"; +public class EhcacheConfigWithManagementTest { - @ClassRule - public static Cluster CLUSTER = newCluster().in(new File("build/cluster")) - .withServiceFragment(RESOURCE_CONFIG).build(); - - @BeforeClass - public static void beforeClass() throws Exception { - CLUSTER.getClusterControl().waitForActive(); + private static final Map resources; + static { + HashMap map = new HashMap<>(); + map.put("primary-server-resource", 64L); + map.put("secondary-server-resource", 64L); + resources = unmodifiableMap(map); } + @ClassRule + public static Cluster CLUSTER = newCluster().in(clusterPath()) + .withServiceFragment(offheapResources(resources)).build(); + @Test public void create_cache_manager() throws Exception { CacheManager cacheManager = newCacheManagerBuilder() // cluster config .with(cluster(CLUSTER.getConnectionURI().resolve("/my-server-entity-3")) - .autoCreate() - .defaultServerResource("primary-server-resource") - .resourcePool("resource-pool-a", 10, MemoryUnit.MB, "secondary-server-resource") // <2> - .resourcePool("resource-pool-b", 8, MemoryUnit.MB)) // will take from primary-server-resource + .autoCreate(server -> server + .defaultServerResource("primary-server-resource") + .resourcePool("resource-pool-a", 10, MemoryUnit.MB, "secondary-server-resource") // <2> + .resourcePool("resource-pool-b", 8, MemoryUnit.MB))) // will take from primary-server-resource // management config .using(new DefaultManagementRegistryConfiguration() .addTags("webapp-1", "server-node-1") diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/management/EhcacheManagerToStringTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/management/EhcacheManagerToStringTest.java index dd5dbe36be..5da65a69e6 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/management/EhcacheManagerToStringTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/management/EhcacheManagerToStringTest.java @@ -36,9 +36,9 @@ import java.util.concurrent.TimeUnit; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; -import static org.junit.Assert.assertThat; public class EhcacheManagerToStringTest extends AbstractClusteringManagementTest { @@ -56,7 +56,7 @@ public void simpleOnHeapToString() throws Exception { .offheap(1, MemoryUnit.MB) .disk(2, MemoryUnit.MB, true)) .withLoaderWriter(new SampleLoaderWriter<>()) - .add(WriteBehindConfigurationBuilder + .withService(WriteBehindConfigurationBuilder .newBatchedWriteBehindConfiguration(1, TimeUnit.SECONDS, 3) .queueSize(3) .concurrencyLevel(1) @@ -82,14 +82,13 @@ public void simpleOnHeapToString() throws Exception { @Test public void clusteredToString() throws Exception { - URI uri = CLUSTER.getConnectionURI().resolve("/my-server-entity-2"); + URI uri = CLUSTER.getCluster().getConnectionURI().resolve("/my-server-entity-2"); try (CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() // cluster config .with(ClusteringServiceConfigurationBuilder.cluster(uri) - .autoCreate() - .defaultServerResource("primary-server-resource") - .resourcePool("resource-pool-a", 10, MemoryUnit.MB)) + .autoCreate(server -> server.defaultServerResource("primary-server-resource") + .resourcePool("resource-pool-a", 10, MemoryUnit.MB))) // management config .using(new DefaultManagementRegistryConfiguration() .addTags("webapp-1", "server-node-1") diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/management/ManagementClusterConnectionTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/management/ManagementClusterConnectionTest.java index 6d70c9dc93..86c4cb55ee 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/management/ManagementClusterConnectionTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/management/ManagementClusterConnectionTest.java @@ -17,90 +17,83 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; -import com.tc.net.proxy.TCPProxy; import org.ehcache.Cache; import org.ehcache.CacheManager; import org.ehcache.Status; -import org.ehcache.clustered.util.TCPProxyUtil; +import org.ehcache.clustered.util.TCPProxyManager; import org.ehcache.config.units.EntryUnit; import org.ehcache.config.units.MemoryUnit; import org.ehcache.management.registry.DefaultManagementRegistryConfiguration; import org.hamcrest.Matchers; import org.junit.AfterClass; -import org.junit.Assert; import org.junit.BeforeClass; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; -import org.terracotta.management.model.capabilities.descriptors.Settings; -import org.terracotta.testing.rules.Cluster; +import org.terracotta.utilities.test.rules.TestRetryer; -import java.io.File; import java.net.URI; -import java.util.ArrayList; +import java.time.Duration; import java.util.Arrays; -import java.util.List; +import java.util.HashMap; +import java.util.Map; +import static java.time.Duration.ofSeconds; +import static java.util.Collections.unmodifiableMap; import static org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder.clusteredDedicated; import static org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder.cluster; -import static org.ehcache.clustered.management.AbstractClusteringManagementTest.createNmsService; -import static org.ehcache.clustered.management.AbstractClusteringManagementTest.initIdentifiers; -import static org.ehcache.clustered.management.AbstractClusteringManagementTest.readTopology; -import static org.ehcache.clustered.management.AbstractClusteringManagementTest.sendManagementCallOnEntityToCollectStats; -import static org.ehcache.clustered.management.AbstractClusteringManagementTest.tearDownCacheManagerAndStatsCollector; -import static org.ehcache.clustered.util.TCPProxyUtil.setDelay; +import static org.ehcache.clustered.management.AbstractClusteringManagementTest.waitForAllNotifications; import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.leaseLength; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.ehcache.testing.StandardCluster.offheapResources; +import static org.ehcache.testing.StandardTimeouts.eventually; import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; -import static org.terracotta.testing.rules.BasicExternalClusterBuilder.newCluster; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.terracotta.utilities.test.rules.TestRetryer.OutputIs.CLASS_RULE; +import static org.terracotta.utilities.test.rules.TestRetryer.tryValues; public class ManagementClusterConnectionTest { - private static final String RESOURCE_CONFIG = - "" - + "" - + "64" - + "64" - + "" - + "\n" - + "" - + "" - + "5" - + "" - + ""; - protected static CacheManager cacheManager; protected static ObjectMapper mapper = new ObjectMapper(); - private static final List proxies = new ArrayList<>(); - - @ClassRule - public static Cluster CLUSTER = newCluster() - .in(new File("build/cluster")) - .withServiceFragment(RESOURCE_CONFIG).build(); + private static TCPProxyManager proxyManager; + private static final Map resources; + static { + HashMap map = new HashMap<>(); + map.put("primary-server-resource", 64L); + map.put("secondary-server-resource", 64L); + resources = unmodifiableMap(map); + } + @ClassRule @Rule + public static TestRetryer CLUSTER = tryValues(ofSeconds(1), ofSeconds(10), ofSeconds(30)) + .map(leaseLength -> new ClusterWithManagement( + newCluster().in(clusterPath()).withServiceFragment( + offheapResources(resources) + leaseLength(leaseLength)).build())) + .outputIs(CLASS_RULE); @BeforeClass public static void beforeClass() throws Exception { mapper.configure(SerializationFeature.INDENT_OUTPUT, true); - CLUSTER.getClusterControl().waitForActive(); + CLUSTER.get().getCluster().getClusterControl().waitForActive(); - // simulate a TMS client - createNmsService(CLUSTER); - - URI connectionURI = TCPProxyUtil.getProxyURI(CLUSTER.getConnectionURI(), proxies); + proxyManager = TCPProxyManager.create(CLUSTER.get().getCluster().getConnectionURI()); + URI connectionURI = proxyManager.getURI(); cacheManager = newCacheManagerBuilder() // cluster config .with(cluster(connectionURI.resolve("/my-server-entity-1")) - .autoCreate() + .autoCreate(server -> server .defaultServerResource("primary-server-resource") .resourcePool("resource-pool-a", 10, MemoryUnit.MB, "secondary-server-resource") // <2> - .resourcePool("resource-pool-b", 10, MemoryUnit.MB)) // will take from primary-server-resource + .resourcePool("resource-pool-b", 10, MemoryUnit.MB))) // will take from primary-server-resource // management config .using(new DefaultManagementRegistryConfiguration() .addTags("webapp-1", "server-node-1") @@ -119,8 +112,10 @@ public static void beforeClass() throws Exception { assertThat(cacheManager.getStatus(), equalTo(Status.AVAILABLE)); // test_notifs_sent_at_CM_init - AbstractClusteringManagementTest.waitForAllNotifications( + waitForAllNotifications(CLUSTER.get().getNmsService(), "CLIENT_CONNECTED", + "CLIENT_PROPERTY_ADDED", + "CLIENT_PROPERTY_ADDED", "CLIENT_REGISTRY_AVAILABLE", "CLIENT_TAGS_UPDATED", "EHCACHE_RESOURCE_POOLS_CONFIGURED", @@ -131,69 +126,57 @@ public static void beforeClass() throws Exception { "SERVER_ENTITY_FETCHED", "SERVER_ENTITY_FETCHED", "SERVER_ENTITY_FETCHED", "SERVER_ENTITY_FETCHED", "SERVER_ENTITY_FETCHED", "SERVER_ENTITY_FETCHED", "SERVER_ENTITY_UNFETCHED" ); + } - initIdentifiers(); - - sendManagementCallOnEntityToCollectStats(); + @AfterClass + public static void afterClass() { + if (proxyManager != null) { + proxyManager.close(); + } } @Test public void test_reconnection() throws Exception { - long count = readTopology().clientStream() + long count = CLUSTER.get().getNmsService().readTopology().clientStream() .filter(client -> client.getName() .startsWith("Ehcache:") && client.isManageable() && client.getTags() .containsAll(Arrays.asList("webapp-1", "server-node-1"))) .count(); - Assert.assertThat(count, Matchers.equalTo(1L)); + assertThat(count, Matchers.equalTo(1L)); String instanceId = getInstanceId(); - setDelay(6000, proxies); - Thread.sleep(6000); - - setDelay(0L, proxies); + long delay = CLUSTER.input().plusSeconds(1L).toMillis(); + proxyManager.setDelay(delay); + try { + Thread.sleep(delay); + } finally { + proxyManager.setDelay(0); + } Cache cache = cacheManager.getCache("dedicated-cache-1", String.class, String.class); String initiate_reconnect = cache.get("initiate reconnect"); assertThat(initiate_reconnect, Matchers.nullValue()); - while (!Thread.currentThread().isInterrupted()) { -// System.out.println(mapper.writeValueAsString(readTopology().toMap())); - - count = readTopology().clientStream() - .filter(client -> client.getName() - .startsWith("Ehcache:") && client.isManageable() && client.getTags() - .containsAll(Arrays.asList("webapp-1", "server-node-1"))) - .count(); - - if (count == 1) { - break; - } else { - Thread.sleep(1_000); + assertThat(() -> { + try { + return CLUSTER.get().getNmsService().readTopology().clientStream() + .filter(client -> client.getName() + .startsWith("Ehcache:") && client.isManageable() && client.getTags() + .containsAll(Arrays.asList("webapp-1", "server-node-1"))) + .count(); + } catch (Exception e) { + throw new AssertionError(e); } - } - - assertThat(Thread.currentThread().isInterrupted(), is(false)); + }, eventually().is(1L)); assertThat(getInstanceId(), equalTo(instanceId)); } private String getInstanceId() throws Exception { - return readTopology().clientStream() + return CLUSTER.get().getNmsService().readTopology().clientStream() .filter(client -> client.getName().startsWith("Ehcache:") && client.isManageable()) - .findFirst().get() - .getManagementRegistry().get() - .getCapability("SettingsCapability").get() - .getDescriptors(Settings.class).stream() - .filter(settings -> settings.containsKey("instanceId")) - .map(settings -> settings.getString("instanceId")) - .findFirst().get(); + .findFirst().get().getContext().get("instanceId"); } - - @AfterClass - public static void afterClass() throws Exception { - tearDownCacheManagerAndStatsCollector(); - } - } diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/reconnect/AutoCreateOnReconnectTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/reconnect/AutoCreateOnReconnectTest.java new file mode 100644 index 0000000000..9da62dec81 --- /dev/null +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/reconnect/AutoCreateOnReconnectTest.java @@ -0,0 +1,70 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.clustered.reconnect; + +import org.ehcache.Cache; +import org.ehcache.PersistentCacheManager; +import org.ehcache.config.units.MemoryUnit; +import org.junit.ClassRule; +import org.junit.Test; +import org.terracotta.testing.rules.Cluster; + +import java.net.URI; + +import static org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder.clusteredDedicated; +import static org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder.cluster; +import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; +import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder; +import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.ehcache.testing.StandardCluster.offheapResource; +import static org.ehcache.testing.StandardTimeouts.eventually; +import static org.hamcrest.MatcherAssert.assertThat; + + +public class AutoCreateOnReconnectTest { + + @ClassRule + public static Cluster CLUSTER = newCluster(1).in(clusterPath()) + .withServiceFragment(offheapResource("primary-server-resource", 64)).build(); + + @Test + public void cacheManagerCanReconnect() throws Exception { + URI connectionURI = CLUSTER.getConnectionURI(); + + try (PersistentCacheManager cacheManager = newCacheManagerBuilder() + .with(cluster(connectionURI.resolve("/crud-cm")) + .autoCreateOnReconnect(server -> server.defaultServerResource("primary-server-resource"))) + .build(true)) { + + Cache cache = cacheManager.createCache("clustered-cache", + newCacheConfigurationBuilder(Long.class, String.class, newResourcePoolsBuilder() + .with(clusteredDedicated("primary-server-resource", 1, MemoryUnit.MB))) + .build()); + + cache.put(1L, "one"); + + CLUSTER.getClusterControl().terminateAllServers(); + CLUSTER.getClusterControl().startAllServers(); + + assertThat(() -> { + cache.put(1L, "two"); + return cache.get(1L); + }, eventually().is("two")); + } + } +} diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/reconnect/BasicCacheReconnectTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/reconnect/BasicCacheReconnectTest.java index 162baf2b26..04e67af0cb 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/reconnect/BasicCacheReconnectTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/reconnect/BasicCacheReconnectTest.java @@ -15,59 +15,50 @@ */ package org.ehcache.clustered.reconnect; -import com.tc.net.proxy.TCPProxy; import org.ehcache.Cache; -import org.ehcache.CacheManager; import org.ehcache.PersistentCacheManager; import org.ehcache.StateTransitionException; -import org.ehcache.clustered.ClusteredTests; import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder; import org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder; import org.ehcache.clustered.client.internal.store.ReconnectInProgressException; -import org.ehcache.clustered.util.TCPProxyUtil; +import org.ehcache.clustered.util.TCPProxyManager; import org.ehcache.config.CacheConfiguration; import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.config.builders.CacheManagerBuilder; import org.ehcache.config.builders.ResourcePoolsBuilder; import org.ehcache.config.units.MemoryUnit; +import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.ClassRule; -import org.junit.Ignore; +import org.junit.Rule; import org.junit.Test; import org.terracotta.testing.rules.Cluster; +import org.terracotta.utilities.test.rules.TestRetryer; -import java.io.File; import java.net.URI; -import java.util.ArrayList; -import java.util.List; +import java.time.Duration; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; -import static org.ehcache.clustered.util.TCPProxyUtil.setDelay; +import static java.time.Duration.ofSeconds; +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.leaseLength; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.ehcache.testing.StandardCluster.offheapResource; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; -import static org.terracotta.testing.rules.BasicExternalClusterBuilder.newCluster; - -public class BasicCacheReconnectTest extends ClusteredTests { - public static final String RESOURCE_CONFIG = - "" - + "" - + "64" - + "" - + "\n" - + "" - + "" - + "5" - + "" - + ""; +import static org.terracotta.utilities.test.rules.TestRetryer.OutputIs.CLASS_RULE; +import static org.terracotta.utilities.test.rules.TestRetryer.tryValues; + +public class BasicCacheReconnectTest { + + private static TCPProxyManager proxyManager; private static PersistentCacheManager cacheManager; private static CacheConfiguration config = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, @@ -76,27 +67,30 @@ public class BasicCacheReconnectTest extends ClusteredTests { .withResilienceStrategy(new ThrowingResiliencyStrategy<>()) .build(); - private static final List proxies = new ArrayList<>(); - - @ClassRule - public static Cluster CLUSTER = - newCluster().in(new File("build/cluster")).withServiceFragment(RESOURCE_CONFIG).build(); + @ClassRule @Rule + public static final TestRetryer CLUSTER = tryValues(ofSeconds(1), ofSeconds(10), ofSeconds(30)) + .map(leaseLength -> newCluster().in(clusterPath()).withServiceFragment( + offheapResource("primary-server-resource", 64) + leaseLength(leaseLength)).build()) + .outputIs(CLASS_RULE); @BeforeClass - public static void waitForActive() throws Exception { - CLUSTER.getClusterControl().waitForActive(); - - URI connectionURI = TCPProxyUtil.getProxyURI(CLUSTER.getConnectionURI(), proxies); + public static void initializeCacheManager() throws Exception { + proxyManager = TCPProxyManager.create(CLUSTER.get().getConnectionURI()); + URI connectionURI = proxyManager.getURI(); CacheManagerBuilder clusteredCacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder() .with(ClusteringServiceConfigurationBuilder.cluster(connectionURI.resolve("/crud-cm")) - .autoCreate() - .defaultServerResource("primary-server-resource")); + .autoCreate(server -> server.defaultServerResource("primary-server-resource"))); cacheManager = clusteredCacheManagerBuilder.build(false); cacheManager.init(); } + @AfterClass + public static void stopProxies() { + proxyManager.close(); + } + @Test public void cacheOpsDuringReconnection() throws Exception { @@ -164,10 +158,13 @@ public void reconnectDuringCacheDestroy() throws Exception { } - private static void expireLease() throws InterruptedException { - setDelay(6000, proxies); - Thread.sleep(6000); - - setDelay(0L, proxies); + private void expireLease() throws InterruptedException { + long delay = CLUSTER.input().plusSeconds(1L).toMillis(); + proxyManager.setDelay(delay); + try { + Thread.sleep(delay); + } finally { + proxyManager.setDelay(0); + } } -} \ No newline at end of file +} diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/reconnect/CacheManagerDestroyReconnectTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/reconnect/CacheManagerDestroyReconnectTest.java index bbb3dc92ca..ba210f368d 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/reconnect/CacheManagerDestroyReconnectTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/reconnect/CacheManagerDestroyReconnectTest.java @@ -15,66 +15,75 @@ */ package org.ehcache.clustered.reconnect; -import com.tc.net.proxy.TCPProxy; import org.ehcache.PersistentCacheManager; -import org.ehcache.clustered.ClusteredTests; import org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder; -import org.ehcache.clustered.util.TCPProxyUtil; +import org.ehcache.clustered.util.TCPProxyManager; import org.ehcache.config.builders.CacheManagerBuilder; +import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.terracotta.testing.rules.Cluster; +import org.terracotta.utilities.test.rules.TestRetryer; -import java.io.File; import java.net.URI; -import java.util.ArrayList; -import java.util.List; +import java.time.Duration; -import static org.ehcache.clustered.reconnect.BasicCacheReconnectTest.RESOURCE_CONFIG; -import static org.ehcache.clustered.util.TCPProxyUtil.setDelay; -import static org.terracotta.testing.rules.BasicExternalClusterBuilder.newCluster; +import static java.time.Duration.ofSeconds; -public class CacheManagerDestroyReconnectTest extends ClusteredTests { +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.leaseLength; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.ehcache.testing.StandardCluster.offheapResource; +import static org.terracotta.utilities.test.rules.TestRetryer.OutputIs.CLASS_RULE; +import static org.terracotta.utilities.test.rules.TestRetryer.tryValues; +public class CacheManagerDestroyReconnectTest { + private static TCPProxyManager proxyManager; private static PersistentCacheManager cacheManager; - private static final List proxies = new ArrayList<>(); - - @ClassRule - public static Cluster CLUSTER = - newCluster().in(new File("build/cluster")).withServiceFragment(RESOURCE_CONFIG).build(); + @ClassRule @Rule + public static final TestRetryer CLUSTER = tryValues(ofSeconds(1), ofSeconds(10), ofSeconds(30)) + .map(leaseLength -> newCluster().in(clusterPath()).withServiceFragment( + offheapResource("primary-server-resource", 64) + leaseLength(leaseLength)).build()) + .outputIs(CLASS_RULE); @BeforeClass - public static void waitForActive() throws Exception { - CLUSTER.getClusterControl().waitForActive(); - - URI connectionURI = TCPProxyUtil.getProxyURI(CLUSTER.getConnectionURI(), proxies); + public static void initializeCacheManager() throws Exception { + proxyManager = TCPProxyManager.create(CLUSTER.get().getConnectionURI()); + URI connectionURI = proxyManager.getURI(); CacheManagerBuilder clusteredCacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder() .with(ClusteringServiceConfigurationBuilder.cluster(connectionURI.resolve("/crud-cm")) - .autoCreate() - .defaultServerResource("primary-server-resource")); + .autoCreate(server -> server.defaultServerResource("primary-server-resource"))); cacheManager = clusteredCacheManagerBuilder.build(false); cacheManager.init(); } + @AfterClass + public static void stopProxies() { + proxyManager.close(); + } + @Test public void testDestroyCacheManagerReconnects() throws Exception { - setDelay(6000, proxies); - Thread.sleep(6000); - - setDelay(0L, proxies); + long delay = CLUSTER.input().plusSeconds(1L).toMillis(); + proxyManager.setDelay(delay); + try { + Thread.sleep(delay); + } finally { + proxyManager.setDelay(0); + } cacheManager.close(); cacheManager.destroy(); System.out.println(cacheManager.getStatus()); - } } diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/reconnect/EventsReconnectTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/reconnect/EventsReconnectTest.java new file mode 100644 index 0000000000..91ddbfb521 --- /dev/null +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/reconnect/EventsReconnectTest.java @@ -0,0 +1,178 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.clustered.reconnect; + +import org.ehcache.Cache; +import org.ehcache.PersistentCacheManager; +import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder; +import org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder; +import org.ehcache.clustered.client.internal.store.ReconnectInProgressException; +import org.ehcache.clustered.util.TCPProxyManager; +import org.ehcache.config.CacheConfiguration; +import org.ehcache.config.builders.CacheConfigurationBuilder; +import org.ehcache.config.builders.CacheEventListenerConfigurationBuilder; +import org.ehcache.config.builders.CacheManagerBuilder; +import org.ehcache.config.builders.ResourcePoolsBuilder; +import org.ehcache.config.units.MemoryUnit; +import org.ehcache.event.CacheEvent; +import org.ehcache.event.CacheEventListener; +import org.ehcache.event.EventType; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.terracotta.testing.rules.Cluster; +import org.terracotta.utilities.test.rules.TestRetryer; + +import java.net.URI; +import java.time.Duration; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; + +import static java.time.Duration.ofSeconds; +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.leaseLength; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.ehcache.testing.StandardCluster.offheapResource; +import static org.ehcache.testing.StandardTimeouts.eventually; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.collection.IsCollectionWithSize.hasSize; +import static org.junit.Assert.fail; + +import static org.terracotta.utilities.test.rules.TestRetryer.OutputIs.CLASS_RULE; +import static org.terracotta.utilities.test.rules.TestRetryer.tryValues; + +public class EventsReconnectTest { + + private static TCPProxyManager proxyManager; + private static PersistentCacheManager cacheManager; + + private static class AccountingCacheEventListener implements CacheEventListener { + private final Map>> events; + + AccountingCacheEventListener() { + events = new HashMap<>(); + clear(); + } + + @Override + public void onEvent(CacheEvent event) { + events.get(event.getType()).add(event); + } + + final void clear() { + for (EventType value : EventType.values()) { + events.put(value, new CopyOnWriteArrayList<>()); + } + } + + } + + private static AccountingCacheEventListener cacheEventListener = new AccountingCacheEventListener<>(); + + private static CacheConfiguration config = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, + ResourcePoolsBuilder.newResourcePoolsBuilder() + .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 1, MemoryUnit.MB))) + .withService(CacheEventListenerConfigurationBuilder + .newEventListenerConfiguration(cacheEventListener, EnumSet.allOf(EventType.class)) + .unordered().asynchronous()) + .withResilienceStrategy(new ThrowingResiliencyStrategy<>()) + .build(); + + @ClassRule @Rule + public static final TestRetryer CLUSTER = tryValues(ofSeconds(1), ofSeconds(10), ofSeconds(30)) + .map(leaseLength -> newCluster().in(clusterPath()).withServiceFragment( + offheapResource("primary-server-resource", 64) + leaseLength(leaseLength)).build()) + .outputIs(CLASS_RULE); + + @BeforeClass + public static void initializeCacheManager() throws Exception { + proxyManager = TCPProxyManager.create(CLUSTER.get().getConnectionURI()); + URI connectionURI = proxyManager.getURI(); + + CacheManagerBuilder clusteredCacheManagerBuilder + = CacheManagerBuilder.newCacheManagerBuilder() + .with(ClusteringServiceConfigurationBuilder.cluster(connectionURI.resolve("/crud-cm")) + .autoCreate(s -> s.defaultServerResource("primary-server-resource"))); + cacheManager = clusteredCacheManagerBuilder.build(false); + cacheManager.init(); + } + + @AfterClass + public static void stopProxies() { + proxyManager.close(); + } + + @Test + public void eventsFlowAgainAfterReconnection() throws Exception { + try { + Cache cache = cacheManager.createCache("clustered-cache", config); + + CompletableFuture future = CompletableFuture.runAsync(() -> + ThreadLocalRandom.current() + .longs() + .filter(val -> val != Long.MAX_VALUE) + .forEach(value -> + cache.put(value, Long.toString(value)))); + + expireLease(); + + try { + future.get(5000, TimeUnit.MILLISECONDS); + fail(); + } catch (ExecutionException e) { + assertThat(e.getCause().getCause().getCause(), instanceOf(ReconnectInProgressException.class)); + } + int beforeDisconnectionEventCounter = cacheEventListener.events.get(EventType.CREATED).size(); + + CompletableFuture getSucceededFuture = CompletableFuture.runAsync(() -> { + while (true) { + try { + cache.put(Long.MAX_VALUE, ""); + break; + } catch (RuntimeException e) { + + } + } + }); + + assertThat(getSucceededFuture::isDone, eventually().is(true)); + getSucceededFuture.getNow(null); + assertThat(() -> cacheEventListener.events.get(EventType.CREATED), eventually().matches(hasSize(beforeDisconnectionEventCounter + 1))); + } finally { + cacheManager.destroyCache("clustered-cache"); + } + } + + private static void expireLease() throws InterruptedException { + long delay = CLUSTER.input().plusSeconds(1L).toMillis(); + proxyManager.setDelay(delay); + try { + Thread.sleep(delay); + } finally { + proxyManager.setDelay(0); + } + } +} diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/replication/BasicClusteredCacheOpsReplicationMultiThreadedTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/replication/BasicClusteredCacheOpsReplicationMultiThreadedTest.java index a7617cba6a..6a2693451e 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/replication/BasicClusteredCacheOpsReplicationMultiThreadedTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/replication/BasicClusteredCacheOpsReplicationMultiThreadedTest.java @@ -19,12 +19,13 @@ import org.ehcache.Cache; import org.ehcache.PersistentCacheManager; import org.ehcache.Status; -import org.ehcache.clustered.ClusteredTests; import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder; import org.ehcache.clustered.client.config.builders.ClusteredStoreConfigurationBuilder; import org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder; import org.ehcache.clustered.client.config.builders.TimeoutsBuilder; import org.ehcache.clustered.common.Consistency; +import org.ehcache.clustered.util.runners.ParallelParameterized; +import org.ehcache.clustered.util.ParallelTestCluster; import org.ehcache.config.CacheConfiguration; import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.config.builders.CacheManagerBuilder; @@ -35,16 +36,16 @@ import org.junit.Before; import org.junit.ClassRule; import org.junit.Ignore; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TestName; import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameter; import org.junit.runners.Parameterized.Parameters; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.terracotta.testing.rules.Cluster; +import org.terracotta.utilities.test.WaitForAssert; -import java.io.File; import java.io.Serializable; import java.time.Duration; import java.util.ArrayList; @@ -62,12 +63,15 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.ehcache.testing.StandardCluster.offheapResource; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; -import static org.terracotta.testing.rules.BasicExternalClusterBuilder.newCluster; + /** * This test asserts Active-Passive fail-over with @@ -75,22 +79,16 @@ * Note that fail-over is happening while client threads are still writing * Finally the same key set correctness is asserted. */ -@RunWith(Parameterized.class) -public class BasicClusteredCacheOpsReplicationMultiThreadedTest extends ClusteredTests { +@RunWith(ParallelParameterized.class) +public class BasicClusteredCacheOpsReplicationMultiThreadedTest { private static final int NUM_OF_THREADS = 10; private static final int JOB_SIZE = 100; - private static final String RESOURCE_CONFIG = - "" - + "" - + "16" - + "" + - "\n"; - - private static PersistentCacheManager CACHE_MANAGER1; - private static PersistentCacheManager CACHE_MANAGER2; - private static Cache CACHE1; - private static Cache CACHE2; + + private PersistentCacheManager cacheManager1; + private PersistentCacheManager cacheManager2; + private Cache cache1; + private Cache cache2; @Parameters(name = "consistency={0}") public static Consistency[] data() { @@ -100,9 +98,13 @@ public static Consistency[] data() { @Parameter public Consistency cacheConsistency; - @ClassRule - public static Cluster CLUSTER = - newCluster(2).in(new File("build/cluster")).withServiceFragment(RESOURCE_CONFIG).build(); + @ClassRule @Rule + public static final ParallelTestCluster CLUSTER = new ParallelTestCluster(newCluster(2).in(clusterPath()) + .withServerHeap(512) + .withServiceFragment(offheapResource("primary-server-resource", 16)).build()); + + @Rule + public final TestName testName = new TestName(); private final Logger log = LoggerFactory.getLogger(getClass()); @@ -115,29 +117,26 @@ public static Consistency[] data() { @Before public void startServers() throws Exception { CLUSTER.getClusterControl().startAllServers(); - CLUSTER.getClusterControl().waitForActive(); - CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); final CacheManagerBuilder clusteredCacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder() .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER.getConnectionURI().resolve("/crud-cm-replication")) .timeouts(TimeoutsBuilder.timeouts() // we need to give some time for the failover to occur .read(Duration.ofMinutes(1)) .write(Duration.ofMinutes(1))) - .autoCreate() - .defaultServerResource("primary-server-resource")); - CACHE_MANAGER1 = clusteredCacheManagerBuilder.build(true); - CACHE_MANAGER2 = clusteredCacheManagerBuilder.build(true); + .autoCreate(server -> server.defaultServerResource("primary-server-resource"))); + cacheManager1 = clusteredCacheManagerBuilder.build(true); + cacheManager2 = clusteredCacheManagerBuilder.build(true); CacheConfiguration config = CacheConfigurationBuilder .newCacheConfigurationBuilder(Long.class, BlobValue.class, ResourcePoolsBuilder.newResourcePoolsBuilder().heap(500, EntryUnit.ENTRIES) .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 4, MemoryUnit.MB))) - .add(ClusteredStoreConfigurationBuilder.withConsistency(cacheConsistency)) + .withService(ClusteredStoreConfigurationBuilder.withConsistency(cacheConsistency)) .build(); - CACHE1 = CACHE_MANAGER1.createCache("clustered-cache", config); - CACHE2 = CACHE_MANAGER2.createCache("clustered-cache", config); + cache1 = cacheManager1.createCache(testName.getMethodName(), config); + cache2 = cacheManager2.createCache(testName.getMethodName(), config); - caches = Arrays.asList(CACHE1, CACHE2); + caches = Arrays.asList(cache1, cache2); } @After @@ -146,12 +145,11 @@ public void tearDown() throws Exception { if(!unprocessed.isEmpty()) { log.warn("Tearing down with {} unprocess task", unprocessed); } - if(CACHE_MANAGER1 != null && CACHE_MANAGER1.getStatus() != Status.UNINITIALIZED) { - CACHE_MANAGER1.close(); + if(cacheManager1 != null && cacheManager1.getStatus() != Status.UNINITIALIZED) { + cacheManager1.close(); } - if(CACHE_MANAGER2 != null && CACHE_MANAGER2.getStatus() != Status.UNINITIALIZED) { - CACHE_MANAGER2.close(); - CACHE_MANAGER2.destroy(); + if(cacheManager2 != null && cacheManager2.getStatus() != Status.UNINITIALIZED) { + cacheManager2.close(); } } @@ -171,10 +169,11 @@ public void testCRUD() throws Exception { //This step is to add values in local tier randomly to test invalidations happen correctly futures.add(executorService.submit(() -> universalSet.forEach(x -> { - CACHE1.get(x); - CACHE2.get(x); + cache1.get(x); + cache2.get(x); }))); + CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); CLUSTER.getClusterControl().terminateActive(); drainTasks(futures); @@ -182,10 +181,10 @@ public void testCRUD() throws Exception { Set readKeysByCache1AfterFailOver = new HashSet<>(); Set readKeysByCache2AfterFailOver = new HashSet<>(); universalSet.forEach(x -> { - if (CACHE1.get(x) != null) { + if (cache1.get(x) != null) { readKeysByCache1AfterFailOver.add(x); } - if (CACHE2.get(x) != null) { + if (cache2.get(x) != null) { readKeysByCache2AfterFailOver.add(x); } }); @@ -214,11 +213,12 @@ public void testBulkOps() throws Exception { //This step is to add values in local tier randomly to test invalidations happen correctly futures.add(executorService.submit(() -> { universalSet.forEach(x -> { - CACHE1.get(x); - CACHE2.get(x); + cache1.get(x); + cache2.get(x); }); })); + CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); CLUSTER.getClusterControl().terminateActive(); drainTasks(futures); @@ -226,10 +226,10 @@ public void testBulkOps() throws Exception { Set readKeysByCache1AfterFailOver = new HashSet<>(); Set readKeysByCache2AfterFailOver = new HashSet<>(); universalSet.forEach(x -> { - if (CACHE1.get(x) != null) { + if (cache1.get(x) != null) { readKeysByCache1AfterFailOver.add(x); } - if (CACHE2.get(x) != null) { + if (cache2.get(x) != null) { readKeysByCache2AfterFailOver.add(x); } }); @@ -261,17 +261,18 @@ public void testClear() throws Exception { drainTasks(futures); universalSet.forEach(x -> { - CACHE1.get(x); - CACHE2.get(x); + cache1.get(x); + cache2.get(x); }); - Future clearFuture = executorService.submit(() -> CACHE1.clear()); + Future clearFuture = executorService.submit(() -> cache1.clear()); + CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); CLUSTER.getClusterControl().terminateActive(); clearFuture.get(); - universalSet.forEach(x -> assertThat(CACHE2.get(x), nullValue())); + universalSet.forEach(x -> assertThat(cache2.get(x), nullValue())); } diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/replication/BasicClusteredCacheOpsReplicationTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/replication/BasicClusteredCacheOpsReplicationTest.java index 536e614e93..85528e82b8 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/replication/BasicClusteredCacheOpsReplicationTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/replication/BasicClusteredCacheOpsReplicationTest.java @@ -18,12 +18,13 @@ import org.ehcache.Cache; import org.ehcache.PersistentCacheManager; -import org.ehcache.clustered.ClusteredTests; import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder; import org.ehcache.clustered.client.config.builders.ClusteredStoreConfigurationBuilder; import org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder; import org.ehcache.clustered.client.config.builders.TimeoutsBuilder; import org.ehcache.clustered.common.Consistency; +import org.ehcache.clustered.util.runners.ParallelParameterized; +import org.ehcache.clustered.util.ParallelTestCluster; import org.ehcache.config.CacheConfiguration; import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.config.builders.CacheManagerBuilder; @@ -33,14 +34,13 @@ import org.junit.After; import org.junit.Before; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TestName; import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameter; import org.junit.runners.Parameterized.Parameters; -import org.terracotta.testing.rules.Cluster; -import java.io.File; import java.time.Duration; import java.util.ArrayList; import java.util.HashMap; @@ -48,25 +48,21 @@ import java.util.Map; import java.util.Set; +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.ehcache.testing.StandardCluster.offheapResource; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; -import static org.terracotta.testing.rules.BasicExternalClusterBuilder.newCluster; -@RunWith(Parameterized.class) -public class BasicClusteredCacheOpsReplicationTest extends ClusteredTests { - private static final String RESOURCE_CONFIG = - "" - + "" - + "16" - + "" + - "\n"; +@RunWith(ParallelParameterized.class) +public class BasicClusteredCacheOpsReplicationTest { - private static PersistentCacheManager CACHE_MANAGER; - private static Cache CACHE1; - private static Cache CACHE2; + private PersistentCacheManager cacheManager; + private Cache cacheOne; + private Cache cacheTwo; @Parameters(name = "consistency={0}") public static Consistency[] data() { @@ -76,45 +72,45 @@ public static Consistency[] data() { @Parameter public Consistency cacheConsistency; - @ClassRule - public static Cluster CLUSTER = - newCluster(2).in(new File("build/cluster")).withServiceFragment(RESOURCE_CONFIG).build(); + @ClassRule @Rule + public static final ParallelTestCluster CLUSTER = new ParallelTestCluster(newCluster(2).in(clusterPath()) + .withServerHeap(512) + .withServiceFragment(offheapResource("primary-server-resource", 32)).build()); + + @Rule + public final TestName testName = new TestName(); @Before public void startServers() throws Exception { CLUSTER.getClusterControl().startAllServers(); - CLUSTER.getClusterControl().waitForActive(); - CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); final CacheManagerBuilder clusteredCacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder() .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER.getConnectionURI().resolve("/cm-replication")) .timeouts(TimeoutsBuilder.timeouts() // we need to give some time for the failover to occur .read(Duration.ofMinutes(1)) .write(Duration.ofMinutes(1))) - .autoCreate() - .defaultServerResource("primary-server-resource")); - CACHE_MANAGER = clusteredCacheManagerBuilder.build(true); + .autoCreate(server -> server.defaultServerResource("primary-server-resource"))); + cacheManager = clusteredCacheManagerBuilder.build(true); CacheConfiguration config = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder().heap(100, EntryUnit.ENTRIES) - .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 4, MemoryUnit.MB))) - .add(ClusteredStoreConfigurationBuilder.withConsistency(cacheConsistency)) + .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 1, MemoryUnit.MB))) + .withService(ClusteredStoreConfigurationBuilder.withConsistency(cacheConsistency)) .build(); - CACHE1 = CACHE_MANAGER.createCache("clustered-cache", config); - CACHE2 = CACHE_MANAGER.createCache("another-cache", config); + cacheOne = cacheManager.createCache(testName.getMethodName() + "-1", config); + cacheTwo = cacheManager.createCache(testName.getMethodName() + "-2", config); } @After - public void tearDown() throws Exception { - CACHE_MANAGER.close(); - CACHE_MANAGER.destroy(); + public void tearDown() { + cacheManager.close(); } @Test public void testCRUD() throws Exception { List> caches = new ArrayList<>(); - caches.add(CACHE1); - caches.add(CACHE2); + caches.add(cacheOne); + caches.add(cacheTwo); caches.forEach(x -> { x.put(1L, "The one"); x.put(2L, "The two"); @@ -127,6 +123,7 @@ public void testCRUD() throws Exception { x.remove(4L); }); + CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); CLUSTER.getClusterControl().terminateActive(); caches.forEach(x -> { @@ -140,8 +137,8 @@ public void testCRUD() throws Exception { @Test public void testBulkOps() throws Exception { List> caches = new ArrayList<>(); - caches.add(CACHE1); - caches.add(CACHE2); + caches.add(cacheOne); + caches.add(cacheTwo); Map entriesMap = new HashMap<>(); entriesMap.put(1L, "one"); @@ -152,6 +149,7 @@ public void testBulkOps() throws Exception { entriesMap.put(6L, "six"); caches.forEach(cache -> cache.putAll(entriesMap)); + CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); CLUSTER.getClusterControl().terminateActive(); Set keySet = entriesMap.keySet(); @@ -170,8 +168,8 @@ public void testBulkOps() throws Exception { @Test public void testCAS() throws Exception { List> caches = new ArrayList<>(); - caches.add(CACHE1); - caches.add(CACHE2); + caches.add(cacheOne); + caches.add(cacheTwo); caches.forEach(cache -> { assertThat(cache.putIfAbsent(1L, "one"), nullValue()); assertThat(cache.putIfAbsent(2L, "two"), nullValue()); @@ -179,6 +177,7 @@ public void testCAS() throws Exception { assertThat(cache.replace(3L, "another one", "yet another one"), is(false)); }); + CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); CLUSTER.getClusterControl().terminateActive(); caches.forEach(cache -> { @@ -193,8 +192,8 @@ public void testCAS() throws Exception { public void testClear() throws Exception { List> caches = new ArrayList<>(); - caches.add(CACHE1); - caches.add(CACHE2); + caches.add(cacheOne); + caches.add(cacheTwo); Map entriesMap = new HashMap<>(); entriesMap.put(1L, "one"); @@ -216,13 +215,14 @@ public void testClear() throws Exception { assertThat(all.get(6L), is("six")); }); - CACHE1.clear(); - CACHE2.clear(); + cacheOne.clear(); + cacheTwo.clear(); + CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); CLUSTER.getClusterControl().terminateActive(); - keySet.forEach(x -> assertThat(CACHE1.get(x), nullValue())); - keySet.forEach(x -> assertThat(CACHE2.get(x), nullValue())); + keySet.forEach(x -> assertThat(cacheOne.get(x), nullValue())); + keySet.forEach(x -> assertThat(cacheTwo.get(x), nullValue())); } diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/replication/BasicClusteredCacheOpsReplicationWithMultipleClientsTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/replication/BasicClusteredCacheOpsReplicationWithMultipleClientsTest.java index 3860a9eb77..3272330d39 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/replication/BasicClusteredCacheOpsReplicationWithMultipleClientsTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/replication/BasicClusteredCacheOpsReplicationWithMultipleClientsTest.java @@ -18,12 +18,13 @@ import org.ehcache.Cache; import org.ehcache.PersistentCacheManager; -import org.ehcache.clustered.ClusteredTests; import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder; import org.ehcache.clustered.client.config.builders.ClusteredStoreConfigurationBuilder; import org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder; import org.ehcache.clustered.client.config.builders.TimeoutsBuilder; import org.ehcache.clustered.common.Consistency; +import org.ehcache.clustered.util.runners.ParallelParameterized; +import org.ehcache.clustered.util.ParallelTestCluster; import org.ehcache.config.CacheConfiguration; import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.config.builders.CacheManagerBuilder; @@ -34,14 +35,13 @@ import org.junit.Before; import org.junit.ClassRule; import org.junit.Ignore; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TestName; import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameter; import org.junit.runners.Parameterized.Parameters; -import org.terracotta.testing.rules.Cluster; -import java.io.File; import java.io.Serializable; import java.time.Duration; import java.util.ArrayList; @@ -53,29 +53,25 @@ import java.util.Set; import java.util.stream.LongStream; +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.ehcache.testing.StandardCluster.offheapResource; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; -import static org.terracotta.testing.rules.BasicExternalClusterBuilder.newCluster; + /** * The point of this test is to assert proper data read after fail-over handling. */ -@RunWith(Parameterized.class) -public class BasicClusteredCacheOpsReplicationWithMultipleClientsTest extends ClusteredTests { - - private static final String RESOURCE_CONFIG = - "" - + "" - + "16" - + "" + - "\n"; +@RunWith(ParallelParameterized.class) +public class BasicClusteredCacheOpsReplicationWithMultipleClientsTest { - private static PersistentCacheManager CACHE_MANAGER1; - private static PersistentCacheManager CACHE_MANAGER2; - private static Cache CACHE1; - private static Cache CACHE2; + private PersistentCacheManager cacheManager1; + private PersistentCacheManager cacheManager2; + private Cache cache1; + private Cache cache2; @Parameters(name = "consistency={0}") public static Consistency[] data() { @@ -85,38 +81,38 @@ public static Consistency[] data() { @Parameter public Consistency cacheConsistency; - @ClassRule - public static Cluster CLUSTER = - newCluster(2).in(new File("build/cluster")).withServiceFragment(RESOURCE_CONFIG).build(); + @ClassRule @Rule + public static final ParallelTestCluster CLUSTER = new ParallelTestCluster(newCluster(2).in(clusterPath()) + .withServerHeap(512) + .withServiceFragment(offheapResource("primary-server-resource", 16)).build()); + + @Rule + public final TestName testName = new TestName(); @Before public void startServers() throws Exception { CLUSTER.getClusterControl().startAllServers(); - CLUSTER.getClusterControl().waitForActive(); - CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); final CacheManagerBuilder clusteredCacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder() .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER.getConnectionURI().resolve("/crud-cm-replication")) - .timeouts(TimeoutsBuilder.timeouts().read(Duration.ofSeconds(20)).write(Duration.ofSeconds(20))) - .autoCreate() - .defaultServerResource("primary-server-resource")); - CACHE_MANAGER1 = clusteredCacheManagerBuilder.build(true); - CACHE_MANAGER2 = clusteredCacheManagerBuilder.build(true); + .timeouts(TimeoutsBuilder.timeouts().read(Duration.ofSeconds(20)).write(Duration.ofSeconds(20))) + .autoCreate(server -> server.defaultServerResource("primary-server-resource"))); + cacheManager1 = clusteredCacheManagerBuilder.build(true); + cacheManager2 = clusteredCacheManagerBuilder.build(true); CacheConfiguration config = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, BlobValue.class, ResourcePoolsBuilder.newResourcePoolsBuilder().heap(500, EntryUnit.ENTRIES) .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 4, MemoryUnit.MB))) - .add(ClusteredStoreConfigurationBuilder.withConsistency(cacheConsistency)) + .withService(ClusteredStoreConfigurationBuilder.withConsistency(cacheConsistency)) .build(); - CACHE1 = CACHE_MANAGER1.createCache("clustered-cache", config); - CACHE2 = CACHE_MANAGER2.createCache("clustered-cache", config); + cache1 = cacheManager1.createCache(testName.getMethodName(), config); + cache2 = cacheManager2.createCache(testName.getMethodName(), config); } @After public void tearDown() throws Exception { - CACHE_MANAGER1.close(); - CACHE_MANAGER2.close(); - CACHE_MANAGER2.destroy(); + cacheManager1.close(); + cacheManager2.close(); } @Test(timeout=180000) @@ -125,29 +121,30 @@ public void testCRUD() throws Exception { LongStream longStream = random.longs(1000); Set added = new HashSet<>(); longStream.forEach(x -> { - CACHE1.put(x, new BlobValue()); + cache1.put(x, new BlobValue()); added.add(x); }); Set readKeysByCache2BeforeFailOver = new HashSet<>(); added.forEach(x -> { - if (CACHE2.get(x) != null) { + if (cache2.get(x) != null) { readKeysByCache2BeforeFailOver.add(x); } }); + CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); CLUSTER.getClusterControl().terminateActive(); Set readKeysByCache1AfterFailOver = new HashSet<>(); added.forEach(x -> { - if (CACHE1.get(x) != null) { + if (cache1.get(x) != null) { readKeysByCache1AfterFailOver.add(x); } }); assertThat(readKeysByCache2BeforeFailOver.size(), greaterThanOrEqualTo(readKeysByCache1AfterFailOver.size())); - readKeysByCache1AfterFailOver.stream().filter(readKeysByCache2BeforeFailOver::contains).forEach(y -> assertThat(CACHE2.get(y), notNullValue())); + readKeysByCache1AfterFailOver.stream().filter(readKeysByCache2BeforeFailOver::contains).forEach(y -> assertThat(cache2.get(y), notNullValue())); } @@ -155,8 +152,8 @@ public void testCRUD() throws Exception { @Ignore //TODO: FIXME: FIX THIS RANDOMLY FAILING TEST public void testBulkOps() throws Exception { List> caches = new ArrayList<>(); - caches.add(CACHE1); - caches.add(CACHE2); + caches.add(cache1); + caches.add(cache2); Map entriesMap = new HashMap<>(); @@ -170,31 +167,32 @@ public void testBulkOps() throws Exception { Set readKeysByCache2BeforeFailOver = new HashSet<>(); keySet.forEach(x -> { - if (CACHE2.get(x) != null) { + if (cache2.get(x) != null) { readKeysByCache2BeforeFailOver.add(x); } }); + CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); CLUSTER.getClusterControl().terminateActive(); Set readKeysByCache1AfterFailOver = new HashSet<>(); keySet.forEach(x -> { - if (CACHE1.get(x) != null) { + if (cache1.get(x) != null) { readKeysByCache1AfterFailOver.add(x); } }); assertThat(readKeysByCache2BeforeFailOver.size(), greaterThanOrEqualTo(readKeysByCache1AfterFailOver.size())); - readKeysByCache1AfterFailOver.stream().filter(readKeysByCache2BeforeFailOver::contains).forEach(y -> assertThat(CACHE2.get(y), notNullValue())); + readKeysByCache1AfterFailOver.stream().filter(readKeysByCache2BeforeFailOver::contains).forEach(y -> assertThat(cache2.get(y), notNullValue())); } @Test(timeout=180000) public void testClear() throws Exception { List> caches = new ArrayList<>(); - caches.add(CACHE1); - caches.add(CACHE2); + caches.add(cache1); + caches.add(cache2); Map entriesMap = new HashMap<>(); @@ -208,19 +206,20 @@ public void testClear() throws Exception { Set readKeysByCache2BeforeFailOver = new HashSet<>(); keySet.forEach(x -> { - if (CACHE2.get(x) != null) { + if (cache2.get(x) != null) { readKeysByCache2BeforeFailOver.add(x); } }); - CACHE1.clear(); + cache1.clear(); + CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); CLUSTER.getClusterControl().terminateActive(); if (cacheConsistency == Consistency.STRONG) { - readKeysByCache2BeforeFailOver.forEach(x -> assertThat(CACHE2.get(x), nullValue())); + readKeysByCache2BeforeFailOver.forEach(x -> assertThat(cache2.get(x), nullValue())); } else { - readKeysByCache2BeforeFailOver.forEach(x -> assertThat(CACHE1.get(x), nullValue())); + readKeysByCache2BeforeFailOver.forEach(x -> assertThat(cache1.get(x), nullValue())); } } diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/replication/BasicClusteredCacheOpsReplicationWithServersApiTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/replication/BasicClusteredCacheOpsReplicationWithServersApiTest.java index d2d1889d3c..bc4167a89e 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/replication/BasicClusteredCacheOpsReplicationWithServersApiTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/replication/BasicClusteredCacheOpsReplicationWithServersApiTest.java @@ -18,7 +18,6 @@ import org.ehcache.Cache; import org.ehcache.PersistentCacheManager; -import org.ehcache.clustered.ClusteredTests; import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder; import org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder; import org.ehcache.clustered.client.config.builders.TimeoutsBuilder; @@ -34,37 +33,33 @@ import org.junit.Test; import org.terracotta.testing.rules.Cluster; -import java.io.File; import java.net.InetSocketAddress; import java.time.Duration; import java.util.ArrayList; import java.util.List; +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.ehcache.testing.StandardCluster.offheapResource; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; -import static org.terracotta.testing.rules.BasicExternalClusterBuilder.newCluster; -public class BasicClusteredCacheOpsReplicationWithServersApiTest extends ClusteredTests { - private static final String CONFIG = - "" - + "" - + "16" - + "" + - "\n"; + +public class BasicClusteredCacheOpsReplicationWithServersApiTest { private static PersistentCacheManager CACHE_MANAGER; private static Cache CACHE1; private static Cache CACHE2; @ClassRule - public static Cluster CLUSTER = newCluster(2).in(new File("build/cluster")).withServiceFragment(CONFIG).build(); + public static Cluster CLUSTER = newCluster(2).in(clusterPath()) + .withServerHeap(512) + .withServiceFragment(offheapResource("primary-server-resource", 16)).build(); @Before public void setUp() throws Exception { CLUSTER.getClusterControl().startAllServers(); - CLUSTER.getClusterControl().waitForActive(); - CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); final CacheManagerBuilder clusteredCacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder() @@ -72,8 +67,7 @@ public void setUp() throws Exception { .timeouts(TimeoutsBuilder.timeouts() // we need to give some time for the failover to occur .read(Duration.ofMinutes(1)) .write(Duration.ofMinutes(1))) - .autoCreate() - .defaultServerResource("primary-server-resource")); + .autoCreate(server -> server.defaultServerResource("primary-server-resource"))); CACHE_MANAGER = clusteredCacheManagerBuilder.build(true); CacheConfiguration config = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder().heap(100, EntryUnit.ENTRIES) @@ -117,6 +111,7 @@ public void testCRUD() throws Exception { x.remove(4L); }); + CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); CLUSTER.getClusterControl().terminateActive(); caches.forEach(x -> { diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/replication/BasicLifeCyclePassiveReplicationTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/replication/BasicLifeCyclePassiveReplicationTest.java index a2ed0c85ea..71f0150aaf 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/replication/BasicLifeCyclePassiveReplicationTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/replication/BasicLifeCyclePassiveReplicationTest.java @@ -17,16 +17,16 @@ package org.ehcache.clustered.replication; import org.ehcache.PersistentCacheManager; -import org.ehcache.clustered.ClusteredTests; import org.ehcache.clustered.client.internal.lock.VoltronReadWriteLock; +import org.ehcache.clustered.util.ParallelTestCluster; +import org.ehcache.clustered.util.runners.Parallel; import org.ehcache.config.builders.CacheManagerBuilder; import org.junit.After; import org.junit.Before; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; -import org.terracotta.testing.rules.Cluster; - -import java.io.File; +import org.junit.runner.RunWith; import static org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder.clusteredDedicated; import static org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder.cluster; @@ -34,40 +34,31 @@ import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder; import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; import static org.ehcache.config.units.MemoryUnit.MB; +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.ehcache.testing.StandardCluster.offheapResource; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; -import static org.terracotta.testing.rules.BasicExternalClusterBuilder.newCluster; -public class BasicLifeCyclePassiveReplicationTest extends ClusteredTests { - private static final String RESOURCE_CONFIG = - "" - + "" - + "16" - + "" + - "\n"; +@RunWith(Parallel.class) +public class BasicLifeCyclePassiveReplicationTest { - @ClassRule - public static Cluster CLUSTER = - newCluster(2).in(new File("build/cluster")).withServiceFragment(RESOURCE_CONFIG).build(); + @ClassRule @Rule + public static final ParallelTestCluster CLUSTER = new ParallelTestCluster(newCluster(2).in(clusterPath()) + .withServerHeap(512) + .withServiceFragment(offheapResource("primary-server-resource", 16)).build()); @Before public void startServers() throws Exception { CLUSTER.getClusterControl().startAllServers(); - CLUSTER.getClusterControl().waitForActive(); - CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); - } - - @After - public void tearDown() throws Exception { - CLUSTER.getClusterControl().terminateActive(); } @Test public void testDestroyCacheManager() throws Exception { CacheManagerBuilder configBuilder = newCacheManagerBuilder().with(cluster(CLUSTER.getConnectionURI().resolve("/destroy-CM")) - .autoCreate().defaultServerResource("primary-server-resource")); + .autoCreate(server -> server.defaultServerResource("primary-server-resource"))); PersistentCacheManager cacheManager1 = configBuilder.build(true); PersistentCacheManager cacheManager2 = configBuilder.build(true); @@ -80,8 +71,8 @@ public void testDestroyCacheManager() throws Exception { e.printStackTrace(); } + CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); CLUSTER.getClusterControl().terminateActive(); - CLUSTER.getClusterControl().waitForActive(); cacheManager1.createCache("test", newCacheConfigurationBuilder(Long.class, String.class, heap(10).with(clusteredDedicated(10, MB)))); } @@ -94,8 +85,8 @@ public void testDestroyLockEntity() throws Exception { VoltronReadWriteLock lock2 = new VoltronReadWriteLock(CLUSTER.newConnection(), "my-lock"); assertThat(lock2.tryWriteLock(), nullValue()); + CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); CLUSTER.getClusterControl().terminateActive(); - CLUSTER.getClusterControl().waitForActive(); hold1.unlock(); } diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/replication/DuplicateTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/replication/DuplicateTest.java index dfd16ecab9..913a8434b5 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/replication/DuplicateTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/replication/DuplicateTest.java @@ -17,7 +17,6 @@ import org.ehcache.Cache; import org.ehcache.PersistentCacheManager; -import org.ehcache.clustered.ClusteredTests; import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder; import org.ehcache.clustered.client.config.builders.ClusteredStoreConfigurationBuilder; import org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder; @@ -35,40 +34,37 @@ import org.junit.Test; import org.terracotta.testing.rules.Cluster; -import java.io.File; import java.lang.reflect.Proxy; import java.time.Duration; import java.util.Arrays; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.Semaphore; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; -import static org.terracotta.testing.rules.BasicExternalClusterBuilder.newCluster; +import static java.util.concurrent.TimeUnit.MINUTES; +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.ehcache.testing.StandardCluster.offheapResource; +import static org.ehcache.testing.StandardTimeouts.eventually; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assume.assumeThat; -public class DuplicateTest extends ClusteredTests { - - private static final String RESOURCE_CONFIG = - "" - + "" - + "512" - + "" + - "\n"; +public class DuplicateTest { private PersistentCacheManager cacheManager; @ClassRule public static Cluster CLUSTER = - newCluster(2).in(new File("build/cluster")).withServiceFragment(RESOURCE_CONFIG).build(); + newCluster(2).in(clusterPath()) + .withServerHeap(512) + .withServiceFragment(offheapResource("primary-server-resource", 512)).build(); @Before public void startServers() throws Exception { CLUSTER.getClusterControl().startAllServers(); - CLUSTER.getClusterControl().waitForActive(); - CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); } @After @@ -83,45 +79,56 @@ public void tearDown() throws Exception { public void duplicateAfterFailoverAreReturningTheCorrectResponse() throws Exception { CacheManagerBuilder builder = CacheManagerBuilder.newCacheManagerBuilder() .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER.getConnectionURI()) - .timeouts(TimeoutsBuilder.timeouts().write(Duration.ofSeconds(30))) - .autoCreate() - .defaultServerResource("primary-server-resource")) + .timeouts(TimeoutsBuilder.timeouts().write(Duration.ofSeconds(60))) + .autoCreate(server -> server.defaultServerResource("primary-server-resource"))) .withCache("cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Integer.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 10, MemoryUnit.MB))) .withResilienceStrategy(failingResilienceStrategy()) - .add(ClusteredStoreConfigurationBuilder.withConsistency(Consistency.STRONG))); + .withService(ClusteredStoreConfigurationBuilder.withConsistency(Consistency.STRONG))); cacheManager = builder.build(true); Cache cache = cacheManager.getCache("cache", Integer.class, String.class); int numEntries = 3000; - AtomicInteger currentEntry = new AtomicInteger(); //Perform put operations in another thread ExecutorService executorService = Executors.newSingleThreadExecutor(); try { + Semaphore failoverAllowed = new Semaphore(0); + Semaphore failoverComplete = new Semaphore(0); Future puts = executorService.submit(() -> { - while (true) { - int i = currentEntry.getAndIncrement(); - if (i >= numEntries) { - break; + try { + for (int i = 0; i < numEntries; i++) { + if (i == 100) { + failoverAllowed.release(); + } + if (i == (numEntries - 100)) { + failoverComplete.acquire(); + } + cache.put(i, "value:" + i); } - cache.put(i, "value:" + i); + } catch (InterruptedException e) { + throw new AssertionError(e); } }); - while (currentEntry.get() < 100); // wait to make sure some entries are added before shutdown - // Failover to mirror when put & replication are in progress + CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); + assertThat(failoverAllowed.tryAcquire(1, MINUTES), is(true)); CLUSTER.getClusterControl().terminateActive(); + failoverComplete.release(); + + assertThat(puts::isDone, eventually().is(true)); + puts.get(); - puts.get(30, TimeUnit.SECONDS); + //if failover didn't interrupt puts then the test is 'moot' + assumeThat(cache.get(0), is(notNullValue())); //Verify cache entries on mirror for (int i = 0; i < numEntries; i++) { - assertThat(cache.get(i)).isEqualTo("value:" + i); + assertThat(cache.get(i), is("value:" + i)); } } finally { executorService.shutdownNow(); @@ -130,14 +137,13 @@ public void duplicateAfterFailoverAreReturningTheCorrectResponse() throws Except } @SuppressWarnings("unchecked") - private ResilienceStrategy failingResilienceStrategy() throws Exception { + private ResilienceStrategy failingResilienceStrategy() { return (ResilienceStrategy) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] { ResilienceStrategy.class}, (proxy, method, args) -> { if(method.getName().endsWith("Failure")) { - fail("Failure on " + method.getName(), findStoreAccessException(args)); // one param is always a SAE - return null; + throw new AssertionError("Failure on " + method.getName(), findStoreAccessException(args)); // one param is always a SAE } switch(method.getName()) { @@ -146,8 +152,7 @@ private ResilienceStrategy failingResilienceStrategy() throws E case "equals": return proxy == args[0]; default: - fail("Unexpected method call: " + method.getName()); - return null; + throw new AssertionError("Unexpected method call: " + method.getName()); } }); } @@ -158,7 +163,6 @@ private StoreAccessException findStoreAccessException(Object[] objects) { return (StoreAccessException) o; } } - fail("There should be an exception somewhere in " + Arrays.toString(objects)); - return null; + throw new AssertionError("There should be an exception somewhere in " + Arrays.toString(objects)); } } diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/replication/OversizedCacheOpsPassiveTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/replication/OversizedCacheOpsPassiveTest.java index 68f1b7639e..f051d34388 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/replication/OversizedCacheOpsPassiveTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/replication/OversizedCacheOpsPassiveTest.java @@ -18,7 +18,6 @@ import org.ehcache.Cache; import org.ehcache.PersistentCacheManager; -import org.ehcache.clustered.ClusteredTests; import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder; import org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder; import org.ehcache.config.CacheConfiguration; @@ -26,55 +25,45 @@ import org.ehcache.config.builders.CacheManagerBuilder; import org.ehcache.config.builders.ResourcePoolsBuilder; import org.ehcache.config.units.MemoryUnit; -import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; import org.terracotta.testing.rules.Cluster; -import java.nio.file.Paths; import java.util.Arrays; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; +import org.junit.Ignore; + +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.ehcache.testing.StandardCluster.offheapResource; -import static org.terracotta.testing.rules.BasicExternalClusterBuilder.newCluster; /** * Test the effect of cache eviction during passive sync. */ -public class OversizedCacheOpsPassiveTest extends ClusteredTests { +@Ignore("OOME on build slaves due to high memory requirements") +public class OversizedCacheOpsPassiveTest { private static final int MAX_PUTS = 3000; private static final int MAX_SWITCH_OVER = 3; private static final int PER_ELEMENT_SIZE = 256 * 1024; private static final int CACHE_SIZE_IN_MB = 2; private static final String LARGE_VALUE = buildLargeString(); - private static final String RESOURCE_CONFIG = - "" - + "" - + "2" - + "" + - "\n"; - @ClassRule public static Cluster CLUSTER = - newCluster(2).in(Paths.get("build", "cluster").toFile()) + newCluster(2).in(clusterPath()) .withSystemProperty("ehcache.sync.data.gets.threshold", "2") - .withServiceFragment(RESOURCE_CONFIG) + .withServiceFragment(offheapResource("primary-server-resource", 2)) + .withServerHeap(2048) .build(); - @BeforeClass - public static void waitForServers() throws Exception { - CLUSTER.getClusterControl().waitForActive(); - CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); - } - @Test public void oversizedPuts() throws Exception { CacheManagerBuilder clusteredCacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder() .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER.getConnectionURI().resolve("/crud-cm")) - .autoCreate() - .defaultServerResource("primary-server-resource")); + .autoCreate(server -> server.defaultServerResource("primary-server-resource"))); CountDownLatch syncLatch = new CountDownLatch(2); CompletableFuture f1 = CompletableFuture.runAsync(() -> doPuts(clusteredCacheManagerBuilder, syncLatch)); @@ -82,10 +71,10 @@ public void oversizedPuts() throws Exception { syncLatch.await(); for (int i = 0; i < MAX_SWITCH_OVER; i++) { + CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); CLUSTER.getClusterControl().terminateActive(); CLUSTER.getClusterControl().waitForActive(); CLUSTER.getClusterControl().startOneServer(); - CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); Thread.sleep(2000); } @@ -99,7 +88,7 @@ private void doPuts(CacheManagerBuilder clusteredCacheMa CacheConfiguration config = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", CACHE_SIZE_IN_MB, MemoryUnit.MB))) - .build(); + .build(); syncLatch.countDown(); Cache cache = cacheManager.createCache("clustered-cache", config); @@ -108,7 +97,8 @@ private void doPuts(CacheManagerBuilder clusteredCacheMa // a small pause try { Thread.sleep(10); - } catch (InterruptedException ignored) { + } catch (InterruptedException e) { + throw new AssertionError(e); } } cache.put(i, LARGE_VALUE); diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/sync/PassiveSyncTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/sync/PassiveSyncTest.java index 6f27031f6d..54b3f1635a 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/sync/PassiveSyncTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/sync/PassiveSyncTest.java @@ -18,7 +18,6 @@ import org.ehcache.Cache; import org.ehcache.PersistentCacheManager; -import org.ehcache.clustered.ClusteredTests; import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder; import org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder; import org.ehcache.config.CacheConfiguration; @@ -32,45 +31,37 @@ import org.junit.Test; import org.terracotta.testing.rules.Cluster; -import java.io.File; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; -import static com.google.code.tempusfugit.temporal.Duration.seconds; -import static com.google.code.tempusfugit.temporal.Timeout.timeout; -import static com.google.code.tempusfugit.temporal.WaitFor.waitOrTimeout; +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.ehcache.testing.StandardCluster.offheapResource; +import static org.ehcache.testing.StandardTimeouts.eventually; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; -import static org.junit.Assert.assertThat; -import static org.terracotta.testing.rules.BasicExternalClusterBuilder.newCluster; +import static org.hamcrest.Matchers.notNullValue; -public class PassiveSyncTest extends ClusteredTests { - private static final String RESOURCE_CONFIG = - "" - + "" - + "16" - + "" + - "\n"; + +public class PassiveSyncTest { @ClassRule - public static Cluster CLUSTER = - newCluster(2).in(new File("build/cluster")).withServiceFragment(RESOURCE_CONFIG).build(); + public static Cluster CLUSTER = newCluster(2).in(clusterPath()) + .withServiceFragment(offheapResource("primary-server-resource", 16)) + .withServerHeap(512) + .build(); @Before public void startServers() throws Exception { - CLUSTER.getClusterControl().startAllServers(); - CLUSTER.getClusterControl().waitForActive(); - CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); + CLUSTER.getClusterControl().terminateOnePassive(); } @Test(timeout = 150000) public void testSync() throws Exception { - CLUSTER.getClusterControl().terminateOnePassive(); - final CacheManagerBuilder clusteredCacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder() .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER.getConnectionURI().resolve("/op-sync")) - .autoCreate() - .defaultServerResource("primary-server-resource")); + .autoCreate(server -> server.defaultServerResource("primary-server-resource"))); final PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(false); cacheManager.init(); @@ -88,12 +79,10 @@ public void testSync() throws Exception { CLUSTER.getClusterControl().startOneServer(); CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); CLUSTER.getClusterControl().terminateActive(); - CLUSTER.getClusterControl().waitForActive(); - // Sometimes the new passive believes there is a second connection and we have to wait for the full reconnect window before getting a result - waitOrTimeout(() -> "value-5".equals(cache.get(-5L)), timeout(seconds(130))); - for (long i = -4; i < 5; i++) { + assertThat(() -> cache.get(0L), eventually().matches(notNullValue())); + for (long i = -5; i < 5; i++) { assertThat(cache.get(i), equalTo("value" + i)); } } finally { @@ -104,13 +93,10 @@ public void testSync() throws Exception { @Ignore @Test public void testLifeCycleOperationsOnSync() throws Exception { - CLUSTER.getClusterControl().terminateOnePassive(); - final CacheManagerBuilder clusteredCacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder() .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER.getConnectionURI().resolve("/lifecycle-sync")) - .autoCreate() - .defaultServerResource("primary-server-resource")); + .autoCreate(server -> server.defaultServerResource("primary-server-resource"))); try (PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(true)) { CacheConfiguration config = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/util/ParallelTestCluster.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/util/ParallelTestCluster.java new file mode 100644 index 0000000000..6fb37df56a --- /dev/null +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/util/ParallelTestCluster.java @@ -0,0 +1,182 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.clustered.util; + +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; +import org.terracotta.connection.Connection; +import org.terracotta.connection.ConnectionException; +import org.terracotta.passthrough.IClusterControl; +import org.terracotta.testing.rules.Cluster; + +import java.net.URI; +import java.util.concurrent.Phaser; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; + +public class ParallelTestCluster implements TestRule { + + private final Cluster cluster; + private final IClusterControl control; + private final AtomicReference nextTask = new AtomicReference<>(); + + private final Phaser membership = new Phaser() { + @Override + protected boolean onAdvance(int phase, int registeredParties) { + activeCycle.bulkRegister(registeredParties); + return false; + } + }; + private final Phaser activeCycle = new Phaser() { + @Override + protected boolean onAdvance(int phase, int registeredParties) { + return false; + } + }; + + public ParallelTestCluster(Cluster cluster) { + this.cluster = cluster; + + IClusterControl underlyingControl = cluster.getClusterControl(); + this.control = new IClusterControl() { + @Override + public void waitForActive() throws Exception { + underlyingControl.waitForActive(); + } + + @Override + public void waitForRunningPassivesInStandby() throws Exception { + underlyingControl.waitForRunningPassivesInStandby(); + } + + @Override + public void startOneServer() { + request(ClusterTask.START_ONE_SERVER); + } + + @Override + public void startAllServers() { + request(ClusterTask.START_ALL_SERVERS); + } + + @Override + public void terminateActive() { + request(ClusterTask.TERMINATE_ACTIVE); + } + + @Override + public void terminateOnePassive() { + request(ClusterTask.TERMINATE_ONE_PASSIVE); + } + + @Override + public void terminateAllServers() { + request(ClusterTask.TERMINATE_ALL_SERVERS); + } + + private void request(ClusterTask task) { + try { + if (nextTask.compareAndSet(null, task)) { + activeCycle.awaitAdvanceInterruptibly(activeCycle.arrive()); + nextTask.getAndSet(null).accept(underlyingControl); + activeCycle.awaitAdvanceInterruptibly(activeCycle.arrive()); + } else { + ClusterTask requestedTask = nextTask.get(); + if (requestedTask.equals(task)) { + activeCycle.awaitAdvanceInterruptibly(activeCycle.arrive()); + activeCycle.awaitAdvanceInterruptibly(activeCycle.arrive()); + } else { + throw new AssertionError("Existing requested task is " + requestedTask); + } + } + } catch (InterruptedException e) { + throw new AssertionError(e); + } + } + }; + } + + public URI getConnectionURI() { + return cluster.getConnectionURI(); + } + + public String[] getClusterHostPorts() { + return cluster.getClusterHostPorts(); + } + + public Connection newConnection() throws ConnectionException { + return cluster.newConnection(); + } + + public IClusterControl getClusterControl() { + return control; + } + + @Override + public Statement apply(Statement base, Description description) { + if (description.isSuite()) { + return cluster.apply(base, description); + } else if (description.isTest()) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + membership.register(); + Thread.sleep(100); + membership.awaitAdvanceInterruptibly(membership.arrive()); + try { + activeCycle.awaitAdvanceInterruptibly(activeCycle.arrive()); + try { + base.evaluate(); + } finally { + activeCycle.arriveAndDeregister(); + } + } finally { + membership.arriveAndDeregister(); + } + } + }; + } else { + return base; + } + } + + enum ClusterTask implements Consumer { + START_ONE_SERVER(IClusterControl::startOneServer), + START_ALL_SERVERS(IClusterControl::startAllServers), + TERMINATE_ACTIVE(IClusterControl::terminateActive), + TERMINATE_ONE_PASSIVE(IClusterControl::terminateOnePassive), + TERMINATE_ALL_SERVERS(IClusterControl::terminateAllServers); + + private final Task task; + + ClusterTask(Task task) { + this.task = task; + } + + public void accept(IClusterControl control) { + try { + task.run(control); + } catch (Exception e) { + throw new AssertionError(e); + } + } + + interface Task { + void run(IClusterControl control) throws Exception; + } + } +} diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/util/TCPProxyManager.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/util/TCPProxyManager.java new file mode 100644 index 0000000000..66bdea9571 --- /dev/null +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/util/TCPProxyManager.java @@ -0,0 +1,117 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.clustered.util; + +import com.tc.net.proxy.TCPProxy; + +import org.terracotta.utilities.test.net.PortManager; + +import java.net.InetAddress; +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import static java.util.Collections.unmodifiableList; + +/** + * Manages creation and use of {@link TCPProxy} instances for a collection of Terracotta servers. + */ +public class TCPProxyManager implements AutoCloseable { + private static final String STRIPE_SEPARATOR = ","; + + private final List proxyPorts; + private final List proxies; + + private TCPProxyManager(List proxyPorts, List proxies) { + this.proxyPorts = unmodifiableList(proxyPorts); + this.proxies = unmodifiableList(proxies); + } + + /** + * Creates a new {@code TCPProxyManager} instance holding {@link TCPProxy} instances for + * the endpoints represented in the specified connection URI. + *

+ * This method creates {@code TCPProxy} instances for servers on {@code localhost}. + *

+ * A reference to the returned {@code TCPProxyManager} must be maintained for the duration + * of the use of the {@code TCPProxy} instances contained therein -- allowing the + * {@code TCPProxyManager} to become weakly-referenced while the proxies are in + * active use could result in random connection-related failures. + * + * @param connectionURI the {@code terracotta} connection URI + * @return a new {@code TCPProxyManager} instance with a {@code TCPProxy} for each endpoint + * @throws Exception if + */ + public static TCPProxyManager create(URI connectionURI) throws Exception { + List primaryPorts = parsePorts(connectionURI); + List proxyPorts = PortManager.getInstance().reservePorts(primaryPorts.size()); + + List proxies = new ArrayList<>(primaryPorts.size()); + InetAddress host = InetAddress.getByName("localhost"); + try { + for (int i = 0; i < primaryPorts.size(); i++) { + TCPProxy proxy = new TCPProxy(proxyPorts.get(i).port(), host, primaryPorts.get(i), 0L, false, null); + proxies.add(proxy); + proxy.start(); + } + } catch (Exception e) { + proxyPorts.forEach(PortManager.PortRef::close); + throw e; + } + + return new TCPProxyManager(proxyPorts, proxies); + } + + /** + * Returns the URI to use for the proxy connection to the Terracotta cluster. + * @return the URI for connection to the Terracotta cluster via the allocated {@link TCPProxy} instances + */ + public URI getURI() { + String uri = proxyPorts.stream() + .map(portRef -> "localhost:" + portRef.port()) + .collect(Collectors.joining(STRIPE_SEPARATOR, "terracotta://", "")); + + return URI.create(uri); + } + + /** + * Sets the delay for each allocated {@link TCPProxy} instance. + * @param delay the non-negative delay + */ + public void setDelay(long delay) { + proxies.forEach(p -> p.setDelay(delay)); + } + + /** + * Stops each allocated {@link TCPProxy} instance and releases the allocated proxy ports. + */ + @Override + public void close() { + proxies.forEach(TCPProxy::stop); + proxyPorts.forEach(PortManager.PortRef::close); + } + + private static List parsePorts(URI connectionURI) { + String withoutProtocol = connectionURI.toString().substring(13); + return Arrays.stream(withoutProtocol.split(STRIPE_SEPARATOR)) + .map(stripe -> stripe.substring(stripe.indexOf(":") + 1)) + .mapToInt(Integer::parseInt) + .boxed() + .collect(Collectors.toList()); + } +} diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/util/TCPProxyUtil.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/util/TCPProxyUtil.java deleted file mode 100644 index 8ba910117f..0000000000 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/util/TCPProxyUtil.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ehcache.clustered.util; - -import com.tc.net.proxy.TCPProxy; -import org.terracotta.testing.common.PortChooser; - -import java.net.InetAddress; -import java.net.URI; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -public final class TCPProxyUtil { - - private static final String STRIPE_SEPARATOR = ","; - - private TCPProxyUtil() { - - } - - public static URI getProxyURI(URI connectionURI, List proxies) throws Exception { - - List ports = parsePorts(connectionURI); - List proxyPorts = createProxyPorts(ports.size()); - - for (int i = 0; i < ports.size(); i++) { - int port = ports.get(i); - int proxyPort = proxyPorts.get(i); - - InetAddress host = InetAddress.getByName("localhost"); - TCPProxy proxy = new TCPProxy(proxyPort, host, port, 0L, false, null); - proxies.add(proxy); - proxy.start(); - } - - return createURI(proxyPorts); - } - - private static List parsePorts(URI connectionURI) { - String uriString = connectionURI.toString(); - String withoutProtocol = uriString.substring(13); - String[] stripes = withoutProtocol.split(STRIPE_SEPARATOR); - - return Arrays.stream(stripes) - .map(stripe -> stripe.substring(stripe.indexOf(":") + 1)) - .mapToInt(Integer::parseInt) - .boxed() - .collect(Collectors.toList()); - } - - private static List createProxyPorts(int portCount) { - PortChooser portChooser = new PortChooser(); - int firstProxyPort = portChooser.chooseRandomPorts(portCount); - - return IntStream - .range(0, portCount) - .map(i -> firstProxyPort + i) - .boxed() - .collect(Collectors.toList()); - } - - private static URI createURI(List proxyPorts) { - - String uri = proxyPorts.stream() - .map(port -> "localhost:" + port) - .collect(Collectors.joining(",", "terracotta://", "")); - - return URI.create(uri); - } - - public static void setDelay(long delay, List proxies) { - for (TCPProxy proxy : proxies) { - proxy.setDelay(delay); - } - } -} diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/util/runners/ExecutorScheduler.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/util/runners/ExecutorScheduler.java new file mode 100644 index 0000000000..6f16641557 --- /dev/null +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/util/runners/ExecutorScheduler.java @@ -0,0 +1,62 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.clustered.util.runners; + +import org.junit.runners.model.RunnerScheduler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; + +public class ExecutorScheduler implements RunnerScheduler { + + private static final Logger LOGGER = LoggerFactory.getLogger(ExecutorScheduler.class); + + public final Supplier executorSupplier; + public final AtomicReference executor = new AtomicReference<>(); + + public ExecutorScheduler(Supplier executorSupplier) { + this.executorSupplier = executorSupplier; + } + + @Override + public void schedule(Runnable childStatement) { + ExecutorService executorService; + while ((executorService = executor.get()) == null && !executor.compareAndSet(null, (executorService = executorSupplier.get()))) { + executorService.shutdown(); + } + executorService.execute(childStatement); + } + + @Override + public void finished() { + ExecutorService departing = executor.getAndSet(null); + departing.shutdown(); + try { + if (!departing.awaitTermination(1, TimeUnit.DAYS)) { + throw new AssertionError(new TimeoutException()); + } + } catch (InterruptedException e) { + List runnables = departing.shutdownNow(); + LOGGER.warn("Forcibly terminating execution of scheduled test tasks due to interrupt (" + runnables.size() + " tasks remain unscheduled)"); + } + } +} diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/util/runners/Parallel.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/util/runners/Parallel.java new file mode 100644 index 0000000000..2876eeaffd --- /dev/null +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/util/runners/Parallel.java @@ -0,0 +1,29 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.clustered.util.runners; + +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.model.InitializationError; + +import static java.util.concurrent.Executors.newCachedThreadPool; + +public class Parallel extends BlockJUnit4ClassRunner { + + public Parallel(Class klass) throws InitializationError { + super(klass); + setScheduler(new ExecutorScheduler(() -> newCachedThreadPool(r -> new Thread(r, "TestRunner-Thread-" + klass)))); + } +} diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/util/runners/ParallelParameterized.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/util/runners/ParallelParameterized.java new file mode 100644 index 0000000000..3c64e2ba32 --- /dev/null +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/util/runners/ParallelParameterized.java @@ -0,0 +1,34 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.clustered.util.runners; + +import org.junit.runners.Parameterized; +import org.junit.runners.ParentRunner; + +import static java.util.concurrent.Executors.newCachedThreadPool; + +public class ParallelParameterized extends Parameterized { + + public ParallelParameterized(Class klass) throws Throwable { + super(klass); + setScheduler(new ExecutorScheduler(() -> newCachedThreadPool(r -> new Thread(r, "TestRunner-Thread-" + klass)))); + getChildren().forEach(child -> { + if (child instanceof ParentRunner) { + ((ParentRunner) child).setScheduler(new ExecutorScheduler(() -> newCachedThreadPool(r -> new Thread(r, "TestRunner-Thread-" + r.toString())))); + } + }); + } +} diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/writebehind/BasicClusteredWriteBehindMultiClientTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/writebehind/BasicClusteredWriteBehindMultiClientTest.java new file mode 100644 index 0000000000..7f30482369 --- /dev/null +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/writebehind/BasicClusteredWriteBehindMultiClientTest.java @@ -0,0 +1,87 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.clustered.writebehind; + +import org.ehcache.Cache; +import org.ehcache.PersistentCacheManager; +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.terracotta.testing.rules.Cluster; + +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.newCluster; + + +public class BasicClusteredWriteBehindMultiClientTest extends WriteBehindTestBase { + + @ClassRule + public static Cluster CLUSTER = + newCluster().in(clusterPath()).withServiceFragment(RESOURCE_CONFIG).build(); + + private PersistentCacheManager cacheManager1; + private PersistentCacheManager cacheManager2; + + private Cache client1; + private Cache client2; + + @Before + public void setUp() throws Exception { + super.setUp(); + + CLUSTER.getClusterControl().startAllServers(); + + cacheManager1 = createCacheManager(CLUSTER.getConnectionURI()); + cacheManager2 = createCacheManager(CLUSTER.getConnectionURI()); + + client1 = cacheManager1.getCache(testName.getMethodName(), Long.class, String.class); + client2 = cacheManager2.getCache(testName.getMethodName(), Long.class, String.class); + } + + @After + public void tearDown() throws Exception { + if (cacheManager1 != null) { + cacheManager1.close(); + } + + if (cacheManager2 != null) { + cacheManager2.close(); + cacheManager2.destroy(); + } + } + + @Test + public void testWriteBehindMultipleClients() throws Exception { + client1.put(KEY, "The one from client1"); + client2.put(KEY, "The one from client2"); + assertValue(client1, "The one from client2"); + client1.remove(KEY); + client2.put(KEY, "The one from client2"); + client1.put(KEY, "The one from client1"); + assertValue(client2, "The one from client1"); + client2.remove(KEY); + assertValue(client1, null); + client1.put(KEY, "The one from client1"); + client1.put(KEY, "The one one from client1"); + client2.remove(KEY); + client2.put(KEY, "The one from client2"); + client2.put(KEY, "The one one from client2"); + + checkValueFromLoaderWriter(client1, "The one one from client2"); + } +} diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/writebehind/BasicClusteredWriteBehindTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/writebehind/BasicClusteredWriteBehindTest.java index 6e47b882a5..69d8d7234b 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/writebehind/BasicClusteredWriteBehindTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/writebehind/BasicClusteredWriteBehindTest.java @@ -17,265 +17,114 @@ package org.ehcache.clustered.writebehind; import org.ehcache.Cache; -import org.ehcache.CacheManager; import org.ehcache.PersistentCacheManager; -import org.ehcache.clustered.ClusteredTests; -import org.ehcache.clustered.client.config.ClusteredStoreConfiguration; -import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder; -import org.ehcache.clustered.common.Consistency; -import org.ehcache.config.CacheConfiguration; -import org.ehcache.config.builders.CacheManagerBuilder; -import org.ehcache.config.builders.ResourcePoolsBuilder; -import org.ehcache.config.builders.WriteBehindConfigurationBuilder; -import org.ehcache.config.units.EntryUnit; -import org.ehcache.config.units.MemoryUnit; +import org.junit.After; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.Timeout; import org.terracotta.testing.rules.Cluster; -import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.util.Arrays; +import java.util.concurrent.TimeUnit; -import static org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder.cluster; -import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; -import static org.hamcrest.Matchers.is; +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.newCluster; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.notNullValue; -import static org.junit.Assert.assertThat; -import static org.terracotta.testing.rules.BasicExternalClusterBuilder.newCluster; -public class BasicClusteredWriteBehindTest extends ClusteredTests { - private static final String RESOURCE_CONFIG = - "" - + "" - + "64" - + "" + - "\n"; +public class BasicClusteredWriteBehindTest extends WriteBehindTestBase { + + @Rule + public Timeout timeout = new Timeout(1, TimeUnit.MINUTES); + + private boolean doThreadDump = true; @ClassRule public static Cluster CLUSTER = - newCluster().in(new File("build/cluster")).withServiceFragment(RESOURCE_CONFIG).build(); - - @BeforeClass - public static void waitForActive() throws Exception { - CLUSTER.getClusterControl().waitForActive(); - } + newCluster().in(clusterPath()).withServiceFragment(RESOURCE_CONFIG).build(); - private final List cacheRecords = new ArrayList<>(); + private PersistentCacheManager cacheManager; + private Cache cache; - private static final String CACHE_NAME = "cache-1"; - private static final long KEY = 1L; + @Before + public void setUp() throws Exception { + super.setUp(); - private RecordingLoaderWriter loaderWriter; + CLUSTER.getClusterControl().startAllServers(); - @Before - public void setUp() { - loaderWriter = new RecordingLoaderWriter<>(); + cacheManager = createCacheManager(CLUSTER.getConnectionURI()); + cache = cacheManager.getCache(testName.getMethodName(), Long.class, String.class); } - @Test - public void testBasicClusteredWriteBehind() { - PersistentCacheManager cacheManager = createCacheManager(); - Cache cache = cacheManager.getCache(CACHE_NAME, Long.class, String.class); - - for (int i = 0; i < 10; i++) { - put(cache, String.valueOf(i)); + @After + public void tearDown() throws Exception { + if (doThreadDump) { + System.out.println("Performing thread dump"); + ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); + ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(true, true); + Arrays.stream(threadInfos).forEach(System.out::println); } - assertValue(cache, String.valueOf(9)); - - verifyRecords(cache); - cache.clear(); + if (cacheManager != null) { + cacheManager.close(); + cacheManager.destroy(); + } } @Test - public void testWriteBehindMultipleClients() { - PersistentCacheManager cacheManager1 = createCacheManager(); - PersistentCacheManager cacheManager2 = createCacheManager(); - Cache client1 = cacheManager1.getCache(CACHE_NAME, Long.class, String.class); - Cache client2 = cacheManager2.getCache(CACHE_NAME, Long.class, String.class); + public void testBasicClusteredWriteBehind() throws Exception { + for (int i = 0; i < 10; i++) { + cache.put(KEY, String.valueOf(i)); + } - put(client1, "The one from client1"); - put(client2, "The one one from client2"); - assertValue(client1, "The one one from client2"); - remove(client1); - put(client2, "The one from client2"); - put(client1, "The one one from client1"); - assertValue(client2, "The one one from client1"); - remove(client2); - assertValue(client1, null); - put(client1, "The one from client1"); - put(client1, "The one one from client1"); - remove(client2); - put(client2, "The one from client2"); - put(client2, "The one one from client2"); - remove(client1); - assertValue(client2, null); + assertValue(cache, "9"); - verifyRecords(client1); - client1.clear(); + checkValueFromLoaderWriter(cache, "9"); + + doThreadDump = false; } @Test - public void testClusteredWriteBehindCAS() { - PersistentCacheManager cacheManager = createCacheManager(); - Cache cache = cacheManager.getCache(CACHE_NAME, Long.class, String.class); - putIfAbsent(cache, "First value", true); + public void testClusteredWriteBehindCAS() throws Exception { + cache.putIfAbsent(KEY, "First value"); assertValue(cache,"First value"); - putIfAbsent(cache, "Second value", false); + cache.putIfAbsent(KEY, "Second value"); assertValue(cache, "First value"); - put(cache, "First value again"); + cache.put(KEY, "First value again"); assertValue(cache, "First value again"); - replace(cache, "Replaced First value", true); + cache.replace(KEY, "Replaced First value"); assertValue(cache, "Replaced First value"); - replace(cache, "Replaced First value", "Replaced First value again", true); + cache.replace(KEY, "Replaced First value", "Replaced First value again"); assertValue(cache, "Replaced First value again"); - replace(cache, "Replaced First", "Tried Replacing First value again", false); + cache.replace(KEY, "Replaced First", "Tried Replacing First value again"); assertValue(cache, "Replaced First value again"); - condRemove(cache, "Replaced First value again", true); + cache.remove(KEY, "Replaced First value again"); assertValue(cache, null); - replace(cache, "Trying to replace value", false); + cache.replace(KEY, "Trying to replace value"); assertValue(cache, null); - put(cache, "new value", true); + cache.put(KEY, "new value"); assertValue(cache, "new value"); - condRemove(cache, "new value", false); + cache.remove(KEY); - verifyRecords(cache); - cache.clear(); + checkValueFromLoaderWriter(cache, null); + + doThreadDump = false; } @Test - public void testClusteredWriteBehindLoading() { - CacheManager cacheManager = createCacheManager(); - Cache cache = cacheManager.getCache(CACHE_NAME, Long.class, String.class); - - put(cache,"Some value"); - tryFlushingUpdatesToSOR(cache); + public void testClusteredWriteBehindLoading() throws Exception { + cache.put(KEY, "Some value"); + checkValueFromLoaderWriter(cache, "Some value"); cache.clear(); assertThat(cache.get(KEY), notNullValue()); - cache.clear(); - } - - private void assertValue(Cache cache, String value) { - assertThat(cache.get(KEY), is(value)); - } - - private void put(Cache cache, String value) { - put(cache, value, true); - } - - private void put(Cache cache, String value, boolean addToCacheRecords) { - cache.put(KEY, value); - if (addToCacheRecords) { - cacheRecords.add(new Record(KEY, cache.get(KEY))); - } - } - - private void putIfAbsent(Cache cache, String value, boolean addToCacheRecords) { - cache.putIfAbsent(KEY, value); - if (addToCacheRecords) { - cacheRecords.add(new Record(KEY, cache.get(KEY))); - } - } - - private void replace(Cache cache, String value, boolean addToCacheRecords) { - cache.replace(KEY, value); - if (addToCacheRecords) { - cacheRecords.add(new Record(KEY, cache.get(KEY))); - } - } - - private void replace(Cache cache, String oldValue, String newValue, boolean addToCacheRecords) { - cache.replace(KEY, oldValue, newValue); - if (addToCacheRecords) { - cacheRecords.add(new Record(KEY, cache.get(KEY))); - } - } - - private void remove(Cache cache) { - cache.remove(KEY); - cacheRecords.add(new Record(KEY, null)); - } - - private void condRemove(Cache cache, String value, boolean addToCacheRecords) { - cache.remove(KEY, value); - if (addToCacheRecords) { - cacheRecords.add(new Record(KEY, null)); - } - } - - private void verifyRecords(Cache cache) { - tryFlushingUpdatesToSOR(cache); - - Map> loaderWriterRecords = loaderWriter.getRecords(); - - Map track = new HashMap<>(); - for (Record cacheRecord : cacheRecords) { - Long key = cacheRecord.getKey(); - int next = track.compute(key, (k, v) -> v == null ? 0 : v + 1); - assertThat(loaderWriterRecords.get(key).get(next), is(cacheRecord.getValue())); - } - } - - private void tryFlushingUpdatesToSOR(Cache cache) { - int retryCount = 1000; - int i = 0; - while (true) { - String value = "flush_queue_" + i; - put(cache, value, false); - try { - Thread.sleep(100); - } catch (InterruptedException e) { - e.printStackTrace(); - } - if (value.equals(loaderWriter.load(KEY))) break; - if (i > retryCount) { - throw new RuntimeException("Couldn't flush updates to SOR after " + retryCount + " tries"); - } - i++; - } - } - - private PersistentCacheManager createCacheManager() { - CacheConfiguration cacheConfiguration = - newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() - .heap(10, EntryUnit.ENTRIES) - .offheap(1, MemoryUnit.MB) - .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 2, MemoryUnit.MB))) - .withLoaderWriter(loaderWriter) - .add(WriteBehindConfigurationBuilder.newUnBatchedWriteBehindConfiguration()) - .add(new ClusteredStoreConfiguration(Consistency.STRONG)) - .build(); - - return CacheManagerBuilder - .newCacheManagerBuilder() - .with(cluster(CLUSTER.getConnectionURI().resolve("/cm-wb")).autoCreate()) - .withCache(CACHE_NAME, cacheConfiguration) - .build(true); - } - - private static final class Record { - private final Long key; - private final String value; - - private Record(Long key, String value) { - this.key = key; - this.value = value; - } - - Long getKey() { - return key; - } - - String getValue() { - return value; - } + doThreadDump = false; } } diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/writebehind/BasicClusteredWriteBehindWithPassiveMultiClientTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/writebehind/BasicClusteredWriteBehindWithPassiveMultiClientTest.java new file mode 100644 index 0000000000..0f17ec636b --- /dev/null +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/writebehind/BasicClusteredWriteBehindWithPassiveMultiClientTest.java @@ -0,0 +1,96 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.clustered.writebehind; + +import org.ehcache.Cache; +import org.ehcache.PersistentCacheManager; +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.terracotta.testing.rules.Cluster; + +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.newCluster; + + +public class BasicClusteredWriteBehindWithPassiveMultiClientTest extends WriteBehindTestBase { + + @ClassRule + public static Cluster CLUSTER = + newCluster(2).in(clusterPath()).withServiceFragment(RESOURCE_CONFIG).build(); + + private PersistentCacheManager cacheManager1; + private PersistentCacheManager cacheManager2; + + private Cache client1; + private Cache client2; + + @Before + public void setUp() throws Exception { + super.setUp(); + + CLUSTER.getClusterControl().startAllServers(); + + cacheManager1 = createCacheManager(CLUSTER.getConnectionURI()); + cacheManager2 = createCacheManager(CLUSTER.getConnectionURI()); + + client1 = cacheManager1.getCache(testName.getMethodName(), Long.class, String.class); + client2 = cacheManager2.getCache(testName.getMethodName(), Long.class, String.class); + } + + @After + public void tearDown() throws Exception { + if (cacheManager1 != null) { + cacheManager1.close(); + } + + if (cacheManager2 != null) { + cacheManager2.close(); + cacheManager2.destroy(); + } + } + + @Test + public void testWriteBehindMultipleClients() throws Exception { + client1.put(KEY, "The one from client1"); + client2.put(KEY, "The one from client2"); + assertValue(client1, "The one from client2"); + client1.remove(KEY); + client2.put(KEY, "The one from client2"); + client1.put(KEY, "The one from client1"); + assertValue(client2, "The one from client1"); + client2.remove(KEY); + assertValue(client1, null); + client1.put(KEY, "The one from client1"); + client1.put(KEY, "The one one from client1"); + assertValue(client2, "The one one from client1"); + client2.remove(KEY); + assertValue(client1, null); + client2.put(KEY, "The one from client2"); + client2.put(KEY, "The one one from client2"); + assertValue(client1, "The one one from client2"); + + CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); + CLUSTER.getClusterControl().terminateActive(); + + assertValue(client1, "The one one from client2"); + assertValue(client2, "The one one from client2"); + + checkValueFromLoaderWriter(client1, "The one one from client2"); + } +} diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/writebehind/BasicClusteredWriteBehindWithPassiveTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/writebehind/BasicClusteredWriteBehindWithPassiveTest.java index 8dceaca796..d7a12d4bd0 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/writebehind/BasicClusteredWriteBehindWithPassiveTest.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/writebehind/BasicClusteredWriteBehindWithPassiveTest.java @@ -18,137 +18,64 @@ import org.ehcache.Cache; import org.ehcache.PersistentCacheManager; -import org.ehcache.clustered.ClusteredTests; -import org.ehcache.clustered.client.config.ClusteredStoreConfiguration; -import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder; -import org.ehcache.clustered.common.Consistency; -import org.ehcache.config.CacheConfiguration; -import org.ehcache.config.builders.CacheManagerBuilder; -import org.ehcache.config.builders.ResourcePoolsBuilder; -import org.ehcache.config.builders.WriteBehindConfigurationBuilder; -import org.ehcache.config.units.EntryUnit; -import org.ehcache.config.units.MemoryUnit; +import org.ehcache.clustered.util.ParallelTestCluster; +import org.ehcache.clustered.util.runners.Parallel; +import org.junit.After; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; -import org.terracotta.testing.rules.Cluster; - -import java.io.File; -import java.util.List; -import java.util.Map; - -import static org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder.cluster; -import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; -import static org.terracotta.testing.rules.BasicExternalClusterBuilder.newCluster; - -public class BasicClusteredWriteBehindWithPassiveTest extends ClusteredTests { - - private static final String RESOURCE_CONFIG = - "" - + "" - + "64" - + "" + - "\n"; - - @ClassRule - public static Cluster CLUSTER = - newCluster(2).in(new File("build/cluster")).withServiceFragment(RESOURCE_CONFIG).build(); - - @BeforeClass - public static void waitForActive() throws Exception { - CLUSTER.getClusterControl().waitForActive(); - CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); - } +import org.junit.runner.RunWith; + +import static org.ehcache.testing.StandardCluster.clusterPath; +import static org.ehcache.testing.StandardCluster.newCluster; + + +@RunWith(Parallel.class) +public class BasicClusteredWriteBehindWithPassiveTest extends WriteBehindTestBase { - private static final String CACHE_NAME = "cache-1"; - private static final long KEY = 1L; + @ClassRule @Rule + public static final ParallelTestCluster CLUSTER = new ParallelTestCluster( + newCluster(2).in(clusterPath()).withServiceFragment(RESOURCE_CONFIG).build() + ); - private RecordingLoaderWriter loaderWriter; + private PersistentCacheManager cacheManager; + private Cache cache; @Before - public void setUp() { - loaderWriter = new RecordingLoaderWriter<>(); + public void setUp() throws Exception { + super.setUp(); + + CLUSTER.getClusterControl().startAllServers(); + + cacheManager = createCacheManager(CLUSTER.getConnectionURI()); + cache = cacheManager.getCache(testName.getMethodName(), Long.class, String.class); + } + + @After + public void tearDown() throws Exception { + if (cacheManager != null) { + cacheManager.close(); + } } @Test public void testBasicClusteredWriteBehind() throws Exception { - PersistentCacheManager cacheManager = createCacheManager(); - Cache cache = cacheManager.getCache(CACHE_NAME, Long.class, String.class); - for (int i = 0; i < 10; i++) { cache.put(KEY, String.valueOf(i)); } - assertValue(cache, String.valueOf(9)); + assertValue(cache, "9"); + CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); CLUSTER.getClusterControl().terminateActive(); - CLUSTER.getClusterControl().waitForActive(); - CLUSTER.getClusterControl().startOneServer(); - assertValue(cache, String.valueOf(9)); + assertValue(cache, "9"); checkValueFromLoaderWriter(cache, String.valueOf(9)); - - cache.clear(); - } - - @Test - public void testWriteBehindMultipleClients() throws Exception { - PersistentCacheManager cacheManager1 = createCacheManager(); - PersistentCacheManager cacheManager2 = createCacheManager(); - Cache client1 = cacheManager1.getCache(CACHE_NAME, Long.class, String.class); - Cache client2 = cacheManager2.getCache(CACHE_NAME, Long.class, String.class); - - client1.put(KEY, "The one from client1"); - client2.put(KEY, "The one one from client2"); - assertValue(client1, "The one one from client2"); - client1.remove(KEY); - client2.put(KEY, "The one from client2"); - client1.put(KEY, "The one one from client1"); - assertValue(client2, "The one one from client1"); - client2.remove(KEY); - assertValue(client1, null); - client1.put(KEY, "The one from client1"); - client1.put(KEY, "The one one from client1"); - client2.remove(KEY); - client2.put(KEY, "The one from client2"); - client2.put(KEY, "The one one from client2"); - client1.remove(KEY); - assertValue(client2, null); - - CLUSTER.getClusterControl().terminateActive(); - CLUSTER.getClusterControl().waitForActive(); - CLUSTER.getClusterControl().startOneServer(); - - assertValue(client1, null); - assertValue(client2, null); - checkValueFromLoaderWriter(client1, null); - - client1.clear(); - } - - private void checkValueFromLoaderWriter(Cache cache, String expected) { - - tryFlushingUpdatesToSOR(cache); - - Map> records = loaderWriter.getRecords(); - List keyRecords = records.get(KEY); - - int index = keyRecords.size() - 1; - while (index >= 0 && keyRecords.get(index) != null && keyRecords.get(index).startsWith("flush_")) { - index--; - } - - assertThat(keyRecords.get(index), is(expected)); - } @Test public void testClusteredWriteBehindCAS() throws Exception { - PersistentCacheManager cacheManager = createCacheManager(); - Cache cache = cacheManager.getCache(CACHE_NAME, Long.class, String.class); cache.putIfAbsent(KEY, "First value"); assertValue(cache,"First value"); cache.putIfAbsent(KEY, "Second value"); @@ -168,51 +95,10 @@ public void testClusteredWriteBehindCAS() throws Exception { cache.put(KEY, "new value"); assertValue(cache, "new value"); + CLUSTER.getClusterControl().waitForRunningPassivesInStandby(); CLUSTER.getClusterControl().terminateActive(); - CLUSTER.getClusterControl().waitForActive(); - CLUSTER.getClusterControl().startOneServer(); - - cache.clear(); - } - - private void assertValue(Cache cache, String value) { - assertThat(cache.get(KEY), is(value)); - } - private void tryFlushingUpdatesToSOR(Cache cache) { - int retryCount = 1000; - int i = 0; - while (true) { - String value = "flush_queue_" + i; - cache.put(KEY, value); - try { - Thread.sleep(100); - } catch (InterruptedException e) { - e.printStackTrace(); - } - if (value.equals(loaderWriter.load(KEY))) break; - if (i > retryCount) { - throw new RuntimeException("Couldn't flush updates to SOR after " + retryCount + " tries"); - } - i++; - } - } - - private PersistentCacheManager createCacheManager() { - CacheConfiguration cacheConfiguration = - newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() - .heap(10, EntryUnit.ENTRIES) - .offheap(1, MemoryUnit.MB) - .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 2, MemoryUnit.MB))) - .withLoaderWriter(loaderWriter) - .add(WriteBehindConfigurationBuilder.newUnBatchedWriteBehindConfiguration()) - .add(new ClusteredStoreConfiguration(Consistency.STRONG)) - .build(); - - return CacheManagerBuilder - .newCacheManagerBuilder() - .with(cluster(CLUSTER.getConnectionURI().resolve("/cm-wb")).autoCreate()) - .withCache(CACHE_NAME, cacheConfiguration) - .build(true); + assertValue(cache, "new value"); + checkValueFromLoaderWriter(cache,"new value"); } } diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/writebehind/RecordingLoaderWriter.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/writebehind/RecordingLoaderWriter.java index e37557687c..c24a9736f6 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/writebehind/RecordingLoaderWriter.java +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/writebehind/RecordingLoaderWriter.java @@ -62,11 +62,7 @@ private void record(K key, V value) { records.computeIfAbsent(key, k -> new ArrayList<>()).add(value); } - public synchronized Map> getRecords() { + synchronized Map> getRecords() { return Collections.unmodifiableMap(records); } - - public void clear() { - records.clear(); - } } diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/writebehind/WriteBehindTestBase.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/writebehind/WriteBehindTestBase.java new file mode 100644 index 0000000000..9546a6bf96 --- /dev/null +++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/writebehind/WriteBehindTestBase.java @@ -0,0 +1,115 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.clustered.writebehind; + +import org.ehcache.Cache; +import org.ehcache.PersistentCacheManager; +import org.ehcache.clustered.client.config.ClusteredStoreConfiguration; +import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder; +import org.ehcache.clustered.client.config.builders.TimeoutsBuilder; +import org.ehcache.clustered.common.Consistency; +import org.ehcache.config.CacheConfiguration; +import org.ehcache.config.builders.CacheManagerBuilder; +import org.ehcache.config.builders.ResourcePoolsBuilder; +import org.ehcache.config.builders.WriteBehindConfigurationBuilder; +import org.ehcache.config.units.EntryUnit; +import org.ehcache.config.units.MemoryUnit; +import org.ehcache.core.internal.resilience.ThrowingResilienceStrategy; +import org.junit.Before; +import org.junit.Rule; +import org.junit.rules.TestName; + +import java.net.URI; +import java.time.Duration; +import java.util.List; +import java.util.Map; + +import static org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder.cluster; +import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; +import static org.ehcache.testing.StandardCluster.offheapResource; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +public class WriteBehindTestBase { + + static final String RESOURCE_CONFIG = offheapResource("primary-server-resource", 64); + + static final long KEY = 1L; + + private static final String FLUSH_QUEUE_MARKER = "FLUSH_QUEUE"; + + @Rule + public final TestName testName = new TestName(); + + private RecordingLoaderWriter loaderWriter; + + @Before + public void setUp() throws Exception { + loaderWriter = new RecordingLoaderWriter<>(); + } + + void checkValueFromLoaderWriter(Cache cache, + String expected) throws Exception { + tryFlushingUpdatesToSOR(cache); + + Map> records = loaderWriter.getRecords(); + List keyRecords = records.get(KEY); + + int index = keyRecords.size() - 1; + while (index >= 0 && keyRecords.get(index) != null && keyRecords.get(index).equals(FLUSH_QUEUE_MARKER)) { + index--; + } + + assertThat(keyRecords.get(index), is(expected)); + } + + private void tryFlushingUpdatesToSOR(Cache cache) throws Exception { + int retryCount = 1000; + while (retryCount-- != 0) { + cache.put(KEY, FLUSH_QUEUE_MARKER); + Thread.sleep(100); + String loadedValue = loaderWriter.load(KEY); + if (loadedValue != null && loadedValue.equals(FLUSH_QUEUE_MARKER)) { + return; + } + } + throw new AssertionError("Couldn't flush updates to SOR after " + retryCount + " tries"); + } + + void assertValue(Cache cache, String value) { + assertThat(cache.get(KEY), is(value)); + } + + PersistentCacheManager createCacheManager(URI clusterUri) { + CacheConfiguration cacheConfiguration = + newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() + .heap(10, EntryUnit.ENTRIES) + .offheap(1, MemoryUnit.MB) + .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 2, MemoryUnit.MB))) + .withLoaderWriter(loaderWriter) + .withService(WriteBehindConfigurationBuilder.newUnBatchedWriteBehindConfiguration()) + .withResilienceStrategy(new ThrowingResilienceStrategy<>()) + .withService(new ClusteredStoreConfiguration(Consistency.STRONG)) + .build(); + + return CacheManagerBuilder + .newCacheManagerBuilder() + .with(cluster(clusterUri.resolve("/cm-wb")).timeouts(TimeoutsBuilder.timeouts().read(Duration.ofMinutes(1)).write(Duration.ofMinutes(1))).autoCreate(c -> c)) + .withCache(testName.getMethodName(), cacheConfiguration) + .build(true); + } +} diff --git a/clustered/integration-test/src/test/java/org/ehcache/testing/ExternalTests.java b/clustered/integration-test/src/test/java/org/ehcache/testing/ExternalTests.java new file mode 100644 index 0000000000..593a907158 --- /dev/null +++ b/clustered/integration-test/src/test/java/org/ehcache/testing/ExternalTests.java @@ -0,0 +1,191 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.testing; + +import org.junit.runner.Description; +import org.junit.runner.Request; +import org.junit.runner.manipulation.Filter; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.ParentRunner; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.TestClass; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.Modifier; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; +import java.util.stream.Collectors; + +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; + +public class ExternalTests extends ParentRunner { + + private final List children; + + public ExternalTests(Class testClass) throws InitializationError, IOException, ClassNotFoundException { + super(testClass); + this.children = singletonList(parseRequest(getTestClass(), parseFilter(getTestClass()))); + } + + @Override + protected List getChildren() { + return children; + } + + @Override + protected Description describeChild(Request child) { + return child.getRunner().getDescription(); + } + + @Override + protected void runChild(Request child, RunNotifier notifier) { + child.getRunner().run(notifier); + } + + private static Filter parseFilter(TestClass testClass) { + return groupAnnotations(testClass, Ignore.class, Ignores.class).stream().map(IgnoreFilter::ignore).reduce(Filter.ALL, Filter::intersect); + } + + private static class IgnoreFilter extends Filter { + + private final Ignore ignore; + + public static Filter ignore(Ignore ignore) { + return new IgnoreFilter(ignore); + } + + private IgnoreFilter(Ignore ignore) { + this.ignore = ignore; + } + + @Override + public boolean shouldRun(Description description) { + if (ignore.value().equals(description.getTestClass())) { + if (ignore.method().isEmpty()) { + return false; + } else { + return !ignore.method().equals(description.getMethodName()); + } + } else { + return true; + } + } + + @Override + public String describe() { + if (ignore.method().isEmpty()) { + return "Ignore " + ignore.value(); + } else { + return "Ignore " + ignore.value() + "#" + ignore.method(); + } + } + } + + private static Request parseRequest(TestClass testClass, Filter filter) throws IOException, ClassNotFoundException { + List froms = groupAnnotations(testClass, From.class, Froms.class); + List tests = groupAnnotations(testClass, Test.class, Tests.class); + + List> classes = new ArrayList<>(); + + for (From from : froms) { + URL location = from.value().getProtectionDomain().getCodeSource().getLocation(); + try (InputStream is = location.openStream(); JarInputStream jis = new JarInputStream(is)) { + while (true) { + JarEntry entry = jis.getNextJarEntry(); + if (entry == null) { + break; + } else if (entry.getName().endsWith("Test.class")) { + classes.add(Class.forName(entry.getName().replace(".class", "").replace('/', '.'))); + } + } + } + } + for (Test test : tests) { + classes.add(test.value()); + } + + return Request.classes(classes.stream() + .filter(c -> Modifier.isPublic(c.getModifiers()) && !Modifier.isAbstract(c.getModifiers())) + .filter(c -> !c.getSimpleName().startsWith("Abstract")).toArray(Class[]::new)) + .filterWith(filter); + + } + + @SuppressWarnings("unchecked") + private static List groupAnnotations(TestClass testClass, Class annoType, Class wrapperType) { + try { + List annotations = new ArrayList<>(); + + WT testsAnn = testClass.getAnnotation(wrapperType); + if (testsAnn != null) { + annotations.addAll(asList((T[]) wrapperType.getMethod("value").invoke(testsAnn))); + } + + T singularAnn = testClass.getAnnotation(annoType); + if (singularAnn != null) { + annotations.add(singularAnn); + } + return annotations; + } catch (ReflectiveOperationException e) { + throw new IllegalArgumentException(e); + } + } + + + @Retention(RetentionPolicy.RUNTIME) + @Repeatable(Tests.class) + public @interface Test { + + Class value(); + } + @Retention(RetentionPolicy.RUNTIME) + public @interface Tests { + Test[] value(); + } + + @Retention(RetentionPolicy.RUNTIME) + @Repeatable(Froms.class) + public @interface From { + + Class value(); + } + @Retention(RetentionPolicy.RUNTIME) + public @interface Froms { + From[] value(); + } + + @Retention(RetentionPolicy.RUNTIME) + @Repeatable(Ignores.class) + public @interface Ignore { + Class value(); + + String method() default ""; + } + + @Retention(RetentionPolicy.RUNTIME) + public @interface Ignores { + Ignore[] value(); + } +} diff --git a/clustered/integration-test/src/test/java/org/ehcache/testing/StandardCluster.java b/clustered/integration-test/src/test/java/org/ehcache/testing/StandardCluster.java new file mode 100644 index 0000000000..199c2691ac --- /dev/null +++ b/clustered/integration-test/src/test/java/org/ehcache/testing/StandardCluster.java @@ -0,0 +1,64 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.testing; + +import org.terracotta.testing.config.ConfigRepoStartupBuilder; +import org.terracotta.testing.rules.BasicExternalClusterBuilder; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Duration; +import java.util.Map; + +import static java.time.temporal.ChronoUnit.SECONDS; +import static java.util.Collections.singletonMap; + +public interface StandardCluster { + static Path clusterPath() { + return Paths.get("build", "cluster"); + } + + static String offheapResource(String name, long size) { + return offheapResources(singletonMap(name, size)); + } + + static String offheapResources(Map resources) { + StringBuilder sb = new StringBuilder(""); + sb.append(""); + for (Map.Entry e : resources.entrySet()) { + sb.append("").append(e.getValue()).append(""); + } + sb.append(""); + return sb.append("\n").toString(); + } + + static BasicExternalClusterBuilder newCluster() { + return BasicExternalClusterBuilder.newCluster().startupBuilder(ConfigRepoStartupBuilder::new); + } + + static BasicExternalClusterBuilder newCluster(int size) { + return BasicExternalClusterBuilder.newCluster(size).startupBuilder(ConfigRepoStartupBuilder::new); + } + + static String leaseLength(Duration leaseLength) { + return "" + + "" + + "" + leaseLength.get(SECONDS) + "" + + "" + + ""; + } + +} diff --git a/clustered/common/build.gradle b/clustered/integration-test/src/test/java/org/ehcache/testing/StandardTimeouts.java similarity index 70% rename from clustered/common/build.gradle rename to clustered/integration-test/src/test/java/org/ehcache/testing/StandardTimeouts.java index 7698facc9a..1ac947b4d5 100644 --- a/clustered/common/build.gradle +++ b/clustered/integration-test/src/test/java/org/ehcache/testing/StandardTimeouts.java @@ -13,11 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package org.ehcache.testing; -apply plugin: EhDeploy +import org.terracotta.utilities.test.matchers.Eventually; -dependencies { - providedImplementation project(':api') - providedImplementation "org.terracotta:entity-common-api:$terracottaApisVersion" - providedImplementation "org.terracotta:runnel:$terracottaPlatformVersion" +import java.time.Duration; + +public interface StandardTimeouts { + + static Eventually.Timeout eventually() { + return Eventually.within(Duration.ofMinutes(1L)); + } } diff --git a/clustered/integration-test/src/test/resources/ExcludeList b/clustered/integration-test/src/test/resources/ExcludeList new file mode 100644 index 0000000000..40f1074143 --- /dev/null +++ b/clustered/integration-test/src/test/resources/ExcludeList @@ -0,0 +1,119 @@ +# This is a dummy test that fails if not in the exclude list. +org.jsr107.tck.CachingTest#dummyTest + +# Hard assertions on the default URI +org.jsr107.tck.CachingTest#getCacheManager_defaultURI +org.jsr107.tck.CachingTest#getCacheManager_nullUriParameter +org.jsr107.tck.CachingTest#getCacheManager_URI +org.jsr107.tck.spi.CachingProviderClassLoaderTest#getCacheManagerSameURI +org.jsr107.tck.spi.CachingProviderClassLoaderTest#getCacheManagerDefaultURI +org.jsr107.tck.spi.CachingProviderTest#getCacheManagerUsingDefaultURI + +# Assumes store-by-reference semantics +org.jsr107.tck.StoreByReferenceTest#get_Existing +org.jsr107.tck.StoreByReferenceTest#get_Existing_NotSameKey +org.jsr107.tck.StoreByReferenceTest#put_Existing_NotSameKey +org.jsr107.tck.StoreByReferenceTest#getAndPut_NotThere +org.jsr107.tck.StoreByReferenceTest#getAndPut_Existing +org.jsr107.tck.StoreByReferenceTest#getAndPut_Existing_NotSameKey +org.jsr107.tck.StoreByReferenceTest#putAll +org.jsr107.tck.StoreByReferenceTest#putIfAbsent_Missing +org.jsr107.tck.StoreByReferenceTest#putIfAbsent_There +org.jsr107.tck.StoreByReferenceTest#replace_3arg +org.jsr107.tck.StoreByReferenceTest#getAndReplace +org.jsr107.tck.TypesTest#simpleAPINoGenericsAndNoTypeEnforcementStoreByReference + +# Assumes inline expiry policy calls +org.jsr107.tck.management.CacheMBStatisticsBeanTest#testExpiryOnCreation +org.jsr107.tck.expiry.CacheExpiryTest#putShouldCallGetExpiry +org.jsr107.tck.expiry.CacheExpiryTest#getAndReplaceShouldCallGetExpiryForModifiedEntry +org.jsr107.tck.expiry.CacheExpiryTest#containsKeyShouldNotCallExpiryPolicyMethods +org.jsr107.tck.expiry.CacheExpiryTest#removeEntryShouldNotCallExpiryPolicyMethods +org.jsr107.tck.expiry.CacheExpiryTest#putIfAbsentShouldCallGetExpiry +org.jsr107.tck.expiry.CacheExpiryTest#replaceSpecificShouldCallGetExpiry +org.jsr107.tck.expiry.CacheExpiryTest#getAllShouldCallGetExpiryForAccessedEntry +org.jsr107.tck.expiry.CacheExpiryTest#removeSpecifiedEntryShouldNotCallExpiryPolicyMethods +org.jsr107.tck.expiry.CacheExpiryTest#replaceShouldCallGetExpiryForModifiedEntry +org.jsr107.tck.expiry.CacheExpiryTest#putAllShouldCallGetExpiry +org.jsr107.tck.expiry.CacheExpiryTest#invokeAllSetValueShouldCallGetExpiry +org.jsr107.tck.expiry.CacheExpiryTest#iteratorNextShouldCallGetExpiryForAccessedEntry +org.jsr107.tck.expiry.CacheExpiryTest#getShouldCallGetExpiryForAccessedEntry +org.jsr107.tck.expiry.CacheExpiryTest#invokeAllReadThroughEnabledGetOnNonExistentEntry +org.jsr107.tck.expiry.CacheExpiryTest#expire_whenCreated_ModifiedExpiryPolicy +org.jsr107.tck.expiry.CacheExpiryTest#expire_whenCreated_ParameterizedExpiryPolicy +org.jsr107.tck.expiry.CacheExpiryTest#expire_whenCreated_TouchedExpiryPolicy +org.jsr107.tck.expiry.CacheExpiryTest#expire_whenCreated_CreatedExpiryPolicy +org.jsr107.tck.expiry.CacheExpiryTest#expire_whenCreated_AccessedExpiryPolicy +org.jsr107.tck.expiry.CacheExpiryTest#getAndRemoveShouldNotCallExpiryPolicyMethods +org.jsr107.tck.expiry.CacheExpiryTest#getAndPutShouldCallEitherCreatedOrModifiedExpiryPolicy + +# Clustered cache TTI is busted(-ish) +org.jsr107.tck.integration.CacheLoaderWithExpiryTest#shouldLoadWhenMissCausedByExpiry +org.jsr107.tck.expiry.CacheExpiryTest#expire_whenAccessed + +# Implement org.ehcache.clustered.client.internal.store.ClusteredStore.computeAndGet +org.jsr107.tck.event.CacheListenerTest#testBrokenCacheEntryListener +org.jsr107.tck.event.CacheListenerTest#testCacheEntryListener +org.jsr107.tck.management.CacheMBStatisticsBeanTest#testCacheStatisticsInvokeEntryProcessorRemove +org.jsr107.tck.management.CacheMBStatisticsBeanTest#testCacheStatisticsInvokeEntryProcessorUpdate +org.jsr107.tck.management.CacheMBStatisticsBeanTest#testCacheStatisticsInvokeEntryProcessorGet +org.jsr107.tck.management.CacheMBStatisticsBeanTest#testCacheStatisticsInvokeEntryProcessorNoOp +org.jsr107.tck.processor.CacheInvokeTest#removeMissing +org.jsr107.tck.processor.CacheInvokeTest#noValueException +org.jsr107.tck.processor.CacheInvokeTest#testProcessorEmptyExceptionIsWrapped +org.jsr107.tck.processor.CacheInvokeTest#varArgumentsPassedIn +org.jsr107.tck.processor.CacheInvokeTest#removeException +org.jsr107.tck.processor.CacheInvokeTest#noValueSetValue +org.jsr107.tck.processor.CacheInvokeTest#noValueNoMutation +org.jsr107.tck.processor.CacheInvokeTest#testProcessorExceptionIsWrapped +org.jsr107.tck.processor.CacheInvokeTest#setValueToNull +org.jsr107.tck.processor.CacheInvokeTest#removeExisting +org.jsr107.tck.processor.CacheInvokeTest#existingException +org.jsr107.tck.processor.CacheInvokeTest#invokeAllgetResultFromMap +org.jsr107.tck.processor.CacheInvokeTest#existingReplace +org.jsr107.tck.processor.CacheInvokeTest#nullGetValue +org.jsr107.tck.expiry.CacheExpiryTest#invokeSetValueShouldCallGetExpiry +org.jsr107.tck.expiry.CacheExpiryTest#invokeGetValueShouldCallGetExpiry +org.jsr107.tck.expiry.CacheExpiryTest#invokeMultiSetValueShouldCallGetExpiry +org.jsr107.tck.expiry.CacheExpiryTest#invokeGetValueWithReadThroughForNonExistentEntryShouldCallGetExpiryForCreatedEntry +org.jsr107.tck.integration.CacheWriterTest#shouldWriteThroughUsingInvoke_setValue_CreateEntryThenRemove +org.jsr107.tck.integration.CacheWriterTest#shouldWriteThroughUsingInvoke_remove +org.jsr107.tck.integration.CacheWriterTest#shouldWriteThroughUsingInvoke_setValue_CreateEntryGetValue +org.jsr107.tck.integration.CacheWriterTest#shouldWriteThroughUsingInvoke_setValue_UpdateEntry +org.jsr107.tck.integration.CacheWriterTest#shouldWriteThroughUsingInvoke_remove_createEntry +org.jsr107.tck.integration.CacheWriterTest#shouldWriteThroughUsingInvoke_setValue_CreateEntry +org.jsr107.tck.integration.CacheWriterTest#shouldWriteThroughUsingInvoke_remove_nonExistingEntry +org.jsr107.tck.integration.CacheLoaderWithoutReadThroughTest#shouldLoadWhenAccessingWithEntryProcessor +org.jsr107.tck.integration.CacheLoaderTest#shouldLoadWhenAccessingWithEntryProcessor +org.jsr107.tck.integration.CacheLoaderWriterTest#shouldLoadWhenAccessingWithEntryProcessor +org.jsr107.tck.processor.CacheInvokeTest#invokeAllEntryProcessorReturnsNullResult +org.jsr107.tck.integration.CacheWriterTest#shouldWriteThroughUsingInvokeAll_setValue_UpdateEntry +org.jsr107.tck.integration.CacheWriterTest#shouldWriteThroughUsingInvokeAll_setValue_CreateEntry +org.jsr107.tck.integration.CacheWriterTest#shouldWriteThroughUsingInvokeAll_setValue_RemoveEntry + +# Implement org.ehcache.clustered.client.internal.store.ClusteredStore.bulkComputeIfAbsent +org.jsr107.tck.integration.CacheLoaderTest#shouldNotLoadMultipleNullEntriesUsingLoadAll +org.jsr107.tck.integration.CacheLoaderTest#shouldNotLoadMultipleNullValuesUsingLoadAll +org.jsr107.tck.integration.CacheLoaderWithoutReadThroughTest#shouldLoadMultipleNonExistingEntryUsingLoadAll +org.jsr107.tck.integration.CacheLoaderWithoutReadThroughTest#shouldNotLoadMultipleNullEntriesUsingLoadAll +org.jsr107.tck.integration.CacheLoaderWithoutReadThroughTest#shouldNotLoadMultipleNullValuesUsingLoadAll +org.jsr107.tck.integration.CacheLoaderWithoutReadThroughTest#shouldLoadSingleMissingEntryUsingLoadAll +org.jsr107.tck.integration.CacheLoaderTest#shouldLoadMultipleNonExistingEntryUsingLoadAll +org.jsr107.tck.integration.CacheLoaderTest#shouldLoadSingleMissingEntryUsingLoadAll +org.jsr107.tck.integration.CacheLoaderWriterTest#shouldLoadSingleMissingEntryUsingLoadAll +org.jsr107.tck.integration.CacheLoaderWriterTest#shouldLoadMultipleNonExistingEntryUsingLoadAll +org.jsr107.tck.expiry.CacheExpiryTest#loadAllWithReadThroughEnabledShouldCallGetExpiryForCreatedEntry + +# Implement org.ehcache.clustered.client.internal.store.ClusteredStore.bulkCompute +org.jsr107.tck.integration.CacheLoaderWithoutReadThroughTest#shouldLoadSingleExistingEntryUsingLoadAll +org.jsr107.tck.integration.CacheLoaderWithoutReadThroughTest#shouldLoadMultipleExistingEntryUsingLoadAll +org.jsr107.tck.integration.CacheLoaderTest#shouldLoadSingleExistingEntryUsingLoadAll +org.jsr107.tck.integration.CacheLoaderWriterTest#shouldNotWriteThroughUsingLoadAll +org.jsr107.tck.integration.CacheLoaderTest#shouldLoadMultipleExistingEntryUsingLoadAll +org.jsr107.tck.integration.CacheLoaderWriterTest#shouldLoadMultipleExistingEntryUsingLoadAll +org.jsr107.tck.integration.CacheLoaderWriterTest#shouldLoadSingleExistingEntryUsingLoadAll + +# Clustered removeAll/putAll incorrectly terminates early when hitting an exception on a key +org.jsr107.tck.integration.CacheWriterTest#shouldWriteThroughRemoveAllSpecific_partialSuccess +org.jsr107.tck.integration.CacheWriterTest#shouldWriteThroughRemoveAll_partialSuccess +org.jsr107.tck.integration.CacheWriterTest#shouldWriteThoughUsingPutAll_partialSuccess diff --git a/clustered/integration-test/src/test/resources/clusteredConfiguration.txt b/clustered/integration-test/src/test/resources/clusteredConfiguration.txt index b72691f038..4e742d8a1c 100644 --- a/clustered/integration-test/src/test/resources/clusteredConfiguration.txt +++ b/clustered/integration-test/src/test/resources/clusteredConfiguration.txt @@ -4,7 +4,7 @@ caches: valueType: java.lang.String serviceConfigurations: None evictionAdvisor: None - expiry: NoExpiryPolicy + expiry: No Expiry resourcePools: pools: heap: @@ -18,7 +18,7 @@ caches: valueType: java.lang.String serviceConfigurations: None evictionAdvisor: None - expiry: NoExpiryPolicy + expiry: No Expiry resourcePools: pools: heap: @@ -34,7 +34,7 @@ services: - org.ehcache.clustered.client.config.ClusteringServiceConfiguration: clusterUri: terracotta://server-1/my-server-entity-2 timeouts: Timeouts{readOperation=PT5S,writeOperation=PT5S,connection=PT2M30S} - autoCreate: true + clientMode: AUTO_CREATE defaultServerResource: primary-server-resource resourcePools: resource-pool-a: [10485760 bytes from ''] diff --git a/clustered/integration-test/src/test/resources/configs/clustered.xml b/clustered/integration-test/src/test/resources/configs/clustered.xml index 007d0ac885..e1e0a2a12a 100644 --- a/clustered/integration-test/src/test/resources/configs/clustered.xml +++ b/clustered/integration-test/src/test/resources/configs/clustered.xml @@ -11,8 +11,8 @@ - + - \ No newline at end of file + diff --git a/clustered/integration-test/src/test/resources/configs/jcache-clustered.xml b/clustered/integration-test/src/test/resources/configs/jcache-clustered.xml index 11061512bc..8e47f844a6 100644 --- a/clustered/integration-test/src/test/resources/configs/jcache-clustered.xml +++ b/clustered/integration-test/src/test/resources/configs/jcache-clustered.xml @@ -15,27 +15,26 @@ ~ limitations under the License. --> - -] -> + xmlns:tc="http://www.ehcache.org/v3/clustered" + xmlns:jsr107="http://www.ehcache.org/v3/jsr107"> - - + + - + + + + + - 10 - 10 + 1000 + 4 - - - \ No newline at end of file + + diff --git a/clustered/integration-test/src/test/resources/configs/offheap-resource.xml b/clustered/integration-test/src/test/resources/configs/offheap-resource.xml index 2b2ab18a3c..fab9f0273e 100644 --- a/clustered/integration-test/src/test/resources/configs/offheap-resource.xml +++ b/clustered/integration-test/src/test/resources/configs/offheap-resource.xml @@ -17,7 +17,6 @@ --> 64 diff --git a/clustered/integration-test/src/test/resources/tc-logback.xml b/clustered/integration-test/src/test/resources/tc-logback.xml new file mode 100644 index 0000000000..4208262b28 --- /dev/null +++ b/clustered/integration-test/src/test/resources/tc-logback.xml @@ -0,0 +1,32 @@ + + + + + %d [%t] %p %c - %m%n + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/clustered/ops-tool/build.gradle b/clustered/ops-tool/build.gradle index 7499269244..0963f3352c 100644 --- a/clustered/ops-tool/build.gradle +++ b/clustered/ops-tool/build.gradle @@ -14,6 +14,10 @@ * limitations under the License. */ +plugins { + id 'org.ehcache.build.conventions.java-library' +} + dependencies { implementation 'com.beust:jcommander:1.47' } diff --git a/clustered/ops-tool/gradle.properties b/clustered/ops-tool/gradle.properties deleted file mode 100644 index 86aa0f75bb..0000000000 --- a/clustered/ops-tool/gradle.properties +++ /dev/null @@ -1,18 +0,0 @@ -# -# Copyright Terracotta, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -subPomName = Ehcache 3 Clustered Ops Tooling -subPomDesc = Operations Tools for Clustered Ehcache 3 diff --git a/clustered/ops-tool/src/test/java/org/ehcache/clustered/operations/OperationsToolTest.java b/clustered/ops-tool/src/test/java/org/ehcache/clustered/operations/OperationsToolTest.java index cebd4ab66e..754d1e0612 100644 --- a/clustered/ops-tool/src/test/java/org/ehcache/clustered/operations/OperationsToolTest.java +++ b/clustered/ops-tool/src/test/java/org/ehcache/clustered/operations/OperationsToolTest.java @@ -16,8 +16,9 @@ package org.ehcache.clustered.operations; import java.io.IOException; + +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; import org.junit.Test; public class OperationsToolTest { diff --git a/clustered/osgi-test/build.gradle b/clustered/osgi-test/build.gradle new file mode 100644 index 0000000000..57909678f5 --- /dev/null +++ b/clustered/osgi-test/build.gradle @@ -0,0 +1,123 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id 'org.ehcache.build.conventions.java' +} + +configurations { + modularOsgiModule + osgiModule + lowerBoundOsgiModule.extendsFrom osgiModule + testCompileOnly.extendsFrom osgiModule +} + +dependencies { + modularOsgiModule project(':ehcache-api') + modularOsgiModule project(':ehcache-core') + modularOsgiModule project(':ehcache-impl') + modularOsgiModule project(':ehcache-xml') + + osgiModule project(':ehcache') + osgiModule project(':clustered:ehcache-clustered') + osgiModule "javax.cache:cache-api:$parent.jcacheVersion" + osgiModule "org.slf4j:slf4j-simple:$parent.slf4jVersion" + osgiModule "org.terracotta:terracotta-utilities-test-tools:$terracottaUtilitiesVersion" + osgiModule "org.terracotta:terracotta-utilities-port-chooser:$terracottaUtilitiesVersion" + osgiModule 'org.apache.felix:org.apache.felix.scr:2.1.6' + osgiModule 'com.sun.activation:javax.activation:1.2.0' + osgiModule 'org.glassfish.hk2:osgi-resource-locator:1.0.2' + + testImplementation project(':osgi-test') + testImplementation 'org.osgi:osgi.core:6.0.0' +} + +configurations.all { + resolutionStrategy { + dependencySubstitution { + substitute(module('org.ops4j.pax.url:pax-url-aether:2.4.5')) + .because('https://github.com/codehaus-plexus/plexus-utils/issues/3' + + ' and https://github.com/codehaus-plexus/plexus-utils/issues/4') + .with(module('org.ops4j.pax.url:pax-url-aether:2.6.3')) + substitute(module('org.ops4j.pax.url:pax-url-classpath:2.4.5')) + .because('https://ops4j1.jira.com/browse/PAXURL-341') + .with(module('org.ops4j.pax.url:pax-url-classpath:2.6.1')) + substitute(module('org.ops4j.pax.url:pax-url-link:2.4.5')) + .because('https://ops4j1.jira.com/browse/PAXURL-341') + .with(module('org.ops4j.pax.url:pax-url-link:2.6.1')) + + substitute(module('biz.aQute.bnd:bndlib:2.4.0')) + .because('Java 9 Stuff') + .with(module('biz.aQute.bnd:biz.aQute.bndlib:5.2.0')) + substitute(module('junit:junit:4.12')) + .because('CVE-2020-15250') + .with(module('junit:junit:4.13.1')) + } + } +} + +sourceSets { + test { + // Needed for PaxExam which makes the dynamic bundle load content of a single dir + // matching the package of the test class + output.resourcesDir = java.outputDir + } +} + +task unzipKit(type: Sync) { + dependsOn project(':clustered:ehcache-clustered').distZip + from zipTree(project(':clustered:ehcache-clustered').distZip.archivePath) + into 'build/ehcache-kit' +} + +tasks.withType(Test) { + dependsOn unzipKit + systemProperty 'kitInstallationPath', "$unzipKit.destinationDir/${project(':clustered:ehcache-clustered').archivesBaseName}-$project.version-kit" +} + +test { + dependsOn configurations.osgiModule, configurations.modularOsgiModule + doFirst { + [configurations.modularOsgiModule, configurations.osgiModule]*.resolvedConfiguration*.resolvedArtifacts*.forEach({ + systemProperty "$it.moduleVersion.id.module:osgi-path", it.file + }) + } +} + +configurations { + lowerBoundOsgiModule { + resolutionStrategy.dependencySubstitution { + substitute module('org.glassfish.jaxb:jaxb-runtime') with module('com.sun.xml.bind:jaxb-osgi:2.2.8-b01') + } + } +} +dependencies { + lowerBoundOsgiModule 'javax.xml.bind:jaxb-api:2.2.9' +} + +tasks.register('lowerBoundTest', Test) { + group = JavaBasePlugin.VERIFICATION_GROUP + dependsOn configurations.lowerBoundOsgiModule, configurations.modularOsgiModule + doFirst { + [configurations.modularOsgiModule, configurations.lowerBoundOsgiModule]*.resolvedConfiguration*.resolvedArtifacts*.forEach({ + systemProperty "$it.moduleVersion.id.module:osgi-path", it.file + }) + } +} + +tasks.named('check') { + dependsOn tasks.lowerBoundTest +} diff --git a/transactions/config/checkstyle-suppressions.xml b/clustered/osgi-test/config/checkstyle-suppressions.xml similarity index 100% rename from transactions/config/checkstyle-suppressions.xml rename to clustered/osgi-test/config/checkstyle-suppressions.xml diff --git a/clustered/osgi-test/src/test/java/org/ehcache/osgi/ClusterSupport.java b/clustered/osgi-test/src/test/java/org/ehcache/osgi/ClusterSupport.java new file mode 100644 index 0000000000..c016e8e341 --- /dev/null +++ b/clustered/osgi-test/src/test/java/org/ehcache/osgi/ClusterSupport.java @@ -0,0 +1,114 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.osgi; + +import org.terracotta.utilities.test.net.PortManager; + +import java.io.Closeable; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.URI; +import java.nio.channels.ServerSocketChannel; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collection; +import java.util.concurrent.TimeUnit; + +import static java.util.Arrays.asList; + +public class ClusterSupport { + + public static Cluster startServer(Path serverDirectory) throws IOException { + Path kitLocation = Paths.get(System.getProperty("kitInstallationPath")); + + PortManager portManager = PortManager.getInstance(); + PortManager.PortRef tsaPort = portManager.reservePort(); + PortManager.PortRef tsaGroupPort = portManager.reservePort(); + + Path serverDir = kitLocation.resolve("server"); + + ProcessBuilder serverProcess = new ProcessBuilder() + .directory(serverDirectory.toFile()) + .command(Paths.get(System.getProperty("java.home")).resolve("bin") + .resolve(System.getProperty("os.name").contains("Windows") ? "java.exe" : "java").toString()); + + serverProcess.command().addAll(asList( + "-Xmx128m", + "-Dtc.install-root=" + serverDir, + "-cp", serverDir.resolve("lib").resolve("tc.jar").toString(), + "com.tc.server.TCServerMain", + "--auto-activate", + "--cluster-name=foo", + "--failover-priority=availability", + "--client-reconnect-window=120s", + "--name=default-server", + "--hostname=localhost", + "--port=" + tsaPort.port(), + "--group-port=" + tsaGroupPort.port(), + "--log-dir=" + serverDirectory.resolve("logs"), + "--config-dir=" + serverDirectory.resolve("repository"), + "--offheap-resources=main:32MB")); + serverProcess.inheritIO(); + + return new Cluster(serverProcess.start(), URI.create("terracotta://localhost:" + tsaPort.port()), serverDirectory, tsaPort, tsaGroupPort); + } + + private static int selectAvailableEphemeralPort() throws IOException { + try (ServerSocketChannel channel = ServerSocketChannel.open().bind(new InetSocketAddress(0))) { + return channel.socket().getLocalPort(); + } + } + + static class Cluster implements Closeable { + + private final Process serverProcess; + private final URI connectionUri; + private final Path workingPath; + private final Collection ports; + + Cluster(Process serverProcess, URI connectionUri, Path workingPath, PortManager.PortRef... ports) { + this.serverProcess = serverProcess; + this.connectionUri = connectionUri; + this.workingPath = workingPath; + this.ports = asList(ports); + } + + public URI getConnectionUri() { + return connectionUri; + } + + @Override + public void close() { + try { + serverProcess.destroyForcibly(); + } finally { + try { + serverProcess.waitFor(60, TimeUnit.SECONDS); + } catch (InterruptedException e) { + throw new AssertionError(e); + } finally { + ports.forEach(PortManager.PortRef::close); + } + } + } + + public Path getWorkingArea() { + return workingPath; + } + } +} + diff --git a/clustered/osgi-test/src/test/java/org/ehcache/osgi/ClusteredOsgiTest.java b/clustered/osgi-test/src/test/java/org/ehcache/osgi/ClusteredOsgiTest.java new file mode 100644 index 0000000000..af954049df --- /dev/null +++ b/clustered/osgi-test/src/test/java/org/ehcache/osgi/ClusteredOsgiTest.java @@ -0,0 +1,226 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.osgi; + +import org.ehcache.Cache; +import org.ehcache.PersistentCacheManager; +import org.ehcache.config.builders.CacheManagerBuilder; +import org.ehcache.config.units.MemoryUnit; +import org.ehcache.core.osgi.EhcacheActivator; +import org.ehcache.core.osgi.OsgiServiceLoader; +import org.ehcache.core.spi.service.ServiceFactory; +import org.ehcache.xml.XmlConfiguration; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.ops4j.pax.exam.Configuration; +import org.ops4j.pax.exam.Option; +import org.ops4j.pax.exam.junit.PaxExam; +import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy; +import org.ops4j.pax.exam.spi.reactors.PerMethod; +import org.osgi.framework.wiring.BundleWiring; +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +import javax.xml.namespace.NamespaceContext; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathFactory; +import java.io.File; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.ServiceLoader; +import java.util.Set; + +import static java.util.Spliterators.spliterator; +import static java.util.stream.Collectors.toSet; +import static java.util.stream.Stream.of; +import static java.util.stream.StreamSupport.stream; +import static org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder.clusteredDedicated; +import static org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder.cluster; +import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; +import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder; +import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; +import static org.ehcache.osgi.ClusterSupport.startServer; +import static org.ehcache.osgi.OsgiTestUtils.baseConfiguration; +import static org.ehcache.osgi.OsgiTestUtils.gradleBundle; +import static org.ehcache.xml.ConfigurationParser.discoverSchema; +import static org.ehcache.xml.XmlConfiguration.CORE_SCHEMA_URL; +import static org.ehcache.osgi.OsgiTestUtils.jaxbConfiguration; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsCollectionContaining.hasItems; +import static org.ops4j.pax.exam.CoreOptions.options; + +@RunWith(PaxExam.class) +@ExamReactorStrategy(PerMethod.class) +public class ClusteredOsgiTest { + + @Rule + public TemporaryFolder serverLocation = new TemporaryFolder(); + + @Configuration + public Option[] individualModules() { + return options( + gradleBundle("org.ehcache.modules:ehcache-api"), + gradleBundle("org.ehcache.modules:ehcache-core"), + gradleBundle("org.ehcache.modules:ehcache-impl"), + gradleBundle("org.ehcache.modules:ehcache-xml"), jaxbConfiguration(), + gradleBundle("org.ehcache:ehcache-clustered"), + + gradleBundle("org.terracotta:statistics"), + gradleBundle("org.ehcache:sizeof"), + gradleBundle("org.terracotta:offheap-store"), + gradleBundle("org.terracotta:terracotta-utilities-tools"), + + baseConfiguration("ClusteredOsgiTest", "individualModules"), + gradleBundle("org.terracotta:terracotta-utilities-test-tools"), + gradleBundle("org.terracotta:terracotta-utilities-port-chooser") + ); + } + + @Configuration + public Option[] uberJar() { + return options( + gradleBundle("org.ehcache:ehcache"), jaxbConfiguration(), + gradleBundle("org.ehcache:ehcache-clustered"), + + baseConfiguration("ClusteredOsgiTest", "uberJar"), + gradleBundle("org.terracotta:terracotta-utilities-tools"), + gradleBundle("org.terracotta:terracotta-utilities-test-tools"), + gradleBundle("org.terracotta:terracotta-utilities-port-chooser") + ); + } + + @Test + public void testProgrammaticClusteredCache() throws Throwable { + try (ClusterSupport.Cluster cluster = startServer(serverLocation.newFolder().toPath())) { + TestMethods.testProgrammaticClusteredCache(cluster); + } + } + + @Test + public void testXmlClusteredCache() throws Throwable { + try (ClusterSupport.Cluster cluster = startServer(serverLocation.newFolder().toPath())) { + TestMethods.testXmlClusteredCache(cluster); + } + } + + @Test + public void testAllServicesAreAvailable() { + TestMethods.testAllServicesAreAvailable(); + } + + private static class TestMethods { + + public static void testProgrammaticClusteredCache(ClusterSupport.Cluster cluster) throws Throwable { + try (PersistentCacheManager cacheManager = newCacheManagerBuilder() + .with(cluster(cluster.getConnectionUri()).autoCreate(c -> c)) + .withCache("clustered-cache", newCacheConfigurationBuilder(Long.class, String.class, + newResourcePoolsBuilder().with(clusteredDedicated("main", 2, MemoryUnit.MB)))) + .build(true)) { + + final Cache cache = cacheManager.getCache("clustered-cache", Long.class, String.class); + + cache.put(1L, "value"); + assertThat(cache.get(1L), is("value")); + } + } + + public static void testXmlClusteredCache(ClusterSupport.Cluster cluster) throws Exception { + File config = cluster.getWorkingArea().resolve("ehcache.xml").toFile(); + + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + documentBuilderFactory.setNamespaceAware(true); + documentBuilderFactory.setSchema(discoverSchema(new StreamSource(CORE_SCHEMA_URL.openStream()))); + + Document doc = documentBuilderFactory.newDocumentBuilder().parse(TestMethods.class.getResourceAsStream("ehcache-clustered-osgi.xml")); + + XPath xpath = XPathFactory.newInstance().newXPath(); + xpath.setNamespaceContext(new SimpleNamespaceContext() + .with("eh", "http://www.ehcache.org/v3") + .with("tc", "http://www.ehcache.org/v3/clustered")); + + Node clusterUriAttribute = (Node) xpath.evaluate("//eh:config/eh:service/tc:cluster/tc:connection/@url", doc, XPathConstants.NODE); + clusterUriAttribute.setTextContent(cluster.getConnectionUri().toString() + "/cache-manager"); + Transformer xformer = TransformerFactory.newInstance().newTransformer(); + xformer.transform(new DOMSource(doc), new StreamResult(config)); + + + try (PersistentCacheManager cacheManager = (PersistentCacheManager) CacheManagerBuilder.newCacheManager( + new XmlConfiguration(config.toURI().toURL(), TestMethods.class.getClassLoader()) + )) { + cacheManager.init(); + + final Cache cache = cacheManager.getCache("clustered-cache", Long.class, Person.class); + + cache.put(1L, new Person("Brian")); + assertThat(cache.get(1L).name, is("Brian")); + } + } + + public static void testAllServicesAreAvailable() { + Set osgiAvailableClasses = + stream(spliterator(OsgiServiceLoader.load(ServiceFactory.class).iterator(), Long.MAX_VALUE, 0), false) + .map(f -> f.getClass().getName()) + .collect(toSet()); + + Set jdkAvailableClasses = of(EhcacheActivator.getCoreBundle().getBundles()) + .map(b -> b.adapt(BundleWiring.class).getClassLoader()) + .flatMap(cl -> + stream(spliterator(ServiceLoader.load(ServiceFactory.class, cl).iterator(), Long.MAX_VALUE, 0), false) + .map(f -> f.getClass().getName())) + .collect(toSet()); + + assertThat(osgiAvailableClasses, hasItems(jdkAvailableClasses.toArray(new String[0]))); + } + } + + static class SimpleNamespaceContext implements NamespaceContext { + + public final Map prefixes = new HashMap<>(); + + public SimpleNamespaceContext with(String prefix, String namespaceUri) { + prefixes.put(prefix, namespaceUri); + return this; + } + + @Override + public String getNamespaceURI(String prefix) { + return prefixes.get(prefix); + } + + @Override + public String getPrefix(String namespaceURI) { + return prefixes.entrySet().stream().filter(e -> namespaceURI.equals(e.getValue())) + .map(Map.Entry::getKey).findFirst().orElse(null); + } + + @Override + public Iterator getPrefixes(String namespaceURI) { + return prefixes.keySet().iterator(); + } + }; +} diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/InternalClusterTierClientEntity.java b/clustered/osgi-test/src/test/java/org/ehcache/osgi/Person.java similarity index 71% rename from clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/InternalClusterTierClientEntity.java rename to clustered/osgi-test/src/test/java/org/ehcache/osgi/Person.java index 4841c72255..568a9b4be5 100644 --- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/InternalClusterTierClientEntity.java +++ b/clustered/osgi-test/src/test/java/org/ehcache/osgi/Person.java @@ -14,10 +14,21 @@ * limitations under the License. */ -package org.ehcache.clustered.client.internal.store; +package org.ehcache.osgi; + +import java.io.Serializable; /** - * InternalClusterTierClientEntity : Marker interface for any extensions that is used internally + * Person */ -public interface InternalClusterTierClientEntity extends ClusterTierClientEntity { +public class Person implements Serializable { + + private static final long serialVersionUID = 1L; + + final String name; + + Person(String name) { + this.name = name; + } + } diff --git a/clustered/osgi-test/src/test/resources/org/ehcache/osgi/ehcache-clustered-osgi.xml b/clustered/osgi-test/src/test/resources/org/ehcache/osgi/ehcache-clustered-osgi.xml new file mode 100644 index 0000000000..13db0d6848 --- /dev/null +++ b/clustered/osgi-test/src/test/resources/org/ehcache/osgi/ehcache-clustered-osgi.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + java.lang.Long + org.ehcache.osgi.Person + + 100 + 1 + + + diff --git a/clustered/server/build.gradle b/clustered/server/build.gradle deleted file mode 100644 index 8dfb3d93d9..0000000000 --- a/clustered/server/build.gradle +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -apply plugin: EhDeploy - -dependencies { - api "org.terracotta:client-message-tracker:$terracottaPlatformVersion" - api("org.terracotta:offheap-resource:$terracottaPlatformVersion") { - transitive = false - } - implementation project(':clustered:common') - implementation group: 'org.terracotta', name: 'offheap-store', version: offheapVersion - implementation group: 'org.slf4j', name: 'slf4j-api', version: slf4jVersion - implementation ("org.terracotta.management:monitoring-service-api:$terracottaPlatformVersion") { - transitive = false - } - implementation "org.terracotta.management.dist:mnm-common:$terracottaPlatformVersion" - providedImplementation "org.terracotta:entity-server-api:$terracottaApisVersion" - providedImplementation "org.terracotta:standard-cluster-services:$terracottaApisVersion" - providedImplementation "org.terracotta:runnel:$terracottaPlatformVersion" -} diff --git a/clustered/server/ehcache-entity/build.gradle b/clustered/server/ehcache-entity/build.gradle new file mode 100644 index 0000000000..2d88b49de3 --- /dev/null +++ b/clustered/server/ehcache-entity/build.gradle @@ -0,0 +1,40 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id 'org.ehcache.build.clustered-server-module' +} + +publishing.publications.withType(MavenPublication) { + pom { + name = 'Ehcache 3 Clustering Server Entity module' + description = 'The Server Entity module of Ehcache 3' + } +} + +dependencies { + service project(':clustered:server:ehcache-service-api') + service "org.terracotta.management:monitoring-service-api:$terracottaPlatformVersion" + service "org.terracotta.management:management-registry:$terracottaPlatformVersion" + + api project(':clustered:ehcache-common') + implementation "org.terracotta:runnel:$terracottaPlatformVersion" + implementation "org.terracotta:offheap-store:$offheapVersion" + implementation "org.terracotta:client-message-tracker:$terracottaPlatformVersion" + + testImplementation project(':clustered:server:ehcache-service') + testImplementation project(':clustered:test-utils') +} diff --git a/xml/config/checkstyle-suppressions.xml b/clustered/server/ehcache-entity/config/checkstyle-suppressions.xml similarity index 80% rename from xml/config/checkstyle-suppressions.xml rename to clustered/server/ehcache-entity/config/checkstyle-suppressions.xml index a287bf4e21..cb41d0baf7 100644 --- a/xml/config/checkstyle-suppressions.xml +++ b/clustered/server/ehcache-entity/config/checkstyle-suppressions.xml @@ -5,6 +5,5 @@ "http://www.puppycrawl.com/dtds/suppressions_1_1.dtd"> - diff --git a/clustered/server/src/main/java/org/ehcache/clustered/lock/server/VoltronReadWriteLockActiveEntity.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/lock/server/VoltronReadWriteLockActiveEntity.java similarity index 100% rename from clustered/server/src/main/java/org/ehcache/clustered/lock/server/VoltronReadWriteLockActiveEntity.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/lock/server/VoltronReadWriteLockActiveEntity.java diff --git a/clustered/server/src/main/java/org/ehcache/clustered/lock/server/VoltronReadWriteLockPassiveEntity.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/lock/server/VoltronReadWriteLockPassiveEntity.java similarity index 100% rename from clustered/server/src/main/java/org/ehcache/clustered/lock/server/VoltronReadWriteLockPassiveEntity.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/lock/server/VoltronReadWriteLockPassiveEntity.java diff --git a/clustered/server/src/main/java/org/ehcache/clustered/lock/server/VoltronReadWriteLockServerEntityService.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/lock/server/VoltronReadWriteLockServerEntityService.java similarity index 100% rename from clustered/server/src/main/java/org/ehcache/clustered/lock/server/VoltronReadWriteLockServerEntityService.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/lock/server/VoltronReadWriteLockServerEntityService.java diff --git a/clustered/server/src/main/java/org/ehcache/clustered/lock/server/messages/LockSyncMessaging.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/lock/server/messages/LockSyncMessaging.java similarity index 100% rename from clustered/server/src/main/java/org/ehcache/clustered/lock/server/messages/LockSyncMessaging.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/lock/server/messages/LockSyncMessaging.java diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/ClusterTierManagerActiveEntity.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/ClusterTierManagerActiveEntity.java similarity index 100% rename from clustered/server/src/main/java/org/ehcache/clustered/server/ClusterTierManagerActiveEntity.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/ClusterTierManagerActiveEntity.java diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/ClusterTierManagerDump.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/ClusterTierManagerDump.java similarity index 100% rename from clustered/server/src/main/java/org/ehcache/clustered/server/ClusterTierManagerDump.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/ClusterTierManagerDump.java diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/ClusterTierManagerPassiveEntity.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/ClusterTierManagerPassiveEntity.java similarity index 100% rename from clustered/server/src/main/java/org/ehcache/clustered/server/ClusterTierManagerPassiveEntity.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/ClusterTierManagerPassiveEntity.java diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/ClusterTierManagerServerEntityService.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/ClusterTierManagerServerEntityService.java similarity index 100% rename from clustered/server/src/main/java/org/ehcache/clustered/server/ClusterTierManagerServerEntityService.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/ClusterTierManagerServerEntityService.java diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/CommunicatorServiceConfiguration.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/CommunicatorServiceConfiguration.java similarity index 100% rename from clustered/server/src/main/java/org/ehcache/clustered/server/CommunicatorServiceConfiguration.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/CommunicatorServiceConfiguration.java diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/ConcurrencyStrategies.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/ConcurrencyStrategies.java similarity index 91% rename from clustered/server/src/main/java/org/ehcache/clustered/server/ConcurrencyStrategies.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/ConcurrencyStrategies.java index 5cd2ef105a..5ac47d1b79 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/ConcurrencyStrategies.java +++ b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/ConcurrencyStrategies.java @@ -23,6 +23,7 @@ import org.ehcache.clustered.common.internal.messages.EhcacheEntityMessage; import org.ehcache.clustered.common.internal.messages.ServerStoreOpMessage; import org.terracotta.entity.ConcurrencyStrategy; +import org.ehcache.clustered.server.internal.messages.EhcacheMessageTrackerCatchup; import static java.util.Collections.singleton; @@ -55,6 +56,7 @@ public Set getKeysForSynchronization() { public static class DefaultConcurrencyStrategy implements ConcurrencyStrategy { public static final int DATA_CONCURRENCY_KEY_OFFSET = DEFAULT_KEY + 1; + public static final int TRACKER_SYNC_KEY = Integer.MAX_VALUE - 1; private final KeySegmentMapper mapper; @@ -69,6 +71,8 @@ public int concurrencyKey(EhcacheEntityMessage entityMessage) { } else if (entityMessage instanceof ConcurrentEntityMessage) { ConcurrentEntityMessage concurrentEntityMessage = (ConcurrentEntityMessage) entityMessage; return DATA_CONCURRENCY_KEY_OFFSET + mapper.getSegmentForKey(concurrentEntityMessage.concurrencyKey()); + } else if (entityMessage instanceof EhcacheMessageTrackerCatchup) { + return MANAGEMENT_KEY; } else { return DEFAULT_KEY; } @@ -80,6 +84,7 @@ public Set getKeysForSynchronization() { for (int i = 0; i <= mapper.getSegments(); i++) { result.add(DEFAULT_KEY + i); } + result.add(TRACKER_SYNC_KEY); return Collections.unmodifiableSet(result); } } diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/EhcacheExecutionStrategy.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/EhcacheExecutionStrategy.java similarity index 93% rename from clustered/server/src/main/java/org/ehcache/clustered/server/EhcacheExecutionStrategy.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/EhcacheExecutionStrategy.java index c5cba5f45c..d24ce6da60 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/EhcacheExecutionStrategy.java +++ b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/EhcacheExecutionStrategy.java @@ -21,6 +21,7 @@ import org.ehcache.clustered.server.internal.messages.PassiveReplicationMessage; import org.ehcache.clustered.common.internal.messages.ServerStoreOpMessage; import org.ehcache.clustered.common.internal.messages.StateRepositoryOpMessage; +import org.ehcache.clustered.server.internal.messages.EhcacheMessageTrackerCatchup; import org.ehcache.clustered.server.internal.messages.EhcacheSyncMessage; import org.terracotta.entity.ExecutionStrategy; @@ -50,6 +51,8 @@ public Location getExecutionLocation(EhcacheEntityMessage message) { return Location.ACTIVE; } else if (message instanceof PassiveReplicationMessage) { return Location.PASSIVE; + } else if (message instanceof EhcacheMessageTrackerCatchup) { + return Location.PASSIVE; } else if (message instanceof EhcacheSyncMessage) { throw new AssertionError("Unexpected use of ExecutionStrategy for sync messages"); } diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/ServerStoreCompatibility.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/ServerStoreCompatibility.java similarity index 100% rename from clustered/server/src/main/java/org/ehcache/clustered/server/ServerStoreCompatibility.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/ServerStoreCompatibility.java diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheDataSyncMessage.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheDataSyncMessage.java similarity index 95% rename from clustered/server/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheDataSyncMessage.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheDataSyncMessage.java index 1937f9389f..39a1fc7ad0 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheDataSyncMessage.java +++ b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheDataSyncMessage.java @@ -18,12 +18,9 @@ import org.ehcache.clustered.common.internal.store.Chain; -import com.tc.classloader.CommonComponent; - import java.util.Collections; import java.util.Map; -@CommonComponent public class EhcacheDataSyncMessage extends EhcacheSyncMessage { private final Map chainMap; diff --git a/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheMessageTrackerCatchup.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheMessageTrackerCatchup.java new file mode 100644 index 0000000000..afae8434a0 --- /dev/null +++ b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheMessageTrackerCatchup.java @@ -0,0 +1,40 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.clustered.server.internal.messages; + +import java.util.Collection; +import org.ehcache.clustered.common.internal.messages.EhcacheEntityMessage; +import org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse; + +import org.terracotta.client.message.tracker.RecordedMessage; + +/** + * Message sending messages that are tracked for duplication. If a passive becoming active receives + * a duplicate, it needs to discard it. + */ +public class EhcacheMessageTrackerCatchup extends EhcacheEntityMessage { + + private final Collection> trackedMessages; + + public EhcacheMessageTrackerCatchup(Collection> trackedMessages) { + this.trackedMessages = trackedMessages; + } + + public Collection> getTrackedMessages() { + return trackedMessages; + } +} diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheMessageTrackerMessage.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheMessageTrackerMessage.java similarity index 62% rename from clustered/server/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheMessageTrackerMessage.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheMessageTrackerMessage.java index c515efcca4..ca6c6fdf1c 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheMessageTrackerMessage.java +++ b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheMessageTrackerMessage.java @@ -16,34 +16,22 @@ package org.ehcache.clustered.server.internal.messages; -import org.ehcache.clustered.common.internal.messages.EhcacheEntityMessage; import org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse; -import org.terracotta.client.message.tracker.OOOMessageHandler; -import org.terracotta.entity.ClientSourceId; import java.util.Map; -import static java.util.stream.Collectors.toMap; - /** * Message sending messages that are tracked for duplication. If a passive becoming active receives * a duplicate, it needs to discard it. */ public class EhcacheMessageTrackerMessage extends EhcacheSyncMessage { - private final int segmentId; private final Map> trackedMessages; - public EhcacheMessageTrackerMessage(int segmentId, Map> trackedMessages) { - this.segmentId = segmentId; + public EhcacheMessageTrackerMessage(Map> trackedMessages) { this.trackedMessages = trackedMessages; } - public EhcacheMessageTrackerMessage(int segmentId, OOOMessageHandler messageHandler) { - this(segmentId, messageHandler.getTrackedClients() - .collect(toMap(ClientSourceId::toLong, clientSourceId -> messageHandler.getTrackedResponsesForSegment(segmentId, clientSourceId)))); - } - @Override public SyncMessageType getMessageType() { return SyncMessageType.MESSAGE_TRACKER; @@ -52,8 +40,4 @@ public SyncMessageType getMessageType() { public Map> getTrackedMessages() { return trackedMessages; } - - public int getSegmentId() { - return segmentId; - } } diff --git a/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheServerCodec.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheServerCodec.java new file mode 100644 index 0000000000..fa03069f68 --- /dev/null +++ b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheServerCodec.java @@ -0,0 +1,179 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.clustered.server.internal.messages; + +import org.ehcache.clustered.common.internal.messages.EhcacheCodec; +import org.ehcache.clustered.common.internal.messages.EhcacheEntityMessage; +import org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse; +import org.ehcache.clustered.common.internal.messages.EhcacheMessageType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.terracotta.entity.MessageCodec; +import org.terracotta.runnel.decoding.Enm; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.terracotta.client.message.tracker.RecordedMessage; +import org.terracotta.entity.ClientDescriptor; +import org.terracotta.entity.ClientSourceId; +import org.terracotta.runnel.Struct; +import org.terracotta.runnel.decoding.StructArrayDecoder; +import org.terracotta.runnel.decoding.StructDecoder; + +import static org.ehcache.clustered.common.internal.messages.BaseCodec.EHCACHE_MESSAGE_TYPES_ENUM_MAPPING; +import static org.ehcache.clustered.common.internal.messages.BaseCodec.MESSAGE_TYPE_FIELD_INDEX; +import static org.ehcache.clustered.common.internal.messages.BaseCodec.MESSAGE_TYPE_FIELD_NAME; +import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.isPassiveReplicationMessage; +import static org.terracotta.runnel.StructBuilder.newStructBuilder; +/** + * EhcacheServerCodec + */ +public class EhcacheServerCodec implements MessageCodec { + + private static final Logger LOGGER = LoggerFactory.getLogger(EhcacheServerCodec.class); + + private final EhcacheCodec clientCodec; + private final PassiveReplicationMessageCodec replicationCodec; + + private static final String CLIENT_ID = "client"; + private static final String TRANSACTION_ID = "transaction"; + private static final String MESSAGE = "message"; + private static final String MESSAGE_SEQUENCE = "sequence"; + + private static final Struct RECORDED_MESSAGE = newStructBuilder() + .int64(CLIENT_ID, 10) + .int64(TRANSACTION_ID, 20) + .byteBuffer(MESSAGE, 30) + .build(); + + private static final Struct MESSAGE_HISTORY = newStructBuilder() + .enm(MESSAGE_TYPE_FIELD_NAME, MESSAGE_TYPE_FIELD_INDEX, EHCACHE_MESSAGE_TYPES_ENUM_MAPPING) + .structs(MESSAGE_SEQUENCE, 20, RECORDED_MESSAGE) + .build(); + + public EhcacheServerCodec(EhcacheCodec clientCodec, PassiveReplicationMessageCodec replicationCodec) { + this.clientCodec = clientCodec; + this.replicationCodec = replicationCodec; + } + + @Override + public byte[] encodeMessage(EhcacheEntityMessage message) { + if (message instanceof PassiveReplicationMessage) { + return replicationCodec.encode((PassiveReplicationMessage) message); + } else if (message instanceof EhcacheMessageTrackerCatchup) { + return encodeCatchup((EhcacheMessageTrackerCatchup)message); + } + return clientCodec.encodeMessage(message); + } + + private byte[] encodeCatchup(EhcacheMessageTrackerCatchup catchup) { + return MESSAGE_HISTORY.encoder() + .enm(MESSAGE_TYPE_FIELD_NAME, EhcacheMessageType.MESSAGE_CATCHUP) + .structs(MESSAGE_SEQUENCE, catchup.getTrackedMessages(), (encoder, value) -> { + encoder.int64(CLIENT_ID, value.getClientSourceId().toLong()); + encoder.int64(TRANSACTION_ID, value.getTransactionId()); + encoder.byteBuffer(MESSAGE, ByteBuffer.wrap(encodeMessage(value.getRequest()))); + }).encode().array(); + } + + @Override + public EhcacheEntityMessage decodeMessage(byte[] payload) { + return decodeMessage(ByteBuffer.wrap(payload)); + } + + private EhcacheEntityMessage decodeMessage(ByteBuffer byteBuffer) { + Enm opCodeEnm = EhcacheCodec.OP_CODE_DECODER.decoder(byteBuffer).enm(MESSAGE_TYPE_FIELD_NAME); + if (!opCodeEnm.isFound()) { + throw new AssertionError("Got a message without an opCode"); + } + if (!opCodeEnm.isValid()) { + LOGGER.warn("Received message with unknown operation code - more recent version at the other end?"); + return null; + } + + byteBuffer.rewind(); + + EhcacheMessageType messageType = opCodeEnm.get(); + if (messageType == EhcacheMessageType.MESSAGE_CATCHUP) { + return decodeCatchup(byteBuffer); + } else if (isPassiveReplicationMessage(messageType)) { + return replicationCodec.decode(messageType, byteBuffer); + } + return clientCodec.decodeMessage(byteBuffer, messageType); + } + + @Override + public byte[] encodeResponse(EhcacheEntityResponse response) { + return clientCodec.encodeResponse(response); + } + + @Override + public EhcacheEntityResponse decodeResponse(byte[] payload) { + return clientCodec.decodeResponse(payload); + } + + private EhcacheMessageTrackerCatchup decodeCatchup(ByteBuffer payload) { + StructArrayDecoder> array = MESSAGE_HISTORY.decoder(payload).structs(MESSAGE_SEQUENCE); + if (array == null) { + return new EhcacheMessageTrackerCatchup(Collections.emptyList()); + } + List> list = new ArrayList<>(array.length()); + while (array.hasNext()) { + StructDecoder>> decoder = array.next(); + long cid = decoder.int64(CLIENT_ID); + long transaction = decoder.int64(TRANSACTION_ID); + ByteBuffer buff = decoder.byteBuffer(MESSAGE); + EhcacheEntityMessage msg = decodeMessage(buff); + + list.add(new RecordedMessage() { + @Override + public ClientSourceId getClientSourceId() { + return new ClientSourceId() { + @Override + public long toLong() { + return cid; + } + + @Override + public boolean matches(ClientDescriptor cd) { + return cd.getSourceId().toLong() == cid; + } + }; + } + + @Override + public long getTransactionId() { + return transaction; + } + + @Override + public EhcacheEntityMessage getRequest() { + return msg; + } + + @Override + public EhcacheEntityResponse getResponse() { + return null; + } + }); + } + return new EhcacheMessageTrackerCatchup(list); + } +} diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheSyncMessageCodec.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheSyncMessageCodec.java similarity index 91% rename from clustered/server/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheSyncMessageCodec.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheSyncMessageCodec.java index be536b5df5..dd4d260f83 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheSyncMessageCodec.java +++ b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheSyncMessageCodec.java @@ -24,6 +24,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.terracotta.entity.SyncMessageCodec; +import org.terracotta.runnel.EnumMapping; import org.terracotta.runnel.Struct; import org.terracotta.runnel.decoding.Enm; import org.terracotta.runnel.decoding.StructArrayDecoder; @@ -47,15 +48,14 @@ import static org.ehcache.clustered.server.internal.messages.SyncMessageType.DATA; import static org.ehcache.clustered.server.internal.messages.SyncMessageType.MESSAGE_TRACKER; import static org.ehcache.clustered.server.internal.messages.SyncMessageType.STATE_REPO; -import static org.ehcache.clustered.server.internal.messages.SyncMessageType.SYNC_MESSAGE_TYPE_FIELD_INDEX; -import static org.ehcache.clustered.server.internal.messages.SyncMessageType.SYNC_MESSAGE_TYPE_FIELD_NAME; -import static org.ehcache.clustered.server.internal.messages.SyncMessageType.SYNC_MESSAGE_TYPE_MAPPING; +import static org.terracotta.runnel.EnumMappingBuilder.newEnumMappingBuilder; import static org.terracotta.runnel.StructBuilder.newStructBuilder; public class EhcacheSyncMessageCodec implements SyncMessageCodec { private static final Logger LOGGER = LoggerFactory.getLogger(EhcacheSyncMessageCodec.class); + private static final String SYNC_MESSAGE_TYPE_FIELD = "msgType"; private static final String CHAIN_FIELD = "chain"; private static final String CHAIN_MAP_ENTRIES_SUB_STRUCT = "entries"; private static final String STATE_REPO_ENTRIES_SUB_STRUCT = "mappings"; @@ -67,13 +67,20 @@ public class EhcacheSyncMessageCodec implements SyncMessageCodec SYNC_MESSAGE_TYPE_MAPPING = newEnumMappingBuilder(SyncMessageType.class) + .mapping(STATE_REPO, 1) + .mapping(DATA, 10) + .mapping(MESSAGE_TRACKER, 20) + .build(); + private static final Struct CHAIN_MAP_ENTRY_STRUCT = newStructBuilder() .int64(KEY_FIELD, 10) .struct(CHAIN_FIELD, 20, CHAIN_STRUCT) .build(); private static final Struct DATA_SYNC_STRUCT = newStructBuilder() - .enm(SYNC_MESSAGE_TYPE_FIELD_NAME, SYNC_MESSAGE_TYPE_FIELD_INDEX, SYNC_MESSAGE_TYPE_MAPPING) + .enm(SYNC_MESSAGE_TYPE_FIELD, SYNC_MESSAGE_TYPE_FIELD_INDEX, SYNC_MESSAGE_TYPE_MAPPING) .structs(CHAIN_MAP_ENTRIES_SUB_STRUCT, 20, CHAIN_MAP_ENTRY_STRUCT) .build(); @@ -83,7 +90,7 @@ public class EhcacheSyncMessageCodec implements SyncMessageCodec encoder = MESSAGE_TRACKER_SYNC_STRUCT.encoder(); encoder - .enm(SYNC_MESSAGE_TYPE_FIELD_NAME, MESSAGE_TRACKER) + .enm(SYNC_MESSAGE_TYPE_FIELD, MESSAGE_TRACKER) .structs(MESSAGE_TRACKER_CLIENTS_STRUCT, syncMessage.getTrackedMessages().entrySet(), (clientEncoder, entry) -> { Map responses = entry.getValue(); @@ -146,8 +153,8 @@ private byte[] encodeMessageTrackerSync(EhcacheMessageTrackerMessage syncMessage responseEncoder.byteBuffer(MESSAGE_TRACKER_RESPONSE_FIELD, encodeResponse(response.getValue())); }); } - }) - .int32(MESSAGE_TRACKER_SEGMENT_FIELD, syncMessage.getSegmentId()); + }); + return encoder.encode().array(); } @@ -157,7 +164,7 @@ private ByteBuffer encodeResponse(EhcacheEntityResponse response) { private byte[] encodeStateRepoSync(EhcacheStateRepoSyncMessage syncMessage) { StructEncoder encoder = STATE_REPO_SYNC_STRUCT.encoder(); - encoder.enm(SYNC_MESSAGE_TYPE_FIELD_NAME, STATE_REPO) + encoder.enm(SYNC_MESSAGE_TYPE_FIELD, STATE_REPO) .string(SERVER_STORE_NAME_FIELD, syncMessage.getCacheId()) .string(STATE_REPO_MAP_NAME_FIELD, syncMessage.getMapId()); encoder.structs(STATE_REPO_ENTRIES_SUB_STRUCT, syncMessage.getMappings().entrySet(), @@ -169,11 +176,11 @@ private byte[] encodeStateRepoSync(EhcacheStateRepoSyncMessage syncMessage) { private byte[] encodeDataSync(EhcacheDataSyncMessage syncMessage) { StructEncoder encoder; encoder = DATA_SYNC_STRUCT.encoder(); - encoder.enm(SYNC_MESSAGE_TYPE_FIELD_NAME, DATA); + encoder.enm(SYNC_MESSAGE_TYPE_FIELD, DATA); encoder.structs(CHAIN_MAP_ENTRIES_SUB_STRUCT, syncMessage.getChainMap().entrySet(), (entryEncoder, entry) -> { entryEncoder.int64(KEY_FIELD, entry.getKey()); - entryEncoder.struct(CHAIN_FIELD, entry.getValue(), ChainCodec::encode); + entryEncoder.struct(CHAIN_FIELD, entry.getValue(), ChainCodec::encodeChain); }); return encoder.encode().array(); } @@ -182,7 +189,7 @@ private byte[] encodeDataSync(EhcacheDataSyncMessage syncMessage) { public EhcacheSyncMessage decode(final int concurrencyKey, final byte[] payload) { ByteBuffer message = wrap(payload); StructDecoder decoder = DATA_SYNC_STRUCT.decoder(message); - Enm enm = decoder.enm(SYNC_MESSAGE_TYPE_FIELD_NAME); + Enm enm = decoder.enm(SYNC_MESSAGE_TYPE_FIELD); if (!enm.isFound()) { throw new AssertionError("Invalid message format - misses the message type field"); } @@ -230,8 +237,7 @@ private EhcacheSyncMessage decodeMessageTracker(ByteBuffer message) { } } } - Integer segmentId = decoder.int32(MESSAGE_TRACKER_SEGMENT_FIELD); - return new EhcacheMessageTrackerMessage(segmentId, trackedMessages); + return new EhcacheMessageTrackerMessage(trackedMessages); } private EhcacheSyncMessage decodeStateRepoSync(ByteBuffer message) { @@ -267,7 +273,7 @@ private Map decodeChainMapEntries(StructDecoder decoder) { StructDecoder entryDecoder = entriesDecoder.next(); Long key = entryDecoder.int64(KEY_FIELD); StructDecoder chainDecoder = entryDecoder.struct(CHAIN_FIELD); - Chain chain = ChainCodec.decode(chainDecoder); + Chain chain = ChainCodec.decodeChain(chainDecoder); chainMap.put(key, chain); entryDecoder.end(); } diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/internal/messages/PassiveReplicationMessage.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/internal/messages/PassiveReplicationMessage.java similarity index 95% rename from clustered/server/src/main/java/org/ehcache/clustered/server/internal/messages/PassiveReplicationMessage.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/internal/messages/PassiveReplicationMessage.java index 6baace78ca..9150683a93 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/internal/messages/PassiveReplicationMessage.java +++ b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/internal/messages/PassiveReplicationMessage.java @@ -21,12 +21,13 @@ import org.ehcache.clustered.common.internal.messages.EhcacheOperationMessage; import org.ehcache.clustered.common.internal.store.Chain; import org.ehcache.clustered.common.internal.store.Element; -import org.ehcache.clustered.common.internal.store.Util; import java.util.List; import java.util.stream.Collectors; import java.util.stream.StreamSupport; +import static org.ehcache.clustered.common.internal.util.ChainBuilder.chainFromList; + /** * This message is sent by the Active Entity to Passive Entity. */ @@ -49,13 +50,13 @@ public ChainReplicationMessage(long key, Chain chain, long transactionId, long o } private Chain dropLastElement(Chain chain) { - if (!chain.isEmpty()) { + if (chain.isEmpty()) { + return chain; + } else { List elements = StreamSupport.stream(chain.spliterator(), false) .collect(Collectors.toList()); elements.remove(elements.size() - 1); // remove last - return Util.getChain(elements); - } else { - return chain; + return chainFromList(elements); } } diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/internal/messages/PassiveReplicationMessageCodec.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/internal/messages/PassiveReplicationMessageCodec.java similarity index 76% rename from clustered/server/src/main/java/org/ehcache/clustered/server/internal/messages/PassiveReplicationMessageCodec.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/internal/messages/PassiveReplicationMessageCodec.java index 3d902a6610..4099ea4ddb 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/internal/messages/PassiveReplicationMessageCodec.java +++ b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/internal/messages/PassiveReplicationMessageCodec.java @@ -19,19 +19,18 @@ import org.ehcache.clustered.common.internal.messages.ChainCodec; import org.ehcache.clustered.common.internal.messages.EhcacheEntityMessage; import org.ehcache.clustered.common.internal.messages.EhcacheMessageType; -import org.ehcache.clustered.common.internal.messages.MessageCodecUtils; import org.ehcache.clustered.common.internal.store.Chain; import org.terracotta.runnel.Struct; import org.terracotta.runnel.decoding.StructDecoder; -import org.terracotta.runnel.encoding.StructEncoder; import java.nio.ByteBuffer; -import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.EHCACHE_MESSAGE_TYPES_ENUM_MAPPING; -import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.MESSAGE_TYPE_FIELD_INDEX; -import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.MESSAGE_TYPE_FIELD_NAME; +import static org.ehcache.clustered.common.internal.messages.BaseCodec.EHCACHE_MESSAGE_TYPES_ENUM_MAPPING; +import static org.ehcache.clustered.common.internal.messages.BaseCodec.MESSAGE_TYPE_FIELD_INDEX; +import static org.ehcache.clustered.common.internal.messages.BaseCodec.MESSAGE_TYPE_FIELD_NAME; import static org.ehcache.clustered.common.internal.messages.MessageCodecUtils.KEY_FIELD; import static org.ehcache.clustered.common.internal.messages.MessageCodecUtils.SERVER_STORE_NAME_FIELD; +import static org.ehcache.clustered.common.internal.messages.MessageCodecUtils.encodeMandatoryFields; import static org.terracotta.runnel.StructBuilder.newStructBuilder; public class PassiveReplicationMessageCodec { @@ -60,14 +59,7 @@ public class PassiveReplicationMessageCodec { .int64(KEY_FIELD, 20) .build(); - private final MessageCodecUtils messageCodecUtils; - - public PassiveReplicationMessageCodec() { - this.messageCodecUtils = new MessageCodecUtils(); - } - public byte[] encode(PassiveReplicationMessage message) { - switch (message.getMessageType()) { case CHAIN_REPLICATION_OP: return encodeChainReplicationMessage((PassiveReplicationMessage.ChainReplicationMessage) message); @@ -81,34 +73,24 @@ public byte[] encode(PassiveReplicationMessage message) { } private byte[] encodeInvalidationCompleteMessage(PassiveReplicationMessage.InvalidationCompleteMessage message) { - StructEncoder encoder = INVALIDATION_COMPLETE_STRUCT.encoder(); - - encoder.enm(MESSAGE_TYPE_FIELD_NAME, message.getMessageType()) - .int64(KEY_FIELD, message.getKey()); - - return encoder.encode().array(); + return encodeMandatoryFields(INVALIDATION_COMPLETE_STRUCT, message) + .int64(KEY_FIELD, message.getKey()) + .encode().array(); } private byte[] encodeClearInvalidationCompleteMessage(PassiveReplicationMessage.ClearInvalidationCompleteMessage message) { - StructEncoder encoder = CLEAR_INVALIDATION_COMPLETE_STRUCT.encoder(); - - encoder.enm(MESSAGE_TYPE_FIELD_NAME, message.getMessageType()); - - return encoder.encode().array(); + return encodeMandatoryFields(CLEAR_INVALIDATION_COMPLETE_STRUCT, message) + .encode().array(); } private byte[] encodeChainReplicationMessage(PassiveReplicationMessage.ChainReplicationMessage message) { - StructEncoder encoder = CHAIN_REPLICATION_STRUCT.encoder(); - - messageCodecUtils.encodeMandatoryFields(encoder, message); - - encoder.int64(TRANSACTION_ID_FIELD, message.getTransactionId()); - encoder.int64(CLIENT_ID_FIELD, message.getClientId()); - encoder.int64(OLDEST_TRANSACTION_ID_FIELD, message.getOldestTransactionId()); - encoder.int64(KEY_FIELD, message.getKey()); - encoder.struct(CHAIN_FIELD, message.getChain(), ChainCodec::encode); - - return encoder.encode().array(); + return encodeMandatoryFields(CHAIN_REPLICATION_STRUCT, message) + .int64(TRANSACTION_ID_FIELD, message.getTransactionId()) + .int64(CLIENT_ID_FIELD, message.getClientId()) + .int64(OLDEST_TRANSACTION_ID_FIELD, message.getOldestTransactionId()) + .int64(KEY_FIELD, message.getKey()) + .struct(CHAIN_FIELD, message.getChain(), ChainCodec::encodeChain) + .encode().array(); } public EhcacheEntityMessage decode(EhcacheMessageType messageType, ByteBuffer messageBuffer) { @@ -146,7 +128,7 @@ private PassiveReplicationMessage.ChainReplicationMessage decodeChainReplication Long oldestTransactionId = decoder.int64(OLDEST_TRANSACTION_ID_FIELD); Long key = decoder.int64(KEY_FIELD); - Chain chain = ChainCodec.decode(decoder.struct(CHAIN_FIELD)); + Chain chain = ChainCodec.decodeChain(decoder.struct(CHAIN_FIELD)); return new PassiveReplicationMessage.ChainReplicationMessage(key, chain, currentTransactionId, oldestTransactionId, clientId); } diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/management/ClusterTierManagement.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/management/ClusterTierManagement.java similarity index 97% rename from clustered/server/src/main/java/org/ehcache/clustered/server/management/ClusterTierManagement.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/management/ClusterTierManagement.java index 4d75e11b77..854376401f 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/management/ClusterTierManagement.java +++ b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/management/ClusterTierManagement.java @@ -53,7 +53,7 @@ public ClusterTierManagement(ServiceRegistry services, EhcacheStateService ehcac if (managementRegistry != null) { // expose settings about server stores - managementRegistry.addManagementProvider(new ServerStoreSettingsManagementProvider(clusterTierManagerIdentifier)); + managementRegistry.addManagementProvider(new ServerStoreSettingsManagementProvider(clusterTierManagerIdentifier, ehcacheStateService.getDefaultServerResource())); // expose settings about pools managementRegistry.addManagementProvider(new PoolSettingsManagementProvider()); diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/management/ClusterTierManagerBinding.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/management/ClusterTierManagerBinding.java similarity index 100% rename from clustered/server/src/main/java/org/ehcache/clustered/server/management/ClusterTierManagerBinding.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/management/ClusterTierManagerBinding.java diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/management/ClusterTierManagerSettingsManagementProvider.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/management/ClusterTierManagerSettingsManagementProvider.java similarity index 100% rename from clustered/server/src/main/java/org/ehcache/clustered/server/management/ClusterTierManagerSettingsManagementProvider.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/management/ClusterTierManagerSettingsManagementProvider.java diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/management/Management.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/management/Management.java similarity index 100% rename from clustered/server/src/main/java/org/ehcache/clustered/server/management/Management.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/management/Management.java diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/management/Notification.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/management/Notification.java similarity index 100% rename from clustered/server/src/main/java/org/ehcache/clustered/server/management/Notification.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/management/Notification.java diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/management/PoolBinding.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/management/PoolBinding.java similarity index 100% rename from clustered/server/src/main/java/org/ehcache/clustered/server/management/PoolBinding.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/management/PoolBinding.java diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/management/PoolSettingsManagementProvider.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/management/PoolSettingsManagementProvider.java similarity index 100% rename from clustered/server/src/main/java/org/ehcache/clustered/server/management/PoolSettingsManagementProvider.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/management/PoolSettingsManagementProvider.java diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/management/PoolStatisticsManagementProvider.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/management/PoolStatisticsManagementProvider.java similarity index 98% rename from clustered/server/src/main/java/org/ehcache/clustered/server/management/PoolStatisticsManagementProvider.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/management/PoolStatisticsManagementProvider.java index 47dbf004b8..867674a8a5 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/management/PoolStatisticsManagementProvider.java +++ b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/management/PoolStatisticsManagementProvider.java @@ -17,11 +17,11 @@ import org.ehcache.clustered.server.state.EhcacheStateService; import org.terracotta.management.model.context.Context; +import org.terracotta.management.model.stats.StatisticRegistry; import org.terracotta.management.registry.Named; import org.terracotta.management.registry.RequiredContext; import org.terracotta.management.registry.ExposedObject; import org.terracotta.management.registry.collect.StatisticProvider; -import org.terracotta.management.registry.collect.StatisticRegistry; import org.terracotta.management.service.monitoring.registry.provider.AbstractExposedStatistics; import org.terracotta.management.service.monitoring.registry.provider.AbstractStatisticsManagementProvider; diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/management/ServerStoreBinding.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/management/ServerStoreBinding.java similarity index 100% rename from clustered/server/src/main/java/org/ehcache/clustered/server/management/ServerStoreBinding.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/management/ServerStoreBinding.java diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/management/ServerStoreSettingsManagementProvider.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/management/ServerStoreSettingsManagementProvider.java similarity index 66% rename from clustered/server/src/main/java/org/ehcache/clustered/server/management/ServerStoreSettingsManagementProvider.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/management/ServerStoreSettingsManagementProvider.java index 88b953730c..fbad7fd5e7 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/management/ServerStoreSettingsManagementProvider.java +++ b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/management/ServerStoreSettingsManagementProvider.java @@ -16,12 +16,14 @@ package org.ehcache.clustered.server.management; import org.ehcache.clustered.common.PoolAllocation; +import org.ehcache.clustered.server.ServerSideServerStore; import org.terracotta.management.model.capabilities.descriptors.Descriptor; import org.terracotta.management.model.capabilities.descriptors.Settings; import org.terracotta.management.model.context.Context; import org.terracotta.management.registry.Named; import org.terracotta.management.registry.RequiredContext; import org.terracotta.management.service.monitoring.registry.provider.AliasBindingManagementProvider; +import org.terracotta.offheapstore.MapInternals; import java.util.ArrayList; import java.util.Collection; @@ -32,10 +34,12 @@ class ServerStoreSettingsManagementProvider extends AliasBindingManagementProvider { private final String clusterTierManagerIdentifier; + private final String defaultServerResource; - ServerStoreSettingsManagementProvider(String clusterTierManagerIdentifier) { + ServerStoreSettingsManagementProvider(String clusterTierManagerIdentifier, String defaultServerResource) { super(ServerStoreBinding.class); this.clusterTierManagerIdentifier = clusterTierManagerIdentifier; + this.defaultServerResource = defaultServerResource; } @Override @@ -44,12 +48,16 @@ public Collection getDescriptors() { descriptors.add(new Settings() .set("type", getCapabilityName()) .set("clusterTierManager", clusterTierManagerIdentifier) - .set("time", System.currentTimeMillis())); + .set("time", System.currentTimeMillis()) + .set("defaultServerResource", defaultServerResource)); return descriptors; } @Override protected ExposedServerStoreBinding internalWrap(Context context, ServerStoreBinding managedObject) { + if (defaultServerResource != null) { + context = context.with("defaultServerResource", defaultServerResource); + } return new ExposedServerStoreBinding(context, managedObject); } @@ -66,19 +74,24 @@ public Collection getDescriptors() { Settings getSettings() { // names taken from ServerStoreConfiguration.isCompatible() - PoolAllocation poolAllocation = getBinding().getValue().getStoreConfiguration().getPoolAllocation(); + ServerSideServerStore value = getBinding().getValue(); + PoolAllocation poolAllocation = value.getStoreConfiguration().getPoolAllocation(); Settings settings = new Settings(getContext()) - .set("resourcePoolType", poolAllocation.getClass().getSimpleName().toLowerCase()) - .set("allocatedMemoryAtTime", getBinding().getValue().getAllocatedMemory()) - .set("tableCapacityAtTime", getBinding().getValue().getTableCapacity()) - .set("vitalMemoryAtTime", getBinding().getValue().getVitalMemory()) - .set("longSizeAtTime", getBinding().getValue().getSize()) - .set("dataAllocatedMemoryAtTime", getBinding().getValue().getDataAllocatedMemory()) - .set("dataOccupiedMemoryAtTime", getBinding().getValue().getDataOccupiedMemory()) - .set("dataSizeAtTime", getBinding().getValue().getDataSize()) - .set("dataVitalMemoryAtTime", getBinding().getValue().getDataVitalMemory()); + .set("resourcePoolType", poolAllocation.getClass().getSimpleName().toLowerCase()); + if (value instanceof MapInternals) { + MapInternals internals = (MapInternals) value; + settings.set("allocatedMemoryAtTime", internals.getAllocatedMemory()) + .set("tableCapacityAtTime", internals.getTableCapacity()) + .set("vitalMemoryAtTime", internals.getVitalMemory()) + .set("longSizeAtTime", internals.getSize()) + .set("dataAllocatedMemoryAtTime", internals.getDataAllocatedMemory()) + .set("dataOccupiedMemoryAtTime", internals.getDataOccupiedMemory()) + .set("dataSizeAtTime", internals.getDataSize()) + .set("dataVitalMemoryAtTime", internals.getDataVitalMemory()); + } if (poolAllocation instanceof PoolAllocation.DedicatedPoolAllocation) { - settings.set("resourcePoolDedicatedResourceName", ((PoolAllocation.DedicatedPoolAllocation) poolAllocation).getResourceName()); + String resourceName = ((PoolAllocation.DedicatedPoolAllocation) poolAllocation).getResourceName(); + settings.set("resourcePoolDedicatedResourceName", resourceName != null ? resourceName : settings.getString("defaultServerResource")); settings.set("resourcePoolDedicatedSize", ((PoolAllocation.DedicatedPoolAllocation) poolAllocation).getSize()); } else if (poolAllocation instanceof PoolAllocation.SharedPoolAllocation) { settings.set("resourcePoolSharedPoolName", ((PoolAllocation.SharedPoolAllocation) poolAllocation).getResourcePoolName()); diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/management/ServerStoreStatisticsManagementProvider.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/management/ServerStoreStatisticsManagementProvider.java similarity index 98% rename from clustered/server/src/main/java/org/ehcache/clustered/server/management/ServerStoreStatisticsManagementProvider.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/management/ServerStoreStatisticsManagementProvider.java index 0d67ccaaa2..c5696c8016 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/management/ServerStoreStatisticsManagementProvider.java +++ b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/management/ServerStoreStatisticsManagementProvider.java @@ -16,10 +16,10 @@ package org.ehcache.clustered.server.management; import org.terracotta.management.model.context.Context; +import org.terracotta.management.model.stats.StatisticRegistry; import org.terracotta.management.registry.Named; import org.terracotta.management.registry.RequiredContext; import org.terracotta.management.registry.collect.StatisticProvider; -import org.terracotta.management.registry.collect.StatisticRegistry; import org.terracotta.management.service.monitoring.registry.provider.AbstractExposedStatistics; import org.terracotta.management.service.monitoring.registry.provider.AbstractStatisticsManagementProvider; diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/store/ClusterTierActiveEntity.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/store/ClusterTierActiveEntity.java similarity index 77% rename from clustered/server/src/main/java/org/ehcache/clustered/server/store/ClusterTierActiveEntity.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/store/ClusterTierActiveEntity.java index 8b1db23bc3..3519f5bbd0 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/store/ClusterTierActiveEntity.java +++ b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/store/ClusterTierActiveEntity.java @@ -26,6 +26,7 @@ import org.ehcache.clustered.common.internal.messages.EhcacheEntityMessage; import org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse; import org.ehcache.clustered.common.internal.messages.EhcacheMessageType; +import org.ehcache.clustered.server.internal.messages.EhcacheMessageTrackerCatchup; import org.ehcache.clustered.common.internal.messages.EhcacheOperationMessage; import org.ehcache.clustered.common.internal.messages.LifecycleMessage; import org.ehcache.clustered.common.internal.messages.LifecycleMessage.ValidateServerStore; @@ -34,7 +35,11 @@ import org.ehcache.clustered.common.internal.messages.ServerStoreOpMessage.AppendMessage; import org.ehcache.clustered.common.internal.messages.ServerStoreOpMessage.ClientInvalidationAck; import org.ehcache.clustered.common.internal.messages.ServerStoreOpMessage.ClientInvalidationAllAck; +import org.ehcache.clustered.common.internal.messages.ServerStoreOpMessage.EnableEventListenerMessage; import org.ehcache.clustered.common.internal.messages.ServerStoreOpMessage.GetMessage; +import org.ehcache.clustered.common.internal.messages.ServerStoreOpMessage.IteratorAdvanceMessage; +import org.ehcache.clustered.common.internal.messages.ServerStoreOpMessage.IteratorCloseMessage; +import org.ehcache.clustered.common.internal.messages.ServerStoreOpMessage.IteratorOpenMessage; import org.ehcache.clustered.common.internal.messages.ServerStoreOpMessage.KeyBasedServerStoreOpMessage; import org.ehcache.clustered.common.internal.messages.ServerStoreOpMessage.LockMessage; import org.ehcache.clustered.common.internal.messages.ServerStoreOpMessage.ReplaceAtHeadMessage; @@ -47,12 +52,14 @@ import org.ehcache.clustered.server.KeySegmentMapper; import org.ehcache.clustered.server.ServerSideServerStore; import org.ehcache.clustered.server.ServerStoreCompatibility; +import org.ehcache.clustered.server.ServerStoreEventListener; import org.ehcache.clustered.server.internal.messages.EhcacheDataSyncMessage; import org.ehcache.clustered.server.internal.messages.EhcacheMessageTrackerMessage; import org.ehcache.clustered.server.internal.messages.PassiveReplicationMessage; import org.ehcache.clustered.server.internal.messages.PassiveReplicationMessage.ClearInvalidationCompleteMessage; import org.ehcache.clustered.server.internal.messages.PassiveReplicationMessage.InvalidationCompleteMessage; import org.ehcache.clustered.server.management.ClusterTierManagement; +import org.ehcache.clustered.server.offheap.InternalChain; import org.ehcache.clustered.server.state.EhcacheStateContext; import org.ehcache.clustered.server.state.EhcacheStateService; import org.ehcache.clustered.server.state.InvalidationTracker; @@ -77,6 +84,8 @@ import org.terracotta.entity.ServiceRegistry; import org.terracotta.entity.StateDumpCollector; +import java.nio.ByteBuffer; +import java.util.AbstractMap; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -84,6 +93,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; @@ -95,9 +105,10 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; +import static java.util.Collections.emptyMap; import static java.util.stream.Collectors.toMap; import static java.util.stream.Collectors.toSet; import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.allInvalidationDone; @@ -106,16 +117,18 @@ import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.failure; import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.getResponse; import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.hashInvalidationDone; +import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.iteratorBatchResponse; import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.lockFailure; import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.lockSuccess; import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.resolveRequest; +import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.serverAppend; import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.serverInvalidateHash; import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.success; import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.isLifecycleMessage; import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.isStateRepoOperationMessage; import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.isStoreOperationMessage; import static org.ehcache.clustered.server.ConcurrencyStrategies.DEFAULT_KEY; -import static org.ehcache.clustered.server.ConcurrencyStrategies.clusterTierConcurrency; +import static org.ehcache.clustered.server.ConcurrencyStrategies.DefaultConcurrencyStrategy.TRACKER_SYNC_KEY; /** * ClusterTierActiveEntity @@ -134,9 +147,7 @@ public class ClusterTierActiveEntity implements ActiveServerEntity()); + private final Executor syncGetsExecutor; private final String storeIdentifier; private final ServerStoreConfiguration configuration; @@ -145,15 +156,14 @@ public class ClusterTierActiveEntity implements ActiveServerEntity messageHandler; private final IEntityMessenger entityMessenger; private final ServerStoreCompatibility storeCompatibility = new ServerStoreCompatibility(); - private final AtomicBoolean reconnectComplete = new AtomicBoolean(true); private final AtomicInteger invalidationIdGenerator = new AtomicInteger(); private final ConcurrentMap clientsWaitingForInvalidation = new ConcurrentHashMap<>(); private final ReconnectMessageCodec reconnectMessageCodec = new ReconnectMessageCodec(); private final ClusterTierManagement management; private final String managerIdentifier; - private final Object inflightInvalidationsMutex = new Object(); - private volatile List inflightInvalidations; + private final Set eventListeners = new HashSet<>(); // accesses are synchronized on eventListeners itself private final Map connectedClients = new ConcurrentHashMap<>(); + private final Map>>> liveIterators = new ConcurrentHashMap<>(); private final int chainCompactionLimit; private final ServerLockManager lockManager; @@ -162,7 +172,7 @@ public class ClusterTierActiveEntity implements ActiveServerEntity(IEntityMessenger.class)); messageHandler = registry.getService(new OOOMessageHandlerConfiguration<>(managerIdentifier + "###" + storeIdentifier, - ClusterTierActiveEntity::isTrackedMessage, defaultMapper.getSegments() + 1, new MessageToTrackerSegmentFunction(clusterTierConcurrency(defaultMapper)))); + ClusterTierActiveEntity::isTrackedMessage)); } catch (ServiceException e) { throw new ConfigurationException("Unable to retrieve service: " + e.getMessage()); } @@ -188,6 +198,7 @@ public ClusterTierActiveEntity(ServiceRegistry registry, ClusterTierEntityConfig } else { lockManager = new NoopLockManager(); } + syncGetsExecutor = getSyncExecutor; } static boolean isTrackedMessage(EhcacheEntityMessage msg) { @@ -216,34 +227,43 @@ public void addStateTo(StateDumpCollector dump) { @Override public void createNew() throws ConfigurationException { ServerSideServerStore store = stateService.createStore(storeIdentifier, configuration, true); - store.setEvictionListener(this::invalidateHashAfterEviction); + store.setEventListener(new Listener()); management.entityCreated(); } - List getInflightInvalidations() { - return this.inflightInvalidations; - } - @Override public void loadExisting() { - inflightInvalidations = new ArrayList<>(); - if (!isStrong()) { - LOGGER.debug("Preparing for handling inflight invalidations"); - addInflightInvalidationsForEventualCaches(); - } - stateService.loadStore(storeIdentifier, configuration).setEvictionListener(this::invalidateHashAfterEviction); - reconnectComplete.set(false); + stateService.loadStore(storeIdentifier, configuration).setEventListener(new Listener()); management.entityPromotionCompleted(); } - private void invalidateHashAfterEviction(long key) { - Set clientsToInvalidate = new HashSet<>(getValidatedClients()); - for (ClientDescriptor clientDescriptorThatHasToInvalidate : clientsToInvalidate) { - LOGGER.debug("SERVER: eviction happened; asking client {} to invalidate hash {} from cache {}", clientDescriptorThatHasToInvalidate, key, storeIdentifier); - try { - clientCommunicator.sendNoResponse(clientDescriptorThatHasToInvalidate, serverInvalidateHash(key)); - } catch (MessageCodecException mce) { - throw new AssertionError("Codec error", mce); + private class Listener implements ServerStoreEventListener { + @Override + public void onAppend(Chain beforeAppend, ByteBuffer appended) { + Set clients = new HashSet<>(getValidatedClients()); + for (ClientDescriptor clientDescriptor : clients) { + LOGGER.debug("SERVER: append happened in cache {}; notifying client {} ", storeIdentifier, clientDescriptor); + try { + clientCommunicator.sendNoResponse(clientDescriptor, serverAppend(appended.duplicate(), beforeAppend)); + } catch (MessageCodecException mce) { + throw new AssertionError("Codec error", mce); + } + } + } + @Override + public void onEviction(long key, InternalChain evictedChain) { + Set clientsToInvalidate = new HashSet<>(getValidatedClients()); + if (!clientsToInvalidate.isEmpty()) { + Chain detachedChain = evictedChain.detach(); + for (ClientDescriptor clientDescriptorThatHasToInvalidate : clientsToInvalidate) { + LOGGER.debug("SERVER: eviction happened; asking client {} to invalidate hash {} from cache {}", clientDescriptorThatHasToInvalidate, key, storeIdentifier); + try { + boolean eventsEnabledForClient = isEventsEnabledFor(clientDescriptorThatHasToInvalidate); + clientCommunicator.sendNoResponse(clientDescriptorThatHasToInvalidate, serverInvalidateHash(key, eventsEnabledForClient ? detachedChain : null)); + } catch (MessageCodecException mce) { + throw new AssertionError("Codec error", mce); + } + } } } } @@ -273,6 +293,10 @@ public void disconnected(ClientDescriptor clientDescriptor) { lockManager.sweepLocksForClient(clientDescriptor, configuration.isWriteBehindConfigured() ? null : heldKeys -> heldKeys.forEach(stateService.getStore(storeIdentifier)::remove)); + liveIterators.remove(clientDescriptor); + + removeEventListener(clientDescriptor, stateService.getStore(storeIdentifier)); + connectedClients.remove(clientDescriptor); } @@ -347,32 +371,13 @@ private EhcacheEntityResponse invokeServerStoreOperation(InvokeContext context, throw new LifecycleException("cluster tier does not exist : '" + storeIdentifier + "'"); } - if (inflightInvalidations != null) { - synchronized (inflightInvalidationsMutex) { - // This logic totally counts on the fact that invokes will only happen - // after all handleReconnects are done, else this is flawed. - if (inflightInvalidations != null) { - List tmpInflightInvalidations = this.inflightInvalidations; - this.inflightInvalidations = null; - LOGGER.debug("Stalling all operations for cluster tier {} for firing inflight invalidations again.", storeIdentifier); - tmpInflightInvalidations.forEach(invalidationState -> { - if (invalidationState.isClearInProgress()) { - invalidateAll(invalidationState.getClientDescriptor()); - } - invalidationState.getInvalidationsInProgress() - .forEach(hashInvalidationToBeResent -> invalidateHashForClient(invalidationState.getClientDescriptor(), hashInvalidationToBeResent)); - }); - } - } - } - switch (message.getMessageType()) { case GET_STORE: { GetMessage getMessage = (GetMessage) message; try { return getResponse(cacheStore.get(getMessage.getKey())); } catch (TimeoutException e) { - throw new AssertionError("Server side store is not expected to throw timeout exception"); + throw new AssertionError("Server side store is not expected to throw timeout exception", e); } } case APPEND: { @@ -389,7 +394,7 @@ private EhcacheEntityResponse invokeServerStoreOperation(InvokeContext context, cacheStore.append(key, appendMessage.getPayload()); newChain = cacheStore.get(key); } catch (TimeoutException e) { - throw new AssertionError("Server side store is not expected to throw timeout exception"); + throw new AssertionError("Server side store is not expected to throw timeout exception", e); } sendMessageToSelfAndDeferRetirement(activeInvokeContext, appendMessage, newChain); invalidateHashForClient(clientDescriptor, key); @@ -416,7 +421,7 @@ private EhcacheEntityResponse invokeServerStoreOperation(InvokeContext context, result = cacheStore.getAndAppend(getAndAppendMessage.getKey(), getAndAppendMessage.getPayload()); newChain = cacheStore.get(getAndAppendMessage.getKey()); } catch (TimeoutException e) { - throw new AssertionError("Server side store is not expected to throw timeout exception"); + throw new AssertionError("Server side store is not expected to throw timeout exception", e); } sendMessageToSelfAndDeferRetirement(activeInvokeContext, getAndAppendMessage, newChain); LOGGER.debug("Send invalidations for key {}", getAndAppendMessage.getKey()); @@ -447,7 +452,7 @@ private EhcacheEntityResponse invokeServerStoreOperation(InvokeContext context, try { cacheStore.clear(); } catch (TimeoutException e) { - throw new AssertionError("Server side store is not expected to throw timeout exception"); + throw new AssertionError("Server side store is not expected to throw timeout exception", e); } InvalidationTracker invalidationTracker = stateService.getInvalidationTracker(storeIdentifier); @@ -464,7 +469,7 @@ private EhcacheEntityResponse invokeServerStoreOperation(InvokeContext context, Chain chain = cacheStore.get(lockMessage.getHash()); return lockSuccess(chain); } catch (TimeoutException e) { - throw new AssertionError("Server side store is not expected to throw timeout exception"); + throw new AssertionError("Server side store is not expected to throw timeout exception", e); } } else { return lockFailure(); @@ -475,11 +480,118 @@ private EhcacheEntityResponse invokeServerStoreOperation(InvokeContext context, lockManager.unlock(unlockMessage.getHash()); return success(); } + case ITERATOR_OPEN: { + IteratorOpenMessage iteratorOpenMessage = (IteratorOpenMessage) message; + try { + Iterator> iterator = cacheStore.iterator(); + List> batch = iteratorBatch(iterator, iteratorOpenMessage.getBatchSize()); + + if (iterator.hasNext()) { + Map>> liveIterators = this.liveIterators.computeIfAbsent(clientDescriptor, client -> new ConcurrentHashMap<>()); + UUID id; + do { + id = UUID.randomUUID(); + } while (liveIterators.putIfAbsent(id, iterator) != null); + return iteratorBatchResponse(id, batch, false); + } else { + return iteratorBatchResponse(UUID.randomUUID(), batch, true); + } + } catch (TimeoutException e) { + throw new AssertionError("Server side store is not expected to throw timeout exception", e); + } + } + case ITERATOR_CLOSE: { + IteratorCloseMessage iteratorCloseMessage = (IteratorCloseMessage) message; + liveIterators.computeIfPresent(clientDescriptor, (client, iterators) -> { + iterators.remove(iteratorCloseMessage.getIdentity()); + if (iterators.isEmpty()) { + return null; + } else { + return iterators; + } + }); + return success(); + } + case ITERATOR_ADVANCE: { + IteratorAdvanceMessage iteratorAdvanceMessage = (IteratorAdvanceMessage) message; + UUID id = iteratorAdvanceMessage.getIdentity(); + + Iterator> iterator = liveIterators.getOrDefault(clientDescriptor, emptyMap()).get(id); + if (iterator == null) { + return failure(new InvalidOperationException("Referenced iterator is already closed (or never existed)")); + } else { + List> batch = iteratorBatch(iterator, iteratorAdvanceMessage.getBatchSize()); + if (iterator.hasNext()) { + return iteratorBatchResponse(id, batch, false); + } else { + liveIterators.computeIfPresent(clientDescriptor, (client, iterators) -> { + iterators.remove(id); + return iterators.isEmpty() ? null : iterators; + }); + return iteratorBatchResponse(id, batch, true); + } + } + } + case ENABLE_EVENT_LISTENER: { + // we need to keep a count of how many clients have registered as events listeners + // as we want to disable events from the store once all listeners are gone + // so we need to keep all requesting client descriptors in a set so that duplicate + // registrations from a single client are ignored + EnableEventListenerMessage enableEventListenerMessage = (EnableEventListenerMessage) message; + if (enableEventListenerMessage.isEnable()) { + addEventListener(clientDescriptor, cacheStore); + } else { + removeEventListener(clientDescriptor, cacheStore); + } + return success(); + } default: throw new AssertionError("Unsupported ServerStore operation : " + message); } } + private void addEventListener(ClientDescriptor clientDescriptor, ServerSideServerStore cacheStore) { + synchronized (eventListeners) { + if (eventListeners.add(clientDescriptor)) { + cacheStore.enableEvents(true); + } + } + } + + private void removeEventListener(ClientDescriptor clientDescriptor, ServerSideServerStore cacheStore) { + synchronized (eventListeners) { + if (eventListeners.remove(clientDescriptor) && eventListeners.isEmpty()) { + cacheStore.enableEvents(false); + } + } + } + + private boolean isEventsEnabledFor(ClientDescriptor clientDescriptor) { + synchronized (eventListeners) { + return eventListeners.contains(clientDescriptor); + } + } + + // for testing + Set getEventListeners() { + synchronized (eventListeners) { + return new HashSet<>(eventListeners); + } + } + + private List> iteratorBatch(Iterator> iterator, int batchSize) { + List> chains = new ArrayList<>(); + int size = 0; + while (iterator.hasNext() && size < batchSize && size >= 0) { + Map.Entry nextChain = iterator.next(); + chains.add(new AbstractMap.SimpleImmutableEntry<>(nextChain.getKey(), nextChain.getValue())); + for (Element e: nextChain.getValue()) { + size += e.getPayload().remaining(); + } + } + return chains; + } + private void invalidateAll(ClientDescriptor originatingClientDescriptor) { int invalidationId = invalidationIdGenerator.getAndIncrement(); Set clientsToInvalidate = new HashSet<>(getValidatedClients()); @@ -553,7 +665,9 @@ private void clientInvalidated(ClientDescriptor clientDescriptor, int invalidati private void invalidateHashForClient(ClientDescriptor originatingClientDescriptor, long key) { int invalidationId = invalidationIdGenerator.getAndIncrement(); - Set clientsToInvalidate = new HashSet<>(getValidatedClients()); + Set validatedClients = getValidatedClients(); + Set clientsToInvalidate = ConcurrentHashMap.newKeySet(validatedClients.size()); + clientsToInvalidate.addAll(validatedClients); if (originatingClientDescriptor != null) { clientsToInvalidate.remove(originatingClientDescriptor); } @@ -603,14 +717,6 @@ private void sendMessageToSelfAndDeferRetirement(ActiveInvokeContext { - if (inflightInvalidations == null) { - throw new AssertionError("Load existing was not invoked before handleReconnect"); - } + try { + this.entityMessenger.messageSelf(new EhcacheMessageTrackerCatchup(this.messageHandler.getRecordedMessages().filter(m->m.getRequest() != null).collect(Collectors.toList()))); + } catch (MessageCodecException mce) { + throw new AssertionError("Codec error", mce); + } - ClusterTierReconnectMessage reconnectMessage = reconnectMessageCodec.decode(bytes); - ServerSideServerStore serverStore = stateService.getStore(storeIdentifier); - addInflightInvalidationsForStrongCache(clientDescriptor, reconnectMessage, serverStore); - lockManager.createLockStateAfterFailover(clientDescriptor, reconnectMessage.getLocksHeld()); + List inflightInvalidations = new ArrayList<>(); - LOGGER.info("Client '{}' successfully reconnected to newly promoted ACTIVE after failover.", clientDescriptor); + InvalidationTracker invalidationTracker = stateService.getInvalidationTracker(storeIdentifier); + if (invalidationTracker != null) { + inflightInvalidations.add(new InvalidationTuple(null, invalidationTracker.getTrackedKeys(), invalidationTracker.isClearInProgress())); + invalidationTracker.clear(); + } - connectedClients.put(clientDescriptor, Boolean.TRUE); - }; - } + return new ReconnectHandler() { - private void addInflightInvalidationsForStrongCache(ClientDescriptor clientDescriptor, ClusterTierReconnectMessage reconnectMessage, ServerSideServerStore serverStore) { - if (serverStore.getStoreConfiguration().getConsistency().equals(Consistency.STRONG)) { - Set invalidationsInProgress = reconnectMessage.getInvalidationsInProgress(); - LOGGER.debug("Number of Inflight Invalidations from client ID {} for cache {} is {}.", clientDescriptor.getSourceId().toLong(), storeIdentifier, invalidationsInProgress - .size()); - inflightInvalidations.add(new InvalidationTuple(clientDescriptor, invalidationsInProgress, reconnectMessage.isClearInProgress())); - } + @Override + public void handleReconnect(ClientDescriptor clientDescriptor, byte[] bytes) { + ClusterTierReconnectMessage reconnectMessage = reconnectMessageCodec.decode(bytes); + ServerSideServerStore serverStore = stateService.getStore(storeIdentifier); + addInflightInvalidationsForStrongCache(clientDescriptor, reconnectMessage, serverStore); + lockManager.createLockStateAfterFailover(clientDescriptor, reconnectMessage.getLocksHeld()); + if (reconnectMessage.isEventsEnabled()) { + addEventListener(clientDescriptor, serverStore); + } + + LOGGER.info("Client '{}' successfully reconnected to newly promoted ACTIVE after failover.", clientDescriptor); + + connectedClients.put(clientDescriptor, Boolean.TRUE); + } + + private void addInflightInvalidationsForStrongCache(ClientDescriptor clientDescriptor, ClusterTierReconnectMessage reconnectMessage, ServerSideServerStore serverStore) { + if (serverStore.getStoreConfiguration().getConsistency().equals(Consistency.STRONG)) { + Set invalidationsInProgress = reconnectMessage.getInvalidationsInProgress(); + LOGGER.debug("Number of Inflight Invalidations from client ID {} for cache {} is {}.", clientDescriptor.getSourceId().toLong(), storeIdentifier, invalidationsInProgress + .size()); + inflightInvalidations.add(new InvalidationTuple(clientDescriptor, invalidationsInProgress, reconnectMessage.isClearInProgress())); + } + } + + @Override + public void close() { + LOGGER.debug("Stalling all operations for cluster tier {} for firing inflight invalidations again.", storeIdentifier); + inflightInvalidations.forEach(invalidationState -> { + if (invalidationState.isClearInProgress()) { + invalidateAll(invalidationState.getClientDescriptor()); + } + invalidationState.getInvalidationsInProgress() + .forEach(hashInvalidationToBeResent -> invalidateHashForClient(invalidationState.getClientDescriptor(), hashInvalidationToBeResent)); + }); + } + }; } @Override @@ -648,13 +783,15 @@ public void synchronizeKeyToPassive(PassiveSynchronizationChannel messageQ = new SynchronousQueue<>(); int segmentId = concurrencyKey - DEFAULT_KEY - 1; Thread thisThread = Thread.currentThread(); CompletableFuture asyncGet = CompletableFuture.runAsync( - () -> doGetsForSync(segmentId, messageQ, syncChannel, thisThread), SYNC_GETS_EXECUTOR); + () -> doGetsForSync(segmentId, messageQ, syncChannel, thisThread), syncGetsExecutor); try { try { while (messageQ.take().execute()) ; @@ -674,8 +811,6 @@ public void synchronizeKeyToPassive(PassiveSynchronizationChannel syncChannel, int concurrencyKey) { + private void sendMessageTrackerReplication(PassiveSynchronizationChannel syncChannel) { Map> clientSourceIdTrackingMap = messageHandler.getTrackedClients() - .collect(toMap(ClientSourceId::toLong, clientSourceId -> messageHandler.getTrackedResponsesForSegment(concurrencyKey, clientSourceId))); + .collect(toMap(ClientSourceId::toLong, clientSourceId -> messageHandler.getRecordedMessages().filter(r->r.getClientSourceId().toLong() == clientSourceId.toLong()).collect(Collectors.toMap(rm->rm.getTransactionId(), rm->rm.getResponse())))); if (!clientSourceIdTrackingMap.isEmpty()) { - syncChannel.synchronizeToPassive(new EhcacheMessageTrackerMessage(concurrencyKey, clientSourceIdTrackingMap)); + syncChannel.synchronizeToPassive(new EhcacheMessageTrackerMessage(clientSourceIdTrackingMap)); } } @@ -832,14 +967,15 @@ public void destroy() { } catch (ClusterException e) { LOGGER.error("Failed to destroy server store - does not exist", e); } + messageHandler.destroy(); management.close(); } - Set getConnectedClients() { + protected Set getConnectedClients() { return connectedClients.keySet(); } - Set getValidatedClients() { + protected Set getValidatedClients() { return connectedClients.entrySet().stream().filter(Map.Entry::getValue).map(Map.Entry::getKey).collect(toSet()); } diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/store/ClusterTierDump.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/store/ClusterTierDump.java similarity index 100% rename from clustered/server/src/main/java/org/ehcache/clustered/server/store/ClusterTierDump.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/store/ClusterTierDump.java diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/store/ClusterTierPassiveEntity.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/store/ClusterTierPassiveEntity.java similarity index 84% rename from clustered/server/src/main/java/org/ehcache/clustered/server/store/ClusterTierPassiveEntity.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/store/ClusterTierPassiveEntity.java index 50a7428a39..c865aa4fe8 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/store/ClusterTierPassiveEntity.java +++ b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/store/ClusterTierPassiveEntity.java @@ -36,6 +36,7 @@ import org.ehcache.clustered.server.internal.messages.PassiveReplicationMessage; import org.ehcache.clustered.server.internal.messages.PassiveReplicationMessage.InvalidationCompleteMessage; import org.ehcache.clustered.server.internal.messages.PassiveReplicationMessage.ChainReplicationMessage; +import org.ehcache.clustered.server.internal.messages.EhcacheMessageTrackerCatchup; import org.ehcache.clustered.server.management.ClusterTierManagement; import org.ehcache.clustered.server.state.EhcacheStateContext; import org.ehcache.clustered.server.state.EhcacheStateService; @@ -44,6 +45,7 @@ import org.slf4j.LoggerFactory; import org.terracotta.client.message.tracker.OOOMessageHandler; import org.terracotta.client.message.tracker.OOOMessageHandlerConfiguration; +import org.terracotta.client.message.tracker.RecordedMessage; import org.terracotta.entity.ClientSourceId; import org.terracotta.entity.ConfigurationException; import org.terracotta.entity.EntityUserException; @@ -55,13 +57,14 @@ import org.terracotta.offheapstore.exceptions.OversizeMappingException; import java.util.concurrent.TimeoutException; +import java.util.stream.Stream; import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.getResponse; import static org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse.success; import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.isPassiveReplicationMessage; import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.isStateRepoOperationMessage; import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.isStoreOperationMessage; -import static org.ehcache.clustered.server.ConcurrencyStrategies.clusterTierConcurrency; + /** * ClusterTierPassiveEntity @@ -87,7 +90,7 @@ public ClusterTierPassiveEntity(ServiceRegistry registry, ClusterTierEntityConfi try { stateService = registry.getService(new EhcacheStoreStateServiceConfig(config.getManagerIdentifier(), defaultMapper)); messageHandler = registry.getService(new OOOMessageHandlerConfiguration<>(managerIdentifier + "###" + storeIdentifier, - ClusterTierActiveEntity::isTrackedMessage, defaultMapper.getSegments() + 1, new MessageToTrackerSegmentFunction(clusterTierConcurrency(defaultMapper)))); + ClusterTierActiveEntity::isTrackedMessage)); } catch (ServiceException e) { throw new ConfigurationException("Unable to retrieve service: " + e.getMessage()); } @@ -183,6 +186,8 @@ private EhcacheEntityResponse invokePassiveInternal(InvokeContext context, Ehcac } } else if (message instanceof EhcacheSyncMessage) { invokeSyncOperation(context, (EhcacheSyncMessage) message); + } else if (message instanceof EhcacheMessageTrackerCatchup) { + invokeCatchup(context, (EhcacheMessageTrackerCatchup) message); } else { throw new AssertionError("Unsupported EhcacheEntityMessage: " + message.getClass()); } @@ -191,6 +196,48 @@ private EhcacheEntityResponse invokePassiveInternal(InvokeContext context, Ehcac return success(); } + private void invokeCatchup(InvokeContext context, EhcacheMessageTrackerCatchup catchup) { + catchup.getTrackedMessages().forEach(r -> { + InvokeContext replay = new InvokeContext() { + @Override + public ClientSourceId getClientSource() { + return context.makeClientSourceId(r.getClientSourceId().toLong()); + } + + @Override + public long getCurrentTransactionId() { + return r.getTransactionId(); + } + + @Override + public long getOldestTransactionId() { + return context.getOldestTransactionId(); + } + + @Override + public boolean isValidClientInformation() { + return true; + } + + @Override + public ClientSourceId makeClientSourceId(long l) { + return context.makeClientSourceId(l); + } + + @Override + public int getConcurrencyKey() { + return 0; + } + }; + try { + messageHandler.invoke(replay, r.getRequest(), this::invokePassiveInternal); + } catch (EntityUserException use) { + // swallow any user exceptions here + } + }); + } + + @SuppressWarnings("deprecation") private void invokeSyncOperation(InvokeContext context, EhcacheSyncMessage message) { switch (message.getMessageType()) { case DATA: @@ -204,8 +251,31 @@ private void invokeSyncOperation(InvokeContext context, EhcacheSyncMessage messa break; case MESSAGE_TRACKER: EhcacheMessageTrackerMessage messageTrackerMessage = (EhcacheMessageTrackerMessage) message; - messageTrackerMessage.getTrackedMessages().forEach((key, value) -> - messageHandler.loadTrackedResponsesForSegment(messageTrackerMessage.getSegmentId(), context.makeClientSourceId(key), value)); + Stream> converted = + messageTrackerMessage.getTrackedMessages().entrySet().stream().flatMap(e -> e.getValue().entrySet().stream().map(v -> { + return new RecordedMessage() { + @Override + public ClientSourceId getClientSourceId() { + return context.makeClientSourceId(e.getKey()); + } + + @Override + public long getTransactionId() { + return v.getKey(); + } + + @Override + public EhcacheEntityMessage getRequest() { + return null; + } + + @Override + public EhcacheEntityResponse getResponse() { + return v.getValue(); + } + }; + })); + messageHandler.loadRecordedMessages(converted); break; default: throw new AssertionError("Unsupported Sync operation " + message.getMessageType()); @@ -306,6 +376,7 @@ public void destroy() { throw new AssertionError(e); } management.close(); + messageHandler.destroy(); } @Override diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/store/ClusterTierServerEntityService.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/store/ClusterTierServerEntityService.java similarity index 88% rename from clustered/server/src/main/java/org/ehcache/clustered/server/store/ClusterTierServerEntityService.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/store/ClusterTierServerEntityService.java index 0e6fff599c..b5a1ebd576 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/store/ClusterTierServerEntityService.java +++ b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/store/ClusterTierServerEntityService.java @@ -16,6 +16,12 @@ package org.ehcache.clustered.server.store; +import java.io.Closeable; +import java.io.IOException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import org.ehcache.clustered.common.internal.messages.CommonConfigCodec; import org.ehcache.clustered.common.internal.messages.ConfigCodec; import org.ehcache.clustered.common.internal.messages.EhcacheCodec; @@ -46,12 +52,15 @@ /** * ClusterTierServerEntityService */ -public class ClusterTierServerEntityService implements EntityServerService { +public class ClusterTierServerEntityService implements EntityServerService, Closeable { private static final long ENTITY_VERSION = 10L; private static final int DEFAULT_CONCURRENCY = 16; private static final KeySegmentMapper DEFAULT_MAPPER = new KeySegmentMapper(DEFAULT_CONCURRENCY); private static final ConfigCodec CONFIG_CODEC = new CommonConfigCodec(); + private static final int MAX_SYNC_CONCURRENCY = 1; + private final ExecutorService syncGets = new ThreadPoolExecutor(0, MAX_SYNC_CONCURRENCY, + 20, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); private final EntityConfigurationCodec configCodec = new EntityConfigurationCodec(CONFIG_CODEC); @@ -65,10 +74,15 @@ public boolean handlesEntityType(String typeName) { return typeName.equals("org.ehcache.clustered.client.internal.store.InternalClusterTierClientEntity"); } + @Override + public void close() throws IOException { + syncGets.shutdownNow(); + } + @Override public ClusterTierActiveEntity createActiveEntity(ServiceRegistry registry, byte[] configuration) throws ConfigurationException { ClusterTierEntityConfiguration clusterTierEntityConfiguration = configCodec.decodeClusteredStoreConfiguration(configuration); - return new ClusterTierActiveEntity(registry, clusterTierEntityConfiguration, DEFAULT_MAPPER); + return new ClusterTierActiveEntity(registry, clusterTierEntityConfiguration, DEFAULT_MAPPER, syncGets); } @Override diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/store/LockManagerImpl.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/store/LockManagerImpl.java similarity index 100% rename from clustered/server/src/main/java/org/ehcache/clustered/server/store/LockManagerImpl.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/store/LockManagerImpl.java diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/store/MessageToTrackerSegmentFunction.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/store/MessageToTrackerSegmentFunction.java similarity index 100% rename from clustered/server/src/main/java/org/ehcache/clustered/server/store/MessageToTrackerSegmentFunction.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/store/MessageToTrackerSegmentFunction.java diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/store/NoopLockManager.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/store/NoopLockManager.java similarity index 100% rename from clustered/server/src/main/java/org/ehcache/clustered/server/store/NoopLockManager.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/store/NoopLockManager.java diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/store/ServerLockManager.java b/clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/store/ServerLockManager.java similarity index 100% rename from clustered/server/src/main/java/org/ehcache/clustered/server/store/ServerLockManager.java rename to clustered/server/ehcache-entity/src/main/java/org/ehcache/clustered/server/store/ServerLockManager.java diff --git a/clustered/server/src/main/resources/META-INF/services/org.terracotta.entity.EntityServerService b/clustered/server/ehcache-entity/src/main/resources/META-INF/services/org.terracotta.entity.EntityServerService similarity index 100% rename from clustered/server/src/main/resources/META-INF/services/org.terracotta.entity.EntityServerService rename to clustered/server/ehcache-entity/src/main/resources/META-INF/services/org.terracotta.entity.EntityServerService diff --git a/clustered/server/src/test/java/org/ehcache/clustered/lock/server/VoltronReadWriteLockActiveEntityTest.java b/clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/lock/server/VoltronReadWriteLockActiveEntityTest.java similarity index 96% rename from clustered/server/src/test/java/org/ehcache/clustered/lock/server/VoltronReadWriteLockActiveEntityTest.java rename to clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/lock/server/VoltronReadWriteLockActiveEntityTest.java index 3dd2518779..4f6112cbd5 100644 --- a/clustered/server/src/test/java/org/ehcache/clustered/lock/server/VoltronReadWriteLockActiveEntityTest.java +++ b/clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/lock/server/VoltronReadWriteLockActiveEntityTest.java @@ -32,8 +32,8 @@ import static org.ehcache.clustered.common.internal.lock.LockMessaging.HoldType.READ; import static org.ehcache.clustered.common.internal.lock.LockMessaging.HoldType.WRITE; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -133,7 +133,7 @@ public void testWriteUnlockNotifiesListeners() throws MessageCodecException { ActiveInvokeContext locker = newContext(); ActiveInvokeContext waiter = newContext(); - ClientDescriptor waiterDescriptor = () -> null; + ClientDescriptor waiterDescriptor = mock(ClientDescriptor.class); when(waiter.getClientDescriptor()).thenReturn(waiterDescriptor); entity.invokeActive(locker, LockMessaging.lock(WRITE)); @@ -149,7 +149,7 @@ public void testReadUnlockNotifiesListeners() throws MessageCodecException { ActiveInvokeContext locker = newContext(); ActiveInvokeContext waiter = newContext(); - ClientDescriptor waiterDescriptor = () -> null; + ClientDescriptor waiterDescriptor = mock(ClientDescriptor.class); when(waiter.getClientDescriptor()).thenReturn(waiterDescriptor); entity.invokeActive(locker, LockMessaging.lock(READ)); diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/ClusterTierManagerActiveEntityTest.java b/clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/ClusterTierManagerActiveEntityTest.java similarity index 88% rename from clustered/server/src/test/java/org/ehcache/clustered/server/ClusterTierManagerActiveEntityTest.java rename to clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/ClusterTierManagerActiveEntityTest.java index 89856b2326..c58061c0cb 100644 --- a/clustered/server/src/test/java/org/ehcache/clustered/server/ClusterTierManagerActiveEntityTest.java +++ b/clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/ClusterTierManagerActiveEntityTest.java @@ -38,6 +38,7 @@ import org.terracotta.offheapresource.OffHeapResource; import org.terracotta.offheapresource.OffHeapResourceIdentifier; import org.terracotta.offheapresource.OffHeapResources; +import org.terracotta.offheapresource.OffHeapUsageEvent; import org.terracotta.offheapstore.util.MemoryUnit; import java.util.Collection; @@ -46,14 +47,16 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.UUID; +import java.util.function.Consumer; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; @@ -80,7 +83,7 @@ public void testDisconnectedNotConnected() throws Exception { EhcacheStateService ehcacheStateService = registry.getService(new EhcacheStateServiceConfig(blankConfiguration, registry, DEFAULT_MAPPER)); final ClusterTierManagerActiveEntity activeEntity = new ClusterTierManagerActiveEntity(blankConfiguration, ehcacheStateService, management); - ClientDescriptor client = new TestClientDescriptor(); + ClientDescriptor client = TestClientDescriptor.newClient(); activeEntity.disconnected(client); // Not expected to fail ... } @@ -103,7 +106,7 @@ public void testConfigure() throws Exception { ClusterTierManagerConfiguration configuration = new ClusterTierManagerConfiguration("identifier", serverSideConfiguration); EhcacheStateService ehcacheStateService = registry.getService(new EhcacheStateServiceConfig(configuration, registry, DEFAULT_MAPPER)); final ClusterTierManagerActiveEntity activeEntity = new ClusterTierManagerActiveEntity(configuration, ehcacheStateService, management); - ClientDescriptor client = new TestClientDescriptor(); + ClientDescriptor client = TestClientDescriptor.newClient(); activeEntity.connected(client); assertThat(registry.getStoreManagerService().getSharedResourcePoolIds(), containsInAnyOrder("primary", "secondary")); @@ -225,15 +228,16 @@ public void testValidate2Clients() throws Exception { EhcacheStateService ehcacheStateService = registry.getService(new EhcacheStateServiceConfig(configuration, registry, DEFAULT_MAPPER)); final ClusterTierManagerActiveEntity activeEntity = new ClusterTierManagerActiveEntity(configuration, ehcacheStateService, management); - TestInvokeContext context = new TestInvokeContext(); - activeEntity.connected(context.getClientDescriptor()); - assertSuccess(activeEntity.invokeActive(context, MESSAGE_FACTORY.validateStoreManager(serverSideConfig))); + TestClientDescriptor client1 = TestClientDescriptor.newClient(); + activeEntity.connected(client1); - TestInvokeContext context2 = new TestInvokeContext(); - activeEntity.connected(context2.getClientDescriptor()); + assertSuccess(activeEntity.invokeActive(client1.invokeContext(), MESSAGE_FACTORY.validateStoreManager(serverSideConfig))); - assertSuccess(activeEntity.invokeActive(context2, MESSAGE_FACTORY.validateStoreManager(serverSideConfig))); + TestClientDescriptor client2 = TestClientDescriptor.newClient(); + activeEntity.connected(client2); + + assertSuccess(activeEntity.invokeActive(client2.invokeContext(), MESSAGE_FACTORY.validateStoreManager(serverSideConfig))); } @Test @@ -251,9 +255,9 @@ public void testValidateAfterConfigure() throws Exception { EhcacheStateService ehcacheStateService = registry.getService(new EhcacheStateServiceConfig(configuration, registry, DEFAULT_MAPPER)); final ClusterTierManagerActiveEntity activeEntity = new ClusterTierManagerActiveEntity(configuration, ehcacheStateService, management); - TestInvokeContext context = new TestInvokeContext(); - activeEntity.connected(context.getClientDescriptor()); - assertSuccess(activeEntity.invokeActive(context, MESSAGE_FACTORY.validateStoreManager(serverSideConfig))); + TestClientDescriptor client = TestClientDescriptor.newClient(); + activeEntity.connected(client); + assertSuccess(activeEntity.invokeActive(client.invokeContext(), MESSAGE_FACTORY.validateStoreManager(serverSideConfig))); } @Test @@ -271,10 +275,10 @@ public void testValidateExtraResource() throws Exception { ClusterTierManagerConfiguration configuration = new ClusterTierManagerConfiguration("identifier", serverSideConfiguration); EhcacheStateService ehcacheStateService = registry.getService(new EhcacheStateServiceConfig(configuration, registry, DEFAULT_MAPPER)); final ClusterTierManagerActiveEntity activeEntity = new ClusterTierManagerActiveEntity(configuration, ehcacheStateService, management); - TestInvokeContext context = new TestInvokeContext(); - activeEntity.connected(context.getClientDescriptor()); + TestClientDescriptor client = TestClientDescriptor.newClient(); + activeEntity.connected(client); - assertFailure(activeEntity.invokeActive(context, + assertFailure(activeEntity.invokeActive(client.invokeContext(), MESSAGE_FACTORY.validateStoreManager(new ServerSideConfigBuilder() .defaultResource("defaultServerResource") .sharedPool("primary", "serverResource1", 4, MemoryUnit.MEGABYTES) @@ -297,10 +301,10 @@ public void testValidateNoDefaultResource() throws Exception { ClusterTierManagerConfiguration configuration = new ClusterTierManagerConfiguration("identifier", serverSideConfiguration); EhcacheStateService ehcacheStateService = registry.getService(new EhcacheStateServiceConfig(configuration, registry, DEFAULT_MAPPER)); final ClusterTierManagerActiveEntity activeEntity = new ClusterTierManagerActiveEntity(configuration, ehcacheStateService, management); - TestInvokeContext context = new TestInvokeContext(); - activeEntity.connected(context.getClientDescriptor()); + TestClientDescriptor client = TestClientDescriptor.newClient(); + activeEntity.connected(client); - assertFailure(activeEntity.invokeActive(context, + assertFailure(activeEntity.invokeActive(client.invokeContext(), MESSAGE_FACTORY.validateStoreManager(new ServerSideConfigBuilder() .defaultResource("defaultServerResource") .sharedPool("primary", "serverResource1", 4, MemoryUnit.MEGABYTES) @@ -340,10 +344,10 @@ public void testValidateIdenticalConfiguration() throws Exception { EhcacheStateService ehcacheStateService = registry.getService(new EhcacheStateServiceConfig(configuration, registry, DEFAULT_MAPPER)); final ClusterTierManagerActiveEntity activeEntity = new ClusterTierManagerActiveEntity(configuration, ehcacheStateService, management); - TestInvokeContext context = new TestInvokeContext(); - activeEntity.connected(context.getClientDescriptor()); + TestClientDescriptor client = TestClientDescriptor.newClient(); + activeEntity.connected(client); - assertThat(activeEntity.invokeActive(context, MESSAGE_FACTORY.validateStoreManager(validateConfig)).getResponseType(), is(EhcacheResponseType.SUCCESS)); + assertThat(activeEntity.invokeActive(client.invokeContext(), MESSAGE_FACTORY.validateStoreManager(validateConfig)).getResponseType(), is(EhcacheResponseType.SUCCESS)); } @Test @@ -362,14 +366,14 @@ public void testValidateSharedPoolNamesDifferent() throws Exception { EhcacheStateService ehcacheStateService = registry.getService(new EhcacheStateServiceConfig(configuration, registry, DEFAULT_MAPPER)); ClusterTierManagerActiveEntity activeEntity = new ClusterTierManagerActiveEntity(configuration, ehcacheStateService, management); - TestInvokeContext context = new TestInvokeContext(); - activeEntity.connected(context.getClientDescriptor()); + TestClientDescriptor client = TestClientDescriptor.newClient(); + activeEntity.connected(client); ServerSideConfiguration validate = new ServerSideConfigBuilder() .defaultResource("defaultServerResource") .sharedPool("primary", "serverResource1", 4, MemoryUnit.MEGABYTES) .sharedPool("ternary", "serverResource2", 8, MemoryUnit.MEGABYTES) .build(); - assertFailure(activeEntity.invokeActive(context, MESSAGE_FACTORY.validateStoreManager(validate)), InvalidServerSideConfigurationException.class, "Pool names not equal."); + assertFailure(activeEntity.invokeActive(client.invokeContext(), MESSAGE_FACTORY.validateStoreManager(validate)), InvalidServerSideConfigurationException.class, "Pool names not equal."); } @Test @@ -388,18 +392,18 @@ public void testValidateDefaultResourceNameDifferent() throws Exception { EhcacheStateService ehcacheStateService = registry.getService(new EhcacheStateServiceConfig(configuration, registry, DEFAULT_MAPPER)); ClusterTierManagerActiveEntity activeEntity = new ClusterTierManagerActiveEntity(configuration, ehcacheStateService, management); - TestInvokeContext context = new TestInvokeContext(); - activeEntity.connected(context.getClientDescriptor()); + TestClientDescriptor client = TestClientDescriptor.newClient(); + activeEntity.connected(client); ServerSideConfiguration validate = new ServerSideConfigBuilder() .defaultResource("defaultServerResource2") .sharedPool("primary", "serverResource1", 4, MemoryUnit.MEGABYTES) .sharedPool("secondary", "serverResource2", 8, MemoryUnit.MEGABYTES) .build(); - assertFailure(activeEntity.invokeActive(context, MESSAGE_FACTORY.validateStoreManager(validate)), InvalidServerSideConfigurationException.class, "Default resource not aligned."); + assertFailure(activeEntity.invokeActive(client.invokeContext(), MESSAGE_FACTORY.validateStoreManager(validate)), InvalidServerSideConfigurationException.class, "Default resource not aligned."); } @Test - public void testValidateClientSharedPoolSizeTooBig() throws Exception { + public void testValidateClientSharedPoolSizeDifferent() throws Exception { OffHeapIdentifierRegistry registry = new OffHeapIdentifierRegistry(); registry.addResource("defaultServerResource1", 8, MemoryUnit.MEGABYTES); registry.addResource("serverResource1", 8, MemoryUnit.MEGABYTES); @@ -414,14 +418,14 @@ public void testValidateClientSharedPoolSizeTooBig() throws Exception { EhcacheStateService ehcacheStateService = registry.getService(new EhcacheStateServiceConfig(configuration, registry, DEFAULT_MAPPER)); ClusterTierManagerActiveEntity activeEntity = new ClusterTierManagerActiveEntity(configuration, ehcacheStateService, management); - TestInvokeContext context = new TestInvokeContext(); - activeEntity.connected(context.getClientDescriptor()); + TestClientDescriptor client = TestClientDescriptor.newClient(); + activeEntity.connected(client); ServerSideConfiguration validate = new ServerSideConfigBuilder() .defaultResource("defaultServerResource1") .sharedPool("primary", "serverResource1", 4, MemoryUnit.MEGABYTES) .sharedPool("secondary", "serverResource2", 36, MemoryUnit.MEGABYTES) .build(); - assertFailure(activeEntity.invokeActive(context, MESSAGE_FACTORY.validateStoreManager(validate)),InvalidServerSideConfigurationException.class, "Pool 'secondary' not equal."); + assertSuccess(activeEntity.invokeActive(client.invokeContext(), MESSAGE_FACTORY.validateStoreManager(validate))); } @Test @@ -440,9 +444,9 @@ public void testValidateSecondClientInheritsFirstClientConfig() throws Exception EhcacheStateService ehcacheStateService = registry.getService(new EhcacheStateServiceConfig(configuration, registry, DEFAULT_MAPPER)); ClusterTierManagerActiveEntity activeEntity = new ClusterTierManagerActiveEntity(configuration, ehcacheStateService, management); - TestInvokeContext context = new TestInvokeContext(); - activeEntity.connected(context.getClientDescriptor()); - assertSuccess(activeEntity.invokeActive(context, MESSAGE_FACTORY.validateStoreManager(null))); + TestClientDescriptor client = TestClientDescriptor.newClient(); + activeEntity.connected(client); + assertSuccess(activeEntity.invokeActive(client.invokeContext(), MESSAGE_FACTORY.validateStoreManager(null))); } @Test @@ -453,11 +457,11 @@ public void testInvalidMessageThrowsError() throws Exception { EhcacheStateService ehcacheStateService = registry.getService(new EhcacheStateServiceConfig(blankConfiguration, registry, DEFAULT_MAPPER)); final ClusterTierManagerActiveEntity activeEntity = new ClusterTierManagerActiveEntity(blankConfiguration, ehcacheStateService, management); - TestInvokeContext context = new TestInvokeContext(); - activeEntity.connected(context.getClientDescriptor()); + TestClientDescriptor client = TestClientDescriptor.newClient(); + activeEntity.connected(client); try { - activeEntity.invokeActive(context, new InvalidMessage()); + activeEntity.invokeActive(client.invokeContext(), new InvalidMessage()); fail("Invalid message should result in AssertionError"); } catch (AssertionError e) { assertThat(e.getMessage(), containsString("Unsupported")); @@ -472,10 +476,10 @@ public void testPrepareForDestroy() throws Exception { EhcacheStateService ehcacheStateService = registry.getService(new EhcacheStateServiceConfig(blankConfiguration, registry, DEFAULT_MAPPER)); final ClusterTierManagerActiveEntity activeEntity = new ClusterTierManagerActiveEntity(blankConfiguration, ehcacheStateService, management); - TestInvokeContext context = new TestInvokeContext(); - activeEntity.connected(context.getClientDescriptor()); + TestClientDescriptor client = TestClientDescriptor.newClient(); + activeEntity.connected(client); - activeEntity.invokeActive(context, MESSAGE_FACTORY.prepareForDestroy()); + activeEntity.invokeActive(client.invokeContext(), MESSAGE_FACTORY.prepareForDestroy()); try { ehcacheStateService.validate(null); @@ -495,10 +499,10 @@ public void testPrepareForDestroyInProgress() throws Exception { final ClusterTierManagerActiveEntity activeEntity = new ClusterTierManagerActiveEntity(blankConfiguration, ehcacheStateService, management); - TestInvokeContext context = new TestInvokeContext(); - activeEntity.connected(context.getClientDescriptor()); + TestClientDescriptor client = TestClientDescriptor.newClient(); + activeEntity.connected(client); - assertFailure(activeEntity.invokeActive(context, MESSAGE_FACTORY.validateStoreManager(null)), DestroyInProgressException.class, "in progress for destroy"); + assertFailure(activeEntity.invokeActive(client.invokeContext(), MESSAGE_FACTORY.validateStoreManager(null)), DestroyInProgressException.class, "in progress for destroy"); } @@ -606,6 +610,11 @@ public Set getAllIdentifiers() { public OffHeapResource getOffHeapResource(OffHeapResourceIdentifier identifier) { return pools.get(identifier); } + + @Override + public boolean addOffHeapResource(OffHeapResourceIdentifier identifier, long capacity) { + return false; + } }, config.getConfig().getConfiguration(), DEFAULT_MAPPER, service -> {}); } return (T) (this.storeManagerService); @@ -665,6 +674,21 @@ public long capacity() { return capacity; } + @Override + public boolean setCapacity(long size) throws IllegalArgumentException { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public void addUsageListener(UUID listenerUUID, float threshold, Consumer consumer) { + + } + + @Override + public void removeUsageListener(UUID listenerUUID) throws IllegalArgumentException { + + } + private long getUsed() { return used; } diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/ClusterTierManagerPassiveEntityTest.java b/clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/ClusterTierManagerPassiveEntityTest.java similarity index 96% rename from clustered/server/src/test/java/org/ehcache/clustered/server/ClusterTierManagerPassiveEntityTest.java rename to clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/ClusterTierManagerPassiveEntityTest.java index 25a340b5a9..1844b33e55 100644 --- a/clustered/server/src/test/java/org/ehcache/clustered/server/ClusterTierManagerPassiveEntityTest.java +++ b/clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/ClusterTierManagerPassiveEntityTest.java @@ -37,6 +37,7 @@ import org.terracotta.offheapresource.OffHeapResource; import org.terracotta.offheapresource.OffHeapResourceIdentifier; import org.terracotta.offheapresource.OffHeapResources; +import org.terracotta.offheapresource.OffHeapUsageEvent; import org.terracotta.offheapstore.util.MemoryUnit; import java.util.Collection; @@ -45,12 +46,14 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.UUID; +import java.util.function.Consumer; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; @@ -366,6 +369,11 @@ public Set getAllIdentifiers() { public OffHeapResource getOffHeapResource(OffHeapResourceIdentifier identifier) { return pools.get(identifier); } + + @Override + public boolean addOffHeapResource(OffHeapResourceIdentifier identifier, long capacity) { + return false; + } }, config.getConfig().getConfiguration(), DEFAULT_MAPPER, service -> {}); } return (T) (this.storeManagerService); @@ -429,6 +437,21 @@ public long capacity() { return capacity; } + @Override + public boolean setCapacity(long size) throws IllegalArgumentException { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public void addUsageListener(UUID listenerUUID, float threshold, Consumer consumer) { + + } + + @Override + public void removeUsageListener(UUID listenerUUID) throws IllegalArgumentException { + + } + private long getUsed() { return used; } diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/DefaultConcurrencyStrategyTest.java b/clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/DefaultConcurrencyStrategyTest.java similarity index 100% rename from clustered/server/src/test/java/org/ehcache/clustered/server/DefaultConcurrencyStrategyTest.java rename to clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/DefaultConcurrencyStrategyTest.java diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/ServerStoreCompatibilityTest.java b/clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/ServerStoreCompatibilityTest.java similarity index 97% rename from clustered/server/src/test/java/org/ehcache/clustered/server/ServerStoreCompatibilityTest.java rename to clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/ServerStoreCompatibilityTest.java index 53fbdfd04a..a4e2709666 100644 --- a/clustered/server/src/test/java/org/ehcache/clustered/server/ServerStoreCompatibilityTest.java +++ b/clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/ServerStoreCompatibilityTest.java @@ -25,9 +25,9 @@ import org.ehcache.clustered.common.PoolAllocation.Unknown; import org.junit.Test; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; /** @@ -176,7 +176,7 @@ public void testConsitencyMismatch() { } @Test - public void testDedicatedPoolResourceTooBig() { + public void testDedicatedPoolResourceTooBig() throws InvalidServerStoreConfigurationException { ServerStoreConfiguration serverConfiguration = new ServerStoreConfiguration(DEDICATED_POOL_ALLOCATION, STORED_KEY_TYPE, STORED_VALUE_TYPE, @@ -193,16 +193,11 @@ public void testDedicatedPoolResourceTooBig() { ServerStoreCompatibility serverStoreCompatibility = new ServerStoreCompatibility(); - try { - serverStoreCompatibility.verify(serverConfiguration, clientConfiguration); - fail("Expected InvalidServerStoreConfigurationException"); - } catch(InvalidServerStoreConfigurationException e) { - assertThat(e.getMessage(), containsString("resourcePoolType")); - } + serverStoreCompatibility.verify(serverConfiguration, clientConfiguration); } @Test - public void testDedicatedPoolResourceTooSmall() { + public void testDedicatedPoolResourceTooSmall() throws InvalidServerStoreConfigurationException { ServerStoreConfiguration serverConfiguration = new ServerStoreConfiguration(DEDICATED_POOL_ALLOCATION, STORED_KEY_TYPE, STORED_VALUE_TYPE, @@ -219,12 +214,7 @@ public void testDedicatedPoolResourceTooSmall() { ServerStoreCompatibility serverStoreCompatibility = new ServerStoreCompatibility(); - try { - serverStoreCompatibility.verify(serverConfiguration, clientConfiguration); - fail("Expected InvalidServerStoreConfigurationException"); - } catch(InvalidServerStoreConfigurationException e) { - assertThat(e.getMessage(), containsString("resourcePoolType")); - } + serverStoreCompatibility.verify(serverConfiguration, clientConfiguration); } @Test diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/TestClientDescriptor.java b/clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/TestClientDescriptor.java similarity index 62% rename from clustered/server/src/test/java/org/ehcache/clustered/server/TestClientDescriptor.java rename to clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/TestClientDescriptor.java index 26b2f20923..dd0dfdbc3e 100644 --- a/clustered/server/src/test/java/org/ehcache/clustered/server/TestClientDescriptor.java +++ b/clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/TestClientDescriptor.java @@ -16,25 +16,40 @@ package org.ehcache.clustered.server; +import org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse; +import org.terracotta.entity.ActiveInvokeContext; import org.terracotta.entity.ClientDescriptor; import org.terracotta.entity.ClientSourceId; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; public final class TestClientDescriptor implements ClientDescriptor { - private static final AtomicInteger counter = new AtomicInteger(0); + private static final AtomicLong counter = new AtomicLong(1L); - private final int clientId = counter.incrementAndGet(); + private final long clientId; + private final AtomicLong transactionId = new AtomicLong(1L); - public static ClientDescriptor create() { - return new TestClientDescriptor(); + public static TestClientDescriptor newClient() { + return new TestClientDescriptor(counter.getAndIncrement()); } + private TestClientDescriptor(long clientId) { + this.clientId = clientId; + } + + public ActiveInvokeContext invokeContext() { + return new TestInvokeContext(this, transactionId.getAndIncrement()); + } @Override public ClientSourceId getSourceId() { return new TestClientSourceId(clientId); } + @Override + public boolean isValidClient() { + return true; + } + @Override public String toString() { return "TestClientDescriptor[" + clientId + "]"; @@ -56,6 +71,6 @@ public boolean equals(Object o) { @Override public int hashCode() { - return clientId; + return Long.hashCode(clientId); } } diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/TestClientSourceId.java b/clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/TestClientSourceId.java similarity index 95% rename from clustered/server/src/test/java/org/ehcache/clustered/server/TestClientSourceId.java rename to clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/TestClientSourceId.java index 9624e3815d..4b287cd07d 100644 --- a/clustered/server/src/test/java/org/ehcache/clustered/server/TestClientSourceId.java +++ b/clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/TestClientSourceId.java @@ -32,6 +32,11 @@ public long toLong() { return id; } + @Override + public boolean isValidClient() { + return true; + } + @Override public boolean matches(ClientDescriptor clientDescriptor) { return clientDescriptor.getSourceId().toLong() == id; diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/TestInvokeContext.java b/clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/TestInvokeContext.java similarity index 78% rename from clustered/server/src/test/java/org/ehcache/clustered/server/TestInvokeContext.java rename to clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/TestInvokeContext.java index 91f54ff049..e1d000868d 100644 --- a/clustered/server/src/test/java/org/ehcache/clustered/server/TestInvokeContext.java +++ b/clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/TestInvokeContext.java @@ -23,16 +23,14 @@ import org.terracotta.entity.ClientDescriptor; import org.terracotta.entity.ClientSourceId; -import java.util.concurrent.atomic.AtomicLong; +final class TestInvokeContext implements ActiveInvokeContext { -public final class TestInvokeContext implements ActiveInvokeContext { + private final ClientDescriptor clientDescriptor; + private final long txnId; - private final AtomicLong currentTransactionId = new AtomicLong(); - - private final ClientDescriptor clientDescriptor = new TestClientDescriptor(); - - public void incrementCurrentTransactionId() { - currentTransactionId.incrementAndGet(); + TestInvokeContext(ClientDescriptor clientDescriptor, long txnId) { + this.clientDescriptor = clientDescriptor; + this.txnId = txnId; } @Override @@ -42,7 +40,7 @@ public ClientDescriptor getClientDescriptor() { @Override public ActiveInvokeChannel openInvokeChannel() { - return null; + throw new UnsupportedOperationException(); } @Override @@ -52,12 +50,12 @@ public ClientSourceId getClientSource() { @Override public long getCurrentTransactionId() { - return currentTransactionId.get(); + return txnId; } @Override public long getOldestTransactionId() { - return 1; + return 0; } @Override diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/internal/messages/EhcacheMessageTrackerMessageTest.java b/clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/internal/messages/EhcacheMessageTrackerMessageTest.java similarity index 97% rename from clustered/server/src/test/java/org/ehcache/clustered/server/internal/messages/EhcacheMessageTrackerMessageTest.java rename to clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/internal/messages/EhcacheMessageTrackerMessageTest.java index b9e294e85b..a0cee7625b 100644 --- a/clustered/server/src/test/java/org/ehcache/clustered/server/internal/messages/EhcacheMessageTrackerMessageTest.java +++ b/clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/internal/messages/EhcacheMessageTrackerMessageTest.java @@ -57,7 +57,7 @@ public void before() { trackingMap.put(id1.toLong(), res1); trackingMap.put(id2.toLong(), res2); - message = new EhcacheMessageTrackerMessage(2, trackingMap); + message = new EhcacheMessageTrackerMessage(trackingMap); } @Test diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/internal/messages/EhcacheServerCodecTest.java b/clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/internal/messages/EhcacheServerCodecTest.java similarity index 100% rename from clustered/server/src/test/java/org/ehcache/clustered/server/internal/messages/EhcacheServerCodecTest.java rename to clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/internal/messages/EhcacheServerCodecTest.java diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/internal/messages/EhcacheSyncMessageCodecTest.java b/clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/internal/messages/EhcacheSyncMessageCodecTest.java similarity index 88% rename from clustered/server/src/test/java/org/ehcache/clustered/server/internal/messages/EhcacheSyncMessageCodecTest.java rename to clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/internal/messages/EhcacheSyncMessageCodecTest.java index d3cfbf137b..ab5ab59a55 100644 --- a/clustered/server/src/test/java/org/ehcache/clustered/server/internal/messages/EhcacheSyncMessageCodecTest.java +++ b/clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/internal/messages/EhcacheSyncMessageCodecTest.java @@ -19,15 +19,16 @@ import org.ehcache.clustered.common.internal.messages.ResponseCodec; import org.ehcache.clustered.common.internal.store.Chain; import org.ehcache.clustered.server.TestClientSourceId; +import org.hamcrest.MatcherAssert; import org.junit.Test; import java.util.HashMap; import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; -import static org.ehcache.clustered.common.internal.store.Util.chainsEqual; -import static org.ehcache.clustered.common.internal.store.Util.createPayload; -import static org.ehcache.clustered.common.internal.store.Util.getChain; +import static org.ehcache.clustered.ChainUtils.createPayload; +import static org.ehcache.clustered.ChainUtils.sequencedChainOf; +import static org.ehcache.clustered.Matchers.matchesChain; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -41,7 +42,7 @@ public class EhcacheSyncMessageCodecTest { @Test public void testDataSyncMessageEncodeDecode() throws Exception { Map chainMap = new HashMap<>(); - Chain chain = getChain(true, createPayload(10L), createPayload(100L), createPayload(1000L)); + Chain chain = sequencedChainOf(createPayload(10L), createPayload(100L), createPayload(1000L)); chainMap.put(1L, chain); chainMap.put(2L, chain); chainMap.put(3L, chain); @@ -50,14 +51,14 @@ public void testDataSyncMessageEncodeDecode() throws Exception { EhcacheDataSyncMessage decoded = (EhcacheDataSyncMessage) codec.decode(0, encodedMessage); Map decodedChainMap = decoded.getChainMap(); assertThat(decodedChainMap).hasSize(3); - assertThat(chainsEqual(decodedChainMap.get(1L), chain)).isTrue(); - assertThat(chainsEqual(decodedChainMap.get(2L), chain)).isTrue(); - assertThat(chainsEqual(decodedChainMap.get(3L), chain)).isTrue(); + MatcherAssert.assertThat(decodedChainMap.get(1L), matchesChain(chain)); + MatcherAssert.assertThat(decodedChainMap.get(2L), matchesChain(chain)); + MatcherAssert.assertThat(decodedChainMap.get(3L), matchesChain(chain)); } @Test public void testMessageTrackerSyncEncodeDecode_emptyMessage() throws Exception { - EhcacheMessageTrackerMessage message = new EhcacheMessageTrackerMessage(1, new HashMap<>()); + EhcacheMessageTrackerMessage message = new EhcacheMessageTrackerMessage(new HashMap<>()); byte[] encodedMessage = codec.encode(0, message); EhcacheMessageTrackerMessage decoded = (EhcacheMessageTrackerMessage) codec.decode(0, encodedMessage); assertThat(decoded.getTrackedMessages()).isEmpty(); @@ -68,7 +69,7 @@ public void testMessageTrackerSyncEncodeDecode_clientWithoutMessage() throws Exc HashMap> trackerMap = new HashMap<>(); trackerMap.put(1L, new HashMap<>()); - EhcacheMessageTrackerMessage message = new EhcacheMessageTrackerMessage(1, trackerMap); + EhcacheMessageTrackerMessage message = new EhcacheMessageTrackerMessage(trackerMap); byte[] encodedMessage = codec.encode(0, message); EhcacheMessageTrackerMessage decoded = (EhcacheMessageTrackerMessage) codec.decode(0, encodedMessage); assertThat(decoded.getTrackedMessages()).isEmpty(); @@ -103,7 +104,7 @@ public void testMessageTrackerSyncEncodeDecode_messages() throws Exception { responses2.put(5L, r5); trackerMap.put(2L, responses2); - EhcacheMessageTrackerMessage message = new EhcacheMessageTrackerMessage(1, trackerMap); + EhcacheMessageTrackerMessage message = new EhcacheMessageTrackerMessage(trackerMap); byte[] encodedMessage = codec.encode(0, message); EhcacheMessageTrackerMessage decoded = (EhcacheMessageTrackerMessage) codec.decode(0, encodedMessage); diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/internal/messages/PassiveReplicationMessageCodecTest.java b/clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/internal/messages/PassiveReplicationMessageCodecTest.java similarity index 87% rename from clustered/server/src/test/java/org/ehcache/clustered/server/internal/messages/PassiveReplicationMessageCodecTest.java rename to clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/internal/messages/PassiveReplicationMessageCodecTest.java index 264b41105b..68054367ee 100644 --- a/clustered/server/src/test/java/org/ehcache/clustered/server/internal/messages/PassiveReplicationMessageCodecTest.java +++ b/clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/internal/messages/PassiveReplicationMessageCodecTest.java @@ -24,14 +24,12 @@ import org.junit.Test; import static java.nio.ByteBuffer.wrap; -import static org.ehcache.clustered.common.internal.store.Util.chainsEqual; -import static org.ehcache.clustered.common.internal.store.Util.createPayload; -import static org.ehcache.clustered.common.internal.store.Util.getChain; +import static org.ehcache.clustered.ChainUtils.chainOf; +import static org.ehcache.clustered.ChainUtils.createPayload; +import static org.ehcache.clustered.Matchers.matchesChain; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; - public class PassiveReplicationMessageCodecTest { @@ -39,7 +37,7 @@ public class PassiveReplicationMessageCodecTest { @Test public void testChainReplicationMessageCodec() { - Chain chain = getChain(false, createPayload(2L), createPayload(20L)); + Chain chain = chainOf(createPayload(2L), createPayload(20L)); ChainReplicationMessage chainReplicationMessage = new ChainReplicationMessage(2L, chain, 200L, 100L, 1L); byte[] encoded = codec.encode(chainReplicationMessage); @@ -49,7 +47,7 @@ public void testChainReplicationMessageCodec() { assertThat(decodedMsg.getTransactionId(), is(chainReplicationMessage.getTransactionId())); assertThat(decodedMsg.getOldestTransactionId(), is(chainReplicationMessage.getOldestTransactionId())); assertThat(decodedMsg.getKey(), is(chainReplicationMessage.getKey())); - assertTrue(chainsEqual(decodedMsg.getChain(), chainReplicationMessage.getChain())); + assertThat(decodedMsg.getChain(), matchesChain(chainReplicationMessage.getChain())); } diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/store/ClusterTierActiveEntityTest.java b/clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/store/ClusterTierActiveEntityTest.java similarity index 56% rename from clustered/server/src/test/java/org/ehcache/clustered/server/store/ClusterTierActiveEntityTest.java rename to clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/store/ClusterTierActiveEntityTest.java index dfb28860d6..12eb467c5e 100644 --- a/clustered/server/src/test/java/org/ehcache/clustered/server/store/ClusterTierActiveEntityTest.java +++ b/clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/store/ClusterTierActiveEntityTest.java @@ -22,33 +22,45 @@ import org.ehcache.clustered.common.PoolAllocation.Shared; import org.ehcache.clustered.common.ServerSideConfiguration; import org.ehcache.clustered.common.internal.ServerStoreConfiguration; +import org.ehcache.clustered.common.internal.exceptions.InvalidOperationException; import org.ehcache.clustered.common.internal.exceptions.InvalidServerStoreConfigurationException; +import org.ehcache.clustered.common.internal.messages.ClusterTierReconnectMessage; import org.ehcache.clustered.common.internal.messages.ConcurrentEntityMessage; import org.ehcache.clustered.common.internal.messages.EhcacheEntityMessage; import org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse; import org.ehcache.clustered.common.internal.messages.EhcacheResponseType; import org.ehcache.clustered.common.internal.messages.LifecycleMessage; +import org.ehcache.clustered.common.internal.messages.ReconnectMessageCodec; import org.ehcache.clustered.common.internal.messages.ServerStoreOpMessage; +import org.ehcache.clustered.common.internal.store.Chain; import org.ehcache.clustered.common.internal.store.ClusterTierEntityConfiguration; +import org.ehcache.clustered.server.CommunicatorServiceConfiguration; import org.ehcache.clustered.server.ConcurrencyStrategies; import org.ehcache.clustered.server.EhcacheStateServiceImpl; import org.ehcache.clustered.server.KeySegmentMapper; import org.ehcache.clustered.server.ServerSideServerStore; -import org.ehcache.clustered.server.ServerStoreEvictionListener; +import org.ehcache.clustered.server.ServerStoreEventListener; import org.ehcache.clustered.server.TestClientDescriptor; -import org.ehcache.clustered.server.TestInvokeContext; import org.ehcache.clustered.server.internal.messages.EhcacheDataSyncMessage; import org.ehcache.clustered.server.internal.messages.PassiveReplicationMessage; import org.ehcache.clustered.server.state.EhcacheStateService; import org.ehcache.clustered.server.state.InvalidationTracker; import org.ehcache.clustered.server.store.ClusterTierActiveEntity.InvalidationHolder; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; +import org.hamcrest.core.IsInstanceOf; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; import org.mockito.Mockito; import org.terracotta.client.message.tracker.OOOMessageHandler; import org.terracotta.client.message.tracker.OOOMessageHandlerConfiguration; import org.terracotta.client.message.tracker.OOOMessageHandlerImpl; +import org.terracotta.entity.ActiveInvokeContext; +import org.mockito.ArgumentMatchers; +import org.terracotta.entity.ActiveServerEntity; import org.terracotta.entity.ClientCommunicator; import org.terracotta.entity.ClientDescriptor; import org.terracotta.entity.ConfigurationException; @@ -64,20 +76,29 @@ import org.terracotta.offheapresource.OffHeapResource; import org.terracotta.offheapresource.OffHeapResourceIdentifier; import org.terracotta.offheapresource.OffHeapResources; +import org.terracotta.offheapresource.OffHeapUsageEvent; import org.terracotta.offheapstore.util.MemoryUnit; import java.nio.ByteBuffer; import java.util.Collection; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; import java.util.Random; import java.util.Set; +import java.util.UUID; import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; +import java.util.function.Consumer; -import static org.ehcache.clustered.common.internal.store.Util.createPayload; +import static org.ehcache.clustered.ChainUtils.createPayload; +import static org.ehcache.clustered.Matchers.entry; +import static org.ehcache.clustered.Matchers.hasPayloads; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; @@ -88,14 +109,16 @@ import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.sameInstance; -import static org.junit.Assert.assertThat; +import static org.hamcrest.core.CombinableMatcher.both; +import static org.hamcrest.core.CombinableMatcher.either; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNotNull; import static org.mockito.Mockito.atLeast; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; @@ -103,6 +126,9 @@ public class ClusterTierActiveEntityTest { private static final KeySegmentMapper DEFAULT_MAPPER = new KeySegmentMapper(16); + private static final int MAX_SYNC_CONCURRENCY = 1; + private static final ExecutorService SYNC_GETS_EXECUTOR = new ThreadPoolExecutor(0, MAX_SYNC_CONCURRENCY, + 20, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); private String defaultStoreName = "store"; private String defaultResource = "default"; @@ -123,14 +149,14 @@ public void setUp() { @Test(expected = ConfigurationException.class) public void testConfigNull() throws Exception { - new ClusterTierActiveEntity(mock(ServiceRegistry.class), null, DEFAULT_MAPPER); + new ClusterTierActiveEntity(mock(ServiceRegistry.class), null, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); } @Test public void testConnected() throws Exception { - ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); - ClientDescriptor client = new TestClientDescriptor(); + ClientDescriptor client = TestClientDescriptor.newClient(); activeEntity.connected(client); Set connectedClients = activeEntity.getConnectedClients(); @@ -140,9 +166,9 @@ public void testConnected() throws Exception { @Test public void testConnectedAgain() throws Exception { - ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); - ClientDescriptor client = new TestClientDescriptor(); + ClientDescriptor client = TestClientDescriptor.newClient(); activeEntity.connected(client); activeEntity.connected(client); @@ -153,12 +179,12 @@ public void testConnectedAgain() throws Exception { @Test public void testConnectedSecond() throws Exception { - ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); - ClientDescriptor client1 = new TestClientDescriptor(); + ClientDescriptor client1 = TestClientDescriptor.newClient(); activeEntity.connected(client1); - ClientDescriptor client2 = new TestClientDescriptor(); + ClientDescriptor client2 = TestClientDescriptor.newClient(); activeEntity.connected(client2); Set connectedClients = activeEntity.getConnectedClients(); @@ -168,9 +194,9 @@ public void testConnectedSecond() throws Exception { @Test public void testDisconnectedNotConnected() throws Exception { - ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); - ClientDescriptor client1 = new TestClientDescriptor(); + ClientDescriptor client1 = TestClientDescriptor.newClient(); activeEntity.disconnected(client1); // Not expected to fail ... } @@ -180,26 +206,59 @@ public void testDisconnectedNotConnected() throws Exception { */ @Test public void testDisconnected() throws Exception { - ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); - ClientDescriptor client1 = new TestClientDescriptor(); + ClientDescriptor client1 = TestClientDescriptor.newClient(); activeEntity.connected(client1); activeEntity.disconnected(client1); assertThat(activeEntity.getConnectedClients(), hasSize(0)); } + @Test + public void testEventListenerEnabledTracking() throws Exception { + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); + activeEntity.createNew(); + + TestClientDescriptor client1 = TestClientDescriptor.newClient(); + TestClientDescriptor client2 = TestClientDescriptor.newClient(); + + // check that connecting clients does not enable listeners by default + activeEntity.connected(client1); + activeEntity.connected(client2); + assertThat(activeEntity.getEventListeners().size(), is(0)); + + assertThat(activeEntity.invokeActive(client1.invokeContext(), new ServerStoreOpMessage.EnableEventListenerMessage(true)), succeeds()); + assertThat(activeEntity.getEventListeners().size(), is(1)); + // a client can register as many times as it wants, it's considered a single listener + assertThat(activeEntity.invokeActive(client1.invokeContext(), new ServerStoreOpMessage.EnableEventListenerMessage(true)), succeeds()); + assertThat(activeEntity.getEventListeners().size(), is(1)); + assertThat(activeEntity.invokeActive(client2.invokeContext(), new ServerStoreOpMessage.EnableEventListenerMessage(true)), succeeds()); + assertThat(activeEntity.getEventListeners().size(), is(2)); + + // check that disabling events is accounted for + assertThat(activeEntity.invokeActive(client2.invokeContext(), new ServerStoreOpMessage.EnableEventListenerMessage(false)), succeeds()); + assertThat(activeEntity.getEventListeners().size(), is(1)); + // check that disabling events from a client that does not have events enabled is a noop + assertThat(activeEntity.invokeActive(client2.invokeContext(), new ServerStoreOpMessage.EnableEventListenerMessage(false)), succeeds()); + assertThat(activeEntity.getEventListeners().size(), is(1)); + + // check that disconnected clients are accounted for + activeEntity.disconnected(client1); + assertThat(activeEntity.getEventListeners().size(), is(0)); + } + /** * Ensures the disconnect of a connected client is properly tracked and does not affect others. */ @Test public void testDisconnectedSecond() throws Exception { - ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); - ClientDescriptor client1 = new TestClientDescriptor(); + ClientDescriptor client1 = TestClientDescriptor.newClient(); activeEntity.connected(client1); - ClientDescriptor client2 = new TestClientDescriptor(); + ClientDescriptor client2 = TestClientDescriptor.newClient(); activeEntity.connected(client2); assertThat(activeEntity.getConnectedClients(), hasSize(2)); @@ -220,63 +279,78 @@ public void testLoadExistingRegistersEvictionListener() throws Exception { IEntityMessenger entityMessenger = mock(IEntityMessenger.class); ServiceRegistry registry = getCustomMockedServiceRegistry(stateService, null, entityMessenger, null, null); - ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(registry, defaultConfiguration, DEFAULT_MAPPER); + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(registry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); activeEntity.loadExisting(); - verify(store).setEvictionListener(any(ServerStoreEvictionListener.class)); + verify(store).setEventListener(any(ServerStoreEventListener.class)); + } + + @Test + public void testEnableEventListenerMessageEnablesOrDisablesEventsOnStore() throws Exception { + EhcacheStateService stateService = mock(EhcacheStateService.class); + + ServerSideServerStore store = mock(ServerSideServerStore.class); + InOrder storeOrderVerifier = Mockito.inOrder(store); + when(stateService.createStore(eq(defaultStoreName), any(), anyBoolean())).thenReturn(store); + when(stateService.getStore(eq(defaultStoreName))).thenReturn(store); + + IEntityMessenger entityMessenger = mock(IEntityMessenger.class); + ServiceRegistry registry = getCustomMockedServiceRegistry(stateService, null, entityMessenger, null, null); + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(registry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); + activeEntity.createNew(); + + TestClientDescriptor client = TestClientDescriptor.newClient(); + + activeEntity.connected(client); + // also check that duplicating enable/disable calls has no effect + assertThat(activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.EnableEventListenerMessage(true)), succeeds()); + assertThat(activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.EnableEventListenerMessage(true)), succeeds()); + assertThat(activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.EnableEventListenerMessage(false)), succeeds()); + assertThat(activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.EnableEventListenerMessage(false)), succeeds()); + + storeOrderVerifier.verify(store).enableEvents(eq(true)); + storeOrderVerifier.verify(store).enableEvents(eq(false)); } @Test public void testAppendInvalidationAcksTakenIntoAccount() throws Exception { - ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); activeEntity.createNew(); - TestInvokeContext context1 = new TestInvokeContext(); - TestInvokeContext context2 = new TestInvokeContext(); - TestInvokeContext context3 = new TestInvokeContext(); - activeEntity.connected(context1.getClientDescriptor()); - activeEntity.connected(context2.getClientDescriptor()); - activeEntity.connected(context3.getClientDescriptor()); + TestClientDescriptor client1 = TestClientDescriptor.newClient(); + TestClientDescriptor client2 = TestClientDescriptor.newClient(); + TestClientDescriptor client3 = TestClientDescriptor.newClient(); + activeEntity.connected(client1); + activeEntity.connected(client2); + activeEntity.connected(client3); // attach to the store - assertSuccess( - activeEntity.invokeActive(context1, new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)) - ); - assertSuccess( - activeEntity.invokeActive(context2, new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)) - ); - assertSuccess( - activeEntity.invokeActive(context3, new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)) - ); + assertThat(activeEntity.invokeActive(client1.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)), succeeds()); + assertThat(activeEntity.invokeActive(client2.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)), succeeds()); + assertThat(activeEntity.invokeActive(client3.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)), succeeds()); // perform an append - assertSuccess( - activeEntity.invokeActive(context1, new ServerStoreOpMessage.AppendMessage(1L, createPayload(1L))) - ); + assertThat(activeEntity.invokeActive(client1.invokeContext(), new ServerStoreOpMessage.AppendMessage(1L, createPayload(1L))), succeeds()); // assert that an invalidation request is pending assertThat(activeEntity.getClientsWaitingForInvalidation().size(), is(1)); InvalidationHolder invalidationHolder = activeEntity.getClientsWaitingForInvalidation().values().iterator().next(); - assertThat(invalidationHolder.clientDescriptorWaitingForInvalidation, is(context1.getClientDescriptor())); + assertThat(invalidationHolder.clientDescriptorWaitingForInvalidation, is(client1)); assertThat(invalidationHolder.clientsHavingToInvalidate.size(), is(2)); - assertThat(invalidationHolder.clientsHavingToInvalidate, containsInAnyOrder(context2.getClientDescriptor(), context3.getClientDescriptor())); + assertThat(invalidationHolder.clientsHavingToInvalidate, containsInAnyOrder(client2, client3)); // client 2 acks - assertSuccess( - activeEntity.invokeActive(context2, new ServerStoreOpMessage.ClientInvalidationAck(1L, activeEntity.getClientsWaitingForInvalidation().keySet().iterator().next())) - ); + assertThat(activeEntity.invokeActive(client2.invokeContext(), new ServerStoreOpMessage.ClientInvalidationAck(1L, activeEntity.getClientsWaitingForInvalidation().keySet().iterator().next())), succeeds()); // assert that client 2 is not waited for anymore assertThat(activeEntity.getClientsWaitingForInvalidation().size(), is(1)); invalidationHolder = activeEntity.getClientsWaitingForInvalidation().values().iterator().next(); - assertThat(invalidationHolder.clientDescriptorWaitingForInvalidation, is(context1.getClientDescriptor())); + assertThat(invalidationHolder.clientDescriptorWaitingForInvalidation, is(client1)); assertThat(invalidationHolder.clientsHavingToInvalidate.size(), is(1)); - assertThat(invalidationHolder.clientsHavingToInvalidate, contains(context3.getClientDescriptor())); + assertThat(invalidationHolder.clientsHavingToInvalidate, contains(client3)); // client 3 acks - assertSuccess( - activeEntity.invokeActive(context3, new ServerStoreOpMessage.ClientInvalidationAck(1L, activeEntity.getClientsWaitingForInvalidation().keySet().iterator().next())) - ); + assertThat(activeEntity.invokeActive(client3.invokeContext(), new ServerStoreOpMessage.ClientInvalidationAck(1L, activeEntity.getClientsWaitingForInvalidation().keySet().iterator().next())), succeeds()); // assert that the invalidation request is done since all clients disconnected assertThat(activeEntity.getClientsWaitingForInvalidation().size(), is(0)); @@ -284,55 +358,43 @@ public void testAppendInvalidationAcksTakenIntoAccount() throws Exception { @Test public void testClearInvalidationAcksTakenIntoAccount() throws Exception { - ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); activeEntity.createNew(); - TestInvokeContext context1 = new TestInvokeContext(); - TestInvokeContext context2 = new TestInvokeContext(); - TestInvokeContext context3 = new TestInvokeContext(); - activeEntity.connected(context1.getClientDescriptor()); - activeEntity.connected(context2.getClientDescriptor()); - activeEntity.connected(context3.getClientDescriptor()); + TestClientDescriptor client1 = TestClientDescriptor.newClient(); + TestClientDescriptor client2 = TestClientDescriptor.newClient(); + TestClientDescriptor client3 = TestClientDescriptor.newClient(); + activeEntity.connected(client1); + activeEntity.connected(client2); + activeEntity.connected(client3); // attach to the store - assertSuccess( - activeEntity.invokeActive(context1, new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)) - ); - assertSuccess( - activeEntity.invokeActive(context2, new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)) - ); - assertSuccess( - activeEntity.invokeActive(context3, new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)) - ); + assertThat(activeEntity.invokeActive(client1.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)), succeeds()); + assertThat(activeEntity.invokeActive(client2.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)), succeeds()); + assertThat(activeEntity.invokeActive(client3.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)), succeeds()); // perform a clear - assertSuccess( - activeEntity.invokeActive(context1, new ServerStoreOpMessage.ClearMessage()) - ); + assertThat(activeEntity.invokeActive(client1.invokeContext(), new ServerStoreOpMessage.ClearMessage()), succeeds()); // assert that an invalidation request is pending assertThat(activeEntity.getClientsWaitingForInvalidation().size(), is(1)); InvalidationHolder invalidationHolder = activeEntity.getClientsWaitingForInvalidation().values().iterator().next(); - assertThat(invalidationHolder.clientDescriptorWaitingForInvalidation, is(context1.getClientDescriptor())); + assertThat(invalidationHolder.clientDescriptorWaitingForInvalidation, is(client1)); assertThat(invalidationHolder.clientsHavingToInvalidate.size(), is(2)); - assertThat(invalidationHolder.clientsHavingToInvalidate, containsInAnyOrder(context2.getClientDescriptor(), context3.getClientDescriptor())); + assertThat(invalidationHolder.clientsHavingToInvalidate, containsInAnyOrder(client2, client3)); // client 2 acks - assertSuccess( - activeEntity.invokeActive(context2, new ServerStoreOpMessage.ClientInvalidationAllAck(activeEntity.getClientsWaitingForInvalidation().keySet().iterator().next())) - ); + assertThat(activeEntity.invokeActive(client2.invokeContext(), new ServerStoreOpMessage.ClientInvalidationAllAck(activeEntity.getClientsWaitingForInvalidation().keySet().iterator().next())), succeeds()); // assert that client 2 is not waited for anymore assertThat(activeEntity.getClientsWaitingForInvalidation().size(), is(1)); invalidationHolder = activeEntity.getClientsWaitingForInvalidation().values().iterator().next(); - assertThat(invalidationHolder.clientDescriptorWaitingForInvalidation, is(context1.getClientDescriptor())); + assertThat(invalidationHolder.clientDescriptorWaitingForInvalidation, is(client1)); assertThat(invalidationHolder.clientsHavingToInvalidate.size(), is(1)); - assertThat(invalidationHolder.clientsHavingToInvalidate, contains(context3.getClientDescriptor())); + assertThat(invalidationHolder.clientsHavingToInvalidate, contains(client3)); // client 3 acks - assertSuccess( - activeEntity.invokeActive(context3, new ServerStoreOpMessage.ClientInvalidationAllAck(activeEntity.getClientsWaitingForInvalidation().keySet().iterator().next())) - ); + assertThat(activeEntity.invokeActive(client3.invokeContext(), new ServerStoreOpMessage.ClientInvalidationAllAck(activeEntity.getClientsWaitingForInvalidation().keySet().iterator().next())), succeeds()); // assert that the invalidation request is done since all clients disconnected assertThat(activeEntity.getClientsWaitingForInvalidation().size(), is(0)); @@ -340,44 +402,36 @@ public void testClearInvalidationAcksTakenIntoAccount() throws Exception { @Test public void testAppendInvalidationDisconnectionOfInvalidatingClientsTakenIntoAccount() throws Exception { - ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); activeEntity.createNew(); - TestInvokeContext context1 = new TestInvokeContext(); - TestInvokeContext context2 = new TestInvokeContext(); - TestInvokeContext context3 = new TestInvokeContext(); - activeEntity.connected(context1.getClientDescriptor()); - activeEntity.connected(context2.getClientDescriptor()); - activeEntity.connected(context3.getClientDescriptor()); + TestClientDescriptor client1 = TestClientDescriptor.newClient(); + TestClientDescriptor client2 = TestClientDescriptor.newClient(); + TestClientDescriptor client3 = TestClientDescriptor.newClient(); + activeEntity.connected(client1); + activeEntity.connected(client2); + activeEntity.connected(client3); // attach to the store - assertSuccess( - activeEntity.invokeActive(context1, new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)) - ); - assertSuccess( - activeEntity.invokeActive(context2, new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)) - ); - assertSuccess( - activeEntity.invokeActive(context3, new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)) - ); + assertThat(activeEntity.invokeActive(client1.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)), succeeds()); + assertThat(activeEntity.invokeActive(client2.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)), succeeds()); + assertThat(activeEntity.invokeActive(client3.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)), succeeds()); // perform an append - assertSuccess( - activeEntity.invokeActive(context1, new ServerStoreOpMessage.AppendMessage(1L, createPayload(1L))) - ); + assertThat(activeEntity.invokeActive(client1.invokeContext(), new ServerStoreOpMessage.AppendMessage(1L, createPayload(1L))), succeeds()); // disconnect client2 - activeEntity.disconnected(context2.getClientDescriptor()); + activeEntity.disconnected(client2); // assert that client 2 is not waited for anymore assertThat(activeEntity.getClientsWaitingForInvalidation().size(), is(1)); InvalidationHolder invalidationHolder = activeEntity.getClientsWaitingForInvalidation().values().iterator().next(); - assertThat(invalidationHolder.clientDescriptorWaitingForInvalidation, is(context1.getClientDescriptor())); + assertThat(invalidationHolder.clientDescriptorWaitingForInvalidation, is(client1)); assertThat(invalidationHolder.clientsHavingToInvalidate.size(), is(1)); - assertThat(invalidationHolder.clientsHavingToInvalidate, contains(context3.getClientDescriptor())); + assertThat(invalidationHolder.clientsHavingToInvalidate, contains(client3)); // disconnect client3 - activeEntity.disconnected(context3.getClientDescriptor()); + activeEntity.disconnected(client3); // assert that the invalidation request is done since all clients disconnected assertThat(activeEntity.getClientsWaitingForInvalidation().size(), is(0)); @@ -385,44 +439,36 @@ public void testAppendInvalidationDisconnectionOfInvalidatingClientsTakenIntoAcc @Test public void testClearInvalidationDisconnectionOfInvalidatingClientsTakenIntoAccount() throws Exception { - ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); activeEntity.createNew(); - TestInvokeContext context1 = new TestInvokeContext(); - TestInvokeContext context2 = new TestInvokeContext(); - TestInvokeContext context3 = new TestInvokeContext(); - activeEntity.connected(context1.getClientDescriptor()); - activeEntity.connected(context2.getClientDescriptor()); - activeEntity.connected(context3.getClientDescriptor()); + TestClientDescriptor client1 = TestClientDescriptor.newClient(); + TestClientDescriptor client2 = TestClientDescriptor.newClient(); + TestClientDescriptor client3 = TestClientDescriptor.newClient(); + activeEntity.connected(client1); + activeEntity.connected(client2); + activeEntity.connected(client3); // attach to the store - assertSuccess( - activeEntity.invokeActive(context1, new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)) - ); - assertSuccess( - activeEntity.invokeActive(context2, new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)) - ); - assertSuccess( - activeEntity.invokeActive(context3, new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)) - ); + assertThat(activeEntity.invokeActive(client1.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)), succeeds()); + assertThat(activeEntity.invokeActive(client2.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)), succeeds()); + assertThat(activeEntity.invokeActive(client3.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)), succeeds()); // perform an append - assertSuccess( - activeEntity.invokeActive(context1, new ServerStoreOpMessage.ClearMessage()) - ); + assertThat(activeEntity.invokeActive(client1.invokeContext(), new ServerStoreOpMessage.ClearMessage()), succeeds()); // disconnect client2 - activeEntity.disconnected(context2.getClientDescriptor()); + activeEntity.disconnected(client2); // assert that client 2 is not waited for anymore assertThat(activeEntity.getClientsWaitingForInvalidation().size(), is(1)); InvalidationHolder invalidationHolder = activeEntity.getClientsWaitingForInvalidation().values().iterator().next(); - assertThat(invalidationHolder.clientDescriptorWaitingForInvalidation, is(context1.getClientDescriptor())); + assertThat(invalidationHolder.clientDescriptorWaitingForInvalidation, is(client1)); assertThat(invalidationHolder.clientsHavingToInvalidate.size(), is(1)); - assertThat(invalidationHolder.clientsHavingToInvalidate, contains(context3.getClientDescriptor())); + assertThat(invalidationHolder.clientsHavingToInvalidate, contains(client3)); // disconnect client3 - activeEntity.disconnected(context3.getClientDescriptor()); + activeEntity.disconnected(client3); // assert that the invalidation request is done since all clients disconnected assertThat(activeEntity.getClientsWaitingForInvalidation().size(), is(0)); @@ -435,34 +481,26 @@ public void testAppendInvalidationDisconnectionOfBlockingClientTakenIntoAccount( .consistency(Consistency.STRONG) .build(); ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, - new ClusterTierEntityConfiguration(identifier, defaultStoreName, serverStoreConfiguration), DEFAULT_MAPPER); + new ClusterTierEntityConfiguration(identifier, defaultStoreName, serverStoreConfiguration), DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); activeEntity.createNew(); - TestInvokeContext context1 = new TestInvokeContext(); - TestInvokeContext context2 = new TestInvokeContext(); - TestInvokeContext context3 = new TestInvokeContext(); - activeEntity.connected(context1.getClientDescriptor()); - activeEntity.connected(context2.getClientDescriptor()); - activeEntity.connected(context3.getClientDescriptor()); + TestClientDescriptor client1 = TestClientDescriptor.newClient(); + TestClientDescriptor client2 = TestClientDescriptor.newClient(); + TestClientDescriptor client3 = TestClientDescriptor.newClient(); + activeEntity.connected(client1); + activeEntity.connected(client2); + activeEntity.connected(client3); // attach to the store - assertSuccess( - activeEntity.invokeActive(context1, new LifecycleMessage.ValidateServerStore(defaultStoreName, serverStoreConfiguration)) - ); - assertSuccess( - activeEntity.invokeActive(context2, new LifecycleMessage.ValidateServerStore(defaultStoreName, serverStoreConfiguration)) - ); - assertSuccess( - activeEntity.invokeActive(context3, new LifecycleMessage.ValidateServerStore(defaultStoreName, serverStoreConfiguration)) - ); + assertThat(activeEntity.invokeActive(client1.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, serverStoreConfiguration)), succeeds()); + assertThat(activeEntity.invokeActive(client2.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, serverStoreConfiguration)), succeeds()); + assertThat(activeEntity.invokeActive(client3.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, serverStoreConfiguration)), succeeds()); // perform an append - assertSuccess( - activeEntity.invokeActive(context1, new ServerStoreOpMessage.AppendMessage(1L, createPayload(1L))) - ); + assertThat(activeEntity.invokeActive(client1.invokeContext(), new ServerStoreOpMessage.AppendMessage(1L, createPayload(1L))), succeeds()); // disconnect client1 - activeEntity.disconnected(context1.getClientDescriptor()); + activeEntity.disconnected(client1); // assert that the invalidation request is done since the originating client disconnected assertThat(activeEntity.getClientsWaitingForInvalidation().size(), is(0)); @@ -475,34 +513,26 @@ public void testClearInvalidationDisconnectionOfBlockingClientTakenIntoAccount() .consistency(Consistency.STRONG) .build(); ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, - new ClusterTierEntityConfiguration(identifier, defaultStoreName, serverStoreConfiguration), DEFAULT_MAPPER); + new ClusterTierEntityConfiguration(identifier, defaultStoreName, serverStoreConfiguration), DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); activeEntity.createNew(); - TestInvokeContext context1 = new TestInvokeContext(); - TestInvokeContext context2 = new TestInvokeContext(); - TestInvokeContext context3 = new TestInvokeContext(); - activeEntity.connected(context1.getClientDescriptor()); - activeEntity.connected(context2.getClientDescriptor()); - activeEntity.connected(context3.getClientDescriptor()); + TestClientDescriptor client1 = TestClientDescriptor.newClient(); + TestClientDescriptor client2 = TestClientDescriptor.newClient(); + TestClientDescriptor client3 = TestClientDescriptor.newClient(); + activeEntity.connected(client1); + activeEntity.connected(client2); + activeEntity.connected(client3); // attach to the store - assertSuccess( - activeEntity.invokeActive(context1, new LifecycleMessage.ValidateServerStore(defaultStoreName, serverStoreConfiguration)) - ); - assertSuccess( - activeEntity.invokeActive(context2, new LifecycleMessage.ValidateServerStore(defaultStoreName, serverStoreConfiguration)) - ); - assertSuccess( - activeEntity.invokeActive(context3, new LifecycleMessage.ValidateServerStore(defaultStoreName, serverStoreConfiguration)) - ); + assertThat(activeEntity.invokeActive(client1.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, serverStoreConfiguration)), succeeds()); + assertThat(activeEntity.invokeActive(client2.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, serverStoreConfiguration)), succeeds()); + assertThat(activeEntity.invokeActive(client3.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, serverStoreConfiguration)), succeeds()); // perform an append - assertSuccess( - activeEntity.invokeActive(context1, new ServerStoreOpMessage.ClearMessage()) - ); + assertThat(activeEntity.invokeActive(client1.invokeContext(), new ServerStoreOpMessage.ClearMessage()), succeeds()); // disconnect client1 - activeEntity.disconnected(context1.getClientDescriptor()); + activeEntity.disconnected(client1); // assert that the invalidation request is done since the originating client disconnected assertThat(activeEntity.getClientsWaitingForInvalidation().size(), is(0)); @@ -510,22 +540,18 @@ public void testClearInvalidationDisconnectionOfBlockingClientTakenIntoAccount() @Test public void testWithAttachmentSucceedsInvokingServerStoreOperation() throws Exception { - ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); activeEntity.createNew(); - TestInvokeContext context = new TestInvokeContext(); - activeEntity.connected(context.getClientDescriptor()); + TestClientDescriptor client = TestClientDescriptor.newClient(); + activeEntity.connected(client); // attach to the store - assertSuccess( - activeEntity.invokeActive(context, new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)) - ); + assertThat(activeEntity.invokeActive(client.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)), succeeds()); - assertSuccess( - activeEntity.invokeActive(context, new ServerStoreOpMessage.AppendMessage(1L, createPayload(1L))) - ); + assertThat(activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.AppendMessage(1L, createPayload(1L))), succeeds()); - EhcacheEntityResponse response = activeEntity.invokeActive(context, new ServerStoreOpMessage.GetMessage(1L)); + EhcacheEntityResponse response = activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.GetMessage(1L)); assertThat(response, instanceOf(EhcacheEntityResponse.GetResponse.class)); EhcacheEntityResponse.GetResponse getResponse = (EhcacheEntityResponse.GetResponse) response; assertThat(getResponse.getChain().isEmpty(), is(false)); @@ -533,7 +559,7 @@ public void testWithAttachmentSucceedsInvokingServerStoreOperation() throws Exce @Test public void testCreateDedicatedServerStore() throws Exception { - ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); activeEntity.createNew(); assertThat(defaultRegistry.getStoreManagerService().getDedicatedResourcePoolIds(), containsInAnyOrder(defaultStoreName)); @@ -543,19 +569,17 @@ public void testCreateDedicatedServerStore() throws Exception { assertThat(activeEntity.getConnectedClients(), empty()); assertThat(defaultRegistry.getStoreManagerService().getStores(), containsInAnyOrder(defaultStoreName)); - TestInvokeContext context = new TestInvokeContext(); - activeEntity.connected(context.getClientDescriptor()); + TestClientDescriptor client = TestClientDescriptor.newClient(); + activeEntity.connected(client); - assertSuccess( - activeEntity.invokeActive(context, new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)) - ); + assertThat(activeEntity.invokeActive(client.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)), succeeds()); - assertThat(activeEntity.getConnectedClients(), contains(context.getClientDescriptor())); + assertThat(activeEntity.getConnectedClients(), contains(client)); /* * Ensure the dedicated resource pool remains after client disconnect. */ - activeEntity.disconnected(context.getClientDescriptor()); + activeEntity.disconnected(client); assertThat(defaultRegistry.getStoreManagerService().getDedicatedResourcePoolIds(), containsInAnyOrder(defaultStoreName)); @@ -565,10 +589,10 @@ public void testCreateDedicatedServerStore() throws Exception { @Test public void testCreateDedicatedServerStoreExisting() throws Exception { - ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); activeEntity.createNew(); - ClusterTierActiveEntity otherEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); + ClusterTierActiveEntity otherEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); try { otherEntity.createNew(); fail("Duplicate creation should fail with an exception"); @@ -579,54 +603,52 @@ public void testCreateDedicatedServerStoreExisting() throws Exception { @Test public void testValidateDedicatedServerStore() throws Exception { - ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); activeEntity.createNew(); - TestInvokeContext context = new TestInvokeContext(); - activeEntity.connected(context.getClientDescriptor()); + TestClientDescriptor client1 = TestClientDescriptor.newClient(); + activeEntity.connected(client1); - TestInvokeContext context2 = new TestInvokeContext(); - activeEntity.connected(context2.getClientDescriptor()); + TestClientDescriptor client2 = TestClientDescriptor.newClient(); + activeEntity.connected(client2); - assertSuccess(activeEntity.invokeActive(context2, - new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration))); + assertThat(activeEntity.invokeActive(client2.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)), succeeds()); assertThat(defaultRegistry.getStoreManagerService().getDedicatedResourcePoolIds(), containsInAnyOrder(defaultStoreName)); assertThat(defaultRegistry.getResource(defaultResource).getUsed(), is(MemoryUnit.MEGABYTES.toBytes(1L))); assertThat(activeEntity.getConnectedClients(), hasSize(2)); - assertThat(activeEntity.getConnectedClients(), containsInAnyOrder(context.getClientDescriptor(), context2.getClientDescriptor())); + assertThat(activeEntity.getConnectedClients(), containsInAnyOrder(client1, client2)); assertThat(defaultRegistry.getStoreManagerService().getStores(), contains(defaultStoreName)); } @Test public void testValidateDedicatedServerStoreBad() throws Exception { - ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); activeEntity.createNew(); - TestInvokeContext context = new TestInvokeContext(); - activeEntity.connected(context.getClientDescriptor()); + TestClientDescriptor client = TestClientDescriptor.newClient(); + activeEntity.connected(client); - assertFailure(activeEntity.invokeActive(context, + assertThat(activeEntity.invokeActive(client.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, new ServerStoreConfigBuilder() - .dedicated(defaultResource, 8, MemoryUnit.MEGABYTES) + .dedicated("banana", 1024, MemoryUnit.KILOBYTES) .build())), - InvalidServerStoreConfigurationException.class); - + failsWith(instanceOf(InvalidServerStoreConfigurationException.class))); } @Test public void testValidateUnknown() throws Exception { - ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); activeEntity.createNew(); - TestInvokeContext context = new TestInvokeContext(); - activeEntity.connected(context.getClientDescriptor()); + TestClientDescriptor client = TestClientDescriptor.newClient(); + activeEntity.connected(client); - assertSuccess(activeEntity.invokeActive(context, new LifecycleMessage.ValidateServerStore(defaultStoreName, - new ServerStoreConfigBuilder().unknown().build()))); + assertThat(activeEntity.invokeActive(client.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, + new ServerStoreConfigBuilder().unknown().build())), succeeds()); } @Test @@ -636,7 +658,7 @@ public void testCreateSharedServerStore() throws Exception { .shared(defaultSharedPool) .build(); ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, - new ClusterTierEntityConfiguration(identifier, defaultStoreName, storeConfiguration), DEFAULT_MAPPER); + new ClusterTierEntityConfiguration(identifier, defaultStoreName, storeConfiguration), DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); activeEntity.createNew(); @@ -656,11 +678,11 @@ public void testCreateSharedServerStoreExisting() throws Exception { .shared(defaultSharedPool) .build(); ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, - new ClusterTierEntityConfiguration(identifier, defaultStoreName, storeConfiguration), DEFAULT_MAPPER); + new ClusterTierEntityConfiguration(identifier, defaultStoreName, storeConfiguration), DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); activeEntity.createNew(); ClusterTierActiveEntity otherEntity = new ClusterTierActiveEntity(defaultRegistry, - new ClusterTierEntityConfiguration(identifier, defaultStoreName, storeConfiguration), DEFAULT_MAPPER); + new ClusterTierEntityConfiguration(identifier, defaultStoreName, storeConfiguration), DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); try { otherEntity.createNew(); fail("Duplicate creation should fail with an exception"); @@ -676,24 +698,24 @@ public void testValidateSharedServerStore() throws Exception { .shared(defaultSharedPool) .build(); ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, - new ClusterTierEntityConfiguration(identifier, defaultStoreName, storeConfiguration), DEFAULT_MAPPER); + new ClusterTierEntityConfiguration(identifier, defaultStoreName, storeConfiguration), DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); activeEntity.createNew(); - TestInvokeContext context = new TestInvokeContext(); - activeEntity.connected(context.getClientDescriptor()); + TestClientDescriptor client = TestClientDescriptor.newClient(); + activeEntity.connected(client); - assertSuccess(activeEntity.invokeActive(context, new LifecycleMessage.ValidateServerStore(defaultStoreName, storeConfiguration))); + assertThat(activeEntity.invokeActive(client.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, storeConfiguration)), succeeds()); - assertThat(activeEntity.getConnectedClients(), contains(context.getClientDescriptor())); + assertThat(activeEntity.getConnectedClients(), contains(client)); } @Test public void testValidateServerStore_DedicatedStoresDifferentSizes() throws Exception { - ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); activeEntity.createNew(); - TestInvokeContext context = new TestInvokeContext(); - activeEntity.connected(context.getClientDescriptor()); + TestClientDescriptor client = TestClientDescriptor.newClient(); + activeEntity.connected(client); ServerStoreConfiguration storeConfiguration = new ServerStoreConfigBuilder() .dedicated(defaultResource, 2, MemoryUnit.MEGABYTES) @@ -706,17 +728,17 @@ public void testValidateServerStore_DedicatedStoresDifferentSizes() throws Excep ", desired: " + storeConfiguration.getPoolAllocation(); - assertFailure(activeEntity.invokeActive(context, new LifecycleMessage.ValidateServerStore(defaultStoreName, storeConfiguration)), - InvalidServerStoreConfigurationException.class, expectedMessageContent); + assertThat(activeEntity.invokeActive(client.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, storeConfiguration)), + succeeds()); } @Test public void testValidateServerStore_DedicatedStoreResourceNamesDifferent() throws Exception { - ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); activeEntity.createNew(); - TestInvokeContext context = new TestInvokeContext(); - activeEntity.connected(context.getClientDescriptor()); + TestClientDescriptor client = TestClientDescriptor.newClient(); + activeEntity.connected(client); ServerStoreConfiguration storeConfiguration = new ServerStoreConfigBuilder() .dedicated("otherResource", 1, MemoryUnit.MEGABYTES) @@ -729,8 +751,8 @@ public void testValidateServerStore_DedicatedStoreResourceNamesDifferent() throw ", desired: " + storeConfiguration.getPoolAllocation(); - assertFailure(activeEntity.invokeActive(context, new LifecycleMessage.ValidateServerStore(defaultStoreName, storeConfiguration)), - InvalidServerStoreConfigurationException.class, expectedMessageContent); + assertThat(activeEntity.invokeActive(client.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, storeConfiguration)), + failsWith(both(IsInstanceOf.any(InvalidServerStoreConfigurationException.class)).and(withMessage(containsString(expectedMessageContent))))); } @Test @@ -740,11 +762,11 @@ public void testValidateServerStore_DifferentSharedPools() throws Exception { .shared(defaultSharedPool) .build(); ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, - new ClusterTierEntityConfiguration(identifier, defaultStoreName, storeConfiguration), DEFAULT_MAPPER); + new ClusterTierEntityConfiguration(identifier, defaultStoreName, storeConfiguration), DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); activeEntity.createNew(); - TestInvokeContext context = new TestInvokeContext(); - activeEntity.connected(context.getClientDescriptor()); + TestClientDescriptor client = TestClientDescriptor.newClient(); + activeEntity.connected(client); ServerStoreConfiguration otherConfiguration = new ServerStoreConfigBuilder() .shared("other") @@ -757,14 +779,13 @@ public void testValidateServerStore_DifferentSharedPools() throws Exception { ", desired: " + otherConfiguration.getPoolAllocation(); - assertFailure(activeEntity.invokeActive(context, - new LifecycleMessage.ValidateServerStore(defaultStoreName, - otherConfiguration)),InvalidServerStoreConfigurationException.class,expectedMessageContent); + assertThat(activeEntity.invokeActive(client.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, otherConfiguration)), + failsWith(both(IsInstanceOf.any(InvalidServerStoreConfigurationException.class)).and(withMessage(containsString(expectedMessageContent))))); } @Test public void testDestroyServerStore() throws Exception { - ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); activeEntity.createNew(); activeEntity.destroy(); @@ -784,7 +805,7 @@ public void testDestroyServerStore() throws Exception { public void testSharedPoolCacheNameCollision() throws Exception { defaultRegistry.addSharedPool(defaultStoreName, MemoryUnit.MEGABYTES.toBytes(2), defaultResource); - ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); activeEntity.createNew(); assertThat(defaultRegistry.getStoreManagerService().getSharedResourcePoolIds(), contains(defaultStoreName)); @@ -799,7 +820,7 @@ public void testCreateNonExistentSharedPool() throws Exception { .build(); ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, - new ClusterTierEntityConfiguration(identifier, defaultStoreName, storeConfiguration), DEFAULT_MAPPER); + new ClusterTierEntityConfiguration(identifier, defaultStoreName, storeConfiguration), DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); try { activeEntity.createNew(); fail("Creation with non-existent shared pool should have failed"); @@ -814,7 +835,7 @@ public void testCreateUnknownServerResource() throws Exception { .dedicated("unknown", 2, MemoryUnit.MEGABYTES) .build(); ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, - new ClusterTierEntityConfiguration(identifier, defaultStoreName, storeConfiguration), DEFAULT_MAPPER); + new ClusterTierEntityConfiguration(identifier, defaultStoreName, storeConfiguration), DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); try { activeEntity.createNew(); fail("Creation with non-existent shared pool should have failed"); @@ -825,14 +846,14 @@ public void testCreateUnknownServerResource() throws Exception { @Test public void testSyncToPassiveNoData() throws Exception { - ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); activeEntity.createNew(); - TestInvokeContext context = new TestInvokeContext(); - activeEntity.connected(context.getClientDescriptor()); + TestClientDescriptor client = TestClientDescriptor.newClient(); + activeEntity.connected(client); - assertSuccess(activeEntity.invokeActive(context, new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration))); + assertThat(activeEntity.invokeActive(client.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)), succeeds()); @SuppressWarnings("unchecked") PassiveSynchronizationChannel syncChannel = mock(PassiveSynchronizationChannel.class); @@ -843,20 +864,20 @@ public void testSyncToPassiveNoData() throws Exception { @Test public void testSyncToPassiveBatchedByDefault() throws Exception { - ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); activeEntity.createNew(); - TestInvokeContext context = new TestInvokeContext(); - activeEntity.connected(context.getClientDescriptor()); + TestClientDescriptor client = TestClientDescriptor.newClient(); + activeEntity.connected(client); - assertSuccess(activeEntity.invokeActive(context, new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration))); + assertThat(activeEntity.invokeActive(client.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)), succeeds()); ByteBuffer payload = ByteBuffer.allocate(512); // Put keys that maps to the same concurrency key - assertSuccess(activeEntity.invokeActive(context, new ServerStoreOpMessage.AppendMessage(1L, payload))); - assertSuccess(activeEntity.invokeActive(context, new ServerStoreOpMessage.AppendMessage(-2L, payload))); - assertSuccess(activeEntity.invokeActive(context, new ServerStoreOpMessage.AppendMessage(17L, payload))); + assertThat(activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.AppendMessage(1L, payload)), succeeds()); + assertThat(activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.AppendMessage(-2L, payload)), succeeds()); + assertThat(activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.AppendMessage(17L, payload)), succeeds()); @SuppressWarnings("unchecked") PassiveSynchronizationChannel syncChannel = mock(PassiveSynchronizationChannel.class); @@ -917,37 +938,42 @@ public void testDataSyncToPassiveException() throws Exception { @Test public void testLoadExistingRecoversInflightInvalidationsForEventualCache() throws Exception { - ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); EhcacheStateServiceImpl ehcacheStateService = defaultRegistry.getStoreManagerService(); ehcacheStateService.createStore(defaultStoreName, defaultStoreConfiguration, false); //Passive would have done this before failover InvalidationTracker invalidationTracker = ehcacheStateService.getInvalidationTracker(defaultStoreName); Random random = new Random(); - random.ints(0, 100).limit(10).forEach(invalidationTracker::trackHashInvalidation); + random.ints(0, 100).distinct().limit(10).forEach(invalidationTracker::trackHashInvalidation); - activeEntity.loadExisting(); + ClientDescriptor client = mock(ClientDescriptor.class); + try (ActiveServerEntity.ReconnectHandler reconnect = activeEntity.startReconnect()) { + reconnect.handleReconnect(client, new ReconnectMessageCodec().encode(new ClusterTierReconnectMessage(false))); + } + + ClientCommunicator clientCommunicator = defaultRegistry.getService(new CommunicatorServiceConfiguration()); - assertThat(activeEntity.getInflightInvalidations().isEmpty(), is(false)); + verify(clientCommunicator, times(10)).sendNoResponse(ArgumentMatchers.eq(client), ArgumentMatchers.isA(EhcacheEntityResponse.ClientInvalidateHash.class)); } @Test @SuppressWarnings("unchecked") public void testReplicationMessageAndOriginalServerStoreOpMessageHasSameConcurrency() throws Exception { - ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); activeEntity.createNew(); IEntityMessenger entityMessenger = defaultRegistry.getEntityMessenger(); - TestInvokeContext context = new TestInvokeContext(); - activeEntity.connected(context.getClientDescriptor()); + TestClientDescriptor client = TestClientDescriptor.newClient(); + activeEntity.connected(client); - assertSuccess(activeEntity.invokeActive(context, new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration))); + assertThat(activeEntity.invokeActive(client.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)), succeeds()); reset(entityMessenger); EhcacheEntityMessage getAndAppend = new ServerStoreOpMessage.GetAndAppendMessage(1L, createPayload(1L)); - activeEntity.invokeActive(context, getAndAppend); + activeEntity.invokeActive(client.invokeContext(), getAndAppend); ArgumentCaptor captor = ArgumentCaptor.forClass(PassiveReplicationMessage.ChainReplicationMessage.class); verify(entityMessenger).messageSelfAndDeferRetirement(isNotNull(), captor.capture()); @@ -958,13 +984,13 @@ public void testReplicationMessageAndOriginalServerStoreOpMessageHasSameConcurre @Test public void testInvalidMessageThrowsError() throws Exception { - ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); - TestInvokeContext context = new TestInvokeContext(); - activeEntity.connected(context.getClientDescriptor()); + TestClientDescriptor client = TestClientDescriptor.newClient(); + activeEntity.connected(client); try { - activeEntity.invokeActive(context, new InvalidMessage()); + activeEntity.invokeActive(client.invokeContext(), new InvalidMessage()); fail("Invalid message should result in AssertionError"); } catch (AssertionError e) { assertThat(e.getMessage(), containsString("Unsupported")); @@ -972,88 +998,186 @@ public void testInvalidMessageThrowsError() throws Exception { } @Test - public void testActiveTracksMessageDuplication() throws Exception { - ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); - activeEntity.createNew(); + public void testActiveMessageTracking() throws Exception { + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); + EhcacheStateServiceImpl ehcacheStateService = defaultRegistry.getStoreManagerService(); + ehcacheStateService.createStore(defaultStoreName, defaultStoreConfiguration, false); //hack to enable message tracking on active - TestInvokeContext context = new TestInvokeContext(); - activeEntity.connected(context.getClientDescriptor()); - assertSuccess(activeEntity.invokeActive(context, new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration))); + TestClientDescriptor client = TestClientDescriptor.newClient(); + activeEntity.connected(client); - ServerStoreOpMessage.AppendMessage message = new ServerStoreOpMessage.AppendMessage(1L, createPayload(1L)); - activeEntity.invokeActive(context, message); + assertThat(activeEntity.invokeActive(client.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)), succeeds()); - // create another message that has the same message ID - message = new ServerStoreOpMessage.AppendMessage(2L, createPayload(1L)); + ActiveInvokeContext context = client.invokeContext(); - activeEntity.invokeActive(context, message); // this invoke should be rejected due to duplicate message id + EhcacheEntityResponse expected = activeEntity.invokeActive(context, new ServerStoreOpMessage.GetAndAppendMessage(1L, createPayload(1L))); - ServerStoreOpMessage.GetMessage getMessage = new ServerStoreOpMessage.GetMessage(2L); - EhcacheEntityResponse.GetResponse response = (EhcacheEntityResponse.GetResponse) activeEntity.invokeActive(context, getMessage); - assertThat(response.getChain().isEmpty(), is(false)); + // this invoke should be rejected due to duplicate message id + EhcacheEntityResponse actual = activeEntity.invokeActive(context, new ServerStoreOpMessage.GetAndAppendMessage(1L, createPayload(2L))); + + assertThat(actual, sameInstance(expected)); + + EhcacheEntityResponse.GetResponse response = (EhcacheEntityResponse.GetResponse) activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.GetMessage(1L)); + assertThat(response.getChain(), hasPayloads(1L)); + } + + @Test @SuppressWarnings("unchecked") + public void testShortIterationIsNotTracked() throws Exception { + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); + EhcacheStateServiceImpl ehcacheStateService = defaultRegistry.getStoreManagerService(); + ehcacheStateService.createStore(defaultStoreName, defaultStoreConfiguration, false); //hack to enable message tracking on active + + TestClientDescriptor client = TestClientDescriptor.newClient(); + activeEntity.connected(client); + + assertThat(activeEntity.invokeActive(client.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)), succeeds()); + + activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.AppendMessage(1L, createPayload(1L))); + activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.AppendMessage(1L, createPayload(2L))); + activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.AppendMessage(2L, createPayload(3L))); + activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.AppendMessage(2L, createPayload(4L))); + + EhcacheEntityResponse.IteratorBatch iteratorBatch = (EhcacheEntityResponse.IteratorBatch) activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.IteratorOpenMessage(Integer.MAX_VALUE)); + + assertThat(iteratorBatch.isLast(), is(true)); + assertThat(iteratorBatch.getChains(), containsInAnyOrder( + entry(is(1L), hasPayloads(1L, 2L)), + entry(is(2L), hasPayloads(3L, 4L)) + )); + + assertThat(activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.IteratorAdvanceMessage(iteratorBatch.getIdentity(), Integer.MAX_VALUE)), failsWith(instanceOf(InvalidOperationException.class))); } @Test - public void testActiveMessageTracking() throws Exception { - ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); + public void testLongIteration() throws Exception { + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); EhcacheStateServiceImpl ehcacheStateService = defaultRegistry.getStoreManagerService(); ehcacheStateService.createStore(defaultStoreName, defaultStoreConfiguration, false); //hack to enable message tracking on active - TestInvokeContext context = new TestInvokeContext(); - activeEntity.connected(context.getClientDescriptor()); + TestClientDescriptor client = TestClientDescriptor.newClient(); + activeEntity.connected(client); - assertSuccess(activeEntity.invokeActive(context, new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration))); + assertThat(activeEntity.invokeActive(client.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)), succeeds()); - context.incrementCurrentTransactionId(); + activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.AppendMessage(1L, createPayload(1L))); + activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.AppendMessage(1L, createPayload(2L))); + activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.AppendMessage(2L, createPayload(3L))); + activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.AppendMessage(2L, createPayload(4L))); - ServerStoreOpMessage.AppendMessage message = new ServerStoreOpMessage.AppendMessage(1L, createPayload(1L)); - EhcacheEntityResponse expected = activeEntity.invokeActive(context, message); + EhcacheEntityResponse.IteratorBatch batchOne = (EhcacheEntityResponse.IteratorBatch) activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.IteratorOpenMessage(1)); - // create another message that has the same message ID - message = new ServerStoreOpMessage.AppendMessage(1L, createPayload(1L)); + Matcher> chainOne = entry(is(1L), hasPayloads(1L, 2L)); + Matcher> chainTwo = entry(is(2L), hasPayloads(3L, 4L)); - EhcacheEntityResponse actual = activeEntity.invokeActive(context, message); // this invoke should be rejected due to duplicate message id - assertThat(actual, sameInstance(expected)); + assertThat(batchOne.isLast(), is(false)); + assertThat(batchOne.getChains(), either(contains(chainOne)).or(contains(chainTwo))); + + EhcacheEntityResponse.IteratorBatch batchTwo = (EhcacheEntityResponse.IteratorBatch) activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.IteratorAdvanceMessage(batchOne.getIdentity(), Integer.MAX_VALUE)); + assertThat(batchTwo.isLast(), is(true)); + if (contains(chainOne).matches(batchOne.getChains())) { + assertThat(batchTwo.getChains(), contains(chainTwo)); + } else { + assertThat(batchTwo.getChains(), contains(chainOne)); + } + + assertThat(activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.IteratorAdvanceMessage(batchOne.getIdentity(), Integer.MAX_VALUE)), failsWith(instanceOf(InvalidOperationException.class))); + } + + @Test + public void testExplicitIteratorClose() throws Exception { + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); + EhcacheStateServiceImpl ehcacheStateService = defaultRegistry.getStoreManagerService(); + ehcacheStateService.createStore(defaultStoreName, defaultStoreConfiguration, false); //hack to enable message tracking on active + + TestClientDescriptor client = TestClientDescriptor.newClient(); + activeEntity.connected(client); + + assertThat(activeEntity.invokeActive(client.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)), succeeds()); + + activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.AppendMessage(1L, createPayload(1L))); + activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.AppendMessage(1L, createPayload(2L))); + activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.AppendMessage(2L, createPayload(3L))); + activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.AppendMessage(2L, createPayload(4L))); + + EhcacheEntityResponse.IteratorBatch batchOne = (EhcacheEntityResponse.IteratorBatch) activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.IteratorOpenMessage(1)); + + Matcher> chainOne = entry(is(1L), hasPayloads(1L, 2L)); + Matcher> chainTwo = entry(is(2L), hasPayloads(3L, 4L)); + + assertThat(batchOne.isLast(), is(false)); + assertThat(batchOne.getChains(), either(contains(chainOne)).or(contains(chainTwo))); + + assertThat(activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.IteratorCloseMessage(batchOne.getIdentity())), succeeds()); + + assertThat(activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.IteratorAdvanceMessage(batchOne.getIdentity(), Integer.MAX_VALUE)), failsWith(instanceOf(InvalidOperationException.class))); } private void prepareAndRunActiveEntityForPassiveSync(BiConsumer testConsumer) throws Exception { - ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); + ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER, SYNC_GETS_EXECUTOR); activeEntity.createNew(); - TestInvokeContext context = new TestInvokeContext(); - activeEntity.connected(context.getClientDescriptor()); + TestClientDescriptor client = TestClientDescriptor.newClient(); + activeEntity.connected(client); - assertSuccess(activeEntity.invokeActive(context, new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration))); + assertThat(activeEntity.invokeActive(client.invokeContext(), new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration)), succeeds()); ByteBuffer payload = ByteBuffer.allocate(512); // Put keys that maps to the same concurrency key ServerStoreOpMessage.AppendMessage testMessage = new ServerStoreOpMessage.AppendMessage(1L, payload); - activeEntity.invokeActive(context, testMessage); - activeEntity.invokeActive(context, new ServerStoreOpMessage.AppendMessage(-2L, payload)); - activeEntity.invokeActive(context, new ServerStoreOpMessage.AppendMessage(17L, payload)); - activeEntity.invokeActive(context, new ServerStoreOpMessage.AppendMessage(33L, payload)); + activeEntity.invokeActive(client.invokeContext(), testMessage); + activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.AppendMessage(-2L, payload)); + activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.AppendMessage(17L, payload)); + activeEntity.invokeActive(client.invokeContext(), new ServerStoreOpMessage.AppendMessage(33L, payload)); ConcurrencyStrategies.DefaultConcurrencyStrategy concurrencyStrategy = new ConcurrencyStrategies.DefaultConcurrencyStrategy(DEFAULT_MAPPER); int concurrencyKey = concurrencyStrategy.concurrencyKey(testMessage); testConsumer.accept(activeEntity, concurrencyKey); } - private void assertSuccess(EhcacheEntityResponse response) throws Exception { - if (!EhcacheResponseType.SUCCESS.equals(response.getResponseType())) { - throw ((EhcacheEntityResponse.Failure) response).getCause(); - } + private Matcher succeeds() { + return new TypeSafeMatcher() { + @Override + protected boolean matchesSafely(EhcacheEntityResponse item) { + return EhcacheResponseType.SUCCESS.equals(item.getResponseType()); + } + + @Override + public void describeTo(Description description) { + description.appendText(" a success response"); + } + }; } - private void assertFailure(EhcacheEntityResponse response, Class expectedException) { - assertThat(response.getResponseType(), is(EhcacheResponseType.FAILURE)); - assertThat(((EhcacheEntityResponse.Failure) response).getCause(), is(instanceOf(expectedException))); + private Matcher failsWith(Matcher failure) { + return new TypeSafeMatcher() { + @Override + protected boolean matchesSafely(EhcacheEntityResponse item) { + if (EhcacheResponseType.FAILURE.equals(item.getResponseType())) { + return failure.matches(((EhcacheEntityResponse.Failure) item).getCause()); + } else { + return false; + } + } + + @Override + public void describeTo(Description description) { + description.appendText(" failure caused by ").appendDescriptionOf(failure); + } + }; } - private void assertFailure(EhcacheEntityResponse response, Class expectedException, String expectedMessageContent) { - assertThat(response.getResponseType(), is(EhcacheResponseType.FAILURE)); - Exception cause = ((EhcacheEntityResponse.Failure) response).getCause(); - assertThat(cause, is(instanceOf(expectedException))); - assertThat(cause.getMessage(), containsString(expectedMessageContent)); + private Matcher withMessage(Matcher message) { + return new TypeSafeMatcher() { + @Override + protected boolean matchesSafely(Throwable item) { + return message.matches(item.getMessage()); + } + + @Override + public void describeTo(Description description) { + description.appendText(" throwable with message ").appendDescriptionOf(message); + } + }; } @SuppressWarnings("unchecked") @@ -1075,7 +1199,7 @@ public T getService(final ServiceConfiguration configuration) { } else if (serviceType.isAssignableFrom(EntityManagementRegistry.class)) { return (T) entityManagementRegistry; } else if (serviceType.isAssignableFrom(OOOMessageHandler.class)) { - return (T) new OOOMessageHandlerImpl<>(message -> true, 1, message -> 0); + return (T) new OOOMessageHandlerImpl<>(message -> true, () -> {}); } throw new AssertionError("Unknown service configuration of type: " + serviceType); } @@ -1213,19 +1337,6 @@ private IEntityMessenger getEntityM return entityMessenger; } - private ClientCommunicator getClientCommunicator() { - return clientCommunicator; - } - - private static Set getIdentifiers(Set pools) { - Set names = new HashSet<>(); - for (OffHeapResourceIdentifier identifier: pools) { - names.add(identifier.getName()); - } - - return Collections.unmodifiableSet(names); - } - @SuppressWarnings("unchecked") @Override public T getService(ServiceConfiguration serviceConfiguration) { @@ -1246,6 +1357,11 @@ public Set getAllIdentifiers() { public OffHeapResource getOffHeapResource(OffHeapResourceIdentifier identifier) { return pools.get(identifier); } + + @Override + public boolean addOffHeapResource(OffHeapResourceIdentifier identifier, long capacity) { + return false; + } }, new ServerSideConfiguration(sharedPools), DEFAULT_MAPPER, service -> {}); try { this.storeManagerService.configure(); @@ -1263,8 +1379,7 @@ public OffHeapResource getOffHeapResource(OffHeapResourceIdentifier identifier) return null; } else if(serviceConfiguration instanceof OOOMessageHandlerConfiguration) { OOOMessageHandlerConfiguration oooMessageHandlerConfiguration = (OOOMessageHandlerConfiguration) serviceConfiguration; - return (T) new OOOMessageHandlerImpl<>(oooMessageHandlerConfiguration.getTrackerPolicy(), - oooMessageHandlerConfiguration.getSegments(), oooMessageHandlerConfiguration.getSegmentationStrategy()); + return (T) new OOOMessageHandlerImpl<>(oooMessageHandlerConfiguration.getTrackerPolicy(), () -> {}); } throw new UnsupportedOperationException("Registry.getService does not support " + serviceConfiguration.getClass().getName()); @@ -1319,6 +1434,21 @@ public long capacity() { return capacity; } + @Override + public boolean setCapacity(long size) throws IllegalArgumentException { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public void addUsageListener(UUID listenerUUID, float threshold, Consumer consumer) { + + } + + @Override + public void removeUsageListener(UUID listenerUUID) throws IllegalArgumentException { + + } + private long getUsed() { return used; } diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/store/ClusterTierPassiveEntityTest.java b/clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/store/ClusterTierPassiveEntityTest.java similarity index 88% rename from clustered/server/src/test/java/org/ehcache/clustered/server/store/ClusterTierPassiveEntityTest.java rename to clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/store/ClusterTierPassiveEntityTest.java index abac774f35..ad6604a27f 100644 --- a/clustered/server/src/test/java/org/ehcache/clustered/server/store/ClusterTierPassiveEntityTest.java +++ b/clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/store/ClusterTierPassiveEntityTest.java @@ -23,10 +23,9 @@ import org.ehcache.clustered.common.internal.messages.ServerStoreOpMessage; import org.ehcache.clustered.common.internal.store.Chain; import org.ehcache.clustered.common.internal.store.ClusterTierEntityConfiguration; -import org.ehcache.clustered.common.internal.store.Util; import org.ehcache.clustered.server.EhcacheStateServiceImpl; import org.ehcache.clustered.server.KeySegmentMapper; -import org.ehcache.clustered.server.TestInvokeContext; +import org.ehcache.clustered.server.TestClientDescriptor; import org.ehcache.clustered.server.internal.messages.PassiveReplicationMessage; import org.ehcache.clustered.server.state.EhcacheStateService; import org.junit.Before; @@ -45,6 +44,7 @@ import org.terracotta.offheapresource.OffHeapResource; import org.terracotta.offheapresource.OffHeapResourceIdentifier; import org.terracotta.offheapresource.OffHeapResources; +import org.terracotta.offheapresource.OffHeapUsageEvent; import org.terracotta.offheapstore.util.MemoryUnit; import java.util.Collection; @@ -53,13 +53,16 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.UUID; +import java.util.function.Consumer; -import static org.ehcache.clustered.common.internal.store.Util.createPayload; +import static org.ehcache.clustered.ChainUtils.createPayload; +import static org.ehcache.clustered.ChainUtils.sequencedChainOf; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; @@ -130,9 +133,9 @@ public void testDestroyServerStore() throws Exception { @Test public void testInvalidMessageThrowsError() throws Exception { ClusterTierPassiveEntity passiveEntity = new ClusterTierPassiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); - TestInvokeContext context = new TestInvokeContext(); + TestClientDescriptor client = TestClientDescriptor.newClient(); try { - passiveEntity.invokePassive(context, new InvalidMessage()); + passiveEntity.invokePassive(client.invokeContext(), new InvalidMessage()); fail("Invalid message should result in AssertionError"); } catch (AssertionError e) { assertThat(e.getMessage(), containsString("Unsupported")); @@ -144,26 +147,26 @@ public void testPassiveTracksMessageDuplication() throws Exception { ClusterTierPassiveEntity passiveEntity = new ClusterTierPassiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); passiveEntity.createNew(); - Chain chain = Util.getChain(true, createPayload(1L)); - TestInvokeContext context = new TestInvokeContext(); + Chain chain = sequencedChainOf(createPayload(1L)); + TestClientDescriptor client = TestClientDescriptor.newClient(); long clientId = 3; PassiveReplicationMessage message1 = new PassiveReplicationMessage.ChainReplicationMessage(2, chain, 2L, 1L, clientId); - passiveEntity.invokePassive(context, message1); + passiveEntity.invokePassive(client.invokeContext(), message1); // Should be added assertThat(passiveEntity.getStateService().getStore(passiveEntity.getStoreIdentifier()).get(2).isEmpty(), is(false)); - Chain emptyChain = Util.getChain(true); + Chain emptyChain = sequencedChainOf(); PassiveReplicationMessage message2 = new PassiveReplicationMessage.ChainReplicationMessage(2, emptyChain, 2L, 1L, clientId); - passiveEntity.invokePassive(context, message2); + passiveEntity.invokePassive(client.invokeContext(), message2); // Should not be cleared, message is a duplicate assertThat(passiveEntity.getStateService().getStore(passiveEntity.getStoreIdentifier()).get(2).isEmpty(), is(false)); PassiveReplicationMessage message3 = new PassiveReplicationMessage.ChainReplicationMessage(2, chain, 3L, 1L, clientId); - passiveEntity.invokePassive(context, message3); + passiveEntity.invokePassive(client.invokeContext(), message3); // Should be added as well, different message id assertThat(passiveEntity.getStateService().getStore(passiveEntity.getStoreIdentifier()).get(2).isEmpty(), is(false)); @@ -173,17 +176,17 @@ public void testPassiveTracksMessageDuplication() throws Exception { public void testOversizeReplaceAtHeadMessage() throws Exception { ClusterTierPassiveEntity passiveEntity = new ClusterTierPassiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); passiveEntity.createNew(); - TestInvokeContext context = new TestInvokeContext(); + TestClientDescriptor client = TestClientDescriptor.newClient(); int key = 2; - Chain chain = Util.getChain(true, createPayload(1L)); + Chain chain = sequencedChainOf(createPayload(1L)); PassiveReplicationMessage message = new PassiveReplicationMessage.ChainReplicationMessage(key, chain, 2L, 1L, 3L); - passiveEntity.invokePassive(context, message); + passiveEntity.invokePassive(client.invokeContext(), message); - Chain oversizeChain = Util.getChain(true, createPayload(2L, 1024 * 1024)); + Chain oversizeChain = sequencedChainOf(createPayload(2L, 1024 * 1024)); ServerStoreOpMessage.ReplaceAtHeadMessage oversizeMsg = new ServerStoreOpMessage.ReplaceAtHeadMessage(key, chain, oversizeChain); - passiveEntity.invokePassive(context, oversizeMsg); + passiveEntity.invokePassive(client.invokeContext(), oversizeMsg); // Should be evicted, the value is oversize. assertThat(passiveEntity.getStateService().getStore(passiveEntity.getStoreIdentifier()).get(key).isEmpty(), is(true)); } @@ -192,12 +195,12 @@ public void testOversizeReplaceAtHeadMessage() throws Exception { public void testOversizeChainReplicationMessage() throws Exception { ClusterTierPassiveEntity passiveEntity = new ClusterTierPassiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); passiveEntity.createNew(); - TestInvokeContext context = new TestInvokeContext(); + TestClientDescriptor client = TestClientDescriptor.newClient(); long key = 2L; - Chain oversizeChain = Util.getChain(true, createPayload(key, 1024 * 1024)); + Chain oversizeChain = sequencedChainOf(createPayload(key, 1024 * 1024)); PassiveReplicationMessage oversizeMsg = new PassiveReplicationMessage.ChainReplicationMessage(key, oversizeChain, 2L, 1L, (long) 3); - passiveEntity.invokePassive(context, oversizeMsg); + passiveEntity.invokePassive(client.invokeContext(), oversizeMsg); // Should be cleared, the value is oversize. assertThat(passiveEntity.getStateService().getStore(passiveEntity.getStoreIdentifier()).get(key).isEmpty(), is(true)); } @@ -341,6 +344,11 @@ public Set getAllIdentifiers() { public OffHeapResource getOffHeapResource(OffHeapResourceIdentifier identifier) { return pools.get(identifier); } + + @Override + public boolean addOffHeapResource(OffHeapResourceIdentifier identifier, long capacity) { + return false; + } }, new ServerSideConfiguration(sharedPools), DEFAULT_MAPPER, service -> {}); try { this.storeManagerService.configure(); @@ -357,8 +365,7 @@ public OffHeapResource getOffHeapResource(OffHeapResourceIdentifier identifier) return null; } else if(serviceConfiguration instanceof OOOMessageHandlerConfiguration) { OOOMessageHandlerConfiguration oooMessageHandlerConfiguration = (OOOMessageHandlerConfiguration) serviceConfiguration; - return (T) new OOOMessageHandlerImpl<>(oooMessageHandlerConfiguration.getTrackerPolicy(), - oooMessageHandlerConfiguration.getSegments(), oooMessageHandlerConfiguration.getSegmentationStrategy()); + return (T) new OOOMessageHandlerImpl<>(oooMessageHandlerConfiguration.getTrackerPolicy(), () -> {}); } throw new UnsupportedOperationException("Registry.getService does not support " + serviceConfiguration.getClass().getName()); @@ -413,6 +420,21 @@ public long capacity() { return capacity; } + @Override + public boolean setCapacity(long size) throws IllegalArgumentException { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public void addUsageListener(UUID listenerUUID, float threshold, Consumer consumer) { + + } + + @Override + public void removeUsageListener(UUID listenerUUID) throws IllegalArgumentException { + + } + private long getUsed() { return used; } diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/store/InvalidMessage.java b/clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/store/InvalidMessage.java similarity index 100% rename from clustered/server/src/test/java/org/ehcache/clustered/server/store/InvalidMessage.java rename to clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/store/InvalidMessage.java diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/store/LockManagerImplTest.java b/clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/store/LockManagerImplTest.java similarity index 79% rename from clustered/server/src/test/java/org/ehcache/clustered/server/store/LockManagerImplTest.java rename to clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/store/LockManagerImplTest.java index 6ca4982330..ea406743e2 100644 --- a/clustered/server/src/test/java/org/ehcache/clustered/server/store/LockManagerImplTest.java +++ b/clustered/server/ehcache-entity/src/test/java/org/ehcache/clustered/server/store/LockManagerImplTest.java @@ -18,21 +18,17 @@ import org.ehcache.clustered.server.TestClientDescriptor; import org.junit.Test; import org.mockito.ArgumentCaptor; -import org.mockito.Mockito; import org.terracotta.entity.ClientDescriptor; -import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; -import static org.hamcrest.Matchers.contains; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; -import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; @@ -41,7 +37,7 @@ public class LockManagerImplTest { @Test public void testLock() { LockManagerImpl lockManager = new LockManagerImpl(); - ClientDescriptor clientDescriptor = new TestClientDescriptor(); + ClientDescriptor clientDescriptor = TestClientDescriptor.newClient(); assertThat(lockManager.lock(1L, clientDescriptor), is(true)); assertThat(lockManager.lock(1L, clientDescriptor), is(false)); assertThat(lockManager.lock(2L, clientDescriptor), is(true)); @@ -50,7 +46,7 @@ public void testLock() { @Test public void testUnlock() { LockManagerImpl lockManager = new LockManagerImpl(); - ClientDescriptor clientDescriptor = new TestClientDescriptor(); + ClientDescriptor clientDescriptor = TestClientDescriptor.newClient(); assertThat(lockManager.lock(1L, clientDescriptor), is(true)); lockManager.unlock(1L); assertThat(lockManager.lock(1L, clientDescriptor), is(true)); @@ -60,8 +56,8 @@ public void testUnlock() { @SuppressWarnings("unchecked") public void testSweepLocksForClient() { LockManagerImpl lockManager = new LockManagerImpl(); - ClientDescriptor clientDescriptor1 = new TestClientDescriptor(); - ClientDescriptor clientDescriptor2 = new TestClientDescriptor(); + ClientDescriptor clientDescriptor1 = TestClientDescriptor.newClient(); + ClientDescriptor clientDescriptor2 = TestClientDescriptor.newClient(); assertThat(lockManager.lock(1L, clientDescriptor1), is(true)); assertThat(lockManager.lock(2L, clientDescriptor1), is(true)); @@ -98,21 +94,22 @@ public void testSweepLocksForClient() { public void testCreateLockStateAfterFailover() { LockManagerImpl lockManager = new LockManagerImpl(); - ClientDescriptor clientDescriptor = new TestClientDescriptor(); + ClientDescriptor clientDescriptor1 = TestClientDescriptor.newClient(); Set locks = new HashSet<>(); locks.add(1L); locks.add(100L); locks.add(1000L); - lockManager.createLockStateAfterFailover(clientDescriptor, locks); + lockManager.createLockStateAfterFailover(clientDescriptor1, locks); - ClientDescriptor clientDescriptor1 = new TestClientDescriptor(); + ClientDescriptor clientDescriptor2 = TestClientDescriptor.newClient(); - assertThat(lockManager.lock(100L, clientDescriptor1), is(false)); - assertThat(lockManager.lock(1000L, clientDescriptor1), is(false)); - assertThat(lockManager.lock(1L, clientDescriptor1), is(false)); + + assertThat(lockManager.lock(100L, clientDescriptor2), is(false)); + assertThat(lockManager.lock(1000L, clientDescriptor2), is(false)); + assertThat(lockManager.lock(1L, clientDescriptor2), is(false)); } -} \ No newline at end of file +} diff --git a/clustered/server/ehcache-service-api/build.gradle b/clustered/server/ehcache-service-api/build.gradle new file mode 100644 index 0000000000..ada3ceba19 --- /dev/null +++ b/clustered/server/ehcache-service-api/build.gradle @@ -0,0 +1,30 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id 'org.ehcache.build.clustered-server-module' +} + +publishing.publications.withType(MavenPublication) { + pom { + name = 'Ehcache 3 Clustering Server Storage API module' + description = 'The Server Storage API module of Ehcache 3' + } +} + +dependencies { + api project(':clustered:ehcache-common-api') +} diff --git a/clustered/server/ehcache-service-api/config/checkstyle-suppressions.xml b/clustered/server/ehcache-service-api/config/checkstyle-suppressions.xml new file mode 100644 index 0000000000..cb41d0baf7 --- /dev/null +++ b/clustered/server/ehcache-service-api/config/checkstyle-suppressions.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/KeySegmentMapper.java b/clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/KeySegmentMapper.java similarity index 93% rename from clustered/server/src/main/java/org/ehcache/clustered/server/KeySegmentMapper.java rename to clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/KeySegmentMapper.java index 138360f263..202c92c71d 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/KeySegmentMapper.java +++ b/clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/KeySegmentMapper.java @@ -16,9 +16,6 @@ package org.ehcache.clustered.server; -import com.tc.classloader.CommonComponent; - -@CommonComponent public class KeySegmentMapper { private final int segments; diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/ServerSideServerStore.java b/clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/ServerSideServerStore.java similarity index 80% rename from clustered/server/src/main/java/org/ehcache/clustered/server/ServerSideServerStore.java rename to clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/ServerSideServerStore.java index be5a576ebc..92400209d2 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/ServerSideServerStore.java +++ b/clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/ServerSideServerStore.java @@ -18,16 +18,13 @@ import org.ehcache.clustered.common.internal.ServerStoreConfiguration; import org.ehcache.clustered.common.internal.store.Chain; import org.ehcache.clustered.common.internal.store.ServerStore; -import org.terracotta.offheapstore.MapInternals; - -import com.tc.classloader.CommonComponent; import java.util.List; import java.util.Set; -@CommonComponent -public interface ServerSideServerStore extends ServerStore, MapInternals { - void setEvictionListener(ServerStoreEvictionListener listener); +public interface ServerSideServerStore extends ServerStore { + void setEventListener(ServerStoreEventListener listener); + void enableEvents(boolean enable); ServerStoreConfiguration getStoreConfiguration(); List> getSegmentKeySets(); void put(long key, Chain chain); diff --git a/clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/ServerStoreEventListener.java b/clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/ServerStoreEventListener.java new file mode 100644 index 0000000000..e161731366 --- /dev/null +++ b/clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/ServerStoreEventListener.java @@ -0,0 +1,46 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.clustered.server; + +import org.ehcache.clustered.common.internal.store.Chain; +import org.ehcache.clustered.server.offheap.InternalChain; + +import java.nio.ByteBuffer; + +/** + * ServerStore event listener interface + */ +public interface ServerStoreEventListener { + + /** + * Called when the ServerStore evicts a mapping. + *

+ * Always fired, even when events are not enabled, see: {@link ServerSideServerStore#enableEvents(boolean)}. + * @param key the key of the evicted mapping + * @param evictedChain the evicted chain. + */ + void onEviction(long key, InternalChain evictedChain); + + /** + * Called when the ServerStore appends to a mapping + *

+ * Not always fired, only when events are enabled, see: {@link ServerSideServerStore#enableEvents(boolean)}. + * @param beforeAppend the chain as it was before the append + * @param appended the appended operation + */ + void onAppend(Chain beforeAppend, ByteBuffer appended); + +} diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheStateRepoSyncMessage.java b/clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheStateRepoSyncMessage.java similarity index 100% rename from clustered/server/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheStateRepoSyncMessage.java rename to clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheStateRepoSyncMessage.java diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheSyncMessage.java b/clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheSyncMessage.java similarity index 93% rename from clustered/server/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheSyncMessage.java rename to clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheSyncMessage.java index 79dd79f908..6882a9574a 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheSyncMessage.java +++ b/clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheSyncMessage.java @@ -18,9 +18,6 @@ import org.ehcache.clustered.common.internal.messages.EhcacheEntityMessage; -import com.tc.classloader.CommonComponent; - -@CommonComponent public abstract class EhcacheSyncMessage extends EhcacheEntityMessage { public abstract SyncMessageType getMessageType(); diff --git a/core/build.gradle b/clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/internal/messages/SyncMessageType.java similarity index 78% rename from core/build.gradle rename to clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/internal/messages/SyncMessageType.java index ec8eea368d..ca0b75108e 100644 --- a/core/build.gradle +++ b/clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/internal/messages/SyncMessageType.java @@ -14,10 +14,13 @@ * limitations under the License. */ -apply plugin: EhDeploy +package org.ehcache.clustered.server.internal.messages; -dependencies { - api project(':api') - api "org.terracotta:statistics:$parent.statisticVersion" - testImplementation project(':spi-tester') +/** + * SyncMessageType + */ +public enum SyncMessageType { + STATE_REPO, + DATA, + MESSAGE_TRACKER; } diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/offheap/InternalChain.java b/clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/offheap/InternalChain.java similarity index 100% rename from clustered/server/src/main/java/org/ehcache/clustered/server/offheap/InternalChain.java rename to clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/offheap/InternalChain.java diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/repo/ServerStateRepository.java b/clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/repo/ServerStateRepository.java similarity index 100% rename from clustered/server/src/main/java/org/ehcache/clustered/server/repo/ServerStateRepository.java rename to clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/repo/ServerStateRepository.java diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/repo/StateRepositoryManager.java b/clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/repo/StateRepositoryManager.java similarity index 97% rename from clustered/server/src/main/java/org/ehcache/clustered/server/repo/StateRepositoryManager.java rename to clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/repo/StateRepositoryManager.java index cdad28f650..67ff72e91b 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/repo/StateRepositoryManager.java +++ b/clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/repo/StateRepositoryManager.java @@ -21,15 +21,12 @@ import org.ehcache.clustered.common.internal.messages.StateRepositoryOpMessage; import org.ehcache.clustered.server.internal.messages.EhcacheStateRepoSyncMessage; -import com.tc.classloader.CommonComponent; - import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import static java.util.Collections.emptyList; -@CommonComponent public class StateRepositoryManager { private final ConcurrentMap mapRepositoryMap = new ConcurrentHashMap<>(); diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/state/EhcacheStateContext.java b/clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/state/EhcacheStateContext.java similarity index 93% rename from clustered/server/src/main/java/org/ehcache/clustered/server/state/EhcacheStateContext.java rename to clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/state/EhcacheStateContext.java index 045ff8a7e7..4f895c685e 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/state/EhcacheStateContext.java +++ b/clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/state/EhcacheStateContext.java @@ -15,12 +15,9 @@ */ package org.ehcache.clustered.server.state; -import com.tc.classloader.CommonComponent; - /** * Marker interface to pass context between begin and end message processing. */ -@CommonComponent @FunctionalInterface public interface EhcacheStateContext extends AutoCloseable { void close(); diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/state/EhcacheStateService.java b/clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/state/EhcacheStateService.java similarity index 92% rename from clustered/server/src/main/java/org/ehcache/clustered/server/state/EhcacheStateService.java rename to clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/state/EhcacheStateService.java index 8309166839..2f3e851947 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/state/EhcacheStateService.java +++ b/clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/state/EhcacheStateService.java @@ -24,23 +24,20 @@ import org.ehcache.clustered.server.repo.StateRepositoryManager; import org.terracotta.entity.ConfigurationException; -import com.tc.classloader.CommonComponent; - import java.util.Map; import java.util.Set; -@CommonComponent public interface EhcacheStateService { String getDefaultServerResource(); Map getSharedResourcePools(); - ResourcePageSource getSharedResourcePageSource(String name); + Object getSharedResourcePageSource(String name); ServerSideConfiguration.Pool getDedicatedResourcePool(String name); - ResourcePageSource getDedicatedResourcePageSource(String name); + Object getDedicatedResourcePageSource(String name); ServerSideServerStore getStore(String name); diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/state/InvalidationTracker.java b/clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/state/InvalidationTracker.java similarity index 93% rename from clustered/server/src/main/java/org/ehcache/clustered/server/state/InvalidationTracker.java rename to clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/state/InvalidationTracker.java index d3c95d3ba2..fe599f20a9 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/state/InvalidationTracker.java +++ b/clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/state/InvalidationTracker.java @@ -16,11 +16,8 @@ package org.ehcache.clustered.server.state; -import com.tc.classloader.CommonComponent; - import java.util.Set; -@CommonComponent public interface InvalidationTracker { boolean isClearInProgress(); diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/state/config/EhcacheStateServiceConfig.java b/clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/state/config/EhcacheStateServiceConfig.java similarity index 96% rename from clustered/server/src/main/java/org/ehcache/clustered/server/state/config/EhcacheStateServiceConfig.java rename to clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/state/config/EhcacheStateServiceConfig.java index ab5c5a1b4a..17912bd1a3 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/state/config/EhcacheStateServiceConfig.java +++ b/clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/state/config/EhcacheStateServiceConfig.java @@ -22,9 +22,6 @@ import org.terracotta.entity.ServiceConfiguration; import org.terracotta.entity.ServiceRegistry; -import com.tc.classloader.CommonComponent; - -@CommonComponent public class EhcacheStateServiceConfig implements ServiceConfiguration { private final ClusterTierManagerConfiguration config; diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/state/config/EhcacheStoreStateServiceConfig.java b/clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/state/config/EhcacheStoreStateServiceConfig.java similarity index 95% rename from clustered/server/src/main/java/org/ehcache/clustered/server/state/config/EhcacheStoreStateServiceConfig.java rename to clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/state/config/EhcacheStoreStateServiceConfig.java index 3235c18d5b..e736370dd4 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/state/config/EhcacheStoreStateServiceConfig.java +++ b/clustered/server/ehcache-service-api/src/main/java/org/ehcache/clustered/server/state/config/EhcacheStoreStateServiceConfig.java @@ -20,9 +20,6 @@ import org.ehcache.clustered.server.state.EhcacheStateService; import org.terracotta.entity.ServiceConfiguration; -import com.tc.classloader.CommonComponent; - -@CommonComponent public class EhcacheStoreStateServiceConfig implements ServiceConfiguration { private final String managerIdentifier; diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/repo/ServerStateRepositoryTest.java b/clustered/server/ehcache-service-api/src/test/java/org/ehcache/clustered/server/repo/ServerStateRepositoryTest.java similarity index 98% rename from clustered/server/src/test/java/org/ehcache/clustered/server/repo/ServerStateRepositoryTest.java rename to clustered/server/ehcache-service-api/src/test/java/org/ehcache/clustered/server/repo/ServerStateRepositoryTest.java index c2201f369d..5e5e275bfd 100644 --- a/clustered/server/src/test/java/org/ehcache/clustered/server/repo/ServerStateRepositoryTest.java +++ b/clustered/server/ehcache-service-api/src/test/java/org/ehcache/clustered/server/repo/ServerStateRepositoryTest.java @@ -25,10 +25,10 @@ import java.util.Map; import java.util.Set; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.*; public class ServerStateRepositoryTest { diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/repo/StateRepositoryManagerTest.java b/clustered/server/ehcache-service-api/src/test/java/org/ehcache/clustered/server/repo/StateRepositoryManagerTest.java similarity index 96% rename from clustered/server/src/test/java/org/ehcache/clustered/server/repo/StateRepositoryManagerTest.java rename to clustered/server/ehcache-service-api/src/test/java/org/ehcache/clustered/server/repo/StateRepositoryManagerTest.java index b5decac5e6..9efda648a1 100644 --- a/clustered/server/src/test/java/org/ehcache/clustered/server/repo/StateRepositoryManagerTest.java +++ b/clustered/server/ehcache-service-api/src/test/java/org/ehcache/clustered/server/repo/StateRepositoryManagerTest.java @@ -20,9 +20,9 @@ import org.ehcache.clustered.common.internal.messages.StateRepositoryOpMessage; import org.junit.Test; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; public class StateRepositoryManagerTest { diff --git a/clustered/server/ehcache-service/build.gradle b/clustered/server/ehcache-service/build.gradle new file mode 100644 index 0000000000..c26735f52f --- /dev/null +++ b/clustered/server/ehcache-service/build.gradle @@ -0,0 +1,39 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id 'org.ehcache.build.clustered-server-module' +} + +publishing.publications.withType(MavenPublication) { + pom { + name = 'Ehcache 3 Clustering Server Storage Implementation module' + description = 'The Server Storage Implementation module of Ehcache 3' + } +} + +dependencies { + service project(':clustered:server:ehcache-service-api') + service "org.terracotta:offheap-resource:$terracottaPlatformVersion" + service "org.terracotta:statistics:$statisticVersion" + + implementation project(':clustered:ehcache-common') + implementation "org.terracotta:offheap-store:$offheapVersion" + + testImplementation project(':clustered:test-utils') + testImplementation "org.terracotta.management:monitoring-service-api:$terracottaPlatformVersion" + testImplementation "org.terracotta:passthrough-server:$terracottaPassthroughTestingVersion" +} diff --git a/clustered/server/ehcache-service/config/checkstyle-suppressions.xml b/clustered/server/ehcache-service/config/checkstyle-suppressions.xml new file mode 100644 index 0000000000..cb41d0baf7 --- /dev/null +++ b/clustered/server/ehcache-service/config/checkstyle-suppressions.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/EhcacheStateServiceImpl.java b/clustered/server/ehcache-service/src/main/java/org/ehcache/clustered/server/EhcacheStateServiceImpl.java similarity index 96% rename from clustered/server/src/main/java/org/ehcache/clustered/server/EhcacheStateServiceImpl.java rename to clustered/server/ehcache-service/src/main/java/org/ehcache/clustered/server/EhcacheStateServiceImpl.java index a31a00289f..c8f58d50a6 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/EhcacheStateServiceImpl.java +++ b/clustered/server/ehcache-service/src/main/java/org/ehcache/clustered/server/EhcacheStateServiceImpl.java @@ -211,20 +211,6 @@ private void checkConfigurationCompatibility(ServerSideConfiguration incomingCon + "Client: " + incomingConfig.getResourcePools().keySet() + " " + "Server: " + sharedResourcePools.keySet().toString()); } - - try { - for (Map.Entry pool : resolveResourcePools(incomingConfig).entrySet()) { - ServerSideConfiguration.Pool serverPool = this.sharedResourcePools.get(pool.getKey()).getPool(); - - if (!serverPool.equals(pool.getValue())) { - throw new InvalidServerSideConfigurationException("Pool '" + pool.getKey() + "' not equal. " - + "Client: " + pool.getValue() + " " - + "Server: " + serverPool); - } - } - } catch (ConfigurationException e) { - throw new InvalidServerSideConfigurationException(e.getMessage()); - } } private static Map resolveResourcePools(ServerSideConfiguration configuration) throws ConfigurationException { diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/ServerStoreImpl.java b/clustered/server/ehcache-service/src/main/java/org/ehcache/clustered/server/ServerStoreImpl.java similarity index 91% rename from clustered/server/src/main/java/org/ehcache/clustered/server/ServerStoreImpl.java rename to clustered/server/ehcache-service/src/main/java/org/ehcache/clustered/server/ServerStoreImpl.java index 72bee490e2..f96b1928ef 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/ServerStoreImpl.java +++ b/clustered/server/ehcache-service/src/main/java/org/ehcache/clustered/server/ServerStoreImpl.java @@ -21,18 +21,18 @@ import org.ehcache.clustered.server.offheap.OffHeapChainMap; import org.ehcache.clustered.server.offheap.OffHeapServerStore; import org.ehcache.clustered.server.state.ResourcePageSource; +import org.terracotta.offheapstore.MapInternals; import org.terracotta.offheapstore.exceptions.OversizeMappingException; import org.terracotta.offheapstore.paging.PageSource; -import com.tc.classloader.CommonComponent; - import java.nio.ByteBuffer; import java.util.AbstractList; +import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; -@CommonComponent -public class ServerStoreImpl implements ServerSideServerStore { +public class ServerStoreImpl implements ServerSideServerStore, MapInternals { private final ServerStoreConfiguration storeConfiguration; private final ResourcePageSource pageSource; @@ -51,8 +51,14 @@ public ServerStoreImpl(ServerStoreConfiguration storeConfiguration, ResourcePage this.store = new OffHeapServerStore(pageSource, mapper, writeBehindConfigured); } - public void setEvictionListener(ServerStoreEvictionListener listener) { - store.setEvictionListener(listener); + @Override + public void setEventListener(ServerStoreEventListener listener) { + store.setEventListener(listener); + } + + @Override + public void enableEvents(boolean enable) { + store.enableEvents(enable); } /** @@ -198,4 +204,9 @@ private void checkPayLoadSize(ByteBuffer payLoad) { ") bigger than pool size (" + pageSource.getPool().getSize() + ")"); } } + + @Override + public Iterator> iterator() { + return store.iterator(); + } } diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/offheap/ChainStorageEngine.java b/clustered/server/ehcache-service/src/main/java/org/ehcache/clustered/server/offheap/ChainStorageEngine.java similarity index 100% rename from clustered/server/src/main/java/org/ehcache/clustered/server/offheap/ChainStorageEngine.java rename to clustered/server/ehcache-service/src/main/java/org/ehcache/clustered/server/offheap/ChainStorageEngine.java diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/offheap/LongPortability.java b/clustered/server/ehcache-service/src/main/java/org/ehcache/clustered/server/offheap/LongPortability.java similarity index 100% rename from clustered/server/src/main/java/org/ehcache/clustered/server/offheap/LongPortability.java rename to clustered/server/ehcache-service/src/main/java/org/ehcache/clustered/server/offheap/LongPortability.java diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/offheap/OffHeapChainMap.java b/clustered/server/ehcache-service/src/main/java/org/ehcache/clustered/server/offheap/OffHeapChainMap.java similarity index 71% rename from clustered/server/src/main/java/org/ehcache/clustered/server/offheap/OffHeapChainMap.java rename to clustered/server/ehcache-service/src/main/java/org/ehcache/clustered/server/offheap/OffHeapChainMap.java index 68fd8eb964..4bed35acdb 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/offheap/OffHeapChainMap.java +++ b/clustered/server/ehcache-service/src/main/java/org/ehcache/clustered/server/offheap/OffHeapChainMap.java @@ -16,20 +16,17 @@ package org.ehcache.clustered.server.offheap; import java.nio.ByteBuffer; -import java.util.ArrayList; +import java.nio.IntBuffer; +import java.util.AbstractMap; import java.util.Collections; import java.util.Iterator; -import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.locks.Lock; import org.ehcache.clustered.common.internal.store.Chain; -import org.ehcache.clustered.common.internal.store.Element; -import org.ehcache.clustered.common.internal.store.Util; import org.terracotta.offheapstore.MapInternals; -import org.terracotta.offheapstore.ReadWriteLockedOffHeapClockCache; import org.terracotta.offheapstore.eviction.EvictionListener; import org.terracotta.offheapstore.eviction.EvictionListeningReadWriteLockedOffHeapClockCache; import org.terracotta.offheapstore.exceptions.OversizeMappingException; @@ -37,13 +34,15 @@ import org.terracotta.offheapstore.storage.portability.Portability; import org.terracotta.offheapstore.util.Factory; -public class OffHeapChainMap implements MapInternals { +import static org.ehcache.clustered.common.internal.util.ChainBuilder.chainFromList; + +public class OffHeapChainMap implements MapInternals, Iterable> { interface ChainMapEvictionListener { - void onEviction(K key); + void onEviction(K key, InternalChain evictedChain); } - protected final ReadWriteLockedOffHeapClockCache heads; + protected final HeadMap heads; private final ChainStorageEngine chainStorage; private volatile ChainMapEvictionListener evictionListener; @@ -54,7 +53,7 @@ private OffHeapChainMap(PageSource source, ChainStorageEngine storageEngine) Map.Entry entry = callable.call(); try { if (evictionListener != null) { - evictionListener.onEviction(entry.getKey()); + evictionListener.onEviction(entry.getKey(), entry.getValue()); } } finally { entry.getValue().close(); @@ -64,9 +63,7 @@ private OffHeapChainMap(PageSource source, ChainStorageEngine storageEngine) } }; - //TODO: EvictionListeningReadWriteLockedOffHeapClockCache lacks ctor that takes shareByThieving - // this.heads = new ReadWriteLockedOffHeapClockCache(source, shareByThieving, chainStorage); - this.heads = new EvictionListeningReadWriteLockedOffHeapClockCache<>(listener, source, chainStorage); + this.heads = new HeadMap<>(listener, source, chainStorage); } public OffHeapChainMap(PageSource source, Factory> storageEngineFactory) { @@ -78,7 +75,7 @@ public OffHeapChainMap(PageSource source, Portability keyPortability, } //For tests - OffHeapChainMap(ReadWriteLockedOffHeapClockCache heads, OffHeapChainStorageEngine chainStorage) { + OffHeapChainMap(HeadMap heads, OffHeapChainStorageEngine chainStorage) { this.chainStorage = chainStorage; this.heads = heads; } @@ -242,73 +239,50 @@ public Set keySet() { } } - private void evict() { - int evictionIndex = heads.getEvictionIndex(); - if (evictionIndex < 0) { - throw new OversizeMappingException("Storage Engine and Eviction Failed - Everything Pinned (" + getSize() + " mappings) \n" + "Storage Engine : " + chainStorage); - } else { - heads.evict(evictionIndex, false); - } - } - - private static final Chain EMPTY_CHAIN = new Chain() { - @Override - public Iterator reverseIterator() { - return Collections.emptyList().iterator(); - } - - @Override - public boolean isEmpty() { - return true; - } - - @Override - public int length() { - return 0; - } - - @Override - public Iterator iterator() { - return Collections.emptyList().iterator(); - } - }; - - public static Chain chain(ByteBuffer... buffers) { - final List list = new ArrayList<>(); - for (ByteBuffer b : buffers) { - list.add(element(b)); - } - - return new Chain() { - - final List elements = Collections.unmodifiableList(list); - - @Override - public Iterator iterator() { - return elements.iterator(); - } - - @Override - public Iterator reverseIterator() { - return Util.reverseIterator(elements); - } + @Override + public Iterator> iterator() { + Iterator> headsIterator = heads.detachedEntryIterator(); + return new Iterator>() { @Override - public boolean isEmpty() { - return elements.isEmpty(); + public boolean hasNext() { + return headsIterator.hasNext(); } @Override - public int length() { - return elements.size(); + public Map.Entry next() { + final Lock lock = heads.readLock(); + lock.lock(); + try { + Map.Entry entry = headsIterator.next(); + InternalChain chain = entry.getValue(); + if (chain == null) { + return new AbstractMap.SimpleImmutableEntry<>(entry.getKey(), EMPTY_CHAIN); + } else { + try { + return new AbstractMap.SimpleImmutableEntry<>(entry.getKey(), chain.detach()); + } finally { + chain.close(); + } + } + } finally { + lock.unlock(); + } } }; } - private static Element element(final ByteBuffer b) { - return b::asReadOnlyBuffer; + private void evict() { + int evictionIndex = heads.getEvictionIndex(); + if (evictionIndex < 0) { + throw new OversizeMappingException("Storage Engine and Eviction Failed - Everything Pinned (" + getSize() + " mappings) \n" + "Storage Engine : " + chainStorage); + } else { + heads.evict(evictionIndex, false); + } } + private static final Chain EMPTY_CHAIN = chainFromList(Collections.emptyList()); + @Override public long getSize() { return heads.getSize(); @@ -380,4 +354,50 @@ public Lock writeLock() { protected void storageEngineFailure(Object failure) { } + static class HeadMap extends EvictionListeningReadWriteLockedOffHeapClockCache { + + public HeadMap(EvictionListener listener, PageSource source, ChainStorageEngine chainStorage) { + super(listener, source, chainStorage); + } + + public Iterator> detachedEntryIterator() { + Lock lock = readLock(); + lock.lock(); + try { + return new LockedEntryIterator() { + @Override + protected Entry create(IntBuffer entry) { + Entry attachedEntry = super.create(entry); + + try (InternalChain chain = attachedEntry.getValue()) { + Chain detachedChain = chain.detach(); + return new SimpleImmutableEntry<>(attachedEntry.getKey(), new InternalChain() { + @Override + public Chain detach() { + return detachedChain; + } + + @Override + public boolean append(ByteBuffer element) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean replace(Chain expected, Chain replacement) { + throw new UnsupportedOperationException(); + } + + @Override + public void close() { + // + } + }); + } + } + }; + } finally { + lock.unlock(); + } + } + } } diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/offheap/OffHeapChainStorageEngine.java b/clustered/server/ehcache-service/src/main/java/org/ehcache/clustered/server/offheap/OffHeapChainStorageEngine.java similarity index 92% rename from clustered/server/src/main/java/org/ehcache/clustered/server/offheap/OffHeapChainStorageEngine.java rename to clustered/server/ehcache-service/src/main/java/org/ehcache/clustered/server/offheap/OffHeapChainStorageEngine.java index 8601790d64..a41e80afdf 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/offheap/OffHeapChainStorageEngine.java +++ b/clustered/server/ehcache-service/src/main/java/org/ehcache/clustered/server/offheap/OffHeapChainStorageEngine.java @@ -17,6 +17,7 @@ import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -26,7 +27,6 @@ import org.ehcache.clustered.common.internal.store.Chain; import org.ehcache.clustered.common.internal.store.Element; import org.ehcache.clustered.common.internal.store.SequencedElement; -import org.ehcache.clustered.common.internal.store.Util; import org.terracotta.offheapstore.paging.OffHeapStorageArea; import org.terracotta.offheapstore.paging.PageSource; import org.terracotta.offheapstore.storage.BinaryStorageEngine; @@ -36,14 +36,38 @@ import org.terracotta.offheapstore.storage.portability.WriteContext; import org.terracotta.offheapstore.util.Factory; -import static java.util.Collections.unmodifiableList; +import static java.util.Collections.emptyList; +import static org.ehcache.clustered.common.internal.util.ChainBuilder.chainFromList; public class OffHeapChainStorageEngine implements ChainStorageEngine, BinaryStorageEngine { + + /* + * ELEMENT + * 0 7 + * 0 | sequence number | + * 8 | length | next | + * 16 | next | [[----- + * --- contents ---]] (length bytes) + * + * `next` is the address of the next element in the chain. + * `next` on the last element in the chain points to the chain head. + * (byte 0 in the heads chain structure, not byte 0 in the element) + */ private static final int ELEMENT_HEADER_SEQUENCE_OFFSET = 0; private static final int ELEMENT_HEADER_LENGTH_OFFSET = 8; private static final int ELEMENT_HEADER_NEXT_OFFSET = 12; private static final int ELEMENT_HEADER_SIZE = 20; + /* + * CHAIN + * 0 7 + * 0 |k-length| k-hash | + * 8 | tail | + * [[--- ELEMENT ---]] + * [[ key-contents ]] (k-length bytes) + * + * `tail` is the address of the last element in the chain + */ private static final int CHAIN_HEADER_KEY_LENGTH_OFFSET = 0; private static final int CHAIN_HEADER_KEY_HASH_OFFSET = 4; private static final int CHAIN_HEADER_TAIL_OFFSET = 8; @@ -386,36 +410,6 @@ private long toExtensionAddress(long chainAddress) { return chainAddress + CHAIN_HEADER_SIZE; } - private static class DetachedChain implements Chain { - - private final List elements; - - private DetachedChain(List buffers) { - this.elements = unmodifiableList(buffers); - } - - @Override - public Iterator reverseIterator() { - return Util.reverseIterator(elements); - } - - @Override - public boolean isEmpty() { - return elements.isEmpty(); - } - - @Override - public int length() { - return elements.size(); - } - - @Override - public Iterator iterator() { - return elements.iterator(); - } - - } - /** * Represents the initial form of a chain before the storage engine writes the chain mapping * to the underlying map against the key. @@ -451,7 +445,7 @@ private static class GenesisLink extends GenesisChain { private final Element element; public GenesisLink(ByteBuffer buffer) { - element = () -> buffer; + element = buffer::asReadOnlyBuffer; } @Override @@ -503,7 +497,7 @@ public Chain detach() { element = storage.readLong(element + ELEMENT_HEADER_NEXT_OFFSET); } while (element != chain); - return new DetachedChain(buffers); + return chainFromList(buffers); } @Override @@ -532,20 +526,24 @@ public boolean replace(Chain expected, Chain replacement) { } } - public boolean removeHeader(Chain header) { + + /** + * @return false if storage can't be allocated for new header when whole chain is not removed, true otherwise + */ + public boolean removeHeader(Chain expected) { long suffixHead = chain + OffHeapChainStorageEngine.this.totalChainHeaderSize; - long prefixTail; - Iterator iterator = header.iterator(); + Iterator expectedIt = expected.iterator(); do { - if (!compare(iterator.next(), suffixHead)) { + if (!compare(expectedIt.next(), suffixHead)) { return true; } - prefixTail = suffixHead; suffixHead = storage.readLong(suffixHead + ELEMENT_HEADER_NEXT_OFFSET); - } while (iterator.hasNext()); + } while (expectedIt.hasNext() && suffixHead != chain); - if (suffixHead == chain) { + if (expectedIt.hasNext()) { + return true; + } else if (suffixHead == chain) { //whole chain removed int slot = owner.getSlotForHashAndEncoding(readKeyHash(chain), chain, ~0); if (!owner.evict(slot, true)) { @@ -570,7 +568,10 @@ public boolean removeHeader(Chain header) { } if (owner.updateEncoding(hash, chain, newChainAddress, ~0)) { - storage.writeLong(prefixTail + ELEMENT_HEADER_NEXT_OFFSET, chain); + // NOTE: we leave the original suffix head attached to the prefix so that it gets freed along with the + // prefix since we took a copy of it for the new chain. + storage.writeLong(suffixHead + ELEMENT_HEADER_NEXT_OFFSET, chain); + storage.writeLong(chain + CHAIN_HEADER_TAIL_OFFSET, suffixHead); chainMoved(chain, newChainAddress); free(); return true; @@ -583,6 +584,10 @@ public boolean removeHeader(Chain header) { } } + /** + * @return false if storage can't be allocated for new header when head of the current chain matches expected + * chain, true otherwise + */ public boolean replaceHeader(Chain expected, Chain replacement) { long suffixHead = chain + OffHeapChainStorageEngine.this.totalChainHeaderSize; long prefixTail; @@ -594,7 +599,11 @@ public boolean replaceHeader(Chain expected, Chain replacement) { } prefixTail = suffixHead; suffixHead = storage.readLong(suffixHead + ELEMENT_HEADER_NEXT_OFFSET); - } while (expectedIt.hasNext()); + } while (expectedIt.hasNext() && suffixHead != chain); + + if (expectedIt.hasNext()) { + return true; + } int hash = readKeyHash(chain); Long newChainAddress = createAttachedChain(readKeyBuffer(chain), hash, replacement.iterator()); @@ -800,16 +809,31 @@ private boolean isHead(long address) { class StorageOwner implements OffHeapStorageArea.Owner { @Override - public boolean evictAtAddress(long address, boolean shrink) { - long chain = findHead(address); + public Collection evictAtAddress(long address, boolean shrink) { + Collection elements = new ArrayList<>(); + long chain = -1L; + long element = address; + do { + elements.add(element); + if (isHead(element)) { + chain = element; + element += OffHeapChainStorageEngine.this.totalChainHeaderSize; + } + element = storage.readLong(element + ELEMENT_HEADER_NEXT_OFFSET); + } while (element != address); + for (AttachedInternalChain activeChain : activeChains) { if (activeChain.chain == chain) { - return false; + return emptyList(); } } int hash = storage.readInt(chain + CHAIN_HEADER_KEY_HASH_OFFSET); int slot = owner.getSlotForHashAndEncoding(hash, chain, ~0); - return owner.evict(slot, shrink); + if (owner.evict(slot, shrink)) { + return elements; + } else { + return emptyList(); + } } @Override diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/offheap/OffHeapServerStore.java b/clustered/server/ehcache-service/src/main/java/org/ehcache/clustered/server/offheap/OffHeapServerStore.java similarity index 74% rename from clustered/server/src/main/java/org/ehcache/clustered/server/offheap/OffHeapServerStore.java rename to clustered/server/ehcache-service/src/main/java/org/ehcache/clustered/server/offheap/OffHeapServerStore.java index 3547c6137a..f3bed5513e 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/offheap/OffHeapServerStore.java +++ b/clustered/server/ehcache-service/src/main/java/org/ehcache/clustered/server/offheap/OffHeapServerStore.java @@ -15,21 +15,24 @@ */ package org.ehcache.clustered.server.offheap; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.List; -import java.util.function.LongConsumer; -import java.util.function.LongFunction; - import org.ehcache.clustered.common.internal.store.Chain; import org.ehcache.clustered.common.internal.store.ServerStore; import org.ehcache.clustered.server.KeySegmentMapper; -import org.ehcache.clustered.server.ServerStoreEvictionListener; +import org.ehcache.clustered.server.ServerStoreEventListener; import org.ehcache.clustered.server.state.ResourcePageSource; import org.terracotta.offheapstore.MapInternals; import org.terracotta.offheapstore.exceptions.OversizeMappingException; import org.terracotta.offheapstore.paging.PageSource; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.function.LongConsumer; +import java.util.function.LongFunction; + import static org.terracotta.offheapstore.util.MemoryUnit.BYTES; import static org.terracotta.offheapstore.util.MemoryUnit.KILOBYTES; import static org.terracotta.offheapstore.util.MemoryUnit.MEGABYTES; @@ -40,6 +43,8 @@ public class OffHeapServerStore implements ServerStore, MapInternals { private final List> segments; private final KeySegmentMapper mapper; + private volatile ServerStoreEventListener listener; + private volatile boolean fireEvents; public OffHeapServerStore(List> segments, KeySegmentMapper mapper) { this.mapper = mapper; @@ -86,7 +91,11 @@ static long getMaxSize(long poolSize) { return maxSize; } - public void setEvictionListener(final ServerStoreEvictionListener listener) { + public void setEventListener(ServerStoreEventListener listener) { + if (this.listener != null) { + throw new IllegalStateException("ServerStoreEventListener instance already set"); + } + this.listener = listener; OffHeapChainMap.ChainMapEvictionListener chainMapEvictionListener = listener::onEviction; for (OffHeapChainMap segment : segments) { segment.setEvictionListener(chainMapEvictionListener); @@ -100,19 +109,40 @@ public Chain get(long key) { @Override public void append(long key, ByteBuffer payLoad) { + LongConsumer lambda; + if (listener != null && fireEvents) { + lambda = (k) -> { + Chain beforeAppend = segmentFor(k).getAndAppend(k, payLoad); + listener.onAppend(beforeAppend, payLoad.duplicate()); + }; + } else { + lambda = (k) -> segmentFor(k).append(k, payLoad); + } + try { - segmentFor(key).append(key, payLoad); + lambda.accept(key); } catch (OversizeMappingException e) { - consumeOversizeMappingException(key, (long k) -> segmentFor(k).append(k, payLoad)); + consumeOversizeMappingException(key, lambda); } } @Override public Chain getAndAppend(long key, ByteBuffer payLoad) { + LongFunction lambda; + if (listener != null && fireEvents) { + lambda = (k) -> { + Chain beforeAppend = segmentFor(k).getAndAppend(k, payLoad); + listener.onAppend(beforeAppend, payLoad.duplicate()); + return beforeAppend; + }; + } else { + lambda = (k) -> segmentFor(k).getAndAppend(k, payLoad); + } + try { - return segmentFor(key).getAndAppend(key, payLoad); + return lambda.apply(key); } catch (OversizeMappingException e) { - return handleOversizeMappingException(key, (long k) -> segmentFor(k).getAndAppend(k, payLoad)); + return handleOversizeMappingException(key, lambda); } } @@ -337,4 +367,80 @@ public long getDataVitalMemory() { return total; } + @Override + public Iterator> iterator() { + return new AggregateIterator>() { + @Override + protected Iterator> getNextIterator() { + return listIterator.next().iterator(); + } + }; + } + + public void enableEvents(boolean enable) { + this.fireEvents = enable; + } + + protected abstract class AggregateIterator implements Iterator { + + protected final Iterator> listIterator; + protected Iterator currentIterator; + + protected abstract Iterator getNextIterator(); + + public AggregateIterator() { + listIterator = segments.iterator(); + while (listIterator.hasNext()) { + currentIterator = getNextIterator(); + if (currentIterator.hasNext()) { + return; + } + } + } + + @Override + public boolean hasNext() { + if (currentIterator == null) { + return false; + } + + if (currentIterator.hasNext()) { + return true; + } else { + while (listIterator.hasNext()) { + currentIterator = getNextIterator(); + if (currentIterator.hasNext()) { + return true; + } + } + return false; + } + } + + @Override + public T next() { + if (currentIterator == null) { + throw new NoSuchElementException(); + } + + if (currentIterator.hasNext()) { + return currentIterator.next(); + } else { + while (listIterator.hasNext()) { + currentIterator = getNextIterator(); + + if (currentIterator.hasNext()) { + return currentIterator.next(); + } + } + } + + throw new NoSuchElementException(); + } + + @Override + public void remove() { + currentIterator.remove(); + } + } } diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/offheap/PinningOffHeapChainMap.java b/clustered/server/ehcache-service/src/main/java/org/ehcache/clustered/server/offheap/PinningOffHeapChainMap.java similarity index 100% rename from clustered/server/src/main/java/org/ehcache/clustered/server/offheap/PinningOffHeapChainMap.java rename to clustered/server/ehcache-service/src/main/java/org/ehcache/clustered/server/offheap/PinningOffHeapChainMap.java diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/state/EhcacheStateServiceDump.java b/clustered/server/ehcache-service/src/main/java/org/ehcache/clustered/server/state/EhcacheStateServiceDump.java similarity index 100% rename from clustered/server/src/main/java/org/ehcache/clustered/server/state/EhcacheStateServiceDump.java rename to clustered/server/ehcache-service/src/main/java/org/ehcache/clustered/server/state/EhcacheStateServiceDump.java diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/state/EhcacheStateServiceProvider.java b/clustered/server/ehcache-service/src/main/java/org/ehcache/clustered/server/state/EhcacheStateServiceProvider.java similarity index 86% rename from clustered/server/src/main/java/org/ehcache/clustered/server/state/EhcacheStateServiceProvider.java rename to clustered/server/ehcache-service/src/main/java/org/ehcache/clustered/server/state/EhcacheStateServiceProvider.java index e0ec5d9bfd..dd2f7fbefb 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/state/EhcacheStateServiceProvider.java +++ b/clustered/server/ehcache-service/src/main/java/org/ehcache/clustered/server/state/EhcacheStateServiceProvider.java @@ -25,17 +25,18 @@ import org.terracotta.entity.PlatformConfiguration; import org.terracotta.entity.ServiceConfiguration; import org.terracotta.entity.ServiceProvider; -import org.terracotta.entity.ServiceProviderCleanupException; import org.terracotta.entity.ServiceProviderConfiguration; import org.terracotta.entity.StateDumpCollector; import org.terracotta.offheapresource.OffHeapResources; +import java.io.Closeable; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -43,7 +44,7 @@ * {@link ServiceProvider} for {@link EhcacheStateService} */ @BuiltinService -public class EhcacheStateServiceProvider implements ServiceProvider { +public class EhcacheStateServiceProvider implements ServiceProvider, Closeable { private static final Logger LOGGER = LoggerFactory.getLogger(EhcacheStateServiceProvider.class); @@ -62,18 +63,20 @@ public void addStateTo(StateDumpCollector dump) { @Override public boolean initialize(ServiceProviderConfiguration configuration, PlatformConfiguration platformConfiguration) { Collection extendedConfiguration = platformConfiguration.getExtendedConfiguration(OffHeapResources.class); + // When a server is activated, there will ALWAYS be one OffHeapResources, that will hold the mapping configured by the user with the "offheap-resources" setting. + // In diagnostic mode, no extended configuration is loaded, so there won't be any OffHeapResources. In that case, we ask this service to be discarded (by returning false). if (extendedConfiguration.size() > 1) { throw new UnsupportedOperationException("There are " + extendedConfiguration.size() + " OffHeapResourcesProvider, this is not supported. " + - "There must be only one!"); + "There must be only one!"); } Iterator iterator = extendedConfiguration.iterator(); if (iterator.hasNext()) { offHeapResourcesProvider = iterator.next(); if (offHeapResourcesProvider.getAllIdentifiers().isEmpty()) { - throw new UnsupportedOperationException("There are no offheap-resource defined, this is not supported. There must be at least one!"); + LOGGER.warn("No offheap-resource defined - this will prevent provider from offering any EhcacheStateService."); } } else { - LOGGER.warn("No offheap-resource defined - this will prevent provider from offering any EhcacheStateService."); + return false; } return true; } @@ -116,6 +119,12 @@ public void prepareForSynchronization() { serviceMap.clear(); } + @Override + public void close() { + //passthrough test cleanup + serviceMap.values().forEach(EhcacheStateService::destroy); + } + public interface DestroyCallback { void destroy(EhcacheStateService service); } diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/state/InvalidationTrackerImpl.java b/clustered/server/ehcache-service/src/main/java/org/ehcache/clustered/server/state/InvalidationTrackerImpl.java similarity index 93% rename from clustered/server/src/main/java/org/ehcache/clustered/server/state/InvalidationTrackerImpl.java rename to clustered/server/ehcache-service/src/main/java/org/ehcache/clustered/server/state/InvalidationTrackerImpl.java index 02f118b354..b060aaaba6 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/state/InvalidationTrackerImpl.java +++ b/clustered/server/ehcache-service/src/main/java/org/ehcache/clustered/server/state/InvalidationTrackerImpl.java @@ -16,11 +16,14 @@ package org.ehcache.clustered.server.state; +import java.util.HashSet; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicBoolean; +import static java.util.Collections.unmodifiableSet; + public class InvalidationTrackerImpl implements InvalidationTracker { private final ConcurrentMap invalidationMap = new ConcurrentHashMap<>(); @@ -67,7 +70,7 @@ public void untrackHashInvalidation(long chainKey) { @Override public Set getTrackedKeys() { - return getInvalidationMap().keySet(); + return unmodifiableSet(new HashSet<>(getInvalidationMap().keySet())); } @Override diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/state/ResourcePageSource.java b/clustered/server/ehcache-service/src/main/java/org/ehcache/clustered/server/state/ResourcePageSource.java similarity index 97% rename from clustered/server/src/main/java/org/ehcache/clustered/server/state/ResourcePageSource.java rename to clustered/server/ehcache-service/src/main/java/org/ehcache/clustered/server/state/ResourcePageSource.java index ceb6e968dd..e77b9c9dde 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/state/ResourcePageSource.java +++ b/clustered/server/ehcache-service/src/main/java/org/ehcache/clustered/server/state/ResourcePageSource.java @@ -15,7 +15,6 @@ */ package org.ehcache.clustered.server.state; -import com.tc.classloader.CommonComponent; import org.ehcache.clustered.common.ServerSideConfiguration; import org.terracotta.offheapstore.buffersource.OffHeapBufferSource; import org.terracotta.offheapstore.paging.OffHeapStorageArea; @@ -30,7 +29,6 @@ * Pairs a {@link ServerSideConfiguration.Pool} and an {@link UpfrontAllocatingPageSource} instance providing storage * for the pool. */ -@CommonComponent public class ResourcePageSource implements PageSource { /** * A description of the resource allocation underlying this {@code PageSource}. diff --git a/clustered/server/src/main/resources/META-INF/services/org.terracotta.entity.ServiceProvider b/clustered/server/ehcache-service/src/main/resources/META-INF/services/org.terracotta.entity.ServiceProvider similarity index 100% rename from clustered/server/src/main/resources/META-INF/services/org.terracotta.entity.ServiceProvider rename to clustered/server/ehcache-service/src/main/resources/META-INF/services/org.terracotta.entity.ServiceProvider diff --git a/clustered/server/src/main/resources/offheap-message.properties b/clustered/server/ehcache-service/src/main/resources/offheap-message.properties similarity index 100% rename from clustered/server/src/main/resources/offheap-message.properties rename to clustered/server/ehcache-service/src/main/resources/offheap-message.properties diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/EhcacheStateServiceImplTest.java b/clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/EhcacheStateServiceImplTest.java similarity index 96% rename from clustered/server/src/test/java/org/ehcache/clustered/server/EhcacheStateServiceImplTest.java rename to clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/EhcacheStateServiceImplTest.java index 865f060072..b9507ea57a 100644 --- a/clustered/server/src/test/java/org/ehcache/clustered/server/EhcacheStateServiceImplTest.java +++ b/clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/EhcacheStateServiceImplTest.java @@ -19,10 +19,10 @@ import org.ehcache.clustered.common.internal.exceptions.DestroyInProgressException; import org.junit.Test; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; public class EhcacheStateServiceImplTest { diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/offheap/ChainMapExtensionTest.java b/clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/offheap/ChainMapExtensionTest.java similarity index 96% rename from clustered/server/src/test/java/org/ehcache/clustered/server/offheap/ChainMapExtensionTest.java rename to clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/offheap/ChainMapExtensionTest.java index ea0690a3a2..8a79bd0daf 100644 --- a/clustered/server/src/test/java/org/ehcache/clustered/server/offheap/ChainMapExtensionTest.java +++ b/clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/offheap/ChainMapExtensionTest.java @@ -32,16 +32,18 @@ import org.terracotta.offheapstore.util.Factory; import java.nio.ByteBuffer; +import java.util.Collection; import java.util.HashSet; import java.util.Set; import java.util.concurrent.locks.Lock; -import static org.ehcache.clustered.server.offheap.OffHeapChainMap.chain; +import static org.ehcache.clustered.ChainUtils.chainOf; +import static java.util.Collections.emptyList; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.emptyIterable; import static org.hamcrest.collection.IsIterableContainingInOrder.contains; -import static org.junit.Assert.assertThat; /** * Test extensibility of chain map storage engine, including binary engine capabilities. @@ -81,7 +83,7 @@ public void testAppendAndReplace() { OffHeapChainMap map = getChainMapWithExtendedStorageEngine(); map.append("foo", buffer(1)); assertThat(map.get("foo"), contains(element(1))); - map.replaceAtHead("foo", chain(buffer(1)), chain()); + map.replaceAtHead("foo", chainOf(buffer(1)), chainOf()); ChainStorageEngine se = map.getStorageEngine(); assertThat(se, is(instanceOf(ExtendedOffHeapChainStorageEngine.class))); @SuppressWarnings("unchecked") @@ -102,7 +104,7 @@ public void testMultipleAppendAndReplace() { assertThat(map.getAndAppend("foo" + i, buffer(1)), contains(element(i))); } for (int i = 10; i < 15; i++) { - map.replaceAtHead("foo" + i, chain(buffer(i), buffer(1)), chain()); + map.replaceAtHead("foo" + i, chainOf(buffer(i), buffer(1)), chainOf()); } ChainStorageEngine se = map.getStorageEngine(); @@ -131,8 +133,7 @@ private OffHeapChainMap getNewMap(ExtendedOffHeapChainStorageEngine> factory = OffHeapChainStorageEngine.createFactory(chainSource, StringPortability.INSTANCE, 4096, 4096, false, false); OffHeapChainStorageEngine storageEngine = (OffHeapChainStorageEngine) factory.newInstance(); - ReadWriteLockedOffHeapClockCache newMap = - new ReadWriteLockedOffHeapClockCache<>(chainSource, storageEngine); + OffHeapChainMap.HeadMap newMap = new OffHeapChainMap.HeadMap<>(e -> {}, chainSource, storageEngine); ese.replayIntoMap(newMap); return new OffHeapChainMap<>(newMap, storageEngine); } @@ -338,8 +339,8 @@ private void localMove(long fromChainAddress, long toChainAddress) { private class ExtendedEngineOwner implements OffHeapStorageArea.Owner { @Override - public boolean evictAtAddress(long address, boolean shrink) { - return false; + public Collection evictAtAddress(long address, boolean shrink) { + return emptyList(); } @Override diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/offheap/ChainMapTest.java b/clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/offheap/ChainMapTest.java similarity index 86% rename from clustered/server/src/test/java/org/ehcache/clustered/server/offheap/ChainMapTest.java rename to clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/offheap/ChainMapTest.java index 5e2cf5e82b..d3f95b0770 100644 --- a/clustered/server/src/test/java/org/ehcache/clustered/server/offheap/ChainMapTest.java +++ b/clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/offheap/ChainMapTest.java @@ -44,13 +44,13 @@ import org.terracotta.offheapstore.storage.portability.StringPortability; import static java.util.Arrays.asList; -import static org.ehcache.clustered.server.offheap.OffHeapChainMap.chain; +import static org.ehcache.clustered.ChainUtils.chainOf; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.lessThan; import static org.hamcrest.collection.IsEmptyIterable.emptyIterable; import static org.hamcrest.collection.IsIterableContainingInOrder.contains; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.terracotta.offheapstore.util.MemoryUnit.KILOBYTES; @@ -79,6 +79,8 @@ public void testInitiallyEmptyChain() { OffHeapChainMap map = new OffHeapChainMap<>(new UnlimitedPageSource(new OffHeapBufferSource()), StringPortability.INSTANCE, minPageSize, maxPageSize, steal); assertThat(map.get("foo"), emptyIterable()); + + emptyAndValidate(map); } @Test @@ -87,6 +89,8 @@ public void testAppendToEmptyChain() { map.append("foo", buffer(1)); assertThat(map.get("foo"), contains(element(1))); + + emptyAndValidate(map); } @Test @@ -95,6 +99,8 @@ public void testGetAndAppendToEmptyChain() { assertThat(map.getAndAppend("foo", buffer(1)), emptyIterable()); assertThat(map.get("foo"), contains(element(1))); + + emptyAndValidate(map); } @Test @@ -104,6 +110,8 @@ public void testAppendToSingletonChain() { map.append("foo", buffer(2)); assertThat(map.get("foo"), contains(element(1), element(2))); + + emptyAndValidate(map); } @Test @@ -113,6 +121,8 @@ public void testGetAndAppendToSingletonChain() { assertThat(map.getAndAppend("foo", buffer(2)), contains(element(1))); assertThat(map.get("foo"), contains(element(1), element(2))); + + emptyAndValidate(map); } @Test @@ -123,6 +133,8 @@ public void testAppendToDoubleChain() { map.append("foo", buffer(3)); assertThat(map.get("foo"), contains(element(1), element(2), element(3))); + + emptyAndValidate(map); } @Test @@ -133,6 +145,8 @@ public void testGetAndAppendToDoubleChain() { assertThat(map.getAndAppend("foo", buffer(3)), contains(element(1), element(2))); assertThat(map.get("foo"), contains(element(1), element(2), element(3))); + + emptyAndValidate(map); } @Test @@ -144,6 +158,8 @@ public void testAppendToTripleChain() { map.append("foo", buffer(4)); assertThat(map.get("foo"), contains(element(1), element(2), element(3), element(4))); + + emptyAndValidate(map); } @Test @@ -155,6 +171,8 @@ public void testGetAndAppendToTripleChain() { assertThat(map.getAndAppend("foo", buffer(4)), contains(element(1), element(2), element(3))); assertThat(map.get("foo"), contains(element(1), element(2), element(3), element(4))); + + emptyAndValidate(map); } @Test @@ -162,11 +180,13 @@ public void testReplaceEmptyChainAtHeadOnEmptyChainFails() { OffHeapChainMap map = new OffHeapChainMap<>(new UnlimitedPageSource(new OffHeapBufferSource()), StringPortability.INSTANCE, minPageSize, maxPageSize, steal); try { - map.replaceAtHead("foo", chain(), chain(buffer(1))); + map.replaceAtHead("foo", chainOf(), chainOf(buffer(1))); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException e) { //expected } + + emptyAndValidate(map); } @Test @@ -175,11 +195,13 @@ public void testReplaceEmptyChainAtHeadOnNonEmptyChain() { map.append("foo", buffer(1)); try { - map.replaceAtHead("foo", chain(), chain(buffer(2))); + map.replaceAtHead("foo", chainOf(), chainOf(buffer(2))); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException e) { //expected } + + emptyAndValidate(map); } @Test @@ -187,8 +209,10 @@ public void testMismatchingReplaceSingletonChainAtHeadOnSingletonChain() { OffHeapChainMap map = new OffHeapChainMap<>(new UnlimitedPageSource(new OffHeapBufferSource()), StringPortability.INSTANCE, minPageSize, maxPageSize, steal); map.append("foo", buffer(1)); - map.replaceAtHead("foo", chain(buffer(2)), chain(buffer(42))); + map.replaceAtHead("foo", chainOf(buffer(2)), chainOf(buffer(42))); assertThat(map.get("foo"), contains(element(1))); + + emptyAndValidate(map); } @Test @@ -196,8 +220,10 @@ public void testReplaceSingletonChainAtHeadOnSingletonChain() { OffHeapChainMap map = new OffHeapChainMap<>(new UnlimitedPageSource(new OffHeapBufferSource()), StringPortability.INSTANCE, minPageSize, maxPageSize, steal); map.append("foo", buffer(1)); - map.replaceAtHead("foo", chain(buffer(1)), chain(buffer(42))); + map.replaceAtHead("foo", chainOf(buffer(1)), chainOf(buffer(42))); assertThat(map.get("foo"), contains(element(42))); + + emptyAndValidate(map); } @Test @@ -206,8 +232,10 @@ public void testReplaceSingletonChainAtHeadOnDoubleChain() { map.append("foo", buffer(1)); map.append("foo", buffer(2)); - map.replaceAtHead("foo", chain(buffer(1)), chain(buffer(42))); + map.replaceAtHead("foo", chainOf(buffer(1)), chainOf(buffer(42))); assertThat(map.get("foo"), contains(element(42), element(2))); + + emptyAndValidate(map); } @Test @@ -217,8 +245,10 @@ public void testReplaceSingletonChainAtHeadOnTripleChain() { map.append("foo", buffer(2)); map.append("foo", buffer(3)); - map.replaceAtHead("foo", chain(buffer(1)), chain(buffer(42))); + map.replaceAtHead("foo", chainOf(buffer(1)), chainOf(buffer(42))); assertThat(map.get("foo"), contains(element(42), element(2), element(3))); + + emptyAndValidate(map); } @Test @@ -227,8 +257,10 @@ public void testMismatchingReplacePluralChainAtHead() { map.append("foo", buffer(1)); map.append("foo", buffer(2)); - map.replaceAtHead("foo", chain(buffer(1), buffer(3)), chain(buffer(42))); + map.replaceAtHead("foo", chainOf(buffer(1), buffer(3)), chainOf(buffer(42))); assertThat(map.get("foo"), contains(element(1), element(2))); + + emptyAndValidate(map); } @Test @@ -237,8 +269,10 @@ public void testReplacePluralChainAtHeadOnDoubleChain() { map.append("foo", buffer(1)); map.append("foo", buffer(2)); - map.replaceAtHead("foo", chain(buffer(1), buffer(2)), chain(buffer(42))); + map.replaceAtHead("foo", chainOf(buffer(1), buffer(2)), chainOf(buffer(42))); assertThat(map.get("foo"), contains(element(42))); + + emptyAndValidate(map); } @Test @@ -248,8 +282,10 @@ public void testReplacePluralChainAtHeadOnTripleChain() { map.append("foo", buffer(2)); map.append("foo", buffer(3)); - map.replaceAtHead("foo", chain(buffer(1), buffer(2)), chain(buffer(42))); + map.replaceAtHead("foo", chainOf(buffer(1), buffer(2)), chainOf(buffer(42))); assertThat(map.get("foo"), contains(element(42), element(3))); + + emptyAndValidate(map); } @Test @@ -260,9 +296,11 @@ public void testReplacePluralChainAtHeadWithEmpty() { map.append("foo", buffer(3)); long before = map.getDataOccupiedMemory(); - map.replaceAtHead("foo", chain(buffer(1), buffer(2)), chain()); + map.replaceAtHead("foo", chainOf(buffer(1), buffer(2)), chainOf()); assertThat(map.getDataOccupiedMemory(), lessThan(before)); assertThat(map.get("foo"), contains(element(3))); + + emptyAndValidate(map); } @Test @@ -272,8 +310,10 @@ public void testSequenceBasedChainComparison() { map.append("foo", buffer(2)); map.append("foo", buffer(3)); - map.replaceAtHead("foo", map.get("foo"), chain()); + map.replaceAtHead("foo", map.get("foo"), chainOf()); assertThat(map.get("foo"), emptyIterable()); + + emptyAndValidate(map); } @Test @@ -284,9 +324,11 @@ public void testReplaceFullPluralChainAtHeadWithEmpty() { map.append("foo", buffer(3)); assertThat(map.getDataOccupiedMemory(), greaterThan(0L)); - map.replaceAtHead("foo", chain(buffer(1), buffer(2), buffer(3)), chain()); + map.replaceAtHead("foo", chainOf(buffer(1), buffer(2), buffer(3)), chainOf()); assertThat(map.getDataOccupiedMemory(), is(0L)); assertThat(map.get("foo"), emptyIterable()); + + emptyAndValidate(map); } @Test @@ -312,6 +354,9 @@ public void testContinualAppendCausingEvictionIsStable() { break; } } + + emptyAndValidate(mapA); + emptyAndValidate(mapB); } else { OffHeapChainMap map = new OffHeapChainMap<>(pageSource, StringPortability.INSTANCE, minPageSize, maxPageSize, false); @@ -329,6 +374,8 @@ public void testContinualAppendCausingEvictionIsStable() { break; } } + + emptyAndValidate(map); } } @@ -336,17 +383,21 @@ public void testContinualAppendCausingEvictionIsStable() { public void testPutWhenKeyIsNotNull() { OffHeapChainMap map = new OffHeapChainMap<>(new UnlimitedPageSource(new OffHeapBufferSource()), StringPortability.INSTANCE, minPageSize, maxPageSize, steal); map.append("key", buffer(3)); - map.put("key", chain(buffer(1), buffer(2))); + map.put("key", chainOf(buffer(1), buffer(2))); assertThat(map.get("key"), contains(element(1), element(2))); + + emptyAndValidate(map); } @Test public void testPutWhenKeyIsNull() { OffHeapChainMap map = new OffHeapChainMap<>(new UnlimitedPageSource(new OffHeapBufferSource()), StringPortability.INSTANCE, minPageSize, maxPageSize, steal); - map.put("key", chain(buffer(1), buffer(2))); + map.put("key", chainOf(buffer(1), buffer(2))); assertThat(map.get("key"), contains(element(1), element(2))); + + emptyAndValidate(map); } @Test @@ -354,11 +405,11 @@ public void testActiveChainsThreadSafety() throws ExecutionException, Interrupte UnlimitedPageSource source = new UnlimitedPageSource(new OffHeapBufferSource()); OffHeapChainStorageEngine chainStorage = new OffHeapChainStorageEngine<>(source, StringPortability.INSTANCE, minPageSize, maxPageSize, steal, steal); - ReadWriteLockedOffHeapClockCache heads = new EvictionListeningReadWriteLockedOffHeapClockCache<>(callable -> {}, source, chainStorage); + OffHeapChainMap.HeadMap heads = new OffHeapChainMap.HeadMap<>(callable -> {}, source, chainStorage); OffHeapChainMap map = new OffHeapChainMap<>(heads, chainStorage); - map.put("key", chain(buffer(1), buffer(2))); + map.put("key", chainOf(buffer(1), buffer(2))); int nThreads = 10; ExecutorService executorService = Executors.newFixedThreadPool(nThreads); @@ -375,6 +426,7 @@ public void testActiveChainsThreadSafety() throws ExecutionException, Interrupte assertThat(chainStorage.getActiveChains().size(), is(0)); + emptyAndValidate(map); } @Test @@ -382,15 +434,16 @@ public void testPutDoesNotLeakWhenMappingIsNotNull() { UnlimitedPageSource source = new UnlimitedPageSource(new OffHeapBufferSource()); OffHeapChainStorageEngine chainStorage = new OffHeapChainStorageEngine<>(source, StringPortability.INSTANCE, minPageSize, maxPageSize, steal, steal); - ReadWriteLockedOffHeapClockCache heads = new EvictionListeningReadWriteLockedOffHeapClockCache<>(callable -> {}, source, chainStorage); + OffHeapChainMap.HeadMap heads = new OffHeapChainMap.HeadMap<>(callable -> {}, source, chainStorage); OffHeapChainMap map = new OffHeapChainMap<>(heads, chainStorage); - map.put("key", chain(buffer(1))); - map.put("key", chain(buffer(2))); + map.put("key", chainOf(buffer(1))); + map.put("key", chainOf(buffer(2))); assertThat(chainStorage.getActiveChains().size(), is(0)); + emptyAndValidate(map); } @Test @@ -446,5 +499,11 @@ public void describeTo(Description description) { }; } - + private void emptyAndValidate(OffHeapChainMap map) { + for (String key : map.keySet()) { + map.replaceAtHead(key, map.get(key), chainOf()); + } + assertThat(map.getSize(), is(0L)); + assertThat(map.getDataOccupiedMemory(), is(0L)); + } } diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/offheap/OffHeapServerStoreTest.java b/clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/offheap/OffHeapServerStoreTest.java similarity index 67% rename from clustered/server/src/test/java/org/ehcache/clustered/server/offheap/OffHeapServerStoreTest.java rename to clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/offheap/OffHeapServerStoreTest.java index a06f0d7fc5..bc71b29b54 100644 --- a/clustered/server/src/test/java/org/ehcache/clustered/server/offheap/OffHeapServerStoreTest.java +++ b/clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/offheap/OffHeapServerStoreTest.java @@ -16,11 +16,14 @@ package org.ehcache.clustered.server.offheap; import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; import java.util.Random; import java.util.concurrent.locks.ReentrantLock; import org.ehcache.clustered.common.internal.store.Chain; import org.ehcache.clustered.server.KeySegmentMapper; +import org.ehcache.clustered.server.ServerStoreEventListener; import org.ehcache.clustered.server.store.ChainBuilder; import org.ehcache.clustered.server.store.ElementBuilder; import org.ehcache.clustered.common.internal.store.ServerStore; @@ -35,11 +38,14 @@ import static java.util.Arrays.asList; import static java.util.Collections.singletonList; +import static org.ehcache.clustered.ChainUtils.chainOf; +import static org.ehcache.clustered.ChainUtils.createPayload; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.lessThanOrEqualTo; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.core.Is.is; -import org.junit.Assert; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.doNothing; @@ -50,6 +56,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.terracotta.offheapstore.util.MemoryUnit.GIGABYTES; +import static org.terracotta.offheapstore.util.MemoryUnit.KILOBYTES; import static org.terracotta.offheapstore.util.MemoryUnit.MEGABYTES; public class OffHeapServerStoreTest extends ServerStoreTest { @@ -83,13 +90,13 @@ public ChainBuilder newChainBuilder() { for (int i = 0; i < buffers.length; i++) { buffers[i] = elements[i].getPayload(); } - return OffHeapChainMap.chain(buffers); + return chainOf(buffers); }; } @Override public ElementBuilder newElementBuilder() { - return payLoad -> () -> payLoad; + return payLoad -> () -> payLoad.asReadOnlyBuffer(); } @Test @@ -302,14 +309,14 @@ public void testServerSideUsageStats() { store.getAndAppend(i, smallValue.duplicate()); } - Assert.assertThat(store.getAllocatedMemory(),lessThanOrEqualTo(maxBytes)); - Assert.assertThat(store.getAllocatedMemory(),greaterThanOrEqualTo(smallLoopCount * oneKb)); - Assert.assertThat(store.getAllocatedMemory(),greaterThanOrEqualTo(store.getOccupiedMemory())); + assertThat(store.getAllocatedMemory(),lessThanOrEqualTo(maxBytes)); + assertThat(store.getAllocatedMemory(),greaterThanOrEqualTo(smallLoopCount * oneKb)); + assertThat(store.getAllocatedMemory(),greaterThanOrEqualTo(store.getOccupiedMemory())); //asserts above already guarantee that occupiedMemory <= maxBytes and that occupiedMemory <= allocatedMemory - Assert.assertThat(store.getOccupiedMemory(),greaterThanOrEqualTo(smallLoopCount * oneKb)); + assertThat(store.getOccupiedMemory(),greaterThanOrEqualTo(smallLoopCount * oneKb)); - Assert.assertThat(store.getSize(), is(smallLoopCount)); + assertThat(store.getSize(), is(smallLoopCount)); int multiplier = 100; long largeLoopCount = 5 + smallLoopCount; @@ -318,15 +325,128 @@ public void testServerSideUsageStats() { store.getAndAppend(i, largeValue.duplicate()); } - Assert.assertThat(store.getAllocatedMemory(),lessThanOrEqualTo(maxBytes)); - Assert.assertThat(store.getAllocatedMemory(),greaterThanOrEqualTo( (smallLoopCount * oneKb) + ( (largeLoopCount - smallLoopCount) * oneKb * multiplier) )); - Assert.assertThat(store.getAllocatedMemory(),greaterThanOrEqualTo(store.getOccupiedMemory())); + assertThat(store.getAllocatedMemory(),lessThanOrEqualTo(maxBytes)); + assertThat(store.getAllocatedMemory(),greaterThanOrEqualTo( (smallLoopCount * oneKb) + ( (largeLoopCount - smallLoopCount) * oneKb * multiplier) )); + assertThat(store.getAllocatedMemory(),greaterThanOrEqualTo(store.getOccupiedMemory())); //asserts above already guarantee that occupiedMemory <= maxBytes and that occupiedMemory <= allocatedMemory - Assert.assertThat(store.getOccupiedMemory(),greaterThanOrEqualTo(smallLoopCount * oneKb)); + assertThat(store.getOccupiedMemory(),greaterThanOrEqualTo(smallLoopCount * oneKb)); - Assert.assertThat(store.getSize(), is(smallLoopCount + (largeLoopCount - smallLoopCount))); + assertThat(store.getSize(), is(smallLoopCount + (largeLoopCount - smallLoopCount))); } + @Test + public void testEvictionFiresEventsWithChainWhenEvictionIsEnabled() { + OffHeapServerStore store = new OffHeapServerStore(new UpfrontAllocatingPageSource(new OffHeapBufferSource(), (long) MEGABYTES.toBytes(1), MEGABYTES.toBytes(1)), new KeySegmentMapper(16), false); + AuditingServerStoreEventListener audit = new AuditingServerStoreEventListener(); + store.setEventListener(audit); + store.enableEvents(true); + + ByteBuffer buffer = ByteBuffer.allocate(KILOBYTES.toBytes(500)); + + store.append(1L, buffer.duplicate()); + store.append(2L, buffer.duplicate()); + store.append(3L, buffer.duplicate()); + + assertThat(store.getSize(), is(1L)); + assertThat(audit.onEviction.size(), is(2)); + assertThat(audit.onEviction.get(0).key, is(1L)); + assertThat(audit.onEviction.get(0).evictedChain, is(notNullValue())); + assertThat(audit.onEviction.get(1).key, is(2L)); + assertThat(audit.onEviction.get(1).evictedChain, is(notNullValue())); + } + + @Test + public void testNoEventFiredWhenDisabled() { + OffHeapServerStore store = new OffHeapServerStore(new UpfrontAllocatingPageSource(new OffHeapBufferSource(), (long) MEGABYTES.toBytes(1), MEGABYTES.toBytes(1)), new KeySegmentMapper(16), false); + AuditingServerStoreEventListener audit = new AuditingServerStoreEventListener(); + store.setEventListener(audit); + + store.append(1L, toBuffer(1)); + store.getAndAppend(1L, toBuffer(2)); + + store.enableEvents(true); + store.append(1L, toBuffer(3)); + store.getAndAppend(1L, toBuffer(4)); + + store.enableEvents(false); + store.append(1L, toBuffer(5)); + store.getAndAppend(1L, toBuffer(6)); + + assertThat(audit.onAppend.size(), is(2)); + assertThat(audit.onAppend.get(0).appended.asIntBuffer().get(), is(3)); + assertThat(audit.onAppend.get(1).appended.asIntBuffer().get(), is(4)); + } + + @Test + public void testAppendFiresEvents() { + OffHeapServerStore store = new OffHeapServerStore(new UpfrontAllocatingPageSource(new OffHeapBufferSource(), (long) MEGABYTES.toBytes(1), MEGABYTES.toBytes(1)), new KeySegmentMapper(16), false); + AuditingServerStoreEventListener audit = new AuditingServerStoreEventListener(); + store.setEventListener(audit); + store.enableEvents(true); + + store.append(1L, toBuffer(1)); + store.append(1L, toBuffer(2)); + store.append(1L, toBuffer(3)); + + assertThat(audit.onAppend.size(), is(3)); + assertThat(audit.onAppend.get(0).appended.asIntBuffer().get(), is(1)); + assertThat(audit.onAppend.get(1).appended.asIntBuffer().get(), is(2)); + assertThat(audit.onAppend.get(2).appended.asIntBuffer().get(), is(3)); + } + + @Test + public void testGetAndAppendFiresEvents() { + OffHeapServerStore store = new OffHeapServerStore(new UpfrontAllocatingPageSource(new OffHeapBufferSource(), (long) MEGABYTES.toBytes(1), MEGABYTES.toBytes(1)), new KeySegmentMapper(16), false); + AuditingServerStoreEventListener audit = new AuditingServerStoreEventListener(); + store.setEventListener(audit); + store.enableEvents(true); + + store.getAndAppend(1L, toBuffer(1)); + store.getAndAppend(1L, toBuffer(2)); + store.getAndAppend(1L, toBuffer(3)); + + assertThat(audit.onAppend.size(), is(3)); + assertThat(audit.onAppend.get(0).appended.asIntBuffer().get(), is(1)); + assertThat(audit.onAppend.get(1).appended.asIntBuffer().get(), is(2)); + assertThat(audit.onAppend.get(2).appended.asIntBuffer().get(), is(3)); + } + + private static class AuditingServerStoreEventListener implements ServerStoreEventListener { + private final List onAppend = new ArrayList<>(); + private final List onEviction = new ArrayList<>(); + @Override + public void onEviction(long key, InternalChain evictedChain) { + onEviction.add(new OnEvictionArgs(key, evictedChain)); + } + @Override + public void onAppend(Chain beforeAppend, ByteBuffer appended) { + onAppend.add(new OnAppendArgs(appended, beforeAppend)); + } + + static class OnEvictionArgs { + OnEvictionArgs(long key, InternalChain evictedChain) { + this.key = key; + this.evictedChain = evictedChain; + } + long key; + InternalChain evictedChain; + } + + static class OnAppendArgs { + OnAppendArgs(ByteBuffer appended, Chain beforeAppend) { + this.appended = appended; + this.beforeAppend = beforeAppend; + } + ByteBuffer appended; + Chain beforeAppend; + } + } + + private static ByteBuffer toBuffer(int i) { + ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES); + buffer.asIntBuffer().put(i); + return buffer; + } } diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/offheap/PinningOffHeapChainMapTest.java b/clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/offheap/PinningOffHeapChainMapTest.java similarity index 78% rename from clustered/server/src/test/java/org/ehcache/clustered/server/offheap/PinningOffHeapChainMapTest.java rename to clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/offheap/PinningOffHeapChainMapTest.java index c1a36790ba..39b699acf3 100644 --- a/clustered/server/src/test/java/org/ehcache/clustered/server/offheap/PinningOffHeapChainMapTest.java +++ b/clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/offheap/PinningOffHeapChainMapTest.java @@ -16,19 +16,14 @@ package org.ehcache.clustered.server.offheap; import org.ehcache.clustered.common.internal.store.Chain; -import org.ehcache.clustered.common.internal.store.Element; -import org.ehcache.clustered.common.internal.store.Util; import org.ehcache.clustered.common.internal.store.operations.OperationCode; import org.junit.Test; import org.terracotta.offheapstore.buffersource.OffHeapBufferSource; import org.terracotta.offheapstore.paging.UnlimitedPageSource; import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; +import static org.ehcache.clustered.ChainUtils.chainOf; import static org.ehcache.clustered.common.internal.store.operations.OperationCode.PUT; import static org.ehcache.clustered.common.internal.store.operations.OperationCode.PUT_IF_ABSENT; import static org.ehcache.clustered.common.internal.store.operations.OperationCode.PUT_WITH_WRITER; @@ -36,8 +31,8 @@ import static org.ehcache.clustered.common.internal.store.operations.OperationCode.REMOVE_CONDITIONAL; import static org.ehcache.clustered.common.internal.store.operations.OperationCode.REPLACE; import static org.ehcache.clustered.common.internal.store.operations.OperationCode.REPLACE_CONDITIONAL; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; public class PinningOffHeapChainMapTest { @Test @@ -76,7 +71,7 @@ public void testGetAndAppendWithNormalOperation() { public void testPutWithPinningChain() { PinningOffHeapChainMap pinningOffHeapChainMap = getPinningOffHeapChainMap(); - pinningOffHeapChainMap.put(1L, chain(buffer(PUT), buffer(REMOVE))); + pinningOffHeapChainMap.put(1L, chainOf(buffer(PUT), buffer(REMOVE))); assertThat(pinningOffHeapChainMap.heads.isPinned(1L), is(true)); } @@ -84,7 +79,7 @@ public void testPutWithPinningChain() { public void testPutWithNormalChain() { PinningOffHeapChainMap pinningOffHeapChainMap = getPinningOffHeapChainMap(); - pinningOffHeapChainMap.put(1L, chain(buffer(PUT), buffer(PUT))); + pinningOffHeapChainMap.put(1L, chainOf(buffer(PUT), buffer(PUT))); assertThat(pinningOffHeapChainMap.heads.isPinned(1L), is(false)); } @@ -93,8 +88,8 @@ public void testReplaceAtHeadWithUnpinningChain() { PinningOffHeapChainMap pinningOffHeapChainMap = getPinningOffHeapChainMap(); ByteBuffer buffer = buffer(PUT_IF_ABSENT); - Chain pinningChain = chain(buffer); - Chain unpinningChain = chain(buffer(PUT)); + Chain pinningChain = chainOf(buffer); + Chain unpinningChain = chainOf(buffer(PUT)); pinningOffHeapChainMap.append(1L, buffer); assertThat(pinningOffHeapChainMap.heads.isPinned(1L), is(true)); @@ -108,8 +103,8 @@ public void testReplaceAtHeadWithPinningChain() { PinningOffHeapChainMap pinningOffHeapChainMap = getPinningOffHeapChainMap(); ByteBuffer buffer = buffer(REPLACE); - Chain pinningChain = chain(buffer); - Chain unpinningChain = chain(buffer(REPLACE_CONDITIONAL)); + Chain pinningChain = chainOf(buffer); + Chain unpinningChain = chainOf(buffer(REPLACE_CONDITIONAL)); pinningOffHeapChainMap.append(1L, buffer); assertThat(pinningOffHeapChainMap.heads.isPinned(1L), is(true)); @@ -123,8 +118,8 @@ public void testReplaceAtHeadWithEmptyChain() { PinningOffHeapChainMap pinningOffHeapChainMap = getPinningOffHeapChainMap(); ByteBuffer buffer = buffer(PUT_WITH_WRITER); - Chain pinningChain = chain(buffer); - Chain unpinningChain = chain(); + Chain pinningChain = chainOf(buffer); + Chain unpinningChain = chainOf(); pinningOffHeapChainMap.append(1L, buffer); assertThat(pinningOffHeapChainMap.heads.isPinned(1L), is(true)); @@ -141,36 +136,4 @@ private PinningOffHeapChainMap getPinningOffHeapChainMap() { return new PinningOffHeapChainMap<>(new UnlimitedPageSource(new OffHeapBufferSource()), LongPortability.INSTANCE, 4096, 4096, false); } - - public static Chain chain(ByteBuffer... buffers) { - final List list = new ArrayList<>(); - for (ByteBuffer b : buffers) { - list.add(b::asReadOnlyBuffer); - } - - return new Chain() { - - final List elements = Collections.unmodifiableList(list); - - @Override - public Iterator iterator() { - return elements.iterator(); - } - - @Override - public Iterator reverseIterator() { - return Util.reverseIterator(elements); - } - - @Override - public boolean isEmpty() { - return elements.isEmpty(); - } - - @Override - public int length() { - return elements.size(); - } - }; - } } diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/state/EhcacheStateServiceProviderTest.java b/clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/state/EhcacheStateServiceProviderTest.java similarity index 99% rename from clustered/server/src/test/java/org/ehcache/clustered/server/state/EhcacheStateServiceProviderTest.java rename to clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/state/EhcacheStateServiceProviderTest.java index 00fd836a27..fc1e2881ad 100644 --- a/clustered/server/src/test/java/org/ehcache/clustered/server/state/EhcacheStateServiceProviderTest.java +++ b/clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/state/EhcacheStateServiceProviderTest.java @@ -36,12 +36,12 @@ import java.util.Collections; import static java.util.Collections.emptyMap; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.sameInstance; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/state/InvalidationTrackerImplTest.java b/clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/state/InvalidationTrackerImplTest.java similarity index 100% rename from clustered/server/src/test/java/org/ehcache/clustered/server/state/InvalidationTrackerImplTest.java rename to clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/state/InvalidationTrackerImplTest.java diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/store/ChainBuilder.java b/clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/store/ChainBuilder.java similarity index 100% rename from clustered/server/src/test/java/org/ehcache/clustered/server/store/ChainBuilder.java rename to clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/store/ChainBuilder.java diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/store/ElementBuilder.java b/clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/store/ElementBuilder.java similarity index 100% rename from clustered/server/src/test/java/org/ehcache/clustered/server/store/ElementBuilder.java rename to clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/store/ElementBuilder.java diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/store/ServerStoreTest.java b/clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/store/ServerStoreTest.java similarity index 61% rename from clustered/server/src/test/java/org/ehcache/clustered/server/store/ServerStoreTest.java rename to clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/store/ServerStoreTest.java index 62dbfc848f..7450450670 100644 --- a/clustered/server/src/test/java/org/ehcache/clustered/server/store/ServerStoreTest.java +++ b/clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/store/ServerStoreTest.java @@ -19,15 +19,27 @@ import org.ehcache.clustered.common.internal.store.Chain; import org.ehcache.clustered.common.internal.store.Element; import org.ehcache.clustered.common.internal.store.ServerStore; -import org.hamcrest.MatcherAssert; import org.hamcrest.core.Is; import org.junit.Test; import java.nio.ByteBuffer; +import java.util.HashSet; import java.util.Iterator; - -import static org.junit.Assert.assertThat; -import static org.hamcrest.Matchers.is; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.concurrent.TimeoutException; + +import static org.ehcache.clustered.ChainUtils.createPayload; +import static org.ehcache.clustered.ChainUtils.readPayload; +import static org.ehcache.clustered.Matchers.hasPayloads; +import static java.util.stream.LongStream.range; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsCollectionContaining.hasItem; +import static org.hamcrest.core.IsCollectionContaining.hasItems; +import static org.hamcrest.core.IsNot.not; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.fail; /** * Verify Server Store @@ -49,31 +61,6 @@ private static void populateStore(ServerStore store) throws Exception { } } - private static long readPayLoad(ByteBuffer byteBuffer) { - return byteBuffer.getLong(); - } - - protected static ByteBuffer createPayload(long key) { - ByteBuffer byteBuffer = ByteBuffer.allocate(8).putLong(key); - byteBuffer.flip(); - return byteBuffer; - } - - private static void assertChainAndReverseChainOnlyHave(Chain chain, long... payLoads) { - Iterator elements = chain.iterator(); - for (long payLoad : payLoads) { - assertThat(readPayLoad(elements.next().getPayload()), is(Long.valueOf(payLoad))); - } - assertThat(elements.hasNext(), is(false)); - - Iterator reverseElements = chain.reverseIterator(); - - for (int i = payLoads.length -1; i >= 0; i--) { - assertThat(readPayLoad(reverseElements.next().getPayload()), is(Long.valueOf(payLoads[i]))); - } - assertThat(reverseElements.hasNext(), is(false)); - } - @Test public void testGetNoMappingExists() throws Exception { ServerStore store = newStore(); @@ -86,28 +73,28 @@ public void testGetNoMappingExists() throws Exception { public void testGetMappingExists() throws Exception { ServerStore store = newStore(); populateStore(store); - Chain chain = store.get(1); + Chain chain = store.get(1L); assertThat(chain.isEmpty(), is(false)); - assertChainAndReverseChainOnlyHave(chain, 1); + assertThat(chain, hasPayloads(1L)); } @Test public void testAppendNoMappingExists() throws Exception { ServerStore store = newStore(); - store.append(1, createPayload(1)); - Chain chain = store.get(1); + store.append(1L, createPayload(1L)); + Chain chain = store.get(1L); assertThat(chain.isEmpty(), is(false)); - assertChainAndReverseChainOnlyHave(chain, 1); + assertThat(chain, hasPayloads(1L)); } @Test public void testAppendMappingExists() throws Exception { ServerStore store = newStore(); populateStore(store); - store.append(2, createPayload(22)); - Chain chain = store.get(2); + store.append(2L, createPayload(22L)); + Chain chain = store.get(2L); assertThat(chain.isEmpty(), is(false)); - assertChainAndReverseChainOnlyHave(chain, 2, 22); + assertThat(chain, hasPayloads(2L, 22L)); } @Test @@ -116,7 +103,7 @@ public void testGetAndAppendNoMappingExists() throws Exception { Chain chain = store.getAndAppend(1, createPayload(1)); assertThat(chain.isEmpty(), is(true)); chain = store.get(1); - assertChainAndReverseChainOnlyHave(chain, 1); + assertThat(chain, hasPayloads(1L)); } @Test @@ -125,10 +112,10 @@ public void testGetAndAppendMappingExists() throws Exception { populateStore(store); Chain chain = store.getAndAppend(1, createPayload(22)); for (Element element : chain) { - assertThat(readPayLoad(element.getPayload()), is(Long.valueOf(1))); + assertThat(readPayload(element.getPayload()), is(Long.valueOf(1))); } chain = store.get(1); - assertChainAndReverseChainOnlyHave(chain, 1, 22); + assertThat(chain, hasPayloads(1, 22)); } @Test @@ -139,7 +126,7 @@ public void testReplaceAtHeadSucceedsMappingExistsHeadMatchesStrictly() throws E store.replaceAtHead(1, existingMapping, chainBuilder.build(elementBuilder.build(createPayload(11)))); Chain chain = store.get(1); - assertChainAndReverseChainOnlyHave(chain, 11); + assertThat(chain, hasPayloads(11)); store.append(2, createPayload(22)); store.append(2, createPayload(222)); @@ -150,7 +137,7 @@ public void testReplaceAtHeadSucceedsMappingExistsHeadMatchesStrictly() throws E chain = store.get(2); - assertChainAndReverseChainOnlyHave(chain, 2222); + assertThat(chain, hasPayloads(2222)); } @Test @@ -165,7 +152,7 @@ public void testReplaceAtHeadSucceedsMappingExistsHeadMatches() throws Exception store.replaceAtHead(1, existingMapping, chainBuilder.build(elementBuilder.build(createPayload(111)))); Chain chain = store.get(1); - assertChainAndReverseChainOnlyHave(chain, 111, 11); + assertThat(chain, hasPayloads(111, 11)); store.append(2, createPayload(22)); existingMapping = store.get(2); @@ -175,7 +162,7 @@ public void testReplaceAtHeadSucceedsMappingExistsHeadMatches() throws Exception store.replaceAtHead(2, existingMapping, chainBuilder.build(elementBuilder.build(createPayload(2222)))); chain = store.get(2); - assertChainAndReverseChainOnlyHave(chain, 2222, 222); + assertThat(chain, hasPayloads(2222, 222)); } @Test @@ -190,24 +177,23 @@ public void testReplaceAtHeadIgnoredMappingExistsHeadMisMatch() throws Exception store.replaceAtHead(1, mappingReadFirst, chainBuilder.build(elementBuilder.build(createPayload(111)))); Chain current = store.get(1); - assertChainAndReverseChainOnlyHave(current, 111); + assertThat(current, hasPayloads(111)); store.append(1, createPayload(1111)); store.replaceAtHead(1, mappingReadFirst, chainBuilder.build(elementBuilder.build(createPayload(11111)))); Chain toVerify = store.get(1); - assertChainAndReverseChainOnlyHave(toVerify, 111, 1111); + assertThat(toVerify, hasPayloads(111, 1111)); } - @Test public void test_append_doesNotConsumeBuffer() throws Exception { ServerStore store = newStore(); ByteBuffer payload = createPayload(1L); store.append(1L, payload); - MatcherAssert.assertThat(payload.remaining(), Is.is(8)); + assertThat(payload.remaining(), Is.is(8)); } @Test @@ -216,18 +202,79 @@ public void test_getAndAppend_doesNotConsumeBuffer() throws Exception { ByteBuffer payload = createPayload(1L); store.getAndAppend(1L, payload); - MatcherAssert.assertThat(payload.remaining(), Is.is(8)); + assertThat(payload.remaining(), Is.is(8)); } @Test - public void test_replaceAtHead_doesNotConsumeBuffer() throws Exception { + public void test_replaceAtHead_doesNotConsumeBuffer() { ServerStore store = newStore(); ByteBuffer payload = createPayload(1L); Chain expected = newChainBuilder().build(newElementBuilder().build(payload), newElementBuilder().build(payload)); Chain update = newChainBuilder().build(newElementBuilder().build(payload)); store.replaceAtHead(1L, expected, update); - MatcherAssert.assertThat(payload.remaining(), Is.is(8)); + assertThat(payload.remaining(), Is.is(8)); + } + + @Test + public void testEmptyIterator() throws TimeoutException { + ServerStore store = newStore(); + + Iterator> chainIterator = store.iterator(); + + assertThat(chainIterator.hasNext(), Is.is(false)); + try { + chainIterator.next(); + fail("Expected NoSuchElementException"); + } catch (NoSuchElementException e) { + //expected + } } + @Test + public void testSingleElementIterator() throws TimeoutException { + ServerStore store = newStore(); + + store.append(1L, createPayload(42L)); + Iterator> chainIterator = store.iterator(); + + assertThat(chainIterator.hasNext(), is(true)); + Map.Entry next = chainIterator.next(); + assertThat(next.getKey(), is(1L)); + assertThat(next.getValue(), hasPayloads(42L)); + assertThat(chainIterator.hasNext(), is(false)); + try { + chainIterator.next(); + fail("Expected NoSuchElementException"); + } catch (NoSuchElementException e) { + //expected + } + } + + @Test + public void testHeavilyPopulatedIterator() throws TimeoutException { + ServerStore store = newStore(); + + range(0, 100).forEach(k -> { + try { + store.append(k, createPayload(k)); + } catch (TimeoutException e) { + throw new AssertionError(); + } + }); + + Iterator> chainIterator = store.iterator(); + + Set longs = new HashSet<>(); + while (chainIterator.hasNext()) { + Map.Entry chain = chainIterator.next(); + for (Element e: chain.getValue()) { + long l = readPayload(e.getPayload()); + assertThat(longs, not(hasItem(l))); + longs.add(l); + } + } + + assertThat(longs, hasItems(range(0, 100).boxed().toArray(Long[]::new))); + } } diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/store/impl/HeapChainBuilder.java b/clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/store/impl/HeapChainBuilder.java similarity index 100% rename from clustered/server/src/test/java/org/ehcache/clustered/server/store/impl/HeapChainBuilder.java rename to clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/store/impl/HeapChainBuilder.java diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/store/impl/HeapChainImpl.java b/clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/store/impl/HeapChainImpl.java similarity index 82% rename from clustered/server/src/test/java/org/ehcache/clustered/server/store/impl/HeapChainImpl.java rename to clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/store/impl/HeapChainImpl.java index 7d44ba5a36..cf4dd0a71a 100644 --- a/clustered/server/src/test/java/org/ehcache/clustered/server/store/impl/HeapChainImpl.java +++ b/clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/store/impl/HeapChainImpl.java @@ -38,11 +38,6 @@ public HeapChainImpl(Element... elements) { createChain(elements); } - @Override - public Iterator reverseIterator() { - return new ReverseChainIterator(); - } - @Override public boolean isEmpty() { return first == null; @@ -135,37 +130,6 @@ public Element next() { this.current = this.current.nextLink; return temp.element; } - - @Override - public void remove() { - throw new UnsupportedOperationException("Remove operation is not supported"); - } - } - - private class ReverseChainIterator implements Iterator { - - private Node current; - - ReverseChainIterator() { - this.current = last; - } - - @Override - public boolean hasNext() { - return current != null; - } - - @Override - public Element next() { - Node temp = this.current; - this.current = this.current.prevLink; - return temp.element; - } - - @Override - public void remove() { - throw new UnsupportedOperationException("Remove operation is not supported"); - } } private static class Node { diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/store/impl/HeapElementBuilder.java b/clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/store/impl/HeapElementBuilder.java similarity index 100% rename from clustered/server/src/test/java/org/ehcache/clustered/server/store/impl/HeapElementBuilder.java rename to clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/store/impl/HeapElementBuilder.java diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/store/impl/HeapElementImpl.java b/clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/store/impl/HeapElementImpl.java similarity index 96% rename from clustered/server/src/test/java/org/ehcache/clustered/server/store/impl/HeapElementImpl.java rename to clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/store/impl/HeapElementImpl.java index c4fef8a0cc..db1d88ce18 100644 --- a/clustered/server/src/test/java/org/ehcache/clustered/server/store/impl/HeapElementImpl.java +++ b/clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/store/impl/HeapElementImpl.java @@ -38,6 +38,6 @@ public long getSequenceNumber() { @Override public ByteBuffer getPayload() { - return this.data.duplicate(); + return this.data.asReadOnlyBuffer(); } } diff --git a/clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/store/impl/ReferenceStoreImpl.java b/clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/store/impl/ReferenceStoreImpl.java new file mode 100644 index 0000000000..8a63f587bd --- /dev/null +++ b/clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/store/impl/ReferenceStoreImpl.java @@ -0,0 +1,105 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.clustered.server.store.impl; + +import org.ehcache.clustered.common.internal.store.Chain; +import org.ehcache.clustered.common.internal.store.Element; +import org.ehcache.clustered.common.internal.store.ServerStore; + +import java.nio.ByteBuffer; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicLong; + +/** + * Implements {@link ServerStore} + */ +public class ReferenceStoreImpl implements ServerStore { + + private final ConcurrentMap map = new ConcurrentHashMap<>(); + private final AtomicLong sequenceGenerator = new AtomicLong(); + + @Override + public Chain get(long key) { + return map.getOrDefault(key, new HeapChainImpl()); + } + + @Override + public void append(long key, ByteBuffer payLoad) { + getAndAppend(key, payLoad); + } + + @Override + public Chain getAndAppend(long key, ByteBuffer payLoad) { + while (true) { + Chain existing = map.get(key); + if (existing == null) { + if (map.putIfAbsent(key, new HeapChainImpl(new HeapElementImpl(sequenceGenerator.incrementAndGet(), payLoad))) == null) { + return new HeapChainImpl(); + } + } else { + if (map.replace(key, existing, cast(existing).append(new HeapElementImpl(sequenceGenerator.incrementAndGet(), payLoad)))) { + return existing; + } + } + } + } + + @Override + public void replaceAtHead(long key, Chain expect, Chain update) { + map.computeIfPresent(key, (k, existing) -> { + Iterator current = existing.iterator(); + Iterator expected = expect.iterator(); + while (expected.hasNext()) { + if (current.hasNext()) { + HeapElementImpl expectedLink = (HeapElementImpl)expected.next(); + if (expectedLink.getSequenceNumber() != ((HeapElementImpl)current.next()).getSequenceNumber()) { + return existing; + } + } else { + return existing; + } + } + + List elements = new LinkedList<>(); + for (Element element : update) { + elements.add(element); + } + while(current.hasNext()) { + elements.add(current.next()); + } + return new HeapChainImpl(elements.toArray(new Element[elements.size()])); + }); + } + + @Override + public void clear() { + map.clear(); + } + + @Override + public Iterator> iterator() { + return map.entrySet().iterator(); + } + + private HeapChainImpl cast(Chain chain) { + return (HeapChainImpl)chain; + } +} diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/store/impl/ReferenceStoreTest.java b/clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/store/impl/ReferenceStoreTest.java similarity index 100% rename from clustered/server/src/test/java/org/ehcache/clustered/server/store/impl/ReferenceStoreTest.java rename to clustered/server/ehcache-service/src/test/java/org/ehcache/clustered/server/store/impl/ReferenceStoreTest.java diff --git a/clustered/server/gradle.properties b/clustered/server/gradle.properties deleted file mode 100644 index 37b82b87c9..0000000000 --- a/clustered/server/gradle.properties +++ /dev/null @@ -1,18 +0,0 @@ -# -# Copyright Terracotta, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -subPomName = Ehcache 3 Server Side Clustering module -subPomDesc = The Server Side Clustering module of Ehcache 3 diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/ServerStoreEvictionListener.java b/clustered/server/src/main/java/org/ehcache/clustered/server/ServerStoreEvictionListener.java deleted file mode 100644 index dd1f162663..0000000000 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/ServerStoreEvictionListener.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ehcache.clustered.server; - -import com.tc.classloader.CommonComponent; - -/** - * ServerStore eviction listener interface - */ -@CommonComponent -public interface ServerStoreEvictionListener { - /** - * Called when the ServerStore evicts a mapping - * @param key the key of the evicted mapping - */ - void onEviction(long key); -} diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheServerCodec.java b/clustered/server/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheServerCodec.java deleted file mode 100644 index 46ce38330b..0000000000 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/internal/messages/EhcacheServerCodec.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ehcache.clustered.server.internal.messages; - -import org.ehcache.clustered.common.internal.messages.EhcacheCodec; -import org.ehcache.clustered.common.internal.messages.EhcacheEntityMessage; -import org.ehcache.clustered.common.internal.messages.EhcacheEntityResponse; -import org.ehcache.clustered.common.internal.messages.EhcacheMessageType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.terracotta.entity.MessageCodec; -import org.terracotta.entity.MessageCodecException; -import org.terracotta.runnel.decoding.Enm; - -import java.nio.ByteBuffer; - -import static java.nio.ByteBuffer.wrap; -import static org.ehcache.clustered.common.internal.messages.EhcacheMessageType.isPassiveReplicationMessage; - -/** - * EhcacheServerCodec - */ -public class EhcacheServerCodec implements MessageCodec { - - private static final Logger LOGGER = LoggerFactory.getLogger(EhcacheServerCodec.class); - - private final EhcacheCodec clientCodec; - private final PassiveReplicationMessageCodec replicationCodec; - - public EhcacheServerCodec(EhcacheCodec clientCodec, PassiveReplicationMessageCodec replicationCodec) { - this.clientCodec = clientCodec; - this.replicationCodec = replicationCodec; - } - - @Override - public byte[] encodeMessage(EhcacheEntityMessage message) { - if (message instanceof PassiveReplicationMessage) { - return replicationCodec.encode((PassiveReplicationMessage) message); - } - return clientCodec.encodeMessage(message); - } - - @Override - public EhcacheEntityMessage decodeMessage(byte[] payload) { - ByteBuffer byteBuffer = wrap(payload); - Enm opCodeEnm = EhcacheCodec.OP_CODE_DECODER.decoder(byteBuffer).enm("opCode"); - if (!opCodeEnm.isFound()) { - throw new AssertionError("Got a message without an opCode"); - } - if (!opCodeEnm.isValid()) { - LOGGER.warn("Received message with unknown operation code - more recent version at the other end?"); - return null; - } - - byteBuffer.rewind(); - - EhcacheMessageType messageType = opCodeEnm.get(); - if (isPassiveReplicationMessage(messageType)) { - return replicationCodec.decode(messageType, byteBuffer); - } - return clientCodec.decodeMessage(byteBuffer, messageType); - } - - @Override - public byte[] encodeResponse(EhcacheEntityResponse response) throws MessageCodecException { - return clientCodec.encodeResponse(response); - } - - @Override - public EhcacheEntityResponse decodeResponse(byte[] payload) throws MessageCodecException { - return clientCodec.decodeResponse(payload); - } -} diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/state/MessageTracker.java b/clustered/server/src/main/java/org/ehcache/clustered/server/state/MessageTracker.java deleted file mode 100644 index bec386781f..0000000000 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/state/MessageTracker.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ehcache.clustered.server.state; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.concurrent.ConcurrentSkipListSet; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - - -/** - * Message Tracker keeps track of messages seen so far in efficient way by keeping track of contiguous and non-contiguous message ids. - * - * Assumption: message ids are generated in contiguous fashion in the increment on 1, starting from 0. - */ -public class MessageTracker { - - private static final Logger LOGGER = LoggerFactory.getLogger(MessageTracker.class); - - // keeping track of highest contiguous message id seen - private volatile long highestContiguousMsgId; - - // Keeping track of non contiguous message Ids higher than highestContiguousMsgId. - private final ConcurrentSkipListSet nonContiguousMsgIds; - - // Lock used for reconciliation. - private final Lock reconciliationLock; - - // Status that the sync is completed. - private volatile boolean isSyncCompleted; - - public MessageTracker(boolean isSyncCompleted) { - this.highestContiguousMsgId = -1L; - this.nonContiguousMsgIds = new ConcurrentSkipListSet<>(); - this.reconciliationLock = new ReentrantLock(); - this.isSyncCompleted = isSyncCompleted; - } - - /** - * Track the given message Id. - * - * @param msgId Message Id to be checked. - */ - public void track(long msgId) { - nonContiguousMsgIds.add(msgId); - tryReconcile(); - } - - /** - * Check wheather the given message id is already seen by track call. - * - * @param msgId Message Identifier to be checked. - * @return true if the given msgId is already tracked otherwise false. - */ - public boolean seen(long msgId) { - boolean seen = nonContiguousMsgIds.contains(msgId) || msgId <= highestContiguousMsgId; - tryReconcile(); - return seen; - } - - /** - * Checks weather non-contiguous message ids set is empty. - * - * @return true if the there is no non contiguous message ids otherwise false - */ - public boolean isEmpty() { - return nonContiguousMsgIds.isEmpty(); - } - - - /** - * Notify Message tracker that the sync is completed. - */ - public void notifySyncCompleted() { - this.isSyncCompleted = true; - } - - /** - * Remove the contiguous seen msgIds from the nonContiguousMsgIds and update highestContiguousMsgId - */ - private void reconcile() { - - // If nonContiguousMsgIds is empty then nothing to reconcile. - if (nonContiguousMsgIds.isEmpty()) { - return; - } - - // This happens when a passive is started after Active has moved on and - // passive starts to see msgIDs starting from a number > 0. - // Once the sync is completed, fast forward highestContiguousMsgId. - // Post sync completion assuming platform will send all msgIds beyond highestContiguousMsgId. - if (highestContiguousMsgId == -1L && isSyncCompleted) { - Long min = nonContiguousMsgIds.last(); - LOGGER.info("Setting highestContiguousMsgId to {} from -1", min); - highestContiguousMsgId = min; - nonContiguousMsgIds.removeIf(msgId -> msgId <= min); - } - - for (long msgId : nonContiguousMsgIds) { - if (msgId <= highestContiguousMsgId) { - nonContiguousMsgIds.remove(msgId); - } else if (msgId > highestContiguousMsgId + 1) { - break; - } else { - // the order is important.. - highestContiguousMsgId = msgId; - nonContiguousMsgIds.remove(msgId); - } - } - - } - - /** - * Try to reconcile, if the lock is available otherwise just return as other thread would have hold the lock and performing reconcile. - */ - private void tryReconcile() { - if (!this.reconciliationLock.tryLock()) { - return; - } - - try { - reconcile(); - - // Keep on warning after every reconcile if nonContiguousMsgIds reaches 500 (kept it a bit higher so that we won't get unnecessary warning due to high concurrency). - if (nonContiguousMsgIds.size() > 500) { - LOGGER.warn("Non - Contiguous Message ID has size : {}, with highestContiguousMsgId as : {}", nonContiguousMsgIds.size(), highestContiguousMsgId); - } - } finally { - this.reconciliationLock.unlock(); - } - } - -} diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/store/impl/ReferenceStoreImpl.java b/clustered/server/src/test/java/org/ehcache/clustered/server/store/impl/ReferenceStoreImpl.java deleted file mode 100644 index 287bee99a7..0000000000 --- a/clustered/server/src/test/java/org/ehcache/clustered/server/store/impl/ReferenceStoreImpl.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ehcache.clustered.server.store.impl; - -import org.ehcache.clustered.common.internal.store.Chain; -import org.ehcache.clustered.common.internal.store.Element; -import org.ehcache.clustered.common.internal.store.ServerStore; - -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; - -/** - * Implements {@link ServerStore} - */ -public class ReferenceStoreImpl implements ServerStore { - - private final Map map = new HashMap<>(); - private final List locks = new ArrayList<>(); - private final AtomicLong sequenceGenerator = new AtomicLong(); - - private final int LOCK_COUNT = 16; - - public ReferenceStoreImpl() { - for (int i = 0; i < LOCK_COUNT; i++) { - locks.add(new ReentrantReadWriteLock()); - } - } - - private ReadWriteLock getLock(long key) { - return locks.get((int)Math.abs(key)%LOCK_COUNT); - } - - @Override - public Chain get(long key) { - Lock lock = getLock(key).readLock(); - lock.lock(); - try { - Chain chain = map.get(key); - if (chain != null) { - return chain; - } else { - return new HeapChainImpl(); - } - } finally { - lock.unlock(); - } - } - - @Override - public void append(long key, ByteBuffer payLoad) { - Lock lock = getLock(key).writeLock(); - lock.lock(); - try { - Chain mapping = map.get(key); - if (mapping == null) { - map.put(key, new HeapChainImpl(new HeapElementImpl(sequenceGenerator.incrementAndGet(), payLoad))); - return; - } - Chain newMapping = cast(mapping).append(new HeapElementImpl(sequenceGenerator.incrementAndGet(), payLoad)); - map.put(key, newMapping); - } finally { - lock.unlock(); - } - } - - @Override - public Chain getAndAppend(long key, ByteBuffer payLoad) { - Lock lock = getLock(key).writeLock(); - lock.lock(); - try { - Chain mapping = map.get(key); - if (mapping != null) { - Chain newMapping = cast(mapping).append(new HeapElementImpl(sequenceGenerator.incrementAndGet(), payLoad)); - map.put(key, newMapping); - return mapping; - } else { - map.put(key, new HeapChainImpl(new HeapElementImpl(sequenceGenerator.incrementAndGet(), payLoad))); - return new HeapChainImpl(); - } - } finally { - lock.unlock(); - } - } - - @Override - public void replaceAtHead(long key, Chain expect, Chain update) { - Lock lock = getLock(key).writeLock(); - lock.lock(); - try { - Chain mapping = map.get(key); - if (mapping == null) { - return; - } - boolean replaceable = true; - List elements = new LinkedList<>(); - Iterator current = mapping.iterator(); - Iterator expected = expect.iterator(); - while (expected.hasNext()) { - if (current.hasNext()) { - HeapElementImpl expectedLink = (HeapElementImpl)expected.next(); - if (expectedLink.getSequenceNumber() != ((HeapElementImpl)current.next()).getSequenceNumber()) { - replaceable = false; - break; - } - } else { - replaceable = false; - break; - } - } - - if (replaceable) { - for (Element element : update) { - elements.add(element); - } - while(current.hasNext()) { - elements.add(current.next()); - } - map.put(key, new HeapChainImpl(elements.toArray(new Element[elements.size()]))); - } - - } finally { - lock.unlock(); - } - } - - private void writeLockAll() { - for (ReadWriteLock lock : locks) { - lock.writeLock().lock(); - } - } - - private void writeUnlockAll() { - for (ReadWriteLock lock : locks) { - lock.writeLock().unlock(); - } - } - - @Override - public void clear() { - writeLockAll(); - try { - map.clear(); - } finally { - writeUnlockAll(); - } - } - - private HeapChainImpl cast(Chain chain) { - return (HeapChainImpl)chain; - } - -} diff --git a/clustered/test-utils/build.gradle b/clustered/test-utils/build.gradle new file mode 100644 index 0000000000..067d92bd33 --- /dev/null +++ b/clustered/test-utils/build.gradle @@ -0,0 +1,8 @@ +plugins { + id 'org.ehcache.build.conventions.java-library' +} + +dependencies { + api project(':clustered:ehcache-common') + api "org.hamcrest:hamcrest-core:$hamcrestVersion" +} diff --git a/clustered/test-utils/config/checkstyle-suppressions.xml b/clustered/test-utils/config/checkstyle-suppressions.xml new file mode 100644 index 0000000000..cb41d0baf7 --- /dev/null +++ b/clustered/test-utils/config/checkstyle-suppressions.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/clustered/test-utils/src/main/java/org/ehcache/clustered/ChainUtils.java b/clustered/test-utils/src/main/java/org/ehcache/clustered/ChainUtils.java new file mode 100644 index 0000000000..b6001e1ac2 --- /dev/null +++ b/clustered/test-utils/src/main/java/org/ehcache/clustered/ChainUtils.java @@ -0,0 +1,109 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.clustered; + +import org.ehcache.clustered.common.internal.store.Chain; +import org.ehcache.clustered.common.internal.store.Element; +import org.ehcache.clustered.common.internal.store.SequencedElement; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +public class ChainUtils { + + public static long readPayload(ByteBuffer byteBuffer) { + return byteBuffer.getLong(); + } + + public static ByteBuffer createPayload(long key) { + ByteBuffer byteBuffer = ByteBuffer.allocate(8).putLong(key); + byteBuffer.flip(); + return byteBuffer.asReadOnlyBuffer(); + } + + public static ByteBuffer createPayload(long key, int payloadSize) { + if (payloadSize < 8) { + throw new IllegalArgumentException("payload must be at least 8 bytes long"); + } + ByteBuffer byteBuffer = ByteBuffer.allocate(payloadSize); + byteBuffer.putLong(key); + for (int i = 0; i < payloadSize - 8; i++) { + byteBuffer.put((byte) 0); + } + byteBuffer.flip(); + return byteBuffer.asReadOnlyBuffer(); + } + + public static Element getElement(final ByteBuffer payload) { + return payload::asReadOnlyBuffer; + } + + public static Chain chainOf(ByteBuffer... buffers) { + List elements = new ArrayList<>(); + for (final ByteBuffer buffer : buffers) { + elements.add(getElement(buffer)); + } + return getChain(elements); + } + + public static Chain sequencedChainOf(ByteBuffer ... buffers) { + List elements = new ArrayList<>(); + long counter = 0; + for (final ByteBuffer buffer : buffers) { + elements.add(getElement(counter++, buffer)); + } + return getChain(elements); + } + + private static Chain getChain(final List elements) { + return new Chain() { + private final List list = Collections.unmodifiableList(elements); + + @Override + public boolean isEmpty() { + return list.isEmpty(); + } + + @Override + public int length() { + return list.size(); + } + + @Override + public Iterator iterator() { + return list.iterator(); + } + }; + } + + public static SequencedElement getElement(final long sequence, final ByteBuffer payload) { + return new SequencedElement() { + @Override + public long getSequenceNumber() { + return sequence; + } + + @Override + public ByteBuffer getPayload() { + return payload.asReadOnlyBuffer(); + } + }; + } +} diff --git a/clustered/test-utils/src/main/java/org/ehcache/clustered/Matchers.java b/clustered/test-utils/src/main/java/org/ehcache/clustered/Matchers.java new file mode 100644 index 0000000000..344af5e0b3 --- /dev/null +++ b/clustered/test-utils/src/main/java/org/ehcache/clustered/Matchers.java @@ -0,0 +1,120 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.clustered; + +import org.ehcache.clustered.common.internal.store.Chain; +import org.ehcache.clustered.common.internal.store.Element; +import org.ehcache.clustered.common.internal.store.SequencedElement; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import static org.ehcache.clustered.ChainUtils.readPayload; + +public class Matchers { + + public static Matcher> entry(Matcher key, Matcher value) { + return new TypeSafeMatcher>() { + @Override + protected boolean matchesSafely(Map.Entry item) { + return key.matches(item.getKey()) && value.matches(item.getValue()); + } + + @Override + public void describeTo(Description description) { + description.appendText("an entry with key ").appendDescriptionOf(key).appendText(" and value ").appendDescriptionOf(value); + } + }; + } + + public static Matcher matchesChain(Chain expected) { + return new TypeSafeMatcher() { + @Override + protected boolean matchesSafely(Chain item) { + Iterator expectedIt = expected.iterator(); + Iterator itemIt = item.iterator(); + + while (expectedIt.hasNext() && itemIt.hasNext()) { + Element expectedNext = expectedIt.next(); + Element itemNext = itemIt.next(); + + if (!expectedNext.getPayload().equals(itemNext.getPayload())) { + return false; + } + } + + return !expectedIt.hasNext() && !itemIt.hasNext(); + } + + @Override + public void describeTo(Description description) { + description.appendText(" a chain matching ").appendValue(expected); + } + }; + } + + public static Matcher hasPayloads(long ... payloads) { + return new TypeSafeMatcher() { + @Override + protected boolean matchesSafely(Chain item) { + Iterator elements = item.iterator(); + for (long payload : payloads) { + if (readPayload(elements.next().getPayload()) != payload) { + return false; + } + } + return !elements.hasNext(); + } + + @Override + public void describeTo(Description description) { + description.appendText(" a chain containing the payloads ").appendValueList("[", ", ", "]", payloads); + } + }; + } + + + public static Matcher sameSequenceAs(Chain original) { + List sequenceNumbers = sequenceNumbersOf(original); + + return new TypeSafeMatcher() { + @Override + protected boolean matchesSafely(Chain item) { + return sequenceNumbers.equals(sequenceNumbersOf(item)); + } + + @Override + public void describeTo(Description description) { + description.appendValue("a chain with sequence numbers matching ").appendValue(original); + } + }; + } + + private static List sequenceNumbersOf(Chain chain) { + List sequenceNumbers = new ArrayList<>(chain.length()); + for (Element element : chain) { + sequenceNumbers.add(((SequencedElement) element).getSequenceNumber()); + } + return sequenceNumbers; + } + +} diff --git a/config/checkstyle.xml b/config/checkstyle.xml index 190ec79cba..6a9374d111 100644 --- a/config/checkstyle.xml +++ b/config/checkstyle.xml @@ -24,25 +24,21 @@ - - - - - - - - - - + + + + + + - + diff --git a/config/owasp-supressions.xml b/config/owasp-supressions.xml new file mode 100644 index 0000000000..8a5a28b93e --- /dev/null +++ b/config/owasp-supressions.xml @@ -0,0 +1,53 @@ + + + + + Ehcache modules are not Gradle! + ^pkg:maven/org\.ehcache.*@.*$ + CVE-2019-11065 + + + Ehcache modules are not Gradle! + ^pkg:maven/org\.ehcache.*@.*$ + CVE-2019-15052 + + + Ehcache modules are not Gradle! + ^pkg:maven/org\.ehcache.*@.*$ + CVE-2019-16370 + + + TC Tripwire is unrelated to the other Tripwire + ^pkg:maven/org\.terracotta/tc\-tripwire\-plugin@.*$ + cpe:/a:tripwire:tripwire + + + BND isn't Eclipse + ^pkg:maven/biz\.aQute\.bnd/biz\.aQute\.bndlib@.*$ + cpe:/a:eclipse:eclipse_ide + + + Ehcache 3 builds require Java 8+ : 4.13.1 is safe + pkg:maven/junit/junit@4.13.1 + CVE-2020-15250 + + + + PAX URL Aether repackages httpclient and isn't (yet) fixed + db40edda8b95d880d2a810560fd5e46eb4fa6909 + CVE-2020-13956 + + + PAX URL Aether repackages commons-io and isn't (yet) fixed + 5060835593e5b6ed18c82fc2e782f0a3c30a00b1 + CVE-2021-29425 + + + PAX Exame JUnit4 doesn't have a 4.13.1 depending release + ^pkg:maven/org\.ops4j\.pax\.exam/pax\-exam\-junit4@.*$ + CVE-2020-15250 + + diff --git a/core-spi-test/.gitignore b/core-spi-test/.gitignore deleted file mode 100755 index ae3c172604..0000000000 --- a/core-spi-test/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/bin/ diff --git a/core-spi-test/build.gradle b/core-spi-test/build.gradle index 5f7ff037f7..0b8db145f4 100644 --- a/core-spi-test/build.gradle +++ b/core-spi-test/build.gradle @@ -13,11 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +plugins { + id 'org.ehcache.build.conventions.java-library' +} dependencies { api project(':spi-tester') - implementation project(':core') - implementation project(':impl') + implementation project(':ehcache-core') + implementation project(':ehcache-impl') implementation "junit:junit:$junitVersion" implementation "org.mockito:mockito-core:$mockitoVersion" implementation "org.hamcrest:hamcrest-library:$hamcrestVersion" diff --git a/core-spi-test/gradle.properties b/core-spi-test/gradle.properties deleted file mode 100644 index 38ab6ff6e6..0000000000 --- a/core-spi-test/gradle.properties +++ /dev/null @@ -1,18 +0,0 @@ -# -# Copyright Terracotta, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -subPomName = Ehcache 3 Core SPI test module -subPomDesc = The Core SPI test module of Ehcache 3 diff --git a/core-spi-test/src/main/java/org/ehcache/internal/store/StoreExpiryEventListenerTest.java b/core-spi-test/src/main/java/org/ehcache/internal/store/StoreExpiryEventListenerTest.java index c450f2e439..2e260e4dac 100644 --- a/core-spi-test/src/main/java/org/ehcache/internal/store/StoreExpiryEventListenerTest.java +++ b/core-spi-test/src/main/java/org/ehcache/internal/store/StoreExpiryEventListenerTest.java @@ -32,8 +32,8 @@ import java.time.Duration; import static org.ehcache.internal.store.StoreCreationEventListenerTest.eventType; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; -import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.hamcrest.MockitoHamcrest.argThat; diff --git a/core-spi-test/src/main/java/org/ehcache/internal/store/StoreFactory.java b/core-spi-test/src/main/java/org/ehcache/internal/store/StoreFactory.java index c2133a97ed..05bd94f3fd 100644 --- a/core-spi-test/src/main/java/org/ehcache/internal/store/StoreFactory.java +++ b/core-spi-test/src/main/java/org/ehcache/internal/store/StoreFactory.java @@ -43,7 +43,7 @@ public interface StoreFactory { Class getValueType(); - ServiceConfiguration[] getServiceConfigurations(); + ServiceConfiguration[] getServiceConfigurations(); ServiceProvider getServiceProvider(); diff --git a/core-spi-test/src/main/java/org/ehcache/internal/store/StoreValueHolderCreationTimeTest.java b/core-spi-test/src/main/java/org/ehcache/internal/store/StoreValueHolderCreationTimeTest.java index edd7130e7b..7978e54112 100644 --- a/core-spi-test/src/main/java/org/ehcache/internal/store/StoreValueHolderCreationTimeTest.java +++ b/core-spi-test/src/main/java/org/ehcache/internal/store/StoreValueHolderCreationTimeTest.java @@ -43,7 +43,7 @@ public void creationTimeCanBeReturned() throws IllegalAccessException, InstantiationException { Store.ValueHolder valueHolder = factory.newValueHolder(factory.createValue(1)); - assertThat(valueHolder.creationTime(TimeUnit.MILLISECONDS), is(notNullValue())); + assertThat(valueHolder.creationTime(), is(notNullValue())); } } diff --git a/core-spi-test/src/main/java/org/ehcache/internal/store/StoreValueHolderLastAccessTimeTest.java b/core-spi-test/src/main/java/org/ehcache/internal/store/StoreValueHolderLastAccessTimeTest.java index 7564b4837e..bf5806bdd9 100644 --- a/core-spi-test/src/main/java/org/ehcache/internal/store/StoreValueHolderLastAccessTimeTest.java +++ b/core-spi-test/src/main/java/org/ehcache/internal/store/StoreValueHolderLastAccessTimeTest.java @@ -43,6 +43,6 @@ public void lastAccessTimeCanBeReturned() throws IllegalAccessException, InstantiationException { Store.ValueHolder valueHolder = factory.newValueHolder(factory.createValue(1)); - assertThat(valueHolder.lastAccessTime(TimeUnit.MILLISECONDS), is(notNullValue())); + assertThat(valueHolder.lastAccessTime(), is(notNullValue())); } } diff --git a/core-spi-test/src/main/java/org/ehcache/internal/tier/AuthoritativeTierFlush.java b/core-spi-test/src/main/java/org/ehcache/internal/tier/AuthoritativeTierFlush.java index 0b1c16e036..e4c31512f3 100644 --- a/core-spi-test/src/main/java/org/ehcache/internal/tier/AuthoritativeTierFlush.java +++ b/core-spi-test/src/main/java/org/ehcache/internal/tier/AuthoritativeTierFlush.java @@ -60,7 +60,7 @@ public void entryIsFlushed() throws LegalSPITesterException { K key = factory.createKey(1); final V value = factory.createValue(1); Store.ValueHolder valueHolder = mock(Store.ValueHolder.class); - when(valueHolder.expirationTime(any(TimeUnit.class))).thenReturn(1L); + when(valueHolder.expirationTime()).thenReturn(1L); tier = factory.newStoreWithCapacity(1L); @@ -81,7 +81,7 @@ public void entryIsNotFlushed() throws LegalSPITesterException { K key = factory.createKey(1); final V value = factory.createValue(1); Store.ValueHolder valueHolder = mock(Store.ValueHolder.class); - when(valueHolder.expirationTime(any(TimeUnit.class))).thenReturn(1L); + when(valueHolder.expirationTime()).thenReturn(1L); tier = factory.newStoreWithCapacity(1L); @@ -99,7 +99,7 @@ public void entryIsNotFlushed() throws LegalSPITesterException { public void entryDoesNotExist() { K key = factory.createKey(1); Store.ValueHolder valueHolder = mock(Store.ValueHolder.class); - when(valueHolder.expirationTime(any(TimeUnit.class))).thenReturn(1L); + when(valueHolder.expirationTime()).thenReturn(1L); tier = factory.newStoreWithCapacity(1L); diff --git a/core-spi-test/src/main/java/org/ehcache/internal/tier/CachingTierFactory.java b/core-spi-test/src/main/java/org/ehcache/internal/tier/CachingTierFactory.java index f3337891c6..3d81c07b9e 100644 --- a/core-spi-test/src/main/java/org/ehcache/internal/tier/CachingTierFactory.java +++ b/core-spi-test/src/main/java/org/ehcache/internal/tier/CachingTierFactory.java @@ -39,7 +39,7 @@ public interface CachingTierFactory { Class getValueType(); - ServiceConfiguration[] getServiceConfigurations(); + ServiceConfiguration[] getServiceConfigurations(); ServiceProvider getServiceProvider(); diff --git a/core-spi-test/src/main/java/org/ehcache/internal/tier/CachingTierGetOrComputeIfAbsent.java b/core-spi-test/src/main/java/org/ehcache/internal/tier/CachingTierGetOrComputeIfAbsent.java index 8799027d56..912e7b4c42 100644 --- a/core-spi-test/src/main/java/org/ehcache/internal/tier/CachingTierGetOrComputeIfAbsent.java +++ b/core-spi-test/src/main/java/org/ehcache/internal/tier/CachingTierGetOrComputeIfAbsent.java @@ -83,7 +83,7 @@ public void returnTheValueHolderCurrentlyInTheCachingTier() throws LegalSPITeste V value = factory.createValue(1); final Store.ValueHolder computedValueHolder = mock(Store.ValueHolder.class); when(computedValueHolder.get()).thenReturn(value); - when(computedValueHolder.expirationTime(any(TimeUnit.class))).thenReturn(Store.ValueHolder.NO_EXPIRE); + when(computedValueHolder.expirationTime()).thenReturn(Store.ValueHolder.NO_EXPIRE); tier = factory.newCachingTier(); diff --git a/core-spi-test/src/main/java/org/ehcache/internal/tier/CachingTierInvalidate.java b/core-spi-test/src/main/java/org/ehcache/internal/tier/CachingTierInvalidate.java index 1e3f1f3b12..1ef04603e4 100644 --- a/core-spi-test/src/main/java/org/ehcache/internal/tier/CachingTierInvalidate.java +++ b/core-spi-test/src/main/java/org/ehcache/internal/tier/CachingTierInvalidate.java @@ -28,8 +28,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; /** * CachingTierInvalidate @@ -137,22 +137,22 @@ public V get() { } @Override - public long creationTime(TimeUnit unit) { + public long creationTime() { return 0L; } @Override - public long expirationTime(TimeUnit unit) { + public long expirationTime() { return 0L; } @Override - public boolean isExpired(long expirationTime, TimeUnit unit) { + public boolean isExpired(long expirationTime) { return false; } @Override - public long lastAccessTime(TimeUnit unit) { + public long lastAccessTime() { return 0L; } diff --git a/core/.gitignore b/core/.gitignore deleted file mode 100755 index ae3c172604..0000000000 --- a/core/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/bin/ diff --git a/core/gradle.properties b/core/gradle.properties deleted file mode 100644 index 33ccc9d13a..0000000000 --- a/core/gradle.properties +++ /dev/null @@ -1,3 +0,0 @@ -subPomName = Ehcache 3 Core module -subPomDesc = The Core module of Ehcache 3 -osgi = {"Export-Package" : ["!org.ehcache.core.internal.*"]} diff --git a/core/src/main/java/org/ehcache/core/internal/util/ClassLoading.java b/core/src/main/java/org/ehcache/core/internal/util/ClassLoading.java deleted file mode 100644 index aec4cd8128..0000000000 --- a/core/src/main/java/org/ehcache/core/internal/util/ClassLoading.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ehcache.core.internal.util; - -import java.io.IOException; -import java.net.URL; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.Enumeration; -import java.util.ServiceLoader; - -public class ClassLoading { - - private static final ClassLoader DEFAULT_CLASSLOADER; - - static { - DEFAULT_CLASSLOADER = AccessController.doPrivileged((PrivilegedAction) DefaultClassLoader::new); - } - - public static ClassLoader getDefaultClassLoader() { - return DEFAULT_CLASSLOADER; - } - - public static ServiceLoader libraryServiceLoaderFor(Class serviceType) { - return ServiceLoader.load(serviceType, ClassLoading.class.getClassLoader()); - } - - private static class DefaultClassLoader extends ClassLoader { - private static final ClassLoader THIS_LOADER = DefaultClassLoader.class.getClassLoader(); - - @Override - public Class loadClass(String name) throws ClassNotFoundException { - ClassLoader loader = Thread.currentThread().getContextClassLoader(); - - if (loader != null) { - try { - return loader.loadClass(name); - } catch (ClassNotFoundException cnfe) { - // - } - } - - return THIS_LOADER.loadClass(name); - } - - @Override - public URL getResource(String name) { - ClassLoader loader = Thread.currentThread().getContextClassLoader(); - - if (loader != null) { - URL res = loader.getResource(name); - if (res != null) { - return res; - } - } - - return THIS_LOADER.getResource(name); - } - - @Override - public Enumeration getResources(String name) throws IOException { - ClassLoader loader = Thread.currentThread().getContextClassLoader(); - - if (loader != null) { - Enumeration resources = loader.getResources(name); - if (resources != null && resources.hasMoreElements()) { - return resources; - } - } - - return THIS_LOADER.getResources(name); - } - } -} diff --git a/core/src/main/java/org/ehcache/core/spi/service/StatisticsService.java b/core/src/main/java/org/ehcache/core/spi/service/StatisticsService.java deleted file mode 100644 index f207415b5e..0000000000 --- a/core/src/main/java/org/ehcache/core/spi/service/StatisticsService.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ehcache.core.spi.service; - -import org.ehcache.core.statistics.CacheStatistics; -import org.ehcache.spi.service.Service; - -/** - * Service providing raw statistics for cache and tier usage. - */ -public interface StatisticsService extends Service { - - /** - * Return the object containing the statistics for a given cache name. - * - * @param cacheName name (alias) of the cache - * @return all the cache statistics - */ - CacheStatistics getCacheStatistics(String cacheName); -} diff --git a/core/src/test/java/org/ehcache/core/config/BaseCacheConfigurationTest.java b/core/src/test/java/org/ehcache/core/config/BaseCacheConfigurationTest.java deleted file mode 100644 index 8039cb69f8..0000000000 --- a/core/src/test/java/org/ehcache/core/config/BaseCacheConfigurationTest.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ehcache.core.config; - -import org.ehcache.config.ResourcePools; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import static org.mockito.Mockito.mock; - -/** - * BaseCacheConfigurationTest - */ -public class BaseCacheConfigurationTest { - - @Rule - public ExpectedException expectedException = ExpectedException.none(); - - @Test - public void testThrowsWithNullKeyType() { - expectedException.expect(NullPointerException.class); - expectedException.expectMessage("keyType"); - - new BaseCacheConfiguration<>(null, String.class, null, - null, null, mock(ResourcePools.class)); - } - - @Test - public void testThrowsWithNullValueType() { - expectedException.expect(NullPointerException.class); - expectedException.expectMessage("valueType"); - - new BaseCacheConfiguration<>(Long.class, null, null, - null, null, mock(ResourcePools.class)); - } - - @Test - public void testThrowsWithNullResourcePools() { - expectedException.expect(NullPointerException.class); - expectedException.expectMessage("resourcePools"); - - new BaseCacheConfiguration<>(Long.class, String.class, null, - null, null, null); - } - -} diff --git a/core/src/test/java/org/ehcache/core/config/ResourcePoolsHelper.java b/core/src/test/java/org/ehcache/core/config/ResourcePoolsHelper.java deleted file mode 100644 index c6f1751f74..0000000000 --- a/core/src/test/java/org/ehcache/core/config/ResourcePoolsHelper.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ehcache.core.config; - -import org.ehcache.config.ResourcePool; -import org.ehcache.config.ResourcePools; -import org.ehcache.config.ResourceType; -import org.ehcache.config.ResourceUnit; -import org.ehcache.config.SizedResourcePool; -import org.ehcache.config.units.EntryUnit; -import org.ehcache.config.units.MemoryUnit; - -import java.util.HashMap; -import java.util.Map; - -/** - * @author Ludovic Orban - */ -public class ResourcePoolsHelper { - - public static ResourcePools createHeapOnlyPools() { - return createHeapOnlyPools(Long.MAX_VALUE); - } - - public static ResourcePools createHeapOnlyPools(long heapSize) { - Map, ResourcePool> poolsMap = new HashMap<>(); - poolsMap.put(ResourceType.Core.HEAP, new SizedResourcePoolImpl<>(ResourceType.Core.HEAP, heapSize, EntryUnit.ENTRIES, false)); - return new ResourcePoolsImpl(poolsMap); - } - - public static ResourcePools createHeapOnlyPools(long heapSize, ResourceUnit resourceUnit) { - Map, ResourcePool> poolsMap = new HashMap<>(); - poolsMap.put(ResourceType.Core.HEAP, new SizedResourcePoolImpl<>(ResourceType.Core.HEAP, heapSize, resourceUnit, false)); - return new ResourcePoolsImpl(poolsMap); - } - - public static ResourcePools createOffheapOnlyPools(long offheapSizeInMb) { - Map, ResourcePool> poolsMap = new HashMap<>(); - poolsMap.put(ResourceType.Core.OFFHEAP, new SizedResourcePoolImpl<>(ResourceType.Core.OFFHEAP, offheapSizeInMb, MemoryUnit.MB, false)); - return new ResourcePoolsImpl(poolsMap); - } - - public static ResourcePools createDiskOnlyPools(long diskSize, ResourceUnit resourceUnit) { - Map, ResourcePool> poolsMap = new HashMap<>(); - poolsMap.put(ResourceType.Core.DISK, new SizedResourcePoolImpl<>(ResourceType.Core.DISK, diskSize, resourceUnit, false)); - return new ResourcePoolsImpl(poolsMap); - } - - public static ResourcePools createHeapDiskPools(long heapSize, long diskSizeInMb) { - Map, ResourcePool> poolsMap = new HashMap<>(); - poolsMap.put(ResourceType.Core.HEAP, new SizedResourcePoolImpl<>(ResourceType.Core.HEAP, heapSize, EntryUnit.ENTRIES, false)); - poolsMap.put(ResourceType.Core.DISK, new SizedResourcePoolImpl<>(ResourceType.Core.DISK, diskSizeInMb, MemoryUnit.MB, false)); - return new ResourcePoolsImpl(poolsMap); - } - - public static ResourcePools createHeapDiskPools(long heapSize, ResourceUnit heapResourceUnit, long diskSizeInMb) { - Map, ResourcePool> poolsMap = new HashMap<>(); - poolsMap.put(ResourceType.Core.HEAP, new SizedResourcePoolImpl<>(ResourceType.Core.HEAP, heapSize, heapResourceUnit, false)); - poolsMap.put(ResourceType.Core.DISK, new SizedResourcePoolImpl<>(ResourceType.Core.DISK, diskSizeInMb, MemoryUnit.MB, false)); - return new ResourcePoolsImpl(poolsMap); - } - -} diff --git a/core/src/test/java/org/ehcache/core/internal/util/CollectionUtilTest.java b/core/src/test/java/org/ehcache/core/internal/util/CollectionUtilTest.java deleted file mode 100644 index 4f5c3e9c57..0000000000 --- a/core/src/test/java/org/ehcache/core/internal/util/CollectionUtilTest.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ehcache.core.internal.util; - -import org.junit.Test; - -import java.util.Arrays; -import java.util.Collections; -import java.util.Map; - -import static org.assertj.core.api.Assertions.*; - -public class CollectionUtilTest { - - @Test - public void findBestCollectionSize_sizeable() { - int size = CollectionUtil.findBestCollectionSize(Arrays.asList(1, 2 ,3), 100); - assertThat(size).isEqualTo(3); - } - - @Test - public void findBestCollectionSize_empty() { - int size = CollectionUtil.findBestCollectionSize(Collections.emptySet(), 100); - assertThat(size).isZero(); - } - - @Test - public void findBestCollectionSize_singleton() { - int size = CollectionUtil.findBestCollectionSize(Collections.singleton(1), 100); - assertThat(size).isEqualTo(1); - } - - @Test - public void findBestCollectionSize_notSizeable() { - int size = CollectionUtil.findBestCollectionSize(() -> null, 100); - assertThat(size).isEqualTo(100); - } - - @Test - public void entry_value() { - Map.Entry entry = CollectionUtil.entry(1, "a"); - assertThat(entry.getKey()).isEqualTo(1); - assertThat(entry.getValue()).isEqualTo("a"); - } - - @Test - public void entry_nullAccepted() { - Map.Entry entry = CollectionUtil.entry(null, null); - assertThat(entry.getKey()).isNull(); - assertThat(entry.getValue()).isNull(); - } - - @Test - public void copyMapButFailOnNull_nullKey() { - Map map = CollectionUtil.map(1, 1, null, 2, 3, 3); - assertThatExceptionOfType(NullPointerException.class) - .isThrownBy(() -> CollectionUtil.copyMapButFailOnNull(map)); - } - - @Test - public void copyMapButFailOnNull_nullValue() { - Map map = CollectionUtil.map(1, 1, 2, null, 3, 3); - assertThatExceptionOfType(NullPointerException.class) - .isThrownBy(() -> CollectionUtil.copyMapButFailOnNull(Collections.singletonMap(1, null))); - } - - @Test - public void copyMapButFailOnNull_copy() { - Map map = CollectionUtil.map(1, 1L, 2, 2L, 3, 3L); - assertThat(map).containsExactly(entry(1, 1L), entry(2, 2L), entry(3, 3L)); - } - - @Test - public void map1() { - Map map = CollectionUtil.map(1, 1L); - assertThat(map).containsExactly(entry(1, 1L)); - } - - @Test - public void map2() { - Map map = CollectionUtil.map(1, 1L, 2, 2L); - assertThat(map).containsExactly(entry(1, 1L), entry(2, 2L)); - } - - @Test - public void map3() { - Map map = CollectionUtil.map(1, 1L, 2, 2L, 3, 3L); - assertThat(map).containsExactly(entry(1, 1L), entry(2, 2L), entry(3, 3L)); - } -} diff --git a/core/src/test/java/org/ehcache/core/spi/store/AbstractValueHolderTest.java b/core/src/test/java/org/ehcache/core/spi/store/AbstractValueHolderTest.java deleted file mode 100644 index 5028f9d57d..0000000000 --- a/core/src/test/java/org/ehcache/core/spi/store/AbstractValueHolderTest.java +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ehcache.core.spi.store; - -import org.ehcache.core.spi.time.TimeSource; -import org.junit.Test; - -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.Is.is; - -/** - * @author Ludovic Orban - */ -public class AbstractValueHolderTest { - - @Test - public void testCreationTime() throws Exception { - AbstractValueHolder valueHolder = newAbstractValueHolder(TimeUnit.MILLISECONDS, 1000L); - - assertThat(valueHolder.creationTime(TimeUnit.SECONDS), is(1L)); - assertThat(valueHolder.creationTime(TimeUnit.MILLISECONDS), is(1000L)); - assertThat(valueHolder.creationTime(TimeUnit.MICROSECONDS), is(1000000L)); - } - - @Test - public void testExpirationTime() throws Exception { - AbstractValueHolder valueHolder = newAbstractValueHolder(TimeUnit.MILLISECONDS, 0L, 1000L); - - assertThat(valueHolder.expirationTime(TimeUnit.SECONDS), is(1L)); - assertThat(valueHolder.expirationTime(TimeUnit.MILLISECONDS), is(1000L)); - assertThat(valueHolder.expirationTime(TimeUnit.MICROSECONDS), is(1000000L)); - } - - - @Test - public void testLastAccessTime() throws Exception { - // last access time defaults to create time - AbstractValueHolder valueHolder = newAbstractValueHolder(TimeUnit.MILLISECONDS, 1000L); - - assertThat(valueHolder.lastAccessTime(TimeUnit.SECONDS), is(1L)); - assertThat(valueHolder.lastAccessTime(TimeUnit.MILLISECONDS), is(1000L)); - assertThat(valueHolder.lastAccessTime(TimeUnit.MICROSECONDS), is(1000000L)); - - valueHolder = newAbstractValueHolder(TimeUnit.MILLISECONDS, 1000L, 0L, 2000L); - - assertThat(valueHolder.lastAccessTime(TimeUnit.SECONDS), is(2L)); - assertThat(valueHolder.lastAccessTime(TimeUnit.MILLISECONDS), is(2000L)); - assertThat(valueHolder.lastAccessTime(TimeUnit.MICROSECONDS), is(2000000L)); - } - - - @Test - public void testIsExpired() throws Exception { - assertThat(newAbstractValueHolder(TimeUnit.MILLISECONDS, 1000L).isExpired(1L, TimeUnit.SECONDS), is(false)); - assertThat(newAbstractValueHolder(TimeUnit.MILLISECONDS, 1000L).isExpired(1000L, TimeUnit.MILLISECONDS), is(false)); - assertThat(newAbstractValueHolder(TimeUnit.MILLISECONDS, 1000L).isExpired(1000000L, TimeUnit.MICROSECONDS), is(false)); - - assertThat(newAbstractValueHolder(TimeUnit.MILLISECONDS, 1000L, 1001L).isExpired(1L, TimeUnit.SECONDS), is(false)); - assertThat(newAbstractValueHolder(TimeUnit.MILLISECONDS, 1000L, 1001L).isExpired(1000L, TimeUnit.MILLISECONDS), is(false)); - assertThat(newAbstractValueHolder(TimeUnit.MILLISECONDS, 1000L, 1001L).isExpired(1000000L, TimeUnit.MICROSECONDS), is(false)); - - assertThat(newAbstractValueHolder(TimeUnit.MILLISECONDS, 1000L, 1000L).isExpired(1L, TimeUnit.SECONDS), is(true)); - assertThat(newAbstractValueHolder(TimeUnit.MILLISECONDS, 1000L, 1000L).isExpired(1000L, TimeUnit.MILLISECONDS), is(true)); - assertThat(newAbstractValueHolder(TimeUnit.MILLISECONDS, 1000L, 1000L).isExpired(1000000L, TimeUnit.MICROSECONDS), is(true)); - } - - @Test - public void testEquals() throws Exception { - assertThat(newAbstractValueHolder(TimeUnit.MILLISECONDS, 0L).equals(newAbstractValueHolder(TimeUnit.MILLISECONDS, 0L)), is(true)); - assertThat(newAbstractValueHolder(TimeUnit.MILLISECONDS, 1L).equals(newAbstractValueHolder(TimeUnit.MILLISECONDS, 0L)), is(false)); - - assertThat(newAbstractValueHolder(TimeUnit.MILLISECONDS, 2L, 0L).equals(newAbstractValueHolder(TimeUnit.MILLISECONDS, 2L, 0L)), is(true)); - assertThat(newAbstractValueHolder(TimeUnit.MILLISECONDS, 2L, 0L).equals(newAbstractValueHolder(TimeUnit.MILLISECONDS, 2L, 1L)), is(false)); - assertThat(newAbstractValueHolder(TimeUnit.MILLISECONDS, 2L, 0L).equals(newAbstractValueHolder(TimeUnit.MILLISECONDS, 3L, 0L)), is(false)); - - assertThat(newAbstractValueHolder(TimeUnit.MILLISECONDS, 0L, 2L, 1L).equals(newAbstractValueHolder(TimeUnit.MILLISECONDS, 0L, 2L, 1L)), is(true)); - assertThat(newAbstractValueHolder(TimeUnit.MILLISECONDS, 1L, 2L, 1L).equals(newAbstractValueHolder(TimeUnit.MILLISECONDS, 0L, 2L, 1L)), is(false)); - - assertThat(newAbstractValueHolder(TimeUnit.MILLISECONDS, 0L, 3L, 1L).equals(newAbstractValueHolder(TimeUnit.MILLISECONDS, 0L, 2L, 1L)), is(false)); - assertThat(newAbstractValueHolder(TimeUnit.MILLISECONDS, 0L, 2L, 3L).equals(newAbstractValueHolder(TimeUnit.MILLISECONDS, 0L, 2L, 1L)), is(false)); - - assertThat(newAbstractValueHolder(TimeUnit.MILLISECONDS, 1L).equals(newAbstractValueHolder(TimeUnit.SECONDS, 1L)), is(false)); - assertThat(newAbstractValueHolder(TimeUnit.NANOSECONDS, 1L).equals(newAbstractValueHolder(TimeUnit.SECONDS, 0L)), is(false)); - assertThat(newAbstractValueHolder(TimeUnit.SECONDS, 0L).equals(newAbstractValueHolder(TimeUnit.NANOSECONDS, 1L)), is(false)); - assertThat(newAbstractValueHolder(TimeUnit.SECONDS, 1L).equals(newAbstractValueHolder(TimeUnit.MILLISECONDS, 1000L)), is(true)); - assertThat(newAbstractValueHolder(TimeUnit.MILLISECONDS, 1000L).equals(newAbstractValueHolder(TimeUnit.SECONDS, 1L)), is(true)); - } - - @Test - public void testSubclassEquals() throws Exception { - assertThat(new AbstractValueHolder(-1, 1000L) { - @Override - public String get() { - return "aaa"; - } - - @Override - protected TimeUnit nativeTimeUnit() { - return TimeUnit.MILLISECONDS; - } - - @Override - public int hashCode() { - return super.hashCode() + get().hashCode(); - } - @Override - public boolean equals(Object obj) { - if (obj instanceof AbstractValueHolder) { - AbstractValueHolder other = (AbstractValueHolder) obj; - return super.equals(obj) && get().equals(other.get()); - } - return false; - } - }.equals(new AbstractValueHolder(-1, 1L) { - @Override - public String get() { - return "aaa"; - } - - @Override - protected TimeUnit nativeTimeUnit() { - return TimeUnit.SECONDS; - } - - @Override - public int hashCode() { - return super.hashCode() + get().hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof AbstractValueHolder) { - AbstractValueHolder other = (AbstractValueHolder)obj; - return super.equals(obj) && get().equals(other.get()); - } - return false; - } - }), is(true)); - - assertThat(new AbstractValueHolder(-1, 1000L) { - @Override - public String get() { - return "aaa"; - } - - @Override - protected TimeUnit nativeTimeUnit() { - return TimeUnit.MICROSECONDS; - } - - @Override - public int hashCode() { - return super.hashCode() + get().hashCode(); - } - @Override - public boolean equals(Object obj) { - if (obj instanceof AbstractValueHolder) { - AbstractValueHolder other = (AbstractValueHolder) obj; - return super.equals(obj) && get().equals(other.get()); - } - return false; - } - }.equals(new AbstractValueHolder(-1, 1L) { - @Override - public String get() { - return "bbb"; - } - - @Override - protected TimeUnit nativeTimeUnit() { - return TimeUnit.MILLISECONDS; - } - - @Override - public int hashCode() { - return super.hashCode() + get().hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof AbstractValueHolder) { - AbstractValueHolder other = (AbstractValueHolder)obj; - return super.equals(obj) && get().equals(other.get()); - } - return false; - } - }), is(false)); - } - - private AbstractValueHolder newAbstractValueHolder(final TimeUnit timeUnit, long creationTime) { - return new AbstractValueHolder(-1, creationTime) { - @Override - protected TimeUnit nativeTimeUnit() { - return timeUnit; - } - @Override - public String get() { - throw new UnsupportedOperationException(); - } - }; - } - private AbstractValueHolder newAbstractValueHolder(final TimeUnit timeUnit, long creationTime, long expirationTime) { - return new AbstractValueHolder(-1, creationTime, expirationTime) { - @Override - protected TimeUnit nativeTimeUnit() { - return timeUnit; - } - @Override - public String get() { - throw new UnsupportedOperationException(); - } - }; - } - private AbstractValueHolder newAbstractValueHolder(final TimeUnit timeUnit, long creationTime, long expirationTime, long lastAccessTime) { - final AbstractValueHolder abstractValueHolder = new AbstractValueHolder(-1, creationTime, expirationTime) { - @Override - protected TimeUnit nativeTimeUnit() { - return timeUnit; - } - - @Override - public String get() { - throw new UnsupportedOperationException(); - } - }; - abstractValueHolder.setLastAccessTime(lastAccessTime, timeUnit); - return abstractValueHolder; - } - - private static class TestTimeSource implements TimeSource { - - private long time = 0; - - @Override - public long getTimeMillis() { - return time; - } - - public void advanceTime(long step) { - time += step; - } - } - -} diff --git a/demos/00-NoCache/gradle.properties b/demos/00-NoCache/gradle.properties deleted file mode 100755 index 83967b0dd6..0000000000 --- a/demos/00-NoCache/gradle.properties +++ /dev/null @@ -1,18 +0,0 @@ -# -# Copyright Terracotta, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -subPomName = Ehcache 3 no cache demo module -subPomDesc = The no cache demo module of Ehcache 3 diff --git a/demos/01-CacheAside/gradle.properties b/demos/01-CacheAside/gradle.properties deleted file mode 100755 index 2f155acc0b..0000000000 --- a/demos/01-CacheAside/gradle.properties +++ /dev/null @@ -1,18 +0,0 @@ -# -# Copyright Terracotta, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -subPomName = Ehcache 3 cache aside demo module -subPomDesc = The cache aside demo module of Ehcache 3 diff --git a/demos/build.gradle b/demos/build.gradle index 4e40075d0a..fbf81c9647 100644 --- a/demos/build.gradle +++ b/demos/build.gradle @@ -1,9 +1,10 @@ plugins { - id 'org.gretty' version '2.1.0' apply false + id 'org.ehcache.build.conventions.war' apply false + id 'org.gretty' apply false } subprojects { - apply plugin: 'war' + apply plugin: 'org.ehcache.build.conventions.war' apply plugin: 'org.gretty' repositories { @@ -17,7 +18,7 @@ subprojects { } dependencies { - implementation project(':impl') + implementation project(':ehcache-impl') implementation 'javax.servlet:javax.servlet-api:3.1.0' runtimeOnly 'ch.qos.logback:logback-classic:1.2.3' runtimeOnly 'com.h2database:h2:1.4.196' diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000000..6e15781ff5 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,388 @@ +#!/usr/bin/env bash + +### +# Call this script to perform a release of Ehcache (Maven central, website, kit and all). +# +# See https://github.com/ehcache/ehcache3/wiki/dev.release for details. +# +# Set a dryRun variable if you want to skip commits and pushes +### + +# to exit in case of error +set -e +# to see what's going on +#set -v + +function pause { + echo + read -p "Press [enter] to continue" +} + +echo 'Welcome to the Ehcache release wizard' +echo +echo 'This wizard will guide you through an Ehcache release. Some steps will be performed automatically, some will require your help' +echo + +if [ -z "$git_origin" ]; then + git_origin=git@github.com:ehcache/ehcache3.git +fi +current_branch=$(git branch | grep '^\*' | cut -d ' ' -f 2) + +read -e -p "You want to deploy from the git branch named ${current_branch}, is that right? (Y/n): " YN +[[ $YN != "y" && $YN != "Y" && $YN != "" ]] && (echo "Please checkout the correct branch and restart this script" && exit 1) + +echo +echo 'We will now make sure you are up-to-date with the origin' +echo + +git pull $git_origin $current_branch + +if [ ! -z "$(git status --porcelain)" ]; then + echo 'You have local changes. Please remove them and relaunch this script' + exit 1 +fi + +echo +echo 'All good' +echo + +read -e -p "Which version do you want to release? " version + +# A major release will end with 0. e.g. 3.7.0, 3.8.0 +if [ "$(echo $version | cut -d '.' -f 3)" == "0" ]; then + is_major=1 +else + is_major=0 +fi + +# A latest version will always be deployed from master. Bugfix of ealier versions will be from a release/x.y branch +if [ "$current_branch" == "master" ]; then + is_latest_version=1 +else + is_latest_version=0 +fi + +read -e -p "You want to deploy version ${version} from branch ${current_branch}. Is that correct? (Y/n)" YN +[[ $YN != "y" && $YN != "Y" && $YN != "" ]] && (echo "Aborting due to wrong input" && exit 1) + +major_version="$(echo $version | cut -d'.' -f 1).$(echo $version | cut -d'.' -f 2)" +short_major_version=${major_version//[.]/} + +echo +echo 'We will start by configuring GitHub correctly' +echo "First make sure you have a milestone for version ${version} at https://github.com/ehcache/ehcache3/milestones" +echo "If you don't, create it. Name it ${version}" + +read -e -p "What is the milestone number? (look at the URL) " milestone + +echo +echo 'Now attach any closed issues and PR since the last version' +read -e -p 'What was the previous version? ' previous_version +echo 'A helpful git log will now be printed' +echo +git --no-pager log v${previous_version}..HEAD +pause + +echo "Now, let's create an issue for the release" +echo "It contains checkboxes that you will check along the release" +echo "Open https://github.com/ehcache/ehcache3/issues" +echo "Press 'New Issue'" +echo "Set the title to 'Release ${version}'" +echo "Attach the issue to the milestone ${version}" +echo "Assign the issue to you" +echo "Set the description to the content of dist/templates/github-release-issue.md" +echo "Create the issue" +pause + +echo "We will now" +echo "1- Create a local release branch named release-${version}." +echo "2- Commit the final version and tag it." +echo "Only the tag will be pushed to origin (not the branch)." +echo + +sed -i '' "s/%VERSION%/${version}/g" dist/templates/github-release.md +sed -i '' "s/%MILESTONE%/${milestone}/g" dist/templates/github-release.md +sed -i '' "s/%MAJORVERSION%/${major_version}/g" dist/templates/github-release.md +echo "Please add a little description for the release in dist/templates/github-release.md" +pause + +sed -i '' "s/ehcacheVersion = .*/ehcacheVersion = ${version}/" gradle.properties +if [ -z "$dryRun" ]; then + git checkout -b release-${version} + git add gradle.properties dist/templates/github-release.md + git commit -m "Version ${version}" + git tag -m ":ship: Release ${version}" -v${version} + git push $git_origin v${version} +else + echo git checkout -b release-${version} + echo git add gradle.properties dist/templates/github-release.md + echo git commit -m "Version ${version}" + echo git tag -m ":ship: Release ${version}" v${version} + echo git push $git_origin v${version} +fi + +echo +echo 'Now launch the release to Maven central' +echo '1- Open http://jenkins.terracotta.eur.ad.sag:8080/view/All%20Pipelines/job/publishers-10.2.0/job/ehcache-releaser-3.x/' +echo '2- Press "Build with parameters' +echo "3- Enter v${version} in the git_tag field" +echo "4- Come back here when it's done" +pause + +echo +echo "We will now create a GitHub release" +echo "Open https://github.com/ehcache/ehcache3/tags" +echo "On our new tag v${version}, you will see three dots at the far right" +echo "Click on it and select 'Create Release'" +echo "Set the Tag version to v${version}" +echo "Set the Release title to 'Ehcache ${version}'" +echo "Set the following in the description" +release_description=$(< dist/templates/github-release.md) +echo "$release_description" +pause + +echo "Add the binaries from Maven central to the release" +echo "They will be downloaded in dist/build/binaries" +mkdir -p dist/build/binaries +pushd dist/build/binaries +wget https://repo1.maven.org/maven2/org/ehcache/ehcache/${version}/ehcache-${version}-javadoc.jar +wget https://repo1.maven.org/maven2/org/ehcache/ehcache/${version}/ehcache-${version}-spi-javadoc.jar +wget https://repo1.maven.org/maven2/org/ehcache/ehcache/${version}/ehcache-${version}.jar +wget https://repo1.maven.org/maven2/org/ehcache/ehcache-clustered/${version}/ehcache-clustered-${version}-javadoc.jar +wget https://repo1.maven.org/maven2/org/ehcache/ehcache-clustered/${version}/ehcache-clustered-${version}-kit.tgz +wget https://repo1.maven.org/maven2/org/ehcache/ehcache-clustered/${version}/ehcache-clustered-${version}-kit.zip +wget https://repo1.maven.org/maven2/org/ehcache/ehcache-clustered/${version}/ehcache-clustered-${version}.jar +wget https://repo1.maven.org/maven2/org/ehcache/ehcache-transactions/${version}/ehcache-transactions-${version}-javadoc.jar +wget https://repo1.maven.org/maven2/org/ehcache/ehcache-transactions/${version}/ehcache-transactions-${version}.jar +popd +pause + +echo "Create the release" +pause + +echo "We are doing good. Now let's attack the website" +read -e -p "Where is the ehcache.org-site clone located? (default:../ehcache.org-site): " site_dir + +if [ "$site_dir" == "" ]; then + site_dir='../ehcache.org-site' +fi + +if [ $is_major ]; then + echo "Adding XSDs since this is a major version" + cp ehcache-xml/src/main/schema/ehcache-core.xsd $site_dir/schema/ehcache-core.xsd + cp ehcache-xml/src/main/schema/ehcache-core.xsd $site_dir/schema/ehcache-core-${major_version}.xsd + cp ehcache-107/src/main/resources/ehcache-107-ext.xsd $site_dir/schema/ehcache-107-ext.xsd + cp ehcache-107/src/main/resources/ehcache-107-ext.xsd $site_dir/schema/ehcache-107-ext-${major_version}.xsd + cp clustered/ehcache-client/src/main/resources/ehcache-clustered-ext.xsd $site_dir/schema/ehcache-clustered-ext.xsd + cp clustered/ehcache-client/src/main/resources/ehcache-clustered-ext.xsd $site_dir/schema/ehcache-clustered-ext-${major_version}.xsd + cp ehcache-transactions/src/main/resources/ehcache-tx-ext.xsd $site_dir/schema/ehcache-tx-ext.xsd + cp ehcache-transactions/src/main/resources/ehcache-tx-ext.xsd $site_dir/schema/ehcache-tx-ext-${major_version}.xsd +fi + +echo "Copy the javadoc from Maven central" +unzip "dist/build/binaries/ehcache-${version}-javadoc.jar" -d "${site_dir}/apidocs/${version}" +unzip "dist/build/binaries/ehcache-clustered-${version}-javadoc.jar" -d "${site_dir}/apidocs/${version}/clustered" +unzip "dist/build/binaries/ehcache-transactions-${version}-javadoc.jar" -d "${site_dir}/apidocs/${version}/transactions" + +echo "Remove Manifests" +rm -rf "${site_dir}/apidocs/${version}/META-INF" "${site_dir}/apidocs/${version}/clustered/META-INF" "${site_dir}/apidocs/${version}/transactions/META-INF" + +pushd $site_dir +if [ -z "$dryRun" ]; then + git checkout master + git pull origin master + git checkout -b "ehcache${version}" +else + echo git checkout master + echo git pull origin master + echo git checkout -b "ehcache${version}" +fi + +if [ $is_major ]; then + echo "Update _config.yml" + echo " -" >> _config.yml + echo " scope:" >> _config.yml + echo " path: \"documentation/${major_version}\"" >> _config.yml + echo " type: \"pages\"" >> _config.yml + echo " values:" >> _config.yml + echo " layout: \"docs35_page\"" >> _config.yml + echo " ehc_version: \"${major_version}\"" >> _config.yml + echo " ehc_javadoc_version: \"${version}\"" >> _config.yml + echo " ehc_checkout_dir_var: \"sourcedir39\"" >> _config.yml + + sed -i '' "s/#needle\_for\_sourcedir/ - sourcedir${short_major_version}=\/_eh${short_major_version}\\ +#needle_for_sourcedir/" _config.yml + sed -i '' "s/current: \"[0-9]\.[0-9]\"/current: \"${major_version}\"/" _config.yml + read -e -p "What is the future version? " future_version + sed -i '' "s/future: \"[0-9]\.[0-9]\"/future: \"${future_version}\"/" _config.yml + + echo "Update home_announcement.html" + sed -i '' "s/Ehcache [0-9]\.[0-9] is now available/Ehcache ${major_version} is now available/" _includes/home_announcement.html + + echo "Update documentation/index.md" + echo "Please add the following line in the current documentation section and move the existing one to history" + echo "|[Ehcache ${major_version} User Guide](/documentation/${major_version}/) |[Core JavaDoc](/apidocs/${version}/index.html){:target=\"_blank\"}
[Clustered Module JavaDoc](/apidocs/${version}/clustered/index.html){:target=\"_blank\"}
[Transactions Module JavaDoc](/apidocs/${version}/transactions/index.html){:target=\"_blank\"}|" + + echo "Update schema/index.md" + sed -i '' "s/\[\/\/\]: # (needle_core)/ * [ehcache-core-${major_version}.xsd](\/schema\/ehcache-core-${major_version}.xsd)\\ +[\/\/]: # (needle_core)/" schema/index.md + sed -i '' "s/\[\/\/\]: # (needle_107)/ * [ehcache-107-ext-${major_version}.xsd](\/schema\/ehcache-107-ext-${major_version}.xsd)\\ +[\/\/]: # (needle_107)/" schema/index.md + sed -i '' "s/\[\/\/\]: # (needle_tx)/ * [ehcache-tx-ext-${major_version}.xsd](\/schema\/ehcache-tx-ext-${major_version}.xsd)\\ +[\/\/]: # (needle_tx)/" schema/index.md + sed -i '' "s/\[\/\/\]: # (needle_clustered)/ * [ehcache-clustered-ext-${major_version}.xsd](\/schema\/ehcache-clustered-ext-${major_version}.xsd)\\ +[\/\/]: # (needle_clustered)/" schema/index.md + +else + echo "Update _config.yml" + sed -i '' "s/ehc_javadoc_version: \"${major_version}\.[0-9]\"/ehc_javadoc_version: \"${version}\"/" _config.yml + + echo "Update documentation/index.md" + echo "Update with the following line in the current documentation section" + echo "|[Ehcache ${major_version} User Guide](/documentation/${major_version}/) |[Core JavaDoc](/apidocs/${version}/index.html){:target=\"_blank\"}
[Clustered Module JavaDoc](/apidocs/${version}/clustered/index.html){:target=\"_blank\"}
[Transactions Module JavaDoc](/apidocs/${version}/transactions/index.html){:target=\"_blank\"}|" +fi + +if [ $is_latest_version ]; then + echo "Update ehc3_quickstart.html" + sed -i '' "s/version>[0-9]\.[0-9]\.[0-9]<\/version/version\>${version}\<\/version/" _includes/ehc3_quickstart.html +fi + +echo "Please make sur the docs35_page layout in _config.yml is still valid" +pause + +echo "Check that README.md table about version, version_dir and branch is still accurate" +pause + +read -e -p "What is the upstream repository name? " samples_upstream + +if [ -z "$dryRun" ]; then + git add . + git commit -m "Release ${version}" + git push --set-upstream ${samples_upstream} "ehcache${version}" +else + echo git add . + echo git commit -m "Release ${version}" + echo git push --set-upstream ${samples_upstream} "ehcache${version}" +fi +popd + +echo "Now please open a PR over branch ehcache${version} https://github.com/ehcache/ehcache3.org-site/pulls" +pause +popd + +echo "Website deployment is done every 15 minutes" +echo "If you want to start it manually: http://jenkins.terracotta.eur.ad.sag:8080/job/websites/job/ehcache.org-site-publisher/" + +echo +echo "Now please update the current and next release version in README.adoc" +echo +pause + +echo +echo "Finally, close the GitHub issue and the milestone" +echo +pause + +echo +echo "We now need to deploy the docker images" +echo +read -e -p "What is the terracotta platform version to deploy?" terracotta_version +read -e -p "What is the Terracotta-OSS docker clone located (default:../docker)?" docker_dir +read -e -p "Which previous image do you want to base your image on?" template_image + +escaped_template_image=${template_image//./\\.} + +echo "You now need to create the appropriate triggers on Docker hub" + +echo "Open https://hub.docker.com/r/terracotta/sample-ehcache-client/~/settings/automated-builds/" +echo "Change the Dockerfile location of the latest tag to /${terracotta_version}/sample-ehcache-client" +echo "Add a line with tag ${terracotta_version} and Dockerfile location /${terracotta_version}/sample-ehcache-client" + +echo "Open https://hub.docker.com/r/terracotta/terracotta-server-oss/~/settings/automated-builds/" +echo "Change the Dockerfile location of the latest tag to /${terracotta_version}/server" +echo "Add a line with tag ${terracotta_version} and Dockerfile location /${terracotta_version}/server" + +pushd $docker_dir + +cp -r $template_image $terracotta_version +sed -i '' "s/${escaped_template_image}/${terracotta_version}/g" ${terracotta_version}/sample-ehcache-client/README.md +sed -i '' "s/ehcache-clustered-[0-9]\.[0-9]\.[0-9]-kit.tgz/ehcache-clustered-${version}-kit.tgz/g" ${terracotta_version}/sample-ehcache-client/Dockerfile +sed -i '' "s/ehcache-clustered\/[0-9]\.[0-9]\.[0-9]/ehcache-clustered\/${version}/" ${terracotta_version}/sample-ehcache-client/Dockerfile + +sed -i '' "s/${escaped_template_image}/${terracotta_version}/g" ${terracotta_version}/server/README.md +sed -i '' "s/ehcache-clustered-[0-9]\.[0-9]\.[0-9]-kit.tgz/ehcache-clustered-${version}-kit.tgz/g" ${terracotta_version}/server/Dockerfile +sed -i '' "s/ehcache-clustered\/[0-9]\.[0-9]\.[0-9]/ehcache-clustered\/${version}/" ${terracotta_version}/server/Dockerfile + +sed -i '' "s/${escaped_template_image}/${terracotta_version}/g" ${terracotta_version}/README.md +sed -i '' "s/${escaped_template_image}/${terracotta_version}/g" ${terracotta_version}/docker-compose.yml + +sed -i '' "s/ehcache [0-9]\.[0-9]\.[0-9] \/ Terracotta Server OSS ${escaped_template_image}/ehcache ${version} \/ Terracotta Server OSS ${terracotta_version}/" README.md +sed -i '' "s/\[\/\/\]: # (needle_version)/* [${terracotta_version}](\/${terracotta_version}), matches Ehcache ${version}, available from : https:\/\/github.com\/ehcache\/ehcache3\/releases\\ +[\/\/]: # (needle_version)/g" README.md + +if [ -z "$dryRun" ]; then + git add . + git commit -m "Release $terracotta_version using Ehcache $version" + git push origin master +else + echo git add . + echo git commit -m "Release $terracotta_version using Ehcache $version" + echo git push origin master +fi +popd + +echo "Images should appear on Docker Hub in https://hub.docker.com/r/terracotta" +echo "Please check" +pause + +if [ $is_latest_version ]; then + echo + echo "And last but not least, upgrade the samples" + echo + read -e -p "Where is the ehcache3-samples clone located? (default:../ehcache3-samples): " samples_dir + + if [ "$samples_dir" == "" ]; then + samples_dir='../ehcache3-samples' + fi + + pushd $samples_dir + + if [ -z "$dryRun" ]; then + git checkout master + git pull origin master + git checkout -b "ehcache${version}" + else + echo git checkout master + echo git pull origin master + echo git checkout -b "ehcache${version}" + fi + + sed -i '' "s/.*<\/ehcache3\.version>/${version}<\/ehcache3.version>/" pom.xml + + sed -i '' "s/terracotta-server-oss:.*/terracotta-server-oss:${terracotta_version}/g" fullstack/README.md + sed -i '' "s/terracotta-server-oss:.*/terracotta-server-oss:${terracotta_version}/g" fullstack/src/main/docker/terracotta-server-ha.yml + sed -i '' "s/terracotta-server-oss:.*/terracotta-server-oss:${terracotta_version}/g" fullstack/src/main/docker/terracotta-server-single.yml + + echo "Make sure the JCache version hasn't changed. If yes, update $samples_dir/pom.xml" + pause + + git add . + read -e -p "What is the upstream repository name? " samples_upstream + + if [ -z "$dryRun" ]; then + git commit -m "Upgrade to Ehcache ${version}" + git push --set-upstream ${samples_upstream} "ehcache${version}" + else + echo git commit -m "Upgrade to Ehcache ${version}" + echo git push --set-upstream ${samples_upstream} "ehcache${version}" + fi + popd + + echo "Now please open a PR over branch ehcache${version} https://github.com/ehcache/ehcache3-samples/pulls" + pause +fi + +echo "All done!" +echo "If needed, call ./start_next_version.sh to bump the version to the next one" +echo +echo "Have a good day!" diff --git a/dist/.gitignore b/dist/.gitignore deleted file mode 100755 index ae3c172604..0000000000 --- a/dist/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/bin/ diff --git a/dist/gradle.properties b/dist/gradle.properties deleted file mode 100644 index 0c433da83d..0000000000 --- a/dist/gradle.properties +++ /dev/null @@ -1,20 +0,0 @@ -# -# Copyright Terracotta, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -subPomName = Ehcache -subPomDesc = End-user ehcache3 jar artifact -javadocExclude = **/core/**, **/impl/**, **/xml/**, **/jsr107/**, **/transactions/**, **/management/**, **/tck/** -osgi = {"Import-Package" : ["!org.ehcache.*", "!org.terracotta.*"]} diff --git a/docs/build.gradle b/docs/build.gradle index 8a4551e0c7..42563d66f0 100644 --- a/docs/build.gradle +++ b/docs/build.gradle @@ -14,56 +14,49 @@ * limitations under the License. */ -plugins { - id 'org.asciidoctor.convert' version '1.5.3' - id 'org.kordamp.gradle.livereload' version '0.2.1' - id 'com.github.jruby-gradle.base' version '1.5.0' -} - -dependencies { - gems 'rubygems:asciidoctor-diagram:1.5.4' -} +import org.asciidoctor.gradle.jvm.AsciidoctorJBasePlugin +import org.asciidoctor.gradle.jvm.AsciidoctorTask -configurations.asciidoctor.dependencies.matching({it.group == 'org.asciidoctor' && it.name == 'asciidoctorj-groovy-dsl'}).all { - exclude group:'org.asciidoctor', module:'asciidoctorj' +plugins { + id 'org.ehcache.build.conventions.base' + id 'org.asciidoctor.jvm.base' } -task copyCSS(type: Copy) { - from ('css') { - include '**' +asciidoctorj { + safeMode 'UNSAFE' + attributes 'skip-front-matter': 'true' + fatalWarnings ~/.*/ + modules { + diagram.version '1.5.18' } - into("${buildDir}/asciidoc/user/css") } -task copyImages(type: Copy) { - from('src/docs/asciidoc/user/images') { - include '**' +def createCopyCssTask(def asciidocTask) { + return tasks.register("copy${asciidocTask.name}CSS", Sync) { + from ('css') { + include '**' + } into("${asciidocTask.outputDir}/css") } - into("${buildDir}/asciidoc/user/images") } -asciidoctor.dependsOn copyCSS, copyImages - -asciidoctorj { - version = '1.5.5' -} - -asciidoctor { - dependsOn jrubyPrepare - gemPath = jrubyPrepare.outputDir - requires = ['asciidoctor-diagram'] - separateOutputDirs = false - attributes 'skip-front-matter': 'true' - +tasks.withType(AsciidoctorTask) { + group = AsciidoctorJBasePlugin.TASK_GROUP resources { from('fonts') { include '*' - into('./user/fonts') - } + } into('./fonts') } + dependsOn createCopyCssTask(it) } -liveReload { - docRoot asciidoctor.outputDir.canonicalPath +tasks.register('userDoc', AsciidoctorTask) { + description = 'Generate the user documentation' + sourceDir file('src/docs/asciidoc/user') + outputDir file("$buildDir/asciidoc/user") } +tasks.register('developerDoc', AsciidoctorTask) { + description = 'Generate the developer documentation' + sourceDir file('src/docs/asciidoc/developer') + outputDir file("$buildDir/asciidoc/developer") +} diff --git a/docs/gradle.properties b/docs/gradle.properties deleted file mode 100644 index 63ef54e3be..0000000000 --- a/docs/gradle.properties +++ /dev/null @@ -1,2 +0,0 @@ -subPomName = Ehcache 3 Documentation module -subPomDesc = The Documentation module of Ehcache 3 \ No newline at end of file diff --git a/docs/images/design/basics/Faulting.png b/docs/images/design/basics/Faulting.png deleted file mode 100644 index 21adaa36d9..0000000000 Binary files a/docs/images/design/basics/Faulting.png and /dev/null differ diff --git a/docs/images/design/basics/baseTypes.png b/docs/images/design/basics/baseTypes.png deleted file mode 100644 index 41d1511efe..0000000000 Binary files a/docs/images/design/basics/baseTypes.png and /dev/null differ diff --git a/docs/images/design/basics/cacheStore.png b/docs/images/design/basics/cacheStore.png deleted file mode 100644 index dfb4520b35..0000000000 Binary files a/docs/images/design/basics/cacheStore.png and /dev/null differ diff --git a/docs/images/design/basics/config.png b/docs/images/design/basics/config.png deleted file mode 100644 index 9b80a5f59a..0000000000 Binary files a/docs/images/design/basics/config.png and /dev/null differ diff --git a/docs/images/design/basics/persistentStateTransitions.jpg b/docs/images/design/basics/persistentStateTransitions.jpg deleted file mode 100644 index 79d04ad686..0000000000 Binary files a/docs/images/design/basics/persistentStateTransitions.jpg and /dev/null differ diff --git a/docs/images/design/basics/stateTransitions.png b/docs/images/design/basics/stateTransitions.png deleted file mode 100644 index a9626ddaa8..0000000000 Binary files a/docs/images/design/basics/stateTransitions.png and /dev/null differ diff --git a/docs/images/design/basics/userManagedCache.png b/docs/images/design/basics/userManagedCache.png deleted file mode 100644 index 2a1a85e69b..0000000000 Binary files a/docs/images/design/basics/userManagedCache.png and /dev/null differ diff --git a/docs/images/design/clustered/putIfAbsentUml.png b/docs/images/design/clustered/putIfAbsentUml.png deleted file mode 100644 index 6dc878eb0b..0000000000 Binary files a/docs/images/design/clustered/putIfAbsentUml.png and /dev/null differ diff --git a/docs/src/docs/asciidoc/developer/clustered-events.adoc b/docs/src/docs/asciidoc/developer/clustered-events.adoc new file mode 100644 index 0000000000..20ee7f6b72 --- /dev/null +++ b/docs/src/docs/asciidoc/developer/clustered-events.adoc @@ -0,0 +1,82 @@ += Ehcache events from clustered caches + +This document describes the core design of what is required to implement ehcache events from caches backed by a +clustered store. + +== High-level requirements + +* Ehcache supports five types of cache events: on *eviction*, on *expiry*, on *removal*, on *update* and on *creation*. +* When an event is fired, every connected client with a registered listener has to receive it. +* Events must be delivered once and only once as long as the client(s), server(s) and network-in-between are all +healthy. +* What happens when there is a client disconnect, a passive take over, a split brain or any other hazard is yet to be +determined. +* No performance impact when the feature isn't used. + +=== Recommandations + +It must be made clear (documentation?) that the eventing mechanism is going to have a performance impact. + +Some features are undesirable because they are unlikely to be practical: + +* Synchronous events would require waiting for a round-trip to all clients before achieving a cache operation. This +would pretty much make such cache unusably slow. Ordered events aren't impossible to do, but would require a serious +engineering effort to get right as keeping the events in a strict order isn't trivial, so it's been left out. +Such configs throw an exception when attempted. +* Guaranteeing event delivery in all cases would require some form of stable store and a fairly complex and costly +2-phase logic. This would also have an unsustainable performance impact. Instead, clients should have a way to figure +out when such hazard happen to compensate for the possible lack of event delivery. + +== Technical facts + +Because of the current implementation: + +* All events find their source in a server: *creation*, *update*, *removal* and *expiration* are additions to the chain. +*Eviction* happens when the server is running low on resource and are detected and notified by the chain. + +This means a cluster-wide listener mechanism has to be created with the following features: + +* Clients can register and unregister themselves. This is because events have a performance impact when enabled. +* Events can be fired from any server. +* Clients have to interpret the server-send event that actually are chain operations and resolve those into +client-facing events. E.g.: the appending of a `RemoveOperation` translates to a *removal* event. + +== The transport mechanism + +An event delivery mechanism must be built to transport the events from the clients and servers to all clients registered +as listeners. It requires the following: + +* API to (un)register a client as a listener, through all layers, down to the chain on the server +* API for a server to fire an `append` event +* Modify existing API for a server to fire an `eviction` event to include the necessary data to fire the client-side +equivalent event(s). + +== The straightforward bits + +Modify the `ClusterTierActiveEntity` listener mechanism: +`ServerStoreEvictionListener` already contains `void onEviction(long key)`. Add an extra `Chain evictedChain` parameter. +Then add `void onAppend(ByteBuffer appended, Chain beforeAppend)` and finally rename the interface to +`ServerStoreEventListener`. + +Assuming the plumbing for firing the above notifications from the servers to the clients is done, the resolver of the +client needs to be modified so that it can be used to interpret the appended to and/or evicted chains. + +== The complicated bits + +The following cases are going to be more complicated to implement: + +* Expiration can't be fired once and only once with the existing chain `Operation` s. A new one has to be introduced +for this purpose: `TimestampOperation`. This basically is a noop that indicates that an expiration has been detected +by a client. +* `TimestampOperation` cannot be interpreted by older clients. Fortunately, the codepath on which it's going to be +added is robust enough to intercept such failure which will en up calling the `ResilienceStrategy` to evict the +value. This is slightly odd, but still correct and we don't expect lots of cases where old clients will be mixed in. +* The new `onAppend` callback is not cheap to perform. Simple appends eventually transform into getAndAppend at the +very lowest layer of the chain to be able to make this call. This also means materializing an offheap chain onto the +heap and sending kilobytes (maybe dozens of them) over the wire. This means it must only be materialized when at least +one client has an event listener configured and only forwarded to clients that do have a configured event listener. +This is going to require a dynamic enabling/disabling mechanism as well as some carefully placed if-not-null checks. +* The new `Chain evictedChain` parameter of the `onEviction` callback isn't cheap to generate (it needs to be +materialized from off-heap onto the heap) nor to transport (can easily reach hundreds of KB) so null must be passed +when it isn't needed, exactly for the same reasons as for `onAppend` above. + diff --git a/docs/src/docs/asciidoc/developer/design.basics.adoc b/docs/src/docs/asciidoc/developer/design.basics.adoc index 3c4fe6d94e..1721682d14 100644 --- a/docs/src/docs/asciidoc/developer/design.basics.adoc +++ b/docs/src/docs/asciidoc/developer/design.basics.adoc @@ -17,7 +17,23 @@ means fulfilling a couple of roles: This diagram tries to summarize the different roles: -image::https://raw.githubusercontent.com/ehcache/ehcache3/master/docs/images/design/basics/baseTypes.png[Base Types] +[plantuml] +.... +@startuml +interface CacheManager +interface Cache +interface Service +interface ServiceProvider + +CacheManager ..> Cache : <> +Cache ..> Service : <> +CacheManager ..> Service : <> +CacheManager ..> ServiceProvider : <> +ServiceProvider ..> Service : <> + +hide members +@enduml +.... A user will only mostly interact with the `CacheManager` and `Cache` API types... He may need to configure specific `Service` types for his `Cache` instances to use. See <> @@ -43,7 +59,32 @@ on the `CacheConfiguration` used to configure the given `Cache`. That indirectio be loaded by the `ServiceProvider`, should none be explicitly provided. That in turn will resolve the required `Store` instance to be provided to the `Cache` being created. -image::https://raw.githubusercontent.com/ehcache/ehcache3/master/docs/images/design/basics/cacheStore.png[Cache's Store] +[plantuml] +.... +@startuml +interface Cache +interface Store + +Cache *--> Store +Store <|-- OnHeapStore +Store <|-- OffHeapStore + +package "on createCache()" <> { + interface CacheManager + interface ServiceProvider + interface Service + interface Store.Provider + + CacheManager ..> Cache : <> + CacheManager ..> ServiceProvider : <> + CacheManager ..> Store.Provider : <> + Service "*" <--* ServiceProvider + Service <|-- Store.Provider +} + +hide members +@enduml +.... The `Cache` also tries to never _fails_ on operations invoked, e.g. a get shouldn't result in throwing an exception if the `Store` that backs it up uses serialization and fails to retrieve the mapping. Instead, Ehcache tries to be resilient and @@ -66,7 +107,19 @@ As the user manages that instance himself, he needs to provide all `Service` ins Also he'll need to invoke lifecycle methods on it (see <>) and finally keep a reference to it, as it won't available in any `CacheManager`. -image::https://raw.githubusercontent.com/ehcache/ehcache3/master/docs/images/design/basics/userManagedCache.png[UserManagedCache] +[plantuml] +.... +@startuml +interface Cache +interface UserManagedCache +interface Service + +UserManagedCache --|> Cache +UserManagedCache ..> Service : <> + +hide members +@enduml +.... == State transitions @@ -78,7 +131,21 @@ A lifecycled instance, e.g. a `CacheManager` or a `UserManagedCache`, has three operations can be performed on the instance; . `AVAILABLE`: The operational state of the instance, all operations can be performed by any amount of threads. -image::https://raw.githubusercontent.com/ehcache/ehcache3/master/docs/images/design/basics/stateTransitions.png[Statuses & transitions] +[plantuml] +.... +@startuml +[*] --> UNINITIALIZED + +UNINITIALIZED --> UNINITIALIZED : transition failure + +UNINITIALIZED --> AVAILABLE : init() +UNINITIALIZED --> MAINTENANCE : toMaintenance() + +AVAILABLE --> UNINITIALIZED : shutdown() +MAINTENANCE --> UNINITIALIZED : shutdown() +hide empty description +@enduml +.... State should only be maintained at the _higher_ user-visible API instance, e.g. a concrete `Cache` instance like `Ehcache`. That means that it is the warrant for blocking operations during state transitions or on an _illegal state_. No need for @@ -103,7 +170,35 @@ immutable. Instances of these types are used to configure some part of the syste configuration_ is introduced, e.g. `RuntimeCacheConfiguration`. That type will expose additional mutative methods for attributes that are mutable. Internally it will also let consumers of the type register listener for these attributes. -image::https://raw.githubusercontent.com/ehcache/ehcache3/master/docs/images/design/basics/config.png[Configuration types] +[plantuml] +.... +@startuml +interface CacheManagerBuilder +interface Configuration +interface CacheConfiguration +interface ServiceConfiguration +interface ServiceCreationConfiguration + +CacheManagerBuilder ..> Configuration : <> +Configuration *--> "*" ServiceCreationConfiguration +Configuration *--> "*" CacheConfiguration +CacheConfiguration *--> "*" ServiceConfiguration +hide members + +package runtime <> { + interface CacheManager + interface Service + interface RuntimeCacheConfiguration + interface Cache + + CacheManager <.. CacheManagerBuilder : <> + CacheManager ..> Service : <> + Cache ..> Service : <> + Cache <.. CacheManager : <> + Cache ..> RuntimeCacheConfiguration : <> +} +@enduml +.... ==== Services creation, `ServiceCreationConfiguration`, `ServiceProvider` and `ServiceConfiguration` @@ -192,7 +287,36 @@ datastructures on disk to store. We think of states of those structures in these . Online: the datastructures are present (with or without any data), referenced by the `Store` and the `Cache` is usable; . Offline: the datastructures are present (with or without data), not referenced by any `Store` and nothing accesses it. -image::https://raw.githubusercontent.com/ehcache/ehcache3/master/docs/images/design/basics/persistentStateTransitions.jpg[Persistence and statuses & their transitions] +[plantuml] +.... +@startuml +state store { +[*] --> UNINITIALIZED + +UNINITIALIZED --> UNINITIALIZED : transition failure + +UNINITIALIZED --> AVAILABLE : init +UNINITIALIZED --> MAINTENANCE : toMaintenance + +AVAILABLE --> UNINITIALIZED : shutdown +MAINTENANCE --> UNINITIALIZED : shutdown + +MAINTENANCE --> MAINTENANCE : destroy +MAINTENANCE --> MAINTENANCE : create +} + +state data { + [*] --> ONLINE : create + ONLINE --> [*] : destroy + + OFFLINE --> ONLINE : init + ONLINE --> OFFLINE : shutdown + OFFLINE --> [*] : destroy + OFFLINE --> OFFLINE : transition failure +} +hide empty description +@enduml +.... The user can fallback to the maintenance mode and the `Maintainable` instance returned when transitioning to the maintenance state. That `Maintainable` can be used to: diff --git a/docs/src/docs/asciidoc/developer/design.tiering.adoc b/docs/src/docs/asciidoc/developer/design.tiering.adoc index 5954e9dc71..b6c66f975f 100644 --- a/docs/src/docs/asciidoc/developer/design.tiering.adoc +++ b/docs/src/docs/asciidoc/developer/design.tiering.adoc @@ -2,7 +2,7 @@ :toc: -The `Store` in Ehcache is designed to hold data in a tiered model. +The `Store` in Ehcache is designed to hold data in a tiered model. A cache will always have at least one tier, possible multiple. As soon as there are multiple tiers, they are immediately divided between a single `AuthoritativeTier` and one or more `CachingTier`. It is the `TieredStore` that wires the authority and caching tiers and provides a unified view of the `Store` for the Cache. @@ -47,19 +47,21 @@ entry is expired/evicted from caching tier, it flushes that information to autho using invalidation listener. All the flushed entries are now marked as evictable in authority. -image::https://raw.githubusercontent.com/ehcache/ehcache3/master/docs/images/design/basics/Faulting.png[Faulting & Flushing] - - . All writes directly go to the Authoritative tier.This tier has all the data such that caching tier always has a subset of authority - - . On return, the mapping in the caching tier is invalidated. - - . On eviction or expiry in the caching tier, the mapping is flushed to the authority. - - . Any reading thread tries to get data from the caching tier and returns - - . If the key is not found in caching tier, it is faulted from authority - - . On return, the mapping faulted from the authority is installed in the caching tier. The install may fail under heavy contention to preserve correctness. +[plantuml] +.... +include::{includedir}/../../uml/put.puml[] +.... + * All writes directly go to the Authoritative tier.This tier has all the data such that caching tier always has a subset of authority + * On return, the mapping in the caching tier is invalidated. + * On eviction or expiry in the caching tier, the mapping is flushed to the authority. + +[plantuml] +.... +include::{includedir}/../../uml/get.puml[] +.... + * Any reading thread tries to get data from the caching tier and returns + * If the key is not found in caching tier, it is faulted from authority + * On return, the mapping faulted from the authority is installed in the caching tier. The install may fail under heavy contention to preserve correctness. diff --git a/docs/src/docs/asciidoc/developer/module.clustering.adoc b/docs/src/docs/asciidoc/developer/module.clustering.adoc index fa41460323..00102bff0a 100644 --- a/docs/src/docs/asciidoc/developer/module.clustering.adoc +++ b/docs/src/docs/asciidoc/developer/module.clustering.adoc @@ -66,4 +66,7 @@ _HA_? ==== putIfAbsent -image::https://github.com/ehcache/ehcache3/blob/master/docs/images/design/clustered/putIfAbsentUml.png[putIfAbsent sequence] +[plantuml] +.... +include::{includedir}/../../uml/putIfAbsentUml.puml[] +.... diff --git a/docs/src/docs/asciidoc/user/.asciidoctorconfig b/docs/src/docs/asciidoc/user/.asciidoctorconfig new file mode 100644 index 0000000000..8bfbdba299 --- /dev/null +++ b/docs/src/docs/asciidoc/user/.asciidoctorconfig @@ -0,0 +1,3 @@ + +:includedir: . +:gradle-rootdir: ../../../../.. diff --git a/docs/src/docs/asciidoc/user/107.adoc b/docs/src/docs/asciidoc/user/107.adoc index 96b9189634..6c627a94af 100644 --- a/docs/src/docs/asciidoc/user/107.adoc +++ b/docs/src/docs/asciidoc/user/107.adoc @@ -1,12 +1,12 @@ --- --- = The Ehcache 3.x JSR-107 Provider -ifndef::sourcedir36[] -include::common.adoc[] -endif::sourcedir36[] +ifndef::sourcedir39[] +include::{includedir}/common.adoc[] +endif::sourcedir39[] ifdef::notBuildingForSite[] -include::menu.adoc[] +include::{includedir}/menu.adoc[] endif::notBuildingForSite[] [[overview]] @@ -59,7 +59,7 @@ Here is a code sample that demonstrates the usage of the basic JCache configurat [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/107/src/test/java/org/ehcache/docs/EhCache107ConfigurationIntegrationDocTest.java[tag=basicConfigurationExample] +include::{sourcedir39}/ehcache-107/src/test/java/org/ehcache/docs/EhCache107ConfigurationIntegrationDocTest.java[tag=basicConfigurationExample] ---- <1> Retrieves the default CachingProvider implementation from the application's classpath. @@ -91,7 +91,7 @@ you can still get to the underlying Ehcache `CacheRuntimeConfiguration`: [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/107/src/test/java/org/ehcache/docs/EhCache107ConfigurationIntegrationDocTest.java[tag=mutableConfigurationExample] +include::{sourcedir39}/ehcache-107/src/test/java/org/ehcache/docs/EhCache107ConfigurationIntegrationDocTest.java[tag=mutableConfigurationExample] ---- <1> Create a JCache cache using the `MutableConfiguration` interface from the JCache specification. @@ -109,7 +109,7 @@ The way you do this is as follows: [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/107/src/test/java/org/ehcache/docs/EhCache107ConfigurationIntegrationDocTest.java[tag=ehcacheCacheManagerConfigurationExample] +include::{sourcedir39}/ehcache-107/src/test/java/org/ehcache/docs/EhCache107ConfigurationIntegrationDocTest.java[tag=ehcacheCacheManagerConfigurationExample] ---- <1> Cast the `CachingProvider` into the Ehcache specific implementation `org.ehcache.jsr107.EhcacheCachingProvider`, <2> Create a configuration using the specific Ehcache `DefaultConfiguration` and pass it some `CacheManager` level configurations, @@ -122,7 +122,7 @@ When using this mechanism, no JCache `CompleteConfiguration` is used and so you [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/107/src/test/java/org/ehcache/docs/EhCache107ConfigurationIntegrationDocTest.java[tag=ehcacheBasedConfigurationExample] +include::{sourcedir39}/ehcache-107/src/test/java/org/ehcache/docs/EhCache107ConfigurationIntegrationDocTest.java[tag=ehcacheBasedConfigurationExample] ---- <1> Create an Ehcache `CacheConfiguration`. @@ -142,14 +142,14 @@ The following is an example of an XML configuration: [source%nowrap,xml,indent=0] ---- -include::{sourcedir36}/107/src/test/resources/org/ehcache/docs/ehcache-jsr107-config.xml[] +include::{sourcedir39}/ehcache-107/src/test/resources/org/ehcache/docs/ehcache-jsr107-config.xml[] ---- Here is an example of how to access the XML configuration using JCache: [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/107/src/test/java/org/ehcache/docs/EhCache107ConfigurationIntegrationDocTest.java[tag=jsr107UsingXMLConfigExample] +include::{sourcedir39}/ehcache-107/src/test/java/org/ehcache/docs/EhCache107ConfigurationIntegrationDocTest.java[tag=jsr107UsingXMLConfigExample] ---- <1> Invoke `javax.cache.spi.CachingProvider.getCacheManager(java.net.URI, java.lang.ClassLoader)` @@ -179,7 +179,7 @@ You can do this at two different levels: [source%nowrap,xml,indent=0] ---- -include::{sourcedir36}/107/src/test/resources/org/ehcache/docs/ehcache-107-mbeans-cache-manager-config.xml[lines=17..-1] +include::{sourcedir39}/ehcache-107/src/test/resources/org/ehcache/docs/ehcache-107-mbeans-cache-manager-config.xml[lines=17..-1] ---- <1> Using the JCache service extension, you can enable MBeans by default. @@ -187,6 +187,7 @@ include::{sourcedir36}/107/src/test/resources/org/ehcache/docs/ehcache-107-mbean <3> The cache `overrideCache` will have both MBeans disabled, overriding the service configuration. <4> The cache `overrideOneCache` will have the statistics MBean disabled, whereas the management MBean will be enabled according to the service configuration. +[[supplement-jsr-107-configurations]] ==== Supplementing JCache cache configurations using Ehcache XML extensions You can also create `cache-templates`. @@ -201,7 +202,7 @@ To do this, add a `jsr107` service in your XML configuration file: [source%nowrap,xml,indent=0] ---- -include::{sourcedir36}/107/src/test/resources/org/ehcache/docs/ehcache-jsr107-template-override.xml[] +include::{sourcedir39}/ehcache-107/src/test/resources/org/ehcache/docs/ehcache-jsr107-template-override.xml[] ---- <1> First, declare a namespace for the JCache extension, e.g. `jsr107`. @@ -218,7 +219,7 @@ Using the above configuration, you can not only supplement but also override the [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/107/src/test/java/org/ehcache/docs/EhCache107ConfigurationIntegrationDocTest.java[tag=jsr107SupplementWithTemplatesExample] +include::{sourcedir39}/ehcache-107/src/test/java/org/ehcache/docs/EhCache107ConfigurationIntegrationDocTest.java[tag=jsr107SupplementWithTemplatesExample] ---- <1> Assume existing JCache configuration code, which is store-by-value by default @@ -283,5 +284,5 @@ If you need _Ehcache through JCache_ behaviour, the following shows the relevant [source%nowrap,xml,indent=0] ---- -include::{sourcedir36}/107/src/test/resources/org/ehcache/docs/ehcache-jsr107-cache-through.xml[tag=cacheThroughCAS] +include::{sourcedir39}/ehcache-107/src/test/resources/org/ehcache/docs/ehcache-jsr107-cache-through.xml[tag=cacheThroughCAS] ---- diff --git a/docs/src/docs/asciidoc/user/cache-event-listeners.adoc b/docs/src/docs/asciidoc/user/cache-event-listeners.adoc index c3f1e7c897..6938d36340 100644 --- a/docs/src/docs/asciidoc/user/cache-event-listeners.adoc +++ b/docs/src/docs/asciidoc/user/cache-event-listeners.adoc @@ -1,16 +1,14 @@ --- --- = Cache Event Listeners -ifndef::sourcedir36[] -include::common.adoc[] -endif::sourcedir36[] +ifndef::sourcedir39[] +include::{includedir}/common.adoc[] +endif::sourcedir39[] ifdef::notBuildingForSite[] -include::menu.adoc[] +include::{includedir}/menu.adoc[] endif::notBuildingForSite[] -NOTE: Clustering is not yet compatible with event listeners. - [[introduction]] == Introduction @@ -20,7 +18,7 @@ Listeners are registered at the cache level - and therefore only receive events [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/impl/src/test/java/org/ehcache/docs/GettingStarted.java[tag=cacheEventListener] +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/GettingStarted.java[tag=cacheEventListener] ---- <1> Create a `CacheEventListenerConfiguration` using the builder indicating the listener and the events to receive (in this case create and update events) @@ -59,7 +57,7 @@ Cache event listeners may also be added and removed while the cache is being use [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/impl/src/test/java/org/ehcache/docs/GettingStarted.java[tag=registerListenerAtRuntime] +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/GettingStarted.java[tag=registerListenerAtRuntime] ---- <1> Create a `CacheEventListener` implementation instance. @@ -75,7 +73,7 @@ Advanced users may want to tune the level of concurrency which may be used for d [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/impl/src/test/java/org/ehcache/docs/GettingStarted.java[tag=configuringEventProcessingQueues] +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/GettingStarted.java[tag=configuringEventProcessingQueues] ---- <1> Indicate the level of concurrency desired diff --git a/docs/src/docs/asciidoc/user/caching-concepts.adoc b/docs/src/docs/asciidoc/user/caching-concepts.adoc index 31c1182cf6..1cd3192ebc 100644 --- a/docs/src/docs/asciidoc/user/caching-concepts.adoc +++ b/docs/src/docs/asciidoc/user/caching-concepts.adoc @@ -1,12 +1,12 @@ --- --- = Concepts Related to Caching -ifndef::sourcedir36[] -include::common.adoc[] -endif::sourcedir36[] +ifndef::sourcedir39[] +include::{includedir}/common.adoc[] +endif::sourcedir39[] ifdef::notBuildingForSite[] -include::menu.adoc[] +include::{includedir}/menu.adoc[] endif::notBuildingForSite[] [[data-freshness-and-expiration]] @@ -61,7 +61,29 @@ The remote server may optionally have a failover server providing improved high Since clustered storage comes with performance penalties due to such factors as network latency as well as for establishing client/server consistency, this tier, by nature, is slower than local off-heap storage. -image::EhcacheTerminology.png[] +[ditaa] +.... + +-----------------------------------+ + |cBE7 Application | + | | + | +-------------------------------+ | + | |c7DE Cache Manager | | Applications may have one or + | | | | more Cache Managers + | | +-------------------------+ | | + | | |c7AE Cache | | | A Cache Manager can manage + | | |+-----------------------+| | | many Caches + | | ||cFA4 Heap Tier || | | + | | |+-----------------------+| | | + | | || || | | Caches are configured to utilize + | | ||cFA4 Off Heap Tier || | | one or more Tiers for storing + | | || || | | cache entries + | | |+-----------------------+| | | + | | ||cA8D Disk Tier || | | Ehcache keeps the hotter data + | | |+-----------------------+| | | in faster tiers + | | +-------------------------+ | | + | +-------------------------------+ | + +-----------------------------------+ +.... == Topology Types @@ -78,7 +100,47 @@ This topology offers offers a selection of consistency options. A distributed topology is the recommended approach in a clustered or scaled-out application environment. It provides the best combination of performance, availability, and scalability. -image::ClusteredEhcacheTopology.png[] +[ditaa] +.... ++------------------------------+ +------------------------------+ +| Application | | Application | +| cBE7| | cBE7| +|+----------------------------+| |+----------------------------+| +|| Cache Manager c7DE|| || Cache Manager c7DE|| +||+--------------------------+|| ||+--------------------------+|| +||| Cache c7AE||| ||| Cache c7AE||| +|||+------------------------+||| |||+------------------------+||| +|||| Heap Tier cFA4|||| |||| Heap Tier cFA4|||| +|||+------------------------+||| |||+------------------------+||| +|||| |||| |||| |||| +|||| Off Heap Tier |||| |||| Off Heap Tier |||| +|||| cFA4|||| |||| cFA4|||| +|||+------------------------+||| |||+------------------------+||| +|||| Clustered Tier cA8D||||<--+ +-->|||| Clustered Tier cA8D|||| +|||+------------------------+||| | | |||+------------------------+||| +||+--------------------------+|| | | ||+--------------------------+|| +|+----------------------------+| | | |+----------------------------+| ++------------------------------+ | | +------------------------------+ + | | + V V + +------------------------------+ + | Terracotta Server | + | c7AE| + |+----------------------------+| + || Cache Clustered || + || Tier Manager cF55|| + |+----------------------------+| + || || + || Off Heap || + || Data Storage || + || cFA4|| + |+----------------------------+| + +------------------------------+ +.... +* Hot data is cached locally, hotter data in faster tiers +* Data cached by one application instance is available to all cluster members. +* Full data is available in the cluster. +* One or more mirror servers may be deployed to provide HA It is common for many production applications to be deployed in clusters of multiple instances for availability and scalability. However, without a distributed cache, application clusters exhibit a number of undesirable behaviors, such as: diff --git a/docs/src/docs/asciidoc/user/caching-patterns.adoc b/docs/src/docs/asciidoc/user/caching-patterns.adoc index 860d2c5df3..b640647156 100644 --- a/docs/src/docs/asciidoc/user/caching-patterns.adoc +++ b/docs/src/docs/asciidoc/user/caching-patterns.adoc @@ -1,12 +1,12 @@ --- --- = Cache Usage Patterns -ifndef::sourcedir36[] -include::common.adoc[] -endif::sourcedir36[] +ifndef::sourcedir39[] +include::{includedir}/common.adoc[] +endif::sourcedir39[] ifdef::notBuildingForSite[] -include::menu.adoc[] +include::{includedir}/menu.adoc[] endif::notBuildingForSite[] There are several common access patterns when using a cache. diff --git a/docs/src/docs/asciidoc/user/caching-terms.adoc b/docs/src/docs/asciidoc/user/caching-terms.adoc index 2d9c3cae2c..d1581a042a 100644 --- a/docs/src/docs/asciidoc/user/caching-terms.adoc +++ b/docs/src/docs/asciidoc/user/caching-terms.adoc @@ -1,12 +1,12 @@ --- --- = Terms Related to Caching -ifndef::sourcedir36[] -include::common.adoc[] -endif::sourcedir36[] +ifndef::sourcedir39[] +include::{includedir}/common.adoc[] +endif::sourcedir39[] ifdef::notBuildingForSite[] -include::menu.adoc[] +include::{includedir}/menu.adoc[] endif::notBuildingForSite[] == Basic Terms @@ -18,7 +18,8 @@ computation. Data that is already in the cache can be repeatedly accessed with m resources. === Cache Entry -A cache entry consists of a key and its mapped data value within the cache. +A cache entry consists of a key and its mapped data value within the cache. This is also sometimes referred to as a +_cache mapping_. === Cache Hit When a data entry is requested from cache and the entry exists for the given key, it is referred to as a cache hit @@ -28,6 +29,18 @@ When a data entry is requested from cache and the entry exists for the given key When a data entry is requested from cache and the entry does not exist for the given key, it is referred to as a cache miss (or simply, a _miss_). +=== Cache Access +Any time a data entry is requested from cache, the cache is _accessed_ no matter if the outcome was a _hit_ or a _miss_. + +=== Hit Ratio +It is the ratio of cache hits over cache accesses, or _hits_ / _accesses_. For instance, a cache accessed 100 times +that hit 90 times has a hit ratio of: _90_ / _100_ or _0.9_ or _90%_. + +=== Miss Ratio +It is the ratio of cache misses over cache accesses, or _misses_ / _accesses_. For instance, a cache accessed 100 times +that missed 30 times has a miss ratio of: _30_ / _100_ or _0.3_ or _30%_. It is the corollary of _hit ratio_ as +_hit ratio_ + _miss ratio_ is always _1.0_ or _100%_. + === System-of-Record (SoR) The authoritative source of truth for the data. The cache acts as a local copy of data retrieved from or stored to the system-of-record (SOR). The SOR is often a traditional database, although it might be a specialized file system or some @@ -41,7 +54,22 @@ data storage capacity). The removal of entries from the cache after some amount of time has passed, typically as a strategy to avoid stale data in the cache. +=== Off-Heap +When large caches put too much pressure on the GC, a common resort is to store the caches' contents _off-heap_, i.e: +still in the memory of the JVM process but out of reach of the garbage collector. The off-heap implementation Ehcache +uses is https://github.com/Terracotta-OSS/offheap-store/[Terracotta's port] of +http://gee.cs.oswego.edu/dl/html/malloc.html[dlmalloc] to Java backed by NIO direct ``ByteBuffer``s. + === Hot Data Data that has recently been used by an application is very likely to be accessed again soon. Such data is considered -_hot_. A cache may attempt to keep the _hottest_ data most quickly available, while attemping to choose the -_least hot_ data for eviction. +_hot_. A cache may attempt to keep the _hottest_ data most quickly available, while attempting to choose the +_least hot_ data for eviction. Following the Pareto Distribution, you ideally want all your hot data to fit into your +caches. + +=== Pareto Distribution +According to https://www.statisticshowto.datasciencecentral.com/pareto-distribution/[Data Science Central], _the Pareto +distribution is a skewed distribution with heavy, or “slowly decaying” tails (i.e. much of the data is in the tails)_. +This is more commonly known as the 80% / 20% rule. +The entire concept of caching is based on the Pareto Distribution, as caches are only effective when their hit ratio +reaches a certain level, i.e.: as a general rule of thumb 80% of your transactions should be served with cached data and +the remaining 20% by data coming from other, more expensive means. diff --git a/docs/src/docs/asciidoc/user/class-loading.adoc b/docs/src/docs/asciidoc/user/class-loading.adoc index b9a4db8e78..f5a6a1b768 100644 --- a/docs/src/docs/asciidoc/user/class-loading.adoc +++ b/docs/src/docs/asciidoc/user/class-loading.adoc @@ -1,12 +1,12 @@ --- --- = Class loading -ifndef::sourcedir36[] -include::common.adoc[] -endif::sourcedir36[] +ifndef::sourcedir39[] +include::{includedir}/common.adoc[] +endif::sourcedir39[] ifdef::notBuildingForSite[] -include::menu.adoc[] +include::{includedir}/menu.adoc[] endif::notBuildingForSite[] [[about]] diff --git a/docs/src/docs/asciidoc/user/clustered-cache.adoc b/docs/src/docs/asciidoc/user/clustered-cache.adoc index 3a0150a3ca..e16f1e2217 100644 --- a/docs/src/docs/asciidoc/user/clustered-cache.adoc +++ b/docs/src/docs/asciidoc/user/clustered-cache.adoc @@ -1,19 +1,59 @@ --- --- = Clustered Cache -ifndef::sourcedir36[] -include::common.adoc[] -endif::sourcedir36[] +ifndef::sourcedir39[] +include::{includedir}/common.adoc[] +endif::sourcedir39[] ifdef::notBuildingForSite[] -include::menu.adoc[] +include::{includedir}/menu.adoc[] endif::notBuildingForSite[] == Introduction Distributed caching allows you to harness additional benefits of horizontal scale-out, without losing on low latency offered by local on-heap tiers. -image::ClusteredEhcacheTopology.png[] +[ditaa] +.... ++------------------------------+ +------------------------------+ +| Application | | Application | +| cBE7| | cBE7| +|+----------------------------+| |+----------------------------+| +|| Cache Manager c7DE|| || Cache Manager c7DE|| +||+--------------------------+|| ||+--------------------------+|| +||| Cache c7AE||| ||| Cache c7AE||| +|||+------------------------+||| |||+------------------------+||| +|||| Heap Tier cFA4|||| |||| Heap Tier cFA4|||| +|||+------------------------+||| |||+------------------------+||| +|||| |||| |||| |||| +|||| Off Heap Tier |||| |||| Off Heap Tier |||| +|||| cFA4|||| |||| cFA4|||| +|||+------------------------+||| |||+------------------------+||| +|||| Clustered Tier cA8D||||<--+ +-->|||| Clustered Tier cA8D|||| +|||+------------------------+||| | | |||+------------------------+||| +||+--------------------------+|| | | ||+--------------------------+|| +|+----------------------------+| | | |+----------------------------+| ++------------------------------+ | | +------------------------------+ + | | + V V + +------------------------------+ + | Terracotta Server | + | c7AE| + |+----------------------------+| + || Cache Clustered || + || Tier Manager cF55|| + |+----------------------------+| + || || + || Off Heap || + || Data Storage || + || cFA4|| + |+----------------------------+| + +------------------------------+ +.... + * Hot data is cached locally, hotter data in faster tiers + * Data cached by one application instance is available to all cluster members. + * Full data is available in the cluster. + * One or more mirror servers may be deployed to provide HA To enable clustering with Terracotta, you will have to deploy a Terracotta server configured with clustered cache storage. For convenience Ehcache 3.1 introduced a downloadable kit that contains the Terracotta Server and also the required client libraries. @@ -58,42 +98,50 @@ Consequently, when resource capacity is reached and triggers eviction, the evict Here is a pictorial representation of the concepts explained above: -image::StoragePools.png[] +[ditaa] +.... ++-----------------------------------------+ +| Primary Server Resource | +| (196 GB) | +| | +| /---------------\ /---------------\ | +| | Shared Pool A | | Shared Pool B | | +| | (32 GB)cBEC| | (24 GB)cBEC| | +| \---------------/ \---------------/ | +| | +| /---------------\ /---------------\ | +| | Fixed Pool C | | Fixed Pool D | | +| | (32 GB)cA8D| | (16 GB)cA8D| | +| \---------------/ \---------------/ | +| cFAA| ++-----------------------------------------+ +| Secondary Server Resource | +| (96 GB) | +| | +| /---------------\ /---------------\ | +| | Shared Pool E | | Fixed Pool F | | +| | (28 GB)cBEC| | (12 GB)cA8D| | +| \---------------/ \---------------/ | +| cF55| ++-----------------------------------------+ +.... [[starting-server]] == Starting the Terracotta Server -You can start the Terracotta Server with the following configuration. -It contains the bare minimum configuration required for the samples in the rest of the document to work. +The snippet below defines two offheap resources named `primary-server-resource` and `secondary-server-resource` having +sizes `128MB` and `96MB` respectively: -[source,xml,indent=0] +[listing] ---- -include::{sourcedir36}/clustered/client/src/test/resources/configs/docs/tc-config.xml[] +offheap-resources=primary-server-resource:128MB,secondary-server-resource:96MB ---- -The above configuration defines two named server off-heap resources: - -<1> An off-heap resource of 128 MB size named `primary-server-resource`. -<2> Another off-heap resource named `secondary-server-resource` with 96 MB capacity. - -The rest of the document explains in detail how you can configure cache managers and caches to consume the server's off-heap resources. +This can either be defined in config properties file or during server startup. Assuming that you have the clustered Ehcache kit available locally, start with extracting the *ehcache-clustered* kit. -Change to your extracted directory and then execute the *start-tc-server* script as below to start the Terracotta server with the above configuration: - -On Windows: -[source,cmd] ----- -cd /server/bin -start-tc-server.bat -f /tc-config.xml ----- - -On Unix/Mac: -[source,bash] ----- -cd /server/bin -./start-tc-server.sh -f /tc-config.xml ----- +Change to your extracted directory and then execute the *start-tc-server* script located under `$KIT_DIR/server/bin` to start the Terracotta server. +You will then need to activate the cluster using `activate` command of config tool which is located under `$KIT_DIR/tools/bin`. NOTE: You will need to have `JAVA_HOME` point to a Java 8 installation while starting the Terracotta server. @@ -109,13 +157,13 @@ Here is a code sample that shows how to configure a cache manager with clusterin [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/clustered/client/src/test/java/org/ehcache/clustered/client/docs/GettingStarted.java[tag=clusteredCacheManagerExample] +include::{sourcedir39}/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/docs/GettingStarted.java[tag=clusteredCacheManagerExample] ---- <1> Returns the `org.ehcache.config.builders.CacheManagerBuilder` instance. <2> Use the `ClusteringServiceConfigurationBuilder` static method `.cluster(URI)` for connecting the cache manager to the clustered storage at the `URI` specified that returns the clustering service configuration builder instance. Sample `URI` provided in the example is pointing to the clustered storage instance named "my-application" on the Terracotta server (assuming the server is running on localhost and port 9410). -<3> Auto-create the clustered storage if it doesn't already exist. +<3> Auto-create the clustered storage if it doesn't already exist. We also allow auto-create on reconnection since the cluster is not persistent. <4> Returns a fully initialized cache manager that can be used to create clustered caches. <5> Close the cache manager. @@ -126,7 +174,7 @@ This code sample demonstrates the usage of the concepts explained in the previou [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/clustered/client/src/test/java/org/ehcache/clustered/client/docs/GettingStarted.java[tag=clusteredCacheManagerWithServerSideConfigExample] +include::{sourcedir39}/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/docs/GettingStarted.java[tag=clusteredCacheManagerWithServerSideConfigExample] ---- <1> `defaultServerResource(String)` on `ClusteringServiceConfigurationBuilder` instance sets the default server off-heap resource for the cache manager. @@ -153,15 +201,16 @@ When configuring a cache manager to connect to a cluster tier manager there are [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/clustered/client/src/test/java/org/ehcache/clustered/client/docs/GettingStarted.java[tag=clusteredCacheManagerLifecycle] +include::{sourcedir39}/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/docs/GettingStarted.java[tag=clusteredCacheManagerLifecycle] ---- - -<1> In auto-create mode if no cluster tier manager exists then one is created with the supplied configuration. +<1> In auto create mode if no cluster tier manager exists then one is created with the supplied configuration. If it exists and its configuration matches the supplied configuration then a connection is established. If the supplied configuration does not match then the cache manager will fail to initialize. -<2> In expected mode if a cluster tier manager exists and its configuration matches the supplied configuration then a connection is established. +<2> In auto create on reconnect mode we additionally support auto creation of any necessary entities when reconnecting to a cluster. +This behavior is useful in an non-persistent cluster in case the cluster loses its state due to a restart (planned or accidental). +<3> In expected mode if a cluster tier manager exists and its configuration matches the supplied configuration then a connection is established. If the supplied configuration does not match or the cluster tier manager does not exist then the cache manager will fail to initialize. -<3> In config-less mode if a cluster tier manager exists then a connection is established without regard to its configuration. +<4> In config-less mode if a cluster tier manager exists then a connection is established without regard to its configuration. If it does not exist then the cache manager will fail to initialize. [[clustered-cache]] @@ -171,7 +220,7 @@ If it does not exist then the cache manager will fail to initialize. [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/clustered/client/src/test/java/org/ehcache/clustered/client/docs/GettingStarted.java[tag=clusteredCacheTieredExample] +include::{sourcedir39}/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/docs/GettingStarted.java[tag=clusteredCacheTieredExample] ---- <1> Configuring the heap tier for cache. @@ -181,7 +230,7 @@ The equivalent XML configuration is as follows: [source%nowrap,xml,indent=0] ---- -include::{sourcedir36}/clustered/client/src/test/resources/configs/docs/ehcache-clustered.xml[tag=tieringSample] +include::{sourcedir39}/clustered/ehcache-client/src/test/resources/configs/docs/ehcache-clustered.xml[tag=tieringSample] ---- <1> Specify the heap tier for cache. @@ -204,7 +253,7 @@ This comes with a latency penalty on the write operation required to give this g [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/clustered/client/src/test/java/org/ehcache/clustered/client/docs/GettingStarted.java[tag=clusteredCacheConsistency] +include::{sourcedir39}/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/docs/GettingStarted.java[tag=clusteredCacheConsistency] ---- <1> Specify the consistency level through the use of additional service configuration, using _strong_ consistency here. @@ -214,7 +263,7 @@ The equivalent XML configuration is as follows: [source%nowrap,xml,indent=0] ---- -include::{sourcedir36}/clustered/client/src/test/resources/configs/docs/ehcache-clustered.xml[tag=consistencySample] +include::{sourcedir39}/clustered/ehcache-client/src/test/resources/configs/docs/ehcache-clustered.xml[tag=consistencySample] ---- <1> Specify the consistency level through a custom service configuration from the `clustered` namespace. @@ -244,12 +293,12 @@ The example code below shows how this can be implemented. [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/clustered/client/src/test/java/org/ehcache/clustered/client/docs/GettingStarted.java[tag=unspecifiedClusteredCacheExample] +include::{sourcedir39}/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/docs/GettingStarted.java[tag=unspecifiedClusteredCacheExample] ---- -<1> Configure the first cache manager with auto create +<1> Configure the first cache manager with auto create on reconnect <2> Build a cache configuration for a clustered `dedicated` resource pool <3> Create cache `my-dedicated-cache` using the cache configuration -<4> Configure the second cache manager as _expecting_ (auto create off) +<4> Configure the second cache manager as _expecting_ <5> Build a cache configuration for a clustered _unspecified_ resource pool, which will use the previously configured clustered _dedicated_ resource pool. <6> Create cache with the same name `my-dedicated-cache` and use the clustered _unspecified_ cache configuration diff --git a/docs/src/docs/asciidoc/user/common.adoc b/docs/src/docs/asciidoc/user/common.adoc index 27131afdbb..54d2e69a7b 100644 --- a/docs/src/docs/asciidoc/user/common.adoc +++ b/docs/src/docs/asciidoc/user/common.adoc @@ -1,6 +1,7 @@ --- --- -ifndef::sourcedir36[] +ifndef::sourcedir39[] +:version: 3.9 :notBuildingForSite: true ifdef::basebackend-html[:outfilesuffix: .html] :source-highlighter: coderay @@ -10,9 +11,9 @@ ifdef::basebackend-html[:outfilesuffix: .html] :icons: font :iconfont-remote!: :iconfont-name: font-awesome.min -:sourcedir36: ../../../../../ +:sourcedir39: {gradle-rootdir} :imagesdir: images :sectanchors: :idprefix: :idseparator: - -endif::sourcedir36[] +endif::sourcedir39[] diff --git a/docs/src/docs/asciidoc/user/config-derive.adoc b/docs/src/docs/asciidoc/user/config-derive.adoc new file mode 100644 index 0000000000..dd4f35e94a --- /dev/null +++ b/docs/src/docs/asciidoc/user/config-derive.adoc @@ -0,0 +1,302 @@ +--- +--- += Configuration Derivation +ifndef::sourcedir39[] +include::{includedir}/common.adoc[] +endif::sourcedir39[] + +ifdef::notBuildingForSite[] +include::{includedir}/menu.adoc[] +endif::notBuildingForSite[] + +== Principles + +The configuration derivation features allows a new Ehcache configuration object to be derived via a transformation on +an existing configuration object. This can be useful for: + + * pre-processing an externally sourced configuration, adding additional settings before creating the cache manager. + * processing the configuration of an existing cache manager to generate a new configuration. + +The basis of the configuration derivation API is the `Configuration.derive()` method that generates a builder seeded +with the configurations values + +[source,java,indent=0] +---- +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/ConfigurationDerivation.java[tag=deriveContract] +---- +<1> Creates a builder seeded with the configuration's state. +<2> Configurations built using the builder are then functionally identical to the original configuration. + +== Core Configuration Changes + +The configuration builder returned by the derive method provide direct methods for modifying core configuration concepts: + +.setting a custom classloader: +[source,java,indent=0] +---- +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/ConfigurationDerivation.java[tag=customClassLoader] +---- + +.adding a cache: +[source,java,indent=0] +---- +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/ConfigurationDerivation.java[tag=withCache] +---- +[cols=".^~a,^.^~d,.^~a"] +|=== +|[source,xml] +---- + + +---- +|→ +|[source,xml] +---- + + + Long.class + Object.class + 10 + + +---- +|=== + +.removing a cache: +[source,java,indent=0] +---- +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/ConfigurationDerivation.java[tag=withoutCache] +---- +[cols=".^~a,^.^~d,.^~a"] +|=== +|[source,xml] +---- + + + Long.class + Object.class + 10 + + +---- +|→ +|[source,xml] +---- + + +---- +|=== + +Updating a cache configuration uses a `UnaryOperator` that is run against a cache configuration +builder seeded using the existing cache configuration. + +.updating a cache, by adding a resource: +[source,java,indent=0] +---- +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/ConfigurationDerivation.java[tag=updateCache] +---- +[cols=".^~a,^.^~d,.^~a"] +|=== +|[source,xml] +---- + + + Long.class + Object.class + 10 + + +---- +|→ +|[source,xml] +---- + + + Long.class + Object.class + + 10 + 100 + + + +---- +|=== + +== Extended Configuration Changes + +Ehcache is a pluggable system, so modifying many of the more complex configurations requires modifying both service +creation configurations and service configurations: + +.adding a service creation configuration (constraining the default thread pool) +[source,java,indent=0] +---- +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/ConfigurationDerivation.java[tag=withServiceCreation] +---- +[cols=".^~a,^.^~d,.^~a"] +|=== +|[source,xml] +---- + + +---- +|→ +|[source,xml] +---- + + + + + +---- +|=== + +.updating a service creation configuration (changing the persistence path) +[source,java,indent=0] +---- +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/ConfigurationDerivation.java[tag=updateServiceCreation] +---- +[cols=".^~a,^.^~d,.^~a"] +|=== +|[source,xml] +---- + + + +---- +|→ +|[source,xml] +---- + + + +---- +|=== + +.adding a service configuration (setting a resilience strategy) +[source,java,indent=0] +---- +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/ConfigurationDerivation.java[tag=withService] +---- +[cols=".^~a,^.^~d,.^~a"] +|=== +|[source,xml] +---- + + + Long.class + Object.class + 10 + + +---- +|→ +|[source,xml] +---- + + + Long.class + Object.class + 10 + + com.example.ThrowingResilienceStrategy + + + +---- +|=== + +.updating a service configuration (changing a clustered cache's consistency) +[source,java,indent=0] +---- +include::{sourcedir39}/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/docs/ConfigurationDerivation.java[tag=updateService] +---- +[cols=".^~a,^.^~d,.^~a"] +|=== +|[source,xml] +---- + + + + + + + + + + 50 + + + + +---- +|→ +|[source,xml] +---- + + + + + + + + + + 50 + + + + +---- +|=== + +=== Removing a service +Removing a service often involves removing both service creation and a service configuration instances since a service +instance its configuration are usually strongly coupled: + +.removing a service (making a cache manager non-clustered) +[source,java,indent=0] +---- +include::{sourcedir39}/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/docs/ConfigurationDerivation.java[tag=removeService] +---- +<1> From all cache configurations... +<2> remove any existing `ClusteredStoreConfiguration` instances. +<3> Create a new resource pool builder... +<4> From the existing resource pools... +<5> filter out any clustered resources. +<6> Add all remaining pools to the new resource pools instance +<7> Finally remove the clustering service creation configuration + +[cols=".^~a,^.^~d,.^~a"] +|=== +|[source,xml] +---- + + + + + + + + + + 100 + 50 + + + + +---- +|→ +|[source,xml] +---- + + + + 100 + + + +---- +|=== diff --git a/docs/src/docs/asciidoc/user/eviction-advisor.adoc b/docs/src/docs/asciidoc/user/eviction-advisor.adoc index 6a8782a44d..e87a8021bf 100644 --- a/docs/src/docs/asciidoc/user/eviction-advisor.adoc +++ b/docs/src/docs/asciidoc/user/eviction-advisor.adoc @@ -1,12 +1,12 @@ --- --- = Eviction Advisor -ifndef::sourcedir36[] -include::common.adoc[] -endif::sourcedir36[] +ifndef::sourcedir39[] +include::{includedir}/common.adoc[] +endif::sourcedir39[] ifdef::notBuildingForSite[] -include::menu.adoc[] +include::{includedir}/menu.adoc[] == Introduction @@ -29,7 +29,7 @@ If the eviction is advised against, Ehcache will try to honor the preference of [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/impl/src/test/java/org/ehcache/docs/GettingStarted.java[tag=cacheEvictionAdvisor] +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/GettingStarted.java[tag=cacheEvictionAdvisor] ---- <1> Configure a constrained heap, as the eviction advisor is only relevant when mappings get evicted from the cache. diff --git a/docs/src/docs/asciidoc/user/examples.adoc b/docs/src/docs/asciidoc/user/examples.adoc index 3ed6feab0d..ad9199d418 100644 --- a/docs/src/docs/asciidoc/user/examples.adoc +++ b/docs/src/docs/asciidoc/user/examples.adoc @@ -1,12 +1,12 @@ --- --- = Examples -ifndef::sourcedir36[] -include::common.adoc[] -endif::sourcedir36[] +ifndef::sourcedir39[] +include::{includedir}/common.adoc[] +endif::sourcedir39[] ifdef::notBuildingForSite[] -include::menu.adoc[] +include::{includedir}/menu.adoc[] endif::notBuildingForSite[] == Peeper - a simple message board @@ -102,5 +102,5 @@ Note the presence of the +Filling cache with peeps+, +Clearing peeps cache+, and [source,xml,indent=0] ---- -include::{sourcedir36}/107/src/test/resources/ehcache-example.xml[] +include::{sourcedir39}/ehcache-107/src/test/resources/ehcache-example.xml[] ---- diff --git a/docs/src/docs/asciidoc/user/expiry.adoc b/docs/src/docs/asciidoc/user/expiry.adoc index 7ad3366e60..5eae004e1a 100644 --- a/docs/src/docs/asciidoc/user/expiry.adoc +++ b/docs/src/docs/asciidoc/user/expiry.adoc @@ -1,12 +1,12 @@ --- --- = Expiry -ifndef::sourcedir36[] -include::common.adoc[] -endif::sourcedir36[] +ifndef::sourcedir39[] +include::{includedir}/common.adoc[] +endif::sourcedir39[] ifdef::notBuildingForSite[] -include::menu.adoc[] +include::{includedir}/menu.adoc[] endif::notBuildingForSite[] == Introduction @@ -20,7 +20,7 @@ Expiry is configured at the cache level, in Java or in XML: [source,java,indent=0] ---- -include::{sourcedir36}/impl/src/test/java/org/ehcache/docs/GettingStarted.java[tag=expiry] +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/GettingStarted.java[tag=expiry] ---- <1> Expiry policy is configured at the cache level, so start by defining a cache configuration, @@ -28,7 +28,7 @@ include::{sourcedir36}/impl/src/test/java/org/ehcache/docs/GettingStarted.java[t [source,xml,indent=0] ---- -include::{sourcedir36}/xml/src/test/resources/configs/docs/expiry.xml[tags=expiry] +include::{sourcedir39}/ehcache-xml/src/test/resources/configs/docs/expiry.xml[tags=expiry] ---- <1> At the cache level, using the predefined _time-to-live_ again. @@ -50,7 +50,7 @@ Supporting your own expiration scheme simply means implementing the `ExpiryPolic [source,java,indent=0] ---- -include::{sourcedir36}/api/src/main/java/org/ehcache/expiry/ExpiryPolicy.java[lines=21..-1] +include::{sourcedir39}/ehcache-api/src/main/java/org/ehcache/expiry/ExpiryPolicy.java[lines=21..-1] ---- The main points to remember on the return value from these methods: @@ -71,7 +71,7 @@ In Java: [source,java,indent=0] ---- -include::{sourcedir36}/impl/src/test/java/org/ehcache/docs/GettingStarted.java[tag=customExpiry] +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/GettingStarted.java[tag=customExpiry] ---- <1> Simply pass your custom expiry instance into the cache builder. @@ -80,7 +80,7 @@ In XML: [source,xml,indent=0] ---- -include::{sourcedir36}/xml/src/test/resources/configs/docs/expiry.xml[tags=customExpiry] +include::{sourcedir39}/ehcache-xml/src/test/resources/configs/docs/expiry.xml[tags=customExpiry] ---- <1> Simply pass the fully qualified class name of your custom expiry. diff --git a/docs/src/docs/asciidoc/user/getting-started.adoc b/docs/src/docs/asciidoc/user/getting-started.adoc index ee1534dd6a..6721782379 100644 --- a/docs/src/docs/asciidoc/user/getting-started.adoc +++ b/docs/src/docs/asciidoc/user/getting-started.adoc @@ -1,13 +1,11 @@ ---- ---- -= Ehcache 3.6 Documentation -ifndef::sourcedir36[] -include::common.adoc[] -endif::sourcedir36[] += Ehcache 3.9 Documentation +ifndef::sourcedir39[] +include::{includedir}/common.adoc[] +endif::sourcedir39[] We feel that the Ehcache 3.x API is a great improvement over the Ehcache 2.x API that has been used by millions of developers. We hope you enjoy this new generation of Ehcache! ifdef::notBuildingForSite[] -include::menu.adoc[] +include::{includedir}/menu.adoc[] endif::notBuildingForSite[] == Configuring Ehcache @@ -27,7 +25,7 @@ As with the previous versions of Ehcache, the canonical way of dealing with `Cac [source,java,indent=0] ---- -include::{sourcedir36}/impl/src/test/java/org/ehcache/docs/GettingStarted.java[tag=cachemanagerExample] +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/GettingStarted.java[tag=cachemanagerExample] ---- <1> The static method `org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder` returns a new `org.ehcache.config.builders.CacheManagerBuilder` instance. @@ -62,7 +60,7 @@ Here is a shorter version featuring 3 important things: [source,java,indent=0] ---- -include::{sourcedir36}/integration-test/src/test/java/org/ehcache/docs/GettingStartedWithStaticImports.java[tag=java7Example] +include::{sourcedir39}/integration-test/src/test/java/org/ehcache/docs/GettingStartedWithStaticImports.java[tag=java7Example] ---- <1> A `CacheManager` implements `Closeable` so can be closed automatically by a try-with-resources. A `CacheManager` must be closed cleanly. In a `finally` block, with a `try-with-resources` or (more frequent for normal applications) in some shutdown hook. @@ -76,7 +74,7 @@ You can create an XML file to configure a `CacheManager`. [source,xml,indent=0] ---- -include::{sourcedir36}/xml/src/test/resources/configs/docs/getting-started.xml[tags=gettingStarted] +include::{sourcedir39}/ehcache-xml/src/test/resources/configs/docs/getting-started.xml[tags=gettingStarted] ---- <1> Declares a `Cache` aliased to `foo`. @@ -110,7 +108,7 @@ In addition, for creating the cache manager with clustering support, you will ne [source,java,indent=0] ---- -include::{sourcedir36}/clustered/client/src/test/java/org/ehcache/clustered/client/docs/GettingStarted.java[tag=clusteredCacheManagerExample] +include::{sourcedir39}/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/docs/GettingStarted.java[tag=clusteredCacheManagerExample] ---- <1> Returns the `org.ehcache.config.builders.CacheManagerBuilder` instance; @@ -139,7 +137,7 @@ A classical example would be using 3 tiers with a persistent disk storage. [source,java,indent=0] ---- -include::{sourcedir36}/impl/src/test/java/org/ehcache/docs/GettingStarted.java[tag=threeTiersCacheManager] +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/GettingStarted.java[tag=threeTiersCacheManager] ---- <1> If you wish to use disk storage (like for persistent `Cache` instances), you'll have to provide a @@ -158,7 +156,7 @@ The following illustrates how to configure a _time-to-live_ expiry. [source,java,indent=0] ---- -include::{sourcedir36}/impl/src/test/java/org/ehcache/docs/GettingStarted.java[tag=expiry] +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/GettingStarted.java[tag=expiry] ---- <1> Expiry is configured at the cache level, so start by defining a cache configuration, diff --git a/docs/src/docs/asciidoc/user/images/ClusteredEhcacheTopology.png b/docs/src/docs/asciidoc/user/images/ClusteredEhcacheTopology.png deleted file mode 100644 index 4bcdb053c2..0000000000 Binary files a/docs/src/docs/asciidoc/user/images/ClusteredEhcacheTopology.png and /dev/null differ diff --git a/docs/src/docs/asciidoc/user/images/EhcacheTerminology.png b/docs/src/docs/asciidoc/user/images/EhcacheTerminology.png deleted file mode 100644 index 2b8d5e1829..0000000000 Binary files a/docs/src/docs/asciidoc/user/images/EhcacheTerminology.png and /dev/null differ diff --git a/docs/src/docs/asciidoc/user/images/Get.png b/docs/src/docs/asciidoc/user/images/Get.png deleted file mode 100644 index 50d7082f20..0000000000 Binary files a/docs/src/docs/asciidoc/user/images/Get.png and /dev/null differ diff --git a/docs/src/docs/asciidoc/user/images/Put.png b/docs/src/docs/asciidoc/user/images/Put.png deleted file mode 100644 index 37b9552222..0000000000 Binary files a/docs/src/docs/asciidoc/user/images/Put.png and /dev/null differ diff --git a/docs/src/docs/asciidoc/user/images/StoragePools.png b/docs/src/docs/asciidoc/user/images/StoragePools.png deleted file mode 100644 index 66df0bbf3b..0000000000 Binary files a/docs/src/docs/asciidoc/user/images/StoragePools.png and /dev/null differ diff --git a/docs/src/docs/asciidoc/user/images/TiersHierarchy.png b/docs/src/docs/asciidoc/user/images/TiersHierarchy.png deleted file mode 100644 index bb3e7be448..0000000000 Binary files a/docs/src/docs/asciidoc/user/images/TiersHierarchy.png and /dev/null differ diff --git a/docs/src/docs/asciidoc/user/index.adoc b/docs/src/docs/asciidoc/user/index.adoc index 027ac12425..62f1869de4 100644 --- a/docs/src/docs/asciidoc/user/index.adoc +++ b/docs/src/docs/asciidoc/user/index.adoc @@ -1,17 +1,15 @@ ---- ---- -= Ehcache 3.6 Documentation Overview -ifndef::sourcedir36[] -include::common.adoc[] -endif::sourcedir36[] += Ehcache {version} Documentation Overview +ifndef::sourcedir39[] +include::{includedir}/common.adoc[] +endif::sourcedir39[] ifdef::notBuildingForSite[] -include::menu.adoc[] +include::{includedir}/menu.adoc[] endif::notBuildingForSite[] == Table of Contents -The Table of Contents provides an overview of the Ehcache 3.6 documentation on this site. +The Table of Contents provides an overview of the Ehcache 3.9 documentation on this site. Each topic below corresponds to a menu item at the left. === Basic Topics @@ -55,4 +53,7 @@ Each topic below corresponds to a menu item at the left. |link:cache-event-listeners{outfilesuffix}[Cache Event Listeners]|Getting notified about events within the cache |link:eviction-advisor{outfilesuffix}[Eviction Advisor]|Affecting the way entries are chosen for eviction |link:class-loading{outfilesuffix}[Class loading]|Ehcache and `ClassLoader` interactions +|link:osgi{outfilesuffix}[OSGi Deployment]|How to use Ehcache in an OSGi Environment +|link:config-derive{outfilesuffix}[Configuration Derivation]|How to derive a new configuration from an existing one +|link:performance{outfilesuffix}[Performance Tuning]|Ehcache Performance Tuning |=== diff --git a/docs/src/docs/asciidoc/user/management.adoc b/docs/src/docs/asciidoc/user/management.adoc index d5b7182f15..14b2c33069 100644 --- a/docs/src/docs/asciidoc/user/management.adoc +++ b/docs/src/docs/asciidoc/user/management.adoc @@ -1,12 +1,12 @@ --- --- = Management and Monitoring -ifndef::sourcedir36[] -include::common.adoc[] -endif::sourcedir36[] +ifndef::sourcedir39[] +include::{includedir}/common.adoc[] +endif::sourcedir39[] ifdef::notBuildingForSite[] -include::menu.adoc[] +include::{includedir}/menu.adoc[] endif::notBuildingForSite[] == Intro @@ -30,7 +30,7 @@ cache manager builder as a service: [source,java,indent=0] ---- -include::{sourcedir36}/management/src/test/java/org/ehcache/docs/ManagementTest.java[tag=usingManagementRegistry] +include::{sourcedir39}/ehcache-management/src/test/java/org/ehcache/docs/ManagementTest.java[tag=usingManagementRegistry] ---- <1> Optional: give a name to your cache manager by using a custom configuration <2> Create an instance of `org.ehcache.management.registry.DefaultManagementRegistryService`. This is only required because the service is used below. @@ -50,7 +50,7 @@ and a cache name to uniquely identify the cache on which you want to query stats [source,java,indent=0] ---- -include::{sourcedir36}/management/src/test/java/org/ehcache/docs/ManagementTest.java[tag=capabilitiesAndContexts] +include::{sourcedir39}/ehcache-management/src/test/java/org/ehcache/docs/ManagementTest.java[tag=capabilitiesAndContexts] ---- <1> Query the `ManagementRegistry` for the registered managed objects' capabilities. <2> Each capability has a unique name you will need to refer to it. @@ -74,7 +74,7 @@ a managed object. Examples of actions could be: clear caches, get their configur [source,java,indent=0] ---- -include::{sourcedir36}/management/src/test/java/org/ehcache/docs/ManagementTest.java[tag=actionCall] +include::{sourcedir39}/ehcache-management/src/test/java/org/ehcache/docs/ManagementTest.java[tag=actionCall] ---- <1> Put something in a cache. <2> Call the 'clear' action on the managed cache. Refer to the descriptors of the provider to get the exact list of @@ -92,7 +92,7 @@ manager by default, but sometimes you may want one `ManagementRegistry` to manag [source,java,indent=0] ---- -include::{sourcedir36}/management/src/test/java/org/ehcache/docs/ManagementTest.java[tag=managingMultipleCacheManagers] +include::{sourcedir39}/ehcache-management/src/test/java/org/ehcache/docs/ManagementTest.java[tag=managingMultipleCacheManagers] ---- <1> Create an instance of `org.ehcache.management.SharedManagementService` <2> Pass it as a service to the first cache manager diff --git a/docs/src/docs/asciidoc/user/menu.adoc b/docs/src/docs/asciidoc/user/menu.adoc index 8bd2d1c68e..b6623d5837 100644 --- a/docs/src/docs/asciidoc/user/menu.adoc +++ b/docs/src/docs/asciidoc/user/menu.adoc @@ -26,6 +26,9 @@ Advanced topics:: - link:./cache-event-listeners{outfilesuffix}[Cache Event Listeners] - link:./eviction-advisor{outfilesuffix}[Eviction Advisor] - link:./class-loading{outfilesuffix}[Class loading in Ehcache] +- link:./osgi{outfilesuffix}[OSGi Deployment] +- link:./config-derive{outfilesuffix}[Configuration Derivation] +- link:./performance{outfilesuffix}[Performance Tuning] Not published:: - link:./management{outfilesuffix}[Management and Monitoring] diff --git a/docs/src/docs/asciidoc/user/migration-guide.adoc b/docs/src/docs/asciidoc/user/migration-guide.adoc index 4046156251..324684b5a3 100644 --- a/docs/src/docs/asciidoc/user/migration-guide.adoc +++ b/docs/src/docs/asciidoc/user/migration-guide.adoc @@ -1,12 +1,12 @@ --- --- = Migration Guide -ifndef::sourcedir36[] -include::common.adoc[] -endif::sourcedir36[] +ifndef::sourcedir39[] +include::{includedir}/common.adoc[] +endif::sourcedir39[] ifdef::notBuildingForSite[] -include::menu.adoc[] +include::{includedir}/menu.adoc[] == Introduction @@ -65,7 +65,7 @@ having dedicated logic in the methods called during the lifecycle of added and u [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/impl/src/test/java/org/ehcache/docs/Ehcache3.java[tag=CustomExpiryEhcache3] +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/Ehcache3.java[tag=CustomExpiryEhcache3] ---- <1> Defining custom expiry to be called during the lifecycle of added mappings. diff --git a/docs/src/docs/asciidoc/user/osgi.adoc b/docs/src/docs/asciidoc/user/osgi.adoc new file mode 100644 index 0000000000..f80b3a8a4b --- /dev/null +++ b/docs/src/docs/asciidoc/user/osgi.adoc @@ -0,0 +1,97 @@ +--- +--- += OSGi Deployment +ifndef::sourcedir39[] +include::{includedir}/common.adoc[] +endif::sourcedir39[] + +ifdef::notBuildingForSite[] +include::{includedir}/menu.adoc[] +endif::notBuildingForSite[] + +== OSGi Compatible Bundles + +The following Ehcache artifacts are also valid OSGi bundles: + +* `org.ehcache:ehcache` ++ +includes: `api`, `core`, `impl`, `xml`, `107` + +* `org.ehcache:ehcache-clustered` +* `org.ehcache:ehcache-transactions` +* `org.ehcache.modules:ehcache-api` +* `org.ehcache.modules:ehcache-core` +* `org.ehcache.modules:ehcache-impl` +* `org.ehcache.modules:ehcache-xml` +* `org.ehcache.modules:ehcache-107` + +== Ehcache Service Lookup & OSGi + +To allow for the extension of its feature set and to support the internal modularity of the source code, Ehcache uses +a `java.util.ServiceLoader` based lookup system to discover the set of available functionalities at runtime. When +deployed as bundles in an OSGi environment this lookup mechanism is replaced by a lookup mechanism based on OSGi +components. Activation of the bundle containing the Ehcache core code will result in the following logging +[source,log] +---- +org.ehcache[org.ehcache.core.osgi.EhcacheActivator] : Detected OSGi Environment (core is in bundle: org.ehcache [13]): Using OSGi Based Service Loading +---- +In this mode, to enable transactional and/or clustered caching it is sufficient to just provision the +`org.ehcache:ehcache-transactions` and/or `org.ehcache:ehcache-clustered` bundles alongside the `org.ehcache:ehcache` +main bundle + +When in this mode of operation it is also possible to provision the Ehcache modules as independent bundles. A minimal +Ehcache configuration will need: + +* `org.ehcache.modules:ehcache-api` +* `org.ehcache.modules:ehcache-core` +* `org.ehcache.modules:ehcache-impl` + +Additional features can then be added by including one or more of: + +* `org.ehcache.modules:ehcache-xml` +* `org.ehcache.modules:ehcache-107` +* `org.ehcache:ehcache-clustered` +* `org.ehcache:ehcache-transactions` + +=== Reverting to JDK Service Lookup + +If the `org.ehcache.core.osgi` property is set to `"false"` as either a framework or system property then Ehcache will +fall back to the JDK based service lookup mechanism. This will result in the following log line: +[source,log] +---- +org.ehcache[org.ehcache.core.osgi.EhcacheActivator] : Detected OSGi Environment (core is in bundle: org.ehcache [13]): OSGi Based Service Loading Disabled Via System/Framework Property - Extensions Outside This Bundle Will Not Be Detected +---- +Enabling debug logging will show the detected set of services: +[source,log] +---- +org.ehcache[org.ehcache.core.osgi.EhcacheActivator] : JDK Service Loading Sees: + org.ehcache.impl.internal.store.heap.OnHeapStore$Provider + org.ehcache.impl.internal.store.offheap.OffHeapStore$Provider + org.ehcache.impl.internal.store.disk.OffHeapDiskStore$Provider + org.ehcache.impl.internal.store.tiering.TieredStore$Provider + org.ehcache.impl.internal.store.tiering.CompoundCachingTier$Provider + org.ehcache.core.spi.time.TimeSourceService + org.ehcache.spi.serialization.SerializationProvider + org.ehcache.spi.loaderwriter.CacheLoaderWriterProvider + org.ehcache.core.events.CacheEventListenerProvider + org.ehcache.core.spi.service.ExecutionService + org.ehcache.core.spi.service.LocalPersistenceService + org.ehcache.impl.persistence.DefaultDiskResourceService + org.ehcache.spi.loaderwriter.WriteBehindProvider + org.ehcache.impl.internal.events.CacheEventDispatcherFactoryImpl + org.ehcache.spi.copy.CopyProvider + org.ehcache.core.spi.store.heap.SizeOfEngineProvider + org.ehcache.core.spi.service.StatisticsService + org.ehcache.spi.resilience.ResilienceStrategyProvider +---- + +In this configuration only features in the bundle with the Ehcache core classes are available. Using this service lookup +mechanism only the `org.ehcache:ehcache` (bundle symbolic name: `org.ehcache`) bundle can be successfully deployed. Use +of this bundle provides for most of the regular Ehcache features, but *does not support transactional or clustered +caching*. + +In order to use transactional or clustered caches in this environment the user must create their own bundle by merging +the `org.ehcache:ehcache` bundle with the `org.ehcache:ehcache-clustered` and/or `org.ehcache:ehcache-transactions` +bundles. Care must be taken when creating the custom bundle that all of the `META-INF/services` files are correctly +merged to allow for correct service discovery. + diff --git a/docs/src/docs/asciidoc/user/performance.adoc b/docs/src/docs/asciidoc/user/performance.adoc new file mode 100644 index 0000000000..f92b36d0ea --- /dev/null +++ b/docs/src/docs/asciidoc/user/performance.adoc @@ -0,0 +1,144 @@ +--- +--- += Performance Tuning +ifndef::sourcedir39[] +include::{includedir}/common.adoc[] +endif::sourcedir39[] + +ifdef::notBuildingForSite[] +include::{includedir}/menu.adoc[] +endif::notBuildingForSite[] + +== Introduction + +Ehcache is fast. Really fast. +It was thought and made to be. +For example, a straight `get(key)` from the cache should be under 500ns. +That's fast and way sufficient for most purpose. + +Keep that in mind. Especially when comparing caching frameworks with a benchmark. +If something looks 10% faster it actually means 50ns faster. +Do you care that much about these 50ns to lose your precious time in benchmarking? + +That said, the way you configure Ehcache can have an impact on performance. +This document is a work in progress. +It will give you the performance impact of classical Ehcache configuration. +It will also give you some advance tuning possibilities. + +In the future, we will add some figures of what "slower" means. +However, always do your own benchmark. + +== Stores + +We probably know that the fastest store is on-heap. +Until you overwhelm the garbage collector. + +Your next best bet is off-heap. + +Try to avoid disk. +Use a remote drive or even an HDD at your own risk. + +We won't talk about clustering here because it's a different realm and its performance is based on many factors. + +The next question would be: "Should I use a single tier?" Is using a single-tier off-heap faster than two-tiers? +The answer depends on what you do with it. +Having two tiers is a bit slower on writing. +It is also a bit slower on reading when the data is not found in the caching tier (on-heap). +However, it will be faster for an entry that is indeed found in a higher tier. + +So again, it depends. +The more you follow the caching hypothesis that the same data is always reused (and so in the caching +tier), the more interesting having two or more tiers will be. As a general rule of thumb, a tier that has a +hit ratio of 50% or less is going to slow down the cache. Either enlarge such tier or completely get rid of it. + +== Byte Sizing + +A on-heap tier can be limited to a number of entries or a number of bytes. +When using bytes, we need to calculate the size of every object added to the cache. +This is of course much slower than calculating the number of entries. + +Size calculation is done using the https://github.com/ehcache/sizeof[SizeOf] library. +This library uses multiple magic tricks to do so. It selects the fastest one for a given environment. +Make sure of what is used to confirm you can't use a faster way on your platform. + +== Serialization + +Off-heap, disk and clustering need to serialize keys and values before storing them. +By default, custom super-fast serializers are used for `Long`, `Integer`, `Character`, `Double`, `Float`, `byte[]` and +`String` objects. +A slightly faster, customized Java serialization is used for other object types. +It is well-known for not being the fastest thing around. +Ehcache uses it because it is supported out of the box. +However, you can increase performance by providing your own serializers. + +== Copier + +By default, on-heap storage stores the entries by reference. +You might want to use a copier to store entries by value for whatever reason. +Out of the box, Ehcache bundles a copier that makes use of its serialization mechanism: +`org.ehcache.impl.copy.SerializingCopier`. +This can be much slower so watch out. + +== Loader-Writer + +Loader-writer is interesting for many reasons. +First, it protects you against the Thundering Herd. +However, it needs to pass through more complicated code to do so. + +We are expecting it to be a tiny bit slower. +But nothing noticeable enough to prevent you from using it. + +== Expiration + +A cache with no expiration will always be faster. + +=== Time to Live + +If you need to set an expiration time, TTL will be the faster one. +This is because the expiration time of an entry is calculated and updated ony when the entry is inserted or updated in the cache. +But it still requires an expiration check at access time. + +So you can expect a 2% drop in performance when using TTL. + +=== Time to Idle + +TTI is slower than TTL. +We need to recalculate and update the expiration time each time the entry is accessed. + +=== Custom + +In general, using a custom `ExpiryPolicy` will be the slowest. +Ehcache had optimised the handling to handle other cases. +When using a custom policy, you are on your own. + +== Allocation rate + +Ehcache won't allocate any object during a simple on-heap `get()`. +However, keep in mind that your configuration might do so. + +For instance, let's say you define an expiry policy like this. + +[source,java,indent=0] +---- +include::{sourcedir39}/integration-test/src/test/java/org/ehcache/docs/Performance.java[tag=expiryAllocation] +---- + +<1> Will instantiate a `Duration` every time an entry is accessed + +In this case, putting the `Duration` as a constant would solve the problem. + +== Time Source + +By default, Ehcache uses a `TimeSource` that will retrieve the system time at every call. It is fast but not super +duper fast. But it is super duper accurate. + +You can trade accuracy for speed by using a `TickingTimeSource`. Please read the javadoc for details but the concept is +that a timer will increase time instead of always retrieving system time. + +Switching to `TickingTimeSource`, even with a granularity of 1ms, can improve the performance of a `get` as high as 30%. + +The drawback is that a timer will continuously run. +Also, time might drift from the real time a bit. +Especially if the granularity of the `systemUpdatePeriod` is big. +Is your expiration needs to be really tightly linked with real time, it can be a problem. +But in most cases, the drifting doesn't matter much. diff --git a/docs/src/docs/asciidoc/user/resilience.adoc b/docs/src/docs/asciidoc/user/resilience.adoc index 1784e7451f..977fce5a7c 100644 --- a/docs/src/docs/asciidoc/user/resilience.adoc +++ b/docs/src/docs/asciidoc/user/resilience.adoc @@ -1,12 +1,12 @@ --- --- = Resilience -ifndef::sourcedir36[] -include::common.adoc[] -endif::sourcedir36[] +ifndef::sourcedir39[] +include::{includedir}/common.adoc[] +endif::sourcedir39[] ifdef::notBuildingForSite[] -include::menu.adoc[] +include::{includedir}/menu.adoc[] endif::notBuildingForSite[] == Introduction @@ -64,7 +64,7 @@ Timeouts can be configured using a dedicated builder or in XML. [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/clustered/client/src/test/java/org/ehcache/clustered/client/docs/Resilience.java[tag=timeoutsExample] +include::{sourcedir39}/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/docs/Resilience.java[tag=timeoutsExample] ---- <1> Start setting timeouts using the build diff --git a/docs/src/docs/asciidoc/user/serializers-copiers.adoc b/docs/src/docs/asciidoc/user/serializers-copiers.adoc index e8639b60de..d91664e230 100644 --- a/docs/src/docs/asciidoc/user/serializers-copiers.adoc +++ b/docs/src/docs/asciidoc/user/serializers-copiers.adoc @@ -1,12 +1,12 @@ --- --- = Serializers and Copiers -ifndef::sourcedir36[] -include::common.adoc[] -endif::sourcedir36[] +ifndef::sourcedir39[] +include::{includedir}/common.adoc[] +endif::sourcedir39[] ifdef::notBuildingForSite[] -include::menu.adoc[] +include::{includedir}/menu.adoc[] endif::notBuildingForSite[] [[overview]] @@ -119,7 +119,7 @@ Implement the following interface, from package `org.ehcache.spi.serialization`: [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/api/src/main/java/org/ehcache/spi/serialization/Serializer.java[lines=20..-1] +include::{sourcedir39}/ehcache-api/src/main/java/org/ehcache/spi/serialization/Serializer.java[lines=20..-1] ---- As the Javadoc states, there are some constructor rules, see the section <> for that. @@ -256,7 +256,7 @@ Implement the following interface: [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/api/src/main/java/org/ehcache/spi/copy/Copier.java[lines=19..-1] +include::{sourcedir39}/ehcache-api/src/main/java/org/ehcache/spi/copy/Copier.java[lines=19..-1] ---- * `T copyForRead(T obj)` is invoked when a copy must be made upon a read operation (like a cache `get()`), diff --git a/docs/src/docs/asciidoc/user/thread-pools.adoc b/docs/src/docs/asciidoc/user/thread-pools.adoc index 5e6e4df6dd..d3967f4a6f 100644 --- a/docs/src/docs/asciidoc/user/thread-pools.adoc +++ b/docs/src/docs/asciidoc/user/thread-pools.adoc @@ -1,12 +1,12 @@ --- --- = Thread Pools -ifndef::sourcedir36[] -include::common.adoc[] -endif::sourcedir36[] +ifndef::sourcedir39[] +include::{includedir}/common.adoc[] +endif::sourcedir39[] ifdef::notBuildingForSite[] -include::menu.adoc[] +include::{includedir}/menu.adoc[] endif::notBuildingForSite[] [[introduction]] @@ -88,7 +88,7 @@ Following are examples of describing how to configure the thread pools the diffe [source%nowrap,java] ---- -include::{sourcedir36}/impl/src/test/java/org/ehcache/docs/ThreadPools.java[tag=diskStore] +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/ThreadPools.java[tag=diskStore] ---- <1> Configure the thread pools. Note that the default one (`dflt`) is required for the events even when no event listener is configured. @@ -99,7 +99,7 @@ include::{sourcedir36}/impl/src/test/java/org/ehcache/docs/ThreadPools.java[tag= [source%nowrap,java] ---- -include::{sourcedir36}/impl/src/test/java/org/ehcache/docs/ThreadPools.java[tag=writeBehind] +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/ThreadPools.java[tag=writeBehind] ---- <1> Configure the thread pools. Note that the default one (`dflt`) is required for the events even when no event listener is configured. @@ -110,7 +110,7 @@ include::{sourcedir36}/impl/src/test/java/org/ehcache/docs/ThreadPools.java[tag= [source%nowrap,java] ---- -include::{sourcedir36}/impl/src/test/java/org/ehcache/docs/ThreadPools.java[tag=events] +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/ThreadPools.java[tag=events] ---- <1> Configure the thread pools. Note that there is no default one so all thread-using services must be configured with explicit defaults. @@ -124,7 +124,7 @@ Following is an example describing how to configure the thread pools the differe [source%nowrap,xml] ---- -include::{sourcedir36}/xml/src/test/resources/configs/docs/thread-pools.xml[tags=threadPools] +include::{sourcedir39}/ehcache-xml/src/test/resources/configs/docs/thread-pools.xml[tags=threadPools] ---- <1> Configure the thread pools. Note that there is no default one. diff --git a/docs/src/docs/asciidoc/user/tiering.adoc b/docs/src/docs/asciidoc/user/tiering.adoc index 0b0a1dfc71..2f63824594 100644 --- a/docs/src/docs/asciidoc/user/tiering.adoc +++ b/docs/src/docs/asciidoc/user/tiering.adoc @@ -1,12 +1,13 @@ --- --- = Ehcache Tiering Options -ifndef::sourcedir36[] -include::common.adoc[] -endif::sourcedir36[] +ifndef::sourcedir39[] +include::{includedir}/common.adoc[] +endif::sourcedir39[] +>>>>>>> Upgrade to Asciidoctor Gradle Plugin 2.0.0 ifdef::notBuildingForSite[] -include::menu.adoc[] +include::{includedir}/menu.adoc[] == Introduction @@ -48,7 +49,7 @@ For this, simply define the single resource in the cache configuration: [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/impl/src/test/java/org/ehcache/docs/Tiering.java[tag=offheapOnly] +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/Tiering.java[tag=offheapOnly] ---- <1> Start with defining the key and value type in the configuration builder. @@ -65,7 +66,7 @@ A heap tier can be sized by entries or by size. [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/impl/src/test/java/org/ehcache/docs/Tiering.java[tag=heap] +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/Tiering.java[tag=heap] ---- <1> Only 10 entries allowed on heap. Eviction will occur when full. @@ -83,7 +84,7 @@ NOTE: Byte sizing has a runtime performance impact that depends on the size and [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/impl/src/test/java/org/ehcache/docs/Tiering.java[tag=byteSizedTieredCache] +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/Tiering.java[tag=byteSizedTieredCache] ---- <1> This will limit the amount of memory used by the heap tier for storing key-value pairs. @@ -103,7 +104,7 @@ If you wish to use off-heap, you'll have to define a resource pool, giving the m [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/impl/src/test/java/org/ehcache/docs/Tiering.java[tag=offheap] +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/Tiering.java[tag=offheap] ---- <1> Only 10 MB allowed off-heap. @@ -126,7 +127,7 @@ The faster and more dedicated the disk is, the faster accessing the data will be [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/impl/src/test/java/org/ehcache/docs/Tiering.java[tag=persistentCacheManager] +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/Tiering.java[tag=persistentCacheManager] ---- <1> To obtain a `PersistentCacheManager` which is a normal `CacheManager` but with the ability to @@ -166,7 +167,7 @@ In some cases, you might want to reduce the concurrency and save resources by re [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/impl/src/test/java/org/ehcache/docs/Tiering.java[tag=diskSegments] +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/Tiering.java[tag=diskSegments] ---- <1> Define an `OffHeapDiskStoreConfiguration` instance specifying the required number of segments. @@ -202,8 +203,22 @@ This leads to the typical pyramid shape for a multi-tiered setup. -- [.left] .Tiers hierarchy -image::TiersHierarchy.png[Tiers hierarchy] - +[ditaa] +.... + +-------------------+ + |cBE7 Heap Tier | + +-+-------------------+-+ + |cFA4 | + | Off Heap Tier | + | | ++-+-----------------------+-+ +|cA8D | +| | +| Disk Tier | +| | +| | ++---------------------------+ +.... Ehcache requires the size of the heap tier to be smaller than the size of the offheap tier, and the size of the offheap tier to be smaller than the size of the disk tier. While Ehcache cannot verify at configuration time that a count-based sizing for heap will be smaller than a byte-based sizing for another tier, you should make sure that is the case during testing. -- @@ -220,7 +235,7 @@ Here is an example using heap, offheap and clustered. [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/clustered/client/src/test/java/org/ehcache/clustered/client/docs/Tiering.java[tag=threeTiersCacheManager] +include::{sourcedir39}/clustered/ehcache-client/src/test/java/org/ehcache/clustered/client/docs/Tiering.java[tag=threeTiersCacheManager] ---- <1> Clustered specific information telling how to connect to the Terracotta cluster @@ -239,7 +254,7 @@ Let's revisit an example used earlier: [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/impl/src/test/java/org/ehcache/docs/Tiering.java[tag=threeTiersCacheManager] +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/Tiering.java[tag=threeTiersCacheManager] ---- This is a cache using 3 tiers (heap, offheap, disk). @@ -253,7 +268,7 @@ Consider for instance this code: [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/impl/src/test/java/org/ehcache/docs/Tiering.java[tag=notShared] +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/Tiering.java[tag=notShared] ---- You will end up with two caches that can contain 10 entries each. @@ -270,7 +285,7 @@ Thus you can't change the sizing of off-heap or disk tiers. [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/impl/src/test/java/org/ehcache/docs/Tiering.java[tag=updateResourcesAtRuntime] +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/Tiering.java[tag=updateResourcesAtRuntime] ---- <1> You will need to create a new `ResourcePools` object with resources of the required size, using `ResourcePoolsBuilder`. @@ -297,19 +312,21 @@ This method destroys a given cache. The cache shouldn't be in use by another cac [[multi-tier-sequence-flow]] == Sequence Flow for Cache Operations with Multiple Tiers - -In order to understand what happens for different cache operations when using multiple tiers, here are examples of _Put_ and _Get_ operations. -The sequence diagrams are oversimplified but still show the main points. - -[.float-group] +[.right] -- -[.left] .Multiple tiers using Put -image::Put.png[Put] -[.left] +[plantuml] +.... +include::{sourcedir39}/docs/src/docs/uml/put.puml[] +.... .Multiple tiers using Get -image::Get.png[Get] +[plantuml] +.... +include::{sourcedir39}/docs/src/docs/uml/get.puml[] +.... -- +In order to understand what happens for different cache operations when using multiple tiers, here are examples of _Put_ and _Get_ operations. +The sequence diagrams are oversimplified but still show the main points. You should then notice the following: @@ -319,5 +336,5 @@ You should then notice the following: * A full cache miss (the value isn't on any tier) will always go all the way down to the authoritative tier. NOTE: The slower your authoritative tier, the slower your `put` operations will be. -For a normal cache usage, it usually doesn't matter since`get` operations are much more frequent than`put` opreations. +For a normal cache usage, it usually doesn't matter since`get` operations are much more frequent than`put` operations. The opposite would mean you probably shouldn't be using a cache in the first place. diff --git a/docs/src/docs/asciidoc/user/usermanaged.adoc b/docs/src/docs/asciidoc/user/usermanaged.adoc index 463ebe28f5..87aac48e79 100644 --- a/docs/src/docs/asciidoc/user/usermanaged.adoc +++ b/docs/src/docs/asciidoc/user/usermanaged.adoc @@ -1,12 +1,12 @@ --- --- = User managed caches -ifndef::sourcedir36[] -include::common.adoc[] -endif::sourcedir36[] +ifndef::sourcedir39[] +include::{includedir}/common.adoc[] +endif::sourcedir39[] ifdef::notBuildingForSite[] -include::menu.adoc[] +include::{includedir}/menu.adoc[] endif::notBuildingForSite[] [[overview]] @@ -44,7 +44,7 @@ The interface definition is shown in this code: [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/api/src/main/java/org/ehcache/UserManagedCache.java[lines=17..-1] +include::{sourcedir39}/ehcache-api/src/main/java/org/ehcache/UserManagedCache.java[lines=17..-1] ---- === User Managed Persistent Cache @@ -63,7 +63,7 @@ The interface definition is shown in this code: [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/api/src/main/java/org/ehcache/PersistentUserManagedCache.java[lines=17..-1] +include::{sourcedir39}/ehcache-api/src/main/java/org/ehcache/PersistentUserManagedCache.java[lines=17..-1] ---- [[code-examples]] @@ -75,7 +75,7 @@ Here is a simple example showing a basic lifecycle of a user managed cache: [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/impl/src/test/java/org/ehcache/docs/UserManagedCaches.java[tag=userManagedCacheExample] +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/UserManagedCaches.java[tag=userManagedCacheExample] ---- <1> Create a `UserManagedCache` instance. You can either pass `true` to have the builder `init()` it for you, @@ -102,7 +102,7 @@ If you want to use disk persistent cache, you will need to create and lifecycle [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/impl/src/test/java/org/ehcache/docs/UserManagedCaches.java[tag=persistentUserManagedCache] +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/UserManagedCaches.java[tag=persistentUserManagedCache] ---- <1> Create the persistence service to be used by the cache for storing data on disk. @@ -125,7 +125,7 @@ For more information on cache event listeners, see the section < Provide the `ExecutorService` for ordered and unordered event delivery. diff --git a/docs/src/docs/asciidoc/user/writers.adoc b/docs/src/docs/asciidoc/user/writers.adoc index 4284dc45cb..e180f52f8e 100644 --- a/docs/src/docs/asciidoc/user/writers.adoc +++ b/docs/src/docs/asciidoc/user/writers.adoc @@ -1,19 +1,17 @@ --- --- = Cache Loaders and Writers -ifndef::sourcedir36[] -include::common.adoc[] -endif::sourcedir36[] +ifndef::sourcedir39[] +include::{includedir}/common.adoc[] +endif::sourcedir39[] ifdef::notBuildingForSite[] -include::menu.adoc[] +include::{includedir}/menu.adoc[] endif::notBuildingForSite[] [[introduction]] == Introduction to Cache Loaders and Writers -NOTE: Ehcache clustering is not yet compatible with _cache-through_. - This section documents the specifics behind the cache-through implementation in Ehcache. Refer to the section <> if you are not familiar with terms like _cache-through_, _read-through_, _write-through_ or _system of record_. @@ -76,7 +74,7 @@ After this time has elapsed, the batch is processed even if incomplete. [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/impl/src/test/java/org/ehcache/docs/GettingStarted.java[tag=writeThroughCache] +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/GettingStarted.java[tag=writeThroughCache] ---- <1> We register a sample `CacheLoaderWriter` that knows about the mapping ("41L" maps to "zero"). @@ -88,7 +86,7 @@ The returned mapping will populate the cache and be returned to the caller. [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/impl/src/test/java/org/ehcache/docs/GettingStarted.java[tag=writeBehindCache] +include::{sourcedir39}/ehcache-impl/src/test/java/org/ehcache/docs/GettingStarted.java[tag=writeBehindCache] ---- <1> For write-behind you need a configured `CacheLoaderWriter`. @@ -98,3 +96,5 @@ include::{sourcedir36}/impl/src/test/java/org/ehcache/docs/GettingStarted.java[t <5> Define the concurrency level of write-behind queue(s). This indicates how many writer threads work in parallel to update the underlying system of record asynchronously. <6> Enable the write coalescing behavior, which ensures that only one update per key per batch reaches the underlying system of record. + +NOTE: `BatchedWriteBehindConfigurationBuilder` configurations are not honoured by clustered caches. diff --git a/docs/src/docs/asciidoc/user/xa.adoc b/docs/src/docs/asciidoc/user/xa.adoc index 16e3a74cc4..e03fb2b3d9 100644 --- a/docs/src/docs/asciidoc/user/xa.adoc +++ b/docs/src/docs/asciidoc/user/xa.adoc @@ -1,12 +1,12 @@ --- --- = XA transactional caches -ifndef::sourcedir36[] -include::common.adoc[] -endif::sourcedir36[] +ifndef::sourcedir39[] +include::{includedir}/common.adoc[] +endif::sourcedir39[] ifdef::notBuildingForSite[] -include::menu.adoc[] +include::{includedir}/menu.adoc[] == Introduction @@ -65,7 +65,7 @@ Here is an example: [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/transactions/src/test/java/org/ehcache/docs/transactions/xa/XAGettingStarted.java[tag=testSimpleXACache] +include::{sourcedir39}/ehcache-transactions/src/test/java/org/ehcache/docs/transactions/xa/XAGettingStarted.java[tag=testSimpleXACache] ---- <1> First start the Bitronix transaction manager. By default, Ehcache will auto-detect it but will throw an exception during the cache manager initialization if BTM isn't started. @@ -96,7 +96,7 @@ Nothing special needs to be configured for this to happen, just ensure that the [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/transactions/src/test/java/org/ehcache/docs/transactions/xa/XAGettingStarted.java[tag=testXACacheWithWriteThrough] +include::{sourcedir39}/ehcache-transactions/src/test/java/org/ehcache/docs/transactions/xa/XAGettingStarted.java[tag=testXACacheWithWriteThrough] ---- <1> First start the Bitronix transaction manager. @@ -122,7 +122,7 @@ Any attempt to access one outside of such context will result in `XACacheExcepti [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/transactions/src/test/java/org/ehcache/docs/transactions/xa/XAGettingStarted.java[tag=testNonTransactionalAccess] +include::{sourcedir39}/ehcache-transactions/src/test/java/org/ehcache/docs/transactions/xa/XAGettingStarted.java[tag=testNonTransactionalAccess] ---- <1> First start the Bitronix transaction manager. @@ -151,7 +151,7 @@ Here is an example: [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/transactions/src/test/java/org/ehcache/docs/transactions/xa/XAGettingStarted.java[tag=testXACacheWithThreeTiers] +include::{sourcedir39}/ehcache-transactions/src/test/java/org/ehcache/docs/transactions/xa/XAGettingStarted.java[tag=testXACacheWithThreeTiers] ---- <1> First start the Bitronix transaction manager. @@ -174,7 +174,7 @@ You can create a XML file to configure a `CacheManager`, lookup a specific trans [source%nowrap,xml,indent=0] ---- -include::{sourcedir36}/transactions/src/test/resources/docs/configs/xa-getting-started.xml[tags=gettingStarted] +include::{sourcedir39}/ehcache-transactions/src/test/resources/docs/configs/xa-getting-started.xml[tags=gettingStarted] ---- <1> Declare a `TransactionManagerLookup` that will lookup your transaction manager. @@ -185,7 +185,7 @@ In order to parse an XML configuration, you can use the XmlConfiguration type: [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/transactions/src/test/java/org/ehcache/docs/transactions/xa/XAGettingStarted.java[tag=testXACacheWithXMLConfig] +include::{sourcedir39}/ehcache-transactions/src/test/java/org/ehcache/docs/transactions/xa/XAGettingStarted.java[tag=testXACacheWithXMLConfig] ---- <1> The Bitronix transaction manager must be started before the cache manager is initialized. @@ -198,7 +198,7 @@ And here is what the BitronixTransactionManagerLookup implementation looks like: [source%nowrap,java,indent=0] ---- -include::{sourcedir36}/transactions/src/main/java/org/ehcache/transactions/xa/txmgr/btm/BitronixTransactionManagerLookup.java[tag=BitronixLookup] +include::{sourcedir39}/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/txmgr/btm/BitronixTransactionManagerLookup.java[tag=BitronixLookup] ---- <1> The `TransactionManagerLookup` interface must be implemented and the offer a `no-arg` constructor. diff --git a/docs/src/docs/asciidoc/user/xml.adoc b/docs/src/docs/asciidoc/user/xml.adoc index a967952f0f..622e4d15b9 100644 --- a/docs/src/docs/asciidoc/user/xml.adoc +++ b/docs/src/docs/asciidoc/user/xml.adoc @@ -1,12 +1,12 @@ --- --- = XML Configuration -ifndef::sourcedir36[] -include::common.adoc[] -endif::sourcedir36[] +ifndef::sourcedir39[] +include::{includedir}/common.adoc[] +endif::sourcedir39[] ifdef::notBuildingForSite[] -include::menu.adoc[] +include::{includedir}/menu.adoc[] endif::notBuildingForSite[] == Introduction @@ -84,12 +84,8 @@ WARNING: Processing of cache template configurations can be triggered lazily by == Property replacement in XML configuration files -Java system properties can be referenced inside XML configuration files. -The property value will replace the property reference during the configuration parsing. - -This is done by using the `${prop.name}` syntax. -It is supported in all attributes and elements values that accept the `${}` characters as legal characters. -This currently rules out all numbers, mostly used in sizing things, and identifiers, such as cache and template names. +Certain elements inside XML configuration files can use `${property-name}` syntax. The value of the given +system-property will replace the property reference during the configuration parsing. WARNING: If the system property does not exist, this will make the configuration parsing fail. @@ -101,6 +97,15 @@ A classical use case for this feature is for disk files location inside the `dir ---- <1> Here `user.home` will be replaced by the value of the system property, something like `/home/user` +Attributes within the core configuration that can use system properties are: + + * Local persistence directory (supports substitution within a string). + * Thread pool minimum and maximum size attributes. + * Write-behind queue size, concurrency, batch size and maximum batch delay. + * Cache TTI and TTL + * Core resource sizes (heap, offheap and disk) + * Disk store writer concurrency and segment count + == XML programmatic parsing NOTE: If you are obtaining your `CacheManager` through the JSR-107 API, what follows is done automatically @@ -108,7 +113,7 @@ NOTE: If you are obtaining your `CacheManager` through the JSR-107 API, what fol [source,java,indent=0] ---- -include::{sourcedir36}/xml/src/test/java/org/ehcache/docs/GettingStarted.java[tag=xmlConfig] +include::{sourcedir39}/ehcache-xml/src/test/java/org/ehcache/docs/GettingStarted.java[tag=xmlConfig] ---- <1> Obtain a `URL` to your XML file's location <2> Instantiate an `XmlConfiguration` passing the XML file's URL to it @@ -121,14 +126,14 @@ to use a `` element from an XML file, e.g. the `/my-config.xml` [source,xml,indent=0] ---- -include::{sourcedir36}/xml/src/test/resources/configs/docs/template-sample.xml[tag=templateSample] +include::{sourcedir39}/ehcache-xml/src/test/resources/configs/docs/template-sample.xml[tag=templateSample] ---- Creating a `CacheConfigurationBuilder` of that `example` `` element, would be done as follows: [source,java,indent=0] ---- -include::{sourcedir36}/xml/src/test/java/org/ehcache/docs/GettingStarted.java[tag=xmlTemplate] +include::{sourcedir39}/ehcache-xml/src/test/java/org/ehcache/docs/GettingStarted.java[tag=xmlTemplate] ---- <1> Creates a builder, inheriting the capacity constraint of 200 entries <2> The inherent properties can be overridden by simply providing a different value prior to building the `CacheConfiguration` @@ -141,14 +146,14 @@ and the string representation of that object will give you the XML equivalent of [source,java,indent=0] ---- -include::{sourcedir36}/xml/src/test/java/org/ehcache/docs/GettingStarted.java[tag=xmlTemplate] +include::{sourcedir39}/ehcache-xml/src/test/java/org/ehcache/docs/GettingStarted.java[tag=xmlTemplate] ---- <1> Creates a builder, inheriting the capacity constraint of 200 entries <2> The inherent properties can be overridden by simply providing a different value prior to building the `CacheConfiguration` [source,java,indent=0] ---- -include::{sourcedir36}/xml/src/test/java/org/ehcache/docs/GettingStarted.java[tag=xmlTranslation] +include::{sourcedir39}/ehcache-xml/src/test/java/org/ehcache/docs/GettingStarted.java[tag=xmlTranslation] ---- <1> Instantiate an `XmlConfiguration` passing the cache manager `Configuration` <2> Retrieve the XML representation using the `toString` method. @@ -176,7 +181,7 @@ The simplest use of the multi-configuration features is to embed multiple cache file: [source,xml,indent=0] ---- -include::{sourcedir36}/xml/src/test/resources/configs/docs/multi/multiple-managers.xml[] +include::{sourcedir39}/ehcache-xml/src/test/resources/configs/docs/multi/multiple-managers.xml[] ---- <1> A top-level `` container with namespace declarations for the `multi` and core schemas <2> Each Ehcache configuration is embedded inside a `configuration` tag with a required (unique) `identity` attribute @@ -184,7 +189,7 @@ include::{sourcedir36}/xml/src/test/resources/configs/docs/multi/multiple-manage These embedded configurations can then be retrieved via an `XmlMultiConfiguration` instance built from the XML document. [source,java,indent=0] ---- -include::{sourcedir36}/xml/src/test/java/org/ehcache/docs/MultiGettingStarted.java[tag=multipleManagers] +include::{sourcedir39}/ehcache-xml/src/test/java/org/ehcache/docs/MultiGettingStarted.java[tag=multipleManagers] ---- <1> The `XmlMultiConfiguration` is assembled from the XML resource. <2> Once assembled the configuration is built. @@ -196,13 +201,13 @@ Multiple variant configurations for a given manager can be provided by including with a required `type` attribute: [source,xml,indent=0] ---- -include::{sourcedir36}/xml/src/test/resources/configs/docs/multi/multiple-variants.xml[tag=variants] +include::{sourcedir39}/ehcache-xml/src/test/resources/configs/docs/multi/multiple-variants.xml[tag=variants] ---- A specific cache configuration can then be retrieved by choosing both a variant and an identity explicitly on retrieval. [source,java,indent=0] ---- -include::{sourcedir36}/xml/src/test/java/org/ehcache/docs/MultiGettingStarted.java[tag=multipleVariants] +include::{sourcedir39}/ehcache-xml/src/test/java/org/ehcache/docs/MultiGettingStarted.java[tag=multipleVariants] ---- The samples above are just samples, variant types can be used to represent any kind of variation: development vs production, clustered vs unclustered, red vs blue, etc. @@ -218,7 +223,7 @@ Multiple cache managers can be retrieved from an `XmlMultiConfiguration` by iter `identities()`: [source,java,indent=0] ---- -include::{sourcedir36}/xml/src/test/java/org/ehcache/docs/MultiGettingStarted.java[tag=multipleRetrieval] +include::{sourcedir39}/ehcache-xml/src/test/java/org/ehcache/docs/MultiGettingStarted.java[tag=multipleRetrieval] ---- <1> From a stream over the set of identities in a mult-configuration. <2> Map each identity to it's unique (non-varianted) configuration. @@ -232,7 +237,7 @@ of parsing XML multi-configuration documents are all just simple invocations of Configurations can be built from scratch as show below: [source,java,indent=0] ---- -include::{sourcedir36}/xml/src/test/java/org/ehcache/docs/MultiGettingStarted.java[tag=building] +include::{sourcedir39}/ehcache-xml/src/test/java/org/ehcache/docs/MultiGettingStarted.java[tag=building] ---- <1> Starting with an initially empty set of configurations. <2> Add a configuration without variants. @@ -242,7 +247,7 @@ include::{sourcedir36}/xml/src/test/java/org/ehcache/docs/MultiGettingStarted.ja They can also be built from existing configurations: [source,java,indent=0] ---- -include::{sourcedir36}/xml/src/test/java/org/ehcache/docs/MultiGettingStarted.java[tag=modifying] +include::{sourcedir39}/ehcache-xml/src/test/java/org/ehcache/docs/MultiGettingStarted.java[tag=modifying] ---- <1> Starting with an existing `XmlMultiConfiguration`. <2> Remove the configuration with identity `"foo"`. @@ -250,7 +255,7 @@ include::{sourcedir36}/xml/src/test/java/org/ehcache/docs/MultiGettingStarted.ja Once built a multi-configuration can be retrieved in XML form: [source,java,indent=0] ---- -include::{sourcedir36}/xml/src/test/java/org/ehcache/docs/MultiGettingStarted.java[tag=rendering] +include::{sourcedir39}/ehcache-xml/src/test/java/org/ehcache/docs/MultiGettingStarted.java[tag=rendering] ---- <1> Retrieving the XML as a rendered string. <2> Retrieving the XML as a DOM (`org.w3c.Document`). diff --git a/docs/src/docs/asciidoc/user/xsds.adoc b/docs/src/docs/asciidoc/user/xsds.adoc index 1c2e425821..879fc7c3e7 100644 --- a/docs/src/docs/asciidoc/user/xsds.adoc +++ b/docs/src/docs/asciidoc/user/xsds.adoc @@ -1,12 +1,12 @@ --- --- = Ehcache XSDs -ifndef::sourcedir36[] -include::common.adoc[] -endif::sourcedir36[] +ifndef::sourcedir39[] +include::{includedir}/common.adoc[] +endif::sourcedir39[] ifdef::notBuildingForSite[] -include::menu.adoc[] +include::{includedir}/menu.adoc[] endif::notBuildingForSite[] == XSD namespaces and locations @@ -19,6 +19,10 @@ endif::notBuildingForSite[] ** Location for 3.4: `http://www.ehcache.org/schema/ehcache-core-3.4.xsd` ** Location for 3.5: `http://www.ehcache.org/schema/ehcache-core-3.5.xsd` ** Location for 3.6: `http://www.ehcache.org/schema/ehcache-core-3.6.xsd` +** Location for 3.7: `http://www.ehcache.org/schema/ehcache-core-3.7.xsd` +** Location for 3.8: `http://www.ehcache.org/schema/ehcache-core-3.8.xsd` +** Location for 3.9: `http://www.ehcache.org/schema/ehcache-core-3.9.xsd` +// needle_for_core_xsd * JSR-107 namespace: `http://www.ehcache.org/v3/jsr107` ** Location for 3.0: `http://www.ehcache.org/schema/ehcache-107-ext-3.0.xsd` ** Location for 3.1: `http://www.ehcache.org/schema/ehcache-107-ext-3.1.xsd` @@ -27,6 +31,10 @@ endif::notBuildingForSite[] ** Location for 3.4: `http://www.ehcache.org/schema/ehcache-107-ext-3.4.xsd` ** Location for 3.5: `http://www.ehcache.org/schema/ehcache-107-ext-3.5.xsd` ** Location for 3.6: `http://www.ehcache.org/schema/ehcache-107-ext-3.6.xsd` +** Location for 3.7: `http://www.ehcache.org/schema/ehcache-107-ext-3.7.xsd` +** Location for 3.8: `http://www.ehcache.org/schema/ehcache-107-ext-3.8.xsd` +** Location for 3.9: `http://www.ehcache.org/schema/ehcache-107-ext-3.9.xsd` +// needle_for_107_xsd * Transactions namespace: `http://www.ehcache.org/v3/tx` ** Location for 3.0: `http://www.ehcache.org/schema/ehcache-tx-ext-3.0.xsd` ** Location for 3.1: `http://www.ehcache.org/schema/ehcache-tx-ext-3.1.xsd` @@ -35,6 +43,10 @@ endif::notBuildingForSite[] ** Location for 3.4: `http://www.ehcache.org/schema/ehcache-tx-ext-3.4.xsd` ** Location for 3.5: `http://www.ehcache.org/schema/ehcache-tx-ext-3.5.xsd` ** Location for 3.6: `http://www.ehcache.org/schema/ehcache-tx-ext-3.6.xsd` +** Location for 3.7: `http://www.ehcache.org/schema/ehcache-tx-ext-3.7.xsd` +** Location for 3.8: `http://www.ehcache.org/schema/ehcache-tx-ext-3.8.xsd` +** Location for 3.9: `http://www.ehcache.org/schema/ehcache-tx-ext-3.9.xsd` +// needle_for_transactions_xsd * Clustering namespace: `http://www.ehcache.org/v3/clustered` ** Location for 3.1: `http://www.ehcache.org/schema/ehcache-clustered-ext-3.1.xsd` ** Location for 3.2: `http://www.ehcache.org/schema/ehcache-clustered-ext-3.2.xsd` @@ -42,12 +54,16 @@ endif::notBuildingForSite[] ** Location for 3.4: `http://www.ehcache.org/schema/ehcache-clustered-ext-3.4.xsd` ** Location for 3.5: `http://www.ehcache.org/schema/ehcache-clustered-ext-3.5.xsd` ** Location for 3.6: `http://www.ehcache.org/schema/ehcache-clustered-ext-3.6.xsd` +** Location for 3.7: `http://www.ehcache.org/schema/ehcache-clustered-ext-3.7.xsd` +** Location for 3.8: `http://www.ehcache.org/schema/ehcache-clustered-ext-3.8.xsd` +** Location for 3.9: `http://www.ehcache.org/schema/ehcache-clustered-ext-3.9.xsd` +// needle_for_clustered_xsd === Usage example [source,xml,indent=0] ---- -include::{sourcedir36}/107/src/test/resources/org/ehcache/docs/public-xsds-location.xml[tag=xsdLocations] +include::{sourcedir39}/ehcache-107/src/test/resources/org/ehcache/docs/public-xsds-location.xml[tag=xsdLocations] ---- [[core]] @@ -55,7 +71,7 @@ include::{sourcedir36}/107/src/test/resources/org/ehcache/docs/public-xsds-locat [source,xsd,indent=0] ---- -include::{sourcedir36}/xml/src/main/resources/ehcache-core.xsd[lines=18..-1] +include::{sourcedir39}/ehcache-xml/src/main/schema/ehcache-core.xsd[lines=18..-1] ---- [[jsr-107-extension]] @@ -63,12 +79,12 @@ include::{sourcedir36}/xml/src/main/resources/ehcache-core.xsd[lines=18..-1] [source,xsd,indent=0] ---- -include::{sourcedir36}/107/src/main/resources/ehcache-107ext.xsd[lines=18..-1] +include::{sourcedir39}/ehcache-107/src/main/resources/ehcache-107-ext.xsd[lines=18..-1] ---- == XA transactions extension [source,xsd,indent=0] ---- -include::{sourcedir36}/transactions/src/main/resources/ehcache-tx-ext.xsd[lines=18..-1] +include::{sourcedir39}/ehcache-transactions/src/main/resources/ehcache-tx-ext.xsd[lines=18..-1] ---- diff --git a/107/README.adoc b/ehcache-107/README.adoc similarity index 98% rename from 107/README.adoc rename to ehcache-107/README.adoc index c9d4e422a3..65e8d93be6 100644 --- a/107/README.adoc +++ b/ehcache-107/README.adoc @@ -54,7 +54,6 @@ constraint. All is needed is adding a jsr107 service in your XML configuration f [source,xml] ---- diff --git a/107/build.gradle b/ehcache-107/build.gradle similarity index 67% rename from 107/build.gradle rename to ehcache-107/build.gradle index 8a205f3f09..035a2dd193 100644 --- a/107/build.gradle +++ b/ehcache-107/build.gradle @@ -14,10 +14,29 @@ * limitations under the License. */ -apply plugin: EhDeploy +plugins { + id 'org.ehcache.build.internal-module' +} + +publishing.publications.withType(MavenPublication) { + pom { + name = 'Ehcache 3 JSR-107 module' + description = 'The JSR-107 compatibility module of Ehcache 3' + } +} configurations { tckTestClasses + + all { + resolutionStrategy { + dependencySubstitution { + substitute(module('junit:junit:4.11')) + .because('CVE-2020-15250') + .with(module('junit:junit:4.13.1')) + } + } + } } sourceSets { @@ -30,31 +49,33 @@ sourceSets { } dependencies { - api project(':api') - providedApi "javax.cache:cache-api:$parent.jcacheVersion" + api project(':ehcache-api') + api "javax.cache:cache-api:$parent.jcacheVersion" - implementation project(':impl') - implementation project(':xml') - implementation "org.terracotta:statistics:$parent.statisticVersion" + implementation project(':ehcache-impl') + implementation project(':ehcache-xml') + implementation "org.terracotta:statistics:$statisticVersion" - tckTestRuntime "javax.cache:cache-tests:$jcacheTckVersion" + compileOnly 'org.osgi:org.osgi.service.component.annotations:1.3.0' + + tckTestRuntimeOnly "javax.cache:cache-tests:$jcacheTckVersion" tckTestClasses("javax.cache:cache-tests:$jcacheTckVersion:tests") { transitive = false } - testCompile project(path: ':xml', configuration: 'testArchives') } javadoc { exclude '**/tck/**' } -test { - if (testJava.javaVersion.isJava9Compatible()) { - jvmArgs += ['--add-modules', 'java.xml.bind'] - } +jar { + bnd( + 'Export-Package': '!org.ehcache.jsr107.tck, !org.ehcache.jsr107.internal.*, org.ehcache.jsr107.*', + 'Import-Package': 'javax.cache.*;resolution:=optional, *', + ) } -task unpackTckTests(type: Copy) { +task unpackTckTests(type: Sync) { from { configurations.tckTestClasses.collect {zipTree(it)} } diff --git a/107/config/checkstyle-suppressions.xml b/ehcache-107/config/checkstyle-suppressions.xml similarity index 100% rename from 107/config/checkstyle-suppressions.xml rename to ehcache-107/config/checkstyle-suppressions.xml diff --git a/107/src/main/java/org/ehcache/jsr107/CacheResources.java b/ehcache-107/src/main/java/org/ehcache/jsr107/CacheResources.java similarity index 100% rename from 107/src/main/java/org/ehcache/jsr107/CacheResources.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/CacheResources.java diff --git a/107/src/main/java/org/ehcache/jsr107/CloseUtil.java b/ehcache-107/src/main/java/org/ehcache/jsr107/CloseUtil.java similarity index 100% rename from 107/src/main/java/org/ehcache/jsr107/CloseUtil.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/CloseUtil.java diff --git a/107/src/main/java/org/ehcache/jsr107/ConfigurationMerger.java b/ehcache-107/src/main/java/org/ehcache/jsr107/ConfigurationMerger.java similarity index 91% rename from 107/src/main/java/org/ehcache/jsr107/ConfigurationMerger.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/ConfigurationMerger.java index 5c6a8f4a75..2987736314 100644 --- a/107/src/main/java/org/ehcache/jsr107/ConfigurationMerger.java +++ b/ehcache-107/src/main/java/org/ehcache/jsr107/ConfigurationMerger.java @@ -23,17 +23,16 @@ import org.ehcache.impl.config.copy.DefaultCopierConfiguration; import org.ehcache.impl.config.copy.DefaultCopyProviderConfiguration; import org.ehcache.impl.config.loaderwriter.DefaultCacheLoaderWriterConfiguration; -import org.ehcache.impl.internal.classes.ClassInstanceConfiguration; import org.ehcache.impl.copy.SerializingCopier; import org.ehcache.jsr107.config.ConfigurationElementState; import org.ehcache.jsr107.config.Jsr107CacheConfiguration; import org.ehcache.jsr107.internal.Jsr107CacheLoaderWriter; -import org.ehcache.spi.copy.Copier; import org.ehcache.spi.loaderwriter.CacheLoaderWriterConfiguration; import org.ehcache.xml.XmlConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Collection; import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -108,7 +107,7 @@ ConfigHolder mergeConfigurations(String cacheName, Configuration ehcacheLoaderWriterConfiguration = builder.getService(DefaultCacheLoaderWriterConfiguration.class); if (ehcacheLoaderWriterConfiguration == null) { useEhcacheLoaderWriter = false; // No template loader/writer - let's activate the JSR-107 one if any @@ -146,25 +145,26 @@ ConfigHolder mergeConfigurations(String cacheName, Configuration CacheConfigurationBuilder handleStoreByValue(Eh107CompleteConfiguration jsr107Configuration, CacheConfigurationBuilder builder, String cacheName) { - DefaultCopierConfiguration copierConfig = builder.getExistingServiceConfiguration(DefaultCopierConfiguration.class); - if(copierConfig == null) { + @SuppressWarnings("unchecked") + Collection> copierConfigs = builder.getServices((Class>) (Class) DefaultCopierConfiguration.class); + if(copierConfigs.isEmpty()) { if(jsr107Configuration.isStoreByValue()) { if (xmlConfiguration != null) { DefaultCopyProviderConfiguration defaultCopyProviderConfiguration = findSingletonAmongst(DefaultCopyProviderConfiguration.class, xmlConfiguration.getServiceCreationConfigurations()); if (defaultCopyProviderConfiguration != null) { - Map, ClassInstanceConfiguration>> defaults = defaultCopyProviderConfiguration.getDefaults(); + Map, DefaultCopierConfiguration> defaults = defaultCopyProviderConfiguration.getDefaults(); handleCopierDefaultsforImmutableTypes(defaults); boolean matchingDefault = false; if (defaults.containsKey(jsr107Configuration.getKeyType())) { matchingDefault = true; } else { - builder = builder.add(new DefaultCopierConfiguration<>(SerializingCopier.asCopierClass(), DefaultCopierConfiguration.Type.KEY)); + builder = builder.withService(new DefaultCopierConfiguration<>(SerializingCopier.asCopierClass(), DefaultCopierConfiguration.Type.KEY)); } if (defaults.containsKey(jsr107Configuration.getValueType())) { matchingDefault = true; } else { - builder = builder.add(new DefaultCopierConfiguration<>(SerializingCopier.asCopierClass(), DefaultCopierConfiguration.Type.VALUE)); + builder = builder.withService(new DefaultCopierConfiguration<>(SerializingCopier.asCopierClass(), DefaultCopierConfiguration.Type.VALUE)); } if (matchingDefault) { LOG.info("CacheManager level copier configuration overwriting JSR-107 by-value semantics for cache {}", cacheName); @@ -191,20 +191,20 @@ private static CacheConfigurationBuilder addDefaultCopiers(CacheCon immutableTypes.add(Character.class); immutableTypes.add(Integer.class); if (immutableTypes.contains(keyType)) { - builder = builder.add(new DefaultCopierConfiguration((Class)Eh107IdentityCopier.class, DefaultCopierConfiguration.Type.KEY)); + builder = builder.withService(new DefaultCopierConfiguration((Class)Eh107IdentityCopier.class, DefaultCopierConfiguration.Type.KEY)); } else { - builder = builder.add(new DefaultCopierConfiguration<>(SerializingCopier.asCopierClass(), DefaultCopierConfiguration.Type.KEY)); + builder = builder.withService(new DefaultCopierConfiguration<>(SerializingCopier.asCopierClass(), DefaultCopierConfiguration.Type.KEY)); } if (immutableTypes.contains(valueType)) { - builder = builder.add(new DefaultCopierConfiguration((Class)Eh107IdentityCopier.class, DefaultCopierConfiguration.Type.VALUE)); + builder = builder.withService(new DefaultCopierConfiguration((Class)Eh107IdentityCopier.class, DefaultCopierConfiguration.Type.VALUE)); } else { - builder = builder.add(new DefaultCopierConfiguration<>(SerializingCopier.asCopierClass(), DefaultCopierConfiguration.Type.VALUE)); + builder = builder.withService(new DefaultCopierConfiguration<>(SerializingCopier.asCopierClass(), DefaultCopierConfiguration.Type.VALUE)); } return builder; } - private static void handleCopierDefaultsforImmutableTypes(Map, ClassInstanceConfiguration>> defaults) { + private static void handleCopierDefaultsforImmutableTypes(Map, DefaultCopierConfiguration> defaults) { addIdentityCopierIfNoneRegistered(defaults, Long.class); addIdentityCopierIfNoneRegistered(defaults, Integer.class); addIdentityCopierIfNoneRegistered(defaults, String.class); @@ -214,7 +214,7 @@ private static void handleCopierDefaultsforImmutableTypes(Map, ClassIns } @SuppressWarnings({"rawtypes", "unchecked"}) - private static void addIdentityCopierIfNoneRegistered(Map, ClassInstanceConfiguration>> defaults, Class clazz) { + private static void addIdentityCopierIfNoneRegistered(Map, DefaultCopierConfiguration> defaults, Class clazz) { if (!defaults.containsKey(clazz)) { defaults.put(clazz, new DefaultCopierConfiguration(Eh107IdentityCopier.class, DefaultCopierConfiguration.Type.VALUE)); } diff --git a/107/src/main/java/org/ehcache/jsr107/DefaultConfigurationResolver.java b/ehcache-107/src/main/java/org/ehcache/jsr107/DefaultConfigurationResolver.java similarity index 100% rename from 107/src/main/java/org/ehcache/jsr107/DefaultConfigurationResolver.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/DefaultConfigurationResolver.java diff --git a/107/src/main/java/org/ehcache/jsr107/Eh107Cache.java b/ehcache-107/src/main/java/org/ehcache/jsr107/Eh107Cache.java similarity index 98% rename from 107/src/main/java/org/ehcache/jsr107/Eh107Cache.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/Eh107Cache.java index 4e64487b9c..9595a55cff 100644 --- a/107/src/main/java/org/ehcache/jsr107/Eh107Cache.java +++ b/ehcache-107/src/main/java/org/ehcache/jsr107/Eh107Cache.java @@ -432,18 +432,7 @@ CacheException closeInternalAfter(CacheException failure) { } void closeInternal() { - closeInternal(false); - } - - private void closeInternal(boolean destroy) { if (hypotheticallyClosed.compareAndSet(false, true)) { - if (destroy) { - try { - clear(false); - } catch (Throwable t) { - throw cacheResources.closeResourcesAfter(new CacheException(t)); - } - } cacheResources.closeResources(); } } @@ -455,10 +444,6 @@ private boolean syncedIsClose() { return hypotheticallyClosed.get(); } - void destroy() { - closeInternal(true); - } - @Override public T unwrap(Class clazz) { return Unwrap.unwrap(clazz, this, ehCache); diff --git a/107/src/main/java/org/ehcache/jsr107/Eh107CacheEntryEvent.java b/ehcache-107/src/main/java/org/ehcache/jsr107/Eh107CacheEntryEvent.java similarity index 100% rename from 107/src/main/java/org/ehcache/jsr107/Eh107CacheEntryEvent.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/Eh107CacheEntryEvent.java diff --git a/107/src/main/java/org/ehcache/jsr107/Eh107CacheLoaderWriter.java b/ehcache-107/src/main/java/org/ehcache/jsr107/Eh107CacheLoaderWriter.java similarity index 100% rename from 107/src/main/java/org/ehcache/jsr107/Eh107CacheLoaderWriter.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/Eh107CacheLoaderWriter.java diff --git a/107/src/main/java/org/ehcache/jsr107/Eh107CacheLoaderWriterProvider.java b/ehcache-107/src/main/java/org/ehcache/jsr107/Eh107CacheLoaderWriterProvider.java similarity index 100% rename from 107/src/main/java/org/ehcache/jsr107/Eh107CacheLoaderWriterProvider.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/Eh107CacheLoaderWriterProvider.java diff --git a/107/src/main/java/org/ehcache/jsr107/Eh107CacheMXBean.java b/ehcache-107/src/main/java/org/ehcache/jsr107/Eh107CacheMXBean.java similarity index 100% rename from 107/src/main/java/org/ehcache/jsr107/Eh107CacheMXBean.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/Eh107CacheMXBean.java diff --git a/107/src/main/java/org/ehcache/jsr107/Eh107CacheManager.java b/ehcache-107/src/main/java/org/ehcache/jsr107/Eh107CacheManager.java similarity index 95% rename from 107/src/main/java/org/ehcache/jsr107/Eh107CacheManager.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/Eh107CacheManager.java index caa07cc3bf..92a15e4397 100644 --- a/107/src/main/java/org/ehcache/jsr107/Eh107CacheManager.java +++ b/ehcache-107/src/main/java/org/ehcache/jsr107/Eh107CacheManager.java @@ -15,6 +15,8 @@ */ package org.ehcache.jsr107; +import org.ehcache.CachePersistenceException; +import org.ehcache.PersistentCacheManager; import org.ehcache.Status; import org.ehcache.config.CacheConfiguration; import org.ehcache.core.InternalCache; @@ -113,7 +115,7 @@ private Eh107Cache wrapEhcacheCache(String alias, InternalCache cacheLoaderWriter = cache.getCacheLoaderWriter(); boolean storeByValueOnHeap = false; - for (ServiceConfiguration serviceConfiguration : cache.getRuntimeConfiguration().getServiceConfigurations()) { + for (ServiceConfiguration serviceConfiguration : cache.getRuntimeConfiguration().getServiceConfigurations()) { if (serviceConfiguration instanceof DefaultCopierConfiguration) { DefaultCopierConfiguration copierConfig = (DefaultCopierConfiguration) serviceConfiguration; if(!copierConfig.getClazz().isAssignableFrom(IdentityCopier.class)) @@ -324,8 +326,17 @@ public void destroyCache(String cacheName) { chain( () -> enableManagement(cache, false), () -> enableStatistics(cache, false), - () -> cache.destroy(), - () -> ehCacheManager.removeCache(cache.getName()) + () -> cache.closeInternal(), + () -> ehCacheManager.removeCache(cache.getName()), + () -> { + if (ehCacheManager instanceof PersistentCacheManager) { + try { + ((PersistentCacheManager) ehCacheManager).destroyCache(cache.getName()); + } catch (CachePersistenceException t) { + throw new IOException(t); + } + } + } ); } catch (Throwable t) { throw new CacheException(t); diff --git a/107/src/main/java/org/ehcache/jsr107/Eh107CacheStatisticsMXBean.java b/ehcache-107/src/main/java/org/ehcache/jsr107/Eh107CacheStatisticsMXBean.java similarity index 96% rename from 107/src/main/java/org/ehcache/jsr107/Eh107CacheStatisticsMXBean.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/Eh107CacheStatisticsMXBean.java index 0c94c9b47f..a2d94cd83b 100644 --- a/107/src/main/java/org/ehcache/jsr107/Eh107CacheStatisticsMXBean.java +++ b/ehcache-107/src/main/java/org/ehcache/jsr107/Eh107CacheStatisticsMXBean.java @@ -18,10 +18,9 @@ import org.ehcache.core.spi.service.StatisticsService; import org.ehcache.core.statistics.CacheOperationOutcomes; import org.ehcache.core.statistics.CacheStatistics; -import org.terracotta.statistics.derived.latency.Jsr107LatencyMonitor; +import org.ehcache.jsr107.internal.Jsr107LatencyMonitor; import java.net.URI; -import java.util.EnumSet; /** * @author Ludovic Orban @@ -45,7 +44,7 @@ class Eh107CacheStatisticsMXBean extends Eh107MXBean implements javax.cache.mana } private > Jsr107LatencyMonitor registerDerivedStatistics(Class outcome, String name) { - Jsr107LatencyMonitor monitor = new Jsr107LatencyMonitor<>(EnumSet.allOf(outcome), 1.0); + Jsr107LatencyMonitor monitor = new Jsr107LatencyMonitor<>(outcome); CacheStatistics cacheStatistics = this.cacheStatistics; cacheStatistics.registerDerivedStatistic(outcome, name, monitor); return monitor; diff --git a/107/src/main/java/org/ehcache/jsr107/Eh107CompleteConfiguration.java b/ehcache-107/src/main/java/org/ehcache/jsr107/Eh107CompleteConfiguration.java similarity index 96% rename from 107/src/main/java/org/ehcache/jsr107/Eh107CompleteConfiguration.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/Eh107CompleteConfiguration.java index 041da404fe..0dba47507c 100644 --- a/107/src/main/java/org/ehcache/jsr107/Eh107CompleteConfiguration.java +++ b/ehcache-107/src/main/java/org/ehcache/jsr107/Eh107CompleteConfiguration.java @@ -20,7 +20,6 @@ import org.ehcache.impl.copy.IdentityCopier; import org.ehcache.spi.service.ServiceConfiguration; -import java.io.ObjectStreamException; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -110,8 +109,8 @@ public Eh107CompleteConfiguration(Configuration config, final CacheConfigu private static boolean isStoreByValue(Configuration config, CacheConfiguration ehcacheConfig) { if(ehcacheConfig != null) { - Collection> serviceConfigurations = ehcacheConfig.getServiceConfigurations(); - for (ServiceConfiguration serviceConfiguration : serviceConfigurations) { + Collection> serviceConfigurations = ehcacheConfig.getServiceConfigurations(); + for (ServiceConfiguration serviceConfiguration : serviceConfigurations) { if (serviceConfiguration instanceof DefaultCopierConfiguration) { DefaultCopierConfiguration copierConfig = (DefaultCopierConfiguration)serviceConfiguration; if(copierConfig.getType().equals(DefaultCopierConfiguration.Type.VALUE)) { diff --git a/107/src/main/java/org/ehcache/jsr107/Eh107Configuration.java b/ehcache-107/src/main/java/org/ehcache/jsr107/Eh107Configuration.java similarity index 100% rename from 107/src/main/java/org/ehcache/jsr107/Eh107Configuration.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/Eh107Configuration.java diff --git a/107/src/main/java/org/ehcache/jsr107/Eh107Expiry.java b/ehcache-107/src/main/java/org/ehcache/jsr107/Eh107Expiry.java similarity index 100% rename from 107/src/main/java/org/ehcache/jsr107/Eh107Expiry.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/Eh107Expiry.java diff --git a/107/src/main/java/org/ehcache/jsr107/Eh107IdentityCopier.java b/ehcache-107/src/main/java/org/ehcache/jsr107/Eh107IdentityCopier.java similarity index 100% rename from 107/src/main/java/org/ehcache/jsr107/Eh107IdentityCopier.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/Eh107IdentityCopier.java diff --git a/107/src/main/java/org/ehcache/jsr107/Eh107MXBean.java b/ehcache-107/src/main/java/org/ehcache/jsr107/Eh107MXBean.java similarity index 100% rename from 107/src/main/java/org/ehcache/jsr107/Eh107MXBean.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/Eh107MXBean.java diff --git a/107/src/main/java/org/ehcache/jsr107/Eh107ReverseConfiguration.java b/ehcache-107/src/main/java/org/ehcache/jsr107/Eh107ReverseConfiguration.java similarity index 100% rename from 107/src/main/java/org/ehcache/jsr107/Eh107ReverseConfiguration.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/Eh107ReverseConfiguration.java diff --git a/107/src/main/java/org/ehcache/jsr107/EhcacheCachingProvider.java b/ehcache-107/src/main/java/org/ehcache/jsr107/EhcacheCachingProvider.java similarity index 89% rename from 107/src/main/java/org/ehcache/jsr107/EhcacheCachingProvider.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/EhcacheCachingProvider.java index 46f353d777..15febe4e31 100644 --- a/107/src/main/java/org/ehcache/jsr107/EhcacheCachingProvider.java +++ b/ehcache-107/src/main/java/org/ehcache/jsr107/EhcacheCachingProvider.java @@ -18,24 +18,26 @@ import org.ehcache.config.Configuration; import org.ehcache.core.EhcacheManager; import org.ehcache.core.config.DefaultConfiguration; -import org.ehcache.core.internal.util.ClassLoading; +import org.ehcache.core.spi.ServiceLocator; import org.ehcache.core.spi.service.ServiceUtils; +import org.ehcache.core.util.ClassLoading; import org.ehcache.impl.config.serializer.DefaultSerializationProviderConfiguration; +import org.ehcache.impl.serialization.PlainJavaSerializer; import org.ehcache.jsr107.config.Jsr107Configuration; import org.ehcache.jsr107.internal.DefaultJsr107Service; -import org.ehcache.spi.service.Service; import org.ehcache.spi.service.ServiceCreationConfiguration; import org.ehcache.xml.XmlConfiguration; +import org.osgi.service.component.annotations.Component; import java.net.URI; import java.net.URISyntaxException; -import java.util.ArrayList; import java.util.Collection; import java.util.Map; import java.util.Properties; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.function.UnaryOperator; import javax.cache.CacheException; import javax.cache.CacheManager; @@ -47,6 +49,7 @@ /** * {@link CachingProvider} implementation for Ehcache. */ +@Component public class EhcacheCachingProvider implements CachingProvider { private static final String DEFAULT_URI_STRING = "urn:X-ehcache:jsr107-default-config"; @@ -137,21 +140,22 @@ Eh107CacheManager getCacheManager(ConfigSupplier configSupplier, Properties prop } private Eh107CacheManager createCacheManager(URI uri, Configuration config, Properties properties) { - Eh107CacheLoaderWriterProvider cacheLoaderWriterFactory = new Eh107CacheLoaderWriterProvider(); - - Collection> serviceCreationConfigurations = config.getServiceCreationConfigurations(); + Collection> serviceCreationConfigurations = config.getServiceCreationConfigurations(); Jsr107Service jsr107Service = new DefaultJsr107Service(ServiceUtils.findSingletonAmongst(Jsr107Configuration.class, serviceCreationConfigurations)); + Eh107CacheLoaderWriterProvider cacheLoaderWriterFactory = new Eh107CacheLoaderWriterProvider(); + @SuppressWarnings("unchecked") + DefaultSerializationProviderConfiguration serializerConfiguration = new DefaultSerializationProviderConfiguration().addSerializerFor(Object.class, (Class) PlainJavaSerializer.class); - Collection services = new ArrayList<>(4); - services.add(cacheLoaderWriterFactory); - services.add(jsr107Service); - - if (ServiceUtils.findSingletonAmongst(DefaultSerializationProviderConfiguration.class, serviceCreationConfigurations) == null) { - services.add(new DefaultJsr107SerializationProvider()); - } + UnaryOperator customization = dependencies -> { + ServiceLocator.DependencySet d = dependencies.with(jsr107Service).with(cacheLoaderWriterFactory); + if (ServiceUtils.findSingletonAmongst(DefaultSerializationProviderConfiguration.class, serviceCreationConfigurations) == null) { + d = d.with(serializerConfiguration); + } + return d; + }; - org.ehcache.CacheManager ehcacheManager = new EhcacheManager(config, services, !jsr107Service.jsr107CompliantAtomics()); + org.ehcache.CacheManager ehcacheManager = new EhcacheManager(config, customization, !jsr107Service.jsr107CompliantAtomics()); ehcacheManager.init(); return new Eh107CacheManager(this, ehcacheManager, jsr107Service, properties, config.getClassLoader(), uri, diff --git a/107/src/main/java/org/ehcache/jsr107/EhcacheExpiryWrapper.java b/ehcache-107/src/main/java/org/ehcache/jsr107/EhcacheExpiryWrapper.java similarity index 100% rename from 107/src/main/java/org/ehcache/jsr107/EhcacheExpiryWrapper.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/EhcacheExpiryWrapper.java diff --git a/107/src/main/java/org/ehcache/jsr107/EventListenerAdaptors.java b/ehcache-107/src/main/java/org/ehcache/jsr107/EventListenerAdaptors.java similarity index 100% rename from 107/src/main/java/org/ehcache/jsr107/EventListenerAdaptors.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/EventListenerAdaptors.java diff --git a/107/src/main/java/org/ehcache/jsr107/ExpiryPolicyToEhcacheExpiry.java b/ehcache-107/src/main/java/org/ehcache/jsr107/ExpiryPolicyToEhcacheExpiry.java similarity index 100% rename from 107/src/main/java/org/ehcache/jsr107/ExpiryPolicyToEhcacheExpiry.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/ExpiryPolicyToEhcacheExpiry.java diff --git a/107/src/main/java/org/ehcache/jsr107/Jsr107Service.java b/ehcache-107/src/main/java/org/ehcache/jsr107/Jsr107Service.java similarity index 100% rename from 107/src/main/java/org/ehcache/jsr107/Jsr107Service.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/Jsr107Service.java diff --git a/107/src/main/java/org/ehcache/jsr107/ListenerResources.java b/ehcache-107/src/main/java/org/ehcache/jsr107/ListenerResources.java similarity index 100% rename from 107/src/main/java/org/ehcache/jsr107/ListenerResources.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/ListenerResources.java diff --git a/107/src/main/java/org/ehcache/jsr107/NullCompletionListener.java b/ehcache-107/src/main/java/org/ehcache/jsr107/NullCompletionListener.java similarity index 100% rename from 107/src/main/java/org/ehcache/jsr107/NullCompletionListener.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/NullCompletionListener.java diff --git a/107/src/main/java/org/ehcache/jsr107/Unwrap.java b/ehcache-107/src/main/java/org/ehcache/jsr107/Unwrap.java similarity index 100% rename from 107/src/main/java/org/ehcache/jsr107/Unwrap.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/Unwrap.java diff --git a/107/src/main/java/org/ehcache/jsr107/config/ConfigurationElementState.java b/ehcache-107/src/main/java/org/ehcache/jsr107/config/ConfigurationElementState.java similarity index 100% rename from 107/src/main/java/org/ehcache/jsr107/config/ConfigurationElementState.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/config/ConfigurationElementState.java diff --git a/107/src/main/java/org/ehcache/jsr107/config/Jsr107CacheConfiguration.java b/ehcache-107/src/main/java/org/ehcache/jsr107/config/Jsr107CacheConfiguration.java similarity index 98% rename from 107/src/main/java/org/ehcache/jsr107/config/Jsr107CacheConfiguration.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/config/Jsr107CacheConfiguration.java index e9d8abd9d9..cba5dfa4f9 100644 --- a/107/src/main/java/org/ehcache/jsr107/config/Jsr107CacheConfiguration.java +++ b/ehcache-107/src/main/java/org/ehcache/jsr107/config/Jsr107CacheConfiguration.java @@ -22,7 +22,7 @@ /** * Jsr107CacheConfiguration */ -public class Jsr107CacheConfiguration implements ServiceConfiguration { +public class Jsr107CacheConfiguration implements ServiceConfiguration { private final ConfigurationElementState statisticsEnabled; private final ConfigurationElementState managementEnabled; diff --git a/107/src/main/java/org/ehcache/jsr107/config/Jsr107Configuration.java b/ehcache-107/src/main/java/org/ehcache/jsr107/config/Jsr107Configuration.java similarity index 99% rename from 107/src/main/java/org/ehcache/jsr107/config/Jsr107Configuration.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/config/Jsr107Configuration.java index d2eeb8bdb7..a6cf09ce10 100644 --- a/107/src/main/java/org/ehcache/jsr107/config/Jsr107Configuration.java +++ b/ehcache-107/src/main/java/org/ehcache/jsr107/config/Jsr107Configuration.java @@ -25,7 +25,7 @@ /** * {@link ServiceCreationConfiguration} for default {@link Jsr107Service} implementation. */ -public class Jsr107Configuration implements ServiceCreationConfiguration { +public class Jsr107Configuration implements ServiceCreationConfiguration { private final String defaultTemplate; private final boolean jsr107CompliantAtomics; diff --git a/107/src/main/java/org/ehcache/jsr107/config/package-info.java b/ehcache-107/src/main/java/org/ehcache/jsr107/config/package-info.java similarity index 100% rename from 107/src/main/java/org/ehcache/jsr107/config/package-info.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/config/package-info.java diff --git a/107/src/main/java/org/ehcache/jsr107/internal/DefaultJsr107Service.java b/ehcache-107/src/main/java/org/ehcache/jsr107/internal/DefaultJsr107Service.java similarity index 100% rename from 107/src/main/java/org/ehcache/jsr107/internal/DefaultJsr107Service.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/internal/DefaultJsr107Service.java diff --git a/107/src/main/java/org/ehcache/jsr107/internal/Jsr107CacheConfigurationParser.java b/ehcache-107/src/main/java/org/ehcache/jsr107/internal/Jsr107CacheConfigurationParser.java similarity index 91% rename from 107/src/main/java/org/ehcache/jsr107/internal/Jsr107CacheConfigurationParser.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/internal/Jsr107CacheConfigurationParser.java index 835b2a36f6..81203ee0a9 100644 --- a/107/src/main/java/org/ehcache/jsr107/internal/Jsr107CacheConfigurationParser.java +++ b/ehcache-107/src/main/java/org/ehcache/jsr107/internal/Jsr107CacheConfigurationParser.java @@ -23,6 +23,7 @@ import org.ehcache.spi.service.ServiceConfiguration; import org.ehcache.xml.CacheServiceConfigurationParser; import org.ehcache.xml.exceptions.XmlConfigurationException; +import org.osgi.service.component.annotations.Component; import org.w3c.dom.Element; import java.io.IOException; @@ -35,10 +36,11 @@ /** * Jsr107CacheConfigurationParser */ +@Component public class Jsr107CacheConfigurationParser implements CacheServiceConfigurationParser { private static final URI NAMESPACE = URI.create("http://www.ehcache.org/v3/jsr107"); - private static final URL XML_SCHEMA = Jsr107CacheConfigurationParser.class.getResource("/ehcache-107ext.xsd"); + private static final URL XML_SCHEMA = Jsr107CacheConfigurationParser.class.getResource("/ehcache-107-ext.xsd"); private static final String MANAGEMENT_ENABLED_ATTRIBUTE = "enable-management"; private static final String STATISTICS_ENABLED_ATTRIBUTE = "enable-statistics"; @@ -53,7 +55,7 @@ public URI getNamespace() { } @Override - public ServiceConfiguration parseServiceConfiguration(Element fragment) { + public ServiceConfiguration parseServiceConfiguration(Element fragment, ClassLoader classLoader) { String localName = fragment.getLocalName(); if ("mbeans".equals(localName)) { ConfigurationElementState managementEnabled = ConfigurationElementState.UNSPECIFIED; @@ -77,7 +79,7 @@ public Class getServiceType() { } @Override - public Element unparseServiceConfiguration(ServiceConfiguration serviceConfiguration) { + public Element unparseServiceConfiguration(ServiceConfiguration serviceConfiguration) { throw new XmlConfigurationException("XML translation of JSR-107 cache elements are not supported"); } diff --git a/107/src/main/java/org/ehcache/jsr107/internal/Jsr107CacheLoaderWriter.java b/ehcache-107/src/main/java/org/ehcache/jsr107/internal/Jsr107CacheLoaderWriter.java similarity index 100% rename from 107/src/main/java/org/ehcache/jsr107/internal/Jsr107CacheLoaderWriter.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/internal/Jsr107CacheLoaderWriter.java diff --git a/ehcache-107/src/main/java/org/ehcache/jsr107/internal/Jsr107LatencyMonitor.java b/ehcache-107/src/main/java/org/ehcache/jsr107/internal/Jsr107LatencyMonitor.java new file mode 100644 index 0000000000..5c03ae35da --- /dev/null +++ b/ehcache-107/src/main/java/org/ehcache/jsr107/internal/Jsr107LatencyMonitor.java @@ -0,0 +1,47 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.jsr107.internal; + +import org.ehcache.core.statistics.ChainedOperationObserver; + +import java.util.EnumSet; + +public class Jsr107LatencyMonitor> implements ChainedOperationObserver { + + private final org.terracotta.statistics.derived.latency.Jsr107LatencyMonitor delegate; + + public Jsr107LatencyMonitor(Class outcome) { + delegate = new org.terracotta.statistics.derived.latency.Jsr107LatencyMonitor<>(EnumSet.allOf(outcome), 1.0); + } + + public double average() { + return delegate.average(); + } + + public void clear() { + delegate.clear(); + } + + @Override + public void begin(long time) { + delegate.begin(time); + } + + @Override + public void end(long time, long latency, T result) { + delegate.end(time, latency, result); + } +} diff --git a/107/src/main/java/org/ehcache/jsr107/internal/Jsr107ServiceConfigurationParser.java b/ehcache-107/src/main/java/org/ehcache/jsr107/internal/Jsr107ServiceConfigurationParser.java similarity index 92% rename from 107/src/main/java/org/ehcache/jsr107/internal/Jsr107ServiceConfigurationParser.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/internal/Jsr107ServiceConfigurationParser.java index a80c67bf5d..83250dbfe5 100644 --- a/107/src/main/java/org/ehcache/jsr107/internal/Jsr107ServiceConfigurationParser.java +++ b/ehcache-107/src/main/java/org/ehcache/jsr107/internal/Jsr107ServiceConfigurationParser.java @@ -22,6 +22,7 @@ import org.ehcache.jsr107.Jsr107Service; import org.ehcache.spi.service.ServiceCreationConfiguration; import org.ehcache.xml.exceptions.XmlConfigurationException; +import org.osgi.service.component.annotations.Component; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -39,10 +40,11 @@ /** * @author Alex Snaps */ +@Component public class Jsr107ServiceConfigurationParser implements CacheManagerServiceConfigurationParser { private static final URI NAMESPACE = URI.create("http://www.ehcache.org/v3/jsr107"); - private static final URL XML_SCHEMA = Jsr107ServiceConfigurationParser.class.getResource("/ehcache-107ext.xsd"); + private static final URL XML_SCHEMA = Jsr107ServiceConfigurationParser.class.getResource("/ehcache-107-ext.xsd"); private static final String ENABLE_MANAGEMENT_ALL_ATTRIBUTE = "enable-management"; private static final String JSR_107_COMPLIANT_ATOMICS_ATTRIBUTE = "jsr-107-compliant-atomics"; private static final String ENABLE_STATISTICS_ALL_ATTRIBUTE = "enable-statistics"; @@ -61,7 +63,7 @@ public URI getNamespace() { } @Override - public ServiceCreationConfiguration parseServiceCreationConfiguration(final Element fragment) { + public ServiceCreationConfiguration parseServiceCreationConfiguration(final Element fragment, ClassLoader classLoader) { boolean jsr107CompliantAtomics = true; ConfigurationElementState enableManagementAll = ConfigurationElementState.UNSPECIFIED; ConfigurationElementState enableStatisticsAll = ConfigurationElementState.UNSPECIFIED; @@ -94,7 +96,7 @@ public Class getServiceType() { } @Override - public Element unparseServiceCreationConfiguration(ServiceCreationConfiguration serviceCreationConfiguration) { + public Element unparseServiceCreationConfiguration(ServiceCreationConfiguration serviceCreationConfiguration) { throw new XmlConfigurationException("XML translation of JSR-107 cache elements are not supported"); } diff --git a/107/src/main/java/org/ehcache/jsr107/internal/WrappedCacheLoaderWriter.java b/ehcache-107/src/main/java/org/ehcache/jsr107/internal/WrappedCacheLoaderWriter.java similarity index 100% rename from 107/src/main/java/org/ehcache/jsr107/internal/WrappedCacheLoaderWriter.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/internal/WrappedCacheLoaderWriter.java diff --git a/107/src/main/java/org/ehcache/jsr107/internal/tck/Eh107MBeanServerBuilder.java b/ehcache-107/src/main/java/org/ehcache/jsr107/internal/tck/Eh107MBeanServerBuilder.java similarity index 100% rename from 107/src/main/java/org/ehcache/jsr107/internal/tck/Eh107MBeanServerBuilder.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/internal/tck/Eh107MBeanServerBuilder.java diff --git a/107/src/main/java/org/ehcache/jsr107/package-info.java b/ehcache-107/src/main/java/org/ehcache/jsr107/package-info.java similarity index 100% rename from 107/src/main/java/org/ehcache/jsr107/package-info.java rename to ehcache-107/src/main/java/org/ehcache/jsr107/package-info.java diff --git a/107/src/main/resources/META-INF/services/javax.cache.spi.CachingProvider b/ehcache-107/src/main/resources/META-INF/services/javax.cache.spi.CachingProvider similarity index 100% rename from 107/src/main/resources/META-INF/services/javax.cache.spi.CachingProvider rename to ehcache-107/src/main/resources/META-INF/services/javax.cache.spi.CachingProvider diff --git a/107/src/main/resources/META-INF/services/org.ehcache.xml.CacheManagerServiceConfigurationParser b/ehcache-107/src/main/resources/META-INF/services/org.ehcache.xml.CacheManagerServiceConfigurationParser similarity index 100% rename from 107/src/main/resources/META-INF/services/org.ehcache.xml.CacheManagerServiceConfigurationParser rename to ehcache-107/src/main/resources/META-INF/services/org.ehcache.xml.CacheManagerServiceConfigurationParser diff --git a/107/src/main/resources/META-INF/services/org.ehcache.xml.CacheServiceConfigurationParser b/ehcache-107/src/main/resources/META-INF/services/org.ehcache.xml.CacheServiceConfigurationParser similarity index 100% rename from 107/src/main/resources/META-INF/services/org.ehcache.xml.CacheServiceConfigurationParser rename to ehcache-107/src/main/resources/META-INF/services/org.ehcache.xml.CacheServiceConfigurationParser diff --git a/107/src/main/resources/ehcache-107ext.xsd b/ehcache-107/src/main/resources/ehcache-107-ext.xsd similarity index 100% rename from 107/src/main/resources/ehcache-107ext.xsd rename to ehcache-107/src/main/resources/ehcache-107-ext.xsd diff --git a/107/src/tck/resources/ExcludeList b/ehcache-107/src/tck/resources/ExcludeList similarity index 100% rename from 107/src/tck/resources/ExcludeList rename to ehcache-107/src/tck/resources/ExcludeList diff --git a/107/src/test/java/com/pany/domain/Client.java b/ehcache-107/src/test/java/com/pany/domain/Client.java similarity index 100% rename from 107/src/test/java/com/pany/domain/Client.java rename to ehcache-107/src/test/java/com/pany/domain/Client.java diff --git a/107/src/test/java/com/pany/domain/Customer.java b/ehcache-107/src/test/java/com/pany/domain/Customer.java similarity index 100% rename from 107/src/test/java/com/pany/domain/Customer.java rename to ehcache-107/src/test/java/com/pany/domain/Customer.java diff --git a/107/src/test/java/com/pany/domain/Product.java b/ehcache-107/src/test/java/com/pany/domain/Product.java similarity index 100% rename from 107/src/test/java/com/pany/domain/Product.java rename to ehcache-107/src/test/java/com/pany/domain/Product.java diff --git a/107/src/test/java/com/pany/ehcache/ClientCopier.java b/ehcache-107/src/test/java/com/pany/ehcache/ClientCopier.java similarity index 100% rename from 107/src/test/java/com/pany/ehcache/ClientCopier.java rename to ehcache-107/src/test/java/com/pany/ehcache/ClientCopier.java diff --git a/107/src/test/java/com/pany/ehcache/MyEvictionAdvisor.java b/ehcache-107/src/test/java/com/pany/ehcache/MyEvictionAdvisor.java similarity index 100% rename from 107/src/test/java/com/pany/ehcache/MyEvictionAdvisor.java rename to ehcache-107/src/test/java/com/pany/ehcache/MyEvictionAdvisor.java diff --git a/107/src/test/java/com/pany/ehcache/Test107CacheEntryListener.java b/ehcache-107/src/test/java/com/pany/ehcache/Test107CacheEntryListener.java similarity index 100% rename from 107/src/test/java/com/pany/ehcache/Test107CacheEntryListener.java rename to ehcache-107/src/test/java/com/pany/ehcache/Test107CacheEntryListener.java diff --git a/107/src/test/java/com/pany/ehcache/TestCacheEventListener.java b/ehcache-107/src/test/java/com/pany/ehcache/TestCacheEventListener.java similarity index 100% rename from 107/src/test/java/com/pany/ehcache/TestCacheEventListener.java rename to ehcache-107/src/test/java/com/pany/ehcache/TestCacheEventListener.java diff --git a/107/src/test/java/com/pany/ehcache/integration/ProductCacheLoaderWriter.java b/ehcache-107/src/test/java/com/pany/ehcache/integration/ProductCacheLoaderWriter.java similarity index 100% rename from 107/src/test/java/com/pany/ehcache/integration/ProductCacheLoaderWriter.java rename to ehcache-107/src/test/java/com/pany/ehcache/integration/ProductCacheLoaderWriter.java diff --git a/107/src/test/java/org/ehcache/ParsesConfigurationExtensionTest.java b/ehcache-107/src/test/java/org/ehcache/ParsesConfigurationExtensionTest.java similarity index 100% rename from 107/src/test/java/org/ehcache/ParsesConfigurationExtensionTest.java rename to ehcache-107/src/test/java/org/ehcache/ParsesConfigurationExtensionTest.java diff --git a/107/src/test/java/org/ehcache/docs/EhCache107ConfigurationIntegrationDocTest.java b/ehcache-107/src/test/java/org/ehcache/docs/EhCache107ConfigurationIntegrationDocTest.java similarity index 99% rename from 107/src/test/java/org/ehcache/docs/EhCache107ConfigurationIntegrationDocTest.java rename to ehcache-107/src/test/java/org/ehcache/docs/EhCache107ConfigurationIntegrationDocTest.java index d54c7ae2b7..165fa321d6 100644 --- a/107/src/test/java/org/ehcache/docs/EhCache107ConfigurationIntegrationDocTest.java +++ b/ehcache-107/src/test/java/org/ehcache/docs/EhCache107ConfigurationIntegrationDocTest.java @@ -51,6 +51,7 @@ import javax.cache.expiry.ExpiryPolicy; import javax.cache.spi.CachingProvider; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; @@ -58,7 +59,6 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; /** @@ -259,7 +259,7 @@ public void testTemplateOverridingStoreByRef() throws Exception { MutableConfiguration mutableConfiguration = new MutableConfiguration<>(); mutableConfiguration.setTypes(Long.class, Client.class).setStoreByValue(false); - Cache myCache = null; + Cache myCache; Client client1 = new Client("client1", 1); myCache = cacheManager.createCache("anotherCache", mutableConfiguration); diff --git a/107/src/test/java/org/ehcache/jsr107/CacheResourcesTest.java b/ehcache-107/src/test/java/org/ehcache/jsr107/CacheResourcesTest.java similarity index 100% rename from 107/src/test/java/org/ehcache/jsr107/CacheResourcesTest.java rename to ehcache-107/src/test/java/org/ehcache/jsr107/CacheResourcesTest.java diff --git a/107/src/test/java/org/ehcache/jsr107/ConfigStatsManagementActivationTest.java b/ehcache-107/src/test/java/org/ehcache/jsr107/ConfigStatsManagementActivationTest.java similarity index 98% rename from 107/src/test/java/org/ehcache/jsr107/ConfigStatsManagementActivationTest.java rename to ehcache-107/src/test/java/org/ehcache/jsr107/ConfigStatsManagementActivationTest.java index a9d90e53f3..cf56e09d7f 100644 --- a/107/src/test/java/org/ehcache/jsr107/ConfigStatsManagementActivationTest.java +++ b/ehcache-107/src/test/java/org/ehcache/jsr107/ConfigStatsManagementActivationTest.java @@ -36,8 +36,8 @@ import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; /** * ConfigStatsManagementActivationTest @@ -130,7 +130,7 @@ public void testEnableCacheLevelProgrammatic() throws Exception { CacheManager cacheManager = provider.getCacheManager(); CacheConfigurationBuilder configurationBuilder = newCacheConfigurationBuilder(Long.class, String.class, heap(10)) - .add(new Jsr107CacheConfiguration(ConfigurationElementState.ENABLED, ConfigurationElementState.ENABLED)); + .withService(new Jsr107CacheConfiguration(ConfigurationElementState.ENABLED, ConfigurationElementState.ENABLED)); Cache cache = cacheManager.createCache("test", Eh107Configuration.fromEhcacheCacheConfiguration(configurationBuilder)); @SuppressWarnings("unchecked") diff --git a/107/src/test/java/org/ehcache/jsr107/ConfigurationMergerTest.java b/ehcache-107/src/test/java/org/ehcache/jsr107/ConfigurationMergerTest.java similarity index 94% rename from 107/src/test/java/org/ehcache/jsr107/ConfigurationMergerTest.java rename to ehcache-107/src/test/java/org/ehcache/jsr107/ConfigurationMergerTest.java index b386b67620..d4783cb90e 100644 --- a/107/src/test/java/org/ehcache/jsr107/ConfigurationMergerTest.java +++ b/ehcache-107/src/test/java/org/ehcache/jsr107/ConfigurationMergerTest.java @@ -51,12 +51,12 @@ import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.sameInstance; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; @@ -92,8 +92,8 @@ public void mergeConfigNoTemplateNoLoaderWriter() { assertThat(configHolder.useEhcacheLoaderWriter, is(false)); boolean storeByValue = false; - Collection> serviceConfigurations = configHolder.cacheConfiguration.getServiceConfigurations(); - for (ServiceConfiguration serviceConfiguration : serviceConfigurations) { + Collection> serviceConfigurations = configHolder.cacheConfiguration.getServiceConfigurations(); + for (ServiceConfiguration serviceConfiguration : serviceConfigurations) { if (serviceConfiguration instanceof DefaultCopierConfiguration) { storeByValue = true; break; @@ -183,7 +183,7 @@ public void jsr107ExpiryGetsOverriddenByTemplate() throws Exception { public void jsr107LoaderGetsOverriddenByTemplate() throws Exception { when(jsr107Service.getTemplateNameForCache("cache")).thenReturn("cacheTemplate"); when(xmlConfiguration.newCacheConfigurationBuilderFromTemplate("cacheTemplate", Object.class, Object.class)).thenReturn( - newCacheConfigurationBuilder(Object.class, Object.class, heap(10)).add(new DefaultCacheLoaderWriterConfiguration((Class)null)) + newCacheConfigurationBuilder(Object.class, Object.class, heap(10)).withService(new DefaultCacheLoaderWriterConfiguration((Class)null)) ); MutableConfiguration configuration = new MutableConfiguration<>(); @@ -200,8 +200,8 @@ public void jsr107LoaderGetsOverriddenByTemplate() throws Exception { @Test public void jsr107StoreByValueGetsOverriddenByTemplate() throws Exception { CacheConfigurationBuilder builder = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) - .add(new DefaultCopierConfiguration((Class)IdentityCopier.class, DefaultCopierConfiguration.Type.KEY)) - .add(new DefaultCopierConfiguration((Class)IdentityCopier.class, DefaultCopierConfiguration.Type.VALUE)); + .withService(new DefaultCopierConfiguration((Class)IdentityCopier.class, DefaultCopierConfiguration.Type.KEY)) + .withService(new DefaultCopierConfiguration((Class)IdentityCopier.class, DefaultCopierConfiguration.Type.VALUE)); when(jsr107Service.getTemplateNameForCache("cache")).thenReturn("cacheTemplate"); when(xmlConfiguration.newCacheConfigurationBuilderFromTemplate("cacheTemplate", Object.class, Object.class)) @@ -212,8 +212,8 @@ public void jsr107StoreByValueGetsOverriddenByTemplate() throws Exception { ConfigurationMerger.ConfigHolder configHolder = merger.mergeConfigurations("cache", configuration); boolean storeByValue = true; - Collection> serviceConfigurations = configHolder.cacheConfiguration.getServiceConfigurations(); - for (ServiceConfiguration serviceConfiguration : serviceConfigurations) { + Collection> serviceConfigurations = configHolder.cacheConfiguration.getServiceConfigurations(); + for (ServiceConfiguration serviceConfiguration : serviceConfigurations) { if (serviceConfiguration instanceof DefaultCopierConfiguration) { DefaultCopierConfiguration copierConfig = (DefaultCopierConfiguration)serviceConfiguration; if(copierConfig.getClazz().isAssignableFrom(IdentityCopier.class)) @@ -355,7 +355,7 @@ public void jsr107DefaultEh107IdentityCopierForImmutableTypesWithCMLevelDefaults assertThat(configHolder1.cacheConfiguration.getServiceConfigurations().isEmpty(), is(true)); - for (ServiceCreationConfiguration serviceCreationConfiguration : xmlConfiguration.getServiceCreationConfigurations()) { + for (ServiceCreationConfiguration serviceCreationConfiguration : xmlConfiguration.getServiceCreationConfigurations()) { if (serviceCreationConfiguration instanceof DefaultCopyProviderConfiguration) { DefaultCopyProviderConfiguration copierConfig = (DefaultCopyProviderConfiguration)serviceCreationConfiguration; assertThat(copierConfig.getDefaults().size(), is(6)); @@ -378,9 +378,9 @@ public void jsr107DefaultEh107IdentityCopierForImmutableTypesWithoutTemplates() assertDefaultCopier(configHolder1.cacheConfiguration.getServiceConfigurations()); } - private static void assertDefaultCopier(Collection> serviceConfigurations) { + private static void assertDefaultCopier(Collection> serviceConfigurations) { boolean noCopierConfigPresent = false; - for (ServiceConfiguration serviceConfiguration : serviceConfigurations) { + for (ServiceConfiguration serviceConfiguration : serviceConfigurations) { if (serviceConfiguration instanceof DefaultCopierConfiguration) { noCopierConfigPresent = true; DefaultCopierConfiguration copierConfig = (DefaultCopierConfiguration)serviceConfiguration; diff --git a/107/src/test/java/org/ehcache/jsr107/DefaultConfigurationResolverTest.java b/ehcache-107/src/test/java/org/ehcache/jsr107/DefaultConfigurationResolverTest.java similarity index 100% rename from 107/src/test/java/org/ehcache/jsr107/DefaultConfigurationResolverTest.java rename to ehcache-107/src/test/java/org/ehcache/jsr107/DefaultConfigurationResolverTest.java diff --git a/107/src/test/java/org/ehcache/jsr107/Eh107CacheTypeTest.java b/ehcache-107/src/test/java/org/ehcache/jsr107/Eh107CacheTypeTest.java similarity index 100% rename from 107/src/test/java/org/ehcache/jsr107/Eh107CacheTypeTest.java rename to ehcache-107/src/test/java/org/ehcache/jsr107/Eh107CacheTypeTest.java diff --git a/107/src/test/java/org/ehcache/jsr107/Eh107XmlIntegrationTest.java b/ehcache-107/src/test/java/org/ehcache/jsr107/Eh107XmlIntegrationTest.java similarity index 100% rename from 107/src/test/java/org/ehcache/jsr107/Eh107XmlIntegrationTest.java rename to ehcache-107/src/test/java/org/ehcache/jsr107/Eh107XmlIntegrationTest.java diff --git a/107/src/test/java/org/ehcache/jsr107/EhCachingProviderTest.java b/ehcache-107/src/test/java/org/ehcache/jsr107/EhCachingProviderTest.java similarity index 100% rename from 107/src/test/java/org/ehcache/jsr107/EhCachingProviderTest.java rename to ehcache-107/src/test/java/org/ehcache/jsr107/EhCachingProviderTest.java diff --git a/107/src/test/java/org/ehcache/jsr107/IteratorTest.java b/ehcache-107/src/test/java/org/ehcache/jsr107/IteratorTest.java similarity index 86% rename from 107/src/test/java/org/ehcache/jsr107/IteratorTest.java rename to ehcache-107/src/test/java/org/ehcache/jsr107/IteratorTest.java index 0cf04f6e73..11e200fbb0 100644 --- a/107/src/test/java/org/ehcache/jsr107/IteratorTest.java +++ b/ehcache-107/src/test/java/org/ehcache/jsr107/IteratorTest.java @@ -23,7 +23,6 @@ import javax.cache.Cache; import javax.cache.CacheManager; import javax.cache.Caching; -import javax.cache.configuration.Factory; import javax.cache.configuration.MutableConfiguration; import javax.cache.expiry.Duration; import javax.cache.expiry.ExpiryPolicy; @@ -33,7 +32,6 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.nullValue; /** * @author Ludovic Orban @@ -55,7 +53,7 @@ private void advanceTime(long delta) { } @Test - public void testIterateExpiredReturnsNull() throws Exception { + public void testIterateExpiredIsSkipped() throws Exception { EhcacheCachingProvider provider = (EhcacheCachingProvider) Caching.getCachingProvider(); TestTimeSource testTimeSource = new TestTimeSource(); TimeSourceConfiguration timeSourceConfiguration = new TimeSourceConfiguration(testTimeSource); @@ -86,16 +84,7 @@ public Duration getExpiryForUpdate() { testTimeSource.advanceTime(1000); Iterator> iterator = testCache.iterator(); - assertThat(iterator.hasNext(), is(true)); - - int loopCount = 0; - while (iterator.hasNext()) { - Cache.Entry next = iterator.next(); - assertThat(next, is(nullValue())); - - loopCount++; - } - assertThat(loopCount, is(1)); + assertThat(iterator.hasNext(), is(false)); cacheManager.close(); } diff --git a/107/src/test/java/org/ehcache/jsr107/Jsr107CacheParserIT.java b/ehcache-107/src/test/java/org/ehcache/jsr107/Jsr107CacheParserIT.java similarity index 90% rename from 107/src/test/java/org/ehcache/jsr107/Jsr107CacheParserIT.java rename to ehcache-107/src/test/java/org/ehcache/jsr107/Jsr107CacheParserIT.java index b825f6ed69..131d328e3a 100644 --- a/107/src/test/java/org/ehcache/jsr107/Jsr107CacheParserIT.java +++ b/ehcache-107/src/test/java/org/ehcache/jsr107/Jsr107CacheParserIT.java @@ -17,7 +17,6 @@ import org.ehcache.config.Configuration; import org.ehcache.xml.XmlConfiguration; -import org.ehcache.xml.XmlConfigurationTest; import org.ehcache.xml.exceptions.XmlConfigurationException; import org.junit.Test; @@ -30,7 +29,7 @@ public class Jsr107CacheParserIT { @Test(expected = XmlConfigurationException.class) public void testJsr107CacheXmlTranslationToString() { - URL resource = XmlConfigurationTest.class.getResource("/ehcache-107.xml"); + URL resource = Jsr107CacheParserIT.class.getResource("/ehcache-107.xml"); Configuration config = new XmlConfiguration(resource); XmlConfiguration xmlConfig = new XmlConfiguration(config); } diff --git a/107/src/test/java/org/ehcache/jsr107/LoadAtomicsWith107Test.java b/ehcache-107/src/test/java/org/ehcache/jsr107/LoadAtomicsWith107Test.java similarity index 98% rename from 107/src/test/java/org/ehcache/jsr107/LoadAtomicsWith107Test.java rename to ehcache-107/src/test/java/org/ehcache/jsr107/LoadAtomicsWith107Test.java index 8ad6714e49..27043ea2af 100644 --- a/107/src/test/java/org/ehcache/jsr107/LoadAtomicsWith107Test.java +++ b/ehcache-107/src/test/java/org/ehcache/jsr107/LoadAtomicsWith107Test.java @@ -32,8 +32,8 @@ import javax.cache.integration.CacheWriter; import javax.cache.spi.CachingProvider; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; diff --git a/107/src/test/java/org/ehcache/jsr107/LoaderWriterConfigTest.java b/ehcache-107/src/test/java/org/ehcache/jsr107/LoaderWriterConfigTest.java similarity index 100% rename from 107/src/test/java/org/ehcache/jsr107/LoaderWriterConfigTest.java rename to ehcache-107/src/test/java/org/ehcache/jsr107/LoaderWriterConfigTest.java diff --git a/107/src/test/java/org/ehcache/jsr107/LoaderWriterTest.java b/ehcache-107/src/test/java/org/ehcache/jsr107/LoaderWriterTest.java similarity index 99% rename from 107/src/test/java/org/ehcache/jsr107/LoaderWriterTest.java rename to ehcache-107/src/test/java/org/ehcache/jsr107/LoaderWriterTest.java index 906cf61f44..bd7265e365 100644 --- a/107/src/test/java/org/ehcache/jsr107/LoaderWriterTest.java +++ b/ehcache-107/src/test/java/org/ehcache/jsr107/LoaderWriterTest.java @@ -35,8 +35,8 @@ import javax.cache.integration.CacheWriter; import javax.cache.spi.CachingProvider; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; diff --git a/107/src/test/java/org/ehcache/jsr107/LongSerializer.java b/ehcache-107/src/test/java/org/ehcache/jsr107/LongSerializer.java similarity index 100% rename from 107/src/test/java/org/ehcache/jsr107/LongSerializer.java rename to ehcache-107/src/test/java/org/ehcache/jsr107/LongSerializer.java diff --git a/107/src/test/java/org/ehcache/jsr107/ResourceCombinationsTest.java b/ehcache-107/src/test/java/org/ehcache/jsr107/ResourceCombinationsTest.java similarity index 98% rename from 107/src/test/java/org/ehcache/jsr107/ResourceCombinationsTest.java rename to ehcache-107/src/test/java/org/ehcache/jsr107/ResourceCombinationsTest.java index e2790f82b7..d8964866fa 100644 --- a/107/src/test/java/org/ehcache/jsr107/ResourceCombinationsTest.java +++ b/ehcache-107/src/test/java/org/ehcache/jsr107/ResourceCombinationsTest.java @@ -41,8 +41,8 @@ import static org.ehcache.config.units.EntryUnit.ENTRIES; import static org.ehcache.config.units.MemoryUnit.MB; import static org.ehcache.jsr107.Eh107Configuration.fromEhcacheCacheConfiguration; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; @RunWith(Parameterized.class) public class ResourceCombinationsTest { diff --git a/107/src/test/java/org/ehcache/jsr107/SerializerTest.java b/ehcache-107/src/test/java/org/ehcache/jsr107/SerializerTest.java similarity index 97% rename from 107/src/test/java/org/ehcache/jsr107/SerializerTest.java rename to ehcache-107/src/test/java/org/ehcache/jsr107/SerializerTest.java index 10bc298ac0..d638dc5097 100644 --- a/107/src/test/java/org/ehcache/jsr107/SerializerTest.java +++ b/ehcache-107/src/test/java/org/ehcache/jsr107/SerializerTest.java @@ -27,7 +27,7 @@ import javax.cache.spi.CachingProvider; import static org.hamcrest.CoreMatchers.equalTo; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; /** * @author rism diff --git a/107/src/test/java/org/ehcache/jsr107/SimpleEh107ConfigTest.java b/ehcache-107/src/test/java/org/ehcache/jsr107/SimpleEh107ConfigTest.java similarity index 97% rename from 107/src/test/java/org/ehcache/jsr107/SimpleEh107ConfigTest.java rename to ehcache-107/src/test/java/org/ehcache/jsr107/SimpleEh107ConfigTest.java index b37a89040a..09a8058b2e 100644 --- a/107/src/test/java/org/ehcache/jsr107/SimpleEh107ConfigTest.java +++ b/ehcache-107/src/test/java/org/ehcache/jsr107/SimpleEh107ConfigTest.java @@ -24,7 +24,6 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; -import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; import javax.cache.Cache; @@ -40,9 +39,9 @@ import javax.cache.integration.CompletionListenerFuture; import javax.cache.spi.CachingProvider; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; /** * SimpleEh107ConfigTest diff --git a/107/src/test/java/org/ehcache/jsr107/StatisticsTest.java b/ehcache-107/src/test/java/org/ehcache/jsr107/StatisticsTest.java similarity index 86% rename from 107/src/test/java/org/ehcache/jsr107/StatisticsTest.java rename to ehcache-107/src/test/java/org/ehcache/jsr107/StatisticsTest.java index 8c88118b5b..5064e6a396 100644 --- a/107/src/test/java/org/ehcache/jsr107/StatisticsTest.java +++ b/ehcache-107/src/test/java/org/ehcache/jsr107/StatisticsTest.java @@ -15,7 +15,6 @@ */ package org.ehcache.jsr107; -import org.hamcrest.Matcher; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -26,15 +25,15 @@ import javax.cache.configuration.MutableConfiguration; import javax.cache.spi.CachingProvider; +import java.time.Duration; import java.util.HashSet; -import java.util.concurrent.Callable; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.lessThan; -import static org.hamcrest.Matchers.not; +import static org.terracotta.utilities.test.matchers.Eventually.within; /** * @author Ludovic Orban @@ -210,8 +209,7 @@ public void test_getAverageGetTime() throws Exception { heapCache.get("key"); heapCache.get("key"); - assertFor(1100L, () -> heapStatistics.getAverageGetTime(), is(not(0.0f))); - assertThat(heapStatistics.getAverageGetTime(), greaterThan(0.0f)); + assertThat(heapStatistics::getAverageGetTime, within(Duration.ofMillis(1100)).matches(greaterThan(0.0f))); } @Test @@ -224,8 +222,7 @@ public void test_getAveragePutTime() throws Exception { heapCache.put("key", "value"); heapCache.put("key", "value"); - assertFor(1100L, () -> heapStatistics.getAveragePutTime(), is(not(0.0f))); - assertThat(heapStatistics.getAveragePutTime(), greaterThan(0.0f)); + assertThat(heapStatistics::getAveragePutTime, within(Duration.ofMillis(1100)).matches(greaterThan(0.0f))); } @Test @@ -244,28 +241,6 @@ public void test_getAverageRemoveTime() throws Exception { heapCache.remove("key3"); heapCache.remove("key4"); - assertFor(1100L, () -> heapStatistics.getAverageRemoveTime(), is(not(0.0f))); - assertThat(heapStatistics.getAverageRemoveTime(), greaterThan(0.0f)); + assertThat(heapStatistics::getAverageRemoveTime, within(Duration.ofMillis(1100)).matches(greaterThan(0.0f))); } - - private static void assertFor(long timeoutInMs, Callable callable, Matcher matcher) throws Exception { - long timeLeftInMs = timeoutInMs; - - while (timeLeftInMs > 0) { - try { - assertThat(callable.call(), matcher); - return; - } catch (AssertionError assertionError) { - try { - Thread.sleep(100); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - timeLeftInMs -= 100; - } - } - - assertThat(callable.call(), matcher); - } - } diff --git a/107/src/test/java/org/ehcache/jsr107/StringSerializer.java b/ehcache-107/src/test/java/org/ehcache/jsr107/StringSerializer.java similarity index 100% rename from 107/src/test/java/org/ehcache/jsr107/StringSerializer.java rename to ehcache-107/src/test/java/org/ehcache/jsr107/StringSerializer.java diff --git a/107/src/test/java/org/ehcache/jsr107/UnwrapTest.java b/ehcache-107/src/test/java/org/ehcache/jsr107/UnwrapTest.java similarity index 98% rename from 107/src/test/java/org/ehcache/jsr107/UnwrapTest.java rename to ehcache-107/src/test/java/org/ehcache/jsr107/UnwrapTest.java index e89a692516..a3233c0fe4 100644 --- a/107/src/test/java/org/ehcache/jsr107/UnwrapTest.java +++ b/ehcache-107/src/test/java/org/ehcache/jsr107/UnwrapTest.java @@ -28,9 +28,9 @@ import javax.cache.event.EventType; import javax.cache.spi.CachingProvider; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsInstanceOf.instanceOf; -import static org.junit.Assert.assertThat; /** * @author rism diff --git a/107/src/test/java/org/ehcache/jsr107/internal/Jsr107CacheConfigurationParserTest.java b/ehcache-107/src/test/java/org/ehcache/jsr107/internal/Jsr107CacheConfigurationParserTest.java similarity index 100% rename from 107/src/test/java/org/ehcache/jsr107/internal/Jsr107CacheConfigurationParserTest.java rename to ehcache-107/src/test/java/org/ehcache/jsr107/internal/Jsr107CacheConfigurationParserTest.java diff --git a/107/src/test/java/org/ehcache/jsr107/internal/Jsr107ServiceConfigurationParserTest.java b/ehcache-107/src/test/java/org/ehcache/jsr107/internal/Jsr107ServiceConfigurationParserTest.java similarity index 100% rename from 107/src/test/java/org/ehcache/jsr107/internal/Jsr107ServiceConfigurationParserTest.java rename to ehcache-107/src/test/java/org/ehcache/jsr107/internal/Jsr107ServiceConfigurationParserTest.java diff --git a/107/src/test/resources/ehcache-107-copiers-immutable-types.xml b/ehcache-107/src/test/resources/ehcache-107-copiers-immutable-types.xml similarity index 97% rename from 107/src/test/resources/ehcache-107-copiers-immutable-types.xml rename to ehcache-107/src/test/resources/ehcache-107-copiers-immutable-types.xml index d2499af468..366d4f5127 100644 --- a/107/src/test/resources/ehcache-107-copiers-immutable-types.xml +++ b/ehcache-107/src/test/resources/ehcache-107-copiers-immutable-types.xml @@ -15,7 +15,6 @@ --> diff --git a/107/src/test/resources/ehcache-107-default-copiers.xml b/ehcache-107/src/test/resources/ehcache-107-default-copiers.xml similarity index 95% rename from 107/src/test/resources/ehcache-107-default-copiers.xml rename to ehcache-107/src/test/resources/ehcache-107-default-copiers.xml index a484c01976..5c707f0335 100644 --- a/107/src/test/resources/ehcache-107-default-copiers.xml +++ b/ehcache-107/src/test/resources/ehcache-107-default-copiers.xml @@ -15,7 +15,6 @@ --> diff --git a/107/src/test/resources/ehcache-107-immutable-types-cm-level-copiers.xml b/ehcache-107/src/test/resources/ehcache-107-immutable-types-cm-level-copiers.xml similarity index 97% rename from 107/src/test/resources/ehcache-107-immutable-types-cm-level-copiers.xml rename to ehcache-107/src/test/resources/ehcache-107-immutable-types-cm-level-copiers.xml index db38f60436..8882458718 100644 --- a/107/src/test/resources/ehcache-107-immutable-types-cm-level-copiers.xml +++ b/ehcache-107/src/test/resources/ehcache-107-immutable-types-cm-level-copiers.xml @@ -15,7 +15,6 @@ --> diff --git a/107/src/test/resources/ehcache-107-integration.xml b/ehcache-107/src/test/resources/ehcache-107-integration.xml similarity index 100% rename from 107/src/test/resources/ehcache-107-integration.xml rename to ehcache-107/src/test/resources/ehcache-107-integration.xml diff --git a/107/src/test/resources/ehcache-107-listeners.xml b/ehcache-107/src/test/resources/ehcache-107-listeners.xml similarity index 80% rename from 107/src/test/resources/ehcache-107-listeners.xml rename to ehcache-107/src/test/resources/ehcache-107-listeners.xml index a32743e097..eb460261ee 100644 --- a/107/src/test/resources/ehcache-107-listeners.xml +++ b/ehcache-107/src/test/resources/ehcache-107-listeners.xml @@ -16,11 +16,8 @@ --> + xmlns:jsr107='http://www.ehcache.org/v3/jsr107'> @@ -42,4 +39,4 @@ 2000 - \ No newline at end of file + diff --git a/107/src/test/resources/ehcache-107-mbeans-cache-config.xml b/ehcache-107/src/test/resources/ehcache-107-mbeans-cache-config.xml similarity index 73% rename from 107/src/test/resources/ehcache-107-mbeans-cache-config.xml rename to ehcache-107/src/test/resources/ehcache-107-mbeans-cache-config.xml index 1e39955b8f..ca599c2396 100644 --- a/107/src/test/resources/ehcache-107-mbeans-cache-config.xml +++ b/ehcache-107/src/test/resources/ehcache-107-mbeans-cache-config.xml @@ -15,11 +15,8 @@ --> + xmlns:jsr107='http://www.ehcache.org/v3/jsr107'> java.lang.String @@ -28,4 +25,4 @@ - \ No newline at end of file + diff --git a/107/src/test/resources/ehcache-107-mbeans-template-config.xml b/ehcache-107/src/test/resources/ehcache-107-mbeans-template-config.xml similarity index 78% rename from 107/src/test/resources/ehcache-107-mbeans-template-config.xml rename to ehcache-107/src/test/resources/ehcache-107-mbeans-template-config.xml index 8824829628..430984105e 100644 --- a/107/src/test/resources/ehcache-107-mbeans-template-config.xml +++ b/ehcache-107/src/test/resources/ehcache-107-mbeans-template-config.xml @@ -15,11 +15,8 @@ --> + xmlns:jsr107='http://www.ehcache.org/v3/jsr107'> @@ -38,4 +35,4 @@ - \ No newline at end of file + diff --git a/107/src/test/resources/ehcache-107-serializer.xml b/ehcache-107/src/test/resources/ehcache-107-serializer.xml similarity index 68% rename from 107/src/test/resources/ehcache-107-serializer.xml rename to ehcache-107/src/test/resources/ehcache-107-serializer.xml index 5722c36df1..8d8deec44e 100644 --- a/107/src/test/resources/ehcache-107-serializer.xml +++ b/ehcache-107/src/test/resources/ehcache-107-serializer.xml @@ -1,10 +1,6 @@ + xmlns:ehcache='http://www.ehcache.org/v3'> org.ehcache.impl.serialization.CompactJavaSerializer @@ -27,4 +23,4 @@ 1 - \ No newline at end of file + diff --git a/107/src/test/resources/ehcache-107-stats.xml b/ehcache-107/src/test/resources/ehcache-107-stats.xml similarity index 72% rename from 107/src/test/resources/ehcache-107-stats.xml rename to ehcache-107/src/test/resources/ehcache-107-stats.xml index 0c58cadef3..909732857e 100644 --- a/107/src/test/resources/ehcache-107-stats.xml +++ b/ehcache-107/src/test/resources/ehcache-107-stats.xml @@ -1,9 +1,6 @@ + xmlns:jsr107='http://www.ehcache.org/v3/jsr107'> @@ -35,4 +32,4 @@ 10 - \ No newline at end of file + diff --git a/107/src/test/resources/ehcache-107-types.xml b/ehcache-107/src/test/resources/ehcache-107-types.xml similarity index 100% rename from 107/src/test/resources/ehcache-107-types.xml rename to ehcache-107/src/test/resources/ehcache-107-types.xml diff --git a/107/src/test/resources/ehcache-107.xml b/ehcache-107/src/test/resources/ehcache-107.xml similarity index 60% rename from 107/src/test/resources/ehcache-107.xml rename to ehcache-107/src/test/resources/ehcache-107.xml index f018320e1e..cd32783126 100644 --- a/107/src/test/resources/ehcache-107.xml +++ b/ehcache-107/src/test/resources/ehcache-107.xml @@ -1,9 +1,6 @@ + xmlns:jsr107='http://www.ehcache.org/v3/jsr107'> @@ -21,4 +18,4 @@ 20 - \ No newline at end of file + diff --git a/107/src/test/resources/ehcache-example.xml b/ehcache-107/src/test/resources/ehcache-example.xml similarity index 100% rename from 107/src/test/resources/ehcache-example.xml rename to ehcache-107/src/test/resources/ehcache-example.xml diff --git a/107/src/test/resources/ehcache-loader-writer-107-load-atomics.xml b/ehcache-107/src/test/resources/ehcache-loader-writer-107-load-atomics.xml similarity index 89% rename from 107/src/test/resources/ehcache-loader-writer-107-load-atomics.xml rename to ehcache-107/src/test/resources/ehcache-loader-writer-107-load-atomics.xml index 3cf28037f4..b8febe0ce1 100644 --- a/107/src/test/resources/ehcache-loader-writer-107-load-atomics.xml +++ b/ehcache-107/src/test/resources/ehcache-loader-writer-107-load-atomics.xml @@ -1,5 +1,4 @@ diff --git a/107/src/test/resources/ehcache-loader-writer-107.xml b/ehcache-107/src/test/resources/ehcache-loader-writer-107.xml similarity index 88% rename from 107/src/test/resources/ehcache-loader-writer-107.xml rename to ehcache-107/src/test/resources/ehcache-loader-writer-107.xml index ee84dd73ba..01a332c67b 100644 --- a/107/src/test/resources/ehcache-loader-writer-107.xml +++ b/ehcache-107/src/test/resources/ehcache-loader-writer-107.xml @@ -1,5 +1,4 @@ diff --git a/107/src/test/resources/org/ehcache/docs/ehcache-107-mbeans-cache-manager-config.xml b/ehcache-107/src/test/resources/org/ehcache/docs/ehcache-107-mbeans-cache-manager-config.xml similarity index 81% rename from 107/src/test/resources/org/ehcache/docs/ehcache-107-mbeans-cache-manager-config.xml rename to ehcache-107/src/test/resources/org/ehcache/docs/ehcache-107-mbeans-cache-manager-config.xml index bebec36a39..5bf3687b4e 100644 --- a/107/src/test/resources/org/ehcache/docs/ehcache-107-mbeans-cache-manager-config.xml +++ b/ehcache-107/src/test/resources/org/ehcache/docs/ehcache-107-mbeans-cache-manager-config.xml @@ -15,12 +15,8 @@ --> + xmlns:jsr107='http://www.ehcache.org/v3/jsr107'> diff --git a/107/src/test/resources/org/ehcache/docs/ehcache-jsr107-cache-through.xml b/ehcache-107/src/test/resources/org/ehcache/docs/ehcache-jsr107-cache-through.xml similarity index 93% rename from 107/src/test/resources/org/ehcache/docs/ehcache-jsr107-cache-through.xml rename to ehcache-107/src/test/resources/org/ehcache/docs/ehcache-jsr107-cache-through.xml index 1061bc0b6d..f587c97f35 100644 --- a/107/src/test/resources/org/ehcache/docs/ehcache-jsr107-cache-through.xml +++ b/ehcache-107/src/test/resources/org/ehcache/docs/ehcache-jsr107-cache-through.xml @@ -15,7 +15,6 @@ --> diff --git a/107/src/test/resources/org/ehcache/docs/ehcache-jsr107-config.xml b/ehcache-107/src/test/resources/org/ehcache/docs/ehcache-jsr107-config.xml similarity index 57% rename from 107/src/test/resources/org/ehcache/docs/ehcache-jsr107-config.xml rename to ehcache-107/src/test/resources/org/ehcache/docs/ehcache-jsr107-config.xml index b52a370326..2a38ad11f9 100644 --- a/107/src/test/resources/org/ehcache/docs/ehcache-jsr107-config.xml +++ b/ehcache-107/src/test/resources/org/ehcache/docs/ehcache-jsr107-config.xml @@ -1,8 +1,4 @@ - + java.lang.Long diff --git a/107/src/test/resources/org/ehcache/docs/ehcache-jsr107-template-override.xml b/ehcache-107/src/test/resources/org/ehcache/docs/ehcache-jsr107-template-override.xml similarity index 85% rename from 107/src/test/resources/org/ehcache/docs/ehcache-jsr107-template-override.xml rename to ehcache-107/src/test/resources/org/ehcache/docs/ehcache-jsr107-template-override.xml index c50dc33616..1e2599caf6 100644 --- a/107/src/test/resources/org/ehcache/docs/ehcache-jsr107-template-override.xml +++ b/ehcache-107/src/test/resources/org/ehcache/docs/ehcache-jsr107-template-override.xml @@ -1,10 +1,6 @@ + xmlns:jsr107='http://www.ehcache.org/v3/jsr107'> diff --git a/107/src/test/resources/org/ehcache/docs/public-xsds-location.xml b/ehcache-107/src/test/resources/org/ehcache/docs/public-xsds-location.xml similarity index 100% rename from 107/src/test/resources/org/ehcache/docs/public-xsds-location.xml rename to ehcache-107/src/test/resources/org/ehcache/docs/public-xsds-location.xml diff --git a/clustered/integration-test/src/test/java/org/ehcache/clustered/ClusteredTestsTest.java b/ehcache-api/build.gradle similarity index 57% rename from clustered/integration-test/src/test/java/org/ehcache/clustered/ClusteredTestsTest.java rename to ehcache-api/build.gradle index 7acfa1dddd..c32a4fa5e2 100644 --- a/clustered/integration-test/src/test/java/org/ehcache/clustered/ClusteredTestsTest.java +++ b/ehcache-api/build.gradle @@ -13,21 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.ehcache.clustered; -import org.junit.Test; - -import java.io.File; +plugins { + id 'org.ehcache.build.internal-module' +} -import static org.assertj.core.api.Assertions.assertThat; +publishing.publications.withType(MavenPublication) { + pom { + name = 'Ehcache 3 API module' + description = 'The API module of Ehcache 3' + } +} -public class ClusteredTestsTest extends ClusteredTests { +checkstyle { + configFile = file("$projectDir/config/checkstyle.xml") +} - @Test - public void test() { - String value = System.getProperty("kitInstallationPath"); - assertThat(new File(value)).exists(); - assertThat(new File(value)).isAbsolute(); - assertThat(new File(value).toString()).doesNotContain(".."); - } +jar { + bnd( + 'Export-Package': 'org.ehcache.*', + 'Import-Package': '*' + ) } + +//TODO : Baseline task is broken pending Gradle artifact resolution fixes +//check.dependsOn(baseline) diff --git a/api/config/checkstyle-suppressions.xml b/ehcache-api/config/checkstyle-suppressions.xml similarity index 100% rename from api/config/checkstyle-suppressions.xml rename to ehcache-api/config/checkstyle-suppressions.xml diff --git a/api/config/checkstyle.xml b/ehcache-api/config/checkstyle.xml similarity index 80% rename from api/config/checkstyle.xml rename to ehcache-api/config/checkstyle.xml index 58acf9aadf..945a03c384 100644 --- a/api/config/checkstyle.xml +++ b/ehcache-api/config/checkstyle.xml @@ -24,13 +24,6 @@ - - - - - - - @@ -41,13 +34,17 @@ - + + + + + + - diff --git a/api/src/main/java/org/ehcache/Cache.java b/ehcache-api/src/main/java/org/ehcache/Cache.java similarity index 97% rename from api/src/main/java/org/ehcache/Cache.java rename to ehcache-api/src/main/java/org/ehcache/Cache.java index 011136b6c9..a904dce76d 100644 --- a/api/src/main/java/org/ehcache/Cache.java +++ b/ehcache-api/src/main/java/org/ehcache/Cache.java @@ -22,6 +22,7 @@ import org.ehcache.spi.loaderwriter.CacheWritingException; import org.ehcache.spi.loaderwriter.CacheLoaderWriter; +import java.util.Iterator; import java.util.Map; import java.util.Set; @@ -254,6 +255,16 @@ public interface Cache extends Iterable> { */ CacheRuntimeConfiguration getRuntimeConfiguration(); + /** + * Returns an iterator over the cache entries. + *

+ * Due to the interactions of the cache and iterator contracts it is possible + * for iteration to return expired entries. + * + * @return an Iterator over the cache entries. + */ + @Override + Iterator> iterator(); /** * A mapping of key to value held in a {@link Cache}. diff --git a/api/src/main/java/org/ehcache/CacheIterationException.java b/ehcache-api/src/main/java/org/ehcache/CacheIterationException.java similarity index 100% rename from api/src/main/java/org/ehcache/CacheIterationException.java rename to ehcache-api/src/main/java/org/ehcache/CacheIterationException.java diff --git a/api/src/main/java/org/ehcache/CacheManager.java b/ehcache-api/src/main/java/org/ehcache/CacheManager.java similarity index 100% rename from api/src/main/java/org/ehcache/CacheManager.java rename to ehcache-api/src/main/java/org/ehcache/CacheManager.java diff --git a/api/src/main/java/org/ehcache/CachePersistenceException.java b/ehcache-api/src/main/java/org/ehcache/CachePersistenceException.java similarity index 100% rename from api/src/main/java/org/ehcache/CachePersistenceException.java rename to ehcache-api/src/main/java/org/ehcache/CachePersistenceException.java diff --git a/api/src/main/java/org/ehcache/PersistentCacheManager.java b/ehcache-api/src/main/java/org/ehcache/PersistentCacheManager.java similarity index 100% rename from api/src/main/java/org/ehcache/PersistentCacheManager.java rename to ehcache-api/src/main/java/org/ehcache/PersistentCacheManager.java diff --git a/api/src/main/java/org/ehcache/PersistentUserManagedCache.java b/ehcache-api/src/main/java/org/ehcache/PersistentUserManagedCache.java similarity index 100% rename from api/src/main/java/org/ehcache/PersistentUserManagedCache.java rename to ehcache-api/src/main/java/org/ehcache/PersistentUserManagedCache.java diff --git a/api/src/main/java/org/ehcache/StateTransitionException.java b/ehcache-api/src/main/java/org/ehcache/StateTransitionException.java similarity index 100% rename from api/src/main/java/org/ehcache/StateTransitionException.java rename to ehcache-api/src/main/java/org/ehcache/StateTransitionException.java diff --git a/api/src/main/java/org/ehcache/Status.java b/ehcache-api/src/main/java/org/ehcache/Status.java similarity index 100% rename from api/src/main/java/org/ehcache/Status.java rename to ehcache-api/src/main/java/org/ehcache/Status.java diff --git a/api/src/main/java/org/ehcache/UserManagedCache.java b/ehcache-api/src/main/java/org/ehcache/UserManagedCache.java similarity index 100% rename from api/src/main/java/org/ehcache/UserManagedCache.java rename to ehcache-api/src/main/java/org/ehcache/UserManagedCache.java diff --git a/api/src/main/java/org/ehcache/ValueSupplier.java b/ehcache-api/src/main/java/org/ehcache/ValueSupplier.java similarity index 100% rename from api/src/main/java/org/ehcache/ValueSupplier.java rename to ehcache-api/src/main/java/org/ehcache/ValueSupplier.java diff --git a/api/src/main/java/org/ehcache/config/Builder.java b/ehcache-api/src/main/java/org/ehcache/config/Builder.java similarity index 100% rename from api/src/main/java/org/ehcache/config/Builder.java rename to ehcache-api/src/main/java/org/ehcache/config/Builder.java diff --git a/api/src/main/java/org/ehcache/config/CacheConfiguration.java b/ehcache-api/src/main/java/org/ehcache/config/CacheConfiguration.java similarity index 83% rename from api/src/main/java/org/ehcache/config/CacheConfiguration.java rename to ehcache-api/src/main/java/org/ehcache/config/CacheConfiguration.java index a00f258d72..ab808dbe7b 100644 --- a/api/src/main/java/org/ehcache/config/CacheConfiguration.java +++ b/ehcache-api/src/main/java/org/ehcache/config/CacheConfiguration.java @@ -40,7 +40,7 @@ public interface CacheConfiguration { * * @return service configurations */ - Collection> getServiceConfigurations(); + Collection> getServiceConfigurations(); /** * The key type for the {@link Cache}. @@ -112,4 +112,17 @@ public interface CacheConfiguration { */ ResourcePools getResourcePools(); + /** + * Create a builder seeded with this configuration. + *

+ * The default implementation throws {@code UnsupportedOperationException} to indicate that configuration derivation + * is not supported. + * + * @see FluentConfigurationBuilder + * @return a configuration builder + * @throws UnsupportedOperationException if configuration derivation is not supported + */ + default FluentCacheConfigurationBuilder derive() throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } } diff --git a/api/src/main/java/org/ehcache/config/CacheRuntimeConfiguration.java b/ehcache-api/src/main/java/org/ehcache/config/CacheRuntimeConfiguration.java similarity index 92% rename from api/src/main/java/org/ehcache/config/CacheRuntimeConfiguration.java rename to ehcache-api/src/main/java/org/ehcache/config/CacheRuntimeConfiguration.java index c111d5aaf6..26d30b59e2 100644 --- a/api/src/main/java/org/ehcache/config/CacheRuntimeConfiguration.java +++ b/ehcache-api/src/main/java/org/ehcache/config/CacheRuntimeConfiguration.java @@ -21,6 +21,7 @@ import org.ehcache.event.EventOrdering; import org.ehcache.event.EventType; +import java.util.EnumSet; import java.util.Set; /** @@ -68,8 +69,10 @@ void registerCacheEventListener(CacheEventListener listene * * @throws java.lang.IllegalStateException if the listener is already registered */ - void registerCacheEventListener(CacheEventListener listener, - EventOrdering ordering, EventFiring firing, EventType eventType, EventType... eventTypes); + default void registerCacheEventListener(CacheEventListener listener, + EventOrdering ordering, EventFiring firing, EventType eventType, EventType... eventTypes) { + registerCacheEventListener(listener, ordering, firing, EnumSet.of(eventType, eventTypes)); + } /** * Deregisters a previously registered {@link org.ehcache.event.CacheEventListener CacheEventListener} instance. diff --git a/api/src/main/java/org/ehcache/config/Configuration.java b/ehcache-api/src/main/java/org/ehcache/config/Configuration.java similarity index 78% rename from api/src/main/java/org/ehcache/config/Configuration.java rename to ehcache-api/src/main/java/org/ehcache/config/Configuration.java index d61e335e77..93a1067085 100644 --- a/api/src/main/java/org/ehcache/config/Configuration.java +++ b/ehcache-api/src/main/java/org/ehcache/config/Configuration.java @@ -46,7 +46,7 @@ public interface Configuration { * * @return a collection of service creations configurations */ - Collection> getServiceCreationConfigurations(); + Collection> getServiceCreationConfigurations(); /** * The {@link ClassLoader} for the {@link org.ehcache.CacheManager CacheManager}. @@ -59,4 +59,18 @@ public interface Configuration { * @return the cache manager {@code ClassLoader} */ ClassLoader getClassLoader(); + + /** + * Creates a builder seeded with this configuration. + *

+ * The default implementation throws {@code UnsupportedOperationException} to indicate that configuration derivation + * is not supported. + * + * @see FluentConfigurationBuilder + * @return a configuration builder + * @throws UnsupportedOperationException if configuration derivation is not supported + */ + default FluentConfigurationBuilder derive() { + throw new UnsupportedOperationException(); + } } diff --git a/api/src/main/java/org/ehcache/config/Eviction.java b/ehcache-api/src/main/java/org/ehcache/config/Eviction.java similarity index 100% rename from api/src/main/java/org/ehcache/config/Eviction.java rename to ehcache-api/src/main/java/org/ehcache/config/Eviction.java diff --git a/api/src/main/java/org/ehcache/config/EvictionAdvisor.java b/ehcache-api/src/main/java/org/ehcache/config/EvictionAdvisor.java similarity index 100% rename from api/src/main/java/org/ehcache/config/EvictionAdvisor.java rename to ehcache-api/src/main/java/org/ehcache/config/EvictionAdvisor.java diff --git a/ehcache-api/src/main/java/org/ehcache/config/FluentCacheConfigurationBuilder.java b/ehcache-api/src/main/java/org/ehcache/config/FluentCacheConfigurationBuilder.java new file mode 100644 index 0000000000..2bd70992fa --- /dev/null +++ b/ehcache-api/src/main/java/org/ehcache/config/FluentCacheConfigurationBuilder.java @@ -0,0 +1,529 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.config; + +import org.ehcache.expiry.ExpiryPolicy; +import org.ehcache.spi.copy.Copier; +import org.ehcache.spi.loaderwriter.CacheLoaderWriter; +import org.ehcache.spi.resilience.ResilienceStrategy; +import org.ehcache.spi.serialization.Serializer; +import org.ehcache.spi.service.ServiceConfiguration; + +import java.util.Collection; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; + +/** + * A fluent builder of {@link CacheConfiguration} instances. + * + * @param cache key type + * @param cache value type + * @param builder sub-type + */ +public interface FluentCacheConfigurationBuilder> extends Builder> { + + /** + * Builds a new {@link CacheConfiguration}. + * + * @return a new {@code CacheConfiguration} + */ + CacheConfiguration build(); + + /** + * Return the unique service configuration of the given type. + *

+ * If there are multiple configuration instances of this type (or subtypes) then an {@code IllegalArgumentException} + * will be thrown. + * + * @param configurationType desired configuration type + * @param configuration type + * @return the service configuration of the given type; @{code null} if there is no service configuration of the given type + * @throws IllegalArgumentException if there are multiple instances of this type + * + * @see #getServices(Class) + * @see #withService(ServiceConfiguration) + * @see #withService(Builder) + * @see #withoutServices(Class) + * @see #withoutServices(Class, Predicate) + * @see #updateServices(Class, UnaryOperator) + */ + default > C getService(Class configurationType) throws IllegalArgumentException { + Collection services = getServices(configurationType); + + switch (services.size()) { + case 0: + return null; + case 1: + return services.iterator().next(); + default: + throw new IllegalArgumentException(configurationType + " does not identify a unique service configuration: " + services); + } + } + + /** + * Returns all the service configurations of the given type. + * + * @param configurationType desired configuration type + * @param configuration type + * @return all services of this type + * + * @see #getService(Class) + * @see #withService(ServiceConfiguration) + * @see #withService(Builder) + * @see #withoutServices(Class) + * @see #withoutServices(Class, Predicate) + * @see #updateServices(Class, UnaryOperator) + */ + > Collection getServices(Class configurationType); + + /** + * Adds a service configuration to this configuration. + *

+ * This will remove any existing service configurations that are incompatible with the supplied one. + * This removal is equivalent to the following: + *

{@code configurations.removeIf(
+   *     existing -> !config.compatibleWith(existing) || !existing.compatibleWith(config)
+   * );}
+ * + * @param config service configuration + * @return an updated builder + * @see ServiceConfiguration#compatibleWith(ServiceConfiguration) + * + * @see #getService(Class) + * @see #getServices(Class) + * @see #withService(Builder) + * @see #withoutServices(Class) + * @see #withoutServices(Class, Predicate) + * @see #updateServices(Class, UnaryOperator) + */ + B withService(ServiceConfiguration config); + + /** + * Adds a service configuration built by the given builder to this configuration. + *

+ * This will remove any existing configurations that are incompatible with the configuration returned by + * {@code builder.build()}. + * + * @param builder service configuration builder + * @return an updated builder + * @see #withService(ServiceConfiguration) + * + * @see #getService(Class) + * @see #getServices(Class) + * @see #withService(ServiceConfiguration) + * @see #withoutServices(Class) + * @see #withoutServices(Class, Predicate) + * @see #updateServices(Class, UnaryOperator) + */ + default B withService(Builder> builder) { + return withService(builder.build()); + } + + /** + * Removes all service configurations of the given type from this configuration. + * + * @param clazz service configuration type + * @return an updated builder + * + * @see #getService(Class) + * @see #getServices(Class) + * @see #withService(ServiceConfiguration) + * @see #withService(Builder) + * @see #withoutServices(Class, Predicate) + * @see #updateServices(Class, UnaryOperator) + */ + default B withoutServices(Class> clazz) { + return withoutServices(clazz, c -> true); + } + + /** + * Removes all service configurations of the given type that pass the predicate. + * + * @param clazz service configuration type + * @param predicate predicate controlling removal + * @param configuration type + * @return an updated builder + * + * @see #getService(Class) + * @see #getServices(Class) + * @see #withService(ServiceConfiguration) + * @see #withService(Builder) + * @see #withoutServices(Class) + * @see #updateServices(Class, UnaryOperator) + */ + > B withoutServices(Class clazz, Predicate predicate); + + /** + * Updates all service configurations of the given type. + *

+ * For each existing service creation configuration instance that is assignment compatible with {@code clazz} the + * following process is performed: + *

    + *
  1. The configuration is converted to its detached representations using the + * {@link ServiceConfiguration#derive()} method.
  2. + *
  3. The detached representation is transformed using the {@code update} unary operator.
  4. + *
  5. A new configuration is generated by passing the transformed detached representation to the existing + * configurations {@link ServiceConfiguration#build(Object)} method.
  6. + *
  7. The new configuration is added to the builders service configuration set.
  8. + *
+ * If there are no service creation configurations assignment compatible with {@code clazz} then an + * {@code IllegalStateException} will be thrown. + * + * @param clazz service configuration concrete type + * @param update configuration mutation function + * @param configuration detached representation type + * @param service configuration type + * @return an updated builder + * @throws IllegalStateException if no matching service configurations exist + * + * @see #getService(Class) + * @see #getServices(Class) + * @see #withService(ServiceConfiguration) + * @see #withService(Builder) + * @see #withoutServices(Class) + * @see #withoutServices(Class, Predicate) + */ + > B updateServices(Class clazz, UnaryOperator update) throws IllegalStateException; + + /** + * Sets the {@link EvictionAdvisor} in the returned builder. + * + * @param evictionAdvisor the eviction advisor to be used + * @return a new builder with the added eviction advisor + * + * @see Eviction#NO_ADVICE + */ + B withEvictionAdvisor(final EvictionAdvisor evictionAdvisor); + + /** + * Sets the {@link ClassLoader} in the returned builder. + *

+ * The {@link ClassLoader} will be used for resolving all non Ehcache types. + * + * @param classLoader the class loader to use + * @return a new builder with the added class loader + * + * @see #withDefaultClassLoader() + */ + B withClassLoader(ClassLoader classLoader); + + /** + * Removes any previously installed custom class loader + * + * @return a new build using the default class loader + * + * @see #withClassLoader(ClassLoader) + */ + B withDefaultClassLoader(); + + /** + * Sets the {@link ResourcePools} in the returned builder. + *

+ * {@link ResourcePools} is what determines the tiering of a cache. + * + * @param resourcePools the resource pools to use + * @return a new builder with the added resource pools + * + * @see #withResourcePools(Builder) + * @see #updateResourcePools(UnaryOperator) + */ + B withResourcePools(ResourcePools resourcePools); + + /** + * Convenience method to set the {@link ResourcePools} through a {@link Builder}. + * + * @param builder the builder providing the resource pool + * @return a new builder with the added resource pools + * + * @see #withResourcePools(ResourcePools) + * @see #updateResourcePools(UnaryOperator) + */ + default B withResourcePools(Builder builder) { + return withResourcePools(builder.build()); + } + + /** + * Updates the configured resource pools. + * + * @param update resource pool update operation + * @return a new build with updated resource pools + * + * @see #withResourcePools(ResourcePools) + * @see #withResourcePools(Builder) + */ + B updateResourcePools(UnaryOperator update); + + /** + * Sets the {@link ExpiryPolicy} configuration in the returned builder. + *

+ * {@code ExpiryPolicy} is what controls data freshness in a cache. + * + * @param expiry the expiry to use + * @return a new builder with the added expiry + * + * @see ExpiryPolicy#NO_EXPIRY + */ + B withExpiry(ExpiryPolicy expiry); + + /** + * Sets the {@link CacheLoaderWriter} in the returned builder. + *

+ * Configuration of a {@link CacheLoaderWriter} is what enables cache-through patterns. + * + * @param loaderWriter the loaderwriter to use + * @return a new builder with the added loaderwriter configuration + * + * @see #withLoaderWriter(Class, Object...) + * @see #withoutLoaderWriter() + */ + B withLoaderWriter(CacheLoaderWriter loaderWriter); + + /** + * Sets the {@link CacheLoaderWriter} (using a class and constructor arguments) in the returned builder. + *

+ * Configuration of a {@link CacheLoaderWriter} is what enables cache-through patterns. + * + * @param loaderWriterClass the loaderwrite class + * @param arguments optional constructor arguments + * @return a new builder with the added loaderwriter configuration + * + * @see #withLoaderWriter(CacheLoaderWriter) + * @see #withoutLoaderWriter() + */ + B withLoaderWriter(Class> loaderWriterClass, Object... arguments); + + /** + * Removes any configured loader-writer. + * + * @return a new build with no configured loader-writer + * + * @see #withLoaderWriter(CacheLoaderWriter) + * @see #withLoaderWriter(Class, Object...) + */ + B withoutLoaderWriter(); + + /** + * Sets the {@link ResilienceStrategy} in the returned builder. + * + * @param resilienceStrategy the resilience strategy to use + * @return a new builder with the added resilience strategy configuration + * + * @see #withResilienceStrategy(Class, Object...) + * @see #withDefaultResilienceStrategy() + */ + B withResilienceStrategy(ResilienceStrategy resilienceStrategy); + + /** + * Sets the {@link ResilienceStrategy} (using a class and constructor arguments) in the returned builder. + * + * @param resilienceStrategyClass the resilience strategy class + * @param arguments optional constructor arguments + * @return a new builder with the added resilience strategy configuration + * + * @see #withResilienceStrategy(ResilienceStrategy) + * @see #withDefaultResilienceStrategy() + */ + @SuppressWarnings("rawtypes") + B withResilienceStrategy(Class resilienceStrategyClass, Object... arguments); + + /** + * Restores configuration of the implementations default resilience strategy. + * + * @return a new builder using the default resilience strategy + * + * @see #withResilienceStrategy(ResilienceStrategy) + * @see #withResilienceStrategy(Class, Object...) + */ + B withDefaultResilienceStrategy(); + + /** + * Adds by-value semantics using the cache key serializer for the key on heap. + *

+ * {@link Copier}s are what enable control of by-reference / by-value semantics for on-heap tier. + * + * @return a new builder with the added key copier + * + * @see #withKeyCopier(Copier) + * @see #withKeyCopier(Class) + * @see #withoutKeyCopier() + */ + B withKeySerializingCopier(); + + /** + * Adds by-value semantics using the cache value serializer for the value on heap. + *

+ * {@link Copier}s are what enable control of by-reference / by-value semantics for on-heap tier. + * + * @return a new builder with the added value copier + * + * @see #withValueCopier(Copier) + * @see #withValueCopier(Class) + * @see #withoutValueCopier() + */ + B withValueSerializingCopier(); + + /** + * Adds by-value semantics using the provided {@link Copier} for the key on heap. + *

+ * {@link Copier}s are what enable control of by-reference / by-value semantics for on-heap tier. + * + * @param keyCopier the key copier to use + * @return a new builder with the added key copier + * + * @see #withKeySerializingCopier() + * @see #withKeyCopier(Class) + * @see #withoutKeyCopier() + */ + B withKeyCopier(Copier keyCopier); + + /** + * Adds by-value semantics using the provided {@link Copier} class for the key on heap. + *

+ * {@link Copier}s are what enable control of by-reference / by-value semantics for on-heap tier. + * + * @param keyCopierClass the key copier class to use + * @return a new builder with the added key copier + * + * @see #withKeySerializingCopier() + * @see #withKeyCopier(Copier) + * @see #withoutKeyCopier() + */ + B withKeyCopier(Class> keyCopierClass); + + /** + * Removes any configured {@link Copier} for keys on heap. + * + * @return a new builder without a key copier + * + * @see #withKeySerializingCopier() + * @see #withKeyCopier(Copier) + * @see #withKeyCopier(Class) + */ + B withoutKeyCopier(); + + /** + * Adds by-value semantics using the provided {@link Copier} for the value on heap. + *

+ * {@link Copier}s are what enable control of by-reference / by-value semantics for on-heap tier. + * + * @param valueCopier the value copier to use + * @return a new builder with the added value copier + * + * @see #withValueSerializingCopier() + * @see #withValueCopier(Class) + * @see #withoutValueCopier() + */ + B withValueCopier(Copier valueCopier); + + /** + * Adds by-value semantics using the provided {@link Copier} class for the value on heap. + *

+ * {@link Copier}s are what enable control of by-reference / by-value semantics for on-heap tier. + * + * @param valueCopierClass the value copier class to use + * @return a new builder with the added value copier + * + * @see #withValueSerializingCopier() + * @see #withValueCopier(Copier) + * @see #withoutValueCopier() + */ + B withValueCopier(Class> valueCopierClass); + + /** + * Removes any configured {@link Copier} for values on heap. + * + * @return a new builder without a value copier + * + * @see #withValueSerializingCopier() + * @see #withValueCopier(Copier) + * @see #withValueCopier(Class) + */ + B withoutValueCopier(); + + /** + * Sets the {@link Serializer} for cache keys in the returned builder. + *

+ * {@link Serializer}s are what enables cache storage beyond the heap tier. + * + * @param keySerializer the key serializer to use + * @return a new builder with the added key serializer + * + * @see #withKeySerializer(Class) + * @see #withDefaultKeySerializer() + */ + B withKeySerializer(Serializer keySerializer); + + /** + * Sets the {@link Serializer} class for cache keys in the returned builder. + *

+ * {@link Serializer}s are what enables cache storage beyond the heap tier. + * + * @param keySerializerClass the key serializer to use + * @return a new builder with the added key serializer + * + * @see #withKeySerializer(Serializer) + * @see #withDefaultKeySerializer() + */ + B withKeySerializer(Class> keySerializerClass); + + /** + * Removes any explicitly configured {@link Serializer} for cache keys. + * + * @return a new builder with no configured key serializer + * + * @see #withKeySerializer(Serializer) + * @see #withKeySerializer(Class) + */ + B withDefaultKeySerializer(); + + /** + * Sets the {@link Serializer} for cache values in the returned builder. + *

+ * {@link Serializer}s are what enables cache storage beyond the heap tier. + * + * @param valueSerializer the key serializer to use + * @return a new builder with the added value serializer + * + * @see #withValueSerializer(Class) + * @see #withDefaultValueSerializer() + */ + B withValueSerializer(Serializer valueSerializer); + + /** + * Sets the {@link Serializer} class for cache values in the returned builder. + *

+ * {@link Serializer}s are what enables cache storage beyond the heap tier. + * + * @param valueSerializerClass the key serializer to use + * @return a new builder with the added value serializer + * + * @see #withValueSerializer(Serializer) + * @see #withDefaultValueSerializer() + */ + B withValueSerializer(Class> valueSerializerClass); + + /** + * Removes any explicitly configured {@link Serializer} for cache values. + * + * @return a new builder with no configured value serializer + * + * @see #withValueSerializer(Serializer) + * @see #withValueSerializer(Class) + */ + B withDefaultValueSerializer(); +} + diff --git a/ehcache-api/src/main/java/org/ehcache/config/FluentConfigurationBuilder.java b/ehcache-api/src/main/java/org/ehcache/config/FluentConfigurationBuilder.java new file mode 100644 index 0000000000..0b2495f111 --- /dev/null +++ b/ehcache-api/src/main/java/org/ehcache/config/FluentConfigurationBuilder.java @@ -0,0 +1,327 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.config; + +import org.ehcache.spi.service.ServiceCreationConfiguration; + +import java.util.Collection; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; + +/** + * A fluent builder of {@link Configuration} instances. + * + * @param builder sub-type + */ +public interface FluentConfigurationBuilder> extends Builder { + + /** + * Return the cache configuration for the given alias. + * + * @param alias cache alias + * @return associated cache configuration + * + * @see #withCache(String, CacheConfiguration) + * @see #withCache(String, Builder) + * @see #updateCache(String, UnaryOperator) + * @see #withoutCache(String) + * @see #updateCaches(UnaryOperator) + */ + CacheConfiguration getCache(String alias); + + /** + * Adds the given cache to this configuration. + *

+ * This will overwrite any existing configuration for the cache with this alias. + * + * @param alias cache alias + * @param config cache configuration + * @return an updated builder + * + * @see #getCache(String) + * @see #withCache(String, Builder) + * @see #updateCache(String, UnaryOperator) + * @see #withoutCache(String) + * @see #updateCaches(UnaryOperator) + */ + B withCache(String alias, CacheConfiguration config); + + /** + * Adds the cache configuration built by a builder to this configuration. + *

+ * This will overwrite any existing configuration for the cache with this alias. + * + * @param alias cache alias + * @param builder cache configuration builder + * @return an updated builder + * + * @see #getCache(String) + * @see #withCache(String, Builder) + * @see #updateCache(String, UnaryOperator) + * @see #withoutCache(String) + * @see #updateCaches(UnaryOperator) + */ + default B withCache(String alias, Builder> builder) { + return withCache(alias, builder.build()); + } + + /** + * Removes the given cache from this configuration. + * + * @param alias cache alias + * @return an updated builder + * + * @see #getCache(String) + * @see #withCache(String, CacheConfiguration) + * @see #withCache(String, Builder) + * @see #updateCache(String, UnaryOperator) + * @see #updateCaches(UnaryOperator) + */ + B withoutCache(String alias); + + /** + * Updates the configuration of the identified cache. + *

+ * If a cache exists for the given alias then the following process is performed: + *

    + *
  1. The configuration is converted to a builder seeded with that configuration. + *
  2. The builder is then transformed using the {@code update} unary operator.
  3. + *
  4. A new configuration is generated by calling {@code build()} on the resultant builder.
  5. + *
  6. The new configuration is associated with the given alias.
  7. + *
+ * If there is no cache associated with the given {@code alias} then an {@code IllegalStateException} will be thrown. + * + * @param alias cache alias + * @param update configuration mutation function + * @return an updated builder + * @throws IllegalArgumentException if no cache configuration exists for {@code alias} + * + * @see #getCache(String) + * @see #withCache(String, CacheConfiguration) + * @see #withCache(String, Builder) + * @see #withoutCache(String) + * @see #updateCaches(UnaryOperator) + */ + B updateCache(String alias, UnaryOperator> update) throws IllegalArgumentException; + + /** + * Updates the configuration of the all caches. + *

+ * For every existing cache the following process is performed: + *

    + *
  1. The configuration is converted to a builder seeded with that configuration. + *
  2. The builder is then transformed using the {@code update} unary operator.
  3. + *
  4. A new configuration is generated by calling {@code build()} on the resultant builder.
  5. + *
  6. The new configuration is associated with the given alias.
  7. + *
+ * + * @param update configuration mutation function + * @return an updated builder + * + * @see #getCache(String) + * @see #withCache(String, CacheConfiguration) + * @see #withCache(String, Builder) + * @see #updateCache(String, UnaryOperator) + * @see #withoutCache(String) + */ + B updateCaches(UnaryOperator> update); + + /** + * Return the unique service creation configuration of the given type. + *

+ * If there are multiple configuration instances of this type (or subtypes) then an {@code IllegalArgumentException} + * will be thrown. + * + * @param configurationType desired configuration type + * @param configuration type + * @return the given configuration type + * @throws IllegalArgumentException if there are multiple instances of this type + * + * @see #getServices(Class) + * @see #withService(ServiceCreationConfiguration) + * @see #withService(Builder) + * @see #withoutServices(Class) + * @see #withoutServices(Class, Predicate) + * @see #updateServices(Class, UnaryOperator) + */ + default > C getService(Class configurationType) throws IllegalArgumentException { + Collection services = getServices(configurationType); + + switch (services.size()) { + case 0: + return null; + case 1: + return services.iterator().next(); + default: + throw new IllegalArgumentException(configurationType + " does not identify a unique service configuration: " + services); + } + } + + /** + * Return the service creation configurations of the given type. + * + * @param configurationType desired configuration type + * @param configuration type + * @return all services of this type + * + * @see #getService(Class) + * @see #withService(ServiceCreationConfiguration) + * @see #withService(Builder) + * @see #withoutServices(Class) + * @see #withoutServices(Class, Predicate) + * @see #updateServices(Class, UnaryOperator) + */ + > Collection getServices(Class configurationType); + + /** + * Adds a service creation configuration to this configuration. + *

+ * This will remove any existing service creation configurations that are incompatible with the supplied one. + * This removal is equivalent to the following: + *

{@code configurations.removeIf(
+   *     existing -> !config.compatibleWith(existing) || !existing.compatibleWith(config)
+   * );}
+ * + * @param config service creation configuration + * @return an updated builder + * @see ServiceCreationConfiguration#compatibleWith(ServiceCreationConfiguration) + * + * @see #getService(Class) + * @see #getServices(Class) + * @see #withService(Builder) + * @see #withoutServices(Class) + * @see #withoutServices(Class, Predicate) + * @see #updateServices(Class, UnaryOperator) + */ + B withService(ServiceCreationConfiguration config); + + /** + * Adds a service creation configuration built by the given builder to this configuration. + *

+ * This will remove any existing configurations that are incompatible with the supplied one. + * + * @param builder service creation configuration builder + * @return an updated builder + * + * @see #getService(Class) + * @see #getServices(Class) + * @see #withService(ServiceCreationConfiguration) + * @see #withoutServices(Class) + * @see #withoutServices(Class, Predicate) + * @see #updateServices(Class, UnaryOperator) + */ + default B withService(Builder> builder) { + return withService(builder.build()); + } + + /** + * Removes all service creation configurations of the given type from this configuration. + * + * @param clazz service configuration type + * @return an updated builder + * + * @see #getService(Class) + * @see #getServices(Class) + * @see #withService(ServiceCreationConfiguration) + * @see #withService(Builder) + * @see #withoutServices(Class, Predicate) + * @see #updateServices(Class, UnaryOperator) + */ + default B withoutServices(Class> clazz) { + return withoutServices(clazz, c -> true); + } + + /** + * Removes all service creation configurations of the given type that pass the predicate. + * + * @param clazz service configuration type + * @param predicate predicate controlling removal + * @param configuration type + * @return an updated builder + * + * @see #getService(Class) + * @see #getServices(Class) + * @see #withService(ServiceCreationConfiguration) + * @see #withService(Builder) + * @see #withoutServices(Class) + * @see #updateServices(Class, UnaryOperator) + */ + > B withoutServices(Class clazz, Predicate predicate); + + /** + * Updates all service creation configurations of the given type. + *

+ * For each existing service creation configuration instance that is assignment compatible with {@code clazz} the + * following process is performed: + *

    + *
  1. The configuration is converted to its detached representations using the + * {@link ServiceCreationConfiguration#derive()} method.
  2. + *
  3. The detached representation is transformed using the {@code update} unary operator.
  4. + *
  5. A new configuration is generated by passing the transformed detached representation to the existing + * configurations {@link ServiceCreationConfiguration#build(Object)} method.
  6. + *
  7. The new configuration is added to the builders service configuration set.
  8. + *
+ * If there are no service creation configurations assignment compatible with {@code clazz} then an + * {@code IllegalStateException} will be thrown. + * + * @param clazz service creation configuration type + * @param update configuration mutation function + * @param configuration detached representation type + * @param service configuration type + * @return an updated builder + * @throws IllegalStateException if no configurations of type {@code C} exist + * + * @see #getService(Class) + * @see #getServices(Class) + * @see #withService(ServiceCreationConfiguration) + * @see #withService(Builder) + * @see #withoutServices(Class) + * @see #withoutServices(Class, Predicate) + */ + > B updateServices(Class clazz, UnaryOperator update) throws IllegalStateException; + + /** + * Return the configured classloader instance. + * + * @return configured classloader + * + * @see #withClassLoader(ClassLoader) + * @see #withDefaultClassLoader() + */ + ClassLoader getClassLoader(); + + /** + * Sets the given class loader as the cache manager classloader + * + * @param classLoader cache manager classloader + * @return an updated builder + * + * @see #getClassLoader() + * @see #withDefaultClassLoader() + */ + B withClassLoader(ClassLoader classLoader); + + /** + * Removes any provided class loader returning to default behavior + * + * @return an updated builder + * + * @see #getClassLoader() + * @see #withClassLoader(ClassLoader) + */ + B withDefaultClassLoader(); +} diff --git a/api/src/main/java/org/ehcache/config/ResourcePool.java b/ehcache-api/src/main/java/org/ehcache/config/ResourcePool.java similarity index 100% rename from api/src/main/java/org/ehcache/config/ResourcePool.java rename to ehcache-api/src/main/java/org/ehcache/config/ResourcePool.java diff --git a/api/src/main/java/org/ehcache/config/ResourcePools.java b/ehcache-api/src/main/java/org/ehcache/config/ResourcePools.java similarity index 100% rename from api/src/main/java/org/ehcache/config/ResourcePools.java rename to ehcache-api/src/main/java/org/ehcache/config/ResourcePools.java diff --git a/api/src/main/java/org/ehcache/config/ResourceType.java b/ehcache-api/src/main/java/org/ehcache/config/ResourceType.java similarity index 100% rename from api/src/main/java/org/ehcache/config/ResourceType.java rename to ehcache-api/src/main/java/org/ehcache/config/ResourceType.java diff --git a/api/src/main/java/org/ehcache/config/ResourceUnit.java b/ehcache-api/src/main/java/org/ehcache/config/ResourceUnit.java similarity index 100% rename from api/src/main/java/org/ehcache/config/ResourceUnit.java rename to ehcache-api/src/main/java/org/ehcache/config/ResourceUnit.java diff --git a/api/src/main/java/org/ehcache/config/SizedResourcePool.java b/ehcache-api/src/main/java/org/ehcache/config/SizedResourcePool.java similarity index 100% rename from api/src/main/java/org/ehcache/config/SizedResourcePool.java rename to ehcache-api/src/main/java/org/ehcache/config/SizedResourcePool.java diff --git a/api/src/main/java/org/ehcache/config/package-info.java b/ehcache-api/src/main/java/org/ehcache/config/package-info.java similarity index 100% rename from api/src/main/java/org/ehcache/config/package-info.java rename to ehcache-api/src/main/java/org/ehcache/config/package-info.java diff --git a/api/src/main/java/org/ehcache/config/units/EntryUnit.java b/ehcache-api/src/main/java/org/ehcache/config/units/EntryUnit.java similarity index 100% rename from api/src/main/java/org/ehcache/config/units/EntryUnit.java rename to ehcache-api/src/main/java/org/ehcache/config/units/EntryUnit.java diff --git a/api/src/main/java/org/ehcache/config/units/MemoryUnit.java b/ehcache-api/src/main/java/org/ehcache/config/units/MemoryUnit.java similarity index 100% rename from api/src/main/java/org/ehcache/config/units/MemoryUnit.java rename to ehcache-api/src/main/java/org/ehcache/config/units/MemoryUnit.java diff --git a/api/src/main/java/org/ehcache/config/units/package-info.java b/ehcache-api/src/main/java/org/ehcache/config/units/package-info.java similarity index 100% rename from api/src/main/java/org/ehcache/config/units/package-info.java rename to ehcache-api/src/main/java/org/ehcache/config/units/package-info.java diff --git a/api/src/main/java/org/ehcache/event/CacheEvent.java b/ehcache-api/src/main/java/org/ehcache/event/CacheEvent.java similarity index 100% rename from api/src/main/java/org/ehcache/event/CacheEvent.java rename to ehcache-api/src/main/java/org/ehcache/event/CacheEvent.java diff --git a/api/src/main/java/org/ehcache/event/CacheEventListener.java b/ehcache-api/src/main/java/org/ehcache/event/CacheEventListener.java similarity index 100% rename from api/src/main/java/org/ehcache/event/CacheEventListener.java rename to ehcache-api/src/main/java/org/ehcache/event/CacheEventListener.java diff --git a/api/src/main/java/org/ehcache/event/EventFiring.java b/ehcache-api/src/main/java/org/ehcache/event/EventFiring.java similarity index 100% rename from api/src/main/java/org/ehcache/event/EventFiring.java rename to ehcache-api/src/main/java/org/ehcache/event/EventFiring.java diff --git a/api/src/main/java/org/ehcache/event/EventOrdering.java b/ehcache-api/src/main/java/org/ehcache/event/EventOrdering.java similarity index 100% rename from api/src/main/java/org/ehcache/event/EventOrdering.java rename to ehcache-api/src/main/java/org/ehcache/event/EventOrdering.java diff --git a/api/src/main/java/org/ehcache/event/EventType.java b/ehcache-api/src/main/java/org/ehcache/event/EventType.java similarity index 100% rename from api/src/main/java/org/ehcache/event/EventType.java rename to ehcache-api/src/main/java/org/ehcache/event/EventType.java diff --git a/api/src/main/java/org/ehcache/event/package-info.java b/ehcache-api/src/main/java/org/ehcache/event/package-info.java similarity index 100% rename from api/src/main/java/org/ehcache/event/package-info.java rename to ehcache-api/src/main/java/org/ehcache/event/package-info.java diff --git a/api/src/main/java/org/ehcache/expiry/Duration.java b/ehcache-api/src/main/java/org/ehcache/expiry/Duration.java similarity index 100% rename from api/src/main/java/org/ehcache/expiry/Duration.java rename to ehcache-api/src/main/java/org/ehcache/expiry/Duration.java diff --git a/api/src/main/java/org/ehcache/expiry/Expirations.java b/ehcache-api/src/main/java/org/ehcache/expiry/Expirations.java similarity index 100% rename from api/src/main/java/org/ehcache/expiry/Expirations.java rename to ehcache-api/src/main/java/org/ehcache/expiry/Expirations.java diff --git a/api/src/main/java/org/ehcache/expiry/Expiry.java b/ehcache-api/src/main/java/org/ehcache/expiry/Expiry.java similarity index 100% rename from api/src/main/java/org/ehcache/expiry/Expiry.java rename to ehcache-api/src/main/java/org/ehcache/expiry/Expiry.java diff --git a/api/src/main/java/org/ehcache/expiry/ExpiryPolicy.java b/ehcache-api/src/main/java/org/ehcache/expiry/ExpiryPolicy.java similarity index 98% rename from api/src/main/java/org/ehcache/expiry/ExpiryPolicy.java rename to ehcache-api/src/main/java/org/ehcache/expiry/ExpiryPolicy.java index c50c95abaa..979d319a25 100644 --- a/api/src/main/java/org/ehcache/expiry/ExpiryPolicy.java +++ b/ehcache-api/src/main/java/org/ehcache/expiry/ExpiryPolicy.java @@ -48,6 +48,11 @@ public interface ExpiryPolicy { * An {@code ExpiryPolicy} that represents a no expiration policy */ ExpiryPolicy NO_EXPIRY = new ExpiryPolicy() { + @Override + public String toString() { + return "No Expiry"; + } + @Override public Duration getExpiryForCreation(Object key, Object value) { return INFINITE; diff --git a/api/src/main/java/org/ehcache/expiry/package-info.java b/ehcache-api/src/main/java/org/ehcache/expiry/package-info.java similarity index 100% rename from api/src/main/java/org/ehcache/expiry/package-info.java rename to ehcache-api/src/main/java/org/ehcache/expiry/package-info.java diff --git a/api/src/main/java/org/ehcache/package-info.java b/ehcache-api/src/main/java/org/ehcache/package-info.java similarity index 100% rename from api/src/main/java/org/ehcache/package-info.java rename to ehcache-api/src/main/java/org/ehcache/package-info.java diff --git a/api/src/main/java/org/ehcache/spi/copy/Copier.java b/ehcache-api/src/main/java/org/ehcache/spi/copy/Copier.java similarity index 100% rename from api/src/main/java/org/ehcache/spi/copy/Copier.java rename to ehcache-api/src/main/java/org/ehcache/spi/copy/Copier.java diff --git a/api/src/main/java/org/ehcache/spi/copy/CopyProvider.java b/ehcache-api/src/main/java/org/ehcache/spi/copy/CopyProvider.java similarity index 96% rename from api/src/main/java/org/ehcache/spi/copy/CopyProvider.java rename to ehcache-api/src/main/java/org/ehcache/spi/copy/CopyProvider.java index d52f1ff636..076c0cda39 100644 --- a/api/src/main/java/org/ehcache/spi/copy/CopyProvider.java +++ b/ehcache-api/src/main/java/org/ehcache/spi/copy/CopyProvider.java @@ -43,7 +43,7 @@ public interface CopyProvider extends Service { * @param the type to copy to/from * @return a non {@code null} {@link Copier} instance */ - Copier createKeyCopier(Class clazz, Serializer serializer, ServiceConfiguration... configs); + Copier createKeyCopier(Class clazz, Serializer serializer, ServiceConfiguration... configs); /** * Creates a value {@link Copier} with the given parameters. @@ -56,7 +56,7 @@ public interface CopyProvider extends Service { * @param the type to copy to/from * @return a non {@code null} {@link Copier} instance */ - Copier createValueCopier(Class clazz, Serializer serializer, ServiceConfiguration... configs); + Copier createValueCopier(Class clazz, Serializer serializer, ServiceConfiguration... configs); /** * Releases the provided {@link Copier} instance. diff --git a/api/src/main/java/org/ehcache/spi/copy/package-info.java b/ehcache-api/src/main/java/org/ehcache/spi/copy/package-info.java similarity index 100% rename from api/src/main/java/org/ehcache/spi/copy/package-info.java rename to ehcache-api/src/main/java/org/ehcache/spi/copy/package-info.java diff --git a/api/src/main/java/org/ehcache/spi/loaderwriter/BulkCacheLoadingException.java b/ehcache-api/src/main/java/org/ehcache/spi/loaderwriter/BulkCacheLoadingException.java similarity index 100% rename from api/src/main/java/org/ehcache/spi/loaderwriter/BulkCacheLoadingException.java rename to ehcache-api/src/main/java/org/ehcache/spi/loaderwriter/BulkCacheLoadingException.java diff --git a/api/src/main/java/org/ehcache/spi/loaderwriter/BulkCacheWritingException.java b/ehcache-api/src/main/java/org/ehcache/spi/loaderwriter/BulkCacheWritingException.java similarity index 100% rename from api/src/main/java/org/ehcache/spi/loaderwriter/BulkCacheWritingException.java rename to ehcache-api/src/main/java/org/ehcache/spi/loaderwriter/BulkCacheWritingException.java diff --git a/api/src/main/java/org/ehcache/spi/loaderwriter/CacheLoaderWriter.java b/ehcache-api/src/main/java/org/ehcache/spi/loaderwriter/CacheLoaderWriter.java similarity index 100% rename from api/src/main/java/org/ehcache/spi/loaderwriter/CacheLoaderWriter.java rename to ehcache-api/src/main/java/org/ehcache/spi/loaderwriter/CacheLoaderWriter.java diff --git a/api/src/main/java/org/ehcache/spi/loaderwriter/CacheLoaderWriterConfiguration.java b/ehcache-api/src/main/java/org/ehcache/spi/loaderwriter/CacheLoaderWriterConfiguration.java similarity index 87% rename from api/src/main/java/org/ehcache/spi/loaderwriter/CacheLoaderWriterConfiguration.java rename to ehcache-api/src/main/java/org/ehcache/spi/loaderwriter/CacheLoaderWriterConfiguration.java index dbe07f0318..dee84aec39 100644 --- a/api/src/main/java/org/ehcache/spi/loaderwriter/CacheLoaderWriterConfiguration.java +++ b/ehcache-api/src/main/java/org/ehcache/spi/loaderwriter/CacheLoaderWriterConfiguration.java @@ -22,8 +22,10 @@ *

* The {@code CacheLoaderWriterProvider} provides write-behind services to a * {@link org.ehcache.Cache Cache}. + * + * @param representation type */ -public interface CacheLoaderWriterConfiguration extends ServiceConfiguration { +public interface CacheLoaderWriterConfiguration extends ServiceConfiguration { /** * {@inheritDoc} */ diff --git a/api/src/main/java/org/ehcache/spi/loaderwriter/CacheLoaderWriterProvider.java b/ehcache-api/src/main/java/org/ehcache/spi/loaderwriter/CacheLoaderWriterProvider.java similarity index 97% rename from api/src/main/java/org/ehcache/spi/loaderwriter/CacheLoaderWriterProvider.java rename to ehcache-api/src/main/java/org/ehcache/spi/loaderwriter/CacheLoaderWriterProvider.java index 34f77b1114..cbef48f166 100644 --- a/api/src/main/java/org/ehcache/spi/loaderwriter/CacheLoaderWriterProvider.java +++ b/ehcache-api/src/main/java/org/ehcache/spi/loaderwriter/CacheLoaderWriterProvider.java @@ -65,7 +65,7 @@ public interface CacheLoaderWriterProvider extends Service { * * @return {@code CacheLoaderWriterConfiguration} configured for the {@code Cache}, otherwise null */ - CacheLoaderWriterConfiguration getPreConfiguredCacheLoaderWriterConfig(String alias); + CacheLoaderWriterConfiguration getPreConfiguredCacheLoaderWriterConfig(String alias); /** * Checks whether {@link org.ehcache.spi.loaderwriter.CacheLoaderWriter} was provided using jsr api diff --git a/api/src/main/java/org/ehcache/spi/loaderwriter/CacheLoadingException.java b/ehcache-api/src/main/java/org/ehcache/spi/loaderwriter/CacheLoadingException.java similarity index 100% rename from api/src/main/java/org/ehcache/spi/loaderwriter/CacheLoadingException.java rename to ehcache-api/src/main/java/org/ehcache/spi/loaderwriter/CacheLoadingException.java diff --git a/api/src/main/java/org/ehcache/spi/loaderwriter/CacheWritingException.java b/ehcache-api/src/main/java/org/ehcache/spi/loaderwriter/CacheWritingException.java similarity index 100% rename from api/src/main/java/org/ehcache/spi/loaderwriter/CacheWritingException.java rename to ehcache-api/src/main/java/org/ehcache/spi/loaderwriter/CacheWritingException.java diff --git a/api/src/main/java/org/ehcache/spi/loaderwriter/WriteBehindConfiguration.java b/ehcache-api/src/main/java/org/ehcache/spi/loaderwriter/WriteBehindConfiguration.java similarity index 95% rename from api/src/main/java/org/ehcache/spi/loaderwriter/WriteBehindConfiguration.java rename to ehcache-api/src/main/java/org/ehcache/spi/loaderwriter/WriteBehindConfiguration.java index b71622f044..b178e56575 100644 --- a/api/src/main/java/org/ehcache/spi/loaderwriter/WriteBehindConfiguration.java +++ b/ehcache-api/src/main/java/org/ehcache/spi/loaderwriter/WriteBehindConfiguration.java @@ -24,8 +24,10 @@ *

* The {@code WriteBehindProvider} provides write-behind services to a * {@link org.ehcache.Cache Cache}. + * + * @param representation type */ -public interface WriteBehindConfiguration extends ServiceConfiguration { +public interface WriteBehindConfiguration extends ServiceConfiguration { /** * The concurrency of the write behind engines queues. diff --git a/api/src/main/java/org/ehcache/spi/loaderwriter/WriteBehindProvider.java b/ehcache-api/src/main/java/org/ehcache/spi/loaderwriter/WriteBehindProvider.java similarity index 98% rename from api/src/main/java/org/ehcache/spi/loaderwriter/WriteBehindProvider.java rename to ehcache-api/src/main/java/org/ehcache/spi/loaderwriter/WriteBehindProvider.java index baa7d0b92a..905faf19a8 100644 --- a/api/src/main/java/org/ehcache/spi/loaderwriter/WriteBehindProvider.java +++ b/ehcache-api/src/main/java/org/ehcache/spi/loaderwriter/WriteBehindProvider.java @@ -37,7 +37,7 @@ public interface WriteBehindProvider extends Service { * * @return the write-behind decorated loader writer */ - CacheLoaderWriter createWriteBehindLoaderWriter(CacheLoaderWriter cacheLoaderWriter, WriteBehindConfiguration configuration); + CacheLoaderWriter createWriteBehindLoaderWriter(CacheLoaderWriter cacheLoaderWriter, WriteBehindConfiguration configuration); /** * Releases a write-behind decorator when the associated {@link org.ehcache.Cache Cache} diff --git a/api/src/main/java/org/ehcache/spi/loaderwriter/package-info.java b/ehcache-api/src/main/java/org/ehcache/spi/loaderwriter/package-info.java similarity index 100% rename from api/src/main/java/org/ehcache/spi/loaderwriter/package-info.java rename to ehcache-api/src/main/java/org/ehcache/spi/loaderwriter/package-info.java diff --git a/api/src/main/java/org/ehcache/spi/persistence/PersistableResourceService.java b/ehcache-api/src/main/java/org/ehcache/spi/persistence/PersistableResourceService.java similarity index 97% rename from api/src/main/java/org/ehcache/spi/persistence/PersistableResourceService.java rename to ehcache-api/src/main/java/org/ehcache/spi/persistence/PersistableResourceService.java index 41e9e12cd2..b45f4c5516 100644 --- a/api/src/main/java/org/ehcache/spi/persistence/PersistableResourceService.java +++ b/ehcache-api/src/main/java/org/ehcache/spi/persistence/PersistableResourceService.java @@ -104,6 +104,8 @@ public interface PersistableResourceService extends MaintainableService { /** * An identifier for an existing persistable resource. + * + * @param the associated persistence service type */ - interface PersistenceSpaceIdentifier extends ServiceConfiguration {} + interface PersistenceSpaceIdentifier extends ServiceConfiguration {} } diff --git a/api/src/main/java/org/ehcache/spi/persistence/StateHolder.java b/ehcache-api/src/main/java/org/ehcache/spi/persistence/StateHolder.java similarity index 100% rename from api/src/main/java/org/ehcache/spi/persistence/StateHolder.java rename to ehcache-api/src/main/java/org/ehcache/spi/persistence/StateHolder.java diff --git a/api/src/main/java/org/ehcache/spi/persistence/StateRepository.java b/ehcache-api/src/main/java/org/ehcache/spi/persistence/StateRepository.java similarity index 100% rename from api/src/main/java/org/ehcache/spi/persistence/StateRepository.java rename to ehcache-api/src/main/java/org/ehcache/spi/persistence/StateRepository.java diff --git a/api/src/main/java/org/ehcache/spi/persistence/package-info.java b/ehcache-api/src/main/java/org/ehcache/spi/persistence/package-info.java similarity index 100% rename from api/src/main/java/org/ehcache/spi/persistence/package-info.java rename to ehcache-api/src/main/java/org/ehcache/spi/persistence/package-info.java diff --git a/api/src/main/java/org/ehcache/spi/resilience/RecoveryStore.java b/ehcache-api/src/main/java/org/ehcache/spi/resilience/RecoveryStore.java similarity index 100% rename from api/src/main/java/org/ehcache/spi/resilience/RecoveryStore.java rename to ehcache-api/src/main/java/org/ehcache/spi/resilience/RecoveryStore.java diff --git a/api/src/main/java/org/ehcache/spi/resilience/ResilienceStrategy.java b/ehcache-api/src/main/java/org/ehcache/spi/resilience/ResilienceStrategy.java similarity index 100% rename from api/src/main/java/org/ehcache/spi/resilience/ResilienceStrategy.java rename to ehcache-api/src/main/java/org/ehcache/spi/resilience/ResilienceStrategy.java diff --git a/api/src/main/java/org/ehcache/spi/resilience/ResilienceStrategyProvider.java b/ehcache-api/src/main/java/org/ehcache/spi/resilience/ResilienceStrategyProvider.java similarity index 100% rename from api/src/main/java/org/ehcache/spi/resilience/ResilienceStrategyProvider.java rename to ehcache-api/src/main/java/org/ehcache/spi/resilience/ResilienceStrategyProvider.java diff --git a/api/src/main/java/org/ehcache/spi/resilience/StoreAccessException.java b/ehcache-api/src/main/java/org/ehcache/spi/resilience/StoreAccessException.java similarity index 100% rename from api/src/main/java/org/ehcache/spi/resilience/StoreAccessException.java rename to ehcache-api/src/main/java/org/ehcache/spi/resilience/StoreAccessException.java diff --git a/api/src/main/java/org/ehcache/spi/resilience/package-info.java b/ehcache-api/src/main/java/org/ehcache/spi/resilience/package-info.java similarity index 100% rename from api/src/main/java/org/ehcache/spi/resilience/package-info.java rename to ehcache-api/src/main/java/org/ehcache/spi/resilience/package-info.java diff --git a/api/src/main/java/org/ehcache/spi/serialization/SerializationProvider.java b/ehcache-api/src/main/java/org/ehcache/spi/serialization/SerializationProvider.java similarity index 95% rename from api/src/main/java/org/ehcache/spi/serialization/SerializationProvider.java rename to ehcache-api/src/main/java/org/ehcache/spi/serialization/SerializationProvider.java index a297362b08..05befb3695 100644 --- a/api/src/main/java/org/ehcache/spi/serialization/SerializationProvider.java +++ b/ehcache-api/src/main/java/org/ehcache/spi/serialization/SerializationProvider.java @@ -50,7 +50,7 @@ public interface SerializationProvider extends Service { * * @throws UnsupportedTypeException if a serializer cannot be created for the given type */ - Serializer createKeySerializer(Class clazz, ClassLoader classLoader, ServiceConfiguration... configs) throws UnsupportedTypeException; + Serializer createKeySerializer(Class clazz, ClassLoader classLoader, ServiceConfiguration... configs) throws UnsupportedTypeException; /** * Creates a value {@link Serializer} with the given parameters. @@ -65,7 +65,7 @@ public interface SerializationProvider extends Service { * * @throws UnsupportedTypeException if a serializer cannot be created for the given type */ - Serializer createValueSerializer(Class clazz, ClassLoader classLoader, ServiceConfiguration... configs) throws UnsupportedTypeException; + Serializer createValueSerializer(Class clazz, ClassLoader classLoader, ServiceConfiguration... configs) throws UnsupportedTypeException; /** * Releases the given {@link Serializer} instance. diff --git a/api/src/main/java/org/ehcache/spi/serialization/Serializer.java b/ehcache-api/src/main/java/org/ehcache/spi/serialization/Serializer.java similarity index 100% rename from api/src/main/java/org/ehcache/spi/serialization/Serializer.java rename to ehcache-api/src/main/java/org/ehcache/spi/serialization/Serializer.java diff --git a/api/src/main/java/org/ehcache/spi/serialization/SerializerException.java b/ehcache-api/src/main/java/org/ehcache/spi/serialization/SerializerException.java similarity index 100% rename from api/src/main/java/org/ehcache/spi/serialization/SerializerException.java rename to ehcache-api/src/main/java/org/ehcache/spi/serialization/SerializerException.java diff --git a/api/src/main/java/org/ehcache/spi/serialization/StatefulSerializer.java b/ehcache-api/src/main/java/org/ehcache/spi/serialization/StatefulSerializer.java similarity index 100% rename from api/src/main/java/org/ehcache/spi/serialization/StatefulSerializer.java rename to ehcache-api/src/main/java/org/ehcache/spi/serialization/StatefulSerializer.java diff --git a/api/src/main/java/org/ehcache/spi/serialization/UnsupportedTypeException.java b/ehcache-api/src/main/java/org/ehcache/spi/serialization/UnsupportedTypeException.java similarity index 100% rename from api/src/main/java/org/ehcache/spi/serialization/UnsupportedTypeException.java rename to ehcache-api/src/main/java/org/ehcache/spi/serialization/UnsupportedTypeException.java diff --git a/api/src/main/java/org/ehcache/spi/serialization/package-info.java b/ehcache-api/src/main/java/org/ehcache/spi/serialization/package-info.java similarity index 100% rename from api/src/main/java/org/ehcache/spi/serialization/package-info.java rename to ehcache-api/src/main/java/org/ehcache/spi/serialization/package-info.java diff --git a/api/src/main/java/org/ehcache/spi/service/MaintainableService.java b/ehcache-api/src/main/java/org/ehcache/spi/service/MaintainableService.java similarity index 100% rename from api/src/main/java/org/ehcache/spi/service/MaintainableService.java rename to ehcache-api/src/main/java/org/ehcache/spi/service/MaintainableService.java diff --git a/api/src/main/java/org/ehcache/spi/service/OptionalServiceDependencies.java b/ehcache-api/src/main/java/org/ehcache/spi/service/OptionalServiceDependencies.java similarity index 92% rename from api/src/main/java/org/ehcache/spi/service/OptionalServiceDependencies.java rename to ehcache-api/src/main/java/org/ehcache/spi/service/OptionalServiceDependencies.java index 231f71de9d..1bfbb528f2 100644 --- a/api/src/main/java/org/ehcache/spi/service/OptionalServiceDependencies.java +++ b/ehcache-api/src/main/java/org/ehcache/spi/service/OptionalServiceDependencies.java @@ -17,6 +17,7 @@ package org.ehcache.spi.service; 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; @@ -26,10 +27,13 @@ */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) +@Inherited public @interface OptionalServiceDependencies { /** * Array of {@link Service} dependency classes + * + * @return the dependency class names */ String[] value(); } diff --git a/api/src/main/java/org/ehcache/spi/service/PluralService.java b/ehcache-api/src/main/java/org/ehcache/spi/service/PluralService.java similarity index 100% rename from api/src/main/java/org/ehcache/spi/service/PluralService.java rename to ehcache-api/src/main/java/org/ehcache/spi/service/PluralService.java diff --git a/api/src/main/java/org/ehcache/spi/service/Service.java b/ehcache-api/src/main/java/org/ehcache/spi/service/Service.java similarity index 100% rename from api/src/main/java/org/ehcache/spi/service/Service.java rename to ehcache-api/src/main/java/org/ehcache/spi/service/Service.java diff --git a/ehcache-api/src/main/java/org/ehcache/spi/service/ServiceConfiguration.java b/ehcache-api/src/main/java/org/ehcache/spi/service/ServiceConfiguration.java new file mode 100644 index 0000000000..2358109150 --- /dev/null +++ b/ehcache-api/src/main/java/org/ehcache/spi/service/ServiceConfiguration.java @@ -0,0 +1,67 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.spi.service; + +/** + * A configuration type to be used when interacting with a {@link Service}. + * + * @param the service type this configuration works with + * @param the type of the detached representation + */ +public interface ServiceConfiguration { + + /** + * Indicates which service this configuration works with. + * + * @return the service type + */ + Class getServiceType(); + + /** + * Derive a detached representation from this configuration + * + * @return a detached representation + * @throws UnsupportedOperationException if the configuration has no representation + */ + default R derive() throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + /** + * Construct a new configuration from the given detached representation. + * + * @param representation a detached representation + * @return a new configuration + * @throws UnsupportedOperationException if the configuration has no representation + */ + default ServiceConfiguration build(R representation) throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + /** + * Returns true if this configuration can co-exist with {@code other} in the same cache configuration. + *

+ * The default implementation of {@code compatibleWith} (as used by many of the implementations) considers any + * instance of the same type (or a sub-type) to be incompatible with this instance. + * + * @param other other service configuration + * @return {@code true} if the two configurations are compatible + */ + default boolean compatibleWith(ServiceConfiguration other) { + return !getClass().isInstance(other); + }; +} diff --git a/ehcache-api/src/main/java/org/ehcache/spi/service/ServiceCreationConfiguration.java b/ehcache-api/src/main/java/org/ehcache/spi/service/ServiceCreationConfiguration.java new file mode 100644 index 0000000000..e2636e2263 --- /dev/null +++ b/ehcache-api/src/main/java/org/ehcache/spi/service/ServiceCreationConfiguration.java @@ -0,0 +1,67 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.spi.service; + +/** + * A configuration type used when creating a {@link Service}. + * + * @param the service type this configuration works with + * @param the type of the detached representation + */ +public interface ServiceCreationConfiguration { + + /** + * Indicates which service consumes this configuration at creation. + * + * @return the service type + */ + Class getServiceType(); + + /** + * Derive a detached representation from this configuration + * + * @return a detached representation + * @throws UnsupportedOperationException if the configuration has no representation + */ + default R derive() throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + /** + * Construct a new configuration from the given detached representation. + * + * @param representation a detached representation + * @return a new configuration + * @throws UnsupportedOperationException if the configuration has no representation + */ + default ServiceCreationConfiguration build(R representation) throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + /** + * Returns true if this configuration can co-exist with {@code other} in the same manager configuration. + *

+ * The default implementation of {@code compatibleWith} (as used by many of the implementations) considers any + * instance of the same type (or a sub-type) to be incompatible with this instance. + * + * @param other other service creation configuration + * @return {@code true} if the two configurations are compatible + */ + default boolean compatibleWith(ServiceCreationConfiguration other) { + return !getClass().isInstance(other); + }; +} diff --git a/api/src/main/java/org/ehcache/spi/service/ServiceDependencies.java b/ehcache-api/src/main/java/org/ehcache/spi/service/ServiceDependencies.java similarity index 92% rename from api/src/main/java/org/ehcache/spi/service/ServiceDependencies.java rename to ehcache-api/src/main/java/org/ehcache/spi/service/ServiceDependencies.java index 770e6103ee..8e582d38bd 100644 --- a/api/src/main/java/org/ehcache/spi/service/ServiceDependencies.java +++ b/ehcache-api/src/main/java/org/ehcache/spi/service/ServiceDependencies.java @@ -17,6 +17,7 @@ package org.ehcache.spi.service; 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; @@ -26,10 +27,13 @@ */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) +@Inherited public @interface ServiceDependencies { /** * Array of {@link Service} dependency classes + * + * @return the dependency class names */ Class[] value(); } diff --git a/api/src/main/java/org/ehcache/spi/service/ServiceProvider.java b/ehcache-api/src/main/java/org/ehcache/spi/service/ServiceProvider.java similarity index 98% rename from api/src/main/java/org/ehcache/spi/service/ServiceProvider.java rename to ehcache-api/src/main/java/org/ehcache/spi/service/ServiceProvider.java index ffc4a8d9d3..d1f2b0c2d1 100644 --- a/api/src/main/java/org/ehcache/spi/service/ServiceProvider.java +++ b/ehcache-api/src/main/java/org/ehcache/spi/service/ServiceProvider.java @@ -17,6 +17,7 @@ package org.ehcache.spi.service; import java.util.Collection; +import java.util.Optional; /** * A repository of {@link Service} instances that can be used to look them up by type. diff --git a/api/src/main/java/org/ehcache/spi/service/package-info.java b/ehcache-api/src/main/java/org/ehcache/spi/service/package-info.java similarity index 100% rename from api/src/main/java/org/ehcache/spi/service/package-info.java rename to ehcache-api/src/main/java/org/ehcache/spi/service/package-info.java diff --git a/api/src/test/java/org/ehcache/CacheManagerTest.java b/ehcache-api/src/test/java/org/ehcache/CacheManagerTest.java similarity index 100% rename from api/src/test/java/org/ehcache/CacheManagerTest.java rename to ehcache-api/src/test/java/org/ehcache/CacheManagerTest.java diff --git a/api/src/test/java/org/ehcache/UserManagedCacheTest.java b/ehcache-api/src/test/java/org/ehcache/UserManagedCacheTest.java similarity index 100% rename from api/src/test/java/org/ehcache/UserManagedCacheTest.java rename to ehcache-api/src/test/java/org/ehcache/UserManagedCacheTest.java diff --git a/api/src/test/java/org/ehcache/config/units/MemoryUnitTest.java b/ehcache-api/src/test/java/org/ehcache/config/units/MemoryUnitTest.java similarity index 100% rename from api/src/test/java/org/ehcache/config/units/MemoryUnitTest.java rename to ehcache-api/src/test/java/org/ehcache/config/units/MemoryUnitTest.java diff --git a/api/src/test/java/org/ehcache/expiry/DurationTest.java b/ehcache-api/src/test/java/org/ehcache/expiry/DurationTest.java similarity index 100% rename from api/src/test/java/org/ehcache/expiry/DurationTest.java rename to ehcache-api/src/test/java/org/ehcache/expiry/DurationTest.java diff --git a/api/src/test/java/org/ehcache/expiry/ExpirationsTest.java b/ehcache-api/src/test/java/org/ehcache/expiry/ExpirationsTest.java similarity index 100% rename from api/src/test/java/org/ehcache/expiry/ExpirationsTest.java rename to ehcache-api/src/test/java/org/ehcache/expiry/ExpirationsTest.java diff --git a/ehcache-core/build.gradle b/ehcache-core/build.gradle new file mode 100644 index 0000000000..adf8ced16a --- /dev/null +++ b/ehcache-core/build.gradle @@ -0,0 +1,42 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id 'org.ehcache.build.internal-module' +} + +publishing.publications.withType(MavenPublication) { + pom { + name = 'Ehcache 3 Core module' + description = 'The Core module of Ehcache 3' + } +} + +dependencies { + api project(':ehcache-api') + implementation "org.terracotta:statistics:$parent.statisticVersion" + compileOnly 'org.osgi:osgi.core:6.0.0' + compileOnly 'org.osgi:org.osgi.service.component.annotations:1.3.0' + testImplementation project(':spi-tester') +} + +jar { + bnd ( + 'Bundle-Activator': 'org.ehcache.core.osgi.EhcacheActivator', + 'Import-Package': '!javax.annotation, *', + 'Export-Package': '!org.ehcache.core.internal.*, org.ehcache.core.*' + ) +} diff --git a/core/config/checkstyle-suppressions.xml b/ehcache-core/config/checkstyle-suppressions.xml similarity index 100% rename from core/config/checkstyle-suppressions.xml rename to ehcache-core/config/checkstyle-suppressions.xml diff --git a/core/src/main/java/org/ehcache/core/CacheConfigurationChangeEvent.java b/ehcache-core/src/main/java/org/ehcache/core/CacheConfigurationChangeEvent.java similarity index 100% rename from core/src/main/java/org/ehcache/core/CacheConfigurationChangeEvent.java rename to ehcache-core/src/main/java/org/ehcache/core/CacheConfigurationChangeEvent.java diff --git a/core/src/main/java/org/ehcache/core/CacheConfigurationChangeListener.java b/ehcache-core/src/main/java/org/ehcache/core/CacheConfigurationChangeListener.java similarity index 100% rename from core/src/main/java/org/ehcache/core/CacheConfigurationChangeListener.java rename to ehcache-core/src/main/java/org/ehcache/core/CacheConfigurationChangeListener.java diff --git a/core/src/main/java/org/ehcache/core/CacheConfigurationProperty.java b/ehcache-core/src/main/java/org/ehcache/core/CacheConfigurationProperty.java similarity index 100% rename from core/src/main/java/org/ehcache/core/CacheConfigurationProperty.java rename to ehcache-core/src/main/java/org/ehcache/core/CacheConfigurationProperty.java diff --git a/core/src/main/java/org/ehcache/core/DefaultCacheManagerProviderService.java b/ehcache-core/src/main/java/org/ehcache/core/DefaultCacheManagerProviderService.java similarity index 100% rename from core/src/main/java/org/ehcache/core/DefaultCacheManagerProviderService.java rename to ehcache-core/src/main/java/org/ehcache/core/DefaultCacheManagerProviderService.java diff --git a/core/src/main/java/org/ehcache/core/Ehcache.java b/ehcache-core/src/main/java/org/ehcache/core/Ehcache.java similarity index 97% rename from core/src/main/java/org/ehcache/core/Ehcache.java rename to ehcache-core/src/main/java/org/ehcache/core/Ehcache.java index 06a56408fc..787b686e4d 100644 --- a/core/src/main/java/org/ehcache/core/Ehcache.java +++ b/ehcache-core/src/main/java/org/ehcache/core/Ehcache.java @@ -16,6 +16,7 @@ package org.ehcache.core; +import java.util.AbstractMap; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; @@ -31,7 +32,7 @@ import org.ehcache.Cache; import org.ehcache.config.CacheConfiguration; import org.ehcache.core.events.CacheEventDispatcher; -import org.ehcache.core.internal.util.CollectionUtil; +import org.ehcache.core.util.CollectionUtil; import org.ehcache.core.spi.store.Store; import org.ehcache.core.spi.store.Store.ValueHolder; import org.ehcache.spi.resilience.ResilienceStrategy; @@ -215,7 +216,7 @@ public V getAndRemove(K key) { ValueHolder existingValue; try { - existingValue = store.getAndCompute(key, (mappedKey, mappedValue) -> null); + existingValue = store.getAndRemove(key); } catch (StoreAccessException e) { getObserver.end(org.ehcache.core.statistics.CacheOperationOutcomes.GetOutcome.FAILURE); removeObserver.end(RemoveOutcome.FAILURE); @@ -239,7 +240,7 @@ public V getAndPut(K key, final V value) { ValueHolder existingValue; try { - existingValue = store.getAndCompute(key, (mappedKey, mappedValue) -> value); + existingValue = store.getAndPut(key, value); } catch (StoreAccessException e) { getObserver.end(org.ehcache.core.statistics.CacheOperationOutcomes.GetOutcome.FAILURE); putObserver.end(PutOutcome.FAILURE); @@ -350,7 +351,7 @@ public static class GetAllFunction implements Function> computeResult = new ArrayList<>(size); for (K key : keys) { - computeResult.add(CollectionUtil.entry(key, null)); + computeResult.add(new AbstractMap.SimpleImmutableEntry<>(key, null)); } return computeResult; diff --git a/core/src/main/java/org/ehcache/core/EhcacheBase.java b/ehcache-core/src/main/java/org/ehcache/core/EhcacheBase.java similarity index 97% rename from core/src/main/java/org/ehcache/core/EhcacheBase.java rename to ehcache-core/src/main/java/org/ehcache/core/EhcacheBase.java index aca14f72c7..aabae905a3 100644 --- a/core/src/main/java/org/ehcache/core/EhcacheBase.java +++ b/ehcache-core/src/main/java/org/ehcache/core/EhcacheBase.java @@ -297,9 +297,6 @@ public V putIfAbsent(final K key, final V value) { if (put[0]) { putIfAbsentObserver.end(PutIfAbsentOutcome.PUT); return null; - } else if (inCache == null) { - putIfAbsentObserver.end(PutIfAbsentOutcome.HIT); - return null; } else { putIfAbsentObserver.end(PutIfAbsentOutcome.HIT); return inCache.get(); @@ -323,7 +320,7 @@ public V putIfAbsent(final K key, final V value) { @Override public Iterator> iterator() { statusTransitioner.checkAvailable(); - return new CacheEntryIterator(false); + return new CacheEntryIterator(); } /** @@ -702,15 +699,21 @@ public boolean remove(K key) { @Override public void removeAll() { + Collection failures = new ArrayList<>(); Store.Iterator>> iterator = store.iterator(); while (iterator.hasNext()) { try { Entry> next = iterator.next(); remove(next.getKey()); } catch (StoreAccessException cae) { - // skip + failures.add(cae); } } + if (!failures.isEmpty()) { + StoreAccessException removeAllFailure = new StoreAccessException("Iteration failures may have prevented a complete removal"); + failures.forEach(removeAllFailure::addSuppressed); + resilienceStrategy.clearFailure(removeAllFailure); + } } } @@ -718,13 +721,11 @@ public void removeAll() { private class CacheEntryIterator implements Iterator> { private final Store.Iterator>> iterator; - private final boolean quiet; private Cache.Entry> current; private Cache.Entry> next; private StoreAccessException nextException; - public CacheEntryIterator(boolean quiet) { - this.quiet = quiet; + public CacheEntryIterator() { this.iterator = store.iterator(); advance(); } @@ -733,7 +734,7 @@ private void advance() { try { while (iterator.hasNext()) { next = iterator.next(); - if (getNoLoader(next.getKey()) != null) { + if (next != null) { return; } } @@ -759,14 +760,14 @@ public Entry next() { throw new NoSuchElementException(); } - if (!quiet) getObserver.begin(); + getObserver.begin(); if (nextException == null) { - if (!quiet) getObserver.end(GetOutcome.HIT); + getObserver.end(GetOutcome.HIT); current = next; advance(); return new ValueHolderBasedEntry<>(current); } else { - if (!quiet) getObserver.end(GetOutcome.FAILURE); + getObserver.end(GetOutcome.FAILURE); StoreAccessException cae = nextException; nextException = null; return resilienceStrategy.iteratorFailure(cae); diff --git a/core/src/main/java/org/ehcache/core/EhcacheManager.java b/ehcache-core/src/main/java/org/ehcache/core/EhcacheManager.java similarity index 86% rename from core/src/main/java/org/ehcache/core/EhcacheManager.java rename to ehcache-core/src/main/java/org/ehcache/core/EhcacheManager.java index 5c066c77cc..b4a05b71a4 100644 --- a/core/src/main/java/org/ehcache/core/EhcacheManager.java +++ b/ehcache-core/src/main/java/org/ehcache/core/EhcacheManager.java @@ -25,7 +25,6 @@ import org.ehcache.config.Configuration; import org.ehcache.config.ResourcePool; import org.ehcache.config.ResourceType; -import org.ehcache.core.config.BaseCacheConfiguration; import org.ehcache.core.config.DefaultConfiguration; import org.ehcache.core.config.store.StoreEventSourceConfiguration; import org.ehcache.core.config.store.StoreStatisticsConfiguration; @@ -34,10 +33,7 @@ import org.ehcache.core.events.CacheEventListenerConfiguration; import org.ehcache.core.events.CacheEventListenerProvider; import org.ehcache.core.events.CacheManagerListener; -import org.ehcache.core.internal.service.ServiceLocator; -import org.ehcache.core.internal.store.StoreConfigurationImpl; -import org.ehcache.core.internal.store.StoreSupport; -import org.ehcache.core.internal.util.ClassLoading; +import org.ehcache.core.spi.ServiceLocator; import org.ehcache.core.resilience.DefaultRecoveryStore; import org.ehcache.core.spi.LifeCycled; import org.ehcache.core.spi.LifeCycledAdapter; @@ -45,11 +41,13 @@ import org.ehcache.core.spi.service.ServiceUtils; import org.ehcache.core.spi.store.InternalCacheManager; import org.ehcache.core.spi.store.Store; +import org.ehcache.core.store.StoreConfigurationImpl; +import org.ehcache.core.store.StoreSupport; +import org.ehcache.core.util.ClassLoading; import org.ehcache.event.CacheEventListener; import org.ehcache.spi.loaderwriter.CacheLoaderWriter; import org.ehcache.spi.loaderwriter.CacheLoaderWriterConfiguration; import org.ehcache.spi.loaderwriter.CacheLoaderWriterProvider; -import org.ehcache.spi.loaderwriter.WriteBehindConfiguration; import org.ehcache.spi.loaderwriter.WriteBehindProvider; import org.ehcache.spi.persistence.PersistableResourceService; import org.ehcache.spi.resilience.ResilienceStrategy; @@ -78,8 +76,9 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.UnaryOperator; -import static org.ehcache.core.internal.service.ServiceLocator.dependencySet; +import static org.ehcache.core.spi.ServiceLocator.dependencySet; import static org.ehcache.core.spi.service.ServiceUtils.findOptionalAmongst; import static org.ehcache.core.spi.service.ServiceUtils.findSingletonAmongst; @@ -113,37 +112,43 @@ public EhcacheManager(Configuration config, Collection services) { } public EhcacheManager(Configuration config, Collection services, boolean useLoaderInAtomics) { + this(config, dependencies -> dependencies.with(services), useLoaderInAtomics); + } + + public EhcacheManager(Configuration config, UnaryOperator customization, boolean useLoaderInAtomics) { final String simpleName = this.getClass().getSimpleName(); this.simpleName = (simpleName.isEmpty() ? this.getClass().getName() : simpleName); this.configuration = new DefaultConfiguration(config); this.cacheManagerClassLoader = config.getClassLoader() != null ? config.getClassLoader() : ClassLoading.getDefaultClassLoader(); this.useLoaderInAtomics = useLoaderInAtomics; validateServicesConfigs(); - this.serviceLocator = resolveServices(services); + this.serviceLocator = resolveServices(customization); } private void validateServicesConfigs() { Set> classes = new HashSet<>(); - for (ServiceCreationConfiguration service : configuration.getServiceCreationConfigurations()) { + for (ServiceCreationConfiguration service : configuration.getServiceCreationConfigurations()) { if (!classes.add(service.getServiceType())) { throw new IllegalStateException("Duplicate creation configuration for service " + service.getServiceType()); } } } - private ServiceLocator resolveServices(Collection services) { + private ServiceLocator resolveServices(UnaryOperator customization) { ServiceLocator.DependencySet builder = dependencySet() .with(Store.Provider.class) .with(CacheLoaderWriterProvider.class) .with(WriteBehindProvider.class) .with(CacheEventDispatcherFactory.class) .with(CacheEventListenerProvider.class) - .with(ResilienceStrategyProvider.class) - .with(services); + .with(ResilienceStrategyProvider.class); + + builder = customization.apply(builder); + if (!builder.contains(CacheManagerProviderService.class)) { builder = builder.with(new DefaultCacheManagerProviderService(this)); } - for (ServiceCreationConfiguration serviceConfig : configuration.getServiceCreationConfigurations()) { + for (ServiceCreationConfiguration serviceConfig : configuration.getServiceCreationConfigurations()) { builder = builder.with(serviceConfig); } return builder.build(); @@ -303,10 +308,10 @@ private Cache createCache(String alias, CacheConfiguration or InternalCache createNewEhcache(String alias, CacheConfiguration config, Class keyType, Class valueType) { - Collection> adjustedServiceConfigs = new ArrayList<>(config.getServiceConfigurations()); + Collection> adjustedServiceConfigs = new ArrayList<>(config.getServiceConfigurations()); - List> unknownServiceConfigs = new ArrayList<>(); - for (ServiceConfiguration serviceConfig : adjustedServiceConfigs) { + List> unknownServiceConfigs = new ArrayList<>(); + for (ServiceConfiguration serviceConfig : adjustedServiceConfigs) { if (!serviceLocator.knowsServiceFor(serviceConfig)) { unknownServiceConfigs.add(serviceConfig); } @@ -318,26 +323,9 @@ InternalCache createNewEhcache(String alias, CacheConfiguration lifeCycledList = new ArrayList<>(); CacheLoaderWriterProvider cacheLoaderWriterProvider = serviceLocator.getService(CacheLoaderWriterProvider.class); - CacheLoaderWriter decorator ; + CacheLoaderWriter loaderWriter; if(cacheLoaderWriterProvider != null) { - CacheLoaderWriter loaderWriter; loaderWriter = cacheLoaderWriterProvider.createCacheLoaderWriter(alias, config); - WriteBehindConfiguration writeBehindConfiguration = - ServiceUtils.findSingletonAmongst(WriteBehindConfiguration.class, config.getServiceConfigurations()); - if(writeBehindConfiguration == null) { - decorator = loaderWriter; - } else { - WriteBehindProvider factory = serviceLocator.getService(WriteBehindProvider.class); - decorator = factory.createWriteBehindLoaderWriter(loaderWriter, writeBehindConfiguration); - if(decorator != null) { - lifeCycledList.add(new LifeCycledAdapter() { - @Override - public void close() { - factory.releaseWriteBehindLoaderWriter(decorator); - } - }); - } - } if (loaderWriter != null) { lifeCycledList.add(new LifeCycledAdapter() { @@ -348,15 +336,15 @@ public void close() throws Exception { }); } } else { - decorator = null; + loaderWriter = null; } - Store store = getStore(alias, config, keyType, valueType, adjustedServiceConfigs, lifeCycledList, decorator); + Store store = getStore(alias, config, keyType, valueType, adjustedServiceConfigs, lifeCycledList, loaderWriter); CacheEventDispatcherFactory cenlProvider = serviceLocator.getService(CacheEventDispatcherFactory.class); CacheEventDispatcher evtService = - cenlProvider.createCacheEventDispatcher(store, adjustedServiceConfigs.toArray(new ServiceConfiguration[adjustedServiceConfigs.size()])); + cenlProvider.createCacheEventDispatcher(store, adjustedServiceConfigs.toArray(new ServiceConfiguration[adjustedServiceConfigs.size()])); lifeCycledList.add(new LifeCycledAdapter() { @Override public void close() { @@ -367,18 +355,19 @@ public void close() { ResilienceStrategyProvider resilienceProvider = serviceLocator.getService(ResilienceStrategyProvider.class); ResilienceStrategy resilienceStrategy; - if (decorator == null) { + if (loaderWriter == null) { resilienceStrategy = resilienceProvider.createResilienceStrategy(alias, config, new DefaultRecoveryStore<>(store)); } else { - resilienceStrategy = resilienceProvider.createResilienceStrategy(alias, config, new DefaultRecoveryStore<>(store), decorator); + resilienceStrategy = resilienceProvider.createResilienceStrategy(alias, config, new DefaultRecoveryStore<>(store), loaderWriter); } - InternalCache cache = new Ehcache<>(config, store, resilienceStrategy, evtService, LoggerFactory.getLogger(Ehcache.class + "-" + alias), decorator); + InternalCache cache = new Ehcache<>(config, store, resilienceStrategy, evtService, LoggerFactory.getLogger(Ehcache.class + "-" + alias), loaderWriter); CacheEventListenerProvider evntLsnrFactory = serviceLocator.getService(CacheEventListenerProvider.class); if (evntLsnrFactory != null) { - Collection evtLsnrConfigs = - ServiceUtils.findAmongst(CacheEventListenerConfiguration.class, config.getServiceConfigurations()); - for (CacheEventListenerConfiguration lsnrConfig: evtLsnrConfigs) { + @SuppressWarnings("unchecked") + Collection> evtLsnrConfigs = + ServiceUtils.>>findAmongst((Class) CacheEventListenerConfiguration.class, config.getServiceConfigurations()); + for (CacheEventListenerConfiguration lsnrConfig: evtLsnrConfigs) { CacheEventListener lsnr = evntLsnrFactory.createEventListener(alias, lsnrConfig); if (lsnr != null) { cache.getRuntimeConfiguration().registerCacheEventListener(lsnr, lsnrConfig.orderingMode(), lsnrConfig.firingMode(), @@ -425,7 +414,7 @@ public void close() throws Exception { */ protected Store getStore(String alias, CacheConfiguration config, Class keyType, Class valueType, - Collection> serviceConfigs, + Collection> serviceConfigs, List lifeCycledList, CacheLoaderWriter loaderWriter) { final Set> resourceTypes = config.getResourcePools().getResourceTypeSet(); @@ -452,7 +441,7 @@ public void close() throws Exception { Serializer keySerializer = null; Serializer valueSerializer = null; final SerializationProvider serialization = serviceLocator.getService(SerializationProvider.class); - ServiceConfiguration[] serviceConfigArray = serviceConfigs.toArray(new ServiceConfiguration[serviceConfigs.size()]); + ServiceConfiguration[] serviceConfigArray = serviceConfigs.toArray(new ServiceConfiguration[serviceConfigs.size()]); if (serialization != null) { try { final Serializer keySer = serialization.createKeySerializer(keyType, config.getClassLoader(), serviceConfigArray); @@ -490,9 +479,10 @@ public void close() throws Exception { } } - Collection> serviceConfigurations = config.getServiceConfigurations(); + Collection> serviceConfigurations = config.getServiceConfigurations(); - int dispatcherConcurrency = findOptionalAmongst(StoreEventSourceConfiguration.class, serviceConfigurations) + @SuppressWarnings("unchecked") + int dispatcherConcurrency = findOptionalAmongst((Class>) (Class) StoreEventSourceConfiguration.class, serviceConfigurations) .map(StoreEventSourceConfiguration::getDispatcherConcurrency) .orElse(StoreEventSourceConfiguration.DEFAULT_DISPATCHER_CONCURRENCY); @@ -542,39 +532,23 @@ private PersistableResourceService getPersistableResourceService(ResourceType * adjusts the config to reflect new classloader & serialization provider */ private CacheConfiguration adjustConfigurationWithCacheManagerDefaults(String alias, CacheConfiguration config) { - ClassLoader cacheClassLoader = config.getClassLoader(); + if (config.getClassLoader() == null && cacheManagerClassLoader != null) { + config = config.derive().withClassLoader(cacheManagerClassLoader).build(); + } - List> configurationList = new ArrayList<>(); - configurationList.addAll(config.getServiceConfigurations()); - CacheLoaderWriterConfiguration loaderWriterConfiguration = findSingletonAmongst(CacheLoaderWriterConfiguration.class, config.getServiceConfigurations()); + CacheLoaderWriterConfiguration loaderWriterConfiguration = findSingletonAmongst(CacheLoaderWriterConfiguration.class, config.getServiceConfigurations()); if (loaderWriterConfiguration == null) { CacheLoaderWriterProvider loaderWriterProvider = serviceLocator.getService(CacheLoaderWriterProvider.class); - ServiceConfiguration preConfiguredCacheLoaderWriterConfig = loaderWriterProvider.getPreConfiguredCacheLoaderWriterConfig(alias); + CacheLoaderWriterConfiguration preConfiguredCacheLoaderWriterConfig = loaderWriterProvider.getPreConfiguredCacheLoaderWriterConfig(alias); if (preConfiguredCacheLoaderWriterConfig != null) { - configurationList.add(preConfiguredCacheLoaderWriterConfig); + config = config.derive().withService(preConfiguredCacheLoaderWriterConfig).build(); } if (loaderWriterProvider.isLoaderJsrProvided(alias)) { - configurationList.add(new CacheLoaderWriterConfiguration() { - }); + config = config.derive().withService(new CacheLoaderWriterConfiguration() {}).build(); } } - ServiceConfiguration[] serviceConfigurations = new ServiceConfiguration[configurationList.size()]; - configurationList.toArray(serviceConfigurations); - - if (cacheClassLoader == null) { - cacheClassLoader = cacheManagerClassLoader; - } - if (cacheClassLoader != config.getClassLoader() ) { - config = new BaseCacheConfiguration<>(config.getKeyType(), config.getValueType(), - config.getEvictionAdvisor(), cacheClassLoader, config.getExpiryPolicy(), - config.getResourcePools(), serviceConfigurations); - } else { - config = new BaseCacheConfiguration<>(config.getKeyType(), config.getValueType(), - config.getEvictionAdvisor(), config.getClassLoader(), config.getExpiryPolicy(), - config.getResourcePools(), serviceConfigurations); - } return config; } diff --git a/core/src/main/java/org/ehcache/core/EhcacheRuntimeConfiguration.java b/ehcache-core/src/main/java/org/ehcache/core/EhcacheRuntimeConfiguration.java similarity index 72% rename from core/src/main/java/org/ehcache/core/EhcacheRuntimeConfiguration.java rename to ehcache-core/src/main/java/org/ehcache/core/EhcacheRuntimeConfiguration.java index 8417844d90..e4e326ad47 100644 --- a/core/src/main/java/org/ehcache/core/EhcacheRuntimeConfiguration.java +++ b/ehcache-core/src/main/java/org/ehcache/core/EhcacheRuntimeConfiguration.java @@ -20,8 +20,8 @@ import org.ehcache.config.CacheRuntimeConfiguration; import org.ehcache.config.EvictionAdvisor; import org.ehcache.config.ResourcePools; -import org.ehcache.core.config.ExpiryUtils; -import org.ehcache.core.internal.events.EventListenerWrapper; +import org.ehcache.config.FluentCacheConfigurationBuilder; +import org.ehcache.core.events.EventListenerWrapper; import org.ehcache.event.CacheEventListener; import org.ehcache.event.EventFiring; import org.ehcache.event.EventOrdering; @@ -31,21 +31,18 @@ import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.EnumSet; import java.util.List; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; +import static java.util.Collections.unmodifiableCollection; + class EhcacheRuntimeConfiguration implements CacheRuntimeConfiguration, InternalRuntimeConfiguration, HumanReadable { - private final Collection> serviceConfigurations; - private final CacheConfiguration config; - private final Class keyType; - private final Class valueType; - private final EvictionAdvisor evictionAdvisor; - private final ClassLoader classLoader; - private final ExpiryPolicy expiry; + private final CacheConfiguration config; + + private final Collection> addedServiceConfigurations = new ArrayList<>(); private volatile ResourcePools resourcePools; private final List cacheConfigurationListenerList @@ -53,12 +50,6 @@ class EhcacheRuntimeConfiguration implements CacheRuntimeConfiguration config) { this.config = config; - this.serviceConfigurations = copy(config.getServiceConfigurations()); - this.keyType = config.getKeyType(); - this.valueType = config.getValueType(); - this.evictionAdvisor = config.getEvictionAdvisor(); - this.classLoader = config.getClassLoader(); - this.expiry = config.getExpiryPolicy(); this.resourcePools = config.getResourcePools(); } @@ -75,39 +66,41 @@ public synchronized void updateResourcePools(ResourcePools pools) { } @Override - public Collection> getServiceConfigurations() { - return this.serviceConfigurations; + public Collection> getServiceConfigurations() { + Collection> configurations = new ArrayList<>(config.getServiceConfigurations()); + configurations.addAll(addedServiceConfigurations); + return unmodifiableCollection(configurations); } @Override public Class getKeyType() { - return this.keyType; + return config.getKeyType(); } @Override public Class getValueType() { - return this.valueType; + return config.getValueType(); } @Override public EvictionAdvisor getEvictionAdvisor() { - return this.evictionAdvisor; + return config.getEvictionAdvisor(); } @Override public ClassLoader getClassLoader() { - return this.classLoader; + return config.getClassLoader(); } @SuppressWarnings("deprecation") @Override public org.ehcache.expiry.Expiry getExpiry() { - return ExpiryUtils.convertToExpiry(expiry); + return config.getExpiry(); } @Override public ExpiryPolicy getExpiryPolicy() { - return expiry; + return config.getExpiryPolicy(); } @Override @@ -115,6 +108,15 @@ public ResourcePools getResourcePools() { return this.resourcePools; } + @Override + public FluentCacheConfigurationBuilder derive() { + FluentCacheConfigurationBuilder builder = config.derive(); + for (ServiceConfiguration service : addedServiceConfigurations) { + builder = builder.withService(service); + } + return builder.updateResourcePools(existing -> resourcePools); + } + @Override public boolean addCacheConfigurationListener(List listeners) { return this.cacheConfigurationListenerList.addAll(listeners); @@ -137,18 +139,12 @@ public synchronized void registerCacheEventListener(CacheEventListener listener, EventOrdering ordering, EventFiring firing, EventType eventType, EventType... eventTypes) { - EventListenerWrapper listenerWrapper = new EventListenerWrapper<>(listener, firing, ordering, EnumSet.of(eventType, eventTypes)); - fireCacheConfigurationChange(CacheConfigurationProperty.ADD_LISTENER, listenerWrapper, listenerWrapper); - } - private Collection copy(Collection collection) { if (collection == null) { return null; } - return Collections.unmodifiableCollection(new ArrayList<>(collection)); + return unmodifiableCollection(new ArrayList<>(collection)); } @SuppressWarnings("unchecked") @@ -163,7 +159,7 @@ private void fireCacheConfigurationChange(CacheConfigurationProperty prop, f @Override public String readableString() { StringBuilder serviceConfigurationsToStringBuilder = new StringBuilder(); - for (ServiceConfiguration serviceConfiguration : serviceConfigurations) { + for (ServiceConfiguration serviceConfiguration : getServiceConfigurations()) { serviceConfigurationsToStringBuilder .append("\n ") .append("- "); @@ -184,20 +180,12 @@ public String readableString() { serviceConfigurationsToStringBuilder.append(" None"); } - String expiryPolicy; - - if (ExpiryPolicy.NO_EXPIRY == expiry) { - expiryPolicy = "NoExpiryPolicy"; - } else { - expiryPolicy = expiry.toString(); - } - return - "keyType: " + keyType.getName() + "\n" + - "valueType: " + valueType.getName() + "\n" + + "keyType: " + getKeyType().getName() + "\n" + + "valueType: " + getValueType().getName() + "\n" + "serviceConfigurations:" + serviceConfigurationsToStringBuilder.toString().replace("\n", "\n ") + "\n" + - "evictionAdvisor: " + ((evictionAdvisor != null) ? evictionAdvisor.getClass().getName() : "None") + "\n" + - "expiry: " + expiryPolicy + "\n" + + "evictionAdvisor: " + ((getEvictionAdvisor() != null) ? getEvictionAdvisor().getClass().getName() : "None") + "\n" + + "expiry: " + getExpiryPolicy() + "\n" + "resourcePools: " + "\n " + ((resourcePools instanceof HumanReadable) ? ((HumanReadable)resourcePools).readableString() : "").replace("\n", "\n "); } } diff --git a/core/src/main/java/org/ehcache/core/HumanReadable.java b/ehcache-core/src/main/java/org/ehcache/core/HumanReadable.java similarity index 100% rename from core/src/main/java/org/ehcache/core/HumanReadable.java rename to ehcache-core/src/main/java/org/ehcache/core/HumanReadable.java diff --git a/core/src/main/java/org/ehcache/core/InternalCache.java b/ehcache-core/src/main/java/org/ehcache/core/InternalCache.java similarity index 100% rename from core/src/main/java/org/ehcache/core/InternalCache.java rename to ehcache-core/src/main/java/org/ehcache/core/InternalCache.java diff --git a/core/src/main/java/org/ehcache/core/InternalRuntimeConfiguration.java b/ehcache-core/src/main/java/org/ehcache/core/InternalRuntimeConfiguration.java similarity index 100% rename from core/src/main/java/org/ehcache/core/InternalRuntimeConfiguration.java rename to ehcache-core/src/main/java/org/ehcache/core/InternalRuntimeConfiguration.java diff --git a/core/src/main/java/org/ehcache/core/InternalStatus.java b/ehcache-core/src/main/java/org/ehcache/core/InternalStatus.java similarity index 100% rename from core/src/main/java/org/ehcache/core/InternalStatus.java rename to ehcache-core/src/main/java/org/ehcache/core/InternalStatus.java diff --git a/core/src/main/java/org/ehcache/core/Jsr107Cache.java b/ehcache-core/src/main/java/org/ehcache/core/Jsr107Cache.java similarity index 100% rename from core/src/main/java/org/ehcache/core/Jsr107Cache.java rename to ehcache-core/src/main/java/org/ehcache/core/Jsr107Cache.java diff --git a/core/src/main/java/org/ehcache/core/PersistentUserManagedEhcache.java b/ehcache-core/src/main/java/org/ehcache/core/PersistentUserManagedEhcache.java similarity index 100% rename from core/src/main/java/org/ehcache/core/PersistentUserManagedEhcache.java rename to ehcache-core/src/main/java/org/ehcache/core/PersistentUserManagedEhcache.java diff --git a/core/src/main/java/org/ehcache/core/SpecIterator.java b/ehcache-core/src/main/java/org/ehcache/core/SpecIterator.java similarity index 100% rename from core/src/main/java/org/ehcache/core/SpecIterator.java rename to ehcache-core/src/main/java/org/ehcache/core/SpecIterator.java diff --git a/core/src/main/java/org/ehcache/core/StatusTransitioner.java b/ehcache-core/src/main/java/org/ehcache/core/StatusTransitioner.java similarity index 100% rename from core/src/main/java/org/ehcache/core/StatusTransitioner.java rename to ehcache-core/src/main/java/org/ehcache/core/StatusTransitioner.java diff --git a/core/src/main/java/org/ehcache/core/collections/ConcurrentWeakIdentityHashMap.java b/ehcache-core/src/main/java/org/ehcache/core/collections/ConcurrentWeakIdentityHashMap.java similarity index 100% rename from core/src/main/java/org/ehcache/core/collections/ConcurrentWeakIdentityHashMap.java rename to ehcache-core/src/main/java/org/ehcache/core/collections/ConcurrentWeakIdentityHashMap.java diff --git a/ehcache-core/src/main/java/org/ehcache/core/config/CoreConfigurationBuilder.java b/ehcache-core/src/main/java/org/ehcache/core/config/CoreConfigurationBuilder.java new file mode 100644 index 0000000000..10fc4fa125 --- /dev/null +++ b/ehcache-core/src/main/java/org/ehcache/core/config/CoreConfigurationBuilder.java @@ -0,0 +1,210 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.core.config; + +import org.ehcache.Cache; +import org.ehcache.config.CacheConfiguration; +import org.ehcache.config.Configuration; +import org.ehcache.config.FluentCacheConfigurationBuilder; +import org.ehcache.config.FluentConfigurationBuilder; +import org.ehcache.spi.service.ServiceCreationConfiguration; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; +import java.util.stream.Collectors; + +import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; +import static java.util.Collections.unmodifiableCollection; +import static java.util.Collections.unmodifiableMap; +import static java.util.Objects.requireNonNull; +import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toMap; + +public class CoreConfigurationBuilder> implements FluentConfigurationBuilder { + + private final Map> caches; + private final Collection> serviceConfigurations; + private final ClassLoader classLoader; + + /** + * Create a configuration builder seeded from the given configuration. + *

+ * Calling {@link #build()} on the returned builder will produce a functionally equivalent configuration to + * {@code seed}. + * + * @param seed configuration to duplicate + * @return a new configuration builder + */ + protected static CoreConfigurationBuilder newConfigurationBuilder(Configuration seed) { + return new CoreConfigurationBuilder<>(new CoreConfigurationBuilder<>(new CoreConfigurationBuilder<>(new CoreConfigurationBuilder<>(), + seed.getCacheConfigurations()), seed.getServiceCreationConfigurations()), seed.getClassLoader()); + } + + protected CoreConfigurationBuilder() { + this.caches = emptyMap(); + this.serviceConfigurations = emptyList(); + this.classLoader = null; + } + + protected CoreConfigurationBuilder(CoreConfigurationBuilder builder, Map> caches) { + this.caches = unmodifiableMap(caches); + this.serviceConfigurations = builder.serviceConfigurations; + this.classLoader = builder.classLoader; + } + + protected CoreConfigurationBuilder(CoreConfigurationBuilder builder, Collection> serviceConfigurations) { + this.caches = builder.caches; + this.serviceConfigurations = unmodifiableCollection(serviceConfigurations); + this.classLoader = builder.classLoader; + } + + protected CoreConfigurationBuilder(CoreConfigurationBuilder builder, ClassLoader classLoader) { + this.caches = builder.caches; + this.serviceConfigurations = builder.serviceConfigurations; + this.classLoader = classLoader; + } + + @Override + public Configuration build() { + return new DefaultConfiguration(caches, classLoader, serviceConfigurations.toArray(new ServiceCreationConfiguration[serviceConfigurations.size()])); + } + + @Override + public CacheConfiguration getCache(String alias) { + return caches.get(alias); + } + + @Override + public B withCache(String alias, CacheConfiguration config) { + Map> newCaches = new HashMap<>(caches); + newCaches.put(alias, config); + return newBuilderWith(newCaches); + } + + @Override + public B withoutCache(String alias) { + Map> newCaches = new HashMap<>(caches); + newCaches.remove(alias); + return newBuilderWith(newCaches); + } + + @Override + public B updateCache(String alias, UnaryOperator> update) { + CacheConfiguration existing = getCache(alias); + if (existing == null) { + throw new IllegalArgumentException("Cache does not exist"); + } else { + return withCache(alias, update.apply(existing.derive()).build()); + } + } + + @Override + public B updateCaches(UnaryOperator> update) { + return newBuilderWith(caches.entrySet().stream().collect( + toMap(Map.Entry::getKey, e -> update.apply(e.getValue().derive()).build()) + )); + } + + @Override + public > Collection getServices(Class configurationType) { + return serviceConfigurations.stream().filter(service -> configurationType.isAssignableFrom(service.getClass())).map(configurationType::cast).collect(toList()); + } + + @Override + public B withService(ServiceCreationConfiguration config) { + List> newServiceConfigurations = new ArrayList<>(serviceConfigurations); + newServiceConfigurations.removeIf(other -> !other.compatibleWith(config) || !config.compatibleWith(other)); + newServiceConfigurations.add(config); + return newBuilderWith(newServiceConfigurations); + } + + @Override + public > B withoutServices(Class clazz, Predicate predicate) { + List> newServiceConfigurations = new ArrayList<>(serviceConfigurations); + newServiceConfigurations.removeIf(c -> clazz.isInstance(c) && predicate.test(clazz.cast(c))); + return newBuilderWith(newServiceConfigurations); + } + + @Override + public > B updateServices(Class clazz, UnaryOperator update) { + @SuppressWarnings("unchecked") + Collection> existing = getServices(clazz); + + if (existing.isEmpty()) { + throw new IllegalStateException("Cannot updates service configurations. No services exist"); + } else { + B otherBuilder = withoutServices(clazz); + for (ServiceCreationConfiguration configuration : existing) { + ServiceCreationConfiguration replacement = configuration.build(update.apply(configuration.derive())); + if (replacement == null) { + throw new NullPointerException(configuration.getClass().getSimpleName() + ".build(...) returned a null configuration instance"); + } else { + otherBuilder = otherBuilder.withService(replacement); + } + } + return otherBuilder; + } + } + + @Override + public ClassLoader getClassLoader() { + return classLoader; + } + + @Override + public B withClassLoader(ClassLoader classLoader) { + return newBuilderWith(requireNonNull(classLoader)); + } + + @Override + public B withDefaultClassLoader() { + return newBuilderWith((ClassLoader) null); + } + + @SuppressWarnings("unchecked") + protected B newBuilderWith(Map> caches) { + if (getClass().equals(CoreConfigurationBuilder.class)) { + return (B) new CoreConfigurationBuilder<>(this, caches); + } else { + throw new AssertionError(); + } + } + + @SuppressWarnings("unchecked") + protected B newBuilderWith(Collection> serviceConfigurations) { + if (getClass().equals(CoreConfigurationBuilder.class)) { + return (B) new CoreConfigurationBuilder<>(this, serviceConfigurations); + } else { + throw new AssertionError(); + } + } + + @SuppressWarnings("unchecked") + protected B newBuilderWith(ClassLoader classLoader) { + if (getClass().equals(CoreConfigurationBuilder.class)) { + return (B) new CoreConfigurationBuilder<>(this, classLoader); + } else { + throw new AssertionError(); + } + } + +} diff --git a/core/src/main/java/org/ehcache/core/config/DefaultConfiguration.java b/ehcache-core/src/main/java/org/ehcache/core/config/DefaultConfiguration.java similarity index 91% rename from core/src/main/java/org/ehcache/core/config/DefaultConfiguration.java rename to ehcache-core/src/main/java/org/ehcache/core/config/DefaultConfiguration.java index 408637fc57..835f08b477 100644 --- a/core/src/main/java/org/ehcache/core/config/DefaultConfiguration.java +++ b/ehcache-core/src/main/java/org/ehcache/core/config/DefaultConfiguration.java @@ -26,12 +26,14 @@ import org.ehcache.config.CacheConfiguration; import org.ehcache.config.CacheRuntimeConfiguration; import org.ehcache.config.Configuration; +import org.ehcache.config.FluentConfigurationBuilder; import org.ehcache.core.HumanReadable; -import org.ehcache.core.internal.util.ClassLoading; +import org.ehcache.core.util.ClassLoading; import org.ehcache.spi.service.ServiceCreationConfiguration; import static java.util.Collections.unmodifiableCollection; import static java.util.Collections.unmodifiableMap; +import static org.ehcache.core.config.CoreConfigurationBuilder.newConfigurationBuilder; /** * Base implementation of {@link Configuration}. @@ -39,7 +41,7 @@ public final class DefaultConfiguration implements Configuration, HumanReadable { private final ConcurrentMap> caches; - private final Collection> services; + private final Collection> services; private final ClassLoader classLoader; /** @@ -66,7 +68,7 @@ public DefaultConfiguration(Configuration cfg) { * * @see #addCacheConfiguration(String, CacheConfiguration) */ - public DefaultConfiguration(ClassLoader classLoader, ServiceCreationConfiguration... services) { + public DefaultConfiguration(ClassLoader classLoader, ServiceCreationConfiguration... services) { this(emptyCacheMap(), classLoader, services); } @@ -78,7 +80,7 @@ public DefaultConfiguration(ClassLoader classLoader, ServiceCreationConfiguratio * @param classLoader the class loader to use for user types * @param services an array of service configurations */ - public DefaultConfiguration(Map> caches, ClassLoader classLoader, ServiceCreationConfiguration... services) { + public DefaultConfiguration(Map> caches, ClassLoader classLoader, ServiceCreationConfiguration... services) { this.services = unmodifiableCollection(Arrays.asList(services)); this.caches = new ConcurrentHashMap<>(caches); this.classLoader = classLoader == null ? ClassLoading.getDefaultClassLoader() : classLoader; @@ -96,7 +98,7 @@ public DefaultConfiguration(Map> caches, ClassL * {@inheritDoc} */ @Override - public Collection> getServiceCreationConfigurations() { + public Collection> getServiceCreationConfigurations() { return services; } @@ -108,6 +110,11 @@ public ClassLoader getClassLoader() { return classLoader; } + @Override + public FluentConfigurationBuilder derive() { + return newConfigurationBuilder(this); + } + private static Map> emptyCacheMap() { return Collections.emptyMap(); } @@ -168,7 +175,7 @@ public String readableString() { } StringBuilder serviceCreationConfigurationsToStringBuilder = new StringBuilder(); - for (ServiceCreationConfiguration serviceCreationConfiguration : services) { + for (ServiceCreationConfiguration serviceCreationConfiguration : services) { serviceCreationConfigurationsToStringBuilder.append("- "); if(serviceCreationConfiguration instanceof HumanReadable) { serviceCreationConfigurationsToStringBuilder diff --git a/core/src/main/java/org/ehcache/core/config/ExpiryUtils.java b/ehcache-core/src/main/java/org/ehcache/core/config/ExpiryUtils.java similarity index 100% rename from core/src/main/java/org/ehcache/core/config/ExpiryUtils.java rename to ehcache-core/src/main/java/org/ehcache/core/config/ExpiryUtils.java diff --git a/core/src/main/java/org/ehcache/core/config/package-info.java b/ehcache-core/src/main/java/org/ehcache/core/config/package-info.java similarity index 73% rename from core/src/main/java/org/ehcache/core/config/package-info.java rename to ehcache-core/src/main/java/org/ehcache/core/config/package-info.java index 6ffe44b748..205a0afd5d 100644 --- a/core/src/main/java/org/ehcache/core/config/package-info.java +++ b/ehcache-core/src/main/java/org/ehcache/core/config/package-info.java @@ -15,7 +15,6 @@ */ /** - * Package holding some core implementations related to configuration such as - * {@link org.ehcache.core.config.ResourcePoolsImpl} and {@link org.ehcache.core.config.DefaultConfiguration}. + * Package holding core configuration implementations and utilities. */ -package org.ehcache.core.config; \ No newline at end of file +package org.ehcache.core.config; diff --git a/core/src/main/java/org/ehcache/core/config/store/StoreEventSourceConfiguration.java b/ehcache-core/src/main/java/org/ehcache/core/config/store/StoreEventSourceConfiguration.java similarity index 83% rename from core/src/main/java/org/ehcache/core/config/store/StoreEventSourceConfiguration.java rename to ehcache-core/src/main/java/org/ehcache/core/config/store/StoreEventSourceConfiguration.java index 6625eaab5d..1e814f673a 100644 --- a/core/src/main/java/org/ehcache/core/config/store/StoreEventSourceConfiguration.java +++ b/ehcache-core/src/main/java/org/ehcache/core/config/store/StoreEventSourceConfiguration.java @@ -16,15 +16,14 @@ package org.ehcache.core.config.store; -import org.ehcache.core.internal.store.StoreConfigurationImpl; import org.ehcache.core.spi.store.Store; import org.ehcache.spi.service.ServiceConfiguration; /** * {@link ServiceConfiguration} used by the {@link org.ehcache.core.EhcacheManager} to populate the dispatcher - * concurrency in the {@link StoreConfigurationImpl}. + * concurrency in the {@link org.ehcache.core.store.StoreConfigurationImpl}. */ -public interface StoreEventSourceConfiguration extends ServiceConfiguration { +public interface StoreEventSourceConfiguration extends ServiceConfiguration { /** * Default dispatcher concurrency diff --git a/core/src/main/java/org/ehcache/core/config/store/StoreStatisticsConfiguration.java b/ehcache-core/src/main/java/org/ehcache/core/config/store/StoreStatisticsConfiguration.java similarity index 86% rename from core/src/main/java/org/ehcache/core/config/store/StoreStatisticsConfiguration.java rename to ehcache-core/src/main/java/org/ehcache/core/config/store/StoreStatisticsConfiguration.java index 473861b995..834ffa6cc6 100644 --- a/core/src/main/java/org/ehcache/core/config/store/StoreStatisticsConfiguration.java +++ b/ehcache-core/src/main/java/org/ehcache/core/config/store/StoreStatisticsConfiguration.java @@ -27,7 +27,7 @@ * Note that statistics about the store size, mapping and so on are not affected * by this configuration. Only operation statistics (e.g. get/put counts) are disabled. */ -public class StoreStatisticsConfiguration implements ServiceConfiguration { +public class StoreStatisticsConfiguration implements ServiceConfiguration { private final boolean operationStatisticsEnabled; @@ -43,4 +43,14 @@ public boolean isOperationStatisticsEnabled() { public Class getServiceType() { return Store.Provider.class; } + + @Override + public Boolean derive() { + return isOperationStatisticsEnabled(); + } + + @Override + public StoreStatisticsConfiguration build(Boolean enabled) { + return new StoreStatisticsConfiguration(enabled); + } } diff --git a/core/src/main/java/org/ehcache/core/config/store/package-info.java b/ehcache-core/src/main/java/org/ehcache/core/config/store/package-info.java similarity index 100% rename from core/src/main/java/org/ehcache/core/config/store/package-info.java rename to ehcache-core/src/main/java/org/ehcache/core/config/store/package-info.java diff --git a/core/src/main/java/org/ehcache/core/events/CacheEventDispatcher.java b/ehcache-core/src/main/java/org/ehcache/core/events/CacheEventDispatcher.java similarity index 100% rename from core/src/main/java/org/ehcache/core/events/CacheEventDispatcher.java rename to ehcache-core/src/main/java/org/ehcache/core/events/CacheEventDispatcher.java diff --git a/core/src/main/java/org/ehcache/core/events/CacheEventDispatcherFactory.java b/ehcache-core/src/main/java/org/ehcache/core/events/CacheEventDispatcherFactory.java similarity index 96% rename from core/src/main/java/org/ehcache/core/events/CacheEventDispatcherFactory.java rename to ehcache-core/src/main/java/org/ehcache/core/events/CacheEventDispatcherFactory.java index 8bf715ed09..a6781f5216 100644 --- a/core/src/main/java/org/ehcache/core/events/CacheEventDispatcherFactory.java +++ b/ehcache-core/src/main/java/org/ehcache/core/events/CacheEventDispatcherFactory.java @@ -38,7 +38,7 @@ public interface CacheEventDispatcherFactory extends Service { * * @return the {@link CacheEventDispatcher} */ - CacheEventDispatcher createCacheEventDispatcher(Store store, ServiceConfiguration... serviceConfigs); + CacheEventDispatcher createCacheEventDispatcher(Store store, ServiceConfiguration... serviceConfigs); /** * Releases an instance of {@link CacheEventDispatcher}, causing it to shutdown and release all diff --git a/core/src/main/java/org/ehcache/core/events/CacheEventListenerConfiguration.java b/ehcache-core/src/main/java/org/ehcache/core/events/CacheEventListenerConfiguration.java similarity index 92% rename from core/src/main/java/org/ehcache/core/events/CacheEventListenerConfiguration.java rename to ehcache-core/src/main/java/org/ehcache/core/events/CacheEventListenerConfiguration.java index 445c84ebba..18bc84296d 100644 --- a/core/src/main/java/org/ehcache/core/events/CacheEventListenerConfiguration.java +++ b/ehcache-core/src/main/java/org/ehcache/core/events/CacheEventListenerConfiguration.java @@ -26,7 +26,7 @@ /** * Configuration contract for setting up {@link org.ehcache.event.CacheEvent} system in a cache. */ -public interface CacheEventListenerConfiguration extends ServiceConfiguration { +public interface CacheEventListenerConfiguration extends ServiceConfiguration { /** * Indicates which {@link EventFiring firing mode} to use diff --git a/core/src/main/java/org/ehcache/core/events/CacheEventListenerProvider.java b/ehcache-core/src/main/java/org/ehcache/core/events/CacheEventListenerProvider.java similarity index 96% rename from core/src/main/java/org/ehcache/core/events/CacheEventListenerProvider.java rename to ehcache-core/src/main/java/org/ehcache/core/events/CacheEventListenerProvider.java index dda77e64fa..4c90b67b63 100644 --- a/core/src/main/java/org/ehcache/core/events/CacheEventListenerProvider.java +++ b/ehcache-core/src/main/java/org/ehcache/core/events/CacheEventListenerProvider.java @@ -35,7 +35,7 @@ public interface CacheEventListenerProvider extends Service { * * @return the CacheEventListener to be registered with the given {@link org.ehcache.Cache} */ - CacheEventListener createEventListener(String alias, ServiceConfiguration serviceConfiguration); + CacheEventListener createEventListener(String alias, ServiceConfiguration serviceConfiguration); /** * Releases a given {@link org.ehcache.event.CacheEventListener} diff --git a/core/src/main/java/org/ehcache/core/events/CacheEvents.java b/ehcache-core/src/main/java/org/ehcache/core/events/CacheEvents.java similarity index 100% rename from core/src/main/java/org/ehcache/core/events/CacheEvents.java rename to ehcache-core/src/main/java/org/ehcache/core/events/CacheEvents.java diff --git a/core/src/main/java/org/ehcache/core/events/CacheManagerListener.java b/ehcache-core/src/main/java/org/ehcache/core/events/CacheManagerListener.java similarity index 100% rename from core/src/main/java/org/ehcache/core/events/CacheManagerListener.java rename to ehcache-core/src/main/java/org/ehcache/core/events/CacheManagerListener.java diff --git a/core/src/main/java/org/ehcache/core/internal/events/EventListenerWrapper.java b/ehcache-core/src/main/java/org/ehcache/core/events/EventListenerWrapper.java similarity index 98% rename from core/src/main/java/org/ehcache/core/internal/events/EventListenerWrapper.java rename to ehcache-core/src/main/java/org/ehcache/core/events/EventListenerWrapper.java index f91477ca1a..2076b68d81 100644 --- a/core/src/main/java/org/ehcache/core/internal/events/EventListenerWrapper.java +++ b/ehcache-core/src/main/java/org/ehcache/core/events/EventListenerWrapper.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.ehcache.core.internal.events; +package org.ehcache.core.events; import org.ehcache.event.CacheEvent; import org.ehcache.event.CacheEventListener; diff --git a/core/src/main/java/org/ehcache/core/events/NullStoreEventDispatcher.java b/ehcache-core/src/main/java/org/ehcache/core/events/NullStoreEventDispatcher.java similarity index 96% rename from core/src/main/java/org/ehcache/core/events/NullStoreEventDispatcher.java rename to ehcache-core/src/main/java/org/ehcache/core/events/NullStoreEventDispatcher.java index 8bc80d7c4a..ff26a97fd7 100644 --- a/core/src/main/java/org/ehcache/core/events/NullStoreEventDispatcher.java +++ b/ehcache-core/src/main/java/org/ehcache/core/events/NullStoreEventDispatcher.java @@ -97,6 +97,11 @@ public void setEventOrdering(boolean ordering) { // Do nothing } + @Override + public void setSynchronous(boolean synchronous) { + // Do nothing + } + @Override public boolean isEventOrdering() { return false; diff --git a/core/src/main/java/org/ehcache/core/events/StateChangeListener.java b/ehcache-core/src/main/java/org/ehcache/core/events/StateChangeListener.java similarity index 100% rename from core/src/main/java/org/ehcache/core/events/StateChangeListener.java rename to ehcache-core/src/main/java/org/ehcache/core/events/StateChangeListener.java diff --git a/core/src/main/java/org/ehcache/core/events/StoreEventDispatcher.java b/ehcache-core/src/main/java/org/ehcache/core/events/StoreEventDispatcher.java similarity index 100% rename from core/src/main/java/org/ehcache/core/events/StoreEventDispatcher.java rename to ehcache-core/src/main/java/org/ehcache/core/events/StoreEventDispatcher.java diff --git a/core/src/main/java/org/ehcache/core/events/StoreEventSink.java b/ehcache-core/src/main/java/org/ehcache/core/events/StoreEventSink.java similarity index 100% rename from core/src/main/java/org/ehcache/core/events/StoreEventSink.java rename to ehcache-core/src/main/java/org/ehcache/core/events/StoreEventSink.java diff --git a/core/src/main/java/org/ehcache/core/events/package-info.java b/ehcache-core/src/main/java/org/ehcache/core/events/package-info.java similarity index 100% rename from core/src/main/java/org/ehcache/core/events/package-info.java rename to ehcache-core/src/main/java/org/ehcache/core/events/package-info.java diff --git a/core/src/main/java/org/ehcache/core/exceptions/ExceptionFactory.java b/ehcache-core/src/main/java/org/ehcache/core/exceptions/ExceptionFactory.java similarity index 100% rename from core/src/main/java/org/ehcache/core/exceptions/ExceptionFactory.java rename to ehcache-core/src/main/java/org/ehcache/core/exceptions/ExceptionFactory.java diff --git a/core/src/main/java/org/ehcache/core/exceptions/StorePassThroughException.java b/ehcache-core/src/main/java/org/ehcache/core/exceptions/StorePassThroughException.java similarity index 100% rename from core/src/main/java/org/ehcache/core/exceptions/StorePassThroughException.java rename to ehcache-core/src/main/java/org/ehcache/core/exceptions/StorePassThroughException.java diff --git a/core/src/main/java/org/ehcache/core/exceptions/package-info.java b/ehcache-core/src/main/java/org/ehcache/core/exceptions/package-info.java similarity index 100% rename from core/src/main/java/org/ehcache/core/exceptions/package-info.java rename to ehcache-core/src/main/java/org/ehcache/core/exceptions/package-info.java diff --git a/ehcache-core/src/main/java/org/ehcache/core/internal/resilience/ThrowingResilienceStrategy.java b/ehcache-core/src/main/java/org/ehcache/core/internal/resilience/ThrowingResilienceStrategy.java new file mode 100644 index 0000000000..b8a6b0cae9 --- /dev/null +++ b/ehcache-core/src/main/java/org/ehcache/core/internal/resilience/ThrowingResilienceStrategy.java @@ -0,0 +1,89 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.core.internal.resilience; + +import org.ehcache.Cache; +import org.ehcache.spi.resilience.ResilienceStrategy; +import org.ehcache.spi.resilience.StoreAccessException; + +import java.util.Map; + +public class ThrowingResilienceStrategy implements ResilienceStrategy { + @Override + public V getFailure(K key, StoreAccessException e) { + throw new RuntimeException(e); + } + + @Override + public boolean containsKeyFailure(K key, StoreAccessException e) { + throw new RuntimeException(e); + } + + @Override + public void putFailure(K key, V value, StoreAccessException e) { + throw new RuntimeException(e); + } + + @Override + public void removeFailure(K key, StoreAccessException e) { + throw new RuntimeException(e); + } + + @Override + public void clearFailure(StoreAccessException e) { + throw new RuntimeException(e); + } + + @Override + public Cache.Entry iteratorFailure(StoreAccessException e) { + throw new RuntimeException(e); + } + + @Override + public V putIfAbsentFailure(K key, V value, StoreAccessException e) { + throw new RuntimeException(e); + } + + @Override + public boolean removeFailure(K key, V value, StoreAccessException e) { + throw new RuntimeException(e); + } + + @Override + public V replaceFailure(K key, V value, StoreAccessException e) { + throw new RuntimeException(e); + } + + @Override + public boolean replaceFailure(K key, V value, V newValue, StoreAccessException e) { + throw new RuntimeException(e); + } + + @Override + public Map getAllFailure(Iterable keys, StoreAccessException e) { + throw new RuntimeException(e); + } + + @Override + public void putAllFailure(Map entries, StoreAccessException e) { + throw new RuntimeException(e); + } + + @Override + public void removeAllFailure(Iterable keys, StoreAccessException e) { + throw new RuntimeException(e); + } +} diff --git a/impl/src/main/java/org/ehcache/impl/internal/statistics/DefaultCacheStatistics.java b/ehcache-core/src/main/java/org/ehcache/core/internal/statistics/DefaultCacheStatistics.java similarity index 74% rename from impl/src/main/java/org/ehcache/impl/internal/statistics/DefaultCacheStatistics.java rename to ehcache-core/src/main/java/org/ehcache/core/internal/statistics/DefaultCacheStatistics.java index 875708c5f8..294d8c81f2 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/statistics/DefaultCacheStatistics.java +++ b/ehcache-core/src/main/java/org/ehcache/core/internal/statistics/DefaultCacheStatistics.java @@ -14,17 +14,18 @@ * limitations under the License. */ -package org.ehcache.impl.internal.statistics; +package org.ehcache.core.internal.statistics; import org.ehcache.core.InternalCache; import org.ehcache.core.statistics.BulkOps; import org.ehcache.core.statistics.CacheOperationOutcomes.GetOutcome; import org.ehcache.core.statistics.CacheOperationOutcomes.PutOutcome; import org.ehcache.core.statistics.CacheStatistics; +import org.ehcache.core.statistics.ChainedOperationObserver; +import org.ehcache.core.statistics.OperationStatistic; import org.ehcache.core.statistics.TierStatistics; -import org.terracotta.statistics.OperationStatistic; -import org.terracotta.statistics.ValueStatistic; -import org.terracotta.statistics.observer.ChainedOperationObserver; +import org.ehcache.core.statistics.ValueStatistic; +import org.terracotta.statistics.ValueStatistics; import java.util.Collections; import java.util.EnumSet; @@ -35,31 +36,31 @@ import static org.ehcache.core.statistics.CacheOperationOutcomes.PutIfAbsentOutcome; import static org.ehcache.core.statistics.CacheOperationOutcomes.RemoveOutcome; import static org.ehcache.core.statistics.CacheOperationOutcomes.ReplaceOutcome; -import static org.ehcache.impl.internal.statistics.StatsUtils.findLowestTier; -import static org.ehcache.impl.internal.statistics.StatsUtils.findOperationStatisticOnChildren; -import static org.ehcache.impl.internal.statistics.StatsUtils.findTiers; -import static org.terracotta.statistics.ValueStatistics.counter; +import static org.ehcache.core.internal.statistics.StatsUtils.findLowestTier; +import static org.ehcache.core.internal.statistics.StatsUtils.findOperationStatisticOnChildren; +import static org.ehcache.core.internal.statistics.StatsUtils.findTiers; +import static org.ehcache.core.statistics.SuppliedValueStatistic.counter; /** * Contains usage statistics relative to a given cache. */ -class DefaultCacheStatistics implements CacheStatistics { +public class DefaultCacheStatistics implements CacheStatistics { private volatile CompensatingCounters compensatingCounters = CompensatingCounters.empty(); - private final OperationStatistic get; - private final OperationStatistic put; - private final OperationStatistic remove; - private final OperationStatistic putIfAbsent; - private final OperationStatistic replace; - private final OperationStatistic conditionalRemove; + private final org.terracotta.statistics.OperationStatistic get; + private final org.terracotta.statistics.OperationStatistic put; + private final org.terracotta.statistics.OperationStatistic remove; + private final org.terracotta.statistics.OperationStatistic putIfAbsent; + private final org.terracotta.statistics.OperationStatistic replace; + private final org.terracotta.statistics.OperationStatistic conditionalRemove; private final InternalCache cache; - private final Map tierStatistics; + private final Map tierStatistics; private final TierStatistics lowestTier; - private final Map> knownStatistics; + private final Map> knownStatistics; public DefaultCacheStatistics(InternalCache cache) { this.cache = cache; @@ -78,7 +79,7 @@ public DefaultCacheStatistics(InternalCache cache) { tierStatistics = new HashMap<>(tierNames.length); for (String tierName : tierNames) { - TierStatistics tierStatistics = new DefaultTierStatistics(cache, tierName); + DefaultTierStatistics tierStatistics = new DefaultTierStatistics(cache, tierName); this.tierStatistics.put(tierName, tierStatistics); if (lowestTierName.equals(tierName)) { lowestTier = tierStatistics; @@ -89,29 +90,29 @@ public DefaultCacheStatistics(InternalCache cache) { knownStatistics = createKnownStatistics(); } + @Override public , S extends ChainedOperationObserver> void registerDerivedStatistic(Class outcomeClass, String statName, S derivedStatistic) { - OperationStatistic stat = findOperationStatisticOnChildren(cache, outcomeClass, statName); + OperationStatistic stat = new DelegatingOperationStatistic<>(findOperationStatisticOnChildren(cache, outcomeClass, statName)); stat.addDerivedStatistic(derivedStatistic); } - private Map> createKnownStatistics() { - Map> knownStatistics = new HashMap<>(30); - knownStatistics.put("Cache:HitCount", counter(this::getCacheHits)); - knownStatistics.put("Cache:MissCount", counter(this::getCacheMisses)); - knownStatistics.put("Cache:PutCount", counter(this::getCachePuts)); - knownStatistics.put("Cache:RemovalCount", counter(this::getCacheRemovals)); - knownStatistics.put("Cache:EvictionCount", counter(this::getCacheEvictions)); - knownStatistics.put("Cache:ExpirationCount", counter(this::getCacheExpirations)); + private Map> createKnownStatistics() { + Map> knownStatistics = new HashMap<>(30); + knownStatistics.put("Cache:HitCount", ValueStatistics.counter(this::getCacheHits)); + knownStatistics.put("Cache:MissCount", ValueStatistics.counter(this::getCacheMisses)); + knownStatistics.put("Cache:PutCount", ValueStatistics.counter(this::getCachePuts)); + knownStatistics.put("Cache:RemovalCount", ValueStatistics.counter(this::getCacheRemovals)); + knownStatistics.put("Cache:EvictionCount", ValueStatistics.counter(this::getCacheEvictions)); + knownStatistics.put("Cache:ExpirationCount", ValueStatistics.counter(this::getCacheExpirations)); - for (TierStatistics tier : tierStatistics.values()) { + for (DefaultTierStatistics tier : tierStatistics.values()) { knownStatistics.putAll(tier.getKnownStatistics()); } return Collections.unmodifiableMap(knownStatistics); } - @Override - public Map> getKnownStatistics() { + public Map> getKnownStatistics() { return knownStatistics; } @@ -123,9 +124,6 @@ public Map getTierStatistics() { @Override public void clear() { compensatingCounters = compensatingCounters.snapshot(this); - for (TierStatistics t : tierStatistics.values()) { - t.clear(); - } } @Override diff --git a/impl/src/main/java/org/ehcache/impl/internal/statistics/DefaultStatisticsService.java b/ehcache-core/src/main/java/org/ehcache/core/internal/statistics/DefaultStatisticsService.java similarity index 53% rename from impl/src/main/java/org/ehcache/impl/internal/statistics/DefaultStatisticsService.java rename to ehcache-core/src/main/java/org/ehcache/core/internal/statistics/DefaultStatisticsService.java index c17de6e887..45c5edcd38 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/statistics/DefaultStatisticsService.java +++ b/ehcache-core/src/main/java/org/ehcache/core/internal/statistics/DefaultStatisticsService.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.ehcache.impl.internal.statistics; +package org.ehcache.core.internal.statistics; import org.ehcache.Cache; import org.ehcache.Status; @@ -24,16 +24,28 @@ import org.ehcache.core.spi.service.CacheManagerProviderService; import org.ehcache.core.spi.service.StatisticsService; import org.ehcache.core.spi.store.InternalCacheManager; +import org.ehcache.core.spi.store.Store; import org.ehcache.core.statistics.CacheStatistics; +import org.ehcache.core.statistics.OperationObserver; +import org.ehcache.core.statistics.OperationStatistic; +import org.ehcache.core.statistics.StatisticType; +import org.ehcache.core.statistics.ZeroOperationStatistic; import org.ehcache.spi.service.Service; import org.ehcache.spi.service.ServiceDependencies; import org.ehcache.spi.service.ServiceProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.terracotta.statistics.MappedOperationStatistic; +import org.terracotta.statistics.StatisticsManager; +import java.io.Serializable; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.function.Supplier; + +import static org.terracotta.statistics.StatisticBuilder.operation; /** * Default implementation using the statistics calculated by the observers set on the caches. @@ -43,11 +55,11 @@ public class DefaultStatisticsService implements StatisticsService, CacheManager private static final Logger LOGGER = LoggerFactory.getLogger(DefaultStatisticsService.class); - private final ConcurrentMap cacheStatistics = new ConcurrentHashMap<>(); + private final ConcurrentMap cacheStatistics = new ConcurrentHashMap<>(); private volatile InternalCacheManager cacheManager; - private volatile boolean started = false; + @Override public CacheStatistics getCacheStatistics(String cacheName) { CacheStatistics stats = cacheStatistics.get(cacheName); if (stats == null) { @@ -56,8 +68,58 @@ public CacheStatistics getCacheStatistics(String cacheName) { return stats; } - public boolean isStarted() { - return started; + @Override + public void registerWithParent(Object toAssociate, Object parent) { + StatisticsManager.associate(toAssociate).withParent(parent); + } + + @Override + public , T extends Enum> OperationStatistic registerStoreStatistics(Store store, String targetName, int tierHeight, String tag, Map> translation, String statisticName) { + + Class outcomeType = getOutcomeType(translation); + + // If the original stat doesn't exist, we do not need to translate it + if (StatsUtils.hasOperationStat(store, outcomeType, targetName)) { + + MappedOperationStatistic operationStatistic = new MappedOperationStatistic<>(store, translation, statisticName, tierHeight, targetName, tag); + StatisticsManager.associate(operationStatistic).withParent(store); + return new DelegatedMappedOperationStatistics<>(operationStatistic); + } else { + return ZeroOperationStatistic.get(); + } + } + + /** + * From the Map of translation, we extract one of the items to get the declaring class of the enum. + * + * @param translation translation map + * @param type of the outcome + * @param type of the possible translations + * @return the outcome type + */ + private static , T extends Enum> Class getOutcomeType(Map> translation) { + Map.Entry> first = translation.entrySet().iterator().next(); + return first.getValue().iterator().next().getDeclaringClass(); + } + + @Override + public void deRegisterFromParent(Object toDisassociate, Object parent) { + StatisticsManager.dissociate(toDisassociate).fromParent(parent); + } + + @Override + public void cleanForNode(Object node) { + StatisticsManager.nodeFor(node).clean(); + } + + @Override + public void registerStatistic(Object context, String name, StatisticType type, Set tags, Supplier valueSupplier) { + StatisticsManager.createPassThroughStatistic(context, name, tags, convert(type), valueSupplier); + } + + @Override + public > OperationObserver createOperationStatistics(String name, Class outcome, String tag, Object context) { + return new DelegatingOperationObserver<>(operation(outcome).named(name).of(context).tag(tag).build()); } @Override @@ -67,8 +129,6 @@ public void start(ServiceProvider serviceProvider) { CacheManagerProviderService cacheManagerProviderService = serviceProvider.getService(CacheManagerProviderService.class); cacheManager = cacheManagerProviderService.getCacheManager(); cacheManager.registerListener(this); - - started = true; } @Override @@ -76,7 +136,6 @@ public void stop() { LOGGER.debug("Stopping service"); cacheManager.deregisterListener(this); cacheStatistics.clear(); - started = false; } @Override @@ -118,4 +177,14 @@ public void cacheRemoved(String alias, Cache cache) { cacheStatistics.remove(alias); } + private static org.terracotta.statistics.StatisticType convert(StatisticType type) { + switch (type) { + case COUNTER: + return org.terracotta.statistics.StatisticType.COUNTER; + case GAUGE: + return org.terracotta.statistics.StatisticType.GAUGE; + default: + throw new IllegalArgumentException("Untranslatable statistic type : " + type); + } + } } diff --git a/impl/src/main/java/org/ehcache/impl/internal/statistics/DefaultStatisticsServiceFactory.java b/ehcache-core/src/main/java/org/ehcache/core/internal/statistics/DefaultStatisticsServiceFactory.java similarity index 79% rename from impl/src/main/java/org/ehcache/impl/internal/statistics/DefaultStatisticsServiceFactory.java rename to ehcache-core/src/main/java/org/ehcache/core/internal/statistics/DefaultStatisticsServiceFactory.java index 2e8aaf561d..c88dbdd6c9 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/statistics/DefaultStatisticsServiceFactory.java +++ b/ehcache-core/src/main/java/org/ehcache/core/internal/statistics/DefaultStatisticsServiceFactory.java @@ -14,21 +14,23 @@ * limitations under the License. */ -package org.ehcache.impl.internal.statistics; +package org.ehcache.core.internal.statistics; import org.ehcache.core.spi.service.ServiceFactory; import org.ehcache.core.spi.service.StatisticsService; import org.ehcache.spi.service.ServiceCreationConfiguration; +import org.osgi.service.component.annotations.Component; +@Component public class DefaultStatisticsServiceFactory implements ServiceFactory { @Override - public StatisticsService create(ServiceCreationConfiguration serviceConfiguration) { + public StatisticsService create(ServiceCreationConfiguration serviceConfiguration) { return new DefaultStatisticsService(); } @Override - public Class getServiceType() { - return StatisticsService.class; + public Class getServiceType() { + return DefaultStatisticsService.class; } } diff --git a/ehcache-core/src/main/java/org/ehcache/core/internal/statistics/DefaultTierStatistics.java b/ehcache-core/src/main/java/org/ehcache/core/internal/statistics/DefaultTierStatistics.java new file mode 100755 index 0000000000..3a5bcf2c84 --- /dev/null +++ b/ehcache-core/src/main/java/org/ehcache/core/internal/statistics/DefaultTierStatistics.java @@ -0,0 +1,246 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.core.internal.statistics; + +import org.ehcache.Cache; +import org.ehcache.core.statistics.StoreOperationOutcomes; +import org.ehcache.core.statistics.TierOperationOutcomes; +import org.ehcache.core.statistics.TierStatistics; +import org.ehcache.core.statistics.ValueStatistic; +import org.terracotta.statistics.OperationStatistic; +import org.terracotta.statistics.ValueStatistics; +import org.terracotta.statistics.ZeroOperationStatistic; + +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.function.Supplier; + +import static org.ehcache.core.internal.statistics.StatsUtils.findStatisticOnDescendants; +import static org.ehcache.core.statistics.SuppliedValueStatistic.counter; +import static org.ehcache.core.statistics.SuppliedValueStatistic.gauge; + +/** + * Contains usage statistics relative to a given tier. + */ +public class DefaultTierStatistics implements TierStatistics { + + private volatile CompensatingCounters compensatingCounters = CompensatingCounters.empty(); + + private final Map> knownStatistics; + + private final OperationStatistic get; + private final OperationStatistic put; + private final OperationStatistic putIfAbsent; + private final OperationStatistic replace; + private final OperationStatistic conditionalReplace; + private final OperationStatistic remove; + private final OperationStatistic conditionalRemove; + private final OperationStatistic eviction; + private final OperationStatistic expiration; + private final OperationStatistic compute; + private final OperationStatistic computeIfAbsent; + + //Ehcache default to -1 if unavailable, but the management layer needs optional or null + // (since -1 can be a normal value for a stat). + private final Optional> mapping; + private final Optional> allocatedMemory; + private final Optional> occupiedMemory; + + public DefaultTierStatistics(Cache cache, String tierName) { + + get = findOperationStatistic(cache, tierName, "tier", "get"); + put = findOperationStatistic(cache, tierName, "put"); + putIfAbsent = findOperationStatistic(cache, tierName, "putIfAbsent"); + replace = findOperationStatistic(cache, tierName, "replace"); + conditionalReplace = findOperationStatistic(cache, tierName, "conditionalReplace"); + remove = findOperationStatistic(cache, tierName, "remove"); + conditionalRemove = findOperationStatistic(cache, tierName, "conditionalRemove"); + eviction = findOperationStatistic(cache, tierName, "tier", "eviction"); + expiration = findOperationStatistic(cache, tierName, "expiration"); + compute = findOperationStatistic(cache, tierName, "compute"); + computeIfAbsent = findOperationStatistic(cache, tierName, "computeIfAbsent"); + + mapping = findValueStatistics(cache, tierName, "mappings"); + allocatedMemory = findValueStatistics(cache, tierName, "allocatedMemory"); + occupiedMemory = findValueStatistics(cache, tierName, "occupiedMemory"); + + Map> knownStatistics = createKnownStatistics(tierName); + this.knownStatistics = Collections.unmodifiableMap(knownStatistics); + } + + private Map> createKnownStatistics(String tierName) { + Map> knownStatistics = new HashMap<>(7); + addIfPresent(knownStatistics, tierName + ":HitCount", get, this::getHits); + addIfPresent(knownStatistics, tierName + ":MissCount", get, this::getMisses); + addIfPresent(knownStatistics, tierName + ":PutCount", put, this::getPuts); + addIfPresent(knownStatistics, tierName + ":RemovalCount", remove, this::getRemovals); + + // These two a special because they are used by the cache so they should always be there + knownStatistics.put(tierName + ":EvictionCount", ValueStatistics.counter(this::getEvictions)); + knownStatistics.put(tierName + ":ExpirationCount", ValueStatistics.counter(this::getExpirations)); + + mapping.ifPresent(longValueStatistic -> knownStatistics.put(tierName + ":MappingCount", ValueStatistics.gauge(this::getMappings))); + allocatedMemory.ifPresent(longValueStatistic -> knownStatistics.put(tierName + ":AllocatedByteSize", ValueStatistics.gauge(this::getAllocatedByteSize))); + occupiedMemory.ifPresent(longValueStatistic -> knownStatistics.put(tierName + ":OccupiedByteSize", ValueStatistics.gauge(this::getOccupiedByteSize))); + return knownStatistics; + } + + /** + * Add the statistic as a known statistic only if the reference statistic is available. We consider that the reference statistic can only be + * an instance of {@code ZeroOperationStatistic} when statistics are disabled. + * + * @param knownStatistics map of known statistics + * @param name the name of the statistic to add + * @param reference the reference statistic that should be available for the statistic to be added + * @param valueSupplier the supplier that will provide the current value for the statistic + * @param type of the supplied value + */ + private static void addIfPresent(Map> knownStatistics, String name, OperationStatistic reference, Supplier valueSupplier) { + if(!(reference instanceof ZeroOperationStatistic)) { + knownStatistics.put(name, ValueStatistics.counter(valueSupplier)); + } + } + + public Map> getKnownStatistics() { + return knownStatistics; + } + + private > OperationStatistic findOperationStatistic(Cache cache, String tierName, String tag, String stat) { + return StatsUtils.>findStatisticOnDescendants(cache, tierName, tag, stat).orElse(ZeroOperationStatistic.get()); + } + + private > OperationStatistic findOperationStatistic(Cache cache, String tierName, String stat) { + return StatsUtils.>findStatisticOnDescendants(cache, tierName, stat).orElse(ZeroOperationStatistic.get()); + } + + private Optional> findValueStatistics(Cache cache, String tierName, String statName) { + return findStatisticOnDescendants(cache, tierName, statName); + } + + /** + * Reset the values for this tier. However, note that {@code mapping, allocatedMemory, occupiedMemory} + * but be reset since it doesn't make sense. + */ + @Override + public void clear() { + compensatingCounters = compensatingCounters.snapshot(this); + } + + @Override + public long getHits() { + return get.sum(EnumSet.of(TierOperationOutcomes.GetOutcome.HIT)) + + putIfAbsent.sum(EnumSet.of(StoreOperationOutcomes.PutIfAbsentOutcome.HIT)) + + replace.sum(EnumSet.of(StoreOperationOutcomes.ReplaceOutcome.REPLACED)) + + compute.sum(EnumSet.of(StoreOperationOutcomes.ComputeOutcome.HIT)) + + computeIfAbsent.sum(EnumSet.of(StoreOperationOutcomes.ComputeIfAbsentOutcome.HIT)) + + conditionalReplace.sum(EnumSet.of(StoreOperationOutcomes.ConditionalReplaceOutcome.REPLACED)) + + conditionalRemove.sum(EnumSet.of(StoreOperationOutcomes.ConditionalRemoveOutcome.REMOVED)) - + compensatingCounters.hits; + } + + @Override + public long getMisses() { + return get.sum(EnumSet.of(TierOperationOutcomes.GetOutcome.MISS)) + + putIfAbsent.sum(EnumSet.of(StoreOperationOutcomes.PutIfAbsentOutcome.PUT)) + + replace.sum(EnumSet.of(StoreOperationOutcomes.ReplaceOutcome.MISS)) + + computeIfAbsent.sum(EnumSet.of(StoreOperationOutcomes.ComputeIfAbsentOutcome.NOOP)) + + conditionalReplace.sum(EnumSet.of(StoreOperationOutcomes.ConditionalReplaceOutcome.MISS)) + + conditionalRemove.sum(EnumSet.of(StoreOperationOutcomes.ConditionalRemoveOutcome.MISS)) - + compensatingCounters.misses; + } + + @Override + public long getPuts() { + return put.sum(EnumSet.of(StoreOperationOutcomes.PutOutcome.PUT)) + + putIfAbsent.sum(EnumSet.of(StoreOperationOutcomes.PutIfAbsentOutcome.PUT)) + + compute.sum(EnumSet.of(StoreOperationOutcomes.ComputeOutcome.PUT)) + + computeIfAbsent.sum(EnumSet.of(StoreOperationOutcomes.ComputeIfAbsentOutcome.PUT)) + + replace.sum(EnumSet.of(StoreOperationOutcomes.ReplaceOutcome.REPLACED)) + + conditionalReplace.sum(EnumSet.of(StoreOperationOutcomes.ConditionalReplaceOutcome.REPLACED)) - + compensatingCounters.puts; + } + + @Override + public long getRemovals() { + return remove.sum(EnumSet.of(StoreOperationOutcomes.RemoveOutcome.REMOVED)) + + compute.sum(EnumSet.of(StoreOperationOutcomes.ComputeOutcome.REMOVED)) + + conditionalRemove.sum(EnumSet.of(StoreOperationOutcomes.ConditionalRemoveOutcome.REMOVED)) - + compensatingCounters.removals; + } + + @Override + public long getEvictions() { + return eviction.sum(EnumSet.of(TierOperationOutcomes.EvictionOutcome.SUCCESS)) - + compensatingCounters.evictions; + } + + @Override + public long getExpirations() { + return expiration.sum() - compensatingCounters.expirations; + } + + @Override + public long getMappings() { + return mapping.map(org.terracotta.statistics.ValueStatistic::value).orElse(-1L); + } + + @Override + public long getAllocatedByteSize() { + return allocatedMemory.map(org.terracotta.statistics.ValueStatistic::value).orElse(-1L); + } + + @Override + public long getOccupiedByteSize() { + return occupiedMemory.map(org.terracotta.statistics.ValueStatistic::value).orElse(-1L); + } + + private static class CompensatingCounters { + final long hits; + final long misses; + final long puts; + final long removals; + final long evictions; + final long expirations; + + private CompensatingCounters(long hits, long misses, long puts, long removals, long evictions, long expirations) { + this.hits = hits; + this.misses = misses; + this.puts = puts; + this.removals = removals; + this.evictions = evictions; + this.expirations = expirations; + } + + static CompensatingCounters empty() { + return new CompensatingCounters(0, 0, 0, 0, 0, 0); + } + + CompensatingCounters snapshot(DefaultTierStatistics statistics) { + return new CompensatingCounters( + statistics.getHits() + hits, + statistics.getMisses() + misses, + statistics.getPuts() + puts, + statistics.getRemovals() + removals, + statistics.getEvictions() + evictions, + statistics.getExpirations() + expirations + ); + } + } +} diff --git a/ehcache-core/src/main/java/org/ehcache/core/internal/statistics/DelegatedMappedOperationStatistics.java b/ehcache-core/src/main/java/org/ehcache/core/internal/statistics/DelegatedMappedOperationStatistics.java new file mode 100644 index 0000000000..65e8c624f2 --- /dev/null +++ b/ehcache-core/src/main/java/org/ehcache/core/internal/statistics/DelegatedMappedOperationStatistics.java @@ -0,0 +1,109 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.core.internal.statistics; + +import org.ehcache.core.statistics.ChainedOperationObserver; +import org.ehcache.core.statistics.OperationStatistic; +import org.terracotta.statistics.MappedOperationStatistic; + +import java.util.Collection; +import java.util.Set; +import java.util.stream.Collectors; + +public class DelegatedMappedOperationStatistics, D extends Enum> implements OperationStatistic { + + private final MappedOperationStatistic delegate; + + public DelegatedMappedOperationStatistics(MappedOperationStatistic operationStatistic) { + this.delegate = operationStatistic; + } + + @Override + public Class type() { + return delegate.type(); + } + + @Override + public long count(D type) { + return delegate.count(type); + } + + @Override + public long sum(Set types) { + return delegate.sum(types); + } + + @Override + public long sum() { + return delegate.sum(); + } + + @Override + public void begin() { + delegate.begin(); + } + + @Override + public void end(D result) { + delegate.end(result); + } + + @Override + public void addDerivedStatistic(ChainedOperationObserver derived) { + delegate.addDerivedStatistic(convert(derived)); + } + + @Override + public void removeDerivedStatistic(ChainedOperationObserver derived) { + delegate.removeDerivedStatistic(convert(derived)); + } + + @Override + public Collection> getDerivedStatistics() { + Collection> derivedStatistics = delegate.getDerivedStatistics(); + return derivedStatistics.stream().map(this::revert).collect(Collectors.toSet()); + } + + private ChainedOperationObserver revert(org.terracotta.statistics.observer.ChainedOperationObserver observer) { + return new ChainedOperationObserver() { + @Override + public void begin(long time) { + observer.begin(time); + } + + @Override + public void end(long time, long latency, D result) { + observer.end(time, latency, result); + } + }; + } + + private org.terracotta.statistics.observer.ChainedOperationObserver convert(ChainedOperationObserver observer) { + return new org.terracotta.statistics.observer.ChainedOperationObserver() { + @Override + public void begin(long time) { + observer.begin(time); + } + + @Override + public void end(long time, long latency, D result) { + observer.end(time, latency, result); + } + }; + } + + +} diff --git a/ehcache-core/src/main/java/org/ehcache/core/internal/statistics/DelegatingOperationObserver.java b/ehcache-core/src/main/java/org/ehcache/core/internal/statistics/DelegatingOperationObserver.java new file mode 100644 index 0000000000..a6b8e9223f --- /dev/null +++ b/ehcache-core/src/main/java/org/ehcache/core/internal/statistics/DelegatingOperationObserver.java @@ -0,0 +1,37 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.core.internal.statistics; + +import org.ehcache.core.statistics.OperationObserver; + +public class DelegatingOperationObserver> implements OperationObserver { + + private final org.terracotta.statistics.observer.OperationObserver observer; + + public DelegatingOperationObserver(org.terracotta.statistics.observer.OperationObserver operationObserver) { + this.observer = operationObserver; + } + + @Override + public void begin() { + this.observer.begin(); + } + + @Override + public void end(T result) { + this.observer.end(result); + } +} diff --git a/ehcache-core/src/main/java/org/ehcache/core/internal/statistics/DelegatingOperationStatistic.java b/ehcache-core/src/main/java/org/ehcache/core/internal/statistics/DelegatingOperationStatistic.java new file mode 100644 index 0000000000..0eb00e07df --- /dev/null +++ b/ehcache-core/src/main/java/org/ehcache/core/internal/statistics/DelegatingOperationStatistic.java @@ -0,0 +1,106 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.core.internal.statistics; + +import org.ehcache.core.statistics.ChainedOperationObserver; +import org.ehcache.core.statistics.OperationStatistic; + +import java.util.Collection; +import java.util.Set; +import java.util.stream.Collectors; + +public class DelegatingOperationStatistic> implements OperationStatistic { + + private final org.terracotta.statistics.OperationStatistic delegate; + + public DelegatingOperationStatistic(org.terracotta.statistics.OperationStatistic statistic) { + this.delegate = statistic; + } + + @Override + public Class type() { + return delegate.type(); + } + + @Override + public long count(T type) { + return delegate.count(type); + } + + @Override + public long sum(Set types) { + return delegate.sum(types); + } + + @Override + public long sum() { + return delegate.sum(); + } + + @Override + public void begin() { + delegate.begin(); + } + + @Override + public void end(T result) { + delegate.end(result); + } + + @Override + public void addDerivedStatistic(ChainedOperationObserver derived) { + delegate.addDerivedStatistic(convert(derived)); + } + + @Override + public void removeDerivedStatistic(ChainedOperationObserver derived) { + delegate.removeDerivedStatistic(convert(derived)); + } + + @Override + public Collection> getDerivedStatistics() { + Collection> derivedStatistics = delegate.getDerivedStatistics(); + return derivedStatistics.stream().map(this::revert).collect(Collectors.toSet()); + } + + private ChainedOperationObserver revert(org.terracotta.statistics.observer.ChainedOperationObserver observer) { + return new ChainedOperationObserver() { + @Override + public void begin(long time) { + observer.begin(time); + } + + @Override + public void end(long time, long latency, T result) { + observer.end(time, latency, result); + } + }; + } + + private org.terracotta.statistics.observer.ChainedOperationObserver convert(ChainedOperationObserver observer) { + return new org.terracotta.statistics.observer.ChainedOperationObserver() { + @Override + public void begin(long time) { + observer.begin(time); + } + + @Override + public void end(long time, long latency, T result) { + observer.end(time, latency, result); + } + }; + } +} diff --git a/impl/src/main/java/org/ehcache/impl/internal/statistics/StatsUtils.java b/ehcache-core/src/main/java/org/ehcache/core/internal/statistics/StatsUtils.java similarity index 93% rename from impl/src/main/java/org/ehcache/impl/internal/statistics/StatsUtils.java rename to ehcache-core/src/main/java/org/ehcache/core/internal/statistics/StatsUtils.java index 4e23231adb..ed0a424911 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/statistics/StatsUtils.java +++ b/ehcache-core/src/main/java/org/ehcache/core/internal/statistics/StatsUtils.java @@ -14,14 +14,17 @@ * limitations under the License. */ -package org.ehcache.impl.internal.statistics; +package org.ehcache.core.internal.statistics; import java.util.Collections; +import java.util.EnumSet; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.function.Consumer; import org.ehcache.Cache; +import org.ehcache.core.statistics.CacheOperationOutcomes; import org.ehcache.core.statistics.StoreOperationOutcomes; import org.terracotta.context.ContextManager; import org.terracotta.context.TreeNode; @@ -29,6 +32,7 @@ import org.terracotta.context.query.Matchers; import org.terracotta.context.query.Query; import org.terracotta.statistics.OperationStatistic; +import org.terracotta.statistics.derived.OperationResultFilter; import static org.terracotta.context.query.Matchers.*; import static org.terracotta.context.query.QueryBuilder.queryBuilder; @@ -252,4 +256,11 @@ protected boolean matchesSafely(OperationStatistic object) { return !result.isEmpty(); } + + public static void registerClearNotification(String alias, Cache cache, Consumer cacheClear) { + OperationStatistic clear = StatsUtils.findOperationStatisticOnChildren(cache, + CacheOperationOutcomes.ClearOutcome.class, "clear"); + clear.addDerivedStatistic(new OperationResultFilter<>(EnumSet.of(CacheOperationOutcomes.ClearOutcome.SUCCESS), + (time, latency) -> cacheClear.accept(alias))); + } } diff --git a/core/src/main/java/org/ehcache/core/internal/util/ValueSuppliers.java b/ehcache-core/src/main/java/org/ehcache/core/internal/util/ValueSuppliers.java similarity index 100% rename from core/src/main/java/org/ehcache/core/internal/util/ValueSuppliers.java rename to ehcache-core/src/main/java/org/ehcache/core/internal/util/ValueSuppliers.java diff --git a/ehcache-core/src/main/java/org/ehcache/core/osgi/EhcacheActivator.java b/ehcache-core/src/main/java/org/ehcache/core/osgi/EhcacheActivator.java new file mode 100644 index 0000000000..485af41100 --- /dev/null +++ b/ehcache-core/src/main/java/org/ehcache/core/osgi/EhcacheActivator.java @@ -0,0 +1,68 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.core.osgi; + +import org.ehcache.core.spi.service.ServiceFactory; +import org.ehcache.core.util.ClassLoading; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.atomic.AtomicReference; + +import static java.util.Spliterators.spliterator; +import static java.util.stream.Collectors.joining; +import static java.util.stream.StreamSupport.stream; + +public class EhcacheActivator implements BundleActivator { + + public static final String OSGI_LOADING = "org.ehcache.core.osgi"; + + private static final Logger LOGGER = LoggerFactory.getLogger(EhcacheActivator.class); + + private static final AtomicReference CORE_BUNDLE = new AtomicReference<>(); + + @Override + public void start(BundleContext context) throws Exception { + BundleContext currentContext = CORE_BUNDLE.getAndUpdate(current -> current == null ? context : current); + if (currentContext == null) { + String greeting = "Detected OSGi Environment (core is in bundle: " + context.getBundle() + ")"; + if ("false".equalsIgnoreCase(context.getProperty(OSGI_LOADING))) { + SafeOsgi.disableOSGiServiceLoading(); + LOGGER.info(greeting + ": OSGi Based Service Loading Disabled Via System/Framework Property - Extensions Outside This Bundle Will Not Be Detected"); + LOGGER.debug("JDK Service Loading Sees:\n\t" + stream(spliterator(ClassLoading.servicesOfType(ServiceFactory.class).iterator(), Long.MAX_VALUE, 0), false) + .map(sf -> sf.getServiceType().getName()).collect(joining("\n\t"))); + } else { + SafeOsgi.enableOSGiServiceLoading(); + LOGGER.info(greeting + ": Using OSGi Based Service Loading"); + } + } else { + throw new IllegalStateException("Multiple bundle instances running against the same core classes: existing bundle: " + currentContext.getBundle() + " new bundle: " + context.getBundle()); + } + } + + @Override + public void stop(BundleContext context) throws Exception { + SafeOsgi.disableOSGiServiceLoading(); + CORE_BUNDLE.set(null); + } + + public static BundleContext getCoreBundle() { + return CORE_BUNDLE.get(); + } +} diff --git a/ehcache-core/src/main/java/org/ehcache/core/osgi/OsgiServiceLoader.java b/ehcache-core/src/main/java/org/ehcache/core/osgi/OsgiServiceLoader.java new file mode 100644 index 0000000000..9dac70dd91 --- /dev/null +++ b/ehcache-core/src/main/java/org/ehcache/core/osgi/OsgiServiceLoader.java @@ -0,0 +1,53 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.core.osgi; + +import org.osgi.framework.BundleContext; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Stream; + +import static java.util.stream.Collectors.toList; + +/** + * An OSGi service based equivalent to {@link java.util.ServiceLoader}. + *

+ * This class is used by the {@link org.ehcache.core.spi.ServiceLocator ServiceLocator} (via + * {@link org.ehcache.core.util.ClassLoading#servicesOfType(Class) ClassLoading.servicesOfType(Class)}) to discover services when running inside an OSGi + * environment. This is needed when the required Ehcache services are split across multiple OSGi bundles. + */ +public class OsgiServiceLoader { + + /** + * Locate all services of type {@code T}. + * + * @param serviceType concrete service class + * @param service type + * @return an iterable of {@code T} services + */ + public static Iterable load(Class serviceType) { + try { + BundleContext coreBundle = EhcacheActivator.getCoreBundle(); + return coreBundle.getServiceReferences(serviceType, null).stream().map(coreBundle::getService).collect(toList()); + } catch (InvalidSyntaxException e) { + throw new AssertionError(e); + } + } +} diff --git a/ehcache-core/src/main/java/org/ehcache/core/osgi/SafeOsgi.java b/ehcache-core/src/main/java/org/ehcache/core/osgi/SafeOsgi.java new file mode 100644 index 0000000000..f36e3a32ad --- /dev/null +++ b/ehcache-core/src/main/java/org/ehcache/core/osgi/SafeOsgi.java @@ -0,0 +1,63 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.core.osgi; + +/** + * A classpath-safe decoupler for the OSGi service loading status. + *

+ * This class provides an OSGi class-decoupled way of checking whether OSGi service loading should be used. It is safe + * to load and call methods on this class when OSGi classes are not present. + */ +public final class SafeOsgi { + + private static volatile boolean OSGI_SERVICE_LOADING; + + /** + * Returns {@code true} if OSGi based service loading should be used. + *

+ * A {@code true} return indicates that Ehcache is running in an OSGi environment and that the user has enabled OSGi + * based service loading. + * + * @return {@code true} if OSGi service loading is enabled. + */ + public static boolean useOSGiServiceLoading() { + return OSGI_SERVICE_LOADING; + } + + /** + * Marks OSGi service loading as enabled. + *

+ * This is called by the {@link EhcacheActivator} when the user has enabled OSGi service loading. + */ + static void enableOSGiServiceLoading() { + OSGI_SERVICE_LOADING = true; + } + + /** + * Marks OSGi service loading as enabled. + *

+ * This is called by the {@link EhcacheActivator} when the user has not enabled OSGi service loading, and also when + * the Ehcache core bundle is stopped. + */ + static void disableOSGiServiceLoading() { + OSGI_SERVICE_LOADING = false; + } + + private SafeOsgi() { + //static holder + } +} diff --git a/core/src/main/java/org/ehcache/core/package-info.java b/ehcache-core/src/main/java/org/ehcache/core/package-info.java similarity index 100% rename from core/src/main/java/org/ehcache/core/package-info.java rename to ehcache-core/src/main/java/org/ehcache/core/package-info.java diff --git a/core/src/main/java/org/ehcache/core/resilience/DefaultRecoveryStore.java b/ehcache-core/src/main/java/org/ehcache/core/resilience/DefaultRecoveryStore.java similarity index 84% rename from core/src/main/java/org/ehcache/core/resilience/DefaultRecoveryStore.java rename to ehcache-core/src/main/java/org/ehcache/core/resilience/DefaultRecoveryStore.java index e56b7076b8..c7276d3c94 100644 --- a/core/src/main/java/org/ehcache/core/resilience/DefaultRecoveryStore.java +++ b/ehcache-core/src/main/java/org/ehcache/core/resilience/DefaultRecoveryStore.java @@ -20,8 +20,9 @@ import org.ehcache.spi.resilience.StoreAccessException; /** - * Default implementation of the {@link RecoveryStore} as used by the {@link RobustResilienceStrategy} and - * {@link RobustLoaderWriterResilienceStrategy}. It simply remove the required keys from the store. + * Default implementation of the {@link RecoveryStore}. + * + * It maps each obliterate operation to the equivalent remove operation. */ public class DefaultRecoveryStore implements RecoveryStore { diff --git a/core/src/main/java/org/ehcache/core/spi/LifeCycled.java b/ehcache-core/src/main/java/org/ehcache/core/spi/LifeCycled.java similarity index 100% rename from core/src/main/java/org/ehcache/core/spi/LifeCycled.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/LifeCycled.java diff --git a/core/src/main/java/org/ehcache/core/spi/LifeCycledAdapter.java b/ehcache-core/src/main/java/org/ehcache/core/spi/LifeCycledAdapter.java similarity index 100% rename from core/src/main/java/org/ehcache/core/spi/LifeCycledAdapter.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/LifeCycledAdapter.java diff --git a/core/src/main/java/org/ehcache/core/internal/service/ServiceLocator.java b/ehcache-core/src/main/java/org/ehcache/core/spi/ServiceLocator.java similarity index 93% rename from core/src/main/java/org/ehcache/core/internal/service/ServiceLocator.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/ServiceLocator.java index 2150a4bfbe..97bf72ed8e 100644 --- a/core/src/main/java/org/ehcache/core/internal/service/ServiceLocator.java +++ b/ehcache-core/src/main/java/org/ehcache/core/spi/ServiceLocator.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.ehcache.core.internal.service; +package org.ehcache.core.spi; import org.ehcache.config.Builder; import org.ehcache.spi.service.OptionalServiceDependencies; @@ -25,7 +25,6 @@ import org.ehcache.spi.service.ServiceCreationConfiguration; import org.ehcache.spi.service.ServiceDependencies; import org.ehcache.core.spi.service.ServiceFactory; -import org.ehcache.core.internal.util.ClassLoading; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,7 +41,6 @@ import java.util.List; import java.util.Map; import java.util.OptionalInt; -import java.util.ServiceLoader; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.Lock; @@ -56,6 +54,10 @@ import static java.util.Collections.unmodifiableSet; import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.toList; +import static java.util.stream.StreamSupport.stream; +import static org.ehcache.core.util.ClassLoading.delegationChain; +import static org.ehcache.core.util.ClassLoading.getDefaultClassLoader; +import static org.ehcache.core.util.ClassLoading.servicesOfType; /** * Provides discovery and tracking services for {@link Service} implementations. @@ -95,7 +97,7 @@ public Collection getServicesOfType(Class serviceType) return services.get(serviceType); } - public boolean knowsServiceFor(ServiceConfiguration serviceConfig) { + public boolean knowsServiceFor(ServiceConfiguration serviceConfig) { return services.contains(serviceConfig.getServiceType()); } @@ -228,8 +230,8 @@ private boolean hasRunningDependents(Service service, Iterable running) public static class DependencySet implements Builder { - @SuppressWarnings("rawtypes") - private final ServiceLoader serviceLoader = ClassLoading.libraryServiceLoaderFor(ServiceFactory.class); + @SuppressWarnings({"rawtypes", "unchecked"}) + private final Iterable> serviceFactories = (Iterable) servicesOfType(ServiceFactory.class); private final ServiceMap provided = new ServiceMap(); private final Set> requested = new HashSet<>(); @@ -247,7 +249,7 @@ public DependencySet with(Iterable services) { return this; } - public DependencySet with(ServiceCreationConfiguration config) { + public DependencySet with(ServiceCreationConfiguration config) { Class serviceType = config.getServiceType(); //TODO : This stanza is due to the way we use configure the JSR-107 service @@ -256,14 +258,14 @@ public DependencySet with(ServiceCreationConfiguration co } @SuppressWarnings("unchecked") - Collection> serviceFactories = getServiceFactories(serviceLoader).stream() + Collection> typedServiceFactories = stream(serviceFactories.spliterator(), false) .filter(f -> serviceType.isAssignableFrom(f.getServiceType())).map(f -> (ServiceFactory) f) .collect(toList()); - OptionalInt highestRank = serviceFactories.stream().mapToInt(ServiceFactory::rank).max(); + OptionalInt highestRank = typedServiceFactories.stream().mapToInt(ServiceFactory::rank).max(); if (highestRank.isPresent()) { - serviceFactories.stream().filter(f -> highestRank.getAsInt() == f.rank()).forEach(f -> with(f.create(config))); + typedServiceFactories.stream().filter(f -> highestRank.getAsInt() == f.rank()).forEach(f -> with(f.create(config))); return this; } else { throw new IllegalStateException("No factories exist for " + serviceType); @@ -329,7 +331,7 @@ public ServiceLocator build() { } if (includeMandatoryServices) { - for (List> factories : getServiceFactories(serviceLoader).stream().collect(groupingBy(ServiceFactory::getServiceType)).values()) { + for (List> factories : stream(serviceFactories.spliterator(), false).collect(groupingBy(ServiceFactory::getServiceType)).values()) { OptionalInt highestRank = factories.stream().mapToInt(ServiceFactory::rank).max(); if (highestRank.isPresent()) { @@ -415,17 +417,17 @@ private ServiceMap lookupService(ServiceMap resolved, Class< */ private Collection> discoverServices(ServiceMap resolved, Class serviceClass) { @SuppressWarnings("unchecked") - Collection> serviceFactories = getServiceFactories(serviceLoader).stream() + Collection> typedServiceFactories = stream(serviceFactories.spliterator(), false) .filter(f -> serviceClass.isAssignableFrom(f.getServiceType())).map(f -> (ServiceFactory) f) .filter(f -> !f.getClass().isAnnotationPresent(ServiceFactory.RequiresConfiguration.class)) .filter(f -> !provided.contains(f.getServiceType())) .filter(f -> !resolved.contains(f.getServiceType())) .collect(toList()); - OptionalInt highestRank = serviceFactories.stream().mapToInt(ServiceFactory::rank).max(); + OptionalInt highestRank = typedServiceFactories.stream().mapToInt(ServiceFactory::rank).max(); if (highestRank.isPresent()) { - return serviceFactories.stream().filter(f -> highestRank.getAsInt() == f.rank()).collect(toList()); + return typedServiceFactories.stream().filter(f -> highestRank.getAsInt() == f.rank()).collect(toList()); } else { return emptyList(); } @@ -466,7 +468,7 @@ private static Set> identifyImmediateDependenciesOf(fin if (optionalAnnotation != null) { for (String className : optionalAnnotation.value()) { try { - Class dependencyClass = ClassLoading.getDefaultClassLoader().loadClass(className); + Class dependencyClass = delegationChain(getDefaultClassLoader(), clazz.getClassLoader()).loadClass(className); if (Service.class.isAssignableFrom(dependencyClass)) { @SuppressWarnings("unchecked") Class serviceDependency = (Class) dependencyClass; @@ -515,15 +517,6 @@ private static Set> identifyTransitiveDependenciesOf(fi return transitive; } - @SuppressWarnings("unchecked") - private static Collection> getServiceFactories(@SuppressWarnings("rawtypes") ServiceLoader serviceFactory) { - List> list = new ArrayList<>(); - for (ServiceFactory factory : serviceFactory) { - list.add((ServiceFactory)factory); - } - return list; - } - private static class DependencyException extends Exception { private static final long serialVersionUID = -5269926129639323941L; diff --git a/core/src/main/java/org/ehcache/core/spi/package-info.java b/ehcache-core/src/main/java/org/ehcache/core/spi/package-info.java similarity index 100% rename from core/src/main/java/org/ehcache/core/spi/package-info.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/package-info.java diff --git a/core/src/main/java/org/ehcache/core/spi/service/CacheManagerProviderService.java b/ehcache-core/src/main/java/org/ehcache/core/spi/service/CacheManagerProviderService.java similarity index 100% rename from core/src/main/java/org/ehcache/core/spi/service/CacheManagerProviderService.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/service/CacheManagerProviderService.java diff --git a/core/src/main/java/org/ehcache/core/spi/service/DiskResourceService.java b/ehcache-core/src/main/java/org/ehcache/core/spi/service/DiskResourceService.java similarity index 99% rename from core/src/main/java/org/ehcache/core/spi/service/DiskResourceService.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/service/DiskResourceService.java index b2afe0c857..cde1fd48d4 100644 --- a/core/src/main/java/org/ehcache/core/spi/service/DiskResourceService.java +++ b/ehcache-core/src/main/java/org/ehcache/core/spi/service/DiskResourceService.java @@ -31,4 +31,4 @@ public interface DiskResourceService extends PersistableResourceService { * @return a {@link FileBasedPersistenceContext} */ FileBasedPersistenceContext createPersistenceContextWithin(PersistenceSpaceIdentifier identifier, String name) throws CachePersistenceException; -} \ No newline at end of file +} diff --git a/core/src/main/java/org/ehcache/core/spi/service/ExecutionService.java b/ehcache-core/src/main/java/org/ehcache/core/spi/service/ExecutionService.java similarity index 100% rename from core/src/main/java/org/ehcache/core/spi/service/ExecutionService.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/service/ExecutionService.java diff --git a/core/src/main/java/org/ehcache/core/spi/service/FileBasedPersistenceContext.java b/ehcache-core/src/main/java/org/ehcache/core/spi/service/FileBasedPersistenceContext.java similarity index 100% rename from core/src/main/java/org/ehcache/core/spi/service/FileBasedPersistenceContext.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/service/FileBasedPersistenceContext.java diff --git a/core/src/main/java/org/ehcache/core/spi/service/LocalPersistenceService.java b/ehcache-core/src/main/java/org/ehcache/core/spi/service/LocalPersistenceService.java similarity index 100% rename from core/src/main/java/org/ehcache/core/spi/service/LocalPersistenceService.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/service/LocalPersistenceService.java diff --git a/core/src/main/java/org/ehcache/core/spi/service/ServiceFactory.java b/ehcache-core/src/main/java/org/ehcache/core/spi/service/ServiceFactory.java similarity index 91% rename from core/src/main/java/org/ehcache/core/spi/service/ServiceFactory.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/service/ServiceFactory.java index b3cfbf0013..54c1688e3e 100644 --- a/core/src/main/java/org/ehcache/core/spi/service/ServiceFactory.java +++ b/ehcache-core/src/main/java/org/ehcache/core/spi/service/ServiceFactory.java @@ -58,12 +58,12 @@ default int rank() { * @param configuration the creation configuration, can be {@code null} for some services * @return the new service, not {@link Service#start(ServiceProvider) started} */ - T create(ServiceCreationConfiguration configuration); + T create(ServiceCreationConfiguration configuration); /** - * Queries a {@code ServiceFactory} to know which {@link Service} type it produces. + * Queries a {@code ServiceFactory} to know which concrete {@link Service} type it produces. * - * @return the class of the produced service. + * @return the concrete class of the produced service. */ Class getServiceType(); diff --git a/core/src/main/java/org/ehcache/core/spi/service/ServiceUtils.java b/ehcache-core/src/main/java/org/ehcache/core/spi/service/ServiceUtils.java similarity index 100% rename from core/src/main/java/org/ehcache/core/spi/service/ServiceUtils.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/service/ServiceUtils.java diff --git a/ehcache-core/src/main/java/org/ehcache/core/spi/service/StatisticsService.java b/ehcache-core/src/main/java/org/ehcache/core/spi/service/StatisticsService.java new file mode 100644 index 0000000000..720edee96f --- /dev/null +++ b/ehcache-core/src/main/java/org/ehcache/core/spi/service/StatisticsService.java @@ -0,0 +1,97 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.core.spi.service; + +import org.ehcache.core.spi.store.Store; +import org.ehcache.core.statistics.CacheStatistics; +import org.ehcache.core.statistics.OperationObserver; +import org.ehcache.core.statistics.OperationStatistic; +import org.ehcache.core.statistics.StatisticType; +import org.ehcache.spi.service.Service; + +import java.io.Serializable; +import java.util.Map; +import java.util.Set; +import java.util.function.Supplier; + +/** + * Service providing raw statistics for cache and tier usage. + */ +public interface StatisticsService extends Service { + + /** + * Return the object containing the statistics for a given cache name. + * + * @param cacheName name (alias) of the cache + * @return all the cache statistics + */ + CacheStatistics getCacheStatistics(String cacheName); + + /** + * Registers the object to parent + * @param toAssociate object to associate + * @param parent to which object is associated + */ + void registerWithParent(Object toAssociate, Object parent); + + /** + * Registers store of the cache for statistics + * @param store {@link Store} of the cache to be registered + * @param targetName statistics name after translation + * @param tierHeight of the store + * @param tag with which the statistics is associated + * @param translation relationship among maintained statistics + * @param statisticName name of the statistic + * @return statistics for the store + */ + , T extends Enum> OperationStatistic registerStoreStatistics(Store store, String targetName, int tierHeight, String tag, Map> translation, String statisticName); + + /** + * De-registers object from the parent + * @param toDeassociate object to dissociate + * @param parent to which object is associated + */ + void deRegisterFromParent(Object toDeassociate, Object parent); + + /** + * Clears all associations + * @param node for which all associations are cleared + */ + void cleanForNode(Object node); + + /** + * Register statistics with value supplier + * @param context association object + * @param name of the statistics + * @param type StatisticType to be registered + * @param tags with which the statistics is associated + * @param valueSupplier supplies the value to maintain statistics + * @param the generic type + */ + void registerStatistic(Object context, String name, StatisticType type, Set tags, Supplier valueSupplier); + + /** + * Create operation statistic for provided type + * @param name of the operation observer + * @param outcome Class of the type of statistic + * @param tag with which the statistics is associated + * @param context association object + * @return the observer for the provided statistics + */ + > OperationObserver createOperationStatistics(String name, Class outcome, String tag, Object context); + +} diff --git a/core/src/main/java/org/ehcache/core/spi/service/package-info.java b/ehcache-core/src/main/java/org/ehcache/core/spi/service/package-info.java similarity index 100% rename from core/src/main/java/org/ehcache/core/spi/service/package-info.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/service/package-info.java diff --git a/core/src/main/java/org/ehcache/core/spi/store/AbstractValueHolder.java b/ehcache-core/src/main/java/org/ehcache/core/spi/store/AbstractValueHolder.java similarity index 68% rename from core/src/main/java/org/ehcache/core/spi/store/AbstractValueHolder.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/store/AbstractValueHolder.java index cb3d0f3b58..b7a3b270fe 100644 --- a/core/src/main/java/org/ehcache/core/spi/store/AbstractValueHolder.java +++ b/ehcache-core/src/main/java/org/ehcache/core/spi/store/AbstractValueHolder.java @@ -53,20 +53,24 @@ protected AbstractValueHolder(long id, long creationTime, long expirationTime) { this.lastAccessTime = creationTime; } - protected abstract TimeUnit nativeTimeUnit(); - @Override - public long creationTime(TimeUnit unit) { - return unit.convert(creationTime, nativeTimeUnit()); + public long creationTime() { + return creationTime; } - public void setExpirationTime(long expirationTime, TimeUnit unit) { + /** + * Set the new expiration time in milliseconds. Can be {@link #NO_EXPIRE} if the entry + * shouldn't expire. + * + * @param expirationTime new expiration time + */ + public void setExpirationTime(long expirationTime) { if (expirationTime == NO_EXPIRE) { updateExpirationTime(NO_EXPIRE); - } else if (expirationTime <= 0) { + } else if (expirationTime < 0) { throw new IllegalArgumentException("invalid expiration time: " + expirationTime); } else { - updateExpirationTime(nativeTimeUnit().convert(expirationTime, unit)); + updateExpirationTime(expirationTime); } } @@ -83,49 +87,48 @@ private void updateExpirationTime(long update) { } public void accessed(long now, Duration expiration) { - final TimeUnit timeUnit = nativeTimeUnit(); if (expiration != null) { if (isExpiryDurationInfinite(expiration)) { - setExpirationTime(Store.ValueHolder.NO_EXPIRE, null); + setExpirationTime(Store.ValueHolder.NO_EXPIRE); } else { long newExpirationTime = ExpiryUtils.getExpirationMillis(now, expiration); - setExpirationTime(newExpirationTime, timeUnit); + setExpirationTime(newExpirationTime); } } - setLastAccessTime(now, timeUnit); + setLastAccessTime(now); } @Override - public long expirationTime(TimeUnit unit) { - final long expire = this.expirationTime; - if (expire == NO_EXPIRE) { - return NO_EXPIRE; - } - return unit.convert(expire, nativeTimeUnit()); + public long expirationTime() { + return this.expirationTime; } @Override - public boolean isExpired(long expirationTime, TimeUnit unit) { - final long expire = this.expirationTime; + public boolean isExpired(long expirationTime) { + long expire = this.expirationTime; if (expire == NO_EXPIRE) { return false; } - return expire <= nativeTimeUnit().convert(expirationTime, unit); + return expire <= expirationTime; } @Override - public long lastAccessTime(TimeUnit unit) { - return unit.convert(lastAccessTime, nativeTimeUnit()); + public long lastAccessTime() { + return lastAccessTime; } - public void setLastAccessTime(long lastAccessTime, TimeUnit unit) { - long update = unit.convert(lastAccessTime, nativeTimeUnit()); + /** + * Set the last time this entry was accessed in milliseconds. + * + * @param lastAccessTime last time the entry was accessed + */ + public void setLastAccessTime(long lastAccessTime) { while (true) { long current = this.lastAccessTime; - if (current >= update) { + if (current >= lastAccessTime) { break; } - if (ACCESSTIME_UPDATER.compareAndSet(this, current, update)) { + if (ACCESSTIME_UPDATER.compareAndSet(this, current, lastAccessTime)) { break; } } @@ -145,9 +148,9 @@ public boolean equals(Object obj) { if (obj instanceof AbstractValueHolder) { AbstractValueHolder other = (AbstractValueHolder) obj; return - other.creationTime(nativeTimeUnit()) == creationTime && creationTime(other.nativeTimeUnit()) == other.creationTime && - other.expirationTime(nativeTimeUnit()) == expirationTime && expirationTime(other.nativeTimeUnit()) == other.expirationTime && - other.lastAccessTime(nativeTimeUnit()) == lastAccessTime && lastAccessTime(other.nativeTimeUnit()) == other.lastAccessTime; + other.creationTime == creationTime && + other.expirationTime == expirationTime && + other.lastAccessTime == lastAccessTime; } return false; } diff --git a/ehcache-core/src/main/java/org/ehcache/core/spi/store/AbstractWrapperStoreProvider.java b/ehcache-core/src/main/java/org/ehcache/core/spi/store/AbstractWrapperStoreProvider.java new file mode 100644 index 0000000000..63e21e0b75 --- /dev/null +++ b/ehcache-core/src/main/java/org/ehcache/core/spi/store/AbstractWrapperStoreProvider.java @@ -0,0 +1,102 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.core.spi.store; + +import org.ehcache.core.collections.ConcurrentWeakIdentityHashMap; +import org.ehcache.core.spi.service.StatisticsService; +import org.ehcache.spi.service.OptionalServiceDependencies; +import org.ehcache.spi.service.Service; +import org.ehcache.spi.service.ServiceConfiguration; +import org.ehcache.spi.service.ServiceProvider; + +import java.util.Arrays; +import java.util.Map; + +import static org.ehcache.core.store.StoreSupport.selectStoreProvider; + +@OptionalServiceDependencies("org.ehcache.core.spi.service.StatisticsService") +public abstract class AbstractWrapperStoreProvider implements WrapperStore.Provider { + + private volatile ServiceProvider serviceProvider; + + private final Map, StoreReference> createdStores = new ConcurrentWeakIdentityHashMap<>(); + + + @Override + public Store createStore(Store.Configuration storeConfig, ServiceConfiguration... serviceConfigs) { + + Store.Provider underlyingStoreProvider = selectStoreProvider(serviceProvider, storeConfig.getResourcePools().getResourceTypeSet(), + Arrays.asList(serviceConfigs)); + Store store = underlyingStoreProvider.createStore(storeConfig, serviceConfigs); + + Store wrappedStore = wrap(store, storeConfig, serviceConfigs); + StatisticsService statisticsService = serviceProvider.getService(StatisticsService.class); + if (statisticsService != null) { + statisticsService.registerWithParent(store, wrappedStore); + } + createdStores.put(wrappedStore, new StoreReference<>(store, underlyingStoreProvider)); + return wrappedStore; + } + + protected abstract Store wrap(Store store, Store.Configuration storeConfig, ServiceConfiguration... serviceConfigs); + + @Override + public void releaseStore(Store resource) { + StoreReference storeRef = createdStores.remove(resource); + if (storeRef != null) { + storeRef.release(); + } + } + + @Override + public void initStore(Store resource) { + StoreReference storeRef = createdStores.get(resource); + if (storeRef != null) { + storeRef.init(); + } + } + + @Override + public void start(ServiceProvider serviceProvider) { + this.serviceProvider = serviceProvider; + } + + @Override + public void stop() { + this.createdStores.clear(); + this.serviceProvider = null; + } + + + private static class StoreReference { + + private final Store store; + private final Store.Provider provider; + + public StoreReference(Store store, Store.Provider provider) { + this.store = store; + this.provider = provider; + } + + public void release() { + provider.releaseStore(store); + } + + public void init() { + provider.initStore(store); + } + } +} diff --git a/core/src/main/java/org/ehcache/core/spi/store/ConfigurationChangeSupport.java b/ehcache-core/src/main/java/org/ehcache/core/spi/store/ConfigurationChangeSupport.java similarity index 100% rename from core/src/main/java/org/ehcache/core/spi/store/ConfigurationChangeSupport.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/store/ConfigurationChangeSupport.java diff --git a/core/src/main/java/org/ehcache/core/spi/store/InternalCacheManager.java b/ehcache-core/src/main/java/org/ehcache/core/spi/store/InternalCacheManager.java similarity index 100% rename from core/src/main/java/org/ehcache/core/spi/store/InternalCacheManager.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/store/InternalCacheManager.java diff --git a/core/src/main/java/org/ehcache/core/spi/store/Store.java b/ehcache-core/src/main/java/org/ehcache/core/spi/store/Store.java similarity index 93% rename from core/src/main/java/org/ehcache/core/spi/store/Store.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/store/Store.java index 82695686fb..d3c0fd82ff 100644 --- a/core/src/main/java/org/ehcache/core/spi/store/Store.java +++ b/ehcache-core/src/main/java/org/ehcache/core/spi/store/Store.java @@ -32,12 +32,13 @@ import java.util.Collection; import java.util.Map; import java.util.Set; -import java.util.concurrent.TimeUnit; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; +import javax.annotation.Nonnull; + /** * The {@code Store} interface represents the backing storage of a {@link Cache}. It abstracts the support for multiple * tiers, eventing, eviction and expiry. @@ -112,6 +113,22 @@ public interface Store extends ConfigurationChangeSupport { */ PutStatus put(K key, V value) throws StoreAccessException; + /** + * Maps the specified key to the specified value in this store. + * Neither the key nor the value can be {@code null}. + * + * @param key key with which the specified value is to be associated + * @param value value to be associated with the specified key + * @return the previously associated value + * + * @throws NullPointerException if any of the arguments is {@code null} + * @throws ClassCastException if the specified key or value are not of the correct types ({@code K} or {@code V}) + * @throws StoreAccessException if the mapping can't be installed + */ + default ValueHolder getAndPut(K key, V value) throws StoreAccessException { + return getAndCompute(key, (k, v) -> value); + } + /** * Maps the specified key to the specified value in this store, unless a non-expired mapping * already exists. @@ -160,6 +177,24 @@ public interface Store extends ConfigurationChangeSupport { */ boolean remove(K key) throws StoreAccessException; + + /** + * Removes the key (and its corresponding value) from this store. + * This method does nothing if the key is not mapped. + *

+ * The key cannot be {@code null}. + * + * @param key the key that needs to be removed + * @return the previously associated value + * + * @throws NullPointerException if the specified key is null + * @throws NullPointerException if the argument is {@code null} + * @throws StoreAccessException if the mapping can't be removed + */ + default ValueHolder getAndRemove(K key) throws StoreAccessException { + return getAndCompute(key, (k, v) -> null); + } + /** * Removes the entry for a key only if currently mapped to the given value * and the entry is not expired. @@ -470,35 +505,31 @@ interface ValueHolder extends Supplier { /** * Accessor to the creation time of this ValueHolder * - * @param unit the timeUnit to return the creation time in - * @return the creation time in the given unit + * @return the creation time in milliseconds */ - long creationTime(TimeUnit unit); + long creationTime(); /** * Accessor to the expiration time of this ValueHolder * - * @param unit the timeUnit to return the creation time in - * @return the expiration time in the given unit. A value of {@link #NO_EXPIRE} means that the ValueHolder will never expire. + * @return the expiration time in milliseconds. A value of {@link #NO_EXPIRE} means that the ValueHolder will never expire. */ - long expirationTime(TimeUnit unit); + long expirationTime(); /** * Check if the ValueHolder is expired relative to the specified time * - * @param expirationTime the expiration time relative to which the expiry check must be made - * @param unit the unit of the expiration time + * @param expirationTime the expiration time (in ms) relative to which the expiry check must be made * @return true if the ValueHolder expired relative to the given expiration time */ - boolean isExpired(long expirationTime, TimeUnit unit); + boolean isExpired(long expirationTime); /** * Accessor to the last access time of the Value held in this ValueHolder * - * @param unit the timeUnit to return the last access time in - * @return the last access time in the given unit + * @return the last access time in milliseconds */ - long lastAccessTime(TimeUnit unit); + long lastAccessTime(); /** * The combination of this identifier and the key that ValueHolder is mapped to should to be @@ -509,6 +540,14 @@ interface ValueHolder extends Supplier { */ long getId(); + /** + * Returns the value held by this value holder. This value can't be {@code null}. + * + * @return the value held + */ + @Nonnull + @Override + V get(); } /** @@ -525,7 +564,7 @@ interface Provider extends Service { * @param serviceConfigs the configurations the Provider may need to configure the Store * @return the Store honoring the configurations passed in */ - Store createStore(Configuration storeConfig, ServiceConfiguration... serviceConfigs); + Store createStore(Configuration storeConfig, ServiceConfiguration... serviceConfigs); /** * Informs this Provider, a Store it created is being disposed (i.e. closed) @@ -551,7 +590,7 @@ interface Provider extends Service { * to handle the resource types specified by {@code resourceTypes}; a rank of 0 indicates the store * can not handle all types specified in {@code resourceTypes} */ - int rank(Set> resourceTypes, Collection> serviceConfigs); + int rank(Set> resourceTypes, Collection> serviceConfigs); } /** diff --git a/core/src/main/java/org/ehcache/core/spi/store/WrapperStore.java b/ehcache-core/src/main/java/org/ehcache/core/spi/store/WrapperStore.java similarity index 95% rename from core/src/main/java/org/ehcache/core/spi/store/WrapperStore.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/store/WrapperStore.java index 2c7f2f29aa..4002b660b4 100644 --- a/core/src/main/java/org/ehcache/core/spi/store/WrapperStore.java +++ b/ehcache-core/src/main/java/org/ehcache/core/spi/store/WrapperStore.java @@ -43,7 +43,7 @@ interface Provider extends Store.Provider { * to the ranking * @return a non-negative rank indicating the ability of a {@code WrapperStore} created by this {@code Provider} */ - int wrapperStoreRank(Collection> serviceConfigs); + int wrapperStoreRank(Collection> serviceConfigs); } } diff --git a/core/src/main/java/org/ehcache/core/spi/store/events/StoreEvent.java b/ehcache-core/src/main/java/org/ehcache/core/spi/store/events/StoreEvent.java similarity index 100% rename from core/src/main/java/org/ehcache/core/spi/store/events/StoreEvent.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/store/events/StoreEvent.java diff --git a/core/src/main/java/org/ehcache/core/spi/store/events/StoreEventFilter.java b/ehcache-core/src/main/java/org/ehcache/core/spi/store/events/StoreEventFilter.java similarity index 100% rename from core/src/main/java/org/ehcache/core/spi/store/events/StoreEventFilter.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/store/events/StoreEventFilter.java diff --git a/core/src/main/java/org/ehcache/core/spi/store/events/StoreEventListener.java b/ehcache-core/src/main/java/org/ehcache/core/spi/store/events/StoreEventListener.java similarity index 100% rename from core/src/main/java/org/ehcache/core/spi/store/events/StoreEventListener.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/store/events/StoreEventListener.java diff --git a/core/src/main/java/org/ehcache/core/spi/store/events/StoreEventSource.java b/ehcache-core/src/main/java/org/ehcache/core/spi/store/events/StoreEventSource.java similarity index 81% rename from core/src/main/java/org/ehcache/core/spi/store/events/StoreEventSource.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/store/events/StoreEventSource.java index 07df024d72..d49187d70b 100644 --- a/core/src/main/java/org/ehcache/core/spi/store/events/StoreEventSource.java +++ b/ehcache-core/src/main/java/org/ehcache/core/spi/store/events/StoreEventSource.java @@ -41,7 +41,16 @@ public interface StoreEventSource { * * @param ordering {@code true} if ordering is desired, {@code false} for no ordering */ - void setEventOrdering(boolean ordering); + void setEventOrdering(boolean ordering) throws IllegalArgumentException; + + /** + * Toggles event synchronicity. + *

+ * If {@code true} it means events will be fire synchronously. + * + * @param synchronous {@code true} if synchronicity is desired, {@code false} for asynchronous. + */ + void setSynchronous(boolean synchronous) throws IllegalArgumentException; /** * Indicates if the related {@link org.ehcache.core.spi.store.Store} is delivering events ordered or not. diff --git a/core/src/main/java/org/ehcache/core/spi/store/events/package-info.java b/ehcache-core/src/main/java/org/ehcache/core/spi/store/events/package-info.java similarity index 100% rename from core/src/main/java/org/ehcache/core/spi/store/events/package-info.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/store/events/package-info.java diff --git a/core/src/main/java/org/ehcache/core/spi/store/heap/LimitExceededException.java b/ehcache-core/src/main/java/org/ehcache/core/spi/store/heap/LimitExceededException.java similarity index 100% rename from core/src/main/java/org/ehcache/core/spi/store/heap/LimitExceededException.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/store/heap/LimitExceededException.java diff --git a/core/src/main/java/org/ehcache/core/spi/store/heap/SizeOfEngine.java b/ehcache-core/src/main/java/org/ehcache/core/spi/store/heap/SizeOfEngine.java similarity index 100% rename from core/src/main/java/org/ehcache/core/spi/store/heap/SizeOfEngine.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/store/heap/SizeOfEngine.java diff --git a/core/src/main/java/org/ehcache/core/spi/store/heap/SizeOfEngineProvider.java b/ehcache-core/src/main/java/org/ehcache/core/spi/store/heap/SizeOfEngineProvider.java similarity index 96% rename from core/src/main/java/org/ehcache/core/spi/store/heap/SizeOfEngineProvider.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/store/heap/SizeOfEngineProvider.java index ffa7f6c8eb..28feed15df 100644 --- a/core/src/main/java/org/ehcache/core/spi/store/heap/SizeOfEngineProvider.java +++ b/ehcache-core/src/main/java/org/ehcache/core/spi/store/heap/SizeOfEngineProvider.java @@ -36,5 +36,5 @@ public interface SizeOfEngineProvider extends Service { * @return {@link SizeOfEngine} instance */ - SizeOfEngine createSizeOfEngine(ResourceUnit resourceUnit, ServiceConfiguration... serviceConfigs); + SizeOfEngine createSizeOfEngine(ResourceUnit resourceUnit, ServiceConfiguration... serviceConfigs); } diff --git a/core/src/main/java/org/ehcache/core/spi/store/heap/package-info.java b/ehcache-core/src/main/java/org/ehcache/core/spi/store/heap/package-info.java similarity index 100% rename from core/src/main/java/org/ehcache/core/spi/store/heap/package-info.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/store/heap/package-info.java diff --git a/core/src/main/java/org/ehcache/core/spi/store/package-info.java b/ehcache-core/src/main/java/org/ehcache/core/spi/store/package-info.java similarity index 100% rename from core/src/main/java/org/ehcache/core/spi/store/package-info.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/store/package-info.java diff --git a/core/src/main/java/org/ehcache/core/spi/store/tiering/AuthoritativeTier.java b/ehcache-core/src/main/java/org/ehcache/core/spi/store/tiering/AuthoritativeTier.java similarity index 97% rename from core/src/main/java/org/ehcache/core/spi/store/tiering/AuthoritativeTier.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/store/tiering/AuthoritativeTier.java index c1c123c431..4cb97c40da 100644 --- a/core/src/main/java/org/ehcache/core/spi/store/tiering/AuthoritativeTier.java +++ b/ehcache-core/src/main/java/org/ehcache/core/spi/store/tiering/AuthoritativeTier.java @@ -114,7 +114,7 @@ interface Provider extends Service { * @param serviceConfigs a collection of service configurations * @return the new authoritative tier */ - AuthoritativeTier createAuthoritativeTier(Configuration storeConfig, ServiceConfiguration... serviceConfigs); + AuthoritativeTier createAuthoritativeTier(Configuration storeConfig, ServiceConfiguration... serviceConfigs); /** * Releases an {@link AuthoritativeTier}. @@ -146,7 +146,7 @@ interface Provider extends Service { * to handle the resource type specified by {@code authorityResource}; a rank of 0 indicates the authority * can not handle the type specified in {@code authorityResource} */ - int rankAuthority(ResourceType authorityResource, Collection> serviceConfigs); + int rankAuthority(ResourceType authorityResource, Collection> serviceConfigs); } } diff --git a/core/src/main/java/org/ehcache/core/spi/store/tiering/CachingTier.java b/ehcache-core/src/main/java/org/ehcache/core/spi/store/tiering/CachingTier.java similarity index 88% rename from core/src/main/java/org/ehcache/core/spi/store/tiering/CachingTier.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/store/tiering/CachingTier.java index 862aecea15..cdaf58a46d 100644 --- a/core/src/main/java/org/ehcache/core/spi/store/tiering/CachingTier.java +++ b/ehcache-core/src/main/java/org/ehcache/core/spi/store/tiering/CachingTier.java @@ -53,6 +53,20 @@ public interface CachingTier extends ConfigurationChangeSupport { */ Store.ValueHolder getOrComputeIfAbsent(K key, Function> source) throws StoreAccessException; + /** + * Either return the value holder currently in the caching tier, or return the provided default. + *

+ * Note that in case of expired value holders, {@code null} will be returned and the mapping will be invalidated. + * + * @param key the key + * @param source the function that computes the default value when absent from this tier + * + * @return the value holder, or {@code null} + * + * @throws StoreAccessException if the mapping cannot be retrieved or stored + */ + Store.ValueHolder getOrDefault(K key, Function> source) throws StoreAccessException; + /** * Removes a mapping, triggering the {@link InvalidationListener} if registered. * @@ -131,7 +145,7 @@ interface Provider extends Service { * * @return the new caching tier */ - CachingTier createCachingTier(Store.Configuration storeConfig, ServiceConfiguration... serviceConfigs); + CachingTier createCachingTier(Store.Configuration storeConfig, ServiceConfiguration... serviceConfigs); /** * Releases a {@link CachingTier}. @@ -163,7 +177,7 @@ interface Provider extends Service { * to handle the resource types specified by {@code resourceTypes}; a rank of 0 indicates the caching tier * can not handle the type specified in {@code resourceTypes} */ - int rankCachingTier(Set> resourceTypes, Collection> serviceConfigs); + int rankCachingTier(Set> resourceTypes, Collection> serviceConfigs); } } diff --git a/core/src/main/java/org/ehcache/core/spi/store/tiering/HigherCachingTier.java b/ehcache-core/src/main/java/org/ehcache/core/spi/store/tiering/HigherCachingTier.java similarity index 97% rename from core/src/main/java/org/ehcache/core/spi/store/tiering/HigherCachingTier.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/store/tiering/HigherCachingTier.java index 4be5992e0a..bda5705091 100644 --- a/core/src/main/java/org/ehcache/core/spi/store/tiering/HigherCachingTier.java +++ b/ehcache-core/src/main/java/org/ehcache/core/spi/store/tiering/HigherCachingTier.java @@ -77,7 +77,7 @@ interface Provider extends Service { * * @return the new higher caching tier */ - HigherCachingTier createHigherCachingTier(Store.Configuration storeConfig, ServiceConfiguration... serviceConfigs); + HigherCachingTier createHigherCachingTier(Store.Configuration storeConfig, ServiceConfiguration... serviceConfigs); /** * Releases a {@link HigherCachingTier}. diff --git a/core/src/main/java/org/ehcache/core/spi/store/tiering/LowerCachingTier.java b/ehcache-core/src/main/java/org/ehcache/core/spi/store/tiering/LowerCachingTier.java similarity index 92% rename from core/src/main/java/org/ehcache/core/spi/store/tiering/LowerCachingTier.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/store/tiering/LowerCachingTier.java index cfee5149f1..d9e2860e01 100644 --- a/core/src/main/java/org/ehcache/core/spi/store/tiering/LowerCachingTier.java +++ b/ehcache-core/src/main/java/org/ehcache/core/spi/store/tiering/LowerCachingTier.java @@ -48,6 +48,16 @@ public interface LowerCachingTier extends ConfigurationChangeSupport { */ Store.ValueHolder installMapping(K key, Function> source) throws StoreAccessException; + /** + * Return the value holder currently in this tier. + * + * @param key the key + * @return the value holder, or {@code null} + * + * @throws StoreAccessException if the mapping cannot be access + */ + Store.ValueHolder get(K key) throws StoreAccessException; + /** * Return the value holder currently in this tier and removes it atomically. * @@ -114,7 +124,7 @@ interface Provider extends Service { * * @return the new lower caching tier */ - LowerCachingTier createCachingTier(Store.Configuration storeConfig, ServiceConfiguration... serviceConfigs); + LowerCachingTier createCachingTier(Store.Configuration storeConfig, ServiceConfiguration... serviceConfigs); /** * Releases a {@link LowerCachingTier}. diff --git a/core/src/main/java/org/ehcache/core/spi/store/tiering/package-info.java b/ehcache-core/src/main/java/org/ehcache/core/spi/store/tiering/package-info.java similarity index 100% rename from core/src/main/java/org/ehcache/core/spi/store/tiering/package-info.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/store/tiering/package-info.java diff --git a/core/src/main/java/org/ehcache/core/spi/time/SystemTimeSource.java b/ehcache-core/src/main/java/org/ehcache/core/spi/time/SystemTimeSource.java similarity index 100% rename from core/src/main/java/org/ehcache/core/spi/time/SystemTimeSource.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/time/SystemTimeSource.java diff --git a/core/src/main/java/org/ehcache/core/spi/time/TickingTimeSource.java b/ehcache-core/src/main/java/org/ehcache/core/spi/time/TickingTimeSource.java similarity index 100% rename from core/src/main/java/org/ehcache/core/spi/time/TickingTimeSource.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/time/TickingTimeSource.java diff --git a/core/src/main/java/org/ehcache/core/spi/time/TimeSource.java b/ehcache-core/src/main/java/org/ehcache/core/spi/time/TimeSource.java similarity index 100% rename from core/src/main/java/org/ehcache/core/spi/time/TimeSource.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/time/TimeSource.java diff --git a/core/src/main/java/org/ehcache/core/spi/time/TimeSourceService.java b/ehcache-core/src/main/java/org/ehcache/core/spi/time/TimeSourceService.java similarity index 100% rename from core/src/main/java/org/ehcache/core/spi/time/TimeSourceService.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/time/TimeSourceService.java diff --git a/core/src/main/java/org/ehcache/core/spi/time/package-info.java b/ehcache-core/src/main/java/org/ehcache/core/spi/time/package-info.java similarity index 100% rename from core/src/main/java/org/ehcache/core/spi/time/package-info.java rename to ehcache-core/src/main/java/org/ehcache/core/spi/time/package-info.java diff --git a/core/src/main/java/org/ehcache/core/statistics/AuthoritativeTierOperationOutcomes.java b/ehcache-core/src/main/java/org/ehcache/core/statistics/AuthoritativeTierOperationOutcomes.java similarity index 100% rename from core/src/main/java/org/ehcache/core/statistics/AuthoritativeTierOperationOutcomes.java rename to ehcache-core/src/main/java/org/ehcache/core/statistics/AuthoritativeTierOperationOutcomes.java diff --git a/core/src/main/java/org/ehcache/core/statistics/BulkOps.java b/ehcache-core/src/main/java/org/ehcache/core/statistics/BulkOps.java similarity index 100% rename from core/src/main/java/org/ehcache/core/statistics/BulkOps.java rename to ehcache-core/src/main/java/org/ehcache/core/statistics/BulkOps.java diff --git a/core/src/main/java/org/ehcache/core/statistics/CacheOperationOutcomes.java b/ehcache-core/src/main/java/org/ehcache/core/statistics/CacheOperationOutcomes.java similarity index 100% rename from core/src/main/java/org/ehcache/core/statistics/CacheOperationOutcomes.java rename to ehcache-core/src/main/java/org/ehcache/core/statistics/CacheOperationOutcomes.java diff --git a/core/src/main/java/org/ehcache/core/statistics/CacheStatistics.java b/ehcache-core/src/main/java/org/ehcache/core/statistics/CacheStatistics.java similarity index 92% rename from core/src/main/java/org/ehcache/core/statistics/CacheStatistics.java rename to ehcache-core/src/main/java/org/ehcache/core/statistics/CacheStatistics.java index 48f74a1e8a..a115c2a5cf 100644 --- a/core/src/main/java/org/ehcache/core/statistics/CacheStatistics.java +++ b/ehcache-core/src/main/java/org/ehcache/core/statistics/CacheStatistics.java @@ -16,9 +16,6 @@ package org.ehcache.core.statistics; -import org.terracotta.statistics.ValueStatistic; -import org.terracotta.statistics.observer.ChainedOperationObserver; - import java.util.Map; /** @@ -26,13 +23,6 @@ */ public interface CacheStatistics { - /** - * List of statistics tracked on this cache - * - * @return a map of statistics per name - */ - Map> getKnownStatistics(); - /** * Map of tier statistics on this cache. Per tier name * diff --git a/core/src/main/java/org/ehcache/core/statistics/CachingTierOperationOutcomes.java b/ehcache-core/src/main/java/org/ehcache/core/statistics/CachingTierOperationOutcomes.java similarity index 100% rename from core/src/main/java/org/ehcache/core/statistics/CachingTierOperationOutcomes.java rename to ehcache-core/src/main/java/org/ehcache/core/statistics/CachingTierOperationOutcomes.java diff --git a/buildSrc/build.gradle b/ehcache-core/src/main/java/org/ehcache/core/statistics/ChainedObserver.java similarity index 77% rename from buildSrc/build.gradle rename to ehcache-core/src/main/java/org/ehcache/core/statistics/ChainedObserver.java index abd602ec69..19b0cbac93 100644 --- a/buildSrc/build.gradle +++ b/ehcache-core/src/main/java/org/ehcache/core/statistics/ChainedObserver.java @@ -13,12 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package org.ehcache.core.statistics; -apply plugin: 'groovy' - -repositories { jcenter() } -dependencies { - compile gradleApi() - compile localGroovy() - compile 'com.github.jengelman.gradle.plugins:shadow:2.0.4' +public interface ChainedObserver { } diff --git a/ehcache-core/src/main/java/org/ehcache/core/statistics/ChainedOperationObserver.java b/ehcache-core/src/main/java/org/ehcache/core/statistics/ChainedOperationObserver.java new file mode 100644 index 0000000000..1dc95ff0dc --- /dev/null +++ b/ehcache-core/src/main/java/org/ehcache/core/statistics/ChainedOperationObserver.java @@ -0,0 +1,24 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.core.statistics; + +public interface ChainedOperationObserver> extends ChainedObserver { + + void begin(long time); + + void end(long time, long latency, T result); + +} diff --git a/core/src/main/java/org/ehcache/core/statistics/HigherCachingTierOperationOutcomes.java b/ehcache-core/src/main/java/org/ehcache/core/statistics/HigherCachingTierOperationOutcomes.java similarity index 100% rename from core/src/main/java/org/ehcache/core/statistics/HigherCachingTierOperationOutcomes.java rename to ehcache-core/src/main/java/org/ehcache/core/statistics/HigherCachingTierOperationOutcomes.java diff --git a/core/src/main/java/org/ehcache/core/statistics/LowerCachingTierOperationsOutcome.java b/ehcache-core/src/main/java/org/ehcache/core/statistics/LowerCachingTierOperationsOutcome.java similarity index 100% rename from core/src/main/java/org/ehcache/core/statistics/LowerCachingTierOperationsOutcome.java rename to ehcache-core/src/main/java/org/ehcache/core/statistics/LowerCachingTierOperationsOutcome.java diff --git a/ehcache-core/src/main/java/org/ehcache/core/statistics/OperationObserver.java b/ehcache-core/src/main/java/org/ehcache/core/statistics/OperationObserver.java new file mode 100644 index 0000000000..55fb6700c5 --- /dev/null +++ b/ehcache-core/src/main/java/org/ehcache/core/statistics/OperationObserver.java @@ -0,0 +1,47 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.core.statistics; + +/** + * Operation observers track the occurrence of processes which take a finite time + * and can potential terminate in different ways. + *

+ * Operations must have an associated enum type that represents their possible + * outcomes. An example of such an enum type would be: + *

+ * enum PlaneFlight {
+ *   LAND, CRASH;
+ * }
+ * 
+ * + * @param Enum type representing the possible operations 'results' + */ +public interface OperationObserver> { + + /** + * Called immediately prior to the operation beginning. + */ + void begin(); + + /** + * Called immediately after the operation completes with no interesting parameters, and with the same thread the called {{@link #begin()}} before. + * + * @param result the operation result + */ + void end(T result); + +} diff --git a/ehcache-core/src/main/java/org/ehcache/core/statistics/OperationStatistic.java b/ehcache-core/src/main/java/org/ehcache/core/statistics/OperationStatistic.java new file mode 100644 index 0000000000..afd1aabdf7 --- /dev/null +++ b/ehcache-core/src/main/java/org/ehcache/core/statistics/OperationStatistic.java @@ -0,0 +1,54 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.core.statistics; + +import java.util.Set; + +import static org.ehcache.core.statistics.SuppliedValueStatistic.counter; + + +public interface OperationStatistic> extends OperationObserver, SourceStatistic> { + + Class type(); + + /** + * Return a {@link ValueStatistic} returning the count for the given result. + * + * @param result the result of interest + * @return a {@code ValueStatistic} instance + */ + default ValueStatistic statistic(T result) { + return counter(() -> count(result)); + } + + default ValueStatistic statistic(Set results) { + return counter(() -> sum(results)); + } + + /** + * Return the count of operations with the given type. + * + * @param type the result type + * @return the operation count + */ + long count(T type); + + long sum(Set types); + + long sum(); + +} diff --git a/ehcache-core/src/main/java/org/ehcache/core/statistics/SourceStatistic.java b/ehcache-core/src/main/java/org/ehcache/core/statistics/SourceStatistic.java new file mode 100644 index 0000000000..b9472ab6f5 --- /dev/null +++ b/ehcache-core/src/main/java/org/ehcache/core/statistics/SourceStatistic.java @@ -0,0 +1,42 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.core.statistics; + +import java.util.Collection; + +public interface SourceStatistic { + + /** + * Register the given {@code Observer} to be called by this {@code SourceStatistic} + * + * @param derived statistic to be registered + */ + void addDerivedStatistic(T derived); + + /** + * Remove the given registered {@code Observer} from this {@code SourceStatistic}. + * + * @param derived statistic to be removed + */ + void removeDerivedStatistic(T derived); + + /** + * Retrieve all registered statistics. + * + * @return an unmodifiable collection of all derived statistics + */ + Collection getDerivedStatistics(); +} diff --git a/api/build.gradle b/ehcache-core/src/main/java/org/ehcache/core/statistics/StatisticType.java similarity index 86% rename from api/build.gradle rename to ehcache-core/src/main/java/org/ehcache/core/statistics/StatisticType.java index 540fd89a80..f24336185a 100644 --- a/api/build.gradle +++ b/ehcache-core/src/main/java/org/ehcache/core/statistics/StatisticType.java @@ -14,8 +14,9 @@ * limitations under the License. */ -apply plugin: EhDeploy +package org.ehcache.core.statistics; -checkstyle { - configFile = file("$projectDir/config/checkstyle.xml") +public enum StatisticType { + COUNTER, + GAUGE } diff --git a/core/src/main/java/org/ehcache/core/statistics/StoreOperationOutcomes.java b/ehcache-core/src/main/java/org/ehcache/core/statistics/StoreOperationOutcomes.java similarity index 100% rename from core/src/main/java/org/ehcache/core/statistics/StoreOperationOutcomes.java rename to ehcache-core/src/main/java/org/ehcache/core/statistics/StoreOperationOutcomes.java diff --git a/ehcache-core/src/main/java/org/ehcache/core/statistics/SuppliedValueStatistic.java b/ehcache-core/src/main/java/org/ehcache/core/statistics/SuppliedValueStatistic.java new file mode 100644 index 0000000000..2ac1974951 --- /dev/null +++ b/ehcache-core/src/main/java/org/ehcache/core/statistics/SuppliedValueStatistic.java @@ -0,0 +1,61 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.core.statistics; + +import java.io.Serializable; +import java.util.Objects; +import java.util.function.Supplier; + + +/** + * This class can be used to create a {@link ValueStatistic} with a specific {@link StatisticType} + * which value is given by a provided {@link Supplier} + * + * @author Mathieu Carbou + */ +public class SuppliedValueStatistic implements ValueStatistic { + + private final Supplier supplier; + private final StatisticType type; + + public SuppliedValueStatistic(StatisticType type, Supplier supplier) { + this.type = Objects.requireNonNull(type); + this.supplier = Objects.requireNonNull(supplier); + } + + @Override + public T value() { + return supplier.get(); + } + + @Override + public StatisticType type() { + return type; + } + + public static ValueStatistic counter(Supplier supplier) { + return supply(StatisticType.COUNTER, supplier); + } + + public static ValueStatistic gauge(Supplier supplier) { + return supply(StatisticType.GAUGE, supplier); + } + + public static ValueStatistic supply(StatisticType type, Supplier supplier) { + return new SuppliedValueStatistic<>(type, supplier); + } +} diff --git a/core/src/main/java/org/ehcache/core/statistics/TierOperationOutcomes.java b/ehcache-core/src/main/java/org/ehcache/core/statistics/TierOperationOutcomes.java similarity index 100% rename from core/src/main/java/org/ehcache/core/statistics/TierOperationOutcomes.java rename to ehcache-core/src/main/java/org/ehcache/core/statistics/TierOperationOutcomes.java diff --git a/core/src/main/java/org/ehcache/core/statistics/TierStatistics.java b/ehcache-core/src/main/java/org/ehcache/core/statistics/TierStatistics.java similarity index 92% rename from core/src/main/java/org/ehcache/core/statistics/TierStatistics.java rename to ehcache-core/src/main/java/org/ehcache/core/statistics/TierStatistics.java index a86946cb55..1335148bd2 100644 --- a/core/src/main/java/org/ehcache/core/statistics/TierStatistics.java +++ b/ehcache-core/src/main/java/org/ehcache/core/statistics/TierStatistics.java @@ -16,8 +16,6 @@ package org.ehcache.core.statistics; -import org.terracotta.statistics.ValueStatistic; - import java.util.Map; /** @@ -25,13 +23,6 @@ */ public interface TierStatistics { - /** - * List of statistics tracked on this cache - * - * @return a map of statistics per name - */ - Map> getKnownStatistics(); - /** * Reset the values for this tier. However, note that {@code mapping, allocatedMemory, occupiedMemory} * won't be reset since it doesn't make sense. diff --git a/ehcache-core/src/main/java/org/ehcache/core/statistics/ValueStatistic.java b/ehcache-core/src/main/java/org/ehcache/core/statistics/ValueStatistic.java new file mode 100644 index 0000000000..885e03de62 --- /dev/null +++ b/ehcache-core/src/main/java/org/ehcache/core/statistics/ValueStatistic.java @@ -0,0 +1,33 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.core.statistics; + +import java.io.Serializable; + +public interface ValueStatistic { + + /** + * @return The statistic type + */ + StatisticType type(); + + /** + * @return The current statistic value + */ + T value(); + +} diff --git a/ehcache-core/src/main/java/org/ehcache/core/statistics/ZeroOperationStatistic.java b/ehcache-core/src/main/java/org/ehcache/core/statistics/ZeroOperationStatistic.java new file mode 100644 index 0000000000..7a09e6cebc --- /dev/null +++ b/ehcache-core/src/main/java/org/ehcache/core/statistics/ZeroOperationStatistic.java @@ -0,0 +1,75 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.core.statistics; + +import java.util.Collection; +import java.util.Collections; +import java.util.Set; + +public class ZeroOperationStatistic> implements OperationStatistic { + + private static final OperationStatistic INSTANCE = new ZeroOperationStatistic<>(); + + @SuppressWarnings("unchecked") + public static > OperationStatistic get() { + return (OperationStatistic) INSTANCE; + } + + @Override + public Class type() { + return null; + } + + @Override + public long count(T type) { + return 0; + } + + @Override + public long sum(Set types) { + return 0; + } + + @Override + public long sum() { + return 0; + } + + @Override + public void addDerivedStatistic(ChainedOperationObserver derived) { + + } + + @Override + public void removeDerivedStatistic(ChainedOperationObserver derived) { + + } + + @Override + public Collection> getDerivedStatistics() { + return Collections.emptyList(); + } + + @Override + public void begin() { + + } + + @Override + public void end(T result) { + + } +} diff --git a/core/src/main/java/org/ehcache/core/statistics/package-info.java b/ehcache-core/src/main/java/org/ehcache/core/statistics/package-info.java similarity index 100% rename from core/src/main/java/org/ehcache/core/statistics/package-info.java rename to ehcache-core/src/main/java/org/ehcache/core/statistics/package-info.java diff --git a/core/src/main/java/org/ehcache/core/internal/store/StoreConfigurationImpl.java b/ehcache-core/src/main/java/org/ehcache/core/store/StoreConfigurationImpl.java similarity index 99% rename from core/src/main/java/org/ehcache/core/internal/store/StoreConfigurationImpl.java rename to ehcache-core/src/main/java/org/ehcache/core/store/StoreConfigurationImpl.java index 0aafe86d47..30fe9c2d49 100644 --- a/core/src/main/java/org/ehcache/core/internal/store/StoreConfigurationImpl.java +++ b/ehcache-core/src/main/java/org/ehcache/core/store/StoreConfigurationImpl.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.ehcache.core.internal.store; +package org.ehcache.core.store; import org.ehcache.config.CacheConfiguration; import org.ehcache.config.EvictionAdvisor; diff --git a/core/src/main/java/org/ehcache/core/internal/store/StoreSupport.java b/ehcache-core/src/main/java/org/ehcache/core/store/StoreSupport.java similarity index 96% rename from core/src/main/java/org/ehcache/core/internal/store/StoreSupport.java rename to ehcache-core/src/main/java/org/ehcache/core/store/StoreSupport.java index 8da0d2320d..5194f6741c 100644 --- a/core/src/main/java/org/ehcache/core/internal/store/StoreSupport.java +++ b/ehcache-core/src/main/java/org/ehcache/core/store/StoreSupport.java @@ -14,10 +14,10 @@ * limitations under the License. */ -package org.ehcache.core.internal.store; +package org.ehcache.core.store; import org.ehcache.config.ResourceType; -import org.ehcache.core.internal.service.ServiceLocator; +import org.ehcache.core.spi.ServiceLocator; import org.ehcache.core.spi.store.Store; import org.ehcache.core.spi.store.WrapperStore; import org.ehcache.spi.service.ServiceProvider; @@ -42,7 +42,7 @@ public final class StoreSupport { private StoreSupport() { } - public static Store.Provider selectWrapperStoreProvider(ServiceProvider serviceProvider, Collection> serviceConfigs) { + public static Store.Provider selectWrapperStoreProvider(ServiceProvider serviceProvider, Collection> serviceConfigs) { Collection storeProviders = serviceProvider.getServicesOfType(WrapperStore.Provider.class); Optional> wrapperProvider = storeProviders.stream() .map(provider -> new Tuple<>(provider.wrapperStoreRank(serviceConfigs), provider)) @@ -78,7 +78,7 @@ private static class Tuple { * multiple {@code Store.Provider} implementations return the same top ranking */ public static Store.Provider selectStoreProvider( - ServiceProvider serviceProvider, Set> resourceTypes, Collection> serviceConfigs) { + ServiceProvider serviceProvider, Set> resourceTypes, Collection> serviceConfigs) { Collection storeProviders = serviceProvider.getServicesOfType(Store.Provider.class); List filteredStoreProviders = storeProviders.stream().filter(provider -> !(provider instanceof WrapperStore.Provider)).collect(Collectors.toList()); diff --git a/impl/src/main/java/org/ehcache/impl/internal/util/ByteBufferInputStream.java b/ehcache-core/src/main/java/org/ehcache/core/util/ByteBufferInputStream.java similarity index 97% rename from impl/src/main/java/org/ehcache/impl/internal/util/ByteBufferInputStream.java rename to ehcache-core/src/main/java/org/ehcache/core/util/ByteBufferInputStream.java index d9af55b39b..e43ca5370f 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/util/ByteBufferInputStream.java +++ b/ehcache-core/src/main/java/org/ehcache/core/util/ByteBufferInputStream.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.ehcache.impl.internal.util; +package org.ehcache.core.util; import java.io.IOException; import java.io.InputStream; diff --git a/ehcache-core/src/main/java/org/ehcache/core/util/ClassLoading.java b/ehcache-core/src/main/java/org/ehcache/core/util/ClassLoading.java new file mode 100644 index 0000000000..a17a34d901 --- /dev/null +++ b/ehcache-core/src/main/java/org/ehcache/core/util/ClassLoading.java @@ -0,0 +1,119 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.core.util; + +import org.ehcache.core.osgi.SafeOsgi; +import org.ehcache.core.osgi.OsgiServiceLoader; + +import java.io.IOException; +import java.net.URL; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; +import java.util.List; +import java.util.ServiceLoader; +import java.util.function.Supplier; + +import static java.security.AccessController.doPrivileged; +import static java.util.Collections.enumeration; +import static java.util.Collections.list; +import static java.util.stream.Collectors.toList; +import static java.util.stream.Stream.concat; +import static java.util.stream.Stream.of; + +public class ClassLoading { + + private static final ClassLoader DEFAULT_CLASSLOADER; + + static { + DEFAULT_CLASSLOADER = delegationChain(() -> Thread.currentThread().getContextClassLoader(), ChainedClassLoader.class.getClassLoader()); + } + + public static ClassLoader getDefaultClassLoader() { + return DEFAULT_CLASSLOADER; + } + + public static Iterable servicesOfType(Class serviceType) { + if (SafeOsgi.useOSGiServiceLoading()) { + return OsgiServiceLoader.load(serviceType); + } else { + return ServiceLoader.load(serviceType, ClassLoading.class.getClassLoader()); + } + } + + @SuppressWarnings("unchecked") + public static ClassLoader delegationChain(Supplier loader, ClassLoader ... loaders) { + return doPrivileged((PrivilegedAction) () -> new ChainedClassLoader(concat(of(loader), of(loaders).map(l -> () -> l)).collect(toList()))); + } + + @SuppressWarnings("unchecked") + public static ClassLoader delegationChain(ClassLoader ... loaders) { + return doPrivileged((PrivilegedAction) () -> new ChainedClassLoader(of(loaders).>map(l -> () -> l).collect(toList()))); + } + + private static class ChainedClassLoader extends ClassLoader { + + private final List> loaders; + + public ChainedClassLoader(List> loaders) { + this.loaders = loaders; + } + + @Override + public Class loadClass(String name) throws ClassNotFoundException { + ClassNotFoundException lastFailure = new ClassNotFoundException(name); + for (Supplier loader : loaders) { + ClassLoader classLoader = loader.get(); + if (classLoader != null) { + try { + return classLoader.loadClass(name); + } catch (ClassNotFoundException cnfe) { + lastFailure = cnfe; + } + } + } + throw lastFailure; + } + + @Override + public URL getResource(String name) { + for (Supplier loader : loaders) { + ClassLoader classLoader = loader.get(); + if (classLoader != null) { + URL resource = classLoader.getResource(name); + if (resource != null) { + return resource; + } + } + } + return null; + } + + @Override + public Enumeration getResources(String name) throws IOException { + Collection aggregate = new ArrayList<>(); + for (Supplier loader : loaders) { + ClassLoader classLoader = loader.get(); + if (classLoader != null) { + aggregate.addAll(list(classLoader.getResources(name))); + } + } + return enumeration(aggregate); + } + } +} diff --git a/core/src/main/java/org/ehcache/core/internal/util/CollectionUtil.java b/ehcache-core/src/main/java/org/ehcache/core/util/CollectionUtil.java similarity index 54% rename from core/src/main/java/org/ehcache/core/internal/util/CollectionUtil.java rename to ehcache-core/src/main/java/org/ehcache/core/util/CollectionUtil.java index 85dfb0b3a3..9acc986ac5 100644 --- a/core/src/main/java/org/ehcache/core/internal/util/CollectionUtil.java +++ b/ehcache-core/src/main/java/org/ehcache/core/util/CollectionUtil.java @@ -13,11 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.ehcache.core.internal.util; +package org.ehcache.core.util; -import java.util.AbstractMap; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -40,19 +38,6 @@ public static int findBestCollectionSize(Iterable iterable, int bestBet) { return (iterable instanceof Collection ? ((Collection) iterable).size() : bestBet); } - /** - * Return a map entry for the key and value provided. - * - * @param key the key of the entry - * @param value the value of the entry - * @param type of the key - * @param type of the value - * @return the map entry for the key and value - */ - public static Map.Entry entry(K key, V value) { - return new AbstractMap.SimpleImmutableEntry<>(key, value); - } - /** * Copy each map entry to a new map but check that each key and value isn't null. Throw * a {@code NullPointerException} if it's the case. @@ -74,56 +59,4 @@ public static Map copyMapButFailOnNull(Map key type - * @param value type - * @return map with one entry - */ - public static Map map(K key0, V value0) { - return Collections.singletonMap(key0, value0); - } - - /** - * Create a map with two key/value pairs. - * - * @param key0 first key - * @param value0 first value - * @param key1 second key - * @param value1 second value - * @param key type - * @param value type - * @return map with two entries - */ - public static Map map(K key0, V value0, K key1, V value1) { - Map map = new HashMap<>(2); - map.put(key0, value0); - map.put(key1, value1); - return map; - } - - /** - * Create a map with three key/value pairs. - * - * @param key0 first key - * @param value0 first value - * @param key1 second key - * @param value1 second value - * @param key2 third key - * @param value2 third value - * @param key type - * @param value type - * @return map with three entries - */ - public static Map map(K key0, V value0, K key1, V value1, K key2, V value2) { - Map map = new HashMap<>(2); - map.put(key0, value0); - map.put(key1, value1); - map.put(key2, value2); - return map; - } } diff --git a/ehcache-core/src/main/resources/META-INF/services/org.ehcache.core.spi.service.ServiceFactory b/ehcache-core/src/main/resources/META-INF/services/org.ehcache.core.spi.service.ServiceFactory new file mode 100644 index 0000000000..7bcce30799 --- /dev/null +++ b/ehcache-core/src/main/resources/META-INF/services/org.ehcache.core.spi.service.ServiceFactory @@ -0,0 +1 @@ +org.ehcache.core.internal.statistics.DefaultStatisticsServiceFactory diff --git a/core/src/test/java/org/ehcache/core/CacheConfigurationChangeListenerTest.java b/ehcache-core/src/test/java/org/ehcache/core/CacheConfigurationChangeListenerTest.java similarity index 87% rename from core/src/test/java/org/ehcache/core/CacheConfigurationChangeListenerTest.java rename to ehcache-core/src/test/java/org/ehcache/core/CacheConfigurationChangeListenerTest.java index 933de8efa3..8b7272811d 100644 --- a/core/src/test/java/org/ehcache/core/CacheConfigurationChangeListenerTest.java +++ b/ehcache-core/src/test/java/org/ehcache/core/CacheConfigurationChangeListenerTest.java @@ -17,10 +17,9 @@ package org.ehcache.core; import org.ehcache.config.CacheConfiguration; -import org.ehcache.core.config.ResourcePoolsHelper; -import org.ehcache.core.config.BaseCacheConfiguration; import org.ehcache.core.events.CacheEventDispatcher; import org.ehcache.core.spi.store.Store; +import org.ehcache.core.util.TestCacheConfig; import org.ehcache.spi.loaderwriter.CacheLoaderWriter; import org.ehcache.spi.resilience.ResilienceStrategy; import org.junit.After; @@ -34,6 +33,7 @@ import java.util.List; import java.util.Set; +import static org.ehcache.core.config.ResourcePoolsHelper.createResourcePools; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.mock; @@ -55,7 +55,7 @@ public void setUp() throws Exception { this.eventNotifier = mock(CacheEventDispatcher.class); ResilienceStrategy resilienceStrategy = mock(ResilienceStrategy.class); CacheLoaderWriter loaderWriter = mock(CacheLoaderWriter.class); - this.config = new BaseCacheConfiguration<>(Object.class, Object.class, null, null, null, ResourcePoolsHelper.createHeapDiskPools(2, 10)); + this.config = new TestCacheConfig<>(Object.class, Object.class, createResourcePools(2L)); this.cache = new Ehcache<>(config, store, resilienceStrategy, eventNotifier, LoggerFactory.getLogger(Ehcache.class + "-" + "CacheConfigurationListenerTest"), loaderWriter); cache.init(); @@ -74,7 +74,7 @@ public void testCacheConfigurationChangeFiresEvent () { = new ArrayList<>(); cacheConfigurationChangeListeners.add(configurationListener); this.runtimeConfiguration.addCacheConfigurationListener(cacheConfigurationChangeListeners); - this.cache.getRuntimeConfiguration().updateResourcePools(ResourcePoolsHelper.createHeapOnlyPools(10)); + this.cache.getRuntimeConfiguration().updateResourcePools(createResourcePools(10L)); assertThat(configurationListener.eventSet.size(), is(1) ); } @@ -85,10 +85,10 @@ public void testRemovingCacheConfigurationListener() { = new ArrayList<>(); cacheConfigurationChangeListeners.add(configurationListener); this.runtimeConfiguration.addCacheConfigurationListener(cacheConfigurationChangeListeners); - this.cache.getRuntimeConfiguration().updateResourcePools(ResourcePoolsHelper.createHeapOnlyPools(20)); + this.cache.getRuntimeConfiguration().updateResourcePools(createResourcePools(20L)); assertThat(configurationListener.eventSet.size(), is(1)); this.runtimeConfiguration.removeCacheConfigurationListener(configurationListener); - this.cache.getRuntimeConfiguration().updateResourcePools(ResourcePoolsHelper.createHeapOnlyPools(5)); + this.cache.getRuntimeConfiguration().updateResourcePools(createResourcePools(5L)); assertThat(configurationListener.eventSet.size(), is(1) ); } diff --git a/core/src/test/java/org/ehcache/core/CacheTest.java b/ehcache-core/src/test/java/org/ehcache/core/CacheTest.java similarity index 94% rename from core/src/test/java/org/ehcache/core/CacheTest.java rename to ehcache-core/src/test/java/org/ehcache/core/CacheTest.java index 8317638e26..fdca889473 100644 --- a/core/src/test/java/org/ehcache/core/CacheTest.java +++ b/ehcache-core/src/test/java/org/ehcache/core/CacheTest.java @@ -33,7 +33,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -230,22 +230,22 @@ public Object get() { } @Override - public long creationTime(final TimeUnit unit) { + public long creationTime() { throw new UnsupportedOperationException("Implement me!"); } @Override - public long expirationTime(TimeUnit unit) { + public long expirationTime() { throw new UnsupportedOperationException("Implement me!"); } @Override - public boolean isExpired(long expirationTime, TimeUnit unit) { + public boolean isExpired(long expirationTime) { throw new UnsupportedOperationException("Implement me!"); } @Override - public long lastAccessTime(final TimeUnit unit) { + public long lastAccessTime() { throw new UnsupportedOperationException("Implement me!"); } @@ -267,22 +267,22 @@ public Object get() { } @Override - public long creationTime(final TimeUnit unit) { + public long creationTime() { throw new UnsupportedOperationException("Implement me!"); } @Override - public long expirationTime(TimeUnit unit) { + public long expirationTime() { throw new UnsupportedOperationException("Implement me!"); } @Override - public boolean isExpired(long expirationTime, TimeUnit unit) { + public boolean isExpired(long expirationTime) { throw new UnsupportedOperationException("Implement me!"); } @Override - public long lastAccessTime(final TimeUnit unit) { + public long lastAccessTime() { throw new UnsupportedOperationException("Implement me!"); } diff --git a/core/src/test/java/org/ehcache/core/EhcacheBasicBulkUtil.java b/ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicBulkUtil.java similarity index 100% rename from core/src/test/java/org/ehcache/core/EhcacheBasicBulkUtil.java rename to ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicBulkUtil.java diff --git a/core/src/test/java/org/ehcache/core/EhcacheBasicClearTest.java b/ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicClearTest.java similarity index 98% rename from core/src/test/java/org/ehcache/core/EhcacheBasicClearTest.java rename to ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicClearTest.java index d2febd7903..8b8e3579b6 100644 --- a/core/src/test/java/org/ehcache/core/EhcacheBasicClearTest.java +++ b/ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicClearTest.java @@ -26,8 +26,8 @@ import org.junit.Test; import org.slf4j.LoggerFactory; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.spy; diff --git a/core/src/test/java/org/ehcache/core/EhcacheBasicContainsKeyTest.java b/ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicContainsKeyTest.java similarity index 99% rename from core/src/test/java/org/ehcache/core/EhcacheBasicContainsKeyTest.java rename to ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicContainsKeyTest.java index 7cc107f1e7..75ec6225f4 100644 --- a/core/src/test/java/org/ehcache/core/EhcacheBasicContainsKeyTest.java +++ b/ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicContainsKeyTest.java @@ -26,8 +26,8 @@ import org.junit.Test; import org.slf4j.LoggerFactory; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; diff --git a/core/src/test/java/org/ehcache/core/EhcacheBasicCrudBase.java b/ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicCrudBase.java similarity index 97% rename from core/src/test/java/org/ehcache/core/EhcacheBasicCrudBase.java rename to ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicCrudBase.java index 400b4b6959..ab019d6d1b 100644 --- a/core/src/test/java/org/ehcache/core/EhcacheBasicCrudBase.java +++ b/ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicCrudBase.java @@ -18,12 +18,11 @@ import org.ehcache.Cache; import org.ehcache.config.CacheConfiguration; -import org.ehcache.core.config.BaseCacheConfiguration; -import org.ehcache.core.config.ResourcePoolsHelper; import org.ehcache.core.events.CacheEventDispatcher; import org.ehcache.core.exceptions.StorePassThroughException; import org.ehcache.core.spi.store.Store; import org.ehcache.core.spi.store.events.StoreEventSource; +import org.ehcache.core.util.TestCacheConfig; import org.ehcache.spi.loaderwriter.BulkCacheLoadingException; import org.ehcache.spi.loaderwriter.BulkCacheWritingException; import org.ehcache.spi.loaderwriter.CacheLoaderWriter; @@ -55,15 +54,13 @@ import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.spy; /** * Provides testing of basic CRUD operations on an {@code Ehcache}. @@ -72,9 +69,7 @@ */ public abstract class EhcacheBasicCrudBase { - protected static final CacheConfiguration CACHE_CONFIGURATION = - new BaseCacheConfiguration<>(String.class, String.class, null, - null, null, ResourcePoolsHelper.createHeapOnlyPools()); + protected static final CacheConfiguration CACHE_CONFIGURATION = new TestCacheConfig<>(String.class, String.class); @Mock protected Store store; @@ -600,23 +595,23 @@ public String get() { } @Override - public long creationTime(final TimeUnit unit) { - return unit.convert(this.creationTime, TimeUnit.MICROSECONDS); + public long creationTime() { + return creationTime; } @Override - public long expirationTime(TimeUnit unit) { + public long expirationTime() { return 0; } @Override - public boolean isExpired(long expirationTime, TimeUnit unit) { + public boolean isExpired(long expirationTime) { return false; } @Override - public long lastAccessTime(final TimeUnit unit) { - return unit.convert(this.lastAccessTime, TimeUnit.MICROSECONDS); + public long lastAccessTime() { + return lastAccessTime; } @Override diff --git a/core/src/test/java/org/ehcache/core/EhcacheBasicGetAllTest.java b/ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicGetAllTest.java similarity index 94% rename from core/src/test/java/org/ehcache/core/EhcacheBasicGetAllTest.java rename to ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicGetAllTest.java index 31e686a994..55df1a5c08 100644 --- a/core/src/test/java/org/ehcache/core/EhcacheBasicGetAllTest.java +++ b/ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicGetAllTest.java @@ -17,8 +17,6 @@ package org.ehcache.core; import org.ehcache.Status; -import org.ehcache.core.internal.resilience.RobustResilienceStrategy; -import org.ehcache.core.resilience.DefaultRecoveryStore; import org.ehcache.core.spi.store.Store; import org.ehcache.core.statistics.CacheOperationOutcomes; import org.ehcache.core.statistics.BulkOps; @@ -42,10 +40,10 @@ import static org.ehcache.core.EhcacheBasicBulkUtil.getEntryMap; import static org.ehcache.core.EhcacheBasicBulkUtil.getNullEntryMap; import static org.ehcache.core.EhcacheBasicBulkUtil.union; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -163,9 +161,7 @@ public void testGetAllStoreAllMatchStoreAccessExceptionBeforeNoLoader() throws E final Ehcache ehcache = this.getEhcache(); final Set fetchKeys = fanIn(KEY_SET_A, KEY_SET_B); - final Map actual = ehcache.getAll(fetchKeys); - - assertThat(actual, equalTo(getNullEntryMap(fetchKeys))); + ehcache.getAll(fetchKeys); verify(this.store).bulkComputeIfAbsent(eq(fetchKeys), getAnyIterableFunction()); // ResilienceStrategy invoked: no assertion for Store content @@ -223,13 +219,11 @@ public void testGetAllStoreNoMatchStoreAccessExceptionBeforeNoLoader() throws Ex final Ehcache ehcache = this.getEhcache(); - final Map actual = ehcache.getAll(KEY_SET_A); - assertThat(actual, equalTo(getNullEntryMap(KEY_SET_A))); + ehcache.getAll(KEY_SET_A); verify(this.store).bulkComputeIfAbsent(eq(KEY_SET_A), getAnyIterableFunction()); // ResilienceStrategy invoked: no assertion for Store content - verify(this.resilienceStrategy) - .getAllFailure(eq(KEY_SET_A), any(StoreAccessException.class)); + verify(this.resilienceStrategy).getAllFailure(eq(KEY_SET_A), any(StoreAccessException.class)); validateStatsNoneof(ehcache); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.GetAllOutcome.FAILURE)); @@ -285,9 +279,7 @@ public void testGetAllStoreSomeMatchStoreAccessExceptionBeforeNoLoader() throws final Ehcache ehcache = this.getEhcache(); final Set fetchKeys = fanIn(KEY_SET_A, KEY_SET_C); - final Map actual = ehcache.getAll(fetchKeys); - - assertThat(actual, equalTo(getNullEntryMap(fetchKeys))); + ehcache.getAll(fetchKeys); verify(this.store).bulkComputeIfAbsent(eq(fetchKeys), getAnyIterableFunction()); // ResilienceStrategy invoked: no assertion for Store content @@ -307,8 +299,8 @@ private void validateStatsNoneof(Ehcache cache) { * * @return a new {@code Ehcache} instance */ + @SuppressWarnings("unchecked") private Ehcache getEhcache() { - this.resilienceStrategy = spy(new RobustResilienceStrategy<>(new DefaultRecoveryStore<>(this.store))); final Ehcache ehcache = new Ehcache<>(CACHE_CONFIGURATION, this.store, resilienceStrategy, cacheEventDispatcher, LoggerFactory .getLogger(Ehcache.class + "-" + "EhcacheBasicGetAllTest")); ehcache.init(); @@ -344,5 +336,4 @@ static Set getAnyStringSet() { static Function, Iterable>> getAnyIterableFunction() { return any(Function.class); // unchecked } - } diff --git a/core/src/test/java/org/ehcache/core/EhcacheBasicGetTest.java b/ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicGetTest.java similarity index 98% rename from core/src/test/java/org/ehcache/core/EhcacheBasicGetTest.java rename to ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicGetTest.java index ba707db0bd..e7bbfde935 100644 --- a/core/src/test/java/org/ehcache/core/EhcacheBasicGetTest.java +++ b/ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicGetTest.java @@ -28,7 +28,7 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; diff --git a/core/src/test/java/org/ehcache/core/EhcacheBasicIteratorTest.java b/ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicIteratorTest.java similarity index 94% rename from core/src/test/java/org/ehcache/core/EhcacheBasicIteratorTest.java rename to ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicIteratorTest.java index 056df6e28d..097ae95c6e 100644 --- a/core/src/test/java/org/ehcache/core/EhcacheBasicIteratorTest.java +++ b/ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicIteratorTest.java @@ -18,9 +18,6 @@ import org.ehcache.Cache; import org.ehcache.Status; -import org.ehcache.CacheIterationException; -import org.ehcache.core.internal.resilience.RobustResilienceStrategy; -import org.ehcache.core.resilience.DefaultRecoveryStore; import org.ehcache.core.spi.store.Store; import org.ehcache.core.spi.store.Store.RemoveStatus; import org.ehcache.spi.loaderwriter.CacheLoaderWriter; @@ -35,16 +32,19 @@ import java.util.Map; import java.util.NoSuchElementException; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.hasEntry; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; /** * Provides testing of basic ITERATOR operations on an {@code Ehcache}. @@ -228,20 +228,16 @@ public void testIteratorStoreAccessException() throws Exception { final Iterator> iterator = ehcache.iterator(); assertThat(iterator, is(notNullValue())); assertThat(iterator.hasNext(), is(true)); - doThrow(new StoreAccessException("")).when(storeIterator).next(); + StoreAccessException exception = new StoreAccessException(""); + doThrow(exception).when(storeIterator).next(); Cache.Entry entry = iterator.next(); assertThat(entry.getKey(), is("foo")); assertThat(entry.getValue(), is("bar")); - doThrow(new StoreAccessException("")).when(storeIterator).next(); doReturn(RemoveStatus.REMOVED).when(this.store).remove(anyString(), anyString()); - try { - iterator.next(); - fail(); - } catch (CacheIterationException e) { - // Expected - } + iterator.next(); + verify(resilienceStrategy).iteratorFailure(exception); assertThat(iterator.hasNext(), is(false)); @@ -281,8 +277,8 @@ protected Map getTestStoreEntries() { * * @return a new {@code Ehcache} instance */ + @SuppressWarnings("unchecked") protected InternalCache getEhcache() throws Exception { - this.resilienceStrategy = spy(new RobustResilienceStrategy<>(new DefaultRecoveryStore<>(this.store))); final Ehcache ehcache = new Ehcache<>(CACHE_CONFIGURATION, this.store, resilienceStrategy, cacheEventDispatcher, LoggerFactory .getLogger(Ehcache.class + "-" + "EhcacheBasicIteratorTest")); ehcache.init(); diff --git a/core/src/test/java/org/ehcache/core/EhcacheBasicPutAllTest.java b/ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicPutAllTest.java similarity index 99% rename from core/src/test/java/org/ehcache/core/EhcacheBasicPutAllTest.java rename to ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicPutAllTest.java index e47b3daa14..c0f49692c3 100644 --- a/core/src/test/java/org/ehcache/core/EhcacheBasicPutAllTest.java +++ b/ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicPutAllTest.java @@ -58,11 +58,11 @@ import static org.ehcache.core.EhcacheBasicBulkUtil.getAltEntryMap; import static org.ehcache.core.EhcacheBasicBulkUtil.getEntryMap; import static org.ehcache.core.EhcacheBasicBulkUtil.union; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.everyItem; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.isIn; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; diff --git a/core/src/test/java/org/ehcache/core/EhcacheBasicPutIfAbsentTest.java b/ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicPutIfAbsentTest.java similarity index 85% rename from core/src/test/java/org/ehcache/core/EhcacheBasicPutIfAbsentTest.java rename to ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicPutIfAbsentTest.java index 338ec5d166..a22f3df49d 100644 --- a/core/src/test/java/org/ehcache/core/EhcacheBasicPutIfAbsentTest.java +++ b/ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicPutIfAbsentTest.java @@ -19,24 +19,19 @@ import java.util.EnumSet; import org.ehcache.Status; -import org.ehcache.config.CacheConfiguration; -import org.ehcache.core.config.BaseCacheConfiguration; -import org.ehcache.core.config.ResourcePoolsHelper; import org.ehcache.core.statistics.CacheOperationOutcomes; -import org.ehcache.expiry.ExpiryPolicy; import org.ehcache.spi.resilience.StoreAccessException; import org.hamcrest.Matchers; import org.junit.Test; import org.slf4j.LoggerFactory; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -141,8 +136,7 @@ public void testPutIfAbsentNoStoreEntryStoreAccessException() throws Exception { ehcache.putIfAbsent("key", "value"); verify(this.store).putIfAbsent(eq("key"), eq("value"), any()); - verify(this.resilienceStrategy) - .putIfAbsentFailure(eq("key"), eq("value"), any(StoreAccessException.class)); + verify(this.resilienceStrategy).putIfAbsentFailure(eq("key"), eq("value"), any(StoreAccessException.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutIfAbsentOutcome.FAILURE)); } @@ -163,8 +157,7 @@ public void testPutIfAbsentHasStoreEntryStoreAccessException() throws Exception ehcache.putIfAbsent("key", "value"); verify(this.store).putIfAbsent(eq("key"), eq("value"), any()); - verify(this.resilienceStrategy) - .putIfAbsentFailure(eq("key"), eq("value"), any(StoreAccessException.class)); + verify(this.resilienceStrategy).putIfAbsentFailure(eq("key"), eq("value"), any(StoreAccessException.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutIfAbsentOutcome.FAILURE)); } @@ -174,9 +167,7 @@ public void testPutIfAbsentHasStoreEntryStoreAccessException() throws Exception * @return a new {@code Ehcache} instance */ private Ehcache getEhcache() { - CacheConfiguration config = new BaseCacheConfiguration<>(String.class, String.class, null, null, - ExpiryPolicy.NO_EXPIRY, ResourcePoolsHelper.createHeapOnlyPools()); - final Ehcache ehcache = new Ehcache<>(config, this.store, resilienceStrategy, cacheEventDispatcher, LoggerFactory.getLogger(Ehcache.class + "-" + "EhcacheBasicPutIfAbsentTest")); + final Ehcache ehcache = new Ehcache<>(CACHE_CONFIGURATION, this.store, resilienceStrategy, cacheEventDispatcher, LoggerFactory.getLogger(Ehcache.class + "-" + "EhcacheBasicPutIfAbsentTest")); ehcache.init(); assertThat("cache not initialized", ehcache.getStatus(), Matchers.is(Status.AVAILABLE)); return ehcache; diff --git a/core/src/test/java/org/ehcache/core/EhcacheBasicPutTest.java b/ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicPutTest.java similarity index 94% rename from core/src/test/java/org/ehcache/core/EhcacheBasicPutTest.java rename to ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicPutTest.java index bdbb49a4fc..308f1f04e9 100644 --- a/core/src/test/java/org/ehcache/core/EhcacheBasicPutTest.java +++ b/ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicPutTest.java @@ -20,8 +20,6 @@ import org.ehcache.Status; import org.ehcache.config.CacheConfiguration; -import org.ehcache.core.internal.resilience.RobustResilienceStrategy; -import org.ehcache.core.resilience.DefaultRecoveryStore; import org.ehcache.core.statistics.CacheOperationOutcomes; import org.ehcache.spi.resilience.StoreAccessException; import org.hamcrest.CoreMatchers; @@ -29,8 +27,7 @@ import org.slf4j.LoggerFactory; import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -161,7 +158,6 @@ public void testPutHasStoreEntryStoreAccessException() throws Exception { ehcache.put("key", "value"); verify(this.store).put(eq("key"), eq("value")); verify(this.resilienceStrategy).putFailure(eq("key"), eq("value"), any(StoreAccessException.class)); - assertThat(fakeStore.getEntryMap().containsKey("key"), is(false)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutOutcome.FAILURE)); } @@ -205,8 +201,8 @@ private Ehcache getEhcache() { return getEhcache(CACHE_CONFIGURATION); } + @SuppressWarnings("unchecked") private Ehcache getEhcache(CacheConfiguration config) { - this.resilienceStrategy = spy(new RobustResilienceStrategy<>(new DefaultRecoveryStore<>(this.store))); final Ehcache ehcache = new Ehcache<>(config, this.store, resilienceStrategy, cacheEventDispatcher, LoggerFactory.getLogger(Ehcache.class + "-" + "EhcacheBasicPutTest")); ehcache.init(); assertThat("cache not initialized", ehcache.getStatus(), CoreMatchers.is(Status.AVAILABLE)); diff --git a/core/src/test/java/org/ehcache/core/EhcacheBasicRemoveAllTest.java b/ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicRemoveAllTest.java similarity index 99% rename from core/src/test/java/org/ehcache/core/EhcacheBasicRemoveAllTest.java rename to ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicRemoveAllTest.java index e2d2b0fb35..687d6862d3 100644 --- a/core/src/test/java/org/ehcache/core/EhcacheBasicRemoveAllTest.java +++ b/ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicRemoveAllTest.java @@ -53,11 +53,11 @@ import static org.ehcache.core.EhcacheBasicBulkUtil.copyWithout; import static org.ehcache.core.EhcacheBasicBulkUtil.fanIn; import static org.ehcache.core.EhcacheBasicBulkUtil.getEntryMap; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.everyItem; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.isIn; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; diff --git a/core/src/test/java/org/ehcache/core/EhcacheBasicRemoveTest.java b/ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicRemoveTest.java similarity index 92% rename from core/src/test/java/org/ehcache/core/EhcacheBasicRemoveTest.java rename to ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicRemoveTest.java index 5f37f8b950..00fa591ada 100644 --- a/core/src/test/java/org/ehcache/core/EhcacheBasicRemoveTest.java +++ b/ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicRemoveTest.java @@ -19,8 +19,6 @@ import java.util.EnumSet; import org.ehcache.Status; -import org.ehcache.core.internal.resilience.RobustResilienceStrategy; -import org.ehcache.core.resilience.DefaultRecoveryStore; import org.ehcache.core.statistics.CacheOperationOutcomes; import org.ehcache.spi.resilience.StoreAccessException; import org.hamcrest.CoreMatchers; @@ -28,14 +26,13 @@ import org.slf4j.LoggerFactory; import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verifyZeroInteractions; /** @@ -92,7 +89,6 @@ public void testRemoveNoStoreEntryStoreAccessException() throws Exception { final Ehcache ehcache = this.getEhcache(); ehcache.remove("key"); - verify(this.store, times(2)).remove(eq("key")); verify(this.resilienceStrategy).removeFailure(eq("key"), any(StoreAccessException.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveOutcome.FAILURE)); } @@ -134,7 +130,6 @@ public void testRemoveHasStoreEntryStoreAccessException() throws Exception { final Ehcache ehcache = this.getEhcache(); ehcache.remove("key"); - verify(this.store, times(2)).remove(eq("key")); verify(this.resilienceStrategy).removeFailure(eq("key"), any(StoreAccessException.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveOutcome.FAILURE)); } @@ -144,8 +139,8 @@ public void testRemoveHasStoreEntryStoreAccessException() throws Exception { * * @return a new {@code Ehcache} instance */ + @SuppressWarnings("unchecked") private Ehcache getEhcache() { - this.resilienceStrategy = spy(new RobustResilienceStrategy<>(new DefaultRecoveryStore<>(this.store))); final Ehcache ehcache = new Ehcache<>(CACHE_CONFIGURATION, this.store, resilienceStrategy, cacheEventDispatcher, LoggerFactory .getLogger(Ehcache.class + "-" + "EhcacheBasicRemoveTest")); ehcache.init(); diff --git a/core/src/test/java/org/ehcache/core/EhcacheBasicRemoveValueTest.java b/ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicRemoveValueTest.java similarity index 99% rename from core/src/test/java/org/ehcache/core/EhcacheBasicRemoveValueTest.java rename to ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicRemoveValueTest.java index 6bb50c2c30..ca5d172e8f 100644 --- a/core/src/test/java/org/ehcache/core/EhcacheBasicRemoveValueTest.java +++ b/ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicRemoveValueTest.java @@ -27,8 +27,8 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; diff --git a/core/src/test/java/org/ehcache/core/EhcacheBasicReplaceTest.java b/ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicReplaceTest.java similarity index 99% rename from core/src/test/java/org/ehcache/core/EhcacheBasicReplaceTest.java rename to ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicReplaceTest.java index 1b8a61603e..f55a9482b7 100644 --- a/core/src/test/java/org/ehcache/core/EhcacheBasicReplaceTest.java +++ b/ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicReplaceTest.java @@ -27,8 +27,8 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; diff --git a/core/src/test/java/org/ehcache/core/EhcacheBasicReplaceValueTest.java b/ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicReplaceValueTest.java similarity index 99% rename from core/src/test/java/org/ehcache/core/EhcacheBasicReplaceValueTest.java rename to ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicReplaceValueTest.java index 5fbc355b05..520d877310 100644 --- a/core/src/test/java/org/ehcache/core/EhcacheBasicReplaceValueTest.java +++ b/ehcache-core/src/test/java/org/ehcache/core/EhcacheBasicReplaceValueTest.java @@ -27,8 +27,8 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; diff --git a/core/src/test/java/org/ehcache/core/EhcacheBulkMethodsTest.java b/ehcache-core/src/test/java/org/ehcache/core/EhcacheBulkMethodsTest.java similarity index 95% rename from core/src/test/java/org/ehcache/core/EhcacheBulkMethodsTest.java rename to ehcache-core/src/test/java/org/ehcache/core/EhcacheBulkMethodsTest.java index 1ad07817b5..2e55654ab4 100644 --- a/core/src/test/java/org/ehcache/core/EhcacheBulkMethodsTest.java +++ b/ehcache-core/src/test/java/org/ehcache/core/EhcacheBulkMethodsTest.java @@ -33,9 +33,9 @@ import java.util.concurrent.TimeUnit; import java.util.function.Function; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.core.IsCollectionContaining.hasItems; -import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -159,22 +159,22 @@ public V get() { } @Override - public long creationTime(TimeUnit unit) { + public long creationTime() { throw new AssertionError(); } @Override - public long expirationTime(TimeUnit unit) { + public long expirationTime() { return 0; } @Override - public boolean isExpired(long expirationTime, TimeUnit unit) { + public boolean isExpired(long expirationTime) { return false; } @Override - public long lastAccessTime(TimeUnit unit) { + public long lastAccessTime() { throw new AssertionError(); } diff --git a/core/src/test/java/org/ehcache/core/EhcacheManagerTest.java b/ehcache-core/src/test/java/org/ehcache/core/EhcacheManagerTest.java similarity index 90% rename from core/src/test/java/org/ehcache/core/EhcacheManagerTest.java rename to ehcache-core/src/test/java/org/ehcache/core/EhcacheManagerTest.java index 1ab41843ac..56c239ae83 100644 --- a/core/src/test/java/org/ehcache/core/EhcacheManagerTest.java +++ b/ehcache-core/src/test/java/org/ehcache/core/EhcacheManagerTest.java @@ -27,16 +27,16 @@ import org.ehcache.config.Configuration; import org.ehcache.config.ResourcePools; import org.ehcache.config.ResourceType; -import org.ehcache.core.config.BaseCacheConfiguration; import org.ehcache.core.config.DefaultConfiguration; import org.ehcache.core.config.ResourcePoolsHelper; import org.ehcache.core.events.CacheEventDispatcher; import org.ehcache.core.events.CacheEventDispatcherFactory; import org.ehcache.core.events.CacheEventListenerProvider; import org.ehcache.core.events.CacheManagerListener; -import org.ehcache.core.internal.util.ClassLoading; import org.ehcache.core.spi.service.LocalPersistenceService; import org.ehcache.core.spi.store.Store; +import org.ehcache.core.util.ClassLoading; +import org.ehcache.core.util.TestCacheConfig; import org.ehcache.spi.loaderwriter.CacheLoaderWriter; import org.ehcache.spi.loaderwriter.CacheLoaderWriterProvider; import org.ehcache.spi.loaderwriter.WriteBehindProvider; @@ -48,9 +48,7 @@ import org.ehcache.spi.service.ServiceProvider; import org.hamcrest.CoreMatchers; import org.junit.Ignore; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; @@ -68,12 +66,14 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasProperty; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; @@ -90,9 +90,6 @@ @SuppressWarnings({ "unchecked", "rawtypes" }) public class EhcacheManagerTest { - @Rule - public ExpectedException expectedException = ExpectedException.none(); - private static Map> newCacheMap() { return new HashMap<>(); } @@ -110,8 +107,7 @@ private List minimumCacheManagerServices() { @Test public void testCanDestroyAndClose() throws Exception { - CacheConfiguration cacheConfiguration = new BaseCacheConfiguration<>(Long.class, String.class, null, - null, null, ResourcePoolsHelper.createHeapOnlyPools(10)); + CacheConfiguration cacheConfiguration = new TestCacheConfig<>(Long.class, String.class); Store.Provider storeProvider = mock(Store.Provider.class); when(storeProvider.rank(any(Set.class), any(Collection.class))).thenReturn(1); @@ -146,7 +142,7 @@ public void testCanDestroyAndClose() throws Exception { @Test public void testConstructionThrowsWhenNotBeingToResolveService() { Map> caches = newCacheMap(); - final DefaultConfiguration config = new DefaultConfiguration(caches, null, (ServiceCreationConfiguration) () -> NoSuchService.class); + final DefaultConfiguration config = new DefaultConfiguration(caches, null, (ServiceCreationConfiguration) () -> NoSuchService.class); try { new EhcacheManager(config); fail("Should have thrown..."); @@ -158,7 +154,7 @@ public void testConstructionThrowsWhenNotBeingToResolveService() { @Test public void testCreationFailsOnDuplicateServiceCreationConfiguration() { Map> caches = newCacheMap(); - DefaultConfiguration config = new DefaultConfiguration(caches, null, (ServiceCreationConfiguration) () -> NoSuchService.class, (ServiceCreationConfiguration) () -> NoSuchService.class); + DefaultConfiguration config = new DefaultConfiguration(caches, null, (ServiceCreationConfiguration) () -> NoSuchService.class, (ServiceCreationConfiguration) () -> NoSuchService.class); try { new EhcacheManager(config); fail("Should have thrown ..."); @@ -188,7 +184,7 @@ public void testStopAllServicesWhenCacheInitializationFails() { @Test public void testNoClassLoaderSpecified() { Map> caches = newCacheMap(); - caches.put("foo", new BaseCacheConfiguration<>(Object.class, Object.class, null, null, null, ResourcePoolsHelper.createHeapOnlyPools())); + caches.put("foo", new TestCacheConfig<>(Object.class, Object.class)); DefaultConfiguration config = new DefaultConfiguration(caches, null); final Store.Provider storeProvider = mock(Store.Provider.class); @@ -222,9 +218,14 @@ public void testClassLoaderSpecified() { assertNotSame(cl1.getClass(), cl2.getClass()); Map> caches = newCacheMap(); - caches.put("foo1", new BaseCacheConfiguration<>(Object.class, Object.class, null, null, null, ResourcePoolsHelper.createHeapOnlyPools())); - caches.put("foo2", new BaseCacheConfiguration<>(Object.class, Object.class, null, null, null, ResourcePoolsHelper.createHeapOnlyPools())); - caches.put("foo3", new BaseCacheConfiguration<>(Object.class, Object.class, null, cl2, null, ResourcePoolsHelper.createHeapOnlyPools())); + caches.put("foo1", new TestCacheConfig<>(Object.class, Object.class)); + caches.put("foo2", new TestCacheConfig<>(Object.class, Object.class)); + caches.put("foo3", new TestCacheConfig(Object.class, Object.class) { + @Override + public ClassLoader getClassLoader() { + return cl2; + } + }); DefaultConfiguration config = new DefaultConfiguration(caches, cl1); final Store.Provider storeProvider = mock(Store.Provider.class); @@ -259,8 +260,7 @@ public void testReturnsNullForNonExistCache() { @Test public void testThrowsWhenAddingExistingCache() { - CacheConfiguration cacheConfiguration = new BaseCacheConfiguration<>(Object.class, Object.class, null, null, null, ResourcePoolsHelper - .createHeapOnlyPools()); + CacheConfiguration cacheConfiguration = new TestCacheConfig<>(Object.class, Object.class); final Store.Provider storeProvider = mock(Store.Provider.class); when(storeProvider.rank(any(Set.class), any(Collection.class))).thenReturn(1); final Store mock = mock(Store.class); @@ -302,8 +302,7 @@ public void testThrowsWhenNotInitialized() { when(storeProvider .createStore(ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(mock); - final CacheConfiguration cacheConfiguration = new BaseCacheConfiguration<>(Integer.class, String.class, null, null, null, ResourcePoolsHelper - .createHeapOnlyPools()); + final CacheConfiguration cacheConfiguration = new TestCacheConfig<>(Integer.class, String.class); Map> caches = newCacheMap(); caches.put("bar", cacheConfiguration); DefaultConfiguration config = new DefaultConfiguration(caches, null); @@ -341,8 +340,7 @@ public void testThrowsWhenRetrievingCacheWithWrongTypes() { when(storeProvider .createStore(ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(mock); - final CacheConfiguration cacheConfiguration = new BaseCacheConfiguration<>(Integer.class, String.class, null, null, null, ResourcePoolsHelper - .createHeapOnlyPools()); + final CacheConfiguration cacheConfiguration = new TestCacheConfig<>(Integer.class, String.class); Map> caches = newCacheMap(); caches.put("bar", cacheConfiguration); DefaultConfiguration config = new DefaultConfiguration(caches, null); @@ -371,7 +369,7 @@ public void testThrowsWhenRetrievingCacheWithWrongTypes() { @Test public void testLifeCyclesCacheLoaders() throws Exception { - ResourcePools resourcePools = ResourcePoolsHelper.createHeapOnlyPools(10); + ResourcePools resourcePools = ResourcePoolsHelper.createResourcePools(100L); final CacheLoaderWriterProvider cacheLoaderWriterProvider = mock(CacheLoaderWriterProvider.class); @@ -421,8 +419,7 @@ public void testLifeCyclesCacheLoaders() throws Exception { @Test public void testDoesNotifyAboutCache() { - final CacheConfiguration cacheConfiguration = new BaseCacheConfiguration<>(Object.class, Object.class, null, null, null, ResourcePoolsHelper - .createHeapOnlyPools()); + final CacheConfiguration cacheConfiguration = new TestCacheConfig<>(Object.class, Object.class); final Store.Provider mock = mock(Store.Provider.class); when(mock.rank(any(Set.class), any(Collection.class))).thenReturn(1); @@ -448,8 +445,7 @@ public void testDoesNotifyAboutCache() { @Test public void testDoesNotNotifyAboutCacheOnInitOrClose() { - final CacheConfiguration cacheConfiguration = new BaseCacheConfiguration<>(Object.class, Object.class, null, null, null, ResourcePoolsHelper - .createHeapOnlyPools()); + final CacheConfiguration cacheConfiguration = new TestCacheConfig<>(Object.class, Object.class); final Store.Provider mock = mock(Store.Provider.class); when(mock.rank(any(Set.class), any(Collection.class))).thenReturn(1); @@ -476,8 +472,7 @@ public void testDoesNotNotifyAboutCacheOnInitOrClose() { @Test public void testClosesStartedCachesDownWhenInitThrows() { final Set> caches = new HashSet<>(); - final CacheConfiguration cacheConfiguration = new BaseCacheConfiguration<>(Object.class, Object.class, null, null, null, ResourcePoolsHelper - .createHeapOnlyPools()); + final CacheConfiguration cacheConfiguration = new TestCacheConfig<>(Object.class, Object.class); final Store.Provider storeProvider = mock(Store.Provider.class); when(storeProvider.rank(any(Set.class), any(Collection.class))).thenReturn(1); final Collection services = getServices(storeProvider, null); @@ -497,7 +492,7 @@ InternalCache createNewEhcache(final String alias, final CacheConfi caches.add(ehcache); if(caches.size() == 1) { when(storeProvider.createStore( - ArgumentMatchers.>any(), ArgumentMatchers.>any())) + ArgumentMatchers.>any(), ArgumentMatchers.>any())) .thenThrow(thrown); } return ehcache; @@ -526,8 +521,7 @@ protected void closeEhcache(final String alias, final InternalCache ehcach @Test public void testClosesAllCachesDownWhenCloseThrows() { final Set caches = new HashSet<>(); - final CacheConfiguration cacheConfiguration = new BaseCacheConfiguration<>(Object.class, Object.class, null, null, null, ResourcePoolsHelper - .createHeapOnlyPools()); + final CacheConfiguration cacheConfiguration = new TestCacheConfig<>(Object.class, Object.class); final Store.Provider storeProvider = mock(Store.Provider.class); when(storeProvider.rank(any(Set.class), any(Collection.class))).thenReturn(1); @@ -589,11 +583,10 @@ public void testDoesNotifyAboutLifecycle() { @Test public void testCloseNoLoaderWriterAndCacheEventListener() throws Exception { - final CacheConfiguration cacheConfiguration = new BaseCacheConfiguration<>(Object.class, Object.class, null, null, null, ResourcePoolsHelper - .createHeapOnlyPools()); + final CacheConfiguration cacheConfiguration = new TestCacheConfig<>(Object.class, Object.class); final Store.Provider storeProvider = spy(new Store.Provider() { @Override - public int rank(final Set> resourceTypes, final Collection> serviceConfigs) { + public int rank(final Set> resourceTypes, final Collection> serviceConfigs) { return 1; } @@ -616,7 +609,7 @@ public void initStore(Store resource) { } @Override - public Store createStore(Store.Configuration storeConfig, ServiceConfiguration... serviceConfigs) { + public Store createStore(Store.Configuration storeConfig, ServiceConfiguration... serviceConfigs) { return null; } }); @@ -631,7 +624,7 @@ public void stop() { } @Override - public CacheEventDispatcher createCacheEventDispatcher(Store store, ServiceConfiguration... serviceConfigs) { + public CacheEventDispatcher createCacheEventDispatcher(Store store, ServiceConfiguration... serviceConfigs) { return null; } @@ -677,8 +670,7 @@ public void testChangesToManagerAreReflectedInConfig() { when(store.getConfigurationChangeListeners()).thenReturn(new ArrayList<>()); when(cacheEventNotificationListenerServiceProvider.createCacheEventDispatcher(store)).thenReturn(mock(CacheEventDispatcher.class)); - CacheConfiguration cache1Configuration = new BaseCacheConfiguration<>(Long.class, String.class, null, null, null, ResourcePoolsHelper - .createHeapOnlyPools()); + CacheConfiguration cache1Configuration = new TestCacheConfig<>(Long.class, String.class); Map> caches = newCacheMap(); caches.put("cache1", cache1Configuration); DefaultConfiguration config = new DefaultConfiguration(caches, null); @@ -694,8 +686,8 @@ public void testChangesToManagerAreReflectedInConfig() { cacheManager.init(); try { - final CacheConfiguration cache2Configuration = new BaseCacheConfiguration<>(Long.class, String.class, null, null, null, ResourcePoolsHelper - .createHeapOnlyPools()); + final CacheConfiguration cache2Configuration = new TestCacheConfig<>(Long.class, String.class, ResourcePoolsHelper + .createResourcePools(100L)); final Cache cache = cacheManager.createCache("cache2", cache2Configuration); final CacheConfiguration cacheConfiguration = cacheManager.getRuntimeConfiguration() .getCacheConfigurations() @@ -725,8 +717,7 @@ public void testCachesAddedAtRuntimeGetReInited() { when(store.getConfigurationChangeListeners()).thenReturn(new ArrayList<>()); when(cacheEventNotificationListenerServiceProvider.createCacheEventDispatcher(store)).thenReturn(mock(CacheEventDispatcher.class)); - CacheConfiguration cache1Configuration = new BaseCacheConfiguration<>(Long.class, String.class, null, null, null, ResourcePoolsHelper - .createHeapOnlyPools()); + CacheConfiguration cache1Configuration = new TestCacheConfig<>(Long.class, String.class); Map> caches = newCacheMap(); caches.put("cache1", cache1Configuration); DefaultConfiguration config = new DefaultConfiguration(caches, null); @@ -742,8 +733,7 @@ public void testCachesAddedAtRuntimeGetReInited() { cacheManager.init(); - CacheConfiguration cache2Configuration = new BaseCacheConfiguration<>(Long.class, String.class, null, null, null, ResourcePoolsHelper - .createHeapOnlyPools()); + CacheConfiguration cache2Configuration = new TestCacheConfig<>(Long.class, String.class, ResourcePoolsHelper.createResourcePools(100L)); cacheManager.createCache("cache2", cache2Configuration); cacheManager.removeCache("cache1"); @@ -777,8 +767,7 @@ public void testCloseWhenRuntimeCacheCreationFails() throws Exception { cacheManager.init(); - CacheConfiguration cacheConfiguration = new BaseCacheConfiguration<>(Long.class, String.class, null, null, null, ResourcePoolsHelper - .createHeapOnlyPools()); + CacheConfiguration cacheConfiguration = new TestCacheConfig<>(Long.class, String.class); try { cacheManager.createCache("cache", cacheConfiguration); @@ -798,8 +787,7 @@ public void testCloseWhenCacheCreationFailsDuringInitialization() throws Excepti when(storeProvider.rank(any(Set.class), any(Collection.class))).thenReturn(1); doThrow(new Error("Test EhcacheManager close.")).when(storeProvider).createStore(any(Store.Configuration.class), ArgumentMatchers.any()); - CacheConfiguration cacheConfiguration = new BaseCacheConfiguration<>(Long.class, String.class, null, null, null, ResourcePoolsHelper - .createHeapOnlyPools()); + CacheConfiguration cacheConfiguration = new TestCacheConfig<>(Long.class, String.class); Map> caches = newCacheMap(); caches.put("cache1", cacheConfiguration); DefaultConfiguration config = new DefaultConfiguration(caches, null); @@ -843,10 +831,8 @@ public void testDestroyCacheFailsIfAlreadyInMaintenanceMode() throws CachePersis thread.start(); thread.join(1000); - expectedException.expect(IllegalStateException.class); - expectedException.expectMessage("State is MAINTENANCE, yet you don't own it!"); - - manager.destroyCache("test"); + IllegalStateException thrown = assertThrows(IllegalStateException.class, () -> manager.destroyCache("test")); + assertThat(thrown, hasProperty("message", is("State is MAINTENANCE, yet you don't own it!"))); } @Test @@ -861,10 +847,8 @@ public void testDestroyCacheFailsAndStopIfStartingServicesFails() throws CachePe EhcacheManager manager = new EhcacheManager(config, services); - expectedException.expect(StateTransitionException.class); - expectedException.expectMessage("failed"); - - manager.destroyCache("test"); + StateTransitionException thrown = assertThrows(StateTransitionException.class, () -> manager.destroyCache("test")); + assertThat(thrown, hasProperty("message", is("failed"))); assertThat(manager.getStatus(), equalTo(Status.UNINITIALIZED)); } diff --git a/core/src/test/java/org/ehcache/core/EhcacheTest.java b/ehcache-core/src/test/java/org/ehcache/core/EhcacheTest.java similarity index 81% rename from core/src/test/java/org/ehcache/core/EhcacheTest.java rename to ehcache-core/src/test/java/org/ehcache/core/EhcacheTest.java index 4f127ddfcc..91d2afda22 100644 --- a/core/src/test/java/org/ehcache/core/EhcacheTest.java +++ b/ehcache-core/src/test/java/org/ehcache/core/EhcacheTest.java @@ -18,11 +18,9 @@ import static org.mockito.Mockito.mock; import org.ehcache.config.CacheConfiguration; -import org.ehcache.core.config.BaseCacheConfiguration; -import org.ehcache.core.config.ResourcePoolsHelper; import org.ehcache.core.events.CacheEventDispatcher; -import org.ehcache.core.resilience.DefaultRecoveryStore; import org.ehcache.core.spi.store.Store; +import org.ehcache.core.util.TestCacheConfig; import org.ehcache.spi.resilience.ResilienceStrategy; import org.slf4j.LoggerFactory; @@ -34,8 +32,7 @@ public class EhcacheTest extends CacheTest { @Override protected InternalCache getCache(Store store) { - final CacheConfiguration config = new BaseCacheConfiguration<>(Object.class, Object.class, null, - null, null, ResourcePoolsHelper.createHeapOnlyPools()); + final CacheConfiguration config = new TestCacheConfig<>(Object.class, Object.class); @SuppressWarnings("unchecked") CacheEventDispatcher cacheEventDispatcher = mock(CacheEventDispatcher.class); @SuppressWarnings("unchecked") diff --git a/core/src/test/java/org/ehcache/core/StatusTransitionerTest.java b/ehcache-core/src/test/java/org/ehcache/core/StatusTransitionerTest.java similarity index 99% rename from core/src/test/java/org/ehcache/core/StatusTransitionerTest.java rename to ehcache-core/src/test/java/org/ehcache/core/StatusTransitionerTest.java index 415c8c2d43..a58e6e9e71 100644 --- a/core/src/test/java/org/ehcache/core/StatusTransitionerTest.java +++ b/ehcache-core/src/test/java/org/ehcache/core/StatusTransitionerTest.java @@ -31,8 +31,8 @@ import static java.util.Arrays.asList; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; diff --git a/core/src/test/java/org/ehcache/core/UserManagedCacheTest.java b/ehcache-core/src/test/java/org/ehcache/core/UserManagedCacheTest.java similarity index 88% rename from core/src/test/java/org/ehcache/core/UserManagedCacheTest.java rename to ehcache-core/src/test/java/org/ehcache/core/UserManagedCacheTest.java index 20e2617c07..8bc98b97da 100644 --- a/core/src/test/java/org/ehcache/core/UserManagedCacheTest.java +++ b/ehcache-core/src/test/java/org/ehcache/core/UserManagedCacheTest.java @@ -18,8 +18,6 @@ import org.ehcache.Status; import org.ehcache.config.CacheConfiguration; -import org.ehcache.core.config.BaseCacheConfiguration; -import org.ehcache.core.config.ResourcePoolsHelper; import org.ehcache.core.events.CacheEventDispatcher; import org.ehcache.core.spi.store.Store; import org.ehcache.StateTransitionException; @@ -31,7 +29,7 @@ import org.slf4j.LoggerFactory; import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.fail; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; @@ -44,8 +42,7 @@ public class UserManagedCacheTest { @Test public void testUserManagedCacheDelegatesLifecycleCallsToStore() throws Exception { final Store store = mock(Store.class); - CacheConfiguration config = new BaseCacheConfiguration<>(Object.class, Object.class, null, null, - null, ResourcePoolsHelper.createHeapOnlyPools()); + CacheConfiguration config = mock(CacheConfiguration.class); Ehcache ehcache = new Ehcache(config, store, mock(ResilienceStrategy.class), mock(CacheEventDispatcher.class), LoggerFactory.getLogger(Ehcache.class + "testUserManagedCacheDelegatesLifecycleCallsToStore")); assertCacheDelegatesLifecycleCallsToStore(ehcache); @@ -67,8 +64,7 @@ private void assertCacheDelegatesLifecycleCallsToStore(InternalCache cache) thro @Test public void testUserManagedEhcacheFailingTransitionGoesToLowestStatus() throws Exception { final Store store = mock(Store.class); - CacheConfiguration config = new BaseCacheConfiguration<>(Object.class, Object.class, null, null, null, ResourcePoolsHelper - .createHeapOnlyPools()); + CacheConfiguration config = mock(CacheConfiguration.class); Ehcache ehcache = new Ehcache(config, store, mock(ResilienceStrategy.class), mock(CacheEventDispatcher.class), LoggerFactory.getLogger(Ehcache.class + "testUserManagedEhcacheFailingTransitionGoesToLowestStatus")); assertFailingTransitionGoesToLowestStatus(ehcache); Ehcache ehcacheWithLoaderWriter = new Ehcache(config, store, mock(ResilienceStrategy.class), diff --git a/core/src/test/java/org/ehcache/core/collections/ConcurrentWeakIdentityHashMapTest.java b/ehcache-core/src/test/java/org/ehcache/core/collections/ConcurrentWeakIdentityHashMapTest.java similarity index 99% rename from core/src/test/java/org/ehcache/core/collections/ConcurrentWeakIdentityHashMapTest.java rename to ehcache-core/src/test/java/org/ehcache/core/collections/ConcurrentWeakIdentityHashMapTest.java index 0b65f80240..0f8bd3c5bb 100644 --- a/core/src/test/java/org/ehcache/core/collections/ConcurrentWeakIdentityHashMapTest.java +++ b/ehcache-core/src/test/java/org/ehcache/core/collections/ConcurrentWeakIdentityHashMapTest.java @@ -25,12 +25,12 @@ import java.util.Set; import java.util.concurrent.ConcurrentMap; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.startsWith; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; /** * @author Alex Snaps diff --git a/ehcache-core/src/test/java/org/ehcache/core/config/CoreConfigurationBuilderTest.java b/ehcache-core/src/test/java/org/ehcache/core/config/CoreConfigurationBuilderTest.java new file mode 100644 index 0000000000..92d3a9969f --- /dev/null +++ b/ehcache-core/src/test/java/org/ehcache/core/config/CoreConfigurationBuilderTest.java @@ -0,0 +1,51 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.core.config; + +import org.ehcache.config.Configuration; +import org.ehcache.core.util.ClassLoading; +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsSame.sameInstance; +import static org.mockito.Mockito.mock; + +public class CoreConfigurationBuilderTest { + + @Test + public void testWithClassLoader() { + ClassLoader classLoader = mock(ClassLoader.class); + + Configuration configuration = new CoreConfigurationBuilder<>() + .withClassLoader(classLoader) + .build(); + + assertThat(configuration.getClassLoader(), sameInstance(classLoader)); + } + + @Test + public void testWithDefaultClassLoader() { + ClassLoader classLoader = mock(ClassLoader.class); + + Configuration configuration = new CoreConfigurationBuilder<>() + .withClassLoader(classLoader) + .withDefaultClassLoader() + .build(); + + assertThat(configuration.getClassLoader(), sameInstance(ClassLoading.getDefaultClassLoader())); + } + +} diff --git a/core/src/test/java/org/ehcache/core/config/ExpiryUtilsTest.java b/ehcache-core/src/test/java/org/ehcache/core/config/ExpiryUtilsTest.java similarity index 100% rename from core/src/test/java/org/ehcache/core/config/ExpiryUtilsTest.java rename to ehcache-core/src/test/java/org/ehcache/core/config/ExpiryUtilsTest.java diff --git a/ehcache-core/src/test/java/org/ehcache/core/config/ResourcePoolsHelper.java b/ehcache-core/src/test/java/org/ehcache/core/config/ResourcePoolsHelper.java new file mode 100644 index 0000000000..fc7f023774 --- /dev/null +++ b/ehcache-core/src/test/java/org/ehcache/core/config/ResourcePoolsHelper.java @@ -0,0 +1,80 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.core.config; + +import org.ehcache.config.ResourcePool; +import org.ehcache.config.ResourcePools; +import org.ehcache.config.ResourceType; +import org.ehcache.config.ResourceUnit; +import org.ehcache.config.SizedResourcePool; +import org.ehcache.config.units.EntryUnit; + +import java.util.Collections; +import java.util.Set; + +/** + * @author Ludovic Orban + */ +public class ResourcePoolsHelper { + + public static ResourcePools createResourcePools(long size) { + return new ResourcePools() { + @Override @SuppressWarnings("unchecked") + public

P getPoolForResource(ResourceType

resourceType) { + if (ResourceType.Core.HEAP.equals(resourceType)) { + return (P) new SizedResourcePool() { + @Override + public long getSize() { + return size; + } + + @Override + public ResourceUnit getUnit() { + return EntryUnit.ENTRIES; + } + + @Override + public ResourceType getType() { + return ResourceType.Core.HEAP; + } + + @Override + public boolean isPersistent() { + return false; + } + + @Override + public void validateUpdate(ResourcePool newPool) { + //all updates are okay + } + }; + } else { + return null; + } + } + + @Override + public Set> getResourceTypeSet() { + return Collections.singleton(ResourceType.Core.HEAP); + } + + @Override + public ResourcePools validateAndMerge(ResourcePools toBeUpdated) throws IllegalArgumentException, UnsupportedOperationException { + return toBeUpdated; + } + }; + } +} diff --git a/ehcache-core/src/test/java/org/ehcache/core/config/store/StoreStatisticsConfigurationTest.java b/ehcache-core/src/test/java/org/ehcache/core/config/store/StoreStatisticsConfigurationTest.java new file mode 100644 index 0000000000..cc3215e927 --- /dev/null +++ b/ehcache-core/src/test/java/org/ehcache/core/config/store/StoreStatisticsConfigurationTest.java @@ -0,0 +1,36 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.core.config.store; + +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNot.not; +import static org.hamcrest.core.IsSame.sameInstance; + +public class StoreStatisticsConfigurationTest { + + @Test + public void testDeriveDetachesCorrectly() { + StoreStatisticsConfiguration configuration = new StoreStatisticsConfiguration(true); + StoreStatisticsConfiguration derived = configuration.build(configuration.derive()); + + assertThat(derived, is(not(sameInstance(configuration)))); + assertThat(derived.isOperationStatisticsEnabled(), is(configuration.isOperationStatisticsEnabled())); + } +} diff --git a/core/src/test/java/org/ehcache/core/events/CacheEventsTest.java b/ehcache-core/src/test/java/org/ehcache/core/events/CacheEventsTest.java similarity index 100% rename from core/src/test/java/org/ehcache/core/events/CacheEventsTest.java rename to ehcache-core/src/test/java/org/ehcache/core/events/CacheEventsTest.java diff --git a/core/src/test/java/org/ehcache/core/exceptions/ExceptionFactoryTest.java b/ehcache-core/src/test/java/org/ehcache/core/exceptions/ExceptionFactoryTest.java similarity index 100% rename from core/src/test/java/org/ehcache/core/exceptions/ExceptionFactoryTest.java rename to ehcache-core/src/test/java/org/ehcache/core/exceptions/ExceptionFactoryTest.java diff --git a/core/src/test/java/org/ehcache/core/exceptions/StorePassThroughExceptionTest.java b/ehcache-core/src/test/java/org/ehcache/core/exceptions/StorePassThroughExceptionTest.java similarity index 100% rename from core/src/test/java/org/ehcache/core/exceptions/StorePassThroughExceptionTest.java rename to ehcache-core/src/test/java/org/ehcache/core/exceptions/StorePassThroughExceptionTest.java diff --git a/ehcache-core/src/test/java/org/ehcache/core/internal/util/CollectionUtilTest.java b/ehcache-core/src/test/java/org/ehcache/core/internal/util/CollectionUtilTest.java new file mode 100644 index 0000000000..af5f56349e --- /dev/null +++ b/ehcache-core/src/test/java/org/ehcache/core/internal/util/CollectionUtilTest.java @@ -0,0 +1,51 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.core.internal.util; + +import org.ehcache.core.util.CollectionUtil; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; + +import static org.assertj.core.api.Assertions.*; + +public class CollectionUtilTest { + + @Test + public void findBestCollectionSize_sizeable() { + int size = CollectionUtil.findBestCollectionSize(Arrays.asList(1, 2 ,3), 100); + assertThat(size).isEqualTo(3); + } + + @Test + public void findBestCollectionSize_empty() { + int size = CollectionUtil.findBestCollectionSize(Collections.emptySet(), 100); + assertThat(size).isZero(); + } + + @Test + public void findBestCollectionSize_singleton() { + int size = CollectionUtil.findBestCollectionSize(Collections.singleton(1), 100); + assertThat(size).isEqualTo(1); + } + + @Test + public void findBestCollectionSize_notSizeable() { + int size = CollectionUtil.findBestCollectionSize(() -> null, 100); + assertThat(size).isEqualTo(100); + } +} diff --git a/core/src/test/java/org/ehcache/core/internal/service/ServiceLocatorPluralTest.java b/ehcache-core/src/test/java/org/ehcache/core/spi/ServiceLocatorPluralTest.java similarity index 97% rename from core/src/test/java/org/ehcache/core/internal/service/ServiceLocatorPluralTest.java rename to ehcache-core/src/test/java/org/ehcache/core/spi/ServiceLocatorPluralTest.java index 6a739d1776..11c14f3f95 100644 --- a/core/src/test/java/org/ehcache/core/internal/service/ServiceLocatorPluralTest.java +++ b/ehcache-core/src/test/java/org/ehcache/core/spi/ServiceLocatorPluralTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.ehcache.core.internal.service; +package org.ehcache.core.spi; import org.ehcache.spi.service.ServiceProvider; import org.ehcache.spi.service.PluralService; @@ -23,17 +23,15 @@ import org.hamcrest.Matchers; import org.junit.Test; -import java.util.Collection; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; -import static org.ehcache.core.internal.service.ServiceLocator.dependencySet; +import static org.ehcache.core.spi.ServiceLocator.dependencySet; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.everyItem; import static org.hamcrest.Matchers.isOneOf; -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; /** diff --git a/core/src/test/java/org/ehcache/core/internal/service/ServiceLocatorTest.java b/ehcache-core/src/test/java/org/ehcache/core/spi/ServiceLocatorTest.java similarity index 98% rename from core/src/test/java/org/ehcache/core/internal/service/ServiceLocatorTest.java rename to ehcache-core/src/test/java/org/ehcache/core/spi/ServiceLocatorTest.java index ed1b545237..6ff0384d7a 100644 --- a/core/src/test/java/org/ehcache/core/internal/service/ServiceLocatorTest.java +++ b/ehcache-core/src/test/java/org/ehcache/core/spi/ServiceLocatorTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.ehcache.core.internal.service; +package org.ehcache.core.spi; import java.io.IOException; import java.net.URL; @@ -43,13 +43,13 @@ import org.hamcrest.CoreMatchers; import org.junit.Test; -import static org.ehcache.core.internal.service.ServiceLocator.dependencySet; +import static org.ehcache.core.spi.ServiceLocator.dependencySet; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.sameInstance; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; @@ -463,8 +463,8 @@ public void testRankedServiceBecomesMandatory() { @ServiceDependencies(TestService.class) @OptionalServiceDependencies({ - "org.ehcache.core.internal.service.OptService1", - "org.ehcache.core.internal.service.OptService2"}) + "org.ehcache.core.spi.OptService1", + "org.ehcache.core.spi.OptService2"}) class ServiceWithOptionalDeps implements Service { @Override @@ -481,7 +481,7 @@ public void stop() { @ServiceDependencies(TestService.class) @OptionalServiceDependencies({ "org.ehcache.core.internal.service.ServiceThatDoesNotExist", - "org.ehcache.core.internal.service.OptService2"}) + "org.ehcache.core.spi.OptService2"}) class ServiceWithOptionalNonExistentDeps implements Service { @Override @@ -518,7 +518,7 @@ public void stop() { class YetAnotherCacheProvider implements CacheProvider { @Override - public Ehcache createCache(Class keyClazz, Class valueClazz, ServiceConfiguration... config) { + public Ehcache createCache(Class keyClazz, Class valueClazz, ServiceConfiguration... config) { return null; } diff --git a/core/src/test/java/org/ehcache/core/spi/service/ServiceUtilsTest.java b/ehcache-core/src/test/java/org/ehcache/core/spi/service/ServiceUtilsTest.java similarity index 100% rename from core/src/test/java/org/ehcache/core/spi/service/ServiceUtilsTest.java rename to ehcache-core/src/test/java/org/ehcache/core/spi/service/ServiceUtilsTest.java diff --git a/core/src/test/java/org/ehcache/core/spi/services/DefaultTestProvidedService.java b/ehcache-core/src/test/java/org/ehcache/core/spi/services/DefaultTestProvidedService.java similarity index 100% rename from core/src/test/java/org/ehcache/core/spi/services/DefaultTestProvidedService.java rename to ehcache-core/src/test/java/org/ehcache/core/spi/services/DefaultTestProvidedService.java diff --git a/core/src/test/java/org/ehcache/core/spi/services/DefaultTestService.java b/ehcache-core/src/test/java/org/ehcache/core/spi/services/DefaultTestService.java similarity index 100% rename from core/src/test/java/org/ehcache/core/spi/services/DefaultTestService.java rename to ehcache-core/src/test/java/org/ehcache/core/spi/services/DefaultTestService.java diff --git a/core/src/test/java/org/ehcache/core/spi/services/FancyCacheProvider.java b/ehcache-core/src/test/java/org/ehcache/core/spi/services/FancyCacheProvider.java similarity index 95% rename from core/src/test/java/org/ehcache/core/spi/services/FancyCacheProvider.java rename to ehcache-core/src/test/java/org/ehcache/core/spi/services/FancyCacheProvider.java index 0907e01815..954c5d66bc 100644 --- a/core/src/test/java/org/ehcache/core/spi/services/FancyCacheProvider.java +++ b/ehcache-core/src/test/java/org/ehcache/core/spi/services/FancyCacheProvider.java @@ -30,7 +30,7 @@ public class FancyCacheProvider implements CacheProvider { public int startStopCounter = 0; @Override - public Ehcache createCache(Class keyClazz, Class valueClazz, ServiceConfiguration... config) { + public Ehcache createCache(Class keyClazz, Class valueClazz, ServiceConfiguration... config) { return null; } diff --git a/core/src/test/java/org/ehcache/core/spi/services/FancyCacheProviderFactory.java b/ehcache-core/src/test/java/org/ehcache/core/spi/services/FancyCacheProviderFactory.java similarity index 90% rename from core/src/test/java/org/ehcache/core/spi/services/FancyCacheProviderFactory.java rename to ehcache-core/src/test/java/org/ehcache/core/spi/services/FancyCacheProviderFactory.java index dd351289c4..baf0e949ce 100644 --- a/core/src/test/java/org/ehcache/core/spi/services/FancyCacheProviderFactory.java +++ b/ehcache-core/src/test/java/org/ehcache/core/spi/services/FancyCacheProviderFactory.java @@ -24,12 +24,12 @@ */ public class FancyCacheProviderFactory implements ServiceFactory { @Override - public FancyCacheProvider create(ServiceCreationConfiguration configuration) { + public FancyCacheProvider create(ServiceCreationConfiguration configuration) { return new FancyCacheProvider(); } @Override - public Class getServiceType() { + public Class getServiceType() { return FancyCacheProvider.class; } } diff --git a/core/src/test/java/org/ehcache/core/spi/services/TestMandatoryServiceFactory.java b/ehcache-core/src/test/java/org/ehcache/core/spi/services/TestMandatoryServiceFactory.java similarity index 95% rename from core/src/test/java/org/ehcache/core/spi/services/TestMandatoryServiceFactory.java rename to ehcache-core/src/test/java/org/ehcache/core/spi/services/TestMandatoryServiceFactory.java index 43e04a9de7..2ed51b19c7 100644 --- a/core/src/test/java/org/ehcache/core/spi/services/TestMandatoryServiceFactory.java +++ b/ehcache-core/src/test/java/org/ehcache/core/spi/services/TestMandatoryServiceFactory.java @@ -28,7 +28,7 @@ public boolean isMandatory() { } @Override - public TestMandatoryService create(ServiceCreationConfiguration configuration) { + public TestMandatoryService create(ServiceCreationConfiguration configuration) { if (configuration == null) { return new TestMandatoryService(null); } else { @@ -41,7 +41,7 @@ public Class getServiceType() { return TestMandatoryService.class; } - public static class TestMandatoryServiceConfiguration implements ServiceCreationConfiguration { + public static class TestMandatoryServiceConfiguration implements ServiceCreationConfiguration { private final String config; diff --git a/core/src/test/java/org/ehcache/core/spi/services/TestProvidedService.java b/ehcache-core/src/test/java/org/ehcache/core/spi/services/TestProvidedService.java similarity index 100% rename from core/src/test/java/org/ehcache/core/spi/services/TestProvidedService.java rename to ehcache-core/src/test/java/org/ehcache/core/spi/services/TestProvidedService.java diff --git a/core/src/test/java/org/ehcache/core/spi/services/TestProvidedServiceFactory.java b/ehcache-core/src/test/java/org/ehcache/core/spi/services/TestProvidedServiceFactory.java similarity index 90% rename from core/src/test/java/org/ehcache/core/spi/services/TestProvidedServiceFactory.java rename to ehcache-core/src/test/java/org/ehcache/core/spi/services/TestProvidedServiceFactory.java index 10233e0343..064bd04320 100644 --- a/core/src/test/java/org/ehcache/core/spi/services/TestProvidedServiceFactory.java +++ b/ehcache-core/src/test/java/org/ehcache/core/spi/services/TestProvidedServiceFactory.java @@ -24,12 +24,12 @@ */ public class TestProvidedServiceFactory implements ServiceFactory { @Override - public TestProvidedService create(ServiceCreationConfiguration configuration) { + public TestProvidedService create(ServiceCreationConfiguration configuration) { return new DefaultTestProvidedService(); } @Override - public Class getServiceType() { + public Class getServiceType() { return TestProvidedService.class; } } diff --git a/core/src/test/java/org/ehcache/core/spi/services/TestService.java b/ehcache-core/src/test/java/org/ehcache/core/spi/services/TestService.java similarity index 100% rename from core/src/test/java/org/ehcache/core/spi/services/TestService.java rename to ehcache-core/src/test/java/org/ehcache/core/spi/services/TestService.java diff --git a/core/src/test/java/org/ehcache/core/spi/services/TestServiceFactory.java b/ehcache-core/src/test/java/org/ehcache/core/spi/services/TestServiceFactory.java similarity index 89% rename from core/src/test/java/org/ehcache/core/spi/services/TestServiceFactory.java rename to ehcache-core/src/test/java/org/ehcache/core/spi/services/TestServiceFactory.java index d056e7324a..df85d34e81 100644 --- a/core/src/test/java/org/ehcache/core/spi/services/TestServiceFactory.java +++ b/ehcache-core/src/test/java/org/ehcache/core/spi/services/TestServiceFactory.java @@ -24,12 +24,12 @@ */ public class TestServiceFactory implements ServiceFactory { @Override - public TestService create(ServiceCreationConfiguration configuration) { + public TestService create(ServiceCreationConfiguration configuration) { return new DefaultTestService(); } @Override - public Class getServiceType() { - return TestService.class; + public Class getServiceType() { + return DefaultTestService.class; } } diff --git a/core/src/test/java/org/ehcache/core/spi/services/ranking/HighRankServiceAFactory.java b/ehcache-core/src/test/java/org/ehcache/core/spi/services/ranking/HighRankServiceAFactory.java similarity index 97% rename from core/src/test/java/org/ehcache/core/spi/services/ranking/HighRankServiceAFactory.java rename to ehcache-core/src/test/java/org/ehcache/core/spi/services/ranking/HighRankServiceAFactory.java index 3861823c67..d7753b7675 100644 --- a/core/src/test/java/org/ehcache/core/spi/services/ranking/HighRankServiceAFactory.java +++ b/ehcache-core/src/test/java/org/ehcache/core/spi/services/ranking/HighRankServiceAFactory.java @@ -26,7 +26,7 @@ public int rank() { } @Override - public RankServiceA create(ServiceCreationConfiguration configuration) { + public RankServiceA create(ServiceCreationConfiguration configuration) { return new RankServiceA("high-rank"); } diff --git a/core/src/test/java/org/ehcache/core/spi/services/ranking/LowRankServiceBFactory.java b/ehcache-core/src/test/java/org/ehcache/core/spi/services/ranking/LowRankServiceBFactory.java similarity index 97% rename from core/src/test/java/org/ehcache/core/spi/services/ranking/LowRankServiceBFactory.java rename to ehcache-core/src/test/java/org/ehcache/core/spi/services/ranking/LowRankServiceBFactory.java index 67892987ff..6ddbd6c7b0 100644 --- a/core/src/test/java/org/ehcache/core/spi/services/ranking/LowRankServiceBFactory.java +++ b/ehcache-core/src/test/java/org/ehcache/core/spi/services/ranking/LowRankServiceBFactory.java @@ -21,7 +21,7 @@ public class LowRankServiceBFactory implements ServiceFactory { @Override - public RankServiceB create(ServiceCreationConfiguration configuration) { + public RankServiceB create(ServiceCreationConfiguration configuration) { return new RankServiceB("low-rank"); } diff --git a/core/src/test/java/org/ehcache/core/spi/services/ranking/MandatoryHighRankServiceBFactory.java b/ehcache-core/src/test/java/org/ehcache/core/spi/services/ranking/MandatoryHighRankServiceBFactory.java similarity index 97% rename from core/src/test/java/org/ehcache/core/spi/services/ranking/MandatoryHighRankServiceBFactory.java rename to ehcache-core/src/test/java/org/ehcache/core/spi/services/ranking/MandatoryHighRankServiceBFactory.java index 76fc641d29..bdb6e2599a 100644 --- a/core/src/test/java/org/ehcache/core/spi/services/ranking/MandatoryHighRankServiceBFactory.java +++ b/ehcache-core/src/test/java/org/ehcache/core/spi/services/ranking/MandatoryHighRankServiceBFactory.java @@ -31,7 +31,7 @@ public int rank() { } @Override - public RankServiceB create(ServiceCreationConfiguration configuration) { + public RankServiceB create(ServiceCreationConfiguration configuration) { return new RankServiceB("high-rank"); } diff --git a/core/src/test/java/org/ehcache/core/spi/services/ranking/MandatoryLowRankServiceAFactory.java b/ehcache-core/src/test/java/org/ehcache/core/spi/services/ranking/MandatoryLowRankServiceAFactory.java similarity index 97% rename from core/src/test/java/org/ehcache/core/spi/services/ranking/MandatoryLowRankServiceAFactory.java rename to ehcache-core/src/test/java/org/ehcache/core/spi/services/ranking/MandatoryLowRankServiceAFactory.java index df08065adb..b691e4cff5 100644 --- a/core/src/test/java/org/ehcache/core/spi/services/ranking/MandatoryLowRankServiceAFactory.java +++ b/ehcache-core/src/test/java/org/ehcache/core/spi/services/ranking/MandatoryLowRankServiceAFactory.java @@ -26,7 +26,7 @@ public boolean isMandatory() { } @Override - public RankServiceA create(ServiceCreationConfiguration configuration) { + public RankServiceA create(ServiceCreationConfiguration configuration) { return new RankServiceA("low-rank"); } diff --git a/core/src/test/java/org/ehcache/core/spi/services/ranking/RankServiceA.java b/ehcache-core/src/test/java/org/ehcache/core/spi/services/ranking/RankServiceA.java similarity index 100% rename from core/src/test/java/org/ehcache/core/spi/services/ranking/RankServiceA.java rename to ehcache-core/src/test/java/org/ehcache/core/spi/services/ranking/RankServiceA.java diff --git a/core/src/test/java/org/ehcache/core/spi/services/ranking/RankServiceB.java b/ehcache-core/src/test/java/org/ehcache/core/spi/services/ranking/RankServiceB.java similarity index 100% rename from core/src/test/java/org/ehcache/core/spi/services/ranking/RankServiceB.java rename to ehcache-core/src/test/java/org/ehcache/core/spi/services/ranking/RankServiceB.java diff --git a/ehcache-core/src/test/java/org/ehcache/core/spi/store/AbstractValueHolderTest.java b/ehcache-core/src/test/java/org/ehcache/core/spi/store/AbstractValueHolderTest.java new file mode 100644 index 0000000000..7105a313c4 --- /dev/null +++ b/ehcache-core/src/test/java/org/ehcache/core/spi/store/AbstractValueHolderTest.java @@ -0,0 +1,171 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.core.spi.store; + +import org.ehcache.core.spi.time.TimeSource; +import org.junit.Test; + +import java.util.concurrent.TimeUnit; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; + +/** + * @author Ludovic Orban + */ +public class AbstractValueHolderTest { + + @Test + public void testCreationTime() throws Exception { + AbstractValueHolder valueHolder = newAbstractValueHolder(1000L); + + assertThat(valueHolder.creationTime(), is(1000L)); + } + + @Test + public void testExpirationTime() throws Exception { + AbstractValueHolder valueHolder = newAbstractValueHolder(0L, 1000L); + + assertThat(valueHolder.expirationTime(), is(1000L)); + } + + + @Test + public void testLastAccessTime() throws Exception { + // last access time defaults to create time + AbstractValueHolder valueHolder = newAbstractValueHolder(1000L); + + assertThat(valueHolder.lastAccessTime(), is(1000L)); + + valueHolder = newAbstractValueHolder(1000L, 0L, 2000L); + + assertThat(valueHolder.lastAccessTime(), is(2000L)); + } + + + @Test + public void testIsExpired() throws Exception { + assertThat(newAbstractValueHolder(1000L).isExpired(1000L), is(false)); + + assertThat(newAbstractValueHolder(1000L, 1001L).isExpired(1000L), is(false)); + + assertThat(newAbstractValueHolder(1000L, 1000L).isExpired(1000L), is(true)); + } + + @Test + public void testEquals() throws Exception { + assertThat(newAbstractValueHolder( 0L).equals(newAbstractValueHolder( 0L)), is(true)); + assertThat(newAbstractValueHolder( 1L).equals(newAbstractValueHolder( 0L)), is(false)); + + assertThat(newAbstractValueHolder(2L, 0L).equals(newAbstractValueHolder(2L, 0L)), is(true)); + assertThat(newAbstractValueHolder(2L, 0L).equals(newAbstractValueHolder(2L, 1L)), is(false)); + assertThat(newAbstractValueHolder(2L, 0L).equals(newAbstractValueHolder(3L, 0L)), is(false)); + + assertThat(newAbstractValueHolder(0L, 2L, 1L).equals(newAbstractValueHolder(0L, 2L, 1L)), is(true)); + assertThat(newAbstractValueHolder(1L, 2L, 1L).equals(newAbstractValueHolder(0L, 2L, 1L)), is(false)); + + assertThat(newAbstractValueHolder(0L, 3L, 1L).equals(newAbstractValueHolder(0L, 2L, 1L)), is(false)); + assertThat(newAbstractValueHolder(0L, 2L, 3L).equals(newAbstractValueHolder(0L, 2L, 1L)), is(false)); + } + + @Test + public void testSubclassEquals() throws Exception { + assertThat(new AbstractValueHolder(-1, 1L) { + @Override + public String get() { + return "aaa"; + } + + @Override + public int hashCode() { + return super.hashCode() + get().hashCode(); + } + @Override + public boolean equals(Object obj) { + if (obj instanceof AbstractValueHolder) { + AbstractValueHolder other = (AbstractValueHolder) obj; + return super.equals(obj) && get().equals(other.get()); + } + return false; + } + }.equals(new AbstractValueHolder(-1, 1L) { + @Override + public String get() { + return "aaa"; + } + + @Override + public int hashCode() { + return super.hashCode() + get().hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof AbstractValueHolder) { + AbstractValueHolder other = (AbstractValueHolder)obj; + return super.equals(obj) && get().equals(other.get()); + } + return false; + } + }), is(true)); + + } + + private AbstractValueHolder newAbstractValueHolder(long creationTime) { + return new AbstractValueHolder(-1, creationTime) { + @Override + public String get() { + throw new UnsupportedOperationException(); + } + }; + } + + private AbstractValueHolder newAbstractValueHolder(long creationTime, long expirationTime) { + return new AbstractValueHolder(-1, creationTime, expirationTime) { + @Override + public String get() { + throw new UnsupportedOperationException(); + } + }; + } + + private AbstractValueHolder newAbstractValueHolder(long creationTime, long expirationTime, long lastAccessTime) { + final AbstractValueHolder abstractValueHolder = new AbstractValueHolder(-1, creationTime, expirationTime) { + @Override + public String get() { + throw new UnsupportedOperationException(); + } + }; + abstractValueHolder.setLastAccessTime(lastAccessTime); + return abstractValueHolder; + } + + private static class TestTimeSource implements TimeSource { + + private long time = 0; + + @Override + public long getTimeMillis() { + return time; + } + + public void advanceTime(long step) { + time += step; + } + } + +} diff --git a/core/src/test/java/org/ehcache/core/spi/store/CacheProvider.java b/ehcache-core/src/test/java/org/ehcache/core/spi/store/CacheProvider.java similarity index 95% rename from core/src/test/java/org/ehcache/core/spi/store/CacheProvider.java rename to ehcache-core/src/test/java/org/ehcache/core/spi/store/CacheProvider.java index 7228e5bdb5..0db6eb7b02 100644 --- a/core/src/test/java/org/ehcache/core/spi/store/CacheProvider.java +++ b/ehcache-core/src/test/java/org/ehcache/core/spi/store/CacheProvider.java @@ -27,7 +27,7 @@ @PluralService public interface CacheProvider extends Service { - Ehcache createCache(Class keyClazz, Class valueClazz, ServiceConfiguration... config); + Ehcache createCache(Class keyClazz, Class valueClazz, ServiceConfiguration... config); void releaseCache(Ehcache resource); } diff --git a/core/src/test/java/org/ehcache/core/spi/time/TickingTimeSourceTest.java b/ehcache-core/src/test/java/org/ehcache/core/spi/time/TickingTimeSourceTest.java similarity index 100% rename from core/src/test/java/org/ehcache/core/spi/time/TickingTimeSourceTest.java rename to ehcache-core/src/test/java/org/ehcache/core/spi/time/TickingTimeSourceTest.java diff --git a/core/src/test/java/org/ehcache/core/internal/store/StoreSupportTest.java b/ehcache-core/src/test/java/org/ehcache/core/store/StoreSupportTest.java similarity index 91% rename from core/src/test/java/org/ehcache/core/internal/store/StoreSupportTest.java rename to ehcache-core/src/test/java/org/ehcache/core/store/StoreSupportTest.java index ae223eac4e..ec93741041 100644 --- a/core/src/test/java/org/ehcache/core/internal/store/StoreSupportTest.java +++ b/ehcache-core/src/test/java/org/ehcache/core/store/StoreSupportTest.java @@ -14,11 +14,11 @@ * limitations under the License. */ -package org.ehcache.core.internal.store; +package org.ehcache.core.store; import org.ehcache.config.ResourcePool; import org.ehcache.config.ResourceType; -import org.ehcache.core.internal.service.ServiceLocator; +import org.ehcache.core.spi.ServiceLocator; import org.ehcache.core.spi.store.Store; import org.ehcache.spi.service.ServiceProvider; import org.ehcache.spi.service.Service; @@ -32,12 +32,13 @@ import java.util.concurrent.atomic.AtomicInteger; import static java.util.Arrays.asList; -import static org.ehcache.core.internal.service.ServiceLocator.dependencySet; +import static org.ehcache.core.spi.ServiceLocator.dependencySet; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.startsWith; -import static org.junit.Assert.*; +import static org.junit.Assert.fail; /** * Tests functionality of {@link StoreSupport} methods. @@ -80,7 +81,7 @@ public void testSelectStoreProvider() throws Exception { final ServiceLocator serviceLocator = dependencySet().with(storeProviders).build(); final Store.Provider selectedProvider = StoreSupport.selectStoreProvider(serviceLocator, Collections.>singleton(anyResourceType), - Collections.>emptyList()); + Collections.>emptyList()); assertThat(selectedProvider, is(Matchers.sameInstance(expectedProvider))); @@ -106,7 +107,7 @@ public void testSelectStoreProviderMultiple() throws Exception { try { StoreSupport.selectStoreProvider(serviceLocator, Collections.>singleton(anyResourceType), - Collections.>emptyList()); + Collections.>emptyList()); fail(); } catch (IllegalStateException e) { // expected @@ -123,7 +124,7 @@ public void testSelectStoreProviderNoProviders() throws Exception { try { StoreSupport.selectStoreProvider(dependencySet().build(), Collections.>singleton(anyResourceType), - Collections.>emptyList()); + Collections.>emptyList()); fail(); } catch (IllegalStateException e) { // expected @@ -163,7 +164,7 @@ public int getTierHeight() { try { StoreSupport.selectStoreProvider(serviceLocator, Collections.>singleton(otherResourceType), - Collections.>emptyList()); + Collections.>emptyList()); fail(); } catch (IllegalStateException e) { // expected @@ -214,7 +215,7 @@ public TestBaseProvider(final int rank) { } @Override - public Store createStore(final Store.Configuration storeConfig, final ServiceConfiguration... serviceConfigs) { + public Store createStore(final Store.Configuration storeConfig, final ServiceConfiguration... serviceConfigs) { throw new UnsupportedOperationException("TestBaseProvider.createStore not implemented"); } @@ -229,7 +230,7 @@ public void initStore(final Store resource) { } @Override - public int rank(final Set> resourceTypes, final Collection> serviceConfigs) { + public int rank(final Set> resourceTypes, final Collection> serviceConfigs) { assertThat(resourceTypes, is(not(nullValue()))); assertThat(serviceConfigs, is(not(nullValue()))); rankAccessCount.incrementAndGet(); diff --git a/core/src/test/java/org/ehcache/core/internal/util/ClassLoadingTest.java b/ehcache-core/src/test/java/org/ehcache/core/util/ClassLoadingTest.java similarity index 54% rename from core/src/test/java/org/ehcache/core/internal/util/ClassLoadingTest.java rename to ehcache-core/src/test/java/org/ehcache/core/util/ClassLoadingTest.java index 90d7495d2b..2e762b1ba3 100644 --- a/core/src/test/java/org/ehcache/core/internal/util/ClassLoadingTest.java +++ b/ehcache-core/src/test/java/org/ehcache/core/util/ClassLoadingTest.java @@ -14,46 +14,58 @@ * limitations under the License. */ -package org.ehcache.core.internal.util; +package org.ehcache.core.util; -import static org.junit.Assert.*; +import static java.util.Collections.list; +import static org.ehcache.core.util.ClassLoading.getDefaultClassLoader; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.collection.IsIterableContainingInOrder.contains; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Enumeration; +import java.util.Map; import java.util.Vector; -import org.ehcache.core.internal.util.ClassLoading; import org.junit.Test; public class ClassLoadingTest { @Test public void testDefaultClassLoader() throws Exception { - String resource = getClass().getName().replace('.', '/').concat(".class"); - ClassLoader thisLoader = getClass().getClassLoader(); - ClassLoader defaultClassLoader = ClassLoading.getDefaultClassLoader(); - - Thread.currentThread().setContextClassLoader(null); - assertSame(thisLoader.loadClass(getClass().getName()), defaultClassLoader.loadClass(getClass().getName())); - assertEquals(thisLoader.getResource(resource), defaultClassLoader.getResource(resource)); - assertEqualEnumeration(thisLoader.getResources(resource), defaultClassLoader.getResources(resource)); - - Thread.currentThread().setContextClassLoader(new FindNothingLoader()); - assertSame(thisLoader.loadClass(getClass().getName()), defaultClassLoader.loadClass(getClass().getName())); - assertEquals(thisLoader.getResource(resource), defaultClassLoader.getResource(resource)); - assertEqualEnumeration(thisLoader.getResources(resource), defaultClassLoader.getResources(resource)); - - URL url = new URL("file:///tmp"); - ClassLoader tc = new TestClassLoader(url); - Thread.currentThread().setContextClassLoader(tc); - Class c = defaultClassLoader.loadClass(getClass().getName()); - assertNotSame(getClass(), c); - assertSame(tc, c.getClassLoader()); - assertEquals(url, defaultClassLoader.getResource(resource)); - assertEqualEnumeration(enumerationOf(url), defaultClassLoader.getResources(resource)); + ClassLoader originalTccl = Thread.currentThread().getContextClassLoader(); + try { + String resource = getClass().getName().replace('.', '/').concat(".class"); + ClassLoader thisLoader = getClass().getClassLoader(); + ClassLoader defaultClassLoader = getDefaultClassLoader(); + + Thread.currentThread().setContextClassLoader(null); + assertSame(thisLoader.loadClass(getClass().getName()), defaultClassLoader.loadClass(getClass().getName())); + assertEquals(thisLoader.getResource(resource), defaultClassLoader.getResource(resource)); + assertThat(list(defaultClassLoader.getResources(resource)), is(list(thisLoader.getResources(resource)))); + + Thread.currentThread().setContextClassLoader(new FindNothingLoader()); + assertSame(thisLoader.loadClass(getClass().getName()), defaultClassLoader.loadClass(getClass().getName())); + assertEquals(thisLoader.getResource(resource), defaultClassLoader.getResource(resource)); + assertThat(list(defaultClassLoader.getResources(resource)), is(list(thisLoader.getResources(resource)))); + + URL url = new URL("file:///tmp"); + ClassLoader tc = new TestClassLoader(url); + Thread.currentThread().setContextClassLoader(tc); + Class c = defaultClassLoader.loadClass(getClass().getName()); + assertNotSame(getClass(), c); + assertSame(tc, c.getClassLoader()); + assertEquals(url, defaultClassLoader.getResource(resource)); + assertThat(list(defaultClassLoader.getResources(resource)), contains(url, thisLoader.getResource(resource))); + } finally { + Thread.currentThread().setContextClassLoader(originalTccl); + } } @SafeVarargs @@ -120,28 +132,4 @@ public Enumeration getResources(String name) throws IOException { return new Vector().elements(); } } - - private void assertEqualEnumeration(Enumeration e1, Enumeration e2) { - while (e1.hasMoreElements()) { - if (!e2.hasMoreElements()) { - throw new AssertionError(); - } - - Object o1 = e1.nextElement(); - Object o2 = e2.nextElement(); - - if (o1 == null || o2 == null) { - throw new AssertionError(); - } - - if ((!o1.equals(o2)) || (!o2.equals(o1))) { - throw new AssertionError(); - } - } - - if (e2.hasMoreElements()) { - throw new AssertionError(); - } - } - } diff --git a/core/src/test/java/org/ehcache/core/util/IsCreated.java b/ehcache-core/src/test/java/org/ehcache/core/util/IsCreated.java similarity index 100% rename from core/src/test/java/org/ehcache/core/util/IsCreated.java rename to ehcache-core/src/test/java/org/ehcache/core/util/IsCreated.java diff --git a/core/src/test/java/org/ehcache/core/util/IsRemoved.java b/ehcache-core/src/test/java/org/ehcache/core/util/IsRemoved.java similarity index 100% rename from core/src/test/java/org/ehcache/core/util/IsRemoved.java rename to ehcache-core/src/test/java/org/ehcache/core/util/IsRemoved.java diff --git a/core/src/test/java/org/ehcache/core/util/IsUpdated.java b/ehcache-core/src/test/java/org/ehcache/core/util/IsUpdated.java similarity index 100% rename from core/src/test/java/org/ehcache/core/util/IsUpdated.java rename to ehcache-core/src/test/java/org/ehcache/core/util/IsUpdated.java diff --git a/core/src/test/java/org/ehcache/core/util/Matchers.java b/ehcache-core/src/test/java/org/ehcache/core/util/Matchers.java similarity index 100% rename from core/src/test/java/org/ehcache/core/util/Matchers.java rename to ehcache-core/src/test/java/org/ehcache/core/util/Matchers.java diff --git a/ehcache-core/src/test/java/org/ehcache/core/util/TestCacheConfig.java b/ehcache-core/src/test/java/org/ehcache/core/util/TestCacheConfig.java new file mode 100644 index 0000000000..7d7ec62d12 --- /dev/null +++ b/ehcache-core/src/test/java/org/ehcache/core/util/TestCacheConfig.java @@ -0,0 +1,262 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.core.util; + +import org.ehcache.config.CacheConfiguration; +import org.ehcache.config.Eviction; +import org.ehcache.config.EvictionAdvisor; +import org.ehcache.config.FluentCacheConfigurationBuilder; +import org.ehcache.config.ResourcePools; +import org.ehcache.expiry.ExpiryPolicy; +import org.ehcache.spi.copy.Copier; +import org.ehcache.spi.loaderwriter.CacheLoaderWriter; +import org.ehcache.spi.resilience.ResilienceStrategy; +import org.ehcache.spi.serialization.Serializer; +import org.ehcache.spi.service.ServiceConfiguration; + +import java.util.Collection; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; + +import static java.util.Collections.emptyList; +import static org.ehcache.core.config.ExpiryUtils.convertToExpiry; +import static org.ehcache.core.config.ResourcePoolsHelper.createResourcePools; + +public class TestCacheConfig implements CacheConfiguration { + + private final Class keyType; + private final Class valueType; + private final ResourcePools resources; + + public TestCacheConfig(Class keyType, Class valueType) { + this(keyType, valueType, createResourcePools(100L)); + } + + public TestCacheConfig(Class keyType, Class valueType, ResourcePools resources) { + this.keyType = keyType; + this.valueType = valueType; + this.resources = resources; + } + + @Override + public Collection> getServiceConfigurations() { + return emptyList(); + } + + @Override + public Class getKeyType() { + return keyType; + } + + @Override + public Class getValueType() { + return valueType; + } + + @Override + public EvictionAdvisor getEvictionAdvisor() { + return Eviction.noAdvice(); + } + + @Override + public ClassLoader getClassLoader() { + return null; + } + + @Override @SuppressWarnings("deprecation") + public org.ehcache.expiry.Expiry getExpiry() { + return convertToExpiry(getExpiryPolicy()); + } + + @Override + public ExpiryPolicy getExpiryPolicy() { + return ExpiryPolicy.NO_EXPIRY; + } + + @Override + public ResourcePools getResourcePools() { + return resources; + } + + @Override + public Builder derive() { + return new Builder(); + } + + public class Builder implements FluentCacheConfigurationBuilder { + + @Override + public CacheConfiguration build() { + return TestCacheConfig.this; + } + + @Override + public > Collection getServices(Class configurationType) { + throw new UnsupportedOperationException(); + } + + @Override + public Builder withService(ServiceConfiguration config) { + throw new UnsupportedOperationException(); + } + + @Override + public > Builder withoutServices(Class clazz, Predicate predicate) { + throw new UnsupportedOperationException(); + } + + @Override + public > Builder updateServices(Class clazz, UnaryOperator update) { + throw new UnsupportedOperationException(); + } + + @Override + public Builder withEvictionAdvisor(EvictionAdvisor evictionAdvisor) { + throw new UnsupportedOperationException(); + } + + @Override + public Builder withClassLoader(ClassLoader classLoader) { + return new TestCacheConfig(getKeyType(), getValueType(), resources) { + @Override + public ClassLoader getClassLoader() { + return classLoader; + } + }.derive(); + } + + @Override + public Builder withDefaultClassLoader() { + throw new UnsupportedOperationException(); + } + + @Override + public Builder withResourcePools(ResourcePools resourcePools) { + throw new UnsupportedOperationException(); + } + + @Override + public Builder updateResourcePools(UnaryOperator update) { + throw new UnsupportedOperationException(); + } + + @Override + public Builder withExpiry(ExpiryPolicy expiry) { + throw new UnsupportedOperationException(); + } + + @Override + public Builder withLoaderWriter(CacheLoaderWriter loaderWriter) { + throw new UnsupportedOperationException(); + } + + @Override + public Builder withLoaderWriter(Class> cacheLoaderWriterClass, Object... arguments) { + throw new UnsupportedOperationException(); + } + + @Override + public Builder withoutLoaderWriter() { + throw new UnsupportedOperationException(); + } + + @Override + public Builder withResilienceStrategy(ResilienceStrategy resilienceStrategy) { + throw new UnsupportedOperationException(); + } + + @Override @SuppressWarnings("rawtypes") + public Builder withResilienceStrategy(Class resilienceStrategyClass, Object... arguments) { + throw new UnsupportedOperationException(); + } + + @Override + public Builder withDefaultResilienceStrategy() { + throw new UnsupportedOperationException(); + } + + @Override + public Builder withKeySerializingCopier() { + throw new UnsupportedOperationException(); + } + + @Override + public Builder withValueSerializingCopier() { + throw new UnsupportedOperationException(); + } + + @Override + public Builder withKeyCopier(Copier keyCopier) { + throw new UnsupportedOperationException(); + } + + @Override + public Builder withKeyCopier(Class> keyCopierClass) { + throw new UnsupportedOperationException(); + } + + @Override + public Builder withoutKeyCopier() { + throw new UnsupportedOperationException(); + } + + @Override + public Builder withValueCopier(Copier valueCopier) { + throw new UnsupportedOperationException(); + } + + @Override + public Builder withValueCopier(Class> valueCopierClass) { + throw new UnsupportedOperationException(); + } + + @Override + public Builder withoutValueCopier() { + throw new UnsupportedOperationException(); + } + + @Override + public Builder withKeySerializer(Serializer keySerializer) { + throw new UnsupportedOperationException(); + } + + @Override + public Builder withKeySerializer(Class> keySerializerClass) { + throw new UnsupportedOperationException(); + } + + @Override + public Builder withDefaultKeySerializer() { + throw new UnsupportedOperationException(); + } + + @Override + public Builder withValueSerializer(Serializer valueSerializer) { + throw new UnsupportedOperationException(); + } + + @Override + public Builder withValueSerializer(Class> valueSerializerClass) { + throw new UnsupportedOperationException(); + } + + @Override + public Builder withDefaultValueSerializer() { + throw new UnsupportedOperationException(); + } + } +} diff --git a/core/src/test/resources/META-INF/services/org.ehcache.core.spi.service.ServiceFactory b/ehcache-core/src/test/resources/META-INF/services/org.ehcache.core.spi.service.ServiceFactory similarity index 100% rename from core/src/test/resources/META-INF/services/org.ehcache.core.spi.service.ServiceFactory rename to ehcache-core/src/test/resources/META-INF/services/org.ehcache.core.spi.service.ServiceFactory diff --git a/ehcache-impl/build.gradle b/ehcache-impl/build.gradle new file mode 100644 index 0000000000..3d338a724a --- /dev/null +++ b/ehcache-impl/build.gradle @@ -0,0 +1,85 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id 'org.ehcache.build.internal-module' +} + +publishing.publications.withType(MavenPublication) { + pom { + name = 'Ehcache 3 Implementation module' + description = 'The implementation module of Ehcache 3' + } +} + +sourceSets { + unsafe +} + +sourceSets { + slowTest { + java.srcDir 'src/slow-test/java' + resources.srcDir 'src/slow-test/resources' + compileClasspath += sourceSets.test.compileClasspath + runtimeClasspath += sourceSets.test.runtimeClasspath + } +} + +task slowTest(type: Test) { + testClassesDirs = sourceSets.slowTest.output.classesDirs + classpath += sourceSets.slowTest.runtimeClasspath + + binResultsDir file("$buildDir/slow-tests-results/binary/$name") + reports.junitXml.destination = file("$buildDir/slow-tests-results") + reports.html.destination = file("$buildDir/reports/slow-tests") +} + + +dependencies { + api project(':ehcache-core') + implementation group: 'org.terracotta', name: 'offheap-store', version: parent.offheapVersion + implementation group: 'org.ehcache', name: 'sizeof', version: parent.sizeofVersion + implementation group: 'org.terracotta', name: 'terracotta-utilities-tools', version: parent.terracottaUtilitiesVersion + compileOnly 'org.osgi:org.osgi.service.component.annotations:1.3.0' + testImplementation project(':core-spi-test') + testImplementation 'org.ow2.asm:asm:6.2' + testImplementation 'org.ow2.asm:asm-commons:6.2' + testImplementation ("org.terracotta:statistics:$parent.statisticVersion") + + unsafeImplementation project(':ehcache-api') + api files(sourceSets.unsafe.output.classesDirs) { + builtBy compileUnsafeJava + } +} + +jar { + from sourceSets.unsafe.output + from "$rootDir/NOTICE" + bnd ( + 'Export-Package': '!org.ehcache.impl.internal.*, org.ehcache.impl.*, org.ehcache.config.builders, ' + + 'org.ehcache.impl.internal.spi.loaderwriter', //ugly 107 induced internal export wart + 'Import-Package': '!sun.misc, !javax.annotation, *', + ) +} + +sourcesJar { + from sourceSets.unsafe.allSource +} + +compileUnsafeJava { + //no -Werror due to unsafe + options.compilerArgs = ['-Xlint:all'] +} diff --git a/impl/config/checkstyle-suppressions.xml b/ehcache-impl/config/checkstyle-suppressions.xml similarity index 100% rename from impl/config/checkstyle-suppressions.xml rename to ehcache-impl/config/checkstyle-suppressions.xml diff --git a/impl/src/main/java/org/ehcache/config/builders/CacheConfigurationBuilder.java b/ehcache-impl/src/main/java/org/ehcache/config/builders/CacheConfigurationBuilder.java similarity index 53% rename from impl/src/main/java/org/ehcache/config/builders/CacheConfigurationBuilder.java rename to ehcache-impl/src/main/java/org/ehcache/config/builders/CacheConfigurationBuilder.java index df07cd11be..f5b875f4b7 100644 --- a/impl/src/main/java/org/ehcache/config/builders/CacheConfigurationBuilder.java +++ b/ehcache-impl/src/main/java/org/ehcache/config/builders/CacheConfigurationBuilder.java @@ -19,9 +19,10 @@ import org.ehcache.config.Builder; import org.ehcache.config.CacheConfiguration; import org.ehcache.config.EvictionAdvisor; +import org.ehcache.config.FluentCacheConfigurationBuilder; import org.ehcache.config.ResourcePools; import org.ehcache.config.units.MemoryUnit; -import org.ehcache.core.config.BaseCacheConfiguration; +import org.ehcache.impl.config.BaseCacheConfiguration; import org.ehcache.core.config.store.StoreEventSourceConfiguration; import org.ehcache.core.spi.store.heap.SizeOfEngine; import org.ehcache.expiry.ExpiryPolicy; @@ -44,11 +45,14 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; +import java.util.Iterator; import java.util.List; +import java.util.function.Predicate; +import java.util.function.Supplier; import java.util.function.UnaryOperator; import static java.util.Objects.requireNonNull; -import static java.util.Optional.ofNullable; +import static java.util.stream.Collectors.toList; import static org.ehcache.core.config.ExpiryUtils.convertToExpiryPolicy; import static org.ehcache.impl.config.store.heap.DefaultSizeOfEngineConfiguration.DEFAULT_MAX_OBJECT_SIZE; import static org.ehcache.impl.config.store.heap.DefaultSizeOfEngineConfiguration.DEFAULT_OBJECT_GRAPH_SIZE; @@ -62,9 +66,9 @@ * instance without modifying the one on which the method was called. * This enables the sharing of builder instances without any risk of seeing them modified by code elsewhere. */ -public class CacheConfigurationBuilder implements Builder> { +public class CacheConfigurationBuilder implements FluentCacheConfigurationBuilder> { - private final Collection> serviceConfigurations = new HashSet<>(); + private final Collection> serviceConfigurations = new HashSet<>(); private ExpiryPolicy expiry; private ClassLoader classLoader = null; private EvictionAdvisor evictionAdvisor; @@ -112,11 +116,14 @@ public static CacheConfigurationBuilder newCacheConfigurationBuilde */ public static CacheConfigurationBuilder newCacheConfigurationBuilder(CacheConfiguration configuration) { CacheConfigurationBuilder builder = newCacheConfigurationBuilder(configuration.getKeyType(), configuration.getValueType(), configuration.getResourcePools()) - .withClassLoader(configuration.getClassLoader()) .withEvictionAdvisor(configuration.getEvictionAdvisor()) .withExpiry(configuration.getExpiryPolicy()); - for (ServiceConfiguration serviceConfig : configuration.getServiceConfigurations()) { - builder = builder.add(serviceConfig); + ClassLoader classLoader = configuration.getClassLoader(); + if (classLoader != null) { + builder = builder.withClassLoader(classLoader); + } + for (ServiceConfiguration serviceConfig : configuration.getServiceConfigurations()) { + builder = builder.withService(serviceConfig); } return builder; } @@ -142,24 +149,21 @@ private CacheConfigurationBuilder(CacheConfigurationBuilder other) { * * @param configuration the service configuration to add * @return a new builder with the added service configuration + * @deprecated in favor of {@link #withService(ServiceConfiguration)} */ - public CacheConfigurationBuilder add(ServiceConfiguration configuration) { - CacheConfigurationBuilder otherBuilder = new CacheConfigurationBuilder<>(this); - - if (getExistingServiceConfiguration(configuration.getClass()) != null) { - if (configuration instanceof DefaultCopierConfiguration) { - DefaultCopierConfiguration copierConfiguration = (DefaultCopierConfiguration) configuration; - otherBuilder.removeExistingCopierConfigFor(copierConfiguration.getType()); - } else if (configuration instanceof DefaultSerializerConfiguration) { - DefaultSerializerConfiguration serializerConfiguration = (DefaultSerializerConfiguration) configuration; - otherBuilder.removeExistingSerializerConfigFor(serializerConfiguration.getType()); - } else if (!(configuration instanceof DefaultCacheEventListenerConfiguration)) { + @Deprecated + public CacheConfigurationBuilder add(ServiceConfiguration configuration) { + if (!getServices(configuration.getClass()).isEmpty()) { + if (configuration instanceof DefaultCopierConfiguration + || configuration instanceof DefaultSerializerConfiguration + || configuration instanceof DefaultCacheEventListenerConfiguration) { + return withService(configuration); + } else { throw new IllegalStateException("Cannot add a generic service configuration when another one already exists. " + - "Rely on specific with* methods or make sure your remove other configuration first."); + "Rely on specific with* methods or make sure your remove other configuration first."); } } - otherBuilder.serviceConfigurations.add(configuration); - return otherBuilder; + return withService(configuration); } /** @@ -169,17 +173,61 @@ public CacheConfigurationBuilder add(ServiceConfiguration configuration * @return a new builder with the added service configuration * * @see #add(ServiceConfiguration) + * @deprecated in favor of {@link #withService(Builder)} */ - public CacheConfigurationBuilder add(Builder> configurationBuilder) { + @Deprecated + public CacheConfigurationBuilder add(Builder> configurationBuilder) { return add(configurationBuilder.build()); } - /** - * Adds an {@link EvictionAdvisor} to the returned builder. - * - * @param evictionAdvisor the eviction advisor to be used - * @return a new builder with the added eviction advisor - */ + @Override + public > Collection getServices(Class configurationType) throws IllegalArgumentException { + return serviceConfigurations.stream().filter(configurationType::isInstance).map(configurationType::cast).collect(toList()); + } + + @Override + public CacheConfigurationBuilder withService(ServiceConfiguration config) { + CacheConfigurationBuilder otherBuilder = new CacheConfigurationBuilder<>(this); + otherBuilder.serviceConfigurations.removeIf(other -> !other.compatibleWith(config) || !config.compatibleWith(other)); + otherBuilder.serviceConfigurations.add(config); + return otherBuilder; + } + + @Override + public CacheConfigurationBuilder withoutServices(Class> clazz) { + CacheConfigurationBuilder otherBuilder = new CacheConfigurationBuilder<>(this); + otherBuilder.serviceConfigurations.removeIf(clazz::isInstance); + return otherBuilder; + } + + @Override + public >CacheConfigurationBuilder withoutServices(Class clazz, Predicate predicate) { + CacheConfigurationBuilder otherBuilder = new CacheConfigurationBuilder<>(this); + otherBuilder.serviceConfigurations.removeIf(c -> clazz.isInstance(c) && predicate.test(clazz.cast(c))); + return otherBuilder; + } + + @Override + public > CacheConfigurationBuilder updateServices(Class clazz, UnaryOperator update) { + Collection> existing = getServices(clazz); + + if (existing.isEmpty()) { + throw new IllegalStateException("Cannot update service configurations. No existing services of type: " + clazz); + } else { + CacheConfigurationBuilder otherBuilder = withoutServices(clazz); + for (ServiceConfiguration configuration : existing) { + ServiceConfiguration replacement = configuration.build(update.apply(configuration.derive())); + if (replacement == null) { + throw new NullPointerException(configuration.getClass().getSimpleName() + ".build(...) returned a null configuration instance"); + } else { + otherBuilder = otherBuilder.withService(replacement); + } + } + return otherBuilder; + } + } + + @Override public CacheConfigurationBuilder withEvictionAdvisor(final EvictionAdvisor evictionAdvisor) { CacheConfigurationBuilder otherBuilder = new CacheConfigurationBuilder<>(this); otherBuilder.evictionAdvisor = evictionAdvisor; @@ -191,8 +239,10 @@ public CacheConfigurationBuilder withEvictionAdvisor(final EvictionAdvisor * * @param configuration the service configuration to remove * @return a new builder without the specified configuration + * @deprecated in favor of {@link #withoutServices(Class)} or {@link #withoutServices(Class, Predicate)} */ - public CacheConfigurationBuilder remove(ServiceConfiguration configuration) { + @Deprecated + public CacheConfigurationBuilder remove(ServiceConfiguration configuration) { CacheConfigurationBuilder otherBuilder = new CacheConfigurationBuilder<>(this); otherBuilder.serviceConfigurations.remove(configuration); return otherBuilder; @@ -202,11 +252,11 @@ public CacheConfigurationBuilder remove(ServiceConfiguration configurat * Clears all {@link ServiceConfiguration}s from the returned builder. * * @return a new builder with no service configurations left + * @deprecated in favor of {@link #withoutServices(Class) withoutServices(ServiceConfiguration.class)} */ + @Deprecated @SuppressWarnings("unchecked") public CacheConfigurationBuilder clearAllServiceConfig() { - CacheConfigurationBuilder otherBuilder = new CacheConfigurationBuilder<>(this); - otherBuilder.serviceConfigurations.clear(); - return otherBuilder; + return withoutServices((Class) ServiceConfiguration.class); } /** @@ -215,14 +265,12 @@ public CacheConfigurationBuilder clearAllServiceConfig() { * @param clazz the service configuration class * @param the type of the service configuration * @return a matching service configuration, or {@code null} if none can be found + * @deprecated in favor of {@link #getService(Class)} */ - public > T getExistingServiceConfiguration(Class clazz) { - for (ServiceConfiguration serviceConfiguration : serviceConfigurations) { - if (clazz.equals(serviceConfiguration.getClass())) { - return clazz.cast(serviceConfiguration); - } - } - return null; + @Deprecated + public > T getExistingServiceConfiguration(Class clazz) { + Iterator iterator = getServices(clazz).iterator(); + return iterator.hasNext() ? iterator.next() : null; } /** @@ -231,39 +279,28 @@ public > T getExistingServiceConfiguration(Cla * @param clazz the service configuration class * @param the type of the service configuration * @return a list with service configurations + * @deprecated in favor of {@link #getServices(Class)} */ - public > List getExistingServiceConfigurations(Class clazz) { - ArrayList results = new ArrayList<>(); - for (ServiceConfiguration serviceConfiguration : serviceConfigurations) { - if (clazz.equals(serviceConfiguration.getClass())) { - results.add(clazz.cast(serviceConfiguration)); - } - } - return results; + @Deprecated + public > List getExistingServiceConfigurations(Class clazz) { + return new ArrayList<>(getServices(clazz)); } - /** - * Adds a {@link ClassLoader} to the returned builder. - *

- * The {@link ClassLoader} will be used for resolving all non Ehcache types. - * - * @param classLoader the class loader to use - * @return a new builder with the added class loader - */ + @Override public CacheConfigurationBuilder withClassLoader(ClassLoader classLoader) { CacheConfigurationBuilder otherBuilder = new CacheConfigurationBuilder<>(this); - otherBuilder.classLoader = classLoader; + otherBuilder.classLoader = requireNonNull(classLoader); return otherBuilder; } - /** - * Adds the {@link ResourcePools} to the returned builder. - *

- * {@link ResourcePools} is what determines the tiering of a cache. - * - * @param resourcePools the resource pools to use - * @return a new builder with the added resource pools - */ + @Override + public CacheConfigurationBuilder withDefaultClassLoader() { + CacheConfigurationBuilder otherBuilder = new CacheConfigurationBuilder<>(this); + otherBuilder.classLoader = null; + return otherBuilder; + } + + @Override public CacheConfigurationBuilder withResourcePools(ResourcePools resourcePools) { if (resourcePools == null) { throw new NullPointerException("Null resource pools"); @@ -273,16 +310,11 @@ public CacheConfigurationBuilder withResourcePools(ResourcePools resourceP return otherBuilder; } - /** - * Convenience method to add a {@link ResourcePools} through a {@link ResourcePoolsBuilder} to the returned builder. - * - * @param resourcePoolsBuilder the builder providing the resource pool - * @return a new builder with the added resource pools - * - * @see #withResourcePools(ResourcePools) - */ - public CacheConfigurationBuilder withResourcePools(ResourcePoolsBuilder resourcePoolsBuilder) { - return withResourcePools(requireNonNull(resourcePoolsBuilder, "Null resource pools builder").build()); + @Override + public CacheConfigurationBuilder updateResourcePools(UnaryOperator update) { + CacheConfigurationBuilder otherBuilder = new CacheConfigurationBuilder<>(this); + otherBuilder.resourcePools = update.apply(resourcePools); + return otherBuilder; } /** @@ -300,14 +332,7 @@ public CacheConfigurationBuilder withExpiry(org.ehcache.expiry.Expiry - * {@code ExpiryPolicy} is what controls data freshness in a cache. - * - * @param expiry the expiry to use - * @return a new builder with the added expiry - */ + @Override public CacheConfigurationBuilder withExpiry(ExpiryPolicy expiry) { if (expiry == null) { throw new NullPointerException("Null expiry"); @@ -326,171 +351,104 @@ public boolean hasConfiguredExpiry() { return expiry != null; } - /** - * Adds a {@link CacheLoaderWriter} to the configured builder. - *

- * Configuration of a {@link CacheLoaderWriter} is what enables cache-through patterns. - * - * @param loaderWriter the loaderwriter to use - * @return a new builder with the added loaderwriter configuration - */ + @Override public CacheConfigurationBuilder withLoaderWriter(CacheLoaderWriter loaderWriter) { - return addOrReplaceConfiguration(new DefaultCacheLoaderWriterConfiguration(requireNonNull(loaderWriter, "Null loaderWriter"))); + return withService(new DefaultCacheLoaderWriterConfiguration(requireNonNull(loaderWriter, "Null loaderWriter"))); } - /** - * Adds a {@link CacheLoaderWriter} configured through a class and optional constructor arguments to the configured - * builder. - *

- * Configuration of a {@link CacheLoaderWriter} is what enables cache-through patterns. - * - * @param loaderWriterClass the loaderwrite class - * @param arguments optional constructor arguments - * @return a new builder with the added loaderwriter configuration - */ + @Override public CacheConfigurationBuilder withLoaderWriter(Class> loaderWriterClass, Object... arguments) { - return addOrReplaceConfiguration(new DefaultCacheLoaderWriterConfiguration(requireNonNull(loaderWriterClass, "Null loaderWriterClass"), arguments)); + return withService(new DefaultCacheLoaderWriterConfiguration(requireNonNull(loaderWriterClass, "Null loaderWriterClass"), arguments)); } - /** - * Adds a {@link ResilienceStrategy} to the configured builder. - * - * @param resilienceStrategy the resilience strategy to use - * @return a new builder with the added resilience strategy configuration - */ + @Override + public CacheConfigurationBuilder withoutLoaderWriter() { + return withoutServices(DefaultCacheLoaderWriterConfiguration.class); + } + + @Override public CacheConfigurationBuilder withResilienceStrategy(ResilienceStrategy resilienceStrategy) { - return addOrReplaceConfiguration(new DefaultResilienceStrategyConfiguration(requireNonNull(resilienceStrategy, "Null resilienceStrategy"))); + return withService(new DefaultResilienceStrategyConfiguration(requireNonNull(resilienceStrategy, "Null resilienceStrategy"))); } - /** - * Adds a {@link ResilienceStrategy} configured through a class and optional constructor arguments to the configured - * builder. - * - * @param resilienceStrategyClass the resilience strategy class - * @param arguments optional constructor arguments - * @return a new builder with the added resilience strategy configuration - */ - @SuppressWarnings("rawtypes") + @Override @SuppressWarnings("rawtypes") public CacheConfigurationBuilder withResilienceStrategy(Class resilienceStrategyClass, Object... arguments) { - return addOrReplaceConfiguration(new DefaultResilienceStrategyConfiguration(requireNonNull(resilienceStrategyClass, "Null resilienceStrategyClass"), arguments)); + return withService(new DefaultResilienceStrategyConfiguration(requireNonNull(resilienceStrategyClass, "Null resilienceStrategyClass"), arguments)); } - /** - * Adds by-value semantic using the cache key serializer for the key on heap. - *

- * {@link Copier}s are what enable control of by-reference / by-value semantics for on-heap tier. - * - * @return a new builder with the added key copier - */ + @Override + public CacheConfigurationBuilder withDefaultResilienceStrategy() { + return withoutServices(DefaultResilienceStrategyConfiguration.class); + } + + @Override public CacheConfigurationBuilder withKeySerializingCopier() { return withKeyCopier(SerializingCopier.asCopierClass()); } - /** - * Adds by-value semantic using the cache value serializer for the value on heap. - *

- * {@link Copier}s are what enable control of by-reference / by-value semantics for on-heap tier. - * - * @return a new builder with the added value copier - */ + @Override public CacheConfigurationBuilder withValueSerializingCopier() { return withValueCopier(SerializingCopier.asCopierClass()); } - /** - * Adds by-value semantic using the provided {@link Copier} for the key on heap. - *

- * {@link Copier}s are what enable control of by-reference / by-value semantics for on-heap tier. - * - * @param keyCopier the key copier to use - * @return a new builder with the added key copier - */ + @Override public CacheConfigurationBuilder withKeyCopier(Copier keyCopier) { - return withCopier(new DefaultCopierConfiguration<>(requireNonNull(keyCopier, "Null key copier"), DefaultCopierConfiguration.Type.KEY)); + return withService(new DefaultCopierConfiguration<>(requireNonNull(keyCopier, "Null key copier"), DefaultCopierConfiguration.Type.KEY)); } - /** - * Adds by-value semantic using the provided {@link Copier} class for the key on heap. - *

- * {@link Copier}s are what enable control of by-reference / by-value semantics for on-heap tier. - * - * @param keyCopierClass the key copier class to use - * @return a new builder with the added key copier - */ + @Override public CacheConfigurationBuilder withKeyCopier(Class> keyCopierClass) { - return withCopier(new DefaultCopierConfiguration<>(requireNonNull(keyCopierClass, "Null key copier class"), DefaultCopierConfiguration.Type.KEY)); + return withService(new DefaultCopierConfiguration<>(requireNonNull(keyCopierClass, "Null key copier class"), DefaultCopierConfiguration.Type.KEY)); } - /** - * Adds by-value semantic using the provided {@link Copier} for the value on heap. - *

- * {@link Copier}s are what enable control of by-reference / by-value semantics for on-heap tier. - * - * @param valueCopier the value copier to use - * @return a new builder with the added value copier - */ + @Override + public CacheConfigurationBuilder withoutKeyCopier() { + return withoutServices(DefaultCopierConfiguration.class, c -> DefaultCopierConfiguration.Type.KEY.equals(c.getType())); + } + + @Override public CacheConfigurationBuilder withValueCopier(Copier valueCopier) { - return withCopier(new DefaultCopierConfiguration<>(requireNonNull(valueCopier, "Null value copier"), DefaultCopierConfiguration.Type.VALUE)); + return withService(new DefaultCopierConfiguration<>(requireNonNull(valueCopier, "Null value copier"), DefaultCopierConfiguration.Type.VALUE)); } - /** - * Adds by-value semantic using the provided {@link Copier} class for the value on heap. - *

- * {@link Copier}s are what enable control of by-reference / by-value semantics for on-heap tier. - * - * @param valueCopierClass the value copier class to use - * @return a new builder with the added value copier - */ + @Override public CacheConfigurationBuilder withValueCopier(Class> valueCopierClass) { - return withCopier(new DefaultCopierConfiguration<>(requireNonNull(valueCopierClass, "Null value copier class"), DefaultCopierConfiguration.Type.VALUE)); + return withService(new DefaultCopierConfiguration<>(requireNonNull(valueCopierClass, "Null value copier class"), DefaultCopierConfiguration.Type.VALUE)); } - /** - * Adds a {@link Serializer} for cache keys to the configured builder. - *

- * {@link Serializer}s are what enables cache storage beyond the heap tier. - * - * @param keySerializer the key serializer to use - * @return a new builder with the added key serializer - */ + @Override + public CacheConfigurationBuilder withoutValueCopier() { + return withoutServices(DefaultCopierConfiguration.class, c -> DefaultCopierConfiguration.Type.VALUE.equals(c.getType())); + } + + @Override public CacheConfigurationBuilder withKeySerializer(Serializer keySerializer) { - return withSerializer(new DefaultSerializerConfiguration<>(requireNonNull(keySerializer, "Null key serializer"), DefaultSerializerConfiguration.Type.KEY)); + return withService(new DefaultSerializerConfiguration<>(requireNonNull(keySerializer, "Null key serializer"), DefaultSerializerConfiguration.Type.KEY)); } - /** - * Adds a {@link Serializer} class for cache keys to the configured builder. - *

- * {@link Serializer}s are what enables cache storage beyond the heap tier. - * - * @param keySerializerClass the key serializer to use - * @return a new builder with the added key serializer - */ + @Override public CacheConfigurationBuilder withKeySerializer(Class> keySerializerClass) { - return withSerializer(new DefaultSerializerConfiguration<>(requireNonNull(keySerializerClass, "Null key serializer class"), DefaultSerializerConfiguration.Type.KEY)); + return withService(new DefaultSerializerConfiguration<>(requireNonNull(keySerializerClass, "Null key serializer class"), DefaultSerializerConfiguration.Type.KEY)); } - /** - * Adds a {@link Serializer} for cache values to the configured builder. - *

- * {@link Serializer}s are what enables cache storage beyond the heap tier. - * - * @param valueSerializer the key serializer to use - * @return a new builder with the added value serializer - */ + @Override + public CacheConfigurationBuilder withDefaultKeySerializer() { + return withoutServices(DefaultSerializerConfiguration.class, config -> DefaultSerializerConfiguration.Type.KEY.equals(config.getType())); + } + + @Override public CacheConfigurationBuilder withValueSerializer(Serializer valueSerializer) { - return withSerializer(new DefaultSerializerConfiguration<>(requireNonNull(valueSerializer, "Null value serializer"), DefaultSerializerConfiguration.Type.VALUE)); + return withService(new DefaultSerializerConfiguration<>(requireNonNull(valueSerializer, "Null value serializer"), DefaultSerializerConfiguration.Type.VALUE)); } - /** - * Adds a {@link Serializer} class for cache values to the configured builder. - *

- * {@link Serializer}s are what enables cache storage beyond the heap tier. - * - * @param valueSerializerClass the key serializer to use - * @return a new builder with the added value serializer - */ + @Override public CacheConfigurationBuilder withValueSerializer(Class> valueSerializerClass) { - return withSerializer(new DefaultSerializerConfiguration<>(requireNonNull(valueSerializerClass, "Null value serializer class"), DefaultSerializerConfiguration.Type.VALUE)); + return withService(new DefaultSerializerConfiguration<>(requireNonNull(valueSerializerClass, "Null value serializer class"), DefaultSerializerConfiguration.Type.VALUE)); + } + + @Override + public CacheConfigurationBuilder withDefaultValueSerializer() { + return withoutServices(DefaultSerializerConfiguration.class, config -> DefaultSerializerConfiguration.Type.VALUE.equals(config.getType())); } /** @@ -499,9 +457,22 @@ public CacheConfigurationBuilder withValueSerializer(Class withDispatcherConcurrency(int dispatcherConcurrency) { - return addOrReplaceConfiguration(new DefaultEventSourceConfiguration(dispatcherConcurrency)); + return withService(new DefaultEventSourceConfiguration(dispatcherConcurrency)); + } + + /** + * Restores the default dispatcher concurrency. + * + * @return a new builder with the default dispatcher concurrency + * + * @see #withDispatcherConcurrency(int) + */ + public CacheConfigurationBuilder withDefaultDispatcherConcurrency() { + return withoutServices(DefaultEventSourceConfiguration.class); } /** @@ -510,9 +481,22 @@ public CacheConfigurationBuilder withDispatcherConcurrency(int dispatcherC * * @param threadPoolAlias the thread pool alias to use * @return a new builder with the added configuration + * + * @see #withDefaultEventListenersThreadPool() */ public CacheConfigurationBuilder withEventListenersThreadPool(String threadPoolAlias) { - return addOrReplaceConfiguration(new DefaultCacheEventDispatcherConfiguration(threadPoolAlias)); + return withService(new DefaultCacheEventDispatcherConfiguration(threadPoolAlias)); + } + + /** + * Restores the default event listener thread pool settings. + * + * @return a new builder with the default event listener thread pool settings + * + * @see #withEventListenersThreadPool(String) + */ + public CacheConfigurationBuilder withDefaultEventListenersThreadPool() { + return withoutServices(DefaultCacheEventDispatcherConfiguration.class); } /** @@ -522,9 +506,25 @@ public CacheConfigurationBuilder withEventListenersThreadPool(String threa * @param threadPoolAlias the thread pool alias * @param concurrency the write concurrency * @return a new builder with the added configuration + * + * @see #withDefaultDiskStoreThreadPool() */ public CacheConfigurationBuilder withDiskStoreThreadPool(String threadPoolAlias, int concurrency) { - return addOrReplaceConfiguration(new OffHeapDiskStoreConfiguration(threadPoolAlias, concurrency)); + return installOrUpdate( + () -> new OffHeapDiskStoreConfiguration(threadPoolAlias, concurrency), + existing -> new OffHeapDiskStoreConfiguration(threadPoolAlias, concurrency, existing.getDiskSegments()) + ); + } + + /** + * Restores the default disk store thread pool settings. + * + * @return a new builder with the default disk store thread pool settings + * + * @see #withDiskStoreThreadPool(String, int) + */ + public CacheConfigurationBuilder withDefaultDiskStoreThreadPool() { + return withoutServices(OffHeapDiskStoreConfiguration.class); } /** @@ -535,11 +535,15 @@ public CacheConfigurationBuilder withDiskStoreThreadPool(String threadPool * * @param size the maximum graph size * @return a new builder with the added / updated configuration + * + * @see #withSizeOfMaxObjectSize(long, MemoryUnit) + * @see #withDefaultSizeOfSettings() */ public CacheConfigurationBuilder withSizeOfMaxObjectGraph(long size) { - return mapServiceConfiguration(DefaultSizeOfEngineConfiguration.class, existing -> ofNullable(existing) - .map(e -> new DefaultSizeOfEngineConfiguration(e.getMaxObjectSize(), e.getUnit(), size)) - .orElse(new DefaultSizeOfEngineConfiguration(DEFAULT_MAX_OBJECT_SIZE, DEFAULT_UNIT, size))); + return installOrUpdate( + () -> new DefaultSizeOfEngineConfiguration(DEFAULT_MAX_OBJECT_SIZE, DEFAULT_UNIT, size), + existing -> new DefaultSizeOfEngineConfiguration(existing.getMaxObjectSize(), existing.getUnit(), size) + ); } /** @@ -551,71 +555,46 @@ public CacheConfigurationBuilder withSizeOfMaxObjectGraph(long size) { * @param size the maximum mapping size * @param unit the memory unit * @return a new builder with the added / updated configuration + * + * @see #withSizeOfMaxObjectGraph(long) + * @see #withDefaultSizeOfSettings() */ public CacheConfigurationBuilder withSizeOfMaxObjectSize(long size, MemoryUnit unit) { - return mapServiceConfiguration(DefaultSizeOfEngineConfiguration.class, existing -> ofNullable(existing) - .map(e -> new DefaultSizeOfEngineConfiguration(size, unit, e.getMaxObjectGraphSize())) - .orElse(new DefaultSizeOfEngineConfiguration(size, unit, DEFAULT_OBJECT_GRAPH_SIZE))); + return installOrUpdate( + () -> new DefaultSizeOfEngineConfiguration(size, unit, DEFAULT_OBJECT_GRAPH_SIZE), + existing -> new DefaultSizeOfEngineConfiguration(size, unit, existing.getMaxObjectGraphSize()) + ); + } + + /** + * Restores the default size-of settings. + * + * @return a new builder with the default size-of settings + * + * @see #withSizeOfMaxObjectGraph(long) + * @see #withSizeOfMaxObjectSize(long, MemoryUnit) + */ + public CacheConfigurationBuilder withDefaultSizeOfSettings() { + return withoutServices(DefaultSizeOfEngineConfiguration.class); } @Override public CacheConfiguration build() { return new BaseCacheConfiguration<>(keyType, valueType, evictionAdvisor, classLoader, expiry, resourcePools, - serviceConfigurations.toArray(new ServiceConfiguration[serviceConfigurations.size()])); + serviceConfigurations.toArray(new ServiceConfiguration[serviceConfigurations.size()])); } - private CacheConfigurationBuilder withSerializer(DefaultSerializerConfiguration configuration) { - CacheConfigurationBuilder otherBuilder = new CacheConfigurationBuilder<>(this); - otherBuilder.removeExistingSerializerConfigFor(configuration.getType()); - otherBuilder.serviceConfigurations.add(configuration); - return otherBuilder; - } + private > CacheConfigurationBuilder installOrUpdate(Supplier supplier, UnaryOperator update) { + C newConfig = supplier.get(); - private void removeExistingSerializerConfigFor(DefaultSerializerConfiguration.Type type) { - @SuppressWarnings({"unchecked","rawtypes"}) - List> existingServiceConfigurations = - (List) getExistingServiceConfigurations(DefaultSerializerConfiguration.class); - for (DefaultSerializerConfiguration configuration : existingServiceConfigurations) { - if (configuration.getType().equals(type)) { - serviceConfigurations.remove(configuration); - } + @SuppressWarnings("unchecked") + Class configType = (Class) newConfig.getClass(); + if (getServices(configType).isEmpty()) { + return withService(newConfig); + } else { + return updateServices(configType, update); } } - private CacheConfigurationBuilder withCopier(DefaultCopierConfiguration configuration) { - CacheConfigurationBuilder otherBuilder = new CacheConfigurationBuilder<>(this); - otherBuilder.removeExistingCopierConfigFor(configuration.getType()); - otherBuilder.serviceConfigurations.add(configuration); - return otherBuilder; - } - - private void removeExistingCopierConfigFor(DefaultCopierConfiguration.Type type) { - @SuppressWarnings({"unchecked","rawtypes"}) - List> existingServiceConfigurations = (List) getExistingServiceConfigurations(DefaultCopierConfiguration.class); - for (DefaultCopierConfiguration configuration : existingServiceConfigurations) { - if (configuration.getType().equals(type)) { - serviceConfigurations.remove(configuration); - } - } - } - - @SuppressWarnings("unchecked") - private > CacheConfigurationBuilder addOrReplaceConfiguration(T configuration) { - return addOrReplaceConfiguration((Class) configuration.getClass(), configuration); - } - - private > CacheConfigurationBuilder addOrReplaceConfiguration(Class configurationType, T configuration) { - return mapServiceConfiguration(configurationType, e -> configuration); - } - - private > CacheConfigurationBuilder mapServiceConfiguration(Class configurationType, UnaryOperator mapper) { - CacheConfigurationBuilder otherBuilder = new CacheConfigurationBuilder<>(this); - T existingServiceConfiguration = otherBuilder.getExistingServiceConfiguration(configurationType); - if (existingServiceConfiguration != null) { - otherBuilder.serviceConfigurations.remove(existingServiceConfiguration); - } - otherBuilder.serviceConfigurations.add(mapper.apply(existingServiceConfiguration)); - return otherBuilder; - } } diff --git a/impl/src/main/java/org/ehcache/config/builders/CacheEventListenerConfigurationBuilder.java b/ehcache-impl/src/main/java/org/ehcache/config/builders/CacheEventListenerConfigurationBuilder.java similarity index 99% rename from impl/src/main/java/org/ehcache/config/builders/CacheEventListenerConfigurationBuilder.java rename to ehcache-impl/src/main/java/org/ehcache/config/builders/CacheEventListenerConfigurationBuilder.java index 7db9fe8c33..6a77388099 100644 --- a/impl/src/main/java/org/ehcache/config/builders/CacheEventListenerConfigurationBuilder.java +++ b/ehcache-impl/src/main/java/org/ehcache/config/builders/CacheEventListenerConfigurationBuilder.java @@ -35,7 +35,7 @@ * instance without modifying the one on which the method was called. * This enables the sharing of builder instances without any risk of seeing them modified by code elsewhere. */ -public class CacheEventListenerConfigurationBuilder implements Builder { +public class CacheEventListenerConfigurationBuilder implements Builder> { private EventOrdering eventOrdering; private EventFiring eventFiringMode; private Object[] listenerArguments = new Object[0]; diff --git a/impl/src/main/java/org/ehcache/config/builders/CacheManagerBuilder.java b/ehcache-impl/src/main/java/org/ehcache/config/builders/CacheManagerBuilder.java similarity index 73% rename from impl/src/main/java/org/ehcache/config/builders/CacheManagerBuilder.java rename to ehcache-impl/src/main/java/org/ehcache/config/builders/CacheManagerBuilder.java index 376678c73f..72af567224 100644 --- a/impl/src/main/java/org/ehcache/config/builders/CacheManagerBuilder.java +++ b/ehcache-impl/src/main/java/org/ehcache/config/builders/CacheManagerBuilder.java @@ -21,6 +21,7 @@ import org.ehcache.config.Builder; import org.ehcache.config.CacheConfiguration; import org.ehcache.config.Configuration; +import org.ehcache.config.FluentConfigurationBuilder; import org.ehcache.config.units.MemoryUnit; import org.ehcache.core.EhcacheManager; import org.ehcache.core.spi.store.heap.SizeOfEngine; @@ -40,6 +41,8 @@ import java.util.Collection; import java.util.HashSet; import java.util.Set; +import java.util.function.Supplier; +import java.util.function.UnaryOperator; import static java.util.Collections.emptySet; import static java.util.Collections.unmodifiableSet; @@ -57,7 +60,7 @@ */ public class CacheManagerBuilder implements Builder { - private final ConfigurationBuilder configBuilder; + private final FluentConfigurationBuilder configBuilder; private final Set services; /** @@ -94,7 +97,7 @@ private CacheManagerBuilder(CacheManagerBuilder builder, Set service this.services = unmodifiableSet(services); } - private CacheManagerBuilder(CacheManagerBuilder builder, ConfigurationBuilder configBuilder) { + private CacheManagerBuilder(CacheManagerBuilder builder, FluentConfigurationBuilder configBuilder) { this.configBuilder = configBuilder; this.services = builder.services; } @@ -132,7 +135,7 @@ T cast(EhcacheManager ehcacheManager) { * @see CacheConfigurationBuilder */ public CacheManagerBuilder withCache(String alias, CacheConfiguration configuration) { - return new CacheManagerBuilder<>(this, configBuilder.addCache(alias, configuration)); + return new CacheManagerBuilder<>(this, configBuilder.withCache(alias, configuration)); } /** @@ -203,16 +206,7 @@ public CacheManagerBuilder using(Service service) { * @return a new builder with the added default copier */ public CacheManagerBuilder withCopier(Class clazz, Class> copier) { - DefaultCopyProviderConfiguration service = configBuilder.findServiceByClass(DefaultCopyProviderConfiguration.class); - if (service == null) { - service = new DefaultCopyProviderConfiguration(); - service.addCopierFor(clazz, copier); - return new CacheManagerBuilder<>(this, configBuilder.addService(service)); - } else { - DefaultCopyProviderConfiguration newConfig = new DefaultCopyProviderConfiguration(service); - newConfig.addCopierFor(clazz, copier, true); - return new CacheManagerBuilder<>(this, configBuilder.removeService(service).addService(newConfig)); - } + return ensureThenUpdate(DefaultCopyProviderConfiguration::new, existing -> existing.addCopierFor(clazz, copier, true)); } /** @@ -224,16 +218,7 @@ public CacheManagerBuilder withCopier(Class clazz, Class CacheManagerBuilder withSerializer(Class clazz, Class> serializer) { - DefaultSerializationProviderConfiguration service = configBuilder.findServiceByClass(DefaultSerializationProviderConfiguration.class); - if (service == null) { - service = new DefaultSerializationProviderConfiguration(); - service.addSerializerFor(clazz, serializer); - return new CacheManagerBuilder<>(this, configBuilder.addService(service)); - } else { - DefaultSerializationProviderConfiguration newConfig = new DefaultSerializationProviderConfiguration(service); - newConfig.addSerializerFor(clazz, serializer, true); - return new CacheManagerBuilder<>(this, configBuilder.removeService(service).addService(newConfig)); - } + return ensureThenUpdate(DefaultSerializationProviderConfiguration::new, config -> config.addSerializerFor(clazz, serializer, true)); } /** @@ -244,14 +229,10 @@ public CacheManagerBuilder withSerializer(Class clazz, Class withDefaultSizeOfMaxObjectGraph(long size) { - DefaultSizeOfEngineProviderConfiguration configuration = configBuilder.findServiceByClass(DefaultSizeOfEngineProviderConfiguration.class); - if (configuration == null) { - return new CacheManagerBuilder<>(this, configBuilder.addService(new DefaultSizeOfEngineProviderConfiguration(DEFAULT_MAX_OBJECT_SIZE, DEFAULT_UNIT, size))); - } else { - ConfigurationBuilder builder = configBuilder.removeService(configuration); - return new CacheManagerBuilder<>(this, builder.addService(new DefaultSizeOfEngineProviderConfiguration(configuration - .getMaxObjectSize(), configuration.getUnit(), size))); - } + return ensureThenUpdate( + () -> new DefaultSizeOfEngineProviderConfiguration(DEFAULT_MAX_OBJECT_SIZE, DEFAULT_UNIT, DEFAULT_OBJECT_GRAPH_SIZE), + existing -> new DefaultSizeOfEngineProviderConfiguration(existing.getMaxObjectSize(), existing.getUnit(), size) + ); } /** @@ -263,14 +244,10 @@ public CacheManagerBuilder withDefaultSizeOfMaxObjectGraph(long size) { * @return a new builder with the added configuration */ public CacheManagerBuilder withDefaultSizeOfMaxObjectSize(long size, MemoryUnit unit) { - DefaultSizeOfEngineProviderConfiguration configuration = configBuilder.findServiceByClass(DefaultSizeOfEngineProviderConfiguration.class); - if (configuration == null) { - return new CacheManagerBuilder<>(this, configBuilder.addService(new DefaultSizeOfEngineProviderConfiguration(size, unit, DEFAULT_OBJECT_GRAPH_SIZE))); - } else { - ConfigurationBuilder builder = configBuilder.removeService(configuration); - return new CacheManagerBuilder<>(this, builder.addService(new DefaultSizeOfEngineProviderConfiguration(size, unit, configuration - .getMaxObjectGraphSize()))); - } + return ensureThenUpdate( + () -> new DefaultSizeOfEngineProviderConfiguration(DEFAULT_MAX_OBJECT_SIZE, DEFAULT_UNIT, DEFAULT_OBJECT_GRAPH_SIZE), + existing -> new DefaultSizeOfEngineProviderConfiguration(size, unit, existing.getMaxObjectGraphSize()) + ); } /** @@ -282,13 +259,7 @@ public CacheManagerBuilder withDefaultSizeOfMaxObjectSize(long size, MemoryUn * @see PooledExecutionServiceConfigurationBuilder */ public CacheManagerBuilder withDefaultWriteBehindThreadPool(String threadPoolAlias) { - WriteBehindProviderConfiguration config = configBuilder.findServiceByClass(WriteBehindProviderConfiguration.class); - if (config == null) { - return new CacheManagerBuilder<>(this, configBuilder.addService(new WriteBehindProviderConfiguration(threadPoolAlias))); - } else { - ConfigurationBuilder builder = configBuilder.removeService(config); - return new CacheManagerBuilder<>(this, builder.addService(new WriteBehindProviderConfiguration(threadPoolAlias))); - } + return using(new WriteBehindProviderConfiguration(threadPoolAlias)); } /** @@ -301,13 +272,7 @@ public CacheManagerBuilder withDefaultWriteBehindThreadPool(String threadPool * @see PooledExecutionServiceConfigurationBuilder */ public CacheManagerBuilder withDefaultDiskStoreThreadPool(String threadPoolAlias) { - OffHeapDiskStoreProviderConfiguration config = configBuilder.findServiceByClass(OffHeapDiskStoreProviderConfiguration.class); - if (config == null) { - return new CacheManagerBuilder<>(this, configBuilder.addService(new OffHeapDiskStoreProviderConfiguration(threadPoolAlias))); - } else { - ConfigurationBuilder builder = configBuilder.removeService(config); - return new CacheManagerBuilder<>(this, builder.addService(new OffHeapDiskStoreProviderConfiguration(threadPoolAlias))); - } + return using(new OffHeapDiskStoreProviderConfiguration(threadPoolAlias)); } /** @@ -320,25 +285,21 @@ public CacheManagerBuilder withDefaultDiskStoreThreadPool(String threadPoolAl * @see PooledExecutionServiceConfigurationBuilder */ public CacheManagerBuilder withDefaultEventListenersThreadPool(String threadPoolAlias) { - CacheEventDispatcherFactoryConfiguration config = configBuilder.findServiceByClass(CacheEventDispatcherFactoryConfiguration.class); - if (config == null) { - return new CacheManagerBuilder<>(this, configBuilder.addService(new CacheEventDispatcherFactoryConfiguration(threadPoolAlias))); - } else { - ConfigurationBuilder builder = configBuilder.removeService(config); - return new CacheManagerBuilder<>(this, builder.addService(new CacheEventDispatcherFactoryConfiguration(threadPoolAlias))); - } + return using(new CacheEventDispatcherFactoryConfiguration(threadPoolAlias)); } /** * Adds a {@link ServiceCreationConfiguration} to the returned builder. *

- * These configurations are used to load services and configure them at creation time. + * These configurations are used to load services and configure them at creation time. This method will remove any + * existing configuration incompatible with the given configuration, before adding the new configuration. * * @param serviceConfiguration the {@code ServiceCreationConfiguration} to use * @return a new builder with the added configuration + * @see FluentConfigurationBuilder#withService(ServiceCreationConfiguration) */ - public CacheManagerBuilder using(ServiceCreationConfiguration serviceConfiguration) { - return new CacheManagerBuilder<>(this, configBuilder.addService(serviceConfiguration)); + public CacheManagerBuilder using(ServiceCreationConfiguration serviceConfiguration) { + return new CacheManagerBuilder<>(this, configBuilder.withService(serviceConfiguration)); } /** @@ -348,11 +309,12 @@ public CacheManagerBuilder using(ServiceCreationConfiguration serviceConfi * * @param overwriteServiceConfiguration the new {@code ServiceCreationConfiguration} to use * @return a new builder with the replaced configuration + * + * @deprecated in favor of {@link #using(ServiceCreationConfiguration)} whose refined contract matches this one */ - public CacheManagerBuilder replacing(ServiceCreationConfiguration overwriteServiceConfiguration) { - ServiceCreationConfiguration existingConfiguration = configBuilder.findServiceByClass(overwriteServiceConfiguration.getClass()); - return new CacheManagerBuilder<>(this, configBuilder.removeService(existingConfiguration) - .addService(overwriteServiceConfiguration)); + @Deprecated + public CacheManagerBuilder replacing(ServiceCreationConfiguration overwriteServiceConfiguration) { + return using(overwriteServiceConfiguration); } /** @@ -365,6 +327,19 @@ public CacheManagerBuilder withClassLoader(ClassLoader classLoader) { return new CacheManagerBuilder<>(this, configBuilder.withClassLoader(classLoader)); } + private > CacheManagerBuilder ensureThenUpdate(Supplier supplier, UnaryOperator update) { + C emptyConfig = supplier.get(); + @SuppressWarnings("unchecked") + Class configType = (Class) emptyConfig.getClass(); + + FluentConfigurationBuilder fluentBuilder = configBuilder; + if (configBuilder.getService(configType) == null) { + fluentBuilder = fluentBuilder.withService(emptyConfig); + } + + return new CacheManagerBuilder<>(this, fluentBuilder.updateServices(configType, update)); + } + /** * Creates a new {@code CacheManagerBuilder} * diff --git a/impl/src/main/java/org/ehcache/config/builders/CacheManagerConfiguration.java b/ehcache-impl/src/main/java/org/ehcache/config/builders/CacheManagerConfiguration.java similarity index 100% rename from impl/src/main/java/org/ehcache/config/builders/CacheManagerConfiguration.java rename to ehcache-impl/src/main/java/org/ehcache/config/builders/CacheManagerConfiguration.java diff --git a/ehcache-impl/src/main/java/org/ehcache/config/builders/ConfigurationBuilder.java b/ehcache-impl/src/main/java/org/ehcache/config/builders/ConfigurationBuilder.java new file mode 100644 index 0000000000..bc10763298 --- /dev/null +++ b/ehcache-impl/src/main/java/org/ehcache/config/builders/ConfigurationBuilder.java @@ -0,0 +1,169 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.config.builders; + +import org.ehcache.config.CacheConfiguration; +import org.ehcache.config.Configuration; +import org.ehcache.core.config.CoreConfigurationBuilder; +import org.ehcache.spi.service.ServiceCreationConfiguration; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.function.Predicate; + +/** + * The {@code ConfigurationBuilder} enables building {@link Configuration}s using a fluent style. + * + * @author Alex Snaps + */ +public final class ConfigurationBuilder extends CoreConfigurationBuilder { + + /** + * Create a new 'empty' configuration builder. + * + * @return a new empty configuration builder + */ + public static ConfigurationBuilder newConfigurationBuilder() { + return new ConfigurationBuilder(); + } + + /** + * Create a configuration builder seeded from the given configuration. + *

+ * Calling {@link #build()} on the returned builder will produce a functionally equivalent configuration to + * {@code seed}. + * + * @param seed configuration to duplicate + * @return a new configuration builder + */ + public static ConfigurationBuilder newConfigurationBuilder(Configuration seed) { + return new ConfigurationBuilder(new ConfigurationBuilder(new ConfigurationBuilder(new ConfigurationBuilder(), + seed.getCacheConfigurations()), seed.getServiceCreationConfigurations()), seed.getClassLoader()); + } + + protected ConfigurationBuilder() { + super(); + } + + protected ConfigurationBuilder(ConfigurationBuilder builder, Map> caches) { + super(builder, caches); + } + + protected ConfigurationBuilder(ConfigurationBuilder builder, Collection> serviceConfigurations) { + super(builder, serviceConfigurations); + } + + protected ConfigurationBuilder(ConfigurationBuilder builder, ClassLoader classLoader) { + super(builder, classLoader); + } + + /** + * Add a cache configuration with the given alias. + *

+ * If a cache with the given alias already exists then an {@code IllegalArgumentException} will be thrown. + * + * @param alias cache alias to be added + * @param config cache configuration + * @return an updated builder + * @deprecated in favor of {@link #withCache(String, CacheConfiguration)} + */ + @Deprecated + public ConfigurationBuilder addCache(String alias, CacheConfiguration config) throws IllegalArgumentException { + CacheConfiguration existing = getCache(alias); + if (existing == null) { + return withCache(alias, config); + } else { + throw new IllegalArgumentException("Cache '" + alias + "' already exists: " + existing); + } + } + + /** + * Removes the cache with the given alias. + * + * @param alias cache alias to be removed + * @return an updated builder + * @deprecated in favor of {@link #withoutCache(String)} + */ + @Deprecated + public ConfigurationBuilder removeCache(String alias) { + return withoutCache(alias); + } + + /** + * Adds the given service to this configuration. + *

+ * If a a service creation configuration of the same concrete type is already present then an {@code IllegalArgumentException} + * will be thrown. + * + * @param serviceConfiguration service creation configuration + * @return an updated builder + * @deprecated in favor of {@link #withService(ServiceCreationConfiguration)} + */ + @Deprecated + public ConfigurationBuilder addService(ServiceCreationConfiguration serviceConfiguration) { + ServiceCreationConfiguration existing = getService(serviceConfiguration.getClass()); + if (existing == null) { + return withService(serviceConfiguration); + } else { + throw new IllegalArgumentException("There is already an instance of " + serviceConfiguration.getClass() + " registered: " + existing.getClass()); + } + } + + /** + * Removes the given service configuration. + * + * @param serviceConfiguration service creation configuration + * @return an updated builder + * @deprecated in favor of {@link #withoutServices(Class)} or {@link #withoutServices(Class, Predicate)} + */ + @Deprecated + public ConfigurationBuilder removeService(ServiceCreationConfiguration serviceConfiguration) { + @SuppressWarnings("unchecked") + List> newServiceConfigurations = new ArrayList>(getServices((Class) ServiceCreationConfiguration.class)); + newServiceConfigurations.remove(serviceConfiguration); + return new ConfigurationBuilder(this, newServiceConfigurations); + } + + /** + * Returns {@code true} if a cache configuration is associated with the given alias. + * + * @param alias cache configuration alias + * @return {@code true} if the given alias is present + * @deprecated in favor of {@link #getCache(String)} + */ + @Deprecated + public boolean containsCache(String alias) { + return getCache(alias) != null; + } + + @Override + protected ConfigurationBuilder newBuilderWith(Map> caches) { + return new ConfigurationBuilder(this, caches); + } + + @Override + protected ConfigurationBuilder newBuilderWith(Collection> serviceConfigurations) { + return new ConfigurationBuilder(this, serviceConfigurations); + } + + @Override + protected ConfigurationBuilder newBuilderWith(ClassLoader classLoader) { + return new ConfigurationBuilder(this, classLoader); + } +} diff --git a/ehcache-impl/src/main/java/org/ehcache/config/builders/ExpiryPolicyBuilder.java b/ehcache-impl/src/main/java/org/ehcache/config/builders/ExpiryPolicyBuilder.java new file mode 100644 index 0000000000..9d1a4b7006 --- /dev/null +++ b/ehcache-impl/src/main/java/org/ehcache/config/builders/ExpiryPolicyBuilder.java @@ -0,0 +1,285 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.config.builders; + +import org.ehcache.config.Builder; +import org.ehcache.expiry.ExpiryPolicy; + +import java.time.Duration; +import java.util.Objects; +import java.util.function.BiFunction; +import java.util.function.Supplier; + +/** + * Builder and utilities for getting predefined {@link ExpiryPolicy} instances. + */ +public final class ExpiryPolicyBuilder implements Builder>{ + + /** + * Get an {@link ExpiryPolicy} instance for a non expiring (ie. "eternal") cache. + * + * @return the no expiry instance + */ + public static ExpiryPolicy noExpiration() { + return ExpiryPolicy.NO_EXPIRY; + } + + /** + * Get a time-to-live (TTL) {@link ExpiryPolicy} instance for the given {@link Duration}. + * + * @param timeToLive the TTL duration + * @return a TTL expiry + */ + public static ExpiryPolicy timeToLiveExpiration(Duration timeToLive) { + Objects.requireNonNull(timeToLive, "TTL duration cannot be null"); + if (timeToLive.isNegative()) { + throw new IllegalArgumentException("TTL duration cannot be negative"); + } + return new TimeToLiveExpiryPolicy(timeToLive); + } + + /** + * Get a time-to-idle (TTI) {@link ExpiryPolicy} instance for the given {@link Duration}. + * + * @param timeToIdle the TTI duration + * @return a TTI expiry + */ + public static ExpiryPolicy timeToIdleExpiration(Duration timeToIdle) { + Objects.requireNonNull(timeToIdle, "TTI duration cannot be null"); + if (timeToIdle.isNegative()) { + throw new IllegalArgumentException("TTI duration cannot be negative"); + } + return new TimeToIdleExpiryPolicy(timeToIdle); + } + + @FunctionalInterface + public interface TriFunction { + /** + * Applies this function to the given arguments. + * + * @param t the first function argument + * @param u the second function argument + * @param v the third function argument + * @return the function result + */ + R apply(T t, U u, V v); + } + + /** + * Fluent API for creating an {@link ExpiryPolicy} instance where you can specify constant values for creation, access and update time. + * Unspecified values will be set to {@link ExpiryPolicy#INFINITE INFINITE} for create and {@code null} for access and update, matching + * the {@link #noExpiration()} no expiration} expiry. + * + * @return an {@link ExpiryPolicy} builder + */ + public static ExpiryPolicyBuilder expiry() { + return new ExpiryPolicyBuilder<>(); + } + + private final BiFunction create; + private final BiFunction, Duration> access; + private final TriFunction, ? super V, Duration> update; + + private ExpiryPolicyBuilder() { + this((k, v) -> ExpiryPolicy.INFINITE, (k, v) -> null, (k, oldV, newV) -> null); + } + + private ExpiryPolicyBuilder(BiFunction create, + BiFunction, Duration> access, + TriFunction, ? super V, Duration> update) { + this.create = create; + this.access = access; + this.update = update; + } + + /** + * Set TTL since creation. + *

+ * Note: Calling this method on a builder with an existing TTL since creation will override the previous value or function. + * + * @param create TTL since creation + * @return a new builder with the TTL since creation + */ + public ExpiryPolicyBuilder create(Duration create) { + Objects.requireNonNull(create, "Create duration cannot be null"); + if (create.isNegative()) { + throw new IllegalArgumentException("Create duration must be positive"); + } + return create((a, b) -> create); + } + + /** + * Set a function giving the TTL since creation. + *

+ * Note: Calling this method on a builder with an existing TTL since creation will override the previous value or function. + * + * @param create Function giving the TTL since creation + * @return a new builder with the TTL creation calculation function + */ + public ExpiryPolicyBuilder create(BiFunction create) { + return new ExpiryPolicyBuilder<>(Objects.requireNonNull(create), access, update); + } + + /** + * Set TTI since last access. + *

+ * Note: Calling this method on a builder with an existing TTI since last access will override the previous value or function. + * + * @param access TTI since last access + * @return a new builder with the TTI since last access + */ + public ExpiryPolicyBuilder access(Duration access) { + if (access != null && access.isNegative()) { + throw new IllegalArgumentException("Access duration must be positive"); + } + return access((a, b) -> access); + } + + /** + * Set a function giving the TTI since last access. + *

+ * Note: Calling this method on a builder with an existing TTI since last access will override the previous value or function. + * + * @param access Function giving the TTI since last access + * @return a new builder with the TTI since last access calculation function + */ + public ExpiryPolicyBuilder access(BiFunction, Duration> access) { + return new ExpiryPolicyBuilder<>(create, Objects.requireNonNull(access), update); + } + + /** + * Set TTL since last update. + *

+ * Note: Calling this method on a builder with an existing TTL since last access will override the previous value or function. + * + * @param update TTL since last update + * @return a new builder with the TTL since last update + */ + public ExpiryPolicyBuilder update(Duration update) { + if (update != null && update.isNegative()) { + throw new IllegalArgumentException("Update duration must be positive"); + } + return update((a, b, c) -> update); + } + + /** + * Set a function giving the TTL since last update. + *

+ * Note: Calling this method on a builder with an existing TTL since last update will override the previous value or function. + * + * @param update Function giving the TTL since last update + * @return a new builder with the TTL since last update calculation function + */ + public ExpiryPolicyBuilder update(TriFunction, V2, Duration> update) { + return new ExpiryPolicyBuilder<>(create, access, Objects.requireNonNull(update)); + } + + /** + * Builds an expiry policy instance. + * + * @return an {@link ExpiryPolicy} + */ + public ExpiryPolicy build() { + return new BaseExpiryPolicy<>(create, access, update); + } + + /** + * Simple implementation of the {@link ExpiryPolicy} interface allowing to set constants to each expiry types. + */ + private static class BaseExpiryPolicy implements ExpiryPolicy { + + private final BiFunction create; + private final BiFunction, Duration> access; + private final TriFunction, ? super V, Duration> update; + + BaseExpiryPolicy(BiFunction create, + BiFunction, Duration> access, + TriFunction, ? super V, Duration> update) { + this.create = create; + this.access = access; + this.update = update; + } + + @Override + public Duration getExpiryForCreation(K key, V value) { + return create.apply(key, value); + } + + @Override + public Duration getExpiryForAccess(K key, Supplier value) { + return access.apply(key, value); + } + + @Override + public Duration getExpiryForUpdate(K key, Supplier oldValue, V newValue) { + return update.apply(key, oldValue, newValue); + } + } + + private static final class TimeToLiveExpiryPolicy extends BaseExpiryPolicy { + private final Duration ttl; + + TimeToLiveExpiryPolicy(Duration ttl) { + super( + (a, b) -> ttl, + (a, b) -> null, + (a, b, c) -> ttl); + this.ttl = ttl; + } + + @Override + public int hashCode() { + return ttl.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof TimeToLiveExpiryPolicy && ttl.equals(((TimeToLiveExpiryPolicy) obj).ttl); + } + + @Override + public String toString() { + return "TTL of " + ttl; + } + } + + private static final class TimeToIdleExpiryPolicy extends BaseExpiryPolicy { + private final Duration tti; + + TimeToIdleExpiryPolicy(Duration tti) { + super( + (a, b) -> tti, + (a, b) -> tti, + (a, b, c) -> tti); + this.tti = tti; + } + + @Override + public int hashCode() { + return tti.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof TimeToIdleExpiryPolicy && tti.equals(((TimeToIdleExpiryPolicy) obj).tti); + } + + @Override + public String toString() { + return "TTI of " + tti; + } + } +} diff --git a/impl/src/main/java/org/ehcache/config/builders/PooledExecutionServiceConfigurationBuilder.java b/ehcache-impl/src/main/java/org/ehcache/config/builders/PooledExecutionServiceConfigurationBuilder.java similarity index 85% rename from impl/src/main/java/org/ehcache/config/builders/PooledExecutionServiceConfigurationBuilder.java rename to ehcache-impl/src/main/java/org/ehcache/config/builders/PooledExecutionServiceConfigurationBuilder.java index ef83df8006..3920d29a24 100644 --- a/impl/src/main/java/org/ehcache/config/builders/PooledExecutionServiceConfigurationBuilder.java +++ b/ehcache-impl/src/main/java/org/ehcache/config/builders/PooledExecutionServiceConfigurationBuilder.java @@ -43,6 +43,16 @@ private PooledExecutionServiceConfigurationBuilder(PooledExecutionServiceConfigu this.pools.addAll(other.pools); } + private PooledExecutionServiceConfigurationBuilder(PooledExecutionServiceConfiguration seed) { + seed.getPoolConfigurations().forEach((alias, config) -> { + Pool pool = new Pool(alias, config.minSize(), config.maxSize()); + if (alias.equals(seed.getDefaultPoolAlias())) { + defaultPool = pool; + } + pools.add(pool); + }); + } + /** * Creates a new instance of {@code PooledExecutionServiceConfigurationBuilder} * @@ -52,6 +62,15 @@ public static PooledExecutionServiceConfigurationBuilder newPooledExecutionServi return new PooledExecutionServiceConfigurationBuilder(); } + /** + * Creates a seeded instance of {@code PooledExecutionServiceConfigurationBuilder} + * + * @return the builder + */ + public static PooledExecutionServiceConfigurationBuilder newPooledExecutionServiceConfigurationBuilder(PooledExecutionServiceConfiguration seed) { + return new PooledExecutionServiceConfigurationBuilder(seed); + } + /** * Adds a default pool configuration to the returned builder. * diff --git a/impl/src/main/java/org/ehcache/config/builders/ResourcePoolsBuilder.java b/ehcache-impl/src/main/java/org/ehcache/config/builders/ResourcePoolsBuilder.java similarity index 97% rename from impl/src/main/java/org/ehcache/config/builders/ResourcePoolsBuilder.java rename to ehcache-impl/src/main/java/org/ehcache/config/builders/ResourcePoolsBuilder.java index 89e3a06807..3e3d595c5b 100644 --- a/impl/src/main/java/org/ehcache/config/builders/ResourcePoolsBuilder.java +++ b/ehcache-impl/src/main/java/org/ehcache/config/builders/ResourcePoolsBuilder.java @@ -20,9 +20,9 @@ import org.ehcache.config.ResourcePool; import org.ehcache.config.SizedResourcePool; import org.ehcache.config.units.EntryUnit; -import org.ehcache.core.config.SizedResourcePoolImpl; +import org.ehcache.impl.config.SizedResourcePoolImpl; import org.ehcache.config.ResourcePools; -import org.ehcache.core.config.ResourcePoolsImpl; +import org.ehcache.impl.config.ResourcePoolsImpl; import org.ehcache.config.ResourceType; import org.ehcache.config.ResourceUnit; import org.ehcache.config.units.MemoryUnit; @@ -32,7 +32,7 @@ import static java.util.Collections.unmodifiableMap; import java.util.HashMap; -import static org.ehcache.core.config.ResourcePoolsImpl.validateResourcePools; +import static org.ehcache.impl.config.ResourcePoolsImpl.validateResourcePools; /** * The {@code ResourcePoolsBuilder} enables building {@link ResourcePools} configurations using a fluent style. diff --git a/impl/src/main/java/org/ehcache/config/builders/UserManagedCacheBuilder.java b/ehcache-impl/src/main/java/org/ehcache/config/builders/UserManagedCacheBuilder.java similarity index 96% rename from impl/src/main/java/org/ehcache/config/builders/UserManagedCacheBuilder.java rename to ehcache-impl/src/main/java/org/ehcache/config/builders/UserManagedCacheBuilder.java index 4ca9ca234c..a22f9b78ff 100644 --- a/impl/src/main/java/org/ehcache/config/builders/UserManagedCacheBuilder.java +++ b/ehcache-impl/src/main/java/org/ehcache/config/builders/UserManagedCacheBuilder.java @@ -29,24 +29,22 @@ import org.ehcache.core.Ehcache; import org.ehcache.core.InternalCache; import org.ehcache.core.PersistentUserManagedEhcache; -import org.ehcache.core.config.BaseCacheConfiguration; +import org.ehcache.impl.config.BaseCacheConfiguration; import org.ehcache.core.config.ExpiryUtils; import org.ehcache.core.events.CacheEventDispatcher; import org.ehcache.core.events.CacheEventListenerConfiguration; import org.ehcache.core.events.CacheEventListenerProvider; -import org.ehcache.core.internal.service.ServiceLocator; -import org.ehcache.core.internal.store.StoreConfigurationImpl; -import org.ehcache.core.internal.store.StoreSupport; -import org.ehcache.core.internal.util.ClassLoading; +import org.ehcache.core.spi.ServiceLocator; import org.ehcache.core.resilience.DefaultRecoveryStore; -import org.ehcache.core.internal.resilience.RobustLoaderWriterResilienceStrategy; -import org.ehcache.core.internal.resilience.RobustResilienceStrategy; import org.ehcache.core.spi.LifeCycled; import org.ehcache.core.spi.LifeCycledAdapter; import org.ehcache.core.spi.service.DiskResourceService; import org.ehcache.core.spi.store.Store; import org.ehcache.core.spi.store.heap.SizeOfEngine; import org.ehcache.core.spi.store.heap.SizeOfEngineProvider; +import org.ehcache.core.store.StoreConfigurationImpl; +import org.ehcache.core.store.StoreSupport; +import org.ehcache.core.util.ClassLoading; import org.ehcache.event.CacheEventListener; import org.ehcache.expiry.ExpiryPolicy; import org.ehcache.impl.config.copy.DefaultCopierConfiguration; @@ -56,6 +54,8 @@ import org.ehcache.impl.copy.SerializingCopier; import org.ehcache.impl.events.CacheEventDispatcherImpl; import org.ehcache.impl.internal.events.DisabledCacheEventNotificationService; +import org.ehcache.impl.internal.resilience.RobustLoaderWriterResilienceStrategy; +import org.ehcache.impl.internal.resilience.RobustResilienceStrategy; import org.ehcache.impl.internal.spi.event.DefaultCacheEventListenerProvider; import org.ehcache.spi.copy.Copier; import org.ehcache.spi.loaderwriter.CacheLoaderWriter; @@ -83,7 +83,7 @@ import static org.ehcache.config.ResourceType.Core.DISK; import static org.ehcache.config.ResourceType.Core.OFFHEAP; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; -import static org.ehcache.core.internal.service.ServiceLocator.dependencySet; +import static org.ehcache.core.spi.ServiceLocator.dependencySet; import static org.ehcache.core.spi.service.ServiceUtils.findSingletonAmongst; import static org.ehcache.impl.config.store.heap.DefaultSizeOfEngineConfiguration.DEFAULT_MAX_OBJECT_SIZE; import static org.ehcache.impl.config.store.heap.DefaultSizeOfEngineConfiguration.DEFAULT_OBJECT_GRAPH_SIZE; @@ -112,7 +112,7 @@ public class UserManagedCacheBuilder> imp private final Class valueType; private String id; private final Set services = new HashSet<>(); - private final Set> serviceCreationConfigurations = new HashSet<>(); + private final Set> serviceCreationConfigurations = new HashSet<>(); private ExpiryPolicy expiry = ExpiryPolicy.NO_EXPIRY; private ClassLoader classLoader = ClassLoading.getDefaultClassLoader(); private EvictionAdvisor evictionAdvisor; @@ -126,7 +126,7 @@ public class UserManagedCacheBuilder> imp private Serializer keySerializer; private Serializer valueSerializer; private int dispatcherConcurrency = 4; - private List eventListenerConfigurations = new ArrayList<>(); + private List> eventListenerConfigurations = new ArrayList<>(); private ExecutorService unOrderedExecutor; private ExecutorService orderedExecutor; private long objectGraphSize = DEFAULT_OBJECT_GRAPH_SIZE; @@ -157,6 +157,7 @@ private UserManagedCacheBuilder(UserManagedCacheBuilder toCopy) { this.valueSerializer = toCopy.valueSerializer; this.useKeySerializingCopier = toCopy.useKeySerializingCopier; this.useValueSerializingCopier = toCopy.useValueSerializingCopier; + this.dispatcherConcurrency = toCopy.dispatcherConcurrency; this.eventListenerConfigurations = toCopy.eventListenerConfigurations; this.unOrderedExecutor = toCopy.unOrderedExecutor; this.orderedExecutor = toCopy.orderedExecutor; @@ -171,7 +172,7 @@ T build(ServiceLocator.DependencySet serviceLocatorBuilder) throws IllegalStateE ServiceLocator serviceLocator; try { - for (ServiceCreationConfiguration serviceCreationConfig : serviceCreationConfigurations) { + for (ServiceCreationConfiguration serviceCreationConfig : serviceCreationConfigurations) { serviceLocatorBuilder = serviceLocatorBuilder.with(serviceCreationConfig); } serviceLocatorBuilder = serviceLocatorBuilder.with(Store.Provider.class); @@ -181,7 +182,7 @@ T build(ServiceLocator.DependencySet serviceLocatorBuilder) throws IllegalStateE throw new IllegalStateException("UserManagedCacheBuilder failed to build.", e); } - List> serviceConfigsList = new ArrayList<>(); + List> serviceConfigsList = new ArrayList<>(); if (keyCopier != null) { serviceConfigsList.add(new DefaultCopierConfiguration<>(keyCopier, DefaultCopierConfiguration.Type.KEY)); @@ -237,7 +238,7 @@ public void close() throws Exception { serviceConfigsList.add(new DefaultSerializerConfiguration<>(this.valueSerializer, DefaultSerializerConfiguration.Type.VALUE)); } - ServiceConfiguration[] serviceConfigs = serviceConfigsList.toArray(new ServiceConfiguration[0]); + ServiceConfiguration[] serviceConfigs = serviceConfigsList.toArray(new ServiceConfiguration[0]); final SerializationProvider serialization = serviceLocator.getService(SerializationProvider.class); if (serialization != null) { try { @@ -365,7 +366,7 @@ private void registerListeners(Cache cache, ServiceProvider servi } else { listenerProvider = new DefaultCacheEventListenerProvider(); } - for (CacheEventListenerConfiguration config : eventListenerConfigurations) { + for (CacheEventListenerConfiguration config : eventListenerConfigurations) { final CacheEventListener listener = listenerProvider.createEventListener(id, config); if (listener != null) { cache.getRuntimeConfiguration().registerCacheEventListener(listener, config.orderingMode(), config.firingMode(), config.fireOn()); @@ -559,7 +560,7 @@ public final UserManagedCacheBuilder withEventListeners(CacheEventListe * @see #withEventExecutors(ExecutorService, ExecutorService) * @see #withEventListeners(CacheEventListenerConfigurationBuilder) */ - public final UserManagedCacheBuilder withEventListeners(CacheEventListenerConfiguration... cacheEventListenerConfigurations) { + public final UserManagedCacheBuilder withEventListeners(CacheEventListenerConfiguration ... cacheEventListenerConfigurations) { UserManagedCacheBuilder otherBuilder = new UserManagedCacheBuilder<>(this); otherBuilder.eventListenerConfigurations.addAll(Arrays.asList(cacheEventListenerConfigurations)); return otherBuilder; @@ -813,7 +814,7 @@ public UserManagedCacheBuilder using(Service service) { * * @see #using(Service) */ - public UserManagedCacheBuilder using(ServiceCreationConfiguration serviceConfiguration) { + public UserManagedCacheBuilder using(ServiceCreationConfiguration serviceConfiguration) { UserManagedCacheBuilder otherBuilder = new UserManagedCacheBuilder<>(this); if (serviceConfiguration instanceof DefaultSizeOfEngineProviderConfiguration) { removeAnySizeOfEngine(otherBuilder); diff --git a/impl/src/main/java/org/ehcache/config/builders/UserManagedCacheConfiguration.java b/ehcache-impl/src/main/java/org/ehcache/config/builders/UserManagedCacheConfiguration.java similarity index 100% rename from impl/src/main/java/org/ehcache/config/builders/UserManagedCacheConfiguration.java rename to ehcache-impl/src/main/java/org/ehcache/config/builders/UserManagedCacheConfiguration.java diff --git a/impl/src/main/java/org/ehcache/config/builders/WriteBehindConfigurationBuilder.java b/ehcache-impl/src/main/java/org/ehcache/config/builders/WriteBehindConfigurationBuilder.java similarity index 97% rename from impl/src/main/java/org/ehcache/config/builders/WriteBehindConfigurationBuilder.java rename to ehcache-impl/src/main/java/org/ehcache/config/builders/WriteBehindConfigurationBuilder.java index 26d78846e0..612c2bc9c3 100644 --- a/impl/src/main/java/org/ehcache/config/builders/WriteBehindConfigurationBuilder.java +++ b/ehcache-impl/src/main/java/org/ehcache/config/builders/WriteBehindConfigurationBuilder.java @@ -31,7 +31,7 @@ * instance without modifying the one on which the method was called. * This enables the sharing of builder instances without any risk of seeing them modified by code elsewhere. */ -public abstract class WriteBehindConfigurationBuilder implements Builder { +public abstract class WriteBehindConfigurationBuilder implements Builder> { protected int concurrency = 1; protected int queueSize = Integer.MAX_VALUE; @@ -197,7 +197,7 @@ public BatchedWriteBehindConfigurationBuilder useThreadPool(String alias) { * @return the write behind configuration */ @Override - public WriteBehindConfiguration build() { + public WriteBehindConfiguration build() { return buildWith(new DefaultBatchingConfiguration(maxDelay, maxDelayUnit, batchSize, coalescing)); } } @@ -220,7 +220,7 @@ private UnBatchedWriteBehindConfigurationBuilder(UnBatchedWriteBehindConfigurati * @return the write behind configuration */ @Override - public WriteBehindConfiguration build() { + public WriteBehindConfiguration build() { return buildWith(null); } @@ -261,7 +261,7 @@ public UnBatchedWriteBehindConfigurationBuilder useThreadPool(String alias) { } } - WriteBehindConfiguration buildWith(BatchingConfiguration batching) { + WriteBehindConfiguration buildWith(BatchingConfiguration batching) { return new DefaultWriteBehindConfiguration(threadPoolAlias, concurrency, queueSize, batching); } diff --git a/impl/src/main/java/org/ehcache/config/builders/package-info.java b/ehcache-impl/src/main/java/org/ehcache/config/builders/package-info.java similarity index 100% rename from impl/src/main/java/org/ehcache/config/builders/package-info.java rename to ehcache-impl/src/main/java/org/ehcache/config/builders/package-info.java diff --git a/core/src/main/java/org/ehcache/core/config/AbstractResourcePool.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/AbstractResourcePool.java similarity index 98% rename from core/src/main/java/org/ehcache/core/config/AbstractResourcePool.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/AbstractResourcePool.java index 767590c879..465bcd203e 100644 --- a/core/src/main/java/org/ehcache/core/config/AbstractResourcePool.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/config/AbstractResourcePool.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.ehcache.core.config; +package org.ehcache.impl.config; import org.ehcache.config.ResourcePool; import org.ehcache.config.ResourceType; diff --git a/core/src/main/java/org/ehcache/core/config/BaseCacheConfiguration.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/BaseCacheConfiguration.java similarity index 85% rename from core/src/main/java/org/ehcache/core/config/BaseCacheConfiguration.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/BaseCacheConfiguration.java index c7089c3c2a..2b9949e977 100644 --- a/core/src/main/java/org/ehcache/core/config/BaseCacheConfiguration.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/config/BaseCacheConfiguration.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.ehcache.core.config; +package org.ehcache.impl.config; import java.util.Arrays; import java.util.Collection; @@ -23,9 +23,13 @@ import org.ehcache.config.CacheConfiguration; import org.ehcache.config.EvictionAdvisor; import org.ehcache.config.ResourcePools; +import org.ehcache.config.builders.CacheConfigurationBuilder; +import org.ehcache.core.config.ExpiryUtils; import org.ehcache.expiry.ExpiryPolicy; import org.ehcache.spi.service.ServiceConfiguration; +import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; + /** * Base implementation of {@link CacheConfiguration}. */ @@ -34,7 +38,7 @@ public class BaseCacheConfiguration implements CacheConfiguration { private final Class keyType; private final Class valueType; private final EvictionAdvisor evictionAdvisor; - private final Collection> serviceConfigurations; + private final Collection> serviceConfigurations; private final ClassLoader classLoader; private final ExpiryPolicy expiry; private final ResourcePools resourcePools; @@ -53,7 +57,7 @@ public class BaseCacheConfiguration implements CacheConfiguration { public BaseCacheConfiguration(Class keyType, Class valueType, EvictionAdvisor evictionAdvisor, ClassLoader classLoader, ExpiryPolicy expiry, - ResourcePools resourcePools, ServiceConfiguration... serviceConfigurations) { + ResourcePools resourcePools, ServiceConfiguration... serviceConfigurations) { if (keyType == null) { throw new NullPointerException("keyType cannot be null"); } @@ -80,7 +84,7 @@ public BaseCacheConfiguration(Class keyType, Class valueType, * {@inheritDoc} */ @Override - public Collection> getServiceConfigurations() { + public Collection> getServiceConfigurations() { return serviceConfigurations; } @@ -140,4 +144,9 @@ public ClassLoader getClassLoader() { public ResourcePools getResourcePools() { return resourcePools; } + + @Override + public CacheConfigurationBuilder derive() { + return newCacheConfigurationBuilder(this); + } } diff --git a/core/src/main/java/org/ehcache/core/config/ResourcePoolsImpl.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/ResourcePoolsImpl.java similarity index 98% rename from core/src/main/java/org/ehcache/core/config/ResourcePoolsImpl.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/ResourcePoolsImpl.java index dca19f48ee..8906bcb5b6 100644 --- a/core/src/main/java/org/ehcache/core/config/ResourcePoolsImpl.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/config/ResourcePoolsImpl.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.ehcache.core.config; +package org.ehcache.impl.config; import org.ehcache.config.ResourcePool; import org.ehcache.config.ResourcePools; @@ -24,7 +24,6 @@ import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; diff --git a/core/src/main/java/org/ehcache/core/config/SizedResourcePoolImpl.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/SizedResourcePoolImpl.java similarity index 98% rename from core/src/main/java/org/ehcache/core/config/SizedResourcePoolImpl.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/SizedResourcePoolImpl.java index 60942b37c3..100ba3c23c 100644 --- a/core/src/main/java/org/ehcache/core/config/SizedResourcePoolImpl.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/config/SizedResourcePoolImpl.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.ehcache.core.config; +package org.ehcache.impl.config; import org.ehcache.config.ResourcePool; import org.ehcache.config.ResourceType; diff --git a/impl/src/main/java/org/ehcache/impl/config/copy/DefaultCopierConfiguration.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/copy/DefaultCopierConfiguration.java similarity index 82% rename from impl/src/main/java/org/ehcache/impl/config/copy/DefaultCopierConfiguration.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/copy/DefaultCopierConfiguration.java index 2ff4e161c5..00705e60f3 100644 --- a/impl/src/main/java/org/ehcache/impl/config/copy/DefaultCopierConfiguration.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/config/copy/DefaultCopierConfiguration.java @@ -25,10 +25,13 @@ * {@link ServiceConfiguration} for the default {@link CopyProvider} implementation. *

* Enables configuring a {@link Copier} for the key or value of a given cache. + *

+ * This class overrides the default {@link ServiceConfiguration#compatibleWith(ServiceConfiguration)} implementation + * to allow for independent configuration of the key and value copiers. * * @param the type which the configured copier can handle */ -public class DefaultCopierConfiguration extends ClassInstanceConfiguration> implements ServiceConfiguration { +public class DefaultCopierConfiguration extends ClassInstanceConfiguration> implements ServiceConfiguration { private final Type type; @@ -67,6 +70,15 @@ public Class getServiceType() { return CopyProvider.class; } + @Override + public boolean compatibleWith(ServiceConfiguration other) { + if (other instanceof DefaultCopierConfiguration) { + return !getType().equals(((DefaultCopierConfiguration) other).getType()); + } else { + return ServiceConfiguration.super.compatibleWith(other); + } + } + /** * Returns the {@link Type} of this configuration * diff --git a/impl/src/main/java/org/ehcache/impl/config/copy/DefaultCopyProviderConfiguration.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/copy/DefaultCopyProviderConfiguration.java similarity index 87% rename from impl/src/main/java/org/ehcache/impl/config/copy/DefaultCopyProviderConfiguration.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/copy/DefaultCopyProviderConfiguration.java index 350fcf0665..db7658d6e2 100644 --- a/impl/src/main/java/org/ehcache/impl/config/copy/DefaultCopyProviderConfiguration.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/config/copy/DefaultCopyProviderConfiguration.java @@ -16,7 +16,6 @@ package org.ehcache.impl.config.copy; -import org.ehcache.impl.internal.classes.ClassInstanceConfiguration; import org.ehcache.impl.internal.classes.ClassInstanceProviderConfiguration; import org.ehcache.spi.copy.Copier; import org.ehcache.spi.copy.CopyProvider; @@ -28,7 +27,7 @@ * Enables configuring {@link Class} - {@link Copier} pairs that will be selected unless cache level configurations * are provided. */ -public class DefaultCopyProviderConfiguration extends ClassInstanceProviderConfiguration, Copier> implements ServiceCreationConfiguration { +public class DefaultCopyProviderConfiguration extends ClassInstanceProviderConfiguration, DefaultCopierConfiguration> implements ServiceCreationConfiguration { /** * Default constructor. @@ -93,9 +92,17 @@ public DefaultCopyProviderConfiguration addCopierFor(Class clazz, Class> configuration = (ClassInstanceConfiguration) new DefaultCopierConfiguration<>(copierClass); - getDefaults().put(clazz, configuration); + getDefaults().put(clazz, new DefaultCopierConfiguration<>(copierClass)); return this; } + + @Override + public DefaultCopyProviderConfiguration derive() { + return new DefaultCopyProviderConfiguration(this); + } + + @Override + public DefaultCopyProviderConfiguration build(DefaultCopyProviderConfiguration configuration) { + return configuration; + } } diff --git a/impl/src/main/java/org/ehcache/impl/config/copy/package-info.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/copy/package-info.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/config/copy/package-info.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/copy/package-info.java diff --git a/impl/src/main/java/org/ehcache/impl/config/event/CacheEventDispatcherFactoryConfiguration.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/event/CacheEventDispatcherFactoryConfiguration.java similarity index 85% rename from impl/src/main/java/org/ehcache/impl/config/event/CacheEventDispatcherFactoryConfiguration.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/event/CacheEventDispatcherFactoryConfiguration.java index ae6abc605c..486d5b2e76 100644 --- a/impl/src/main/java/org/ehcache/impl/config/event/CacheEventDispatcherFactoryConfiguration.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/config/event/CacheEventDispatcherFactoryConfiguration.java @@ -25,7 +25,7 @@ * Enables configuring the default thread pool alias to be used by the * {@link org.ehcache.core.events.CacheEventDispatcher}s */ -public class CacheEventDispatcherFactoryConfiguration implements ServiceCreationConfiguration { +public class CacheEventDispatcherFactoryConfiguration implements ServiceCreationConfiguration { private final String threadPoolAlias; @@ -54,4 +54,14 @@ public String getThreadPoolAlias() { public Class getServiceType() { return CacheEventDispatcherFactory.class; } + + @Override + public String derive() { + return getThreadPoolAlias(); + } + + @Override + public CacheEventDispatcherFactoryConfiguration build(String alias) { + return new CacheEventDispatcherFactoryConfiguration(alias); + } } diff --git a/impl/src/main/java/org/ehcache/impl/config/event/DefaultCacheEventDispatcherConfiguration.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/event/DefaultCacheEventDispatcherConfiguration.java similarity index 85% rename from impl/src/main/java/org/ehcache/impl/config/event/DefaultCacheEventDispatcherConfiguration.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/event/DefaultCacheEventDispatcherConfiguration.java index 2bbc11369b..300142b82a 100644 --- a/impl/src/main/java/org/ehcache/impl/config/event/DefaultCacheEventDispatcherConfiguration.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/config/event/DefaultCacheEventDispatcherConfiguration.java @@ -25,7 +25,7 @@ * Enables configuring the thread pool to be used by a {@link org.ehcache.core.events.CacheEventDispatcher} for * a given cache. */ -public class DefaultCacheEventDispatcherConfiguration implements ServiceConfiguration { +public class DefaultCacheEventDispatcherConfiguration implements ServiceConfiguration { private final String threadPoolAlias; @@ -54,4 +54,14 @@ public Class getServiceType() { public String getThreadPoolAlias() { return threadPoolAlias; } + + @Override + public String derive() { + return getThreadPoolAlias(); + } + + @Override + public DefaultCacheEventDispatcherConfiguration build(String alias) { + return new DefaultCacheEventDispatcherConfiguration(alias); + } } diff --git a/impl/src/main/java/org/ehcache/impl/config/event/DefaultCacheEventListenerConfiguration.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/event/DefaultCacheEventListenerConfiguration.java similarity index 90% rename from impl/src/main/java/org/ehcache/impl/config/event/DefaultCacheEventListenerConfiguration.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/event/DefaultCacheEventListenerConfiguration.java index cf3edd13d2..91e9720a54 100644 --- a/impl/src/main/java/org/ehcache/impl/config/event/DefaultCacheEventListenerConfiguration.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/config/event/DefaultCacheEventListenerConfiguration.java @@ -23,6 +23,7 @@ import org.ehcache.event.EventOrdering; import org.ehcache.event.EventType; import org.ehcache.impl.internal.classes.ClassInstanceConfiguration; +import org.ehcache.spi.service.ServiceConfiguration; import java.util.EnumSet; import java.util.Set; @@ -31,9 +32,12 @@ * {@link org.ehcache.spi.service.ServiceConfiguration} for the default {@link CacheEventListenerProvider}. *

* Enables configuring a {@link CacheEventListener} for a given cache. + *

+ * This class overrides the default {@link ServiceConfiguration#compatibleWith(ServiceConfiguration)} implementation + * to allow for the configuration of multiple cache event listeners on the same cache. */ public class DefaultCacheEventListenerConfiguration extends ClassInstanceConfiguration> - implements CacheEventListenerConfiguration { + implements CacheEventListenerConfiguration { private final EnumSet eventsToFireOn; private EventFiring eventFiringMode = EventFiring.ASYNCHRONOUS; @@ -131,4 +135,9 @@ public EventOrdering orderingMode() { public EnumSet fireOn() { return eventsToFireOn; } + + @Override + public boolean compatibleWith(ServiceConfiguration other) { + return true; + } } diff --git a/impl/src/main/java/org/ehcache/impl/config/event/DefaultEventSourceConfiguration.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/event/DefaultEventSourceConfiguration.java similarity index 86% rename from impl/src/main/java/org/ehcache/impl/config/event/DefaultEventSourceConfiguration.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/event/DefaultEventSourceConfiguration.java index 3238486a14..1c549db83e 100644 --- a/impl/src/main/java/org/ehcache/impl/config/event/DefaultEventSourceConfiguration.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/config/event/DefaultEventSourceConfiguration.java @@ -23,7 +23,7 @@ * {@link org.ehcache.spi.service.ServiceConfiguration} for a {@link org.ehcache.core.spi.store.Store.Provider} * related to {@link org.ehcache.core.spi.store.events.StoreEvent}s. */ -public class DefaultEventSourceConfiguration implements StoreEventSourceConfiguration { +public class DefaultEventSourceConfiguration implements StoreEventSourceConfiguration { private final int dispatcherConcurrency; @@ -53,4 +53,14 @@ public int getDispatcherConcurrency() { public Class getServiceType() { return Store.Provider.class; } + + @Override + public Integer derive() { + return getDispatcherConcurrency(); + } + + @Override + public DefaultEventSourceConfiguration build(Integer concurrency) { + return new DefaultEventSourceConfiguration(concurrency); + } } diff --git a/impl/src/main/java/org/ehcache/impl/config/event/package-info.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/event/package-info.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/config/event/package-info.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/event/package-info.java diff --git a/impl/src/main/java/org/ehcache/impl/config/executor/PooledExecutionServiceConfiguration.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/executor/PooledExecutionServiceConfiguration.java similarity index 83% rename from impl/src/main/java/org/ehcache/impl/config/executor/PooledExecutionServiceConfiguration.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/executor/PooledExecutionServiceConfiguration.java index 37adf3c05f..1073f8630f 100644 --- a/impl/src/main/java/org/ehcache/impl/config/executor/PooledExecutionServiceConfiguration.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/config/executor/PooledExecutionServiceConfiguration.java @@ -18,10 +18,13 @@ import java.util.HashMap; import java.util.Map; + +import org.ehcache.config.builders.PooledExecutionServiceConfigurationBuilder; import org.ehcache.core.spi.service.ExecutionService; import org.ehcache.spi.service.ServiceCreationConfiguration; import static java.util.Collections.unmodifiableMap; +import static org.ehcache.config.builders.PooledExecutionServiceConfigurationBuilder.newPooledExecutionServiceConfigurationBuilder; /** * {@link ServiceCreationConfiguration} for the pooled {@link ExecutionService} implementation. @@ -36,7 +39,7 @@ * @see org.ehcache.impl.config.store.disk.OffHeapDiskStoreProviderConfiguration * @see org.ehcache.impl.config.store.disk.OffHeapDiskStoreConfiguration */ -public class PooledExecutionServiceConfiguration implements ServiceCreationConfiguration { +public class PooledExecutionServiceConfiguration implements ServiceCreationConfiguration { private final Map poolConfigurations = new HashMap<>(); @@ -52,18 +55,20 @@ public class PooledExecutionServiceConfiguration implements ServiceCreationConfi * @param alias the pool alias * @param minSize the minimum size * @param maxSize the maximum size + * @return this configuration object with a new default pool * * @throws NullPointerException if alias is null * @throws IllegalArgumentException if another default was configured already or if another pool with the same * alias was configured already */ - public void addDefaultPool(String alias, int minSize, int maxSize) { + public PooledExecutionServiceConfiguration addDefaultPool(String alias, int minSize, int maxSize) { if (alias == null) { throw new NullPointerException("Pool alias cannot be null"); } if (defaultAlias == null) { addPool(alias, minSize, maxSize); defaultAlias = alias; + return this; } else { throw new IllegalArgumentException("'" + defaultAlias + "' is already configured as the default pool"); } @@ -75,11 +80,12 @@ public void addDefaultPool(String alias, int minSize, int maxSize) { * @param alias the pool alias * @param minSize the minimum size * @param maxSize the maximum size + * @return this configuration object with a new pool * * @throws NullPointerException if alias is null * @throws IllegalArgumentException if another pool with the same alias was configured already */ - public void addPool(String alias, int minSize, int maxSize) { + public PooledExecutionServiceConfiguration addPool(String alias, int minSize, int maxSize) { if (alias == null) { throw new NullPointerException("Pool alias cannot be null"); } @@ -87,6 +93,7 @@ public void addPool(String alias, int minSize, int maxSize) { throw new IllegalArgumentException("A pool with the alias '" + alias + "' is already configured"); } else { poolConfigurations.put(alias, new PoolConfiguration(minSize, maxSize)); + return this; } } @@ -116,6 +123,16 @@ public Class getServiceType() { return ExecutionService.class; } + @Override + public PooledExecutionServiceConfigurationBuilder derive() { + return newPooledExecutionServiceConfigurationBuilder(this); + } + + @Override + public PooledExecutionServiceConfiguration build(PooledExecutionServiceConfigurationBuilder builder) { + return builder.build(); + } + /** * Configuration class representing a pool configuration. */ diff --git a/impl/src/main/java/org/ehcache/impl/config/executor/package-info.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/executor/package-info.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/config/executor/package-info.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/executor/package-info.java diff --git a/impl/src/main/java/org/ehcache/impl/config/loaderwriter/DefaultCacheLoaderWriterConfiguration.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/loaderwriter/DefaultCacheLoaderWriterConfiguration.java similarity index 79% rename from impl/src/main/java/org/ehcache/impl/config/loaderwriter/DefaultCacheLoaderWriterConfiguration.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/loaderwriter/DefaultCacheLoaderWriterConfiguration.java index 4a50d361f7..4902c4c564 100644 --- a/impl/src/main/java/org/ehcache/impl/config/loaderwriter/DefaultCacheLoaderWriterConfiguration.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/config/loaderwriter/DefaultCacheLoaderWriterConfiguration.java @@ -25,7 +25,7 @@ /** * {@link ServiceConfiguration} for the default {@link CacheLoaderWriterProvider}. */ -public class DefaultCacheLoaderWriterConfiguration extends ClassInstanceConfiguration> implements CacheLoaderWriterConfiguration { +public class DefaultCacheLoaderWriterConfiguration extends ClassInstanceConfiguration> implements CacheLoaderWriterConfiguration { /** * Creates a new configuration object with the specified {@link CacheLoaderWriter} class and associated constructor @@ -47,4 +47,17 @@ public DefaultCacheLoaderWriterConfiguration(CacheLoaderWriter loaderWrite super(loaderWriter); } + protected DefaultCacheLoaderWriterConfiguration(DefaultCacheLoaderWriterConfiguration configuration) { + super(configuration); + } + + @Override + public DefaultCacheLoaderWriterConfiguration derive() { + return new DefaultCacheLoaderWriterConfiguration(this); + } + + @Override + public DefaultCacheLoaderWriterConfiguration build(DefaultCacheLoaderWriterConfiguration configuration) { + return configuration; + } } diff --git a/impl/src/main/java/org/ehcache/impl/config/loaderwriter/DefaultCacheLoaderWriterProviderConfiguration.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/loaderwriter/DefaultCacheLoaderWriterProviderConfiguration.java similarity index 71% rename from impl/src/main/java/org/ehcache/impl/config/loaderwriter/DefaultCacheLoaderWriterProviderConfiguration.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/loaderwriter/DefaultCacheLoaderWriterProviderConfiguration.java index 4ff724373d..cf3b4bd744 100644 --- a/impl/src/main/java/org/ehcache/impl/config/loaderwriter/DefaultCacheLoaderWriterProviderConfiguration.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/config/loaderwriter/DefaultCacheLoaderWriterProviderConfiguration.java @@ -24,7 +24,15 @@ /** * {@link ServiceCreationConfiguration} for the default {@link CacheLoaderWriterProvider}. */ -public class DefaultCacheLoaderWriterProviderConfiguration extends ClassInstanceProviderConfiguration> implements ServiceCreationConfiguration { +public class DefaultCacheLoaderWriterProviderConfiguration extends ClassInstanceProviderConfiguration implements ServiceCreationConfiguration { + + public DefaultCacheLoaderWriterProviderConfiguration() { + super(); + } + + public DefaultCacheLoaderWriterProviderConfiguration(DefaultCacheLoaderWriterProviderConfiguration config) { + super(config); + } /** * {@inheritDoc} @@ -48,4 +56,14 @@ public DefaultCacheLoaderWriterProviderConfiguration addLoaderFor(String alias, getDefaults().put(alias, new DefaultCacheLoaderWriterConfiguration(clazz, arguments)); return this; } + + @Override + public DefaultCacheLoaderWriterProviderConfiguration derive() { + return new DefaultCacheLoaderWriterProviderConfiguration(this); + } + + @Override + public DefaultCacheLoaderWriterProviderConfiguration build(DefaultCacheLoaderWriterProviderConfiguration configuration) { + return configuration; + } } diff --git a/impl/src/main/java/org/ehcache/impl/config/loaderwriter/package-info.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/loaderwriter/package-info.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/config/loaderwriter/package-info.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/loaderwriter/package-info.java diff --git a/impl/src/main/java/org/ehcache/impl/config/loaderwriter/writebehind/DefaultBatchingConfiguration.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/loaderwriter/writebehind/DefaultBatchingConfiguration.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/config/loaderwriter/writebehind/DefaultBatchingConfiguration.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/loaderwriter/writebehind/DefaultBatchingConfiguration.java diff --git a/impl/src/main/java/org/ehcache/impl/config/loaderwriter/writebehind/DefaultWriteBehindConfiguration.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/loaderwriter/writebehind/DefaultWriteBehindConfiguration.java similarity index 98% rename from impl/src/main/java/org/ehcache/impl/config/loaderwriter/writebehind/DefaultWriteBehindConfiguration.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/loaderwriter/writebehind/DefaultWriteBehindConfiguration.java index 57486009ea..7362998bae 100644 --- a/impl/src/main/java/org/ehcache/impl/config/loaderwriter/writebehind/DefaultWriteBehindConfiguration.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/config/loaderwriter/writebehind/DefaultWriteBehindConfiguration.java @@ -22,7 +22,7 @@ /** * {@link org.ehcache.spi.service.ServiceConfiguration} for the default {@link WriteBehindProvider}. */ -public class DefaultWriteBehindConfiguration implements WriteBehindConfiguration { +public class DefaultWriteBehindConfiguration implements WriteBehindConfiguration { private final BatchingConfiguration batchingConfig; private final int concurrency; diff --git a/impl/src/main/java/org/ehcache/impl/config/loaderwriter/writebehind/WriteBehindProviderConfiguration.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/loaderwriter/writebehind/WriteBehindProviderConfiguration.java similarity index 81% rename from impl/src/main/java/org/ehcache/impl/config/loaderwriter/writebehind/WriteBehindProviderConfiguration.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/loaderwriter/writebehind/WriteBehindProviderConfiguration.java index 114f7dda21..1d116a1007 100644 --- a/impl/src/main/java/org/ehcache/impl/config/loaderwriter/writebehind/WriteBehindProviderConfiguration.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/config/loaderwriter/writebehind/WriteBehindProviderConfiguration.java @@ -23,7 +23,7 @@ * * @author cdennis */ -public class WriteBehindProviderConfiguration implements ServiceCreationConfiguration { +public class WriteBehindProviderConfiguration implements ServiceCreationConfiguration { private final String threadPoolAlias; @@ -39,4 +39,14 @@ public String getThreadPoolAlias() { public Class getServiceType() { return WriteBehindProvider.class; } + + @Override + public String derive() { + return getThreadPoolAlias(); + } + + @Override + public WriteBehindProviderConfiguration build(String alias) { + return new WriteBehindProviderConfiguration(alias); + } } diff --git a/impl/src/main/java/org/ehcache/impl/config/loaderwriter/writebehind/package-info.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/loaderwriter/writebehind/package-info.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/config/loaderwriter/writebehind/package-info.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/loaderwriter/writebehind/package-info.java diff --git a/impl/src/main/java/org/ehcache/impl/config/persistence/CacheManagerPersistenceConfiguration.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/persistence/CacheManagerPersistenceConfiguration.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/config/persistence/CacheManagerPersistenceConfiguration.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/persistence/CacheManagerPersistenceConfiguration.java diff --git a/impl/src/main/java/org/ehcache/impl/config/persistence/DefaultPersistenceConfiguration.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/persistence/DefaultPersistenceConfiguration.java similarity index 85% rename from impl/src/main/java/org/ehcache/impl/config/persistence/DefaultPersistenceConfiguration.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/persistence/DefaultPersistenceConfiguration.java index 743e6c0022..76030889c9 100644 --- a/impl/src/main/java/org/ehcache/impl/config/persistence/DefaultPersistenceConfiguration.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/config/persistence/DefaultPersistenceConfiguration.java @@ -24,7 +24,7 @@ /** * {@link ServiceCreationConfiguration} for the default {@link LocalPersistenceService}. */ -public class DefaultPersistenceConfiguration implements ServiceCreationConfiguration { +public class DefaultPersistenceConfiguration implements ServiceCreationConfiguration { private final File rootDirectory; @@ -53,4 +53,14 @@ public File getRootDirectory() { public Class getServiceType() { return LocalPersistenceService.class; } + + @Override + public File derive() { + return getRootDirectory(); + } + + @Override + public DefaultPersistenceConfiguration build(File file) { + return new DefaultPersistenceConfiguration(file); + } } diff --git a/impl/src/main/java/org/ehcache/impl/config/persistence/UserManagedPersistenceContext.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/persistence/UserManagedPersistenceContext.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/config/persistence/UserManagedPersistenceContext.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/persistence/UserManagedPersistenceContext.java diff --git a/impl/src/main/java/org/ehcache/impl/config/persistence/package-info.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/persistence/package-info.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/config/persistence/package-info.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/persistence/package-info.java diff --git a/impl/src/main/java/org/ehcache/impl/config/resilience/DefaultResilienceStrategyConfiguration.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/resilience/DefaultResilienceStrategyConfiguration.java similarity index 89% rename from impl/src/main/java/org/ehcache/impl/config/resilience/DefaultResilienceStrategyConfiguration.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/resilience/DefaultResilienceStrategyConfiguration.java index 96fdc78630..4ba945686c 100644 --- a/impl/src/main/java/org/ehcache/impl/config/resilience/DefaultResilienceStrategyConfiguration.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/config/resilience/DefaultResilienceStrategyConfiguration.java @@ -27,7 +27,7 @@ /** * {@link ServiceConfiguration} for the default {@link ResilienceStrategyProvider}. */ -public class DefaultResilienceStrategyConfiguration extends ClassInstanceConfiguration> implements ServiceConfiguration { +public class DefaultResilienceStrategyConfiguration extends ClassInstanceConfiguration> implements ServiceConfiguration { /** * Creates a resilience strategy configuration that instantiates instances of the given class on demand. @@ -52,11 +52,25 @@ public DefaultResilienceStrategyConfiguration(ResilienceStrategy instance) super(instance); } + protected DefaultResilienceStrategyConfiguration(DefaultResilienceStrategyConfiguration configuration) { + super(configuration); + } + @Override public Class getServiceType() { return ResilienceStrategyProvider.class; } + @Override + public DefaultResilienceStrategyConfiguration derive() { + return new DefaultResilienceStrategyConfiguration(this); + } + + @Override + public DefaultResilienceStrategyConfiguration build(DefaultResilienceStrategyConfiguration config) { + return config; + } + /** * Returns a configuration object bound to the given store and cache loader-writer. * diff --git a/impl/src/main/java/org/ehcache/impl/config/resilience/DefaultResilienceStrategyProviderConfiguration.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/resilience/DefaultResilienceStrategyProviderConfiguration.java similarity index 87% rename from impl/src/main/java/org/ehcache/impl/config/resilience/DefaultResilienceStrategyProviderConfiguration.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/resilience/DefaultResilienceStrategyProviderConfiguration.java index 11188620db..1538bceb5e 100644 --- a/impl/src/main/java/org/ehcache/impl/config/resilience/DefaultResilienceStrategyProviderConfiguration.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/config/resilience/DefaultResilienceStrategyProviderConfiguration.java @@ -15,9 +15,9 @@ */ package org.ehcache.impl.config.resilience; -import org.ehcache.core.internal.resilience.RobustLoaderWriterResilienceStrategy; -import org.ehcache.core.internal.resilience.RobustResilienceStrategy; import org.ehcache.impl.internal.classes.ClassInstanceProviderConfiguration; +import org.ehcache.impl.internal.resilience.RobustLoaderWriterResilienceStrategy; +import org.ehcache.impl.internal.resilience.RobustResilienceStrategy; import org.ehcache.spi.resilience.ResilienceStrategy; import org.ehcache.spi.resilience.ResilienceStrategyProvider; import org.ehcache.spi.service.ServiceCreationConfiguration; @@ -25,7 +25,7 @@ /** * {@link ServiceCreationConfiguration} for the default {@link ResilienceStrategyProvider}. */ -public class DefaultResilienceStrategyProviderConfiguration extends ClassInstanceProviderConfiguration> implements ServiceCreationConfiguration { +public class DefaultResilienceStrategyProviderConfiguration extends ClassInstanceProviderConfiguration implements ServiceCreationConfiguration { @SuppressWarnings("rawtypes") private static final Class DEFAULT_RESILIENCE = RobustResilienceStrategy.class; @@ -35,6 +35,12 @@ public class DefaultResilienceStrategyProviderConfiguration extends ClassInstanc private DefaultResilienceStrategyConfiguration defaultRegularConfiguration; private DefaultResilienceStrategyConfiguration defaultLoaderWriterConfiguration; + private DefaultResilienceStrategyProviderConfiguration(DefaultResilienceStrategyProviderConfiguration config) { + super(config); + this.defaultRegularConfiguration = config.defaultRegularConfiguration; + this.defaultLoaderWriterConfiguration = config.defaultLoaderWriterConfiguration; + } + public DefaultResilienceStrategyProviderConfiguration() { this.defaultRegularConfiguration = new DefaultResilienceStrategyConfiguration(DEFAULT_RESILIENCE); this.defaultLoaderWriterConfiguration = new DefaultResilienceStrategyConfiguration(DEFAULT_LOADER_WRITER_RESILIENCE); @@ -154,4 +160,14 @@ public DefaultResilienceStrategyProviderConfiguration addResilienceStrategyFor(S getDefaults().put(alias, new DefaultResilienceStrategyConfiguration(resilienceStrategy)); return this; } + + @Override + public DefaultResilienceStrategyProviderConfiguration derive() { + return new DefaultResilienceStrategyProviderConfiguration(this); + } + + @Override + public DefaultResilienceStrategyProviderConfiguration build(DefaultResilienceStrategyProviderConfiguration configuration) { + return configuration; + } } diff --git a/impl/src/main/java/org/ehcache/impl/config/serializer/DefaultSerializationProviderConfiguration.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/serializer/DefaultSerializationProviderConfiguration.java similarity index 92% rename from impl/src/main/java/org/ehcache/impl/config/serializer/DefaultSerializationProviderConfiguration.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/serializer/DefaultSerializationProviderConfiguration.java index 184cbd963f..961ce124ca 100644 --- a/impl/src/main/java/org/ehcache/impl/config/serializer/DefaultSerializationProviderConfiguration.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/config/serializer/DefaultSerializationProviderConfiguration.java @@ -30,7 +30,7 @@ /** * {@link ServiceCreationConfiguration} for the default {@link SerializationProvider}. */ -public class DefaultSerializationProviderConfiguration implements ServiceCreationConfiguration { +public class DefaultSerializationProviderConfiguration implements ServiceCreationConfiguration { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultSerializationProviderConfiguration.class); @@ -130,4 +130,14 @@ private static boolean isConstructorPresent(Class clazz, Class... args) { public Map, Class>> getDefaultSerializers() { return unmodifiableMap(defaultSerializers); } + + @Override + public DefaultSerializationProviderConfiguration derive() { + return new DefaultSerializationProviderConfiguration(this); + } + + @Override + public DefaultSerializationProviderConfiguration build(DefaultSerializationProviderConfiguration configuration) { + return configuration; + } } diff --git a/impl/src/main/java/org/ehcache/impl/config/serializer/DefaultSerializerConfiguration.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/serializer/DefaultSerializerConfiguration.java similarity index 80% rename from impl/src/main/java/org/ehcache/impl/config/serializer/DefaultSerializerConfiguration.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/serializer/DefaultSerializerConfiguration.java index e50a76ad64..33ad82c349 100644 --- a/impl/src/main/java/org/ehcache/impl/config/serializer/DefaultSerializerConfiguration.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/config/serializer/DefaultSerializerConfiguration.java @@ -23,8 +23,11 @@ /** * {@link ServiceConfiguration} for the default {@link SerializationProvider}. + *

+ * This class overrides the default {@link ServiceConfiguration#compatibleWith(ServiceConfiguration)} implementation + * to allow for independent configuration of the key and value serializers. */ -public class DefaultSerializerConfiguration extends ClassInstanceConfiguration> implements ServiceConfiguration { +public class DefaultSerializerConfiguration extends ClassInstanceConfiguration> implements ServiceConfiguration { private final Type type; @@ -67,6 +70,15 @@ public Type getType() { return type; } + @Override + public boolean compatibleWith(ServiceConfiguration other) { + if (other instanceof DefaultSerializerConfiguration) { + return !getType().equals(((DefaultSerializerConfiguration) other).getType()); + } else { + return ServiceConfiguration.super.compatibleWith(other); + } + } + /** * Serialization provider types */ diff --git a/impl/src/main/java/org/ehcache/impl/config/serializer/package-info.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/serializer/package-info.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/config/serializer/package-info.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/serializer/package-info.java diff --git a/impl/src/main/java/org/ehcache/impl/config/store/disk/OffHeapDiskStoreConfiguration.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/store/disk/OffHeapDiskStoreConfiguration.java similarity index 91% rename from impl/src/main/java/org/ehcache/impl/config/store/disk/OffHeapDiskStoreConfiguration.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/store/disk/OffHeapDiskStoreConfiguration.java index 828c9ed92e..ad32d7b7d5 100644 --- a/impl/src/main/java/org/ehcache/impl/config/store/disk/OffHeapDiskStoreConfiguration.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/config/store/disk/OffHeapDiskStoreConfiguration.java @@ -22,7 +22,7 @@ /** * {@link ServiceConfiguration} for the default {@link org.ehcache.core.spi.store.Store off heap disk store}. */ -public class OffHeapDiskStoreConfiguration implements ServiceConfiguration { +public class OffHeapDiskStoreConfiguration implements ServiceConfiguration { public static final int DEFAULT_WRITER_CONCURRENCY = 1; public static final int DEFAULT_DISK_SEGMENTS = 16; @@ -106,4 +106,14 @@ public int getDiskSegments() { public Class getServiceType() { return OffHeapDiskStore.Provider.class; } + + @Override + public OffHeapDiskStoreConfiguration derive() { + return new OffHeapDiskStoreConfiguration(threadPoolAlias, writerConcurrency, diskSegments); + } + + @Override + public OffHeapDiskStoreConfiguration build(OffHeapDiskStoreConfiguration config) { + return config; + } } diff --git a/impl/src/main/java/org/ehcache/impl/config/store/disk/OffHeapDiskStoreProviderConfiguration.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/store/disk/OffHeapDiskStoreProviderConfiguration.java similarity index 85% rename from impl/src/main/java/org/ehcache/impl/config/store/disk/OffHeapDiskStoreProviderConfiguration.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/store/disk/OffHeapDiskStoreProviderConfiguration.java index c597a181b6..61c2161337 100644 --- a/impl/src/main/java/org/ehcache/impl/config/store/disk/OffHeapDiskStoreProviderConfiguration.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/config/store/disk/OffHeapDiskStoreProviderConfiguration.java @@ -22,7 +22,7 @@ /** * {@link ServiceCreationConfiguration} for the default {@link org.ehcache.core.spi.store.Store off heap disk store}. */ -public class OffHeapDiskStoreProviderConfiguration implements ServiceCreationConfiguration { +public class OffHeapDiskStoreProviderConfiguration implements ServiceCreationConfiguration { private final String threadPoolAlias; @@ -55,4 +55,14 @@ public String getThreadPoolAlias() { public Class getServiceType() { return OffHeapDiskStore.Provider.class; } + + @Override + public String derive() { + return getThreadPoolAlias(); + } + + @Override + public OffHeapDiskStoreProviderConfiguration build(String alias) { + return new OffHeapDiskStoreProviderConfiguration(alias); + } } diff --git a/impl/src/main/java/org/ehcache/impl/config/store/disk/package-info.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/store/disk/package-info.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/config/store/disk/package-info.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/store/disk/package-info.java diff --git a/impl/src/main/java/org/ehcache/impl/config/store/heap/DefaultSizeOfEngineConfiguration.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/store/heap/DefaultSizeOfEngineConfiguration.java similarity index 89% rename from impl/src/main/java/org/ehcache/impl/config/store/heap/DefaultSizeOfEngineConfiguration.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/store/heap/DefaultSizeOfEngineConfiguration.java index 174fc4966a..d9e3bc89ea 100644 --- a/impl/src/main/java/org/ehcache/impl/config/store/heap/DefaultSizeOfEngineConfiguration.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/config/store/heap/DefaultSizeOfEngineConfiguration.java @@ -23,7 +23,7 @@ /** * {@link ServiceConfiguration} for the default {@link SizeOfEngineProvider}. */ -public class DefaultSizeOfEngineConfiguration implements ServiceConfiguration { +public class DefaultSizeOfEngineConfiguration implements ServiceConfiguration { /** * Default maximum object graph count after which sizing stops @@ -105,4 +105,14 @@ public MemoryUnit getUnit() { return this.unit; } + + @Override + public DefaultSizeOfEngineConfiguration derive() { + return new DefaultSizeOfEngineConfiguration(maxObjectSize, unit, objectGraphSize); + } + + @Override + public DefaultSizeOfEngineConfiguration build(DefaultSizeOfEngineConfiguration config) { + return config; + } } diff --git a/impl/src/main/java/org/ehcache/impl/config/store/heap/DefaultSizeOfEngineProviderConfiguration.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/store/heap/DefaultSizeOfEngineProviderConfiguration.java similarity index 86% rename from impl/src/main/java/org/ehcache/impl/config/store/heap/DefaultSizeOfEngineProviderConfiguration.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/store/heap/DefaultSizeOfEngineProviderConfiguration.java index 169acbd632..53543da5d8 100644 --- a/impl/src/main/java/org/ehcache/impl/config/store/heap/DefaultSizeOfEngineProviderConfiguration.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/config/store/heap/DefaultSizeOfEngineProviderConfiguration.java @@ -23,7 +23,7 @@ /** * {@link ServiceCreationConfiguration} for the default {@link SizeOfEngineProvider}. */ -public class DefaultSizeOfEngineProviderConfiguration implements ServiceCreationConfiguration { +public class DefaultSizeOfEngineProviderConfiguration implements ServiceCreationConfiguration { private final long objectGraphSize; private final long maxObjectSize; @@ -89,4 +89,14 @@ public long getMaxObjectSize() { public MemoryUnit getUnit() { return this.unit; } + + @Override + public DefaultSizeOfEngineProviderConfiguration derive() { + return new DefaultSizeOfEngineProviderConfiguration(maxObjectSize, unit, objectGraphSize); + } + + @Override + public DefaultSizeOfEngineProviderConfiguration build(DefaultSizeOfEngineProviderConfiguration configuration) { + return configuration; + } } diff --git a/impl/src/main/java/org/ehcache/impl/config/store/heap/package-info.java b/ehcache-impl/src/main/java/org/ehcache/impl/config/store/heap/package-info.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/config/store/heap/package-info.java rename to ehcache-impl/src/main/java/org/ehcache/impl/config/store/heap/package-info.java diff --git a/impl/src/main/java/org/ehcache/impl/copy/IdentityCopier.java b/ehcache-impl/src/main/java/org/ehcache/impl/copy/IdentityCopier.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/copy/IdentityCopier.java rename to ehcache-impl/src/main/java/org/ehcache/impl/copy/IdentityCopier.java diff --git a/impl/src/main/java/org/ehcache/impl/copy/ReadWriteCopier.java b/ehcache-impl/src/main/java/org/ehcache/impl/copy/ReadWriteCopier.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/copy/ReadWriteCopier.java rename to ehcache-impl/src/main/java/org/ehcache/impl/copy/ReadWriteCopier.java diff --git a/impl/src/main/java/org/ehcache/impl/copy/SerializingCopier.java b/ehcache-impl/src/main/java/org/ehcache/impl/copy/SerializingCopier.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/copy/SerializingCopier.java rename to ehcache-impl/src/main/java/org/ehcache/impl/copy/SerializingCopier.java diff --git a/impl/src/main/java/org/ehcache/impl/copy/package-info.java b/ehcache-impl/src/main/java/org/ehcache/impl/copy/package-info.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/copy/package-info.java rename to ehcache-impl/src/main/java/org/ehcache/impl/copy/package-info.java diff --git a/impl/src/main/java/org/ehcache/impl/events/CacheEventAdapter.java b/ehcache-impl/src/main/java/org/ehcache/impl/events/CacheEventAdapter.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/events/CacheEventAdapter.java rename to ehcache-impl/src/main/java/org/ehcache/impl/events/CacheEventAdapter.java diff --git a/impl/src/main/java/org/ehcache/impl/events/CacheEventDispatcherImpl.java b/ehcache-impl/src/main/java/org/ehcache/impl/events/CacheEventDispatcherImpl.java similarity index 96% rename from impl/src/main/java/org/ehcache/impl/events/CacheEventDispatcherImpl.java rename to ehcache-impl/src/main/java/org/ehcache/impl/events/CacheEventDispatcherImpl.java index 655201f5e7..283f074797 100644 --- a/impl/src/main/java/org/ehcache/impl/events/CacheEventDispatcherImpl.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/events/CacheEventDispatcherImpl.java @@ -21,7 +21,7 @@ import org.ehcache.core.CacheConfigurationProperty; import org.ehcache.core.events.CacheEventDispatcher; import org.ehcache.core.events.CacheEvents; -import org.ehcache.core.internal.events.EventListenerWrapper; +import org.ehcache.core.events.EventListenerWrapper; import org.ehcache.event.CacheEvent; import org.ehcache.event.CacheEventListener; import org.ehcache.event.EventFiring; @@ -107,6 +107,9 @@ private synchronized void registerCacheEventListener(EventListenerWrapper aSyncListenersList.add(wrapper); break; case SYNCHRONOUS: + if (syncListenersList.isEmpty()) { + storeEventSource.setSynchronous(true); + } syncListenersList.add(wrapper); break; default: @@ -148,6 +151,9 @@ private synchronized boolean removeWrapperFromList(EventListenerWrapper wr if (--listenersCount == 0) { storeEventSource.removeEventListener(eventListener); } + if (syncListenersList.isEmpty()) { + storeEventSource.setSynchronous(false); + } return true; } return false; @@ -160,6 +166,7 @@ private synchronized boolean removeWrapperFromList(EventListenerWrapper wr public synchronized void shutdown() { storeEventSource.removeEventListener(eventListener); storeEventSource.setEventOrdering(false); + storeEventSource.setSynchronous(false); syncListenersList.clear(); aSyncListenersList.clear(); unOrderedExectuor.shutdown(); diff --git a/impl/src/main/java/org/ehcache/impl/events/EventDispatchTask.java b/ehcache-impl/src/main/java/org/ehcache/impl/events/EventDispatchTask.java similarity index 96% rename from impl/src/main/java/org/ehcache/impl/events/EventDispatchTask.java rename to ehcache-impl/src/main/java/org/ehcache/impl/events/EventDispatchTask.java index 79897ca453..fbdcbdf88d 100644 --- a/impl/src/main/java/org/ehcache/impl/events/EventDispatchTask.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/events/EventDispatchTask.java @@ -16,7 +16,7 @@ package org.ehcache.impl.events; -import org.ehcache.core.internal.events.EventListenerWrapper; +import org.ehcache.core.events.EventListenerWrapper; import org.ehcache.event.CacheEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/impl/src/main/java/org/ehcache/impl/events/package-info.java b/ehcache-impl/src/main/java/org/ehcache/impl/events/package-info.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/events/package-info.java rename to ehcache-impl/src/main/java/org/ehcache/impl/events/package-info.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/DefaultTimeSourceService.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/DefaultTimeSourceService.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/DefaultTimeSourceService.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/DefaultTimeSourceService.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/TimeSourceConfiguration.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/TimeSourceConfiguration.java similarity index 86% rename from impl/src/main/java/org/ehcache/impl/internal/TimeSourceConfiguration.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/TimeSourceConfiguration.java index f8c6a178a7..28c400f961 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/TimeSourceConfiguration.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/TimeSourceConfiguration.java @@ -24,7 +24,7 @@ * * This configuration has to be applied at the {@link org.ehcache.CacheManager} level. */ -public class TimeSourceConfiguration implements ServiceCreationConfiguration { +public class TimeSourceConfiguration implements ServiceCreationConfiguration { private final TimeSource timeSource; @@ -51,4 +51,13 @@ public TimeSource getTimeSource() { return this.timeSource; } + @Override + public TimeSource derive() { + return getTimeSource(); + } + + @Override + public TimeSourceConfiguration build(TimeSource timeSource) { + return new TimeSourceConfiguration(timeSource); + } } diff --git a/impl/src/main/java/org/ehcache/impl/internal/TimeSourceServiceFactory.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/TimeSourceServiceFactory.java similarity index 83% rename from impl/src/main/java/org/ehcache/impl/internal/TimeSourceServiceFactory.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/TimeSourceServiceFactory.java index ab7577b741..add65271c6 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/TimeSourceServiceFactory.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/TimeSourceServiceFactory.java @@ -16,21 +16,23 @@ package org.ehcache.impl.internal; +import org.ehcache.core.spi.service.ServiceFactory; import org.ehcache.core.spi.time.TimeSourceService; import org.ehcache.spi.service.ServiceCreationConfiguration; -import org.ehcache.core.spi.service.ServiceFactory; +import org.osgi.service.component.annotations.Component; /** * TimeSourceServiceFactory */ +@Component public class TimeSourceServiceFactory implements ServiceFactory { @Override - public TimeSourceService create(ServiceCreationConfiguration configuration) { + public TimeSourceService create(ServiceCreationConfiguration configuration) { return new DefaultTimeSourceService((TimeSourceConfiguration) configuration); } @Override - public Class getServiceType() { - return TimeSourceService.class; + public Class getServiceType() { + return DefaultTimeSourceService.class; } } diff --git a/impl/src/main/java/org/ehcache/impl/internal/classes/ClassInstanceConfiguration.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/classes/ClassInstanceConfiguration.java similarity index 82% rename from impl/src/main/java/org/ehcache/impl/internal/classes/ClassInstanceConfiguration.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/classes/ClassInstanceConfiguration.java index e7cea1f690..5ebcc3355a 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/classes/ClassInstanceConfiguration.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/classes/ClassInstanceConfiguration.java @@ -43,6 +43,16 @@ public ClassInstanceConfiguration(T instance) { this.arguments = null; } + protected ClassInstanceConfiguration(ClassInstanceConfiguration configuration) { + this.instance = configuration.getInstance(); + this.clazz = configuration.getClazz(); + if (instance == null) { + this.arguments = asList(configuration.getArguments()); + } else { + this.arguments = null; + } + } + public Class getClazz() { return clazz; } diff --git a/impl/src/main/java/org/ehcache/impl/internal/classes/ClassInstanceProvider.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/classes/ClassInstanceProvider.java similarity index 77% rename from impl/src/main/java/org/ehcache/impl/internal/classes/ClassInstanceProvider.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/classes/ClassInstanceProvider.java index 8b6104cebc..1e41f4313d 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/classes/ClassInstanceProvider.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/classes/ClassInstanceProvider.java @@ -17,7 +17,6 @@ package org.ehcache.impl.internal.classes; import org.ehcache.config.CacheConfiguration; -import org.ehcache.impl.config.resilience.DefaultResilienceStrategyConfiguration; import org.ehcache.spi.service.ServiceProvider; import org.ehcache.spi.service.Service; import org.ehcache.spi.service.ServiceConfiguration; @@ -40,12 +39,12 @@ /** * @author Alex Snaps */ -public class ClassInstanceProvider { +public class ClassInstanceProvider, T> { /** * The order in which entries are put in is kept. */ - protected final Map> preconfigured = Collections.synchronizedMap(new LinkedHashMap>()); + protected final Map preconfigured = Collections.synchronizedMap(new LinkedHashMap()); /** * Instances provided by this provider vs their counts. @@ -53,17 +52,16 @@ public class ClassInstanceProvider { protected final ConcurrentWeakIdentityHashMap providedVsCount = new ConcurrentWeakIdentityHashMap<>(); protected final Set instantiated = Collections.newSetFromMap(new ConcurrentWeakIdentityHashMap()); - private final Class> cacheLevelConfig; + private final Class cacheLevelConfig; private final boolean uniqueClassLevelConfig; - protected ClassInstanceProvider(ClassInstanceProviderConfiguration factoryConfig, - Class> cacheLevelConfig) { + protected ClassInstanceProvider(ClassInstanceProviderConfiguration factoryConfig, + Class cacheLevelConfig) { this(factoryConfig, cacheLevelConfig, false); } - protected ClassInstanceProvider(ClassInstanceProviderConfiguration factoryConfig, - Class> cacheLevelConfig, - boolean uniqueClassLevelConfig) { + protected ClassInstanceProvider(ClassInstanceProviderConfiguration factoryConfig, + Class cacheLevelConfig, boolean uniqueClassLevelConfig) { this.uniqueClassLevelConfig = uniqueClassLevelConfig; if (factoryConfig != null) { preconfigured.putAll(factoryConfig.getDefaults()); @@ -71,16 +69,16 @@ protected ClassInstanceProvider(ClassInstanceProviderConfiguration factory this.cacheLevelConfig = cacheLevelConfig; } - protected ClassInstanceConfiguration getPreconfigured(K alias) { + protected C getPreconfigured(K alias) { return preconfigured.get(alias); } protected T newInstance(K alias, CacheConfiguration cacheConfiguration) { - ClassInstanceConfiguration config = null; + C config = null; if (uniqueClassLevelConfig) { config = findSingletonAmongst(cacheLevelConfig, cacheConfiguration.getServiceConfigurations()); } else { - Iterator> iterator = + Iterator iterator = findAmongst(cacheLevelConfig, cacheConfiguration.getServiceConfigurations()).iterator(); if (iterator.hasNext()) { config = iterator.next(); @@ -89,25 +87,24 @@ protected T newInstance(K alias, CacheConfiguration cacheConfiguration) { return newInstance(alias, config); } - protected T newInstance(K alias, ServiceConfiguration... serviceConfigurations) { - ClassInstanceConfiguration config = null; - Iterator> iterator = - findAmongst(cacheLevelConfig, serviceConfigurations).iterator(); + protected T newInstance(K alias, ServiceConfiguration... serviceConfigurations) { + C config = null; + Iterator iterator = findAmongst(cacheLevelConfig, (Object[]) serviceConfigurations).iterator(); if (iterator.hasNext()) { config = iterator.next(); } return newInstance(alias, config); } - protected T newInstance(K alias, ServiceConfiguration serviceConfiguration) { - ClassInstanceConfiguration config = null; + protected T newInstance(K alias, ServiceConfiguration serviceConfiguration) { + C config = null; if (serviceConfiguration != null && cacheLevelConfig.isAssignableFrom(serviceConfiguration.getClass())) { config = cacheLevelConfig.cast(serviceConfiguration); } return newInstance(alias, config); } - private T newInstance(K alias, ClassInstanceConfiguration config) { + private T newInstance(K alias, ClassInstanceConfiguration config) { if (config == null) { config = getPreconfigured(alias); if (config == null) { diff --git a/impl/src/main/java/org/ehcache/impl/internal/classes/ClassInstanceProviderConfiguration.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/classes/ClassInstanceProviderConfiguration.java similarity index 67% rename from impl/src/main/java/org/ehcache/impl/internal/classes/ClassInstanceProviderConfiguration.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/classes/ClassInstanceProviderConfiguration.java index 5a2330618c..cd102c7dc9 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/classes/ClassInstanceProviderConfiguration.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/classes/ClassInstanceProviderConfiguration.java @@ -16,8 +16,6 @@ package org.ehcache.impl.internal.classes; -import org.ehcache.impl.internal.classes.ClassInstanceConfiguration; - import java.util.LinkedHashMap; import java.util.Map; @@ -27,11 +25,19 @@ * * @author Alex Snaps */ -public class ClassInstanceProviderConfiguration { +public class ClassInstanceProviderConfiguration> { + + private final Map defaults; - private final Map> defaults = new LinkedHashMap<>(); + public ClassInstanceProviderConfiguration() { + this.defaults = new LinkedHashMap<>(); + } + + public ClassInstanceProviderConfiguration(ClassInstanceProviderConfiguration config) { + this.defaults = new LinkedHashMap<>(config.getDefaults()); + } - public Map> getDefaults() { + public Map getDefaults() { return defaults; } diff --git a/impl/src/main/java/org/ehcache/impl/internal/classes/commonslang/ArrayUtils.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/classes/commonslang/ArrayUtils.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/classes/commonslang/ArrayUtils.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/classes/commonslang/ArrayUtils.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/classes/commonslang/ClassUtils.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/classes/commonslang/ClassUtils.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/classes/commonslang/ClassUtils.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/classes/commonslang/ClassUtils.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/classes/commonslang/reflect/ConstructorUtils.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/classes/commonslang/reflect/ConstructorUtils.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/classes/commonslang/reflect/ConstructorUtils.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/classes/commonslang/reflect/ConstructorUtils.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/classes/commonslang/reflect/MemberUtils.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/classes/commonslang/reflect/MemberUtils.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/classes/commonslang/reflect/MemberUtils.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/classes/commonslang/reflect/MemberUtils.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/classes/commonslang/reflect/MethodUtils.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/classes/commonslang/reflect/MethodUtils.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/classes/commonslang/reflect/MethodUtils.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/classes/commonslang/reflect/MethodUtils.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/events/AbstractStoreEventDispatcher.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/events/AbstractStoreEventDispatcher.java similarity index 90% rename from impl/src/main/java/org/ehcache/impl/internal/events/AbstractStoreEventDispatcher.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/events/AbstractStoreEventDispatcher.java index 47e0f5e164..f2bc22432d 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/events/AbstractStoreEventDispatcher.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/events/AbstractStoreEventDispatcher.java @@ -30,7 +30,7 @@ /** * AbstractStoreEventDispatcher */ -abstract class AbstractStoreEventDispatcher implements StoreEventDispatcher { +public abstract class AbstractStoreEventDispatcher implements StoreEventDispatcher { protected static final StoreEventSink NO_OP_EVENT_SINK = new CloseableStoreEventSink() { @Override @@ -124,6 +124,11 @@ public void setEventOrdering(boolean ordering) { this.ordered = ordering; } + @Override + public void setSynchronous(boolean synchronous) throws IllegalArgumentException { + //dispatcher is synchronous by default + } + @Override public boolean isEventOrdering() { return ordered; @@ -143,4 +148,9 @@ public void releaseEventSinkAfterFailure(StoreEventSink eventSink, Throwab public void reset(StoreEventSink eventSink) { ((CloseableStoreEventSink) eventSink).reset(); } + + @Override + public StoreEventSink eventSink() { + return new InvocationScopedEventSink<>(getFilters(), isEventOrdering(), getOrderedQueues(), getListeners()); + } } diff --git a/impl/src/main/java/org/ehcache/impl/internal/events/CacheEventDispatcherFactoryImpl.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/events/CacheEventDispatcherFactoryImpl.java similarity index 97% rename from impl/src/main/java/org/ehcache/impl/internal/events/CacheEventDispatcherFactoryImpl.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/events/CacheEventDispatcherFactoryImpl.java index d4b92c7f54..166d1c8f07 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/events/CacheEventDispatcherFactoryImpl.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/events/CacheEventDispatcherFactoryImpl.java @@ -61,7 +61,7 @@ public void stop() { } @Override - public CacheEventDispatcher createCacheEventDispatcher(Store store, ServiceConfiguration... serviceConfigs) { + public CacheEventDispatcher createCacheEventDispatcher(Store store, ServiceConfiguration... serviceConfigs) { String threadPoolAlias = defaultThreadPoolAlias; DefaultCacheEventDispatcherConfiguration config = findSingletonAmongst(DefaultCacheEventDispatcherConfiguration.class, (Object[]) serviceConfigs); if (config != null) { diff --git a/impl/src/main/java/org/ehcache/impl/internal/events/CacheEventNotificationListenerServiceProviderFactory.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/events/CacheEventNotificationListenerServiceProviderFactory.java similarity index 93% rename from impl/src/main/java/org/ehcache/impl/internal/events/CacheEventNotificationListenerServiceProviderFactory.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/events/CacheEventNotificationListenerServiceProviderFactory.java index acb05eae07..5c16d68427 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/events/CacheEventNotificationListenerServiceProviderFactory.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/events/CacheEventNotificationListenerServiceProviderFactory.java @@ -19,11 +19,13 @@ import org.ehcache.core.events.CacheEventDispatcherFactory; import org.ehcache.spi.service.ServiceCreationConfiguration; import org.ehcache.core.spi.service.ServiceFactory; +import org.osgi.service.component.annotations.Component; +@Component public class CacheEventNotificationListenerServiceProviderFactory implements ServiceFactory { @Override - public CacheEventDispatcherFactory create(ServiceCreationConfiguration configuration) { + public CacheEventDispatcherFactory create(ServiceCreationConfiguration configuration) { if (configuration == null) { return new CacheEventDispatcherFactoryImpl(); } else if (configuration instanceof CacheEventDispatcherFactoryConfiguration) { diff --git a/impl/src/main/java/org/ehcache/impl/internal/events/CloseableStoreEventSink.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/events/CloseableStoreEventSink.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/events/CloseableStoreEventSink.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/events/CloseableStoreEventSink.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/events/DisabledCacheEventNotificationService.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/events/DisabledCacheEventNotificationService.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/events/DisabledCacheEventNotificationService.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/events/DisabledCacheEventNotificationService.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/events/FireableStoreEventHolder.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/events/FireableStoreEventHolder.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/events/FireableStoreEventHolder.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/events/FireableStoreEventHolder.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/events/FudgingInvocationScopedEventSink.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/events/FudgingInvocationScopedEventSink.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/events/FudgingInvocationScopedEventSink.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/events/FudgingInvocationScopedEventSink.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/events/InvocationScopedEventSink.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/events/InvocationScopedEventSink.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/events/InvocationScopedEventSink.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/events/InvocationScopedEventSink.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/events/StoreEventImpl.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/events/StoreEventImpl.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/events/StoreEventImpl.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/events/StoreEventImpl.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/events/StoreEvents.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/events/StoreEvents.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/events/StoreEvents.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/events/StoreEvents.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/events/ThreadLocalStoreEventDispatcher.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/events/ThreadLocalStoreEventDispatcher.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/events/ThreadLocalStoreEventDispatcher.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/events/ThreadLocalStoreEventDispatcher.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/executor/DefaultExecutionServiceFactory.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/executor/DefaultExecutionServiceFactory.java similarity index 81% rename from impl/src/main/java/org/ehcache/impl/internal/executor/DefaultExecutionServiceFactory.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/executor/DefaultExecutionServiceFactory.java index c1423cd0a3..a1eaa43e2f 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/executor/DefaultExecutionServiceFactory.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/executor/DefaultExecutionServiceFactory.java @@ -15,19 +15,21 @@ */ package org.ehcache.impl.internal.executor; +import org.ehcache.core.spi.service.ExecutionService; import org.ehcache.core.spi.service.ServiceFactory; import org.ehcache.impl.config.executor.PooledExecutionServiceConfiguration; -import org.ehcache.core.spi.service.ExecutionService; import org.ehcache.spi.service.ServiceCreationConfiguration; +import org.osgi.service.component.annotations.Component; /** * * @author cdennis */ +@Component public class DefaultExecutionServiceFactory implements ServiceFactory { @Override - public ExecutionService create(ServiceCreationConfiguration configuration) { + public ExecutionService create(ServiceCreationConfiguration configuration) { if (configuration == null) { return new OnDemandExecutionService(); } else if (configuration instanceof PooledExecutionServiceConfiguration) { @@ -40,7 +42,12 @@ public ExecutionService create(ServiceCreationConfiguration co } @Override - public Class getServiceType() { + public Class getServiceType() { + /* + * XXX : There isn't a unique concrete type returned by this factory + * Currently this isn't a problem since neither of the concrete types + * returned have service depencies. + */ return ExecutionService.class; } diff --git a/impl/src/main/java/org/ehcache/impl/internal/executor/ExecutorUtil.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/executor/ExecutorUtil.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/executor/ExecutorUtil.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/executor/ExecutorUtil.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/executor/OnDemandExecutionService.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/executor/OnDemandExecutionService.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/executor/OnDemandExecutionService.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/executor/OnDemandExecutionService.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/executor/OutOfBandScheduledExecutor.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/executor/OutOfBandScheduledExecutor.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/executor/OutOfBandScheduledExecutor.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/executor/OutOfBandScheduledExecutor.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/executor/PartitionedOrderedExecutor.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/executor/PartitionedOrderedExecutor.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/executor/PartitionedOrderedExecutor.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/executor/PartitionedOrderedExecutor.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/executor/PartitionedScheduledExecutor.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/executor/PartitionedScheduledExecutor.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/executor/PartitionedScheduledExecutor.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/executor/PartitionedScheduledExecutor.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/executor/PartitionedUnorderedExecutor.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/executor/PartitionedUnorderedExecutor.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/executor/PartitionedUnorderedExecutor.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/executor/PartitionedUnorderedExecutor.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/executor/PooledExecutionService.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/executor/PooledExecutionService.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/executor/PooledExecutionService.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/executor/PooledExecutionService.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/AbstractWriteBehind.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/AbstractWriteBehind.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/AbstractWriteBehind.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/AbstractWriteBehind.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/BatchingLocalHeapWriteBehindQueue.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/BatchingLocalHeapWriteBehindQueue.java similarity index 99% rename from impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/BatchingLocalHeapWriteBehindQueue.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/BatchingLocalHeapWriteBehindQueue.java index 78ebb88879..4a2dd7b85a 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/BatchingLocalHeapWriteBehindQueue.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/BatchingLocalHeapWriteBehindQueue.java @@ -73,7 +73,7 @@ public class BatchingLocalHeapWriteBehindQueue extends AbstractWriteBehind private volatile Batch openBatch; - public BatchingLocalHeapWriteBehindQueue(ExecutionService executionService, String defaultThreadPool, WriteBehindConfiguration config, CacheLoaderWriter cacheLoaderWriter) { + public BatchingLocalHeapWriteBehindQueue(ExecutionService executionService, String defaultThreadPool, WriteBehindConfiguration config, CacheLoaderWriter cacheLoaderWriter) { super(cacheLoaderWriter); this.cacheLoaderWriter = cacheLoaderWriter; BatchingConfiguration batchingConfig = config.getBatchingConfiguration(); diff --git a/impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/NonBatchingLocalHeapWriteBehindQueue.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/NonBatchingLocalHeapWriteBehindQueue.java similarity index 98% rename from impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/NonBatchingLocalHeapWriteBehindQueue.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/NonBatchingLocalHeapWriteBehindQueue.java index 8c6e64dd74..2fcc075a32 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/NonBatchingLocalHeapWriteBehindQueue.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/NonBatchingLocalHeapWriteBehindQueue.java @@ -44,7 +44,7 @@ public class NonBatchingLocalHeapWriteBehindQueue extends AbstractWriteBeh private final BlockingQueue executorQueue; private final ExecutorService executor; - public NonBatchingLocalHeapWriteBehindQueue(ExecutionService executionService, String defaultThreadPool, WriteBehindConfiguration config, CacheLoaderWriter cacheLoaderWriter) { + public NonBatchingLocalHeapWriteBehindQueue(ExecutionService executionService, String defaultThreadPool, WriteBehindConfiguration config, CacheLoaderWriter cacheLoaderWriter) { super(cacheLoaderWriter); this.cacheLoaderWriter = cacheLoaderWriter; this.executorQueue = new LinkedBlockingQueue<>(config.getMaxQueueSize()); diff --git a/impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/StripedWriteBehind.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/StripedWriteBehind.java similarity index 96% rename from impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/StripedWriteBehind.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/StripedWriteBehind.java index 5f599c172e..af9a99c1f6 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/StripedWriteBehind.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/StripedWriteBehind.java @@ -35,7 +35,7 @@ public class StripedWriteBehind implements WriteBehind { private final List> stripes = new ArrayList<>(); - public StripedWriteBehind(ExecutionService executionService, String defaultThreadPool, WriteBehindConfiguration config, CacheLoaderWriter cacheLoaderWriter) { + public StripedWriteBehind(ExecutionService executionService, String defaultThreadPool, WriteBehindConfiguration config, CacheLoaderWriter cacheLoaderWriter) { int writeBehindConcurrency = config.getConcurrency(); for (int i = 0; i < writeBehindConcurrency; i++) { if (config.getBatchingConfiguration() == null) { diff --git a/impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/WriteBehind.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/WriteBehind.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/WriteBehind.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/WriteBehind.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/WriteBehindProviderFactory.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/WriteBehindProviderFactory.java similarity index 92% rename from impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/WriteBehindProviderFactory.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/WriteBehindProviderFactory.java index 1328dd60b2..f70df30c7d 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/WriteBehindProviderFactory.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/WriteBehindProviderFactory.java @@ -15,25 +15,27 @@ */ package org.ehcache.impl.internal.loaderwriter.writebehind; +import org.ehcache.core.spi.service.ExecutionService; +import org.ehcache.core.spi.service.ServiceFactory; import org.ehcache.impl.config.loaderwriter.writebehind.WriteBehindProviderConfiguration; -import org.ehcache.spi.service.ServiceProvider; import org.ehcache.spi.loaderwriter.CacheLoaderWriter; import org.ehcache.spi.loaderwriter.WriteBehindConfiguration; import org.ehcache.spi.loaderwriter.WriteBehindProvider; -import org.ehcache.core.spi.service.ExecutionService; import org.ehcache.spi.service.Service; import org.ehcache.spi.service.ServiceCreationConfiguration; import org.ehcache.spi.service.ServiceDependencies; -import org.ehcache.core.spi.service.ServiceFactory; +import org.ehcache.spi.service.ServiceProvider; +import org.osgi.service.component.annotations.Component; /** * @author Abhilash * */ +@Component public class WriteBehindProviderFactory implements ServiceFactory { @Override - public WriteBehindProvider create(ServiceCreationConfiguration configuration) { + public WriteBehindProvider create(ServiceCreationConfiguration configuration) { if (configuration == null) { return new Provider(); } else if (configuration instanceof WriteBehindProviderConfiguration) { @@ -69,7 +71,7 @@ public void start(ServiceProvider serviceProvider) { } @Override - public WriteBehind createWriteBehindLoaderWriter(CacheLoaderWriter cacheLoaderWriter, WriteBehindConfiguration configuration) { + public WriteBehind createWriteBehindLoaderWriter(CacheLoaderWriter cacheLoaderWriter, WriteBehindConfiguration configuration) { if (cacheLoaderWriter == null) { throw new NullPointerException("WriteBehind requires a non null CacheLoaderWriter."); } @@ -85,8 +87,8 @@ public void releaseWriteBehindLoaderWriter(CacheLoaderWriter cacheLoaderWr } @Override - public Class getServiceType() { - return WriteBehindProvider.class; + public Class getServiceType() { + return Provider.class; } } diff --git a/impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/operations/BatchOperation.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/operations/BatchOperation.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/operations/BatchOperation.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/operations/BatchOperation.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/operations/DeleteAllOperation.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/operations/DeleteAllOperation.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/operations/DeleteAllOperation.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/operations/DeleteAllOperation.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/operations/DeleteOperation.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/operations/DeleteOperation.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/operations/DeleteOperation.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/operations/DeleteOperation.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/operations/KeyBasedOperation.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/operations/KeyBasedOperation.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/operations/KeyBasedOperation.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/operations/KeyBasedOperation.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/operations/SingleOperation.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/operations/SingleOperation.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/operations/SingleOperation.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/operations/SingleOperation.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/operations/WriteAllOperation.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/operations/WriteAllOperation.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/operations/WriteAllOperation.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/operations/WriteAllOperation.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/operations/WriteOperation.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/operations/WriteOperation.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/operations/WriteOperation.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/loaderwriter/writebehind/operations/WriteOperation.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/persistence/DefaultDiskResourceServiceFactory.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/persistence/DefaultDiskResourceServiceFactory.java similarity index 84% rename from impl/src/main/java/org/ehcache/impl/internal/persistence/DefaultDiskResourceServiceFactory.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/persistence/DefaultDiskResourceServiceFactory.java index 3680ed45cd..bc142ee477 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/persistence/DefaultDiskResourceServiceFactory.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/persistence/DefaultDiskResourceServiceFactory.java @@ -19,16 +19,18 @@ import org.ehcache.core.spi.service.ServiceFactory; import org.ehcache.impl.persistence.DefaultDiskResourceService; import org.ehcache.spi.service.ServiceCreationConfiguration; +import org.osgi.service.component.annotations.Component; +@Component public class DefaultDiskResourceServiceFactory implements ServiceFactory { @Override - public DefaultDiskResourceService create(final ServiceCreationConfiguration serviceConfiguration) { + public DefaultDiskResourceService create(final ServiceCreationConfiguration serviceConfiguration) { return new DefaultDiskResourceService(); } @Override - public Class getServiceType() { + public Class getServiceType() { return DefaultDiskResourceService.class; } } diff --git a/impl/src/main/java/org/ehcache/impl/internal/persistence/DefaultLocalPersistenceServiceFactory.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/persistence/DefaultLocalPersistenceServiceFactory.java similarity index 83% rename from impl/src/main/java/org/ehcache/impl/internal/persistence/DefaultLocalPersistenceServiceFactory.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/persistence/DefaultLocalPersistenceServiceFactory.java index 55ae580bc2..dfc91f30b3 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/persistence/DefaultLocalPersistenceServiceFactory.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/persistence/DefaultLocalPersistenceServiceFactory.java @@ -16,25 +16,27 @@ package org.ehcache.impl.internal.persistence; -import org.ehcache.impl.config.persistence.DefaultPersistenceConfiguration; import org.ehcache.core.spi.service.LocalPersistenceService; +import org.ehcache.core.spi.service.ServiceFactory; +import org.ehcache.impl.config.persistence.DefaultPersistenceConfiguration; import org.ehcache.impl.persistence.DefaultLocalPersistenceService; import org.ehcache.spi.service.ServiceCreationConfiguration; -import org.ehcache.core.spi.service.ServiceFactory; +import org.osgi.service.component.annotations.Component; /** * @author Alex Snaps */ +@Component @ServiceFactory.RequiresConfiguration public class DefaultLocalPersistenceServiceFactory implements ServiceFactory { @Override - public DefaultLocalPersistenceService create(final ServiceCreationConfiguration serviceConfiguration) { + public DefaultLocalPersistenceService create(final ServiceCreationConfiguration serviceConfiguration) { return new DefaultLocalPersistenceService((DefaultPersistenceConfiguration) serviceConfiguration); } @Override - public Class getServiceType() { - return LocalPersistenceService.class; + public Class getServiceType() { + return DefaultLocalPersistenceService.class; } } diff --git a/core/src/main/java/org/ehcache/core/internal/resilience/AbstractResilienceStrategy.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/resilience/AbstractResilienceStrategy.java similarity index 98% rename from core/src/main/java/org/ehcache/core/internal/resilience/AbstractResilienceStrategy.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/resilience/AbstractResilienceStrategy.java index 60ac37f9cc..614e98fe71 100644 --- a/core/src/main/java/org/ehcache/core/internal/resilience/AbstractResilienceStrategy.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/resilience/AbstractResilienceStrategy.java @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.ehcache.core.internal.resilience; +package org.ehcache.impl.internal.resilience; import org.ehcache.Cache; import org.ehcache.CacheIterationException; -import org.ehcache.core.internal.util.Pacer; +import org.ehcache.impl.internal.util.Pacer; import org.ehcache.core.spi.time.SystemTimeSource; import org.ehcache.spi.resilience.RecoveryStore; import org.ehcache.spi.resilience.ResilienceStrategy; diff --git a/core/src/main/java/org/ehcache/core/internal/resilience/RobustLoaderWriterResilienceStrategy.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/resilience/RobustLoaderWriterResilienceStrategy.java similarity index 99% rename from core/src/main/java/org/ehcache/core/internal/resilience/RobustLoaderWriterResilienceStrategy.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/resilience/RobustLoaderWriterResilienceStrategy.java index 114149fb32..7f018f7d82 100644 --- a/core/src/main/java/org/ehcache/core/internal/resilience/RobustLoaderWriterResilienceStrategy.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/resilience/RobustLoaderWriterResilienceStrategy.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.ehcache.core.internal.resilience; +package org.ehcache.impl.internal.resilience; import org.ehcache.core.exceptions.ExceptionFactory; import org.ehcache.spi.loaderwriter.BulkCacheLoadingException; diff --git a/core/src/main/java/org/ehcache/core/internal/resilience/RobustResilienceStrategy.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/resilience/RobustResilienceStrategy.java similarity index 93% rename from core/src/main/java/org/ehcache/core/internal/resilience/RobustResilienceStrategy.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/resilience/RobustResilienceStrategy.java index 553864999d..3ec5cb8bbb 100644 --- a/core/src/main/java/org/ehcache/core/internal/resilience/RobustResilienceStrategy.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/resilience/RobustResilienceStrategy.java @@ -13,16 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.ehcache.core.internal.resilience; +package org.ehcache.impl.internal.resilience; -import org.ehcache.core.internal.util.CollectionUtil; -import org.ehcache.core.spi.store.Store; import org.ehcache.spi.resilience.RecoveryStore; import org.ehcache.spi.resilience.StoreAccessException; +import java.util.Collection; import java.util.HashMap; import java.util.Map; -import java.util.Objects; /** * Default resilience strategy used by a {@link org.ehcache.Cache} without {@link org.ehcache.spi.loaderwriter.CacheLoaderWriter}. @@ -173,8 +171,7 @@ public boolean replaceFailure(K key, V value, V newValue, StoreAccessException e public Map getAllFailure(Iterable keys, StoreAccessException e) { cleanup(keys, e); - int size = CollectionUtil.findBestCollectionSize(keys, 16); // 16 is the HashMap default - HashMap result = new HashMap<>(size); + HashMap result = keys instanceof Collection ? new HashMap<>(((Collection) keys).size()) : new HashMap<>(); for (K key : keys) { result.put(key, null); } diff --git a/impl/src/main/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngine.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngine.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngine.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngine.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngineProvider.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngineProvider.java similarity index 97% rename from impl/src/main/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngineProvider.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngineProvider.java index f83d9e94ef..02384cf566 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngineProvider.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngineProvider.java @@ -50,7 +50,7 @@ public void stop() { } @Override - public SizeOfEngine createSizeOfEngine(ResourceUnit resourceUnit, ServiceConfiguration... serviceConfigs) { + public SizeOfEngine createSizeOfEngine(ResourceUnit resourceUnit, ServiceConfiguration... serviceConfigs) { boolean isByteSized = resourceUnit instanceof MemoryUnit; if(!isByteSized) { return new NoopSizeOfEngine(); // Noop Size of Engine diff --git a/impl/src/main/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngineProviderFactory.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngineProviderFactory.java similarity index 88% rename from impl/src/main/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngineProviderFactory.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngineProviderFactory.java index 3cbd00c945..7f03033ba6 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngineProviderFactory.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngineProviderFactory.java @@ -16,21 +16,22 @@ package org.ehcache.impl.internal.sizeof; +import org.ehcache.core.spi.service.ServiceFactory; +import org.ehcache.core.spi.store.heap.SizeOfEngineProvider; import org.ehcache.impl.config.store.heap.DefaultSizeOfEngineConfiguration; import org.ehcache.impl.config.store.heap.DefaultSizeOfEngineProviderConfiguration; import org.ehcache.spi.service.ServiceCreationConfiguration; -import org.ehcache.core.spi.service.ServiceFactory; -import org.ehcache.core.spi.store.heap.SizeOfEngineProvider; +import org.osgi.service.component.annotations.Component; /** * @author Abhilash * */ - +@Component public class DefaultSizeOfEngineProviderFactory implements ServiceFactory { @Override - public SizeOfEngineProvider create(ServiceCreationConfiguration configuration) { + public SizeOfEngineProvider create(ServiceCreationConfiguration configuration) { long maxTraversals = DefaultSizeOfEngineConfiguration.DEFAULT_OBJECT_GRAPH_SIZE; long maxSize = DefaultSizeOfEngineConfiguration.DEFAULT_MAX_OBJECT_SIZE; if(configuration != null) { @@ -42,8 +43,8 @@ public SizeOfEngineProvider create(ServiceCreationConfiguration getServiceType() { - return SizeOfEngineProvider.class; + public Class getServiceType() { + return DefaultSizeOfEngineProvider.class; } } diff --git a/impl/src/main/java/org/ehcache/impl/internal/sizeof/NoopSizeOfEngine.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/sizeof/NoopSizeOfEngine.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/sizeof/NoopSizeOfEngine.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/sizeof/NoopSizeOfEngine.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/sizeof/listeners/EhcacheVisitorListener.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/sizeof/listeners/EhcacheVisitorListener.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/sizeof/listeners/EhcacheVisitorListener.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/sizeof/listeners/EhcacheVisitorListener.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/sizeof/listeners/exceptions/VisitorListenerException.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/sizeof/listeners/exceptions/VisitorListenerException.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/sizeof/listeners/exceptions/VisitorListenerException.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/sizeof/listeners/exceptions/VisitorListenerException.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/spi/copy/DefaultCopyProvider.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/spi/copy/DefaultCopyProvider.java similarity index 91% rename from impl/src/main/java/org/ehcache/impl/internal/spi/copy/DefaultCopyProvider.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/spi/copy/DefaultCopyProvider.java index 1ca546b355..61530b2352 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/spi/copy/DefaultCopyProvider.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/spi/copy/DefaultCopyProvider.java @@ -20,7 +20,6 @@ import org.ehcache.impl.config.copy.DefaultCopierConfiguration; import org.ehcache.impl.config.copy.DefaultCopierConfiguration.Type; import org.ehcache.impl.config.copy.DefaultCopyProviderConfiguration; -import org.ehcache.impl.internal.classes.ClassInstanceConfiguration; import org.ehcache.impl.internal.classes.ClassInstanceProvider; import org.ehcache.impl.copy.IdentityCopier; import org.ehcache.impl.copy.SerializingCopier; @@ -36,7 +35,7 @@ /** * @author Albin Suresh */ -public class DefaultCopyProvider extends ClassInstanceProvider, Copier> implements CopyProvider { +public class DefaultCopyProvider extends ClassInstanceProvider, DefaultCopierConfiguration, Copier> implements CopyProvider { private static final Logger LOG = LoggerFactory.getLogger(DefaultCopyProvider.class); @@ -47,12 +46,12 @@ public DefaultCopyProvider(DefaultCopyProviderConfiguration configuration) { @Override - public Copier createKeyCopier(final Class clazz, Serializer serializer, ServiceConfiguration... configs) { + public Copier createKeyCopier(final Class clazz, Serializer serializer, ServiceConfiguration... configs) { return createCopier(Type.KEY, clazz, serializer, configs); } @Override - public Copier createValueCopier(final Class clazz, Serializer serializer, ServiceConfiguration... configs) { + public Copier createValueCopier(final Class clazz, Serializer serializer, ServiceConfiguration... configs) { return createCopier(Type.VALUE, clazz, serializer, configs); } @@ -64,10 +63,10 @@ public void releaseCopier(Copier copier) throws Exception { } private Copier createCopier(Type type, Class clazz, - Serializer serializer, ServiceConfiguration... configs) { + Serializer serializer, ServiceConfiguration... configs) { DefaultCopierConfiguration conf = find(type, configs); Copier copier; - final ClassInstanceConfiguration> preConfigured = preconfigured.get(clazz); + final DefaultCopierConfiguration preConfigured = preconfigured.get(clazz); if (conf != null && conf.getClazz().isAssignableFrom(SerializingCopier.class)) { if (serializer == null) { throw new IllegalStateException("No Serializer configured for type '" + clazz.getName() @@ -99,7 +98,7 @@ private Copier createCopier(Class clazz, DefaultCopierConfiguration } @SuppressWarnings("unchecked") - private static DefaultCopierConfiguration find(Type type, ServiceConfiguration... serviceConfigurations) { + private static DefaultCopierConfiguration find(Type type, ServiceConfiguration... serviceConfigurations) { DefaultCopierConfiguration result = null; @SuppressWarnings("rawtypes") diff --git a/impl/src/main/java/org/ehcache/impl/internal/spi/copy/DefaultCopyProviderFactory.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/spi/copy/DefaultCopyProviderFactory.java similarity index 88% rename from impl/src/main/java/org/ehcache/impl/internal/spi/copy/DefaultCopyProviderFactory.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/spi/copy/DefaultCopyProviderFactory.java index e6c2b83ec6..3616eab932 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/spi/copy/DefaultCopyProviderFactory.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/spi/copy/DefaultCopyProviderFactory.java @@ -16,19 +16,20 @@ package org.ehcache.impl.internal.spi.copy; +import org.ehcache.core.spi.service.ServiceFactory; import org.ehcache.impl.config.copy.DefaultCopyProviderConfiguration; -import org.ehcache.impl.internal.spi.copy.DefaultCopyProvider; import org.ehcache.spi.copy.CopyProvider; import org.ehcache.spi.service.ServiceCreationConfiguration; -import org.ehcache.core.spi.service.ServiceFactory; +import org.osgi.service.component.annotations.Component; /** * @author Albin Suresh */ +@Component public class DefaultCopyProviderFactory implements ServiceFactory { @Override - public CopyProvider create(final ServiceCreationConfiguration configuration) { + public CopyProvider create(final ServiceCreationConfiguration configuration) { if (configuration != null && !(configuration instanceof DefaultCopyProviderConfiguration)) { throw new IllegalArgumentException("Expected a configuration of type DefaultCopyProviderConfiguration but got " + configuration.getClass().getSimpleName()); @@ -38,7 +39,7 @@ public CopyProvider create(final ServiceCreationConfiguration conf } @Override - public Class getServiceType() { - return CopyProvider.class; + public Class getServiceType() { + return DefaultCopyProvider.class; } } diff --git a/impl/src/main/java/org/ehcache/impl/internal/spi/event/DefaultCacheEventListenerProvider.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/spi/event/DefaultCacheEventListenerProvider.java similarity index 87% rename from impl/src/main/java/org/ehcache/impl/internal/spi/event/DefaultCacheEventListenerProvider.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/spi/event/DefaultCacheEventListenerProvider.java index 63c550731c..e9409a511e 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/spi/event/DefaultCacheEventListenerProvider.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/spi/event/DefaultCacheEventListenerProvider.java @@ -25,7 +25,7 @@ /** * @author rism */ -public class DefaultCacheEventListenerProvider extends ClassInstanceProvider> implements CacheEventListenerProvider { +public class DefaultCacheEventListenerProvider extends ClassInstanceProvider> implements CacheEventListenerProvider { public DefaultCacheEventListenerProvider() { super(null, DefaultCacheEventListenerConfiguration.class); @@ -33,7 +33,7 @@ public DefaultCacheEventListenerProvider() { @SuppressWarnings("unchecked") @Override - public CacheEventListener createEventListener(String alias, ServiceConfiguration serviceConfiguration) { + public CacheEventListener createEventListener(String alias, ServiceConfiguration serviceConfiguration) { return (CacheEventListener) newInstance(alias, serviceConfiguration); } @@ -41,4 +41,4 @@ public CacheEventListener createEventListener(String alias, Service public void releaseEventListener(CacheEventListener cacheEventListener) throws Exception { releaseInstance(cacheEventListener); } -} \ No newline at end of file +} diff --git a/impl/src/main/java/org/ehcache/impl/internal/spi/event/DefaultCacheEventListenerProviderFactory.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/spi/event/DefaultCacheEventListenerProviderFactory.java similarity index 76% rename from impl/src/main/java/org/ehcache/impl/internal/spi/event/DefaultCacheEventListenerProviderFactory.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/spi/event/DefaultCacheEventListenerProviderFactory.java index 5a419fe4c9..6545a34b7a 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/spi/event/DefaultCacheEventListenerProviderFactory.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/spi/event/DefaultCacheEventListenerProviderFactory.java @@ -17,21 +17,23 @@ package org.ehcache.impl.internal.spi.event; import org.ehcache.core.events.CacheEventListenerProvider; -import org.ehcache.spi.service.ServiceCreationConfiguration; import org.ehcache.core.spi.service.ServiceFactory; +import org.ehcache.spi.service.ServiceCreationConfiguration; +import org.osgi.service.component.annotations.Component; /** * @author rism */ +@Component public class DefaultCacheEventListenerProviderFactory implements ServiceFactory { @Override - public DefaultCacheEventListenerProvider create(ServiceCreationConfiguration configuration) { + public CacheEventListenerProvider create(ServiceCreationConfiguration configuration) { return new DefaultCacheEventListenerProvider(); } @Override - public Class getServiceType() { - return CacheEventListenerProvider.class; + public Class getServiceType() { + return DefaultCacheEventListenerProvider.class; } -} \ No newline at end of file +} diff --git a/impl/src/main/java/org/ehcache/impl/internal/spi/loaderwriter/DefaultCacheLoaderWriterProvider.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/spi/loaderwriter/DefaultCacheLoaderWriterProvider.java similarity index 90% rename from impl/src/main/java/org/ehcache/impl/internal/spi/loaderwriter/DefaultCacheLoaderWriterProvider.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/spi/loaderwriter/DefaultCacheLoaderWriterProvider.java index 80871b9435..1c1682b805 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/spi/loaderwriter/DefaultCacheLoaderWriterProvider.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/spi/loaderwriter/DefaultCacheLoaderWriterProvider.java @@ -30,7 +30,7 @@ /** * @author Alex Snaps */ -public class DefaultCacheLoaderWriterProvider extends ClassInstanceProvider> implements CacheLoaderWriterProvider { +public class DefaultCacheLoaderWriterProvider extends ClassInstanceProvider> implements CacheLoaderWriterProvider { private final Set cachesWithJsrRegisteredLoaders = new HashSet<>(); @@ -50,8 +50,8 @@ public void releaseCacheLoaderWriter(String alias, CacheLoaderWriter cache } @Override - public CacheLoaderWriterConfiguration getPreConfiguredCacheLoaderWriterConfig(String alias) { - return (CacheLoaderWriterConfiguration) getPreconfigured(alias); + public CacheLoaderWriterConfiguration getPreConfiguredCacheLoaderWriterConfig(String alias) { + return getPreconfigured(alias); } @Override diff --git a/impl/src/main/java/org/ehcache/impl/internal/spi/loaderwriter/DefaultCacheLoaderWriterProviderFactory.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/spi/loaderwriter/DefaultCacheLoaderWriterProviderFactory.java similarity index 82% rename from impl/src/main/java/org/ehcache/impl/internal/spi/loaderwriter/DefaultCacheLoaderWriterProviderFactory.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/spi/loaderwriter/DefaultCacheLoaderWriterProviderFactory.java index e99a445507..475c7df4a3 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/spi/loaderwriter/DefaultCacheLoaderWriterProviderFactory.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/spi/loaderwriter/DefaultCacheLoaderWriterProviderFactory.java @@ -16,19 +16,20 @@ package org.ehcache.impl.internal.spi.loaderwriter; +import org.ehcache.core.spi.service.ServiceFactory; import org.ehcache.impl.config.loaderwriter.DefaultCacheLoaderWriterProviderConfiguration; -import org.ehcache.impl.internal.spi.loaderwriter.DefaultCacheLoaderWriterProvider; import org.ehcache.spi.loaderwriter.CacheLoaderWriterProvider; import org.ehcache.spi.service.ServiceCreationConfiguration; -import org.ehcache.core.spi.service.ServiceFactory; +import org.osgi.service.component.annotations.Component; /** * @author Alex Snaps */ +@Component public class DefaultCacheLoaderWriterProviderFactory implements ServiceFactory { @Override - public DefaultCacheLoaderWriterProvider create(ServiceCreationConfiguration configuration) { + public CacheLoaderWriterProvider create(ServiceCreationConfiguration configuration) { if (configuration != null && !(configuration instanceof DefaultCacheLoaderWriterProviderConfiguration)) { throw new IllegalArgumentException("Expected a configuration of type DefaultCacheLoaderWriterProviderConfiguration but got " + configuration .getClass() @@ -38,7 +39,7 @@ public DefaultCacheLoaderWriterProvider create(ServiceCreationConfiguration getServiceType() { - return CacheLoaderWriterProvider.class; + public Class getServiceType() { + return DefaultCacheLoaderWriterProvider.class; } } diff --git a/impl/src/main/java/org/ehcache/impl/internal/spi/resilience/DefaultResilienceStrategyProvider.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/spi/resilience/DefaultResilienceStrategyProvider.java similarity index 90% rename from impl/src/main/java/org/ehcache/impl/internal/spi/resilience/DefaultResilienceStrategyProvider.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/spi/resilience/DefaultResilienceStrategyProvider.java index 956a99b755..0807f32cad 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/spi/resilience/DefaultResilienceStrategyProvider.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/spi/resilience/DefaultResilienceStrategyProvider.java @@ -18,9 +18,7 @@ import org.ehcache.config.CacheConfiguration; import org.ehcache.impl.config.resilience.DefaultResilienceStrategyConfiguration; import org.ehcache.impl.config.resilience.DefaultResilienceStrategyProviderConfiguration; -import org.ehcache.impl.internal.classes.ClassInstanceConfiguration; import org.ehcache.impl.internal.classes.ClassInstanceProvider; -import org.ehcache.impl.internal.classes.ClassInstanceProviderConfiguration; import org.ehcache.spi.loaderwriter.CacheLoaderWriter; import org.ehcache.spi.resilience.RecoveryStore; import org.ehcache.spi.resilience.ResilienceStrategy; @@ -82,11 +80,11 @@ public void stop() { } } - static class ComponentProvider extends ClassInstanceProvider> { + static class ComponentProvider extends ClassInstanceProvider> { private DefaultResilienceStrategyConfiguration defaultConfiguration; - protected ComponentProvider(DefaultResilienceStrategyConfiguration dflt, ClassInstanceProviderConfiguration> factoryConfig) { + protected ComponentProvider(DefaultResilienceStrategyConfiguration dflt, DefaultResilienceStrategyProviderConfiguration factoryConfig) { super(factoryConfig, DefaultResilienceStrategyConfiguration.class); this.defaultConfiguration = dflt; } @@ -95,7 +93,7 @@ protected ComponentProvider(DefaultResilienceStrategyConfiguration dflt, ClassIn public ResilienceStrategy create(String alias, DefaultResilienceStrategyConfiguration config, RecoveryStore recoveryStore, CacheLoaderWriter loaderWriter) { if (config == null) { - DefaultResilienceStrategyConfiguration preconfigured = (DefaultResilienceStrategyConfiguration) getPreconfigured(alias); + DefaultResilienceStrategyConfiguration preconfigured = getPreconfigured(alias); if (preconfigured == null) { return (ResilienceStrategy) newInstance(alias, defaultConfiguration.bind(recoveryStore, loaderWriter)); } else { @@ -109,7 +107,7 @@ public ResilienceStrategy create(String alias, DefaultResilienceStr @SuppressWarnings("unchecked") public ResilienceStrategy create(String alias, DefaultResilienceStrategyConfiguration config, RecoveryStore recoveryStore) { if (config == null) { - DefaultResilienceStrategyConfiguration preconfigured = (DefaultResilienceStrategyConfiguration) getPreconfigured(alias); + DefaultResilienceStrategyConfiguration preconfigured = getPreconfigured(alias); if (preconfigured == null) { return (ResilienceStrategy) newInstance(alias, defaultConfiguration.bind(recoveryStore)); } else { diff --git a/impl/src/main/java/org/ehcache/impl/internal/spi/resilience/DefaultResilienceStrategyProviderFactory.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/spi/resilience/DefaultResilienceStrategyProviderFactory.java similarity index 86% rename from impl/src/main/java/org/ehcache/impl/internal/spi/resilience/DefaultResilienceStrategyProviderFactory.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/spi/resilience/DefaultResilienceStrategyProviderFactory.java index f09c0d1352..e0f74d0bdb 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/spi/resilience/DefaultResilienceStrategyProviderFactory.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/spi/resilience/DefaultResilienceStrategyProviderFactory.java @@ -19,10 +19,12 @@ import org.ehcache.impl.config.resilience.DefaultResilienceStrategyProviderConfiguration; import org.ehcache.spi.resilience.ResilienceStrategyProvider; import org.ehcache.spi.service.ServiceCreationConfiguration; +import org.osgi.service.component.annotations.Component; +@Component public class DefaultResilienceStrategyProviderFactory implements ServiceFactory { @Override - public ResilienceStrategyProvider create(ServiceCreationConfiguration configuration) { + public ResilienceStrategyProvider create(ServiceCreationConfiguration configuration) { if (configuration == null) { return new DefaultResilienceStrategyProvider(); } else if (configuration instanceof DefaultResilienceStrategyProviderConfiguration) { @@ -35,8 +37,8 @@ public ResilienceStrategyProvider create(ServiceCreationConfiguration getServiceType() { - return ResilienceStrategyProvider.class; + public Class getServiceType() { + return DefaultResilienceStrategyProvider.class; } } diff --git a/impl/src/main/java/org/ehcache/impl/internal/spi/serialization/DefaultSerializationProvider.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/spi/serialization/DefaultSerializationProvider.java similarity index 96% rename from impl/src/main/java/org/ehcache/impl/internal/spi/serialization/DefaultSerializationProvider.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/spi/serialization/DefaultSerializationProvider.java index 38e16d1c90..e5bfa39a3a 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/spi/serialization/DefaultSerializationProvider.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/spi/serialization/DefaultSerializationProvider.java @@ -71,7 +71,7 @@ public DefaultSerializationProvider(DefaultSerializationProviderConfiguration co } @Override - public Serializer createKeySerializer(Class clazz, ClassLoader classLoader, ServiceConfiguration... configs) throws UnsupportedTypeException { + public Serializer createKeySerializer(Class clazz, ClassLoader classLoader, ServiceConfiguration... configs) throws UnsupportedTypeException { DefaultSerializerConfiguration configuration = find(DefaultSerializerConfiguration.Type.KEY, configs); Serializer serializer = getUserProvidedSerializer(configuration); if (serializer == null) { @@ -83,7 +83,7 @@ public Serializer createKeySerializer(Class clazz, ClassLoader classLo } @Override - public Serializer createValueSerializer(Class clazz, ClassLoader classLoader, ServiceConfiguration... configs) throws UnsupportedTypeException { + public Serializer createValueSerializer(Class clazz, ClassLoader classLoader, ServiceConfiguration... configs) throws UnsupportedTypeException { DefaultSerializerConfiguration configuration = find(DefaultSerializerConfiguration.Type.VALUE, configs); Serializer serializer = getUserProvidedSerializer(configuration); if (serializer == null) { @@ -94,7 +94,7 @@ public Serializer createValueSerializer(Class clazz, ClassLoader class return serializer; } - private Serializer createSerializer(Class clazz, ClassLoader classLoader, DefaultSerializerConfiguration config, ServiceConfiguration... configs) throws UnsupportedTypeException { + private Serializer createSerializer(Class clazz, ClassLoader classLoader, DefaultSerializerConfiguration config, ServiceConfiguration... configs) throws UnsupportedTypeException { Class> klazz = getSerializerClassFor(clazz, config); try { @@ -208,7 +208,7 @@ private static Serializer getUserProvidedSerializer(DefaultSerializerConf } @SuppressWarnings("unchecked") - private static DefaultSerializerConfiguration find(DefaultSerializerConfiguration.Type type, ServiceConfiguration... serviceConfigurations) { + private static DefaultSerializerConfiguration find(DefaultSerializerConfiguration.Type type, ServiceConfiguration... serviceConfigurations) { DefaultSerializerConfiguration result = null; @SuppressWarnings("rawtypes") diff --git a/impl/src/main/java/org/ehcache/impl/internal/spi/serialization/DefaultSerializationProviderFactory.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/spi/serialization/DefaultSerializationProviderFactory.java similarity index 86% rename from impl/src/main/java/org/ehcache/impl/internal/spi/serialization/DefaultSerializationProviderFactory.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/spi/serialization/DefaultSerializationProviderFactory.java index a738fee32a..19f85eb43a 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/spi/serialization/DefaultSerializationProviderFactory.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/spi/serialization/DefaultSerializationProviderFactory.java @@ -16,19 +16,20 @@ package org.ehcache.impl.internal.spi.serialization; +import org.ehcache.core.spi.service.ServiceFactory; import org.ehcache.impl.config.serializer.DefaultSerializationProviderConfiguration; -import org.ehcache.impl.internal.spi.serialization.DefaultSerializationProvider; import org.ehcache.spi.serialization.SerializationProvider; import org.ehcache.spi.service.ServiceCreationConfiguration; -import org.ehcache.core.spi.service.ServiceFactory; +import org.osgi.service.component.annotations.Component; /** * @author Ludovic Orban */ +@Component public class DefaultSerializationProviderFactory implements ServiceFactory { @Override - public DefaultSerializationProvider create(ServiceCreationConfiguration configuration) { + public DefaultSerializationProvider create(ServiceCreationConfiguration configuration) { if (configuration != null && !(configuration instanceof DefaultSerializationProviderConfiguration)) { throw new IllegalArgumentException("Expected a configuration of type DefaultSerializationProviderConfiguration but got " + configuration .getClass() @@ -38,7 +39,7 @@ public DefaultSerializationProvider create(ServiceCreationConfiguration getServiceType() { - return SerializationProvider.class; + public Class getServiceType() { + return DefaultSerializationProvider.class; } } diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/BinaryValueHolder.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/BinaryValueHolder.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/store/BinaryValueHolder.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/BinaryValueHolder.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/basic/NopStore.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/basic/NopStore.java similarity index 94% rename from impl/src/main/java/org/ehcache/impl/internal/store/basic/NopStore.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/basic/NopStore.java index 841a31cf62..8ff6b2af6c 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/store/basic/NopStore.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/basic/NopStore.java @@ -27,6 +27,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Set; import java.util.function.BiFunction; import java.util.function.Consumer; @@ -83,7 +84,7 @@ public PutStatus put(K key, V value) throws StoreAccessException { @Override public ValueHolder putIfAbsent(K key, V value, Consumer put) throws StoreAccessException { - return EmptyValueHolder.empty(); + return null; } @Override @@ -134,6 +135,11 @@ public void setEventOrdering(boolean ordering) { } + @Override + public void setSynchronous(boolean synchronous) throws IllegalArgumentException { + + } + @Override public boolean isEventOrdering() { return false; @@ -151,14 +157,14 @@ public boolean hasNext() { @Override public Cache.Entry> next() { - return null; + throw new NoSuchElementException(); } }; } @Override public ValueHolder getAndCompute(K key, BiFunction mappingFunction) { - return EmptyValueHolder.empty(); + return null; } @Override @@ -180,7 +186,7 @@ public Map> bulkCompute(Set keys, Function> bulkCompute(Set keys, Function>, Iterable>> remappingFunction, Supplier replaceEqual) { Map> map = new HashMap<>(keys.size()); for(K key : keys) { - map.put(key, EmptyValueHolder.empty()); + map.put(key, null); } return map; } @@ -189,7 +195,7 @@ public Map> bulkCompute(Set keys, Function> bulkComputeIfAbsent(Set keys, Function, Iterable>> mappingFunction) { Map> map = new HashMap<>(keys.size()); for(K key : keys) { - map.put(key, EmptyValueHolder.empty()); + map.put(key, null); } return map; } diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/disk/DiskWriteThreadPool.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/disk/DiskWriteThreadPool.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/store/disk/DiskWriteThreadPool.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/disk/DiskWriteThreadPool.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/disk/EhcachePersistentConcurrentOffHeapClockCache.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/disk/EhcachePersistentConcurrentOffHeapClockCache.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/store/disk/EhcachePersistentConcurrentOffHeapClockCache.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/disk/EhcachePersistentConcurrentOffHeapClockCache.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStore.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStore.java similarity index 90% rename from impl/src/main/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStore.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStore.java index 2a01f96888..d1f91c5681 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStore.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStore.java @@ -22,6 +22,8 @@ import org.ehcache.config.EvictionAdvisor; import org.ehcache.config.ResourceType; import org.ehcache.core.spi.service.DiskResourceService; +import org.ehcache.core.spi.service.StatisticsService; +import org.ehcache.core.statistics.OperationStatistic; import org.ehcache.impl.config.store.disk.OffHeapDiskStoreConfiguration; import org.ehcache.config.units.MemoryUnit; import org.ehcache.core.events.StoreEventDispatcher; @@ -32,13 +34,13 @@ import org.ehcache.impl.internal.store.offheap.EhcacheOffHeapBackingMap; import org.ehcache.impl.internal.store.offheap.SwitchableEvictionAdvisor; import org.ehcache.impl.internal.store.offheap.OffHeapValueHolder; -import org.ehcache.impl.internal.store.offheap.portability.OffHeapValueHolderPortability; import org.ehcache.impl.internal.store.offheap.portability.SerializerPortability; import org.ehcache.core.spi.time.TimeSource; import org.ehcache.core.spi.time.TimeSourceService; import org.ehcache.spi.persistence.PersistableResourceService.PersistenceSpaceIdentifier; import org.ehcache.spi.persistence.StateRepository; import org.ehcache.spi.serialization.StatefulSerializer; +import org.ehcache.spi.service.OptionalServiceDependencies; import org.ehcache.spi.service.ServiceProvider; import org.ehcache.core.spi.store.Store; import org.ehcache.core.spi.store.tiering.AuthoritativeTier; @@ -59,8 +61,6 @@ import org.terracotta.offheapstore.disk.storage.FileBackedStorageEngine; import org.terracotta.offheapstore.storage.portability.Portability; import org.terracotta.offheapstore.util.Factory; -import org.terracotta.statistics.OperationStatistic; -import org.terracotta.statistics.StatisticsManager; import java.io.File; import java.io.FileInputStream; @@ -69,9 +69,9 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Proxy; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Properties; @@ -82,6 +82,7 @@ import static java.lang.Math.max; import static org.ehcache.config.Eviction.noAdvice; import static org.ehcache.core.spi.service.ServiceUtils.findSingletonAmongst; +import static java.util.Arrays.asList; import static org.terracotta.offheapstore.util.MemoryUnit.BYTES; /** @@ -113,8 +114,8 @@ public class OffHeapDiskStore extends AbstractOffHeapStore implement public OffHeapDiskStore(FileBasedPersistenceContext fileBasedPersistenceContext, ExecutionService executionService, String threadPoolAlias, int writerConcurrency, int diskSegments, - final Configuration config, TimeSource timeSource, StoreEventDispatcher eventDispatcher, long sizeInBytes) { - super(config, timeSource, eventDispatcher); + final Configuration config, TimeSource timeSource, StoreEventDispatcher eventDispatcher, long sizeInBytes, StatisticsService statisticsService) { + super(config, timeSource, eventDispatcher, statisticsService); this.fileBasedPersistenceContext = fileBasedPersistenceContext; this.executionService = executionService; this.threadPoolAlias = threadPoolAlias; @@ -215,11 +216,11 @@ private EhcachePersistentConcurrentOffHeapClockCache> r MappedPageSource source = new MappedPageSource(dataFile, false, size); try { PersistentPortability keyPortability = persistent(new SerializerPortability<>(keySerializer)); - PersistentPortability> elementPortability = persistent(new OffHeapValueHolderPortability<>(valueSerializer)); + PersistentPortability> valuePortability = persistent(createValuePortability(valueSerializer)); DiskWriteThreadPool writeWorkers = new DiskWriteThreadPool(executionService, threadPoolAlias, writerConcurrency); Factory>> storageEngineFactory = FileBackedStorageEngine.createFactory(source, - max((size / diskSegments) / 10, 1024), BYTES, keyPortability, elementPortability, writeWorkers, false); + max((size / diskSegments) / 10, 1024), BYTES, keyPortability, valuePortability, writeWorkers, false); EhcachePersistentSegmentFactory> factory = new EhcachePersistentSegmentFactory<>( source, @@ -253,11 +254,11 @@ private EhcachePersistentConcurrentOffHeapClockCache> c MappedPageSource source = new MappedPageSource(getDataFile(), size); PersistentPortability keyPortability = persistent(new SerializerPortability<>(keySerializer)); - PersistentPortability> elementPortability = persistent(new OffHeapValueHolderPortability<>(valueSerializer)); + PersistentPortability> valuePortability = persistent(createValuePortability(valueSerializer)); DiskWriteThreadPool writeWorkers = new DiskWriteThreadPool(executionService, threadPoolAlias, writerConcurrency); Factory>> storageEngineFactory = FileBackedStorageEngine.createFactory(source, - max((size / diskSegments) / 10, 1024), BYTES, keyPortability, elementPortability, writeWorkers, true); + max((size / diskSegments) / 10, 1024), BYTES, keyPortability, valuePortability, writeWorkers, true); EhcachePersistentSegmentFactory> factory = new EhcachePersistentSegmentFactory<>( source, @@ -297,7 +298,6 @@ public static class Provider extends BaseStoreProvider implements AuthoritativeT private final Map, OperationStatistic[]> tierOperationStatistics = new ConcurrentWeakIdentityHashMap<>(); private final Map, PersistenceSpaceIdentifier> createdStores = new ConcurrentWeakIdentityHashMap<>(); private final String defaultThreadPool; - private volatile ServiceProvider serviceProvider; private volatile DiskResourceService diskPersistenceService; public Provider() { @@ -314,17 +314,17 @@ protected ResourceType getResourceType() { } @Override - public int rank(final Set> resourceTypes, final Collection> serviceConfigs) { + public int rank(final Set> resourceTypes, final Collection> serviceConfigs) { return resourceTypes.equals(Collections.singleton(getResourceType())) ? 1 : 0; } @Override - public int rankAuthority(ResourceType authorityResource, Collection> serviceConfigs) { + public int rankAuthority(ResourceType authorityResource, Collection> serviceConfigs) { return authorityResource.equals(getResourceType()) ? 1 : 0; } @Override - public OffHeapDiskStore createStore(Configuration storeConfig, ServiceConfiguration... serviceConfigs) { + public OffHeapDiskStore createStore(Configuration storeConfig, ServiceConfiguration... serviceConfigs) { OffHeapDiskStore store = createStoreInternal(storeConfig, new ThreadLocalStoreEventDispatcher<>(storeConfig.getDispatcherConcurrency()), serviceConfigs); tierOperationStatistics.put(store, new OperationStatistic[] { @@ -335,12 +335,12 @@ public OffHeapDiskStore createStore(Configuration storeConfig return store; } - private OffHeapDiskStore createStoreInternal(Configuration storeConfig, StoreEventDispatcher eventDispatcher, ServiceConfiguration... serviceConfigs) { - if (serviceProvider == null) { + private OffHeapDiskStore createStoreInternal(Configuration storeConfig, StoreEventDispatcher eventDispatcher, ServiceConfiguration... serviceConfigs) { + if (getServiceProvider() == null) { throw new NullPointerException("ServiceProvider is null in OffHeapDiskStore.Provider."); } - TimeSource timeSource = serviceProvider.getService(TimeSourceService.class).getTimeSource(); - ExecutionService executionService = serviceProvider.getService(ExecutionService.class); + TimeSource timeSource = getServiceProvider().getService(TimeSourceService.class).getTimeSource(); + ExecutionService executionService = getServiceProvider().getService(ExecutionService.class); SizedResourcePool diskPool = storeConfig.getResourcePools().getPoolForResource(getResourceType()); if (!(diskPool.getUnit() instanceof MemoryUnit)) { @@ -370,7 +370,7 @@ private OffHeapDiskStore createStoreInternal(Configuration st OffHeapDiskStore offHeapStore = new OffHeapDiskStore<>(persistenceContext, executionService, threadPoolAlias, writerConcurrency, diskSegments, - storeConfig, timeSource, eventDispatcher, unit.toBytes(diskPool.getSize())); + storeConfig, timeSource, eventDispatcher, unit.toBytes(diskPool.getSize()), getServiceProvider().getService(StatisticsService.class)); createdStores.put(offHeapStore, space); return offHeapStore; } catch (CachePersistenceException cpex) { @@ -386,7 +386,7 @@ public void releaseStore(Store resource) { try { OffHeapDiskStore offHeapDiskStore = (OffHeapDiskStore)resource; close(offHeapDiskStore); - StatisticsManager.nodeFor(offHeapDiskStore).clean(); + getStatisticsService().ifPresent(s -> s.cleanForNode(offHeapDiskStore)); tierOperationStatistics.remove(offHeapDiskStore); } catch (IOException e) { throw new RuntimeException(e); @@ -444,7 +444,7 @@ static void init(final OffHeapDiskStore resource) { @Override public void start(ServiceProvider serviceProvider) { - this.serviceProvider = serviceProvider; + super.start(serviceProvider); diskPersistenceService = serviceProvider.getService(DiskResourceService.class); if (diskPersistenceService == null) { throw new IllegalStateException("Unable to find file based persistence service"); @@ -453,13 +453,16 @@ public void start(ServiceProvider serviceProvider) { @Override public void stop() { - this.serviceProvider = null; - createdStores.clear(); - diskPersistenceService = null; + try { + createdStores.clear(); + diskPersistenceService = null; + } finally { + super.stop(); + } } @Override - public AuthoritativeTier createAuthoritativeTier(Configuration storeConfig, ServiceConfiguration... serviceConfigs) { + public AuthoritativeTier createAuthoritativeTier(Configuration storeConfig, ServiceConfiguration... serviceConfigs) { OffHeapDiskStore authoritativeTier = createStoreInternal(storeConfig, new ThreadLocalStoreEventDispatcher<>(storeConfig .getDispatcherConcurrency()), serviceConfigs); @@ -488,18 +491,23 @@ public void initAuthoritativeTier(AuthoritativeTier resource) { */ @SuppressWarnings("unchecked") public static PersistentPortability persistent(final Portability normal) { - final Class normalKlazz = normal.getClass(); - Class[] delegateInterfaces = normalKlazz.getInterfaces(); - Class[] proxyInterfaces = Arrays.copyOf(delegateInterfaces, delegateInterfaces.length + 1); - proxyInterfaces[delegateInterfaces.length] = PersistentPortability.class; - - return (PersistentPortability) Proxy.newProxyInstance(normal.getClass().getClassLoader(), proxyInterfaces, (o, method, os) -> { - if (method.getDeclaringClass().equals(Persistent.class)) { - return null; - } else { - return method.invoke(normal, os); + if (normal instanceof PersistentPortability) { + return (PersistentPortability) normal; + } else { + LinkedHashSet> proxyInterfaces = new LinkedHashSet<>(); + for (Class klazz = normal.getClass(); klazz != null; klazz = klazz.getSuperclass()) { + proxyInterfaces.addAll(asList(klazz.getInterfaces())); } - }); + proxyInterfaces.add(PersistentPortability.class); + + return (PersistentPortability) Proxy.newProxyInstance(normal.getClass().getClassLoader(), proxyInterfaces.toArray(new Class[0]), (o, method, os) -> { + if (method.getDeclaringClass().equals(Persistent.class)) { + return null; + } else { + return method.invoke(normal, os); + } + }); + } } String getThreadPoolAlias() { diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStoreProviderFactory.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStoreProviderFactory.java similarity index 88% rename from impl/src/main/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStoreProviderFactory.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStoreProviderFactory.java index b259debf1a..b7d4d4b8ca 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStoreProviderFactory.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStoreProviderFactory.java @@ -16,17 +16,19 @@ package org.ehcache.impl.internal.store.disk; +import org.ehcache.core.spi.service.ServiceFactory; import org.ehcache.impl.config.store.disk.OffHeapDiskStoreProviderConfiguration; import org.ehcache.spi.service.ServiceCreationConfiguration; -import org.ehcache.core.spi.service.ServiceFactory; +import org.osgi.service.component.annotations.Component; /** * @author Chris Dennis */ +@Component public class OffHeapDiskStoreProviderFactory implements ServiceFactory { @Override - public OffHeapDiskStore.Provider create(ServiceCreationConfiguration configuration) { + public OffHeapDiskStore.Provider create(ServiceCreationConfiguration configuration) { if (configuration == null) { return new OffHeapDiskStore.Provider(); } else if (configuration instanceof OffHeapDiskStoreProviderConfiguration) { @@ -37,7 +39,7 @@ public OffHeapDiskStore.Provider create(ServiceCreationConfiguration getServiceType() { + public Class getServiceType() { return OffHeapDiskStore.Provider.class; } } diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/disk/factories/EhcachePersistentSegmentFactory.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/disk/factories/EhcachePersistentSegmentFactory.java similarity index 85% rename from impl/src/main/java/org/ehcache/impl/internal/store/disk/factories/EhcachePersistentSegmentFactory.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/disk/factories/EhcachePersistentSegmentFactory.java index 11828ce2ae..d8bd571a3a 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/store/disk/factories/EhcachePersistentSegmentFactory.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/disk/factories/EhcachePersistentSegmentFactory.java @@ -16,6 +16,7 @@ package org.ehcache.impl.internal.store.disk.factories; +import org.ehcache.core.spi.store.Store; import org.ehcache.impl.internal.store.offheap.SwitchableEvictionAdvisor; import org.ehcache.impl.internal.store.offheap.factories.EhcacheSegmentFactory.EhcacheSegment; import org.ehcache.impl.internal.store.offheap.factories.EhcacheSegmentFactory.EhcacheSegment.EvictionListener; @@ -26,6 +27,10 @@ import org.terracotta.offheapstore.pinning.PinnableSegment; import org.terracotta.offheapstore.util.Factory; +import java.nio.IntBuffer; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; import java.util.concurrent.locks.Lock; import static org.ehcache.impl.internal.store.offheap.factories.EhcacheSegmentFactory.EhcacheSegment.ADVISED_AGAINST_EVICTION; @@ -111,5 +116,29 @@ public boolean evict(int index, boolean shrink) { lock.unlock(); } } + + @Override + protected Set> createEntrySet() { + return new EntrySet(); + } + + private class EntrySet extends LockedEntrySet { + @Override + public Iterator> iterator() { + readLock().lock(); + try { + return new LockedEntryIterator() { + @Override + protected Map.Entry create(IntBuffer entry) { + Map.Entry entryObject = super.create(entry); + ((Store.ValueHolder) entryObject.getValue()).get(); + return entryObject; + } + }; + } finally { + readLock().unlock(); + } + } + } } } diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/heap/Backend.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/heap/Backend.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/store/heap/Backend.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/heap/Backend.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/heap/KeyCopyBackend.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/heap/KeyCopyBackend.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/store/heap/KeyCopyBackend.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/heap/KeyCopyBackend.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/heap/OnHeapStore.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/heap/OnHeapStore.java similarity index 89% rename from impl/src/main/java/org/ehcache/impl/internal/store/heap/OnHeapStore.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/heap/OnHeapStore.java index 01be072a1a..a1a6423b09 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/store/heap/OnHeapStore.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/heap/OnHeapStore.java @@ -29,8 +29,12 @@ import org.ehcache.core.config.ExpiryUtils; import org.ehcache.core.events.StoreEventDispatcher; import org.ehcache.core.events.StoreEventSink; +import org.ehcache.core.spi.service.StatisticsService; +import org.ehcache.core.statistics.StatisticType; +import org.ehcache.core.statistics.OperationObserver; +import org.ehcache.core.statistics.OperationStatistic; import org.ehcache.impl.internal.concurrent.EvictingConcurrentMap; -import org.ehcache.impl.internal.store.basic.BaseStore; +import org.ehcache.impl.store.BaseStore; import org.ehcache.spi.resilience.StoreAccessException; import org.ehcache.core.spi.store.heap.LimitExceededException; import org.ehcache.expiry.ExpiryPolicy; @@ -38,7 +42,7 @@ import org.ehcache.impl.internal.concurrent.ConcurrentHashMap; import org.ehcache.impl.copy.SerializingCopier; import org.ehcache.core.events.NullStoreEventDispatcher; -import org.ehcache.impl.internal.events.ScopedStoreEventDispatcher; +import org.ehcache.impl.store.DefaultStoreEventDispatcher; import org.ehcache.impl.internal.sizeof.NoopSizeOfEngine; import org.ehcache.impl.internal.store.heap.holders.CopiedOnHeapValueHolder; import org.ehcache.impl.internal.store.heap.holders.OnHeapValueHolder; @@ -50,7 +54,6 @@ import org.ehcache.sizeof.annotations.IgnoreSizeOf; import org.ehcache.spi.serialization.Serializer; import org.ehcache.spi.serialization.StatefulSerializer; -import org.ehcache.spi.service.ServiceProvider; import org.ehcache.core.spi.store.Store; import org.ehcache.core.spi.store.events.StoreEventSource; import org.ehcache.core.spi.store.tiering.CachingTier; @@ -58,7 +61,7 @@ import org.ehcache.impl.internal.store.BinaryValueHolder; import org.ehcache.spi.copy.Copier; import org.ehcache.spi.copy.CopyProvider; -import org.ehcache.spi.service.Service; +import org.ehcache.spi.service.OptionalServiceDependencies; import org.ehcache.spi.service.ServiceConfiguration; import org.ehcache.spi.service.ServiceDependencies; import org.ehcache.core.spi.store.heap.SizeOfEngine; @@ -70,9 +73,6 @@ import org.ehcache.core.statistics.TierOperationOutcomes; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.terracotta.statistics.OperationStatistic; -import org.terracotta.statistics.StatisticsManager; -import org.terracotta.statistics.observer.OperationObserver; import java.time.Duration; import java.util.ArrayList; @@ -85,10 +85,10 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.NoSuchElementException; import java.util.Objects; import java.util.Random; import java.util.Set; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiFunction; @@ -99,8 +99,6 @@ import static org.ehcache.config.Eviction.noAdvice; import static org.ehcache.core.config.ExpiryUtils.isExpiryDurationInfinite; import static org.ehcache.core.exceptions.StorePassThroughException.handleException; -import static org.terracotta.statistics.StatisticType.COUNTER; -import static org.terracotta.statistics.StatisticType.GAUGE; /** * {@link Store} and {@link HigherCachingTier} implementation for on heap. @@ -135,7 +133,7 @@ public class OnHeapStore extends BaseStore implements HigherCachingT } else if (u instanceof Fault) { return 1; } else { - return Long.signum(u.lastAccessTime(TimeUnit.NANOSECONDS) - t.lastAccessTime(TimeUnit.NANOSECONDS)); + return Long.signum(u.lastAccessTime() - t.lastAccessTime()); } }; @@ -149,6 +147,7 @@ public class OnHeapStore extends BaseStore implements HigherCachingT private final Copier valueCopier; private final SizeOfEngine sizeOfEngine; + private final OnHeapStrategy strategy; private volatile long capacity; private final EvictionAdvisor evictionAdvisor; @@ -200,13 +199,13 @@ public void cacheConfigurationChange(CacheConfigurationChangeEvent event) { private static final Supplier REPLACE_EQUALS_TRUE = () -> Boolean.TRUE; - public OnHeapStore(Configuration config, TimeSource timeSource, Copier keyCopier, Copier valueCopier, SizeOfEngine sizeOfEngine, StoreEventDispatcher eventDispatcher) { - this(config, timeSource, keyCopier, valueCopier, sizeOfEngine, eventDispatcher, ConcurrentHashMap::new); + public OnHeapStore(Configuration config, TimeSource timeSource, Copier keyCopier, Copier valueCopier, SizeOfEngine sizeOfEngine, StoreEventDispatcher eventDispatcher, StatisticsService statisticsService) { + this(config, timeSource, keyCopier, valueCopier, sizeOfEngine, eventDispatcher, ConcurrentHashMap::new, statisticsService); } public OnHeapStore(Configuration config, TimeSource timeSource, Copier keyCopier, Copier valueCopier, - SizeOfEngine sizeOfEngine, StoreEventDispatcher eventDispatcher, Supplier> backingMapSupplier) { - super(config); + SizeOfEngine sizeOfEngine, StoreEventDispatcher eventDispatcher, Supplier> backingMapSupplier, StatisticsService statisticsService) { + super(config, statisticsService); Objects.requireNonNull(keyCopier, "keyCopier must not be null"); @@ -236,6 +235,8 @@ public OnHeapStore(Configuration config, TimeSource timeSource, Copier this.map = new KeyCopyBackend<>(byteSized, keyCopier, castBackend(backingMapSupplier)); } + strategy = OnHeapStrategy.strategy(this, expiry, timeSource); + getObserver = createObserver("get", StoreOperationOutcomes.GetOutcome.class, true); putObserver = createObserver("put", StoreOperationOutcomes.PutOutcome.class, true); removeObserver = createObserver("remove", StoreOperationOutcomes.RemoveOutcome.class, true); @@ -258,9 +259,9 @@ public OnHeapStore(Configuration config, TimeSource timeSource, Copier silentInvalidateAllWithHashObserver = createObserver("silentInvalidateAllWithHash", HigherCachingTierOperationOutcomes.SilentInvalidateAllWithHashOutcome.class, true); Set tags = new HashSet<>(Arrays.asList(getStatisticsTag(), "tier")); - registerStatistic("mappings", COUNTER, tags, () -> map.mappingCount()); + registerStatistic("mappings", StatisticType.COUNTER, tags, () -> map.mappingCount()); if (byteSized) { - registerStatistic("occupiedMemory", GAUGE, tags, () -> map.byteSize()); + registerStatistic("occupiedMemory", StatisticType.GAUGE, tags, () -> map.byteSize()); } } @@ -287,7 +288,7 @@ public ValueHolder get(K key) throws StoreAccessException { return null; } - setAccessTimeAndExpiryThenReturnMappingOutsideLock(key, mapping, timeSource.getTimeMillis()); + strategy.setAccessAndExpiryTimeWhenCallerOutsideLock(key, mapping, timeSource.getTimeMillis()); getObserver.end(StoreOperationOutcomes.GetOutcome.HIT); return mapping; @@ -303,7 +304,7 @@ private OnHeapValueHolder getQuiet(K key) throws StoreAccessException { return null; } - if (mapping.isExpired(timeSource.getTimeMillis(), TimeUnit.MILLISECONDS)) { + if (strategy.isExpired(mapping)) { expireMappingUnderLock(key, mapping); return null; } @@ -335,7 +336,7 @@ public PutStatus put(K key, V value) throws StoreAccessException { long delta = 0; - if (mappedValue != null && mappedValue.isExpired(now, TimeUnit.MILLISECONDS)) { + if (mappedValue != null && mappedValue.isExpired(now)) { delta -= mappedValue.size(); mappedValue = null; } @@ -396,7 +397,7 @@ public boolean remove(K key) throws StoreAccessException { map.computeIfPresent(key, (mappedKey, mappedValue) -> { updateUsageInBytesIfRequired(- mappedValue.size()); - if (mappedValue.isExpired(now, TimeUnit.MILLISECONDS)) { + if (mappedValue.isExpired(now)) { fireOnExpirationEvent(mappedKey, mappedValue, eventSink); return null; } @@ -440,7 +441,7 @@ public ValueHolder putIfAbsent(K key, V value, Consumer put) throws OnHeapValueHolder holder; - if (mappedValue == null || mappedValue.isExpired(now, TimeUnit.MILLISECONDS)) { + if (mappedValue == null || mappedValue.isExpired(now)) { if (mappedValue != null) { delta -= mappedValue.size(); fireOnExpirationEvent(mappedKey, mappedValue, eventSink); @@ -453,7 +454,7 @@ public ValueHolder putIfAbsent(K key, V value, Consumer put) throws entryActuallyAdded.set(holder != null); } else { returnValue.set(mappedValue); - holder = setAccessTimeAndExpiryThenReturnMappingUnderLock(key, mappedValue, now, eventSink); + holder = strategy.setAccessAndExpiryWhenCallerlUnderLock(key, mappedValue, now, eventSink); if (holder == null) { delta -= mappedValue.size(); } @@ -494,7 +495,7 @@ public RemoveStatus remove(K key, V value) throws StoreAccessException { map.computeIfPresent(key, (mappedKey, mappedValue) -> { long now = timeSource.getTimeMillis(); - if (mappedValue.isExpired(now, TimeUnit.MILLISECONDS)) { + if (mappedValue.isExpired(now)) { updateUsageInBytesIfRequired(- mappedValue.size()); fireOnExpirationEvent(mappedKey, mappedValue, eventSink); return null; @@ -505,7 +506,7 @@ public RemoveStatus remove(K key, V value) throws StoreAccessException { return null; } else { outcome.set(RemoveStatus.KEY_PRESENT); - OnHeapValueHolder holder = setAccessTimeAndExpiryThenReturnMappingUnderLock(key, mappedValue, now, eventSink); + OnHeapValueHolder holder = strategy.setAccessAndExpiryWhenCallerlUnderLock(key, mappedValue, now, eventSink); if (holder == null) { updateUsageInBytesIfRequired(- mappedValue.size()); } @@ -547,7 +548,7 @@ public ValueHolder replace(K key, V value) throws StoreAccessException { map.computeIfPresent(key, (mappedKey, mappedValue) -> { long now = timeSource.getTimeMillis(); - if (mappedValue.isExpired(now, TimeUnit.MILLISECONDS)) { + if (mappedValue.isExpired(now)) { updateUsageInBytesIfRequired(- mappedValue.size()); fireOnExpirationEvent(mappedKey, mappedValue, eventSink); return null; @@ -594,7 +595,7 @@ public ReplaceStatus replace(K key, V oldValue, V newValue) throws StoreAccessEx long now = timeSource.getTimeMillis(); V existingValue = mappedValue.get(); - if (mappedValue.isExpired(now, TimeUnit.MILLISECONDS)) { + if (mappedValue.isExpired(now)) { fireOnExpirationEvent(mappedKey, mappedValue, eventSink); updateUsageInBytesIfRequired(- mappedValue.size()); return null; @@ -609,7 +610,7 @@ public ReplaceStatus replace(K key, V oldValue, V newValue) throws StoreAccessEx return holder; } else { outcome.set(ReplaceStatus.MISS_PRESENT); - OnHeapValueHolder holder = setAccessTimeAndExpiryThenReturnMappingUnderLock(key, mappedValue, now, eventSink); + OnHeapValueHolder holder = strategy.setAccessAndExpiryWhenCallerlUnderLock(key, mappedValue, now, eventSink); if (holder == null) { updateUsageInBytesIfRequired(- mappedValue.size()); } @@ -644,29 +645,47 @@ public void clear() { @Override public Iterator>> iterator() { + java.util.Iterator>> iterator = map.entrySetIterator(); return new Iterator>>() { - private final java.util.Iterator>> it = map.entrySetIterator(); + private Cache.Entry> prefetched = advance(); @Override public boolean hasNext() { - return it.hasNext(); + return prefetched != null; } @Override - public Cache.Entry> next() { - Entry> next = it.next(); - K key = next.getKey(); - OnHeapValueHolder value = next.getValue(); - return new Cache.Entry>() { - @Override - public K getKey() { - return key; - } - @Override - public ValueHolder getValue() { - return value; + public Cache.Entry> next() throws StoreAccessException { + if (prefetched == null) { + throw new NoSuchElementException(); + } else { + Cache.Entry> next = prefetched; + prefetched = advance(); + return next; + } + } + + private Cache.Entry> advance() { + while (iterator.hasNext()) { + Entry> next = iterator.next(); + + if (strategy.isExpired(next.getValue())) { + expireMappingUnderLock(next.getKey(), next.getValue()); + } else { + return new Cache.Entry>() { + @Override + public K getKey() { + return next.getKey(); + } + + @Override + public ValueHolder getValue() { + return next.getValue(); + } + }; } - }; + } + return null; } }; } @@ -693,7 +712,7 @@ public ValueHolder getOrComputeIfAbsent(K key, Function> so // If we have a real value (not a fault), we make sure it is not expired // If yes, we remove it and ask the source just in case. If no, we return it (below) if (!(cachedValue instanceof Fault)) { - if (cachedValue.isExpired(now, TimeUnit.MILLISECONDS)) { + if (cachedValue.isExpired(now)) { expireMappingUnderLock(key, cachedValue); // On expiration, we might still be able to get a value from the fault. For instance, when a load-writer is used @@ -705,7 +724,7 @@ public ValueHolder getOrComputeIfAbsent(K key, Function> so } } else { - setAccessTimeAndExpiryThenReturnMappingOutsideLock(key, cachedValue, now); + strategy.setAccessAndExpiryTimeWhenCallerOutsideLock(key, cachedValue, now); } } @@ -718,6 +737,33 @@ public ValueHolder getOrComputeIfAbsent(K key, Function> so } } + @Override + public ValueHolder getOrDefault(K key, Function> source) throws StoreAccessException { + try { + Backend backEnd = map; + + // First try to find the value from heap + OnHeapValueHolder cachedValue = backEnd.get(key); + + if (cachedValue == null) { + return source.apply(key); + } else { + // If we have a real value (not a fault), we make sure it is not expired + if (!(cachedValue instanceof Fault)) { + if (cachedValue.isExpired(timeSource.getTimeMillis())) { + expireMappingUnderLock(key, cachedValue); + return null; + } + } + + // Return the value that we found in the cache (by getting the fault or just returning the plain value depending on what we found) + return getValue(cachedValue); + } + } catch (RuntimeException re) { + throw handleException(re); + } + } + private ValueHolder resolveFault(K key, Backend backEnd, long now, Fault fault) throws StoreAccessException { try { ValueHolder value = fault.getValueHolder(); @@ -753,7 +799,7 @@ private ValueHolder resolveFault(K key, Backend backEnd, long now, Faul ValueHolder p = getValue(invalidatedValue.get()); if (p != null) { - if (p.isExpired(now, TimeUnit.MILLISECONDS)) { + if (p.isExpired(now)) { getOrComputeIfAbsentObserver.end(CachingTierOperationOutcomes.GetOrComputeIfAbsentOutcome.FAULT_FAILED_MISS); return null; } @@ -1009,32 +1055,32 @@ public V get() { } @Override - public long creationTime(TimeUnit unit) { + public long creationTime() { throw new UnsupportedOperationException(); } @Override - public void setExpirationTime(long expirationTime, TimeUnit unit) { + public void setExpirationTime(long expirationTime) { throw new UnsupportedOperationException(); } @Override - public long expirationTime(TimeUnit unit) { + public long expirationTime() { throw new UnsupportedOperationException(); } @Override - public boolean isExpired(long expirationTime, TimeUnit unit) { + public boolean isExpired(long expirationTime) { throw new UnsupportedOperationException(); } @Override - public long lastAccessTime(TimeUnit unit) { + public long lastAccessTime() { return Long.MAX_VALUE; } @Override - public void setLastAccessTime(long lastAccessTime, TimeUnit unit) { + public void setLastAccessTime(long lastAccessTime) { throw new UnsupportedOperationException(); } @@ -1080,7 +1126,7 @@ public ValueHolder getAndCompute(K key, BiFunction { long delta = 0L; - if (mappedValue != null && mappedValue.isExpired(now, TimeUnit.MILLISECONDS)) { + if (mappedValue != null && mappedValue.isExpired(now)) { fireOnExpirationEvent(mappedKey, mappedValue, eventSink); delta -= mappedValue.size(); mappedValue = null; @@ -1148,7 +1194,7 @@ public ValueHolder computeAndGet(K key, BiFunction computeResult = map.compute(key, (mappedKey, mappedValue) -> { long delta = 0L; - if (mappedValue != null && mappedValue.isExpired(now, TimeUnit.MILLISECONDS)) { + if (mappedValue != null && mappedValue.isExpired(now)) { fireOnExpirationEvent(mappedKey, mappedValue, eventSink); delta -= mappedValue.size(); mappedValue = null; @@ -1165,7 +1211,7 @@ public ValueHolder computeAndGet(K key, BiFunction computeAndGet(K key, BiFunction computeIfAbsent(K key, Function ma long delta = 0; OnHeapValueHolder holder; - if (mappedValue == null || mappedValue.isExpired(now, TimeUnit.MILLISECONDS)) { + if (mappedValue == null || mappedValue.isExpired(now)) { if (mappedValue != null) { delta -= mappedValue.size(); fireOnExpirationEvent(mappedKey, mappedValue, eventSink); @@ -1250,7 +1296,7 @@ public ValueHolder computeIfAbsent(K key, Function ma } else { previousValue.set(mappedValue); outcome.set(StoreOperationOutcomes.ComputeIfAbsentOutcome.HIT); - holder = setAccessTimeAndExpiryThenReturnMappingUnderLock(key, mappedValue, now, eventSink); + holder = strategy.setAccessAndExpiryWhenCallerlUnderLock(key, mappedValue, now, eventSink); if (holder == null) { delta -= mappedValue.size(); } @@ -1352,45 +1398,7 @@ public StoreEventSource getStoreEventSource() { return storeEventDispatcher; } - private void setAccessTimeAndExpiryThenReturnMappingOutsideLock(K key, OnHeapValueHolder valueHolder, long now) { - Duration duration; - try { - duration = expiry.getExpiryForAccess(key, valueHolder); - if (duration != null && duration.isNegative()) { - duration = Duration.ZERO; - } - } catch (RuntimeException re) { - LOG.error("Expiry computation caused an exception - Expiry duration will be 0 ", re); - duration = Duration.ZERO; - } - valueHolder.accessed(now, duration); - if (Duration.ZERO.equals(duration)) { - // Expires mapping through computeIfPresent - expireMappingUnderLock(key, valueHolder); - } - } - - private OnHeapValueHolder setAccessTimeAndExpiryThenReturnMappingUnderLock(K key, OnHeapValueHolder valueHolder, long now, - StoreEventSink eventSink) { - Duration duration = Duration.ZERO; - try { - duration = expiry.getExpiryForAccess(key, valueHolder); - if (duration != null && duration.isNegative()) { - duration = Duration.ZERO; - } - } catch (RuntimeException re) { - LOG.error("Expiry computation caused an exception - Expiry duration will be 0 ", re); - } - valueHolder.accessed(now, duration); - if (Duration.ZERO.equals(duration)) { - // Fires event, must happen under lock - fireOnExpirationEvent(key, valueHolder, eventSink); - return null; - } - return valueHolder; - } - - private void expireMappingUnderLock(K key, ValueHolder value) { + void expireMappingUnderLock(K key, ValueHolder value) { StoreEventSink eventSink = storeEventDispatcher.eventSink(); try { @@ -1413,15 +1421,8 @@ private OnHeapValueHolder newUpdateValueHolder(K key, OnHeapValueHolder ol Objects.requireNonNull(oldValue); Objects.requireNonNull(newValue); - Duration duration = Duration.ZERO; - try { - duration = expiry.getExpiryForUpdate(key, oldValue, newValue); - if (duration != null && duration.isNegative()) { - duration = Duration.ZERO; - } - } catch (RuntimeException re) { - LOG.error("Expiry computation caused an exception - Expiry duration will be 0 ", re); - } + Duration duration = strategy.getUpdateDuration(key, oldValue, newValue); + if (Duration.ZERO.equals(duration)) { eventSink.updated(key, oldValue, newValue); eventSink.expired(key, () -> newValue); @@ -1430,7 +1431,7 @@ private OnHeapValueHolder newUpdateValueHolder(K key, OnHeapValueHolder ol long expirationTime; if (duration == null) { - expirationTime = oldValue.expirationTime(OnHeapValueHolder.TIME_UNIT); + expirationTime = oldValue.expirationTime(); } else { if (isExpiryDurationInfinite(duration)) { expirationTime = ValueHolder.NO_EXPIRE; @@ -1471,15 +1472,7 @@ private OnHeapValueHolder newCreateValueHolder(K key, V value, long now, Stor } private OnHeapValueHolder importValueFromLowerTier(K key, ValueHolder valueHolder, long now, Backend backEnd, Fault fault) { - Duration expiration = Duration.ZERO; - try { - expiration = expiry.getExpiryForAccess(key, valueHolder); - if (expiration != null && expiration.isNegative()) { - expiration = Duration.ZERO; - } - } catch (RuntimeException re) { - LOG.error("Expiry computation caused an exception - Expiry duration will be 0 ", re); - } + Duration expiration = strategy.getAccessDuration(key, valueHolder); if (Duration.ZERO.equals(expiration)) { invalidateInGetOrComputeIfAbsent(backEnd, key, valueHolder, fault, now, Duration.ZERO); @@ -1616,7 +1609,7 @@ boolean evict(StoreEventSink eventSink) { } } - private void fireOnExpirationEvent(K mappedKey, ValueHolder mappedValue, StoreEventSink eventSink) { + void fireOnExpirationEvent(K mappedKey, ValueHolder mappedValue, StoreEventSink eventSink) { expirationObserver.begin(); expirationObserver.end(StoreOperationOutcomes.ExpirationOutcome.SUCCESS); eventSink.expired(mappedKey, mappedValue); @@ -1624,9 +1617,10 @@ private void fireOnExpirationEvent(K mappedKey, ValueHolder mappedValue, Stor } @ServiceDependencies({TimeSourceService.class, CopyProvider.class, SizeOfEngineProvider.class}) + @OptionalServiceDependencies("org.ehcache.core.spi.service.Statis" + + "ticsService") public static class Provider extends BaseStoreProvider implements CachingTier.Provider, HigherCachingTier.Provider { - private volatile ServiceProvider serviceProvider; private final Map, List>> createdStores = new ConcurrentWeakIdentityHashMap<>(); private final Map, OperationStatistic[]> tierOperationStatistics = new ConcurrentWeakIdentityHashMap<>(); @@ -1636,18 +1630,18 @@ protected ResourceType getResourceType() { } @Override - public int rank(Set> resourceTypes, Collection> serviceConfigs) { + public int rank(Set> resourceTypes, Collection> serviceConfigs) { return resourceTypes.equals(Collections.singleton(getResourceType())) ? 1 : 0; } @Override - public int rankCachingTier(Set> resourceTypes, Collection> serviceConfigs) { + public int rankCachingTier(Set> resourceTypes, Collection> serviceConfigs) { return rank(resourceTypes, serviceConfigs); } @Override - public OnHeapStore createStore(Configuration storeConfig, ServiceConfiguration... serviceConfigs) { - OnHeapStore store = createStoreInternal(storeConfig, new ScopedStoreEventDispatcher<>(storeConfig.getDispatcherConcurrency()), serviceConfigs); + public OnHeapStore createStore(Configuration storeConfig, ServiceConfiguration... serviceConfigs) { + OnHeapStore store = createStoreInternal(storeConfig, new DefaultStoreEventDispatcher<>(storeConfig.getDispatcherConcurrency()), serviceConfigs); tierOperationStatistics.put(store, new OperationStatistic[] { createTranslatedStatistic(store, "get", TierOperationOutcomes.GET_TRANSLATION, "get"), @@ -1657,18 +1651,18 @@ public OnHeapStore createStore(Configuration storeConfig, Ser } public OnHeapStore createStoreInternal(Configuration storeConfig, StoreEventDispatcher eventDispatcher, - ServiceConfiguration... serviceConfigs) { - TimeSource timeSource = serviceProvider.getService(TimeSourceService.class).getTimeSource(); - CopyProvider copyProvider = serviceProvider.getService(CopyProvider.class); + ServiceConfiguration... serviceConfigs) { + TimeSource timeSource = getServiceProvider().getService(TimeSourceService.class).getTimeSource(); + CopyProvider copyProvider = getServiceProvider().getService(CopyProvider.class); Copier keyCopier = copyProvider.createKeyCopier(storeConfig.getKeyType(), storeConfig.getKeySerializer(), serviceConfigs); Copier valueCopier = copyProvider.createValueCopier(storeConfig.getValueType(), storeConfig.getValueSerializer(), serviceConfigs); List> copiers = Arrays.asList(keyCopier, valueCopier); - SizeOfEngineProvider sizeOfEngineProvider = serviceProvider.getService(SizeOfEngineProvider.class); + SizeOfEngineProvider sizeOfEngineProvider = getServiceProvider().getService(SizeOfEngineProvider.class); SizeOfEngine sizeOfEngine = sizeOfEngineProvider.createSizeOfEngine( storeConfig.getResourcePools().getPoolForResource(ResourceType.Core.HEAP).getUnit(), serviceConfigs); - OnHeapStore onHeapStore = new OnHeapStore<>(storeConfig, timeSource, keyCopier, valueCopier, sizeOfEngine, eventDispatcher, ConcurrentHashMap::new); + OnHeapStore onHeapStore = new OnHeapStore<>(storeConfig, timeSource, keyCopier, valueCopier, sizeOfEngine, eventDispatcher, ConcurrentHashMap::new, getServiceProvider().getService(StatisticsService.class)); createdStores.put(onHeapStore, copiers); return onHeapStore; } @@ -1681,10 +1675,10 @@ public void releaseStore(Store resource) { } OnHeapStore onHeapStore = (OnHeapStore)resource; close(onHeapStore); - StatisticsManager.nodeFor(onHeapStore).clean(); + getStatisticsService().ifPresent(s -> s.cleanForNode(onHeapStore)); tierOperationStatistics.remove(onHeapStore); - CopyProvider copyProvider = serviceProvider.getService(CopyProvider.class); + CopyProvider copyProvider = getServiceProvider().getService(CopyProvider.class); for (Copier copier: copiers) { try { copyProvider.releaseCopier(copier); @@ -1719,19 +1713,17 @@ private void checkResource(Object resource) { } } - @Override - public void start(ServiceProvider serviceProvider) { - this.serviceProvider = serviceProvider; - } - @Override public void stop() { - this.serviceProvider = null; - createdStores.clear(); + try { + createdStores.clear(); + } finally { + super.stop(); + } } @Override - public CachingTier createCachingTier(Configuration storeConfig, ServiceConfiguration... serviceConfigs) { + public CachingTier createCachingTier(Configuration storeConfig, ServiceConfiguration... serviceConfigs) { OnHeapStore cachingTier = createStoreInternal(storeConfig, NullStoreEventDispatcher.nullStoreEventDispatcher(), serviceConfigs); this.tierOperationStatistics.put(cachingTier, new OperationStatistic[] { @@ -1759,8 +1751,8 @@ public void initCachingTier(CachingTier resource) { } @Override - public HigherCachingTier createHigherCachingTier(Configuration storeConfig, ServiceConfiguration... serviceConfigs) { - OnHeapStore higherCachingTier = createStoreInternal(storeConfig, new ScopedStoreEventDispatcher<>(storeConfig + public HigherCachingTier createHigherCachingTier(Configuration storeConfig, ServiceConfiguration... serviceConfigs) { + OnHeapStore higherCachingTier = createStoreInternal(storeConfig, new DefaultStoreEventDispatcher<>(storeConfig .getDispatcherConcurrency()), serviceConfigs); this.tierOperationStatistics.put(higherCachingTier, new OperationStatistic[] { diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/heap/OnHeapStoreProviderFactory.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/heap/OnHeapStoreProviderFactory.java similarity index 85% rename from impl/src/main/java/org/ehcache/impl/internal/store/heap/OnHeapStoreProviderFactory.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/heap/OnHeapStoreProviderFactory.java index 13f97b8df6..bb1fe01943 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/store/heap/OnHeapStoreProviderFactory.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/heap/OnHeapStoreProviderFactory.java @@ -16,21 +16,23 @@ package org.ehcache.impl.internal.store.heap; -import org.ehcache.spi.service.ServiceCreationConfiguration; import org.ehcache.core.spi.service.ServiceFactory; +import org.ehcache.spi.service.ServiceCreationConfiguration; +import org.osgi.service.component.annotations.Component; /** * @author Alex Snaps */ +@Component public class OnHeapStoreProviderFactory implements ServiceFactory { @Override - public OnHeapStore.Provider create(ServiceCreationConfiguration configuration) { + public OnHeapStore.Provider create(ServiceCreationConfiguration configuration) { return new OnHeapStore.Provider(); } @Override - public Class getServiceType() { + public Class getServiceType() { return OnHeapStore.Provider.class; } } diff --git a/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/heap/OnHeapStrategy.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/heap/OnHeapStrategy.java new file mode 100644 index 0000000000..7910481e39 --- /dev/null +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/heap/OnHeapStrategy.java @@ -0,0 +1,262 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.impl.internal.store.heap; + +import org.ehcache.core.events.StoreEventSink; +import org.ehcache.core.spi.store.Store; +import org.ehcache.core.spi.time.TimeSource; +import org.ehcache.expiry.ExpiryPolicy; +import org.ehcache.impl.internal.store.heap.holders.OnHeapValueHolder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.time.Duration; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; + +/** + * Specialized behavior for an OnHeapStore allowing optimization depending on the expiry policy used. + * + * @param type of the keys stored + * @param type of the values stored + */ +interface OnHeapStrategy { + + Logger LOG = LoggerFactory.getLogger(OnHeapStore.class); + + static OnHeapStrategy strategy(OnHeapStore store, ExpiryPolicy expiry, TimeSource timeSource) { + if(expiry == ExpiryPolicy.NO_EXPIRY) { + LOG.debug("No expiration strategy detected"); + return new NoExpirationStrategy<>(); + } + if(expiry.getClass().getName().equals("org.ehcache.config.builders.ExpiryPolicyBuilder$TimeToLiveExpiryPolicy")) { + LOG.debug("TTL expiration strategy detected"); + return new TTLStrategy<>(expiry, timeSource); + } + LOG.debug("TTI or custom expiration strategy detected"); + return new AllStrategy<>(store, expiry, timeSource); + } + + /** + * Tells if a given mapping is expired. + * + * @param mapping mapping to test for expiration + * @return if the mapping is expired + */ + boolean isExpired(OnHeapValueHolder mapping); + + /** + * Set the access time on the mapping and its expiry time if it is access sensitive (TTI). We expect this action to + * be called when the caller isn't holding any lock. + * + * @param key key of the mapping. Used to remove it form the map if needed + * @param valueHolder the mapping + * @param now the current time + */ + void setAccessAndExpiryTimeWhenCallerOutsideLock(K key, OnHeapValueHolder valueHolder, long now); + + /** + * Set the access time on the mapping and its expiry time if it is access sensitive (TTI). We expect this action to + * be called when the caller is currently holding a lock. + * + * @param key key of the mapping. Used to remove it form the map if needed + * @param valueHolder the mapping + * @param now the current time + * @param eventSink sink where the expiration request will be sent + * @return the mapping or null if it was removed + */ + OnHeapValueHolder setAccessAndExpiryWhenCallerlUnderLock(K key, OnHeapValueHolder valueHolder, long now, StoreEventSink eventSink); + + /** + * Get the new expiry duration as per {@link ExpiryPolicy#getExpiryForAccess(Object, Supplier)}. + * + * @param key key of the mapping + * @param valueHolder the mapping + * @return new access expiry duration + */ + Duration getAccessDuration(K key, Store.ValueHolder valueHolder); + + /** + * Get the new expiry duration as per {@link ExpiryPolicy#getExpiryForUpdate(Object, Supplier, Object)}. + * + * @param key key of the mapping + * @param oldValue the old mapping to be updated + * @param newValue the new value for the mapping + * @return new access expiry duration + */ + Duration getUpdateDuration(K key, OnHeapValueHolder oldValue, V newValue); + + /** + * All purpose strategy. Covers any case that can't be optimized due to the uncertainty of the expiry policy used. + * + * @param type of the keys stored + * @param type of the values stored + */ + class AllStrategy implements OnHeapStrategy { + private final OnHeapStore store; + private final ExpiryPolicy expiry; + private final TimeSource timeSource; + + public AllStrategy(OnHeapStore store, ExpiryPolicy expiry, TimeSource timeSource) { + this.store = store; + this.expiry = expiry; + this.timeSource = timeSource; + } + + @Override + public boolean isExpired(OnHeapValueHolder mapping) { + return mapping.isExpired(timeSource.getTimeMillis()); + } + + @Override + public void setAccessAndExpiryTimeWhenCallerOutsideLock(K key, OnHeapValueHolder valueHolder, long now) { + Duration duration = getAccessDuration(key, valueHolder); + if (Duration.ZERO.equals(duration)) { + // Expires mapping through computeIfPresent + store.expireMappingUnderLock(key, valueHolder); + } else { + valueHolder.accessed(now, duration); + } + } + + public Duration getAccessDuration(K key, Store.ValueHolder valueHolder) { + Duration duration; + try { + duration = expiry.getExpiryForAccess(key, valueHolder); + if (duration != null && duration.isNegative()) { + duration = Duration.ZERO; + } + } catch (RuntimeException re) { + LOG.error("Expiry computation caused an exception - Expiry duration will be 0 ", re); + duration = Duration.ZERO; + } + return duration; + } + + public Duration getUpdateDuration(K key, OnHeapValueHolder oldValue, V newValue) { + Duration duration; + try { + duration = expiry.getExpiryForUpdate(key, oldValue, newValue); + if (duration != null && duration.isNegative()) { + duration = Duration.ZERO; + } + } catch (RuntimeException re) { + LOG.error("Expiry computation caused an exception - Expiry duration will be 0 ", re); + duration = Duration.ZERO; + } + return duration; + } + + public OnHeapValueHolder setAccessAndExpiryWhenCallerlUnderLock(K key, OnHeapValueHolder valueHolder, long now, + StoreEventSink eventSink) { + Duration duration = getAccessDuration(key, valueHolder); + if (Duration.ZERO.equals(duration)) { + // Fires event, must happen under lock + store.fireOnExpirationEvent(key, valueHolder, eventSink); + return null; + } else { + valueHolder.accessed(now, duration); + } + return valueHolder; + } + + } + + /** + * Strategy used when entries are never expiring. + * + * @param type of the keys stored + * @param type of the values stored + */ + class NoExpirationStrategy implements OnHeapStrategy { + + @Override + public boolean isExpired(OnHeapValueHolder mapping) { + return false; + } + + @Override + public void setAccessAndExpiryTimeWhenCallerOutsideLock(K key, OnHeapValueHolder valueHolder, long now) { + valueHolder.accessed(now, null); + } + + public OnHeapValueHolder setAccessAndExpiryWhenCallerlUnderLock(K key, OnHeapValueHolder valueHolder, long now, + StoreEventSink eventSink) { + valueHolder.accessed(now, null); + return valueHolder; + } + + public Duration getAccessDuration(K key, Store.ValueHolder valueHolder) { + return null; + } + + public Duration getUpdateDuration(K key, OnHeapValueHolder oldValue, V newValue) { + return null; + } + } + + /** + * Strategy used when entries are expiring due to TTL only. + * + * @param type of the keys stored + * @param type of the values stored + */ + class TTLStrategy implements OnHeapStrategy { + private final TimeSource timeSource; + private final ExpiryPolicy expiry; + + public TTLStrategy(ExpiryPolicy expiry, TimeSource timeSource) { + this.timeSource = timeSource; + this.expiry = expiry; + } + + @Override + public boolean isExpired(OnHeapValueHolder mapping) { + return mapping.isExpired(timeSource.getTimeMillis()); + } + + @Override + public void setAccessAndExpiryTimeWhenCallerOutsideLock(K key, OnHeapValueHolder valueHolder, long now) { + valueHolder.accessed(now, null); + } + + public OnHeapValueHolder setAccessAndExpiryWhenCallerlUnderLock(K key, OnHeapValueHolder valueHolder, long now, + StoreEventSink eventSink) { + valueHolder.accessed(now, null); + return valueHolder; + } + + public Duration getAccessDuration(K key, Store.ValueHolder valueHolder) { + return null; + } + + public Duration getUpdateDuration(K key, OnHeapValueHolder oldValue, V newValue) { + Duration duration; + try { + duration = expiry.getExpiryForUpdate(key, oldValue, newValue); + if (duration != null && duration.isNegative()) { + duration = Duration.ZERO; + } + } catch (RuntimeException re) { + LOG.error("Expiry computation caused an exception - Expiry duration will be 0 ", re); + duration = Duration.ZERO; + } + return duration; + } + } + +} diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/heap/SimpleBackend.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/heap/SimpleBackend.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/store/heap/SimpleBackend.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/heap/SimpleBackend.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/BaseOnHeapKey.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/BaseOnHeapKey.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/BaseOnHeapKey.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/BaseOnHeapKey.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/CopiedOnHeapKey.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/CopiedOnHeapKey.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/CopiedOnHeapKey.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/CopiedOnHeapKey.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/CopiedOnHeapValueHolder.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/CopiedOnHeapValueHolder.java similarity index 95% rename from impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/CopiedOnHeapValueHolder.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/CopiedOnHeapValueHolder.java index 931e02513a..554b2f6b8f 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/CopiedOnHeapValueHolder.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/CopiedOnHeapValueHolder.java @@ -20,6 +20,8 @@ import org.ehcache.core.spi.store.Store; import org.ehcache.spi.copy.Copier; +import java.util.concurrent.TimeUnit; + /** * @author Albin Suresh */ @@ -51,7 +53,7 @@ protected CopiedOnHeapValueHolder(long id, V value, long creationTime, long expi * @param expiration computed expiration duration */ public CopiedOnHeapValueHolder(Store.ValueHolder valueHolder, V value, boolean evictionAdvice, Copier valueCopier, long now, java.time.Duration expiration) { - super(valueHolder.getId(), valueHolder.creationTime(TIME_UNIT), valueHolder.expirationTime(TIME_UNIT), evictionAdvice); + super(valueHolder.getId(), valueHolder.creationTime(), valueHolder.expirationTime(), evictionAdvice); if (value == null) { throw new NullPointerException("null value"); } diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/LookupOnlyOnHeapKey.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/LookupOnlyOnHeapKey.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/LookupOnlyOnHeapKey.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/LookupOnlyOnHeapKey.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/OnHeapKey.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/OnHeapKey.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/OnHeapKey.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/OnHeapKey.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/OnHeapValueHolder.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/OnHeapValueHolder.java similarity index 92% rename from impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/OnHeapValueHolder.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/OnHeapValueHolder.java index 5fc391b95c..947d7092db 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/OnHeapValueHolder.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/OnHeapValueHolder.java @@ -25,8 +25,6 @@ */ public abstract class OnHeapValueHolder extends AbstractValueHolder { - public static final TimeUnit TIME_UNIT = TimeUnit.MILLISECONDS; - private final boolean evictionAdvice; private long size; @@ -55,11 +53,6 @@ public void setSize(long size) { this.size = size; } - @Override - final protected TimeUnit nativeTimeUnit() { - return TIME_UNIT; - } - @Override public boolean equals(Object obj) { if (obj != null && this.getClass().equals(obj.getClass())) { diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/SerializedOnHeapValueHolder.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/SerializedOnHeapValueHolder.java similarity index 93% rename from impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/SerializedOnHeapValueHolder.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/SerializedOnHeapValueHolder.java index 7903d54d9e..a5dcaf8d9d 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/SerializedOnHeapValueHolder.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/heap/holders/SerializedOnHeapValueHolder.java @@ -49,12 +49,12 @@ public SerializedOnHeapValueHolder(V value, long creationTime, long expirationTi } public SerializedOnHeapValueHolder(Store.ValueHolder valueHolder, V value, boolean evictionAdvice, Serializer serializer, long now, java.time.Duration expiration) { - this(valueHolder.getId(), value, valueHolder.creationTime(TIME_UNIT), valueHolder.expirationTime(TIME_UNIT), evictionAdvice, serializer); + this(valueHolder.getId(), value, valueHolder.creationTime(), valueHolder.expirationTime(), evictionAdvice, serializer); this.accessed(now, expiration); } public SerializedOnHeapValueHolder(Store.ValueHolder valueHolder, ByteBuffer binaryValue, boolean evictionAdvice, Serializer serializer, long now, java.time.Duration expiration) { - super(valueHolder.getId(), valueHolder.creationTime(TIME_UNIT), valueHolder.expirationTime(TIME_UNIT), evictionAdvice); + super(valueHolder.getId(), valueHolder.creationTime(), valueHolder.expirationTime(), evictionAdvice); this.buffer = binaryValue; this.serializer = serializer; this.accessed(now, expiration); diff --git a/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/loaderwriter/LoaderWriterStoreProvider.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/loaderwriter/LoaderWriterStoreProvider.java new file mode 100644 index 0000000000..4ba520285f --- /dev/null +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/loaderwriter/LoaderWriterStoreProvider.java @@ -0,0 +1,90 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.impl.internal.store.loaderwriter; + +import org.ehcache.config.ResourceType; +import org.ehcache.core.spi.store.AbstractWrapperStoreProvider; +import org.ehcache.core.spi.store.Store; +import org.ehcache.spi.loaderwriter.CacheLoaderWriter; +import org.ehcache.spi.loaderwriter.CacheLoaderWriterConfiguration; +import org.ehcache.spi.loaderwriter.CacheLoaderWriterProvider; +import org.ehcache.spi.loaderwriter.WriteBehindConfiguration; +import org.ehcache.spi.loaderwriter.WriteBehindProvider; +import org.ehcache.spi.service.Service; +import org.ehcache.spi.service.ServiceConfiguration; +import org.ehcache.spi.service.ServiceDependencies; +import org.ehcache.spi.service.ServiceProvider; + +import java.util.Collection; +import java.util.Set; + +import static org.ehcache.core.spi.service.ServiceUtils.findSingletonAmongst; + +@ServiceDependencies({CacheLoaderWriterProvider.class, WriteBehindProvider.class}) +public class LoaderWriterStoreProvider extends AbstractWrapperStoreProvider { + + private volatile WriteBehindProvider writeBehindProvider; + + @Override + protected Store wrap(Store store, Store.Configuration storeConfig, ServiceConfiguration... serviceConfigs) { + WriteBehindConfiguration writeBehindConfiguration = findSingletonAmongst(WriteBehindConfiguration.class, (Object[]) serviceConfigs); + LocalLoaderWriterStore loaderWriterStore; + if(writeBehindConfiguration == null) { + loaderWriterStore = new LocalLoaderWriterStore<>(store, storeConfig.getCacheLoaderWriter(), storeConfig.useLoaderInAtomics(), storeConfig.getExpiry()); + } else { + CacheLoaderWriter writeBehindLoaderWriter = writeBehindProvider.createWriteBehindLoaderWriter(storeConfig.getCacheLoaderWriter(), writeBehindConfiguration); + loaderWriterStore = new LocalWriteBehindLoaderWriterStore<>(store, writeBehindLoaderWriter, storeConfig.useLoaderInAtomics(), storeConfig.getExpiry()); + } + return loaderWriterStore; + } + + @Override + public void releaseStore(Store resource) { + try { + if (resource instanceof LocalWriteBehindLoaderWriterStore) { + writeBehindProvider.releaseWriteBehindLoaderWriter(((LocalWriteBehindLoaderWriterStore) resource).getCacheLoaderWriter()); + } + } finally { + super.releaseStore(resource); + } + } + + @Override + public void start(ServiceProvider serviceProvider) { + super.start(serviceProvider); + this.writeBehindProvider = serviceProvider.getService(WriteBehindProvider.class); + } + + @Override + public void stop() { + this.writeBehindProvider = null; + super.stop(); + } + + @Override + public int rank(Set> resourceTypes, Collection> serviceConfigs) { + throw new UnsupportedOperationException("Its a Wrapper store provider, does not support regular ranking"); + } + + @Override + public int wrapperStoreRank(Collection> serviceConfigs) { + CacheLoaderWriterConfiguration loaderWriterConfiguration = findSingletonAmongst(CacheLoaderWriterConfiguration.class, serviceConfigs); + if (loaderWriterConfiguration == null) { + return 0; + } + return 2; + } +} diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/loaderwriter/LoaderWriterStoreProviderFactory.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/loaderwriter/LoaderWriterStoreProviderFactory.java similarity index 90% rename from impl/src/main/java/org/ehcache/impl/internal/store/loaderwriter/LoaderWriterStoreProviderFactory.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/loaderwriter/LoaderWriterStoreProviderFactory.java index 7d3f831266..2a5137b9b1 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/store/loaderwriter/LoaderWriterStoreProviderFactory.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/loaderwriter/LoaderWriterStoreProviderFactory.java @@ -17,10 +17,12 @@ import org.ehcache.core.spi.service.ServiceFactory; import org.ehcache.spi.service.ServiceCreationConfiguration; +import org.osgi.service.component.annotations.Component; +@Component public class LoaderWriterStoreProviderFactory implements ServiceFactory { @Override - public LoaderWriterStoreProvider create(ServiceCreationConfiguration configuration) { + public LoaderWriterStoreProvider create(ServiceCreationConfiguration configuration) { return new LoaderWriterStoreProvider(); } diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/loaderwriter/LoaderWriterValueHolder.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/loaderwriter/LoaderWriterValueHolder.java similarity index 85% rename from impl/src/main/java/org/ehcache/impl/internal/store/loaderwriter/LoaderWriterValueHolder.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/loaderwriter/LoaderWriterValueHolder.java index 984084f7b3..70a80aa162 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/store/loaderwriter/LoaderWriterValueHolder.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/loaderwriter/LoaderWriterValueHolder.java @@ -17,12 +17,8 @@ import org.ehcache.core.spi.store.AbstractValueHolder; -import java.util.concurrent.TimeUnit; - public class LoaderWriterValueHolder extends AbstractValueHolder { - private static final TimeUnit TIME_UNIT = TimeUnit.MILLISECONDS; - private final V value; public LoaderWriterValueHolder(V value) { @@ -33,11 +29,6 @@ public LoaderWriterValueHolder(V value) { this.value = value; } - @Override - protected TimeUnit nativeTimeUnit() { - return TIME_UNIT; - } - @Override public V get() { return value; diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/loaderwriter/LocalLoaderWriterStore.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/loaderwriter/LocalLoaderWriterStore.java similarity index 97% rename from impl/src/main/java/org/ehcache/impl/internal/store/loaderwriter/LocalLoaderWriterStore.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/loaderwriter/LocalLoaderWriterStore.java index c25aff48b6..066bf01033 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/store/loaderwriter/LocalLoaderWriterStore.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/loaderwriter/LocalLoaderWriterStore.java @@ -19,7 +19,7 @@ import org.ehcache.core.CacheConfigurationChangeListener; import org.ehcache.core.Ehcache; import org.ehcache.core.exceptions.StorePassThroughException; -import org.ehcache.core.internal.util.CollectionUtil; +import org.ehcache.core.util.CollectionUtil; import org.ehcache.core.spi.store.Store; import org.ehcache.core.spi.store.WrapperStore; import org.ehcache.core.spi.store.events.StoreEventSource; @@ -30,7 +30,6 @@ import org.ehcache.spi.resilience.StoreAccessException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.terracotta.context.ContextManager; import java.time.Duration; import java.util.Collection; @@ -65,7 +64,6 @@ public LocalLoaderWriterStore(Store delegate, CacheLoaderWriter putIfAbsent(K key, V value, Consumer put) throws throw new StorePassThroughException(newCacheWritingException(e)); } + // Here were a returning an actual value instead of null because the mappingFunction is called by a map.compute(). So we + // want the compute to actually set the value to the backend. However, the putIfAbsent should return null since there + // was no previous value. This is why we use put.accept(true). This will tell EhcacheBase: "Hey! A put was done, you should return null" put.accept(true); return value; }; diff --git a/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/loaderwriter/LocalWriteBehindLoaderWriterStore.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/loaderwriter/LocalWriteBehindLoaderWriterStore.java new file mode 100644 index 0000000000..bfbcf6e936 --- /dev/null +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/loaderwriter/LocalWriteBehindLoaderWriterStore.java @@ -0,0 +1,34 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.impl.internal.store.loaderwriter; + +import org.ehcache.core.spi.store.Store; +import org.ehcache.expiry.ExpiryPolicy; +import org.ehcache.spi.loaderwriter.CacheLoaderWriter; + +public class LocalWriteBehindLoaderWriterStore extends LocalLoaderWriterStore { + + private final CacheLoaderWriter cacheLoaderWriter; + + public LocalWriteBehindLoaderWriterStore(Store delegate, CacheLoaderWriter cacheLoaderWriter, boolean useLoaderInAtomics, ExpiryPolicy expiry) { + super(delegate, cacheLoaderWriter, useLoaderInAtomics, expiry); + this.cacheLoaderWriter = cacheLoaderWriter; + } + + public CacheLoaderWriter getCacheLoaderWriter() { + return cacheLoaderWriter; + } +} diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/offheap/AbstractOffHeapStore.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/AbstractOffHeapStore.java similarity index 95% rename from impl/src/main/java/org/ehcache/impl/internal/store/offheap/AbstractOffHeapStore.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/AbstractOffHeapStore.java index 998e133db8..fda2c6cb8a 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/store/offheap/AbstractOffHeapStore.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/AbstractOffHeapStore.java @@ -18,12 +18,13 @@ import java.io.Serializable; import java.time.Duration; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Set; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiFunction; @@ -36,8 +37,12 @@ import org.ehcache.core.config.ExpiryUtils; import org.ehcache.core.events.StoreEventDispatcher; import org.ehcache.core.events.StoreEventSink; -import org.ehcache.impl.internal.store.basic.BaseStore; +import org.ehcache.core.spi.service.StatisticsService; +import org.ehcache.core.statistics.StatisticType; +import org.ehcache.core.statistics.OperationObserver; +import org.ehcache.impl.store.BaseStore; import org.ehcache.spi.resilience.StoreAccessException; +import org.ehcache.impl.internal.store.offheap.portability.OffHeapValueHolderPortability; import org.ehcache.core.spi.time.TimeSource; import org.ehcache.expiry.ExpiryPolicy; import org.ehcache.impl.internal.store.offheap.factories.EhcacheSegmentFactory; @@ -51,16 +56,14 @@ import org.ehcache.core.statistics.StoreOperationOutcomes; import org.ehcache.impl.internal.store.BinaryValueHolder; import org.ehcache.impl.store.HashUtils; +import org.ehcache.spi.serialization.Serializer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.terracotta.offheapstore.exceptions.OversizeMappingException; -import org.terracotta.statistics.StatisticType; -import org.terracotta.statistics.observer.OperationObserver; import static org.ehcache.core.config.ExpiryUtils.isExpiryDurationInfinite; import static org.ehcache.core.exceptions.StorePassThroughException.handleException; -import static org.terracotta.statistics.StatisticsManager.tags; -import static org.terracotta.statistics.StatisticType.GAUGE; +import static org.ehcache.core.statistics.StatisticType.GAUGE; public abstract class AbstractOffHeapStore extends BaseStore implements AuthoritativeTier, LowerCachingTier { @@ -103,8 +106,8 @@ public abstract class AbstractOffHeapStore extends BaseStore impleme @SuppressWarnings("unchecked") private volatile CachingTier.InvalidationListener invalidationListener = (CachingTier.InvalidationListener) NULL_INVALIDATION_LISTENER; - public AbstractOffHeapStore(Configuration config, TimeSource timeSource, StoreEventDispatcher eventDispatcher) { - super(config); + public AbstractOffHeapStore(Configuration config, TimeSource timeSource, StoreEventDispatcher eventDispatcher, StatisticsService statisticsService) { + super(config, statisticsService); expiry = config.getExpiry(); @@ -133,7 +136,7 @@ public AbstractOffHeapStore(Configuration config, TimeSource timeSource, S this.getAndRemoveObserver= createObserver("getAndRemove", LowerCachingTierOperationsOutcome.GetAndRemoveOutcome.class, true); this.installMappingObserver= createObserver("installMapping", LowerCachingTierOperationsOutcome.InstallMappingOutcome.class, true); - Set tags = tags(getStatisticsTag(), "tier"); + Set tags = new HashSet<>(Arrays.asList(getStatisticsTag(), "tier")); registerStatistic("allocatedMemory", GAUGE, tags, EhcacheOffHeapBackingMap::allocatedMemory); registerStatistic("occupiedMemory", GAUGE, tags, EhcacheOffHeapBackingMap::occupiedMemory); registerStatistic("dataAllocatedMemory", GAUGE, tags, EhcacheOffHeapBackingMap::dataAllocatedMemory); @@ -181,7 +184,7 @@ private Store.ValueHolder internalGet(K key, final boolean updateAccess, fina OffHeapValueHolder result = backingMap().computeIfPresent(key, (mappedKey, mappedValue) -> { long now = timeSource.getTimeMillis(); - if (mappedValue.isExpired(now, TimeUnit.MILLISECONDS)) { + if (mappedValue.isExpired(now)) { onExpiration(mappedKey, mappedValue, eventSink); return null; } @@ -230,7 +233,7 @@ public PutStatus put(final K key, final V value) throws StoreAccessException { try { BiFunction, OffHeapValueHolder> mappingFunction = (mappedKey, mappedValue) -> { - if (mappedValue != null && mappedValue.isExpired(now, TimeUnit.MILLISECONDS)) { + if (mappedValue != null && mappedValue.isExpired(now)) { mappedValue = null; } @@ -274,7 +277,7 @@ public Store.ValueHolder putIfAbsent(final K key, final V value, Consumer, OffHeapValueHolder> mappingFunction = (mappedKey, mappedValue) -> { long now = timeSource.getTimeMillis(); - if (mappedValue == null || mappedValue.isExpired(now, TimeUnit.MILLISECONDS)) { + if (mappedValue == null || mappedValue.isExpired(now)) { if (mappedValue != null) { onExpiration(mappedKey, mappedValue, eventSink); } @@ -316,7 +319,7 @@ public boolean remove(final K key) throws StoreAccessException { backingMap().computeIfPresent(key, (mappedKey, mappedValue) -> { - if (mappedValue != null && mappedValue.isExpired(now, TimeUnit.MILLISECONDS)) { + if (mappedValue != null && mappedValue.isExpired(now)) { onExpiration(mappedKey, mappedValue, eventSink); return null; } @@ -357,7 +360,7 @@ public RemoveStatus remove(final K key, final V value) throws StoreAccessExcepti backingMap().computeIfPresent(key, (mappedKey, mappedValue) -> { long now = timeSource.getTimeMillis(); - if (mappedValue.isExpired(now, TimeUnit.MILLISECONDS)) { + if (mappedValue.isExpired(now)) { onExpiration(mappedKey, mappedValue, eventSink); return null; } else if (mappedValue.get().equals(value)) { @@ -402,12 +405,13 @@ public ValueHolder replace(final K key, final V value) throws NullPointerExce BiFunction, OffHeapValueHolder> mappingFunction = (mappedKey, mappedValue) -> { long now = timeSource.getTimeMillis(); - if (mappedValue == null || mappedValue.isExpired(now, TimeUnit.MILLISECONDS)) { + if (mappedValue == null || mappedValue.isExpired(now)) { if (mappedValue != null) { onExpiration(mappedKey, mappedValue, eventSink); } return null; } else { + mappedValue.forceDeserialization(); returnValue.set(mappedValue); return newUpdatedValueHolder(mappedKey, value, mappedValue, now, eventSink); } @@ -443,7 +447,7 @@ public ReplaceStatus replace(final K key, final V oldValue, final V newValue) th BiFunction, OffHeapValueHolder> mappingFunction = (mappedKey, mappedValue) -> { long now = timeSource.getTimeMillis(); - if (mappedValue == null || mappedValue.isExpired(now, TimeUnit.MILLISECONDS)) { + if (mappedValue == null || mappedValue.isExpired(now)) { if (mappedValue != null) { onExpiration(mappedKey, mappedValue, eventSink); } @@ -533,7 +537,7 @@ public ValueHolder getAndCompute(K key, BiFunction, OffHeapValueHolder> computeFunction = (mappedKey, mappedValue) -> { long now = timeSource.getTimeMillis(); V existingValue = null; - if (mappedValue == null || mappedValue.isExpired(now, TimeUnit.MILLISECONDS)) { + if (mappedValue == null || mappedValue.isExpired(now)) { if (mappedValue != null) { onExpiration(mappedKey, mappedValue, eventSink); } @@ -601,7 +605,7 @@ public ValueHolder computeAndGet(final K key, final BiFunction, OffHeapValueHolder> computeFunction = (mappedKey, mappedValue) -> { long now = timeSource.getTimeMillis(); V existingValue = null; - if (mappedValue == null || mappedValue.isExpired(now, TimeUnit.MILLISECONDS)) { + if (mappedValue == null || mappedValue.isExpired(now)) { if (mappedValue != null) { onExpiration(mappedKey, mappedValue, eventSink); } @@ -685,7 +689,7 @@ private Store.ValueHolder internalComputeIfAbsent(final K key, final Function final StoreEventSink eventSink = eventDispatcher.eventSink(); BiFunction, OffHeapValueHolder> computeFunction = (mappedKey, mappedValue) -> { long now = timeSource.getTimeMillis(); - if (mappedValue == null || mappedValue.isExpired(now, TimeUnit.MILLISECONDS)) { + if (mappedValue == null || mappedValue.isExpired(now)) { if (mappedValue != null) { onExpiration(mappedKey, mappedValue, eventSink); } @@ -820,7 +824,7 @@ public ValueHolder getAndFault(K key) throws StoreAccessException { final StoreEventSink eventSink = eventDispatcher.eventSink(); try { mappedValue = backingMap().computeIfPresentAndPin(key, (mappedKey, mappedValue1) -> { - if(mappedValue1.isExpired(timeSource.getTimeMillis(), TimeUnit.MILLISECONDS)) { + if(mappedValue1.isExpired(timeSource.getTimeMillis())) { onExpiration(mappedKey, mappedValue1, eventSink); return null; } @@ -857,7 +861,7 @@ public boolean flush(K key, final ValueHolder valueFlushed) { try { boolean result = backingMap().computeIfPinned(key, (k, valuePresent) -> { if (valuePresent.getId() == valueFlushed.getId()) { - if (valueFlushed.isExpired(timeSource.getTimeMillis(), TimeUnit.MILLISECONDS)) { + if (valueFlushed.isExpired(timeSource.getTimeMillis())) { onExpiration(k, valuePresent, eventSink); return null; } @@ -964,7 +968,7 @@ public ValueHolder getAndRemove(final K key) throws StoreAccessException { final AtomicReference> valueHolderAtomicReference = new AtomicReference<>(); BiFunction, OffHeapValueHolder> computeFunction = (mappedKey, mappedValue) -> { long now = timeSource.getTimeMillis(); - if (mappedValue == null || mappedValue.isExpired(now, TimeUnit.MILLISECONDS)) { + if (mappedValue == null || mappedValue.isExpired(now)) { if (mappedValue != null) { onExpirationInCachingTier(mappedValue, key); } @@ -998,7 +1002,7 @@ public ValueHolder installMapping(final K key, final Function valueHolder = source.apply(k); if (valueHolder != null) { - if (valueHolder.isExpired(timeSource.getTimeMillis(), TimeUnit.MILLISECONDS)) { + if (valueHolder.isExpired(timeSource.getTimeMillis())) { onExpirationInCachingTier(valueHolder, key); return null; } else { @@ -1086,7 +1090,7 @@ private OffHeapValueHolder newUpdatedValueHolder(K key, V value, OffHeapValue } if (duration == null) { - return new BasicOffHeapValueHolder<>(backingMap().nextIdFor(key), value, now, existing.expirationTime(OffHeapValueHolder.TIME_UNIT)); + return new BasicOffHeapValueHolder<>(backingMap().nextIdFor(key), value, now, existing.expirationTime()); } else if (isExpiryDurationInfinite(duration)) { return new BasicOffHeapValueHolder<>(backingMap().nextIdFor(key), value, now, OffHeapValueHolder.NO_EXPIRE); } else { @@ -1112,11 +1116,11 @@ private OffHeapValueHolder newCreateValueHolder(K key, V value, long now, Sto private OffHeapValueHolder newTransferValueHolder(ValueHolder valueHolder) { if (valueHolder instanceof BinaryValueHolder && ((BinaryValueHolder) valueHolder).isBinaryValueAvailable()) { return new BinaryOffHeapValueHolder<>(valueHolder.getId(), valueHolder.get(), ((BinaryValueHolder) valueHolder).getBinaryValue(), - valueHolder.creationTime(OffHeapValueHolder.TIME_UNIT), valueHolder.expirationTime(OffHeapValueHolder.TIME_UNIT), - valueHolder.lastAccessTime(OffHeapValueHolder.TIME_UNIT)); + valueHolder.creationTime(), valueHolder.expirationTime(), + valueHolder.lastAccessTime()); } else { - return new BasicOffHeapValueHolder<>(valueHolder.getId(), valueHolder.get(), valueHolder.creationTime(OffHeapValueHolder.TIME_UNIT), - valueHolder.expirationTime(OffHeapValueHolder.TIME_UNIT), valueHolder.lastAccessTime(OffHeapValueHolder.TIME_UNIT)); + return new BasicOffHeapValueHolder<>(valueHolder.getId(), valueHolder.get(), valueHolder.creationTime(), + valueHolder.expirationTime(), valueHolder.lastAccessTime()); } } @@ -1148,6 +1152,10 @@ private void onExpiration(K mappedKey, ValueHolder mappedValue, StoreEventSin protected abstract SwitchableEvictionAdvisor> evictionAdvisor(); + protected OffHeapValueHolderPortability createValuePortability(Serializer serializer) { + return new OffHeapValueHolderPortability<>(serializer); + } + protected static SwitchableEvictionAdvisor> wrap(EvictionAdvisor delegate) { return new OffHeapEvictionAdvisorWrapper<>(delegate); } diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/offheap/BasicOffHeapValueHolder.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/BasicOffHeapValueHolder.java similarity index 95% rename from impl/src/main/java/org/ehcache/impl/internal/store/offheap/BasicOffHeapValueHolder.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/BasicOffHeapValueHolder.java index 56edba07ac..ce755034f0 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/store/offheap/BasicOffHeapValueHolder.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/BasicOffHeapValueHolder.java @@ -18,6 +18,8 @@ import org.ehcache.core.spi.store.Store; +import java.util.concurrent.TimeUnit; + /** * BasicOffHeapValueHolder */ @@ -31,7 +33,7 @@ public BasicOffHeapValueHolder(long id, V value, long creationTime, long expireT public BasicOffHeapValueHolder(long id, V value, long creationTime, long expireTime, long lastAccessTime) { super(id, creationTime, expireTime); - setLastAccessTime(lastAccessTime, TIME_UNIT); + setLastAccessTime(lastAccessTime); this.value = value; } diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/offheap/BinaryOffHeapValueHolder.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/BinaryOffHeapValueHolder.java similarity index 96% rename from impl/src/main/java/org/ehcache/impl/internal/store/offheap/BinaryOffHeapValueHolder.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/BinaryOffHeapValueHolder.java index c4ba670664..18df2eb356 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/store/offheap/BinaryOffHeapValueHolder.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/BinaryOffHeapValueHolder.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.nio.ByteBuffer; +import java.util.concurrent.TimeUnit; /** * BinaryOffHeapValueHolder @@ -33,7 +34,7 @@ final class BinaryOffHeapValueHolder extends OffHeapValueHolder implements BinaryOffHeapValueHolder(long id, V value, ByteBuffer binaryValue, long creationTime, long expireTime, long lastAccessTime) { super(id, creationTime, expireTime); this.value = value; - setLastAccessTime(lastAccessTime, TIME_UNIT); + setLastAccessTime(lastAccessTime); this.binaryValue = binaryValue; } diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/offheap/EhcacheConcurrentOffHeapClockCache.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/EhcacheConcurrentOffHeapClockCache.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/store/offheap/EhcacheConcurrentOffHeapClockCache.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/EhcacheConcurrentOffHeapClockCache.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/offheap/EhcacheOffHeapBackingMap.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/EhcacheOffHeapBackingMap.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/store/offheap/EhcacheOffHeapBackingMap.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/EhcacheOffHeapBackingMap.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/offheap/HeuristicConfiguration.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/HeuristicConfiguration.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/store/offheap/HeuristicConfiguration.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/HeuristicConfiguration.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/offheap/LazyOffHeapValueHolder.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/LazyOffHeapValueHolder.java similarity index 78% rename from impl/src/main/java/org/ehcache/impl/internal/store/offheap/LazyOffHeapValueHolder.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/LazyOffHeapValueHolder.java index 5d44f16e30..c5557bd50f 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/store/offheap/LazyOffHeapValueHolder.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/LazyOffHeapValueHolder.java @@ -23,14 +23,12 @@ import org.ehcache.spi.serialization.Serializer; import org.terracotta.offheapstore.storage.portability.WriteContext; -import java.io.IOException; import java.nio.ByteBuffer; -import java.util.concurrent.TimeUnit; /** * OffHeapValueHolder variant that supports lazy deserialization and also serving the binary value if detached. */ -public final class LazyOffHeapValueHolder extends OffHeapValueHolder implements BinaryValueHolder { +public class LazyOffHeapValueHolder extends OffHeapValueHolder implements BinaryValueHolder { private final Serializer valueSerializer; private final WriteContext writeContext; @@ -40,7 +38,7 @@ public final class LazyOffHeapValueHolder extends OffHeapValueHolder imple public LazyOffHeapValueHolder(long id, ByteBuffer binaryValue, Serializer serializer, long creationTime, long expireTime, long lastAccessTime, WriteContext writeContext) { super(id, creationTime, expireTime); - setLastAccessTime(lastAccessTime, TIME_UNIT); + setLastAccessTime(lastAccessTime); this.binaryValue = binaryValue; this.valueSerializer = serializer; this.writeContext = writeContext; @@ -72,8 +70,8 @@ void updateMetadata(final Store.ValueHolder valueFlushed) { if(getId() != valueFlushed.getId()) { throw new IllegalArgumentException("Wrong id passed in [this.id != id] : " + getId() + " != " + valueFlushed.getId()); } - this.setLastAccessTime(valueFlushed.lastAccessTime(LazyOffHeapValueHolder.TIME_UNIT), LazyOffHeapValueHolder.TIME_UNIT); - this.setExpirationTime(valueFlushed.expirationTime(LazyOffHeapValueHolder.TIME_UNIT), LazyOffHeapValueHolder.TIME_UNIT); + this.setLastAccessTime(valueFlushed.lastAccessTime()); + this.setExpirationTime(valueFlushed.expirationTime()); } /** @@ -81,8 +79,8 @@ void updateMetadata(final Store.ValueHolder valueFlushed) { */ @Override void writeBack() { - writeContext.setLong(OffHeapValueHolderPortability.ACCESS_TIME_OFFSET, lastAccessTime(TimeUnit.MILLISECONDS)); - writeContext.setLong(OffHeapValueHolderPortability.EXPIRE_TIME_OFFSET, expirationTime(TimeUnit.MILLISECONDS)); + writeContext.setLong(OffHeapValueHolderPortability.ACCESS_TIME_OFFSET, lastAccessTime()); + writeContext.setLong(OffHeapValueHolderPortability.EXPIRE_TIME_OFFSET, expirationTime()); writeContext.flush(); } @@ -92,14 +90,18 @@ void writeBack() { @Override void forceDeserialization() { if (value == null) { - try { - value = valueSerializer.read(binaryValue.duplicate()); - } catch (ClassNotFoundException e) { - throw new SerializerException(e); - } catch (SerializerException e) { - throw new SerializerException("Seeing this exception and having no other " + - "serialization related issues is a red flag!", e); - } + value = deserialize(); + } + } + + V deserialize() { + try { + return valueSerializer.read(binaryValue.duplicate()); + } catch (ClassNotFoundException e) { + throw new SerializerException(e); + } catch (SerializerException e) { + throw new SerializerException("Seeing this exception and having no other " + + "serialization related issues is a red flag!", e); } } @@ -125,5 +127,4 @@ private enum Mode { private void writeObject(java.io.ObjectOutputStream out) { throw new UnsupportedOperationException("This subclass of AbstractValueHolder is NOT serializable"); } - } diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/offheap/MemorySizeParser.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/MemorySizeParser.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/store/offheap/MemorySizeParser.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/MemorySizeParser.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/offheap/OffHeapMapStatistics.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/OffHeapMapStatistics.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/store/offheap/OffHeapMapStatistics.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/OffHeapMapStatistics.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/offheap/OffHeapStore.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/OffHeapStore.java similarity index 90% rename from impl/src/main/java/org/ehcache/impl/internal/store/offheap/OffHeapStore.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/OffHeapStore.java index e08b2204ad..acaf26bc75 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/store/offheap/OffHeapStore.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/OffHeapStore.java @@ -22,11 +22,12 @@ import org.ehcache.config.ResourceType; import org.ehcache.config.units.MemoryUnit; import org.ehcache.core.events.StoreEventDispatcher; +import org.ehcache.core.spi.service.StatisticsService; +import org.ehcache.core.statistics.OperationStatistic; import org.ehcache.spi.resilience.StoreAccessException; import org.ehcache.core.events.NullStoreEventDispatcher; import org.ehcache.impl.internal.events.ThreadLocalStoreEventDispatcher; import org.ehcache.impl.internal.store.offheap.factories.EhcacheSegmentFactory; -import org.ehcache.impl.internal.store.offheap.portability.OffHeapValueHolderPortability; import org.ehcache.impl.internal.store.offheap.portability.SerializerPortability; import org.ehcache.core.spi.time.TimeSource; import org.ehcache.core.spi.time.TimeSourceService; @@ -52,8 +53,6 @@ import org.terracotta.offheapstore.storage.PointerSize; import org.terracotta.offheapstore.storage.portability.Portability; import org.terracotta.offheapstore.util.Factory; -import org.terracotta.statistics.OperationStatistic; -import org.terracotta.statistics.StatisticsManager; import java.util.Collection; import java.util.Collections; @@ -76,8 +75,8 @@ public class OffHeapStore extends AbstractOffHeapStore { private volatile EhcacheConcurrentOffHeapClockCache> map; - public OffHeapStore(final Configuration config, TimeSource timeSource, StoreEventDispatcher eventDispatcher, long sizeInBytes) { - super(config, timeSource, eventDispatcher); + public OffHeapStore(final Configuration config, TimeSource timeSource, StoreEventDispatcher eventDispatcher, long sizeInBytes, StatisticsService statisticsService) { + super(config, timeSource, eventDispatcher, statisticsService); EvictionAdvisor evictionAdvisor = config.getEvictionAdvisor(); if (evictionAdvisor != null) { this.evictionAdvisor = wrap(evictionAdvisor); @@ -103,9 +102,9 @@ private EhcacheConcurrentOffHeapClockCache> createBacki HeuristicConfiguration config = new HeuristicConfiguration(size); PageSource source = new UpfrontAllocatingPageSource(getBufferSource(), config.getMaximumSize(), config.getMaximumChunkSize(), config.getMinimumChunkSize()); Portability keyPortability = new SerializerPortability<>(keySerializer); - Portability> elementPortability = new OffHeapValueHolderPortability<>(valueSerializer); + Portability> valuePortability = createValuePortability(valueSerializer); Factory>> storageEngineFactory = OffHeapBufferStorageEngine.createFactory(PointerSize.INT, source, config - .getSegmentDataPageSize(), keyPortability, elementPortability, false, true); + .getSegmentDataPageSize(), keyPortability, valuePortability, false, true); Factory>> segmentFactory = new EhcacheSegmentFactory<>( source, @@ -132,7 +131,6 @@ public static class Provider extends BaseStoreProvider implements AuthoritativeT private static final Logger LOGGER = LoggerFactory.getLogger(Provider.class); - private volatile ServiceProvider serviceProvider; private final Set> createdStores = Collections.newSetFromMap(new ConcurrentWeakIdentityHashMap<>()); private final Map, OperationStatistic[]> tierOperationStatistics = new ConcurrentWeakIdentityHashMap<>(); @@ -142,17 +140,17 @@ protected ResourceType getResourceType() { } @Override - public int rank(final Set> resourceTypes, final Collection> serviceConfigs) { + public int rank(final Set> resourceTypes, final Collection> serviceConfigs) { return resourceTypes.equals(Collections.singleton(ResourceType.Core.OFFHEAP)) ? 1 : 0; } @Override - public int rankAuthority(ResourceType authorityResource, Collection> serviceConfigs) { + public int rankAuthority(ResourceType authorityResource, Collection> serviceConfigs) { return authorityResource.equals(ResourceType.Core.OFFHEAP) ? 1 : 0; } @Override - public OffHeapStore createStore(Configuration storeConfig, ServiceConfiguration... serviceConfigs) { + public OffHeapStore createStore(Configuration storeConfig, ServiceConfiguration... serviceConfigs) { OffHeapStore store = createStoreInternal(storeConfig, new ThreadLocalStoreEventDispatcher<>(storeConfig.getDispatcherConcurrency()), serviceConfigs); tierOperationStatistics.put(store, new OperationStatistic[] { @@ -163,11 +161,11 @@ public OffHeapStore createStore(Configuration storeConfig, Se return store; } - private OffHeapStore createStoreInternal(Configuration storeConfig, StoreEventDispatcher eventDispatcher, ServiceConfiguration... serviceConfigs) { - if (serviceProvider == null) { + private OffHeapStore createStoreInternal(Configuration storeConfig, StoreEventDispatcher eventDispatcher, ServiceConfiguration... serviceConfigs) { + if (getServiceProvider() == null) { throw new NullPointerException("ServiceProvider is null in OffHeapStore.Provider."); } - TimeSource timeSource = serviceProvider.getService(TimeSourceService.class).getTimeSource(); + TimeSource timeSource = getServiceProvider().getService(TimeSourceService.class).getTimeSource(); SizedResourcePool offHeapPool = storeConfig.getResourcePools().getPoolForResource(getResourceType()); if (!(offHeapPool.getUnit() instanceof MemoryUnit)) { @@ -177,7 +175,7 @@ private OffHeapStore createStoreInternal(Configuration storeC OffHeapStore offHeapStore = new OffHeapStore<>(storeConfig, timeSource, eventDispatcher, unit.toBytes(offHeapPool - .getSize())); + .getSize()), getServiceProvider().getService(StatisticsService.class)); createdStores.add(offHeapStore); return offHeapStore; } @@ -189,7 +187,7 @@ public void releaseStore(Store resource) { } OffHeapStore offHeapStore = (OffHeapStore) resource; close(offHeapStore); - StatisticsManager.nodeFor(offHeapStore).clean(); + getStatisticsService().ifPresent(s -> s.cleanForNode(offHeapStore)); tierOperationStatistics.remove(offHeapStore); } @@ -224,19 +222,17 @@ static void init(final OffHeapStore resource) { resource.map = resource.createBackingMap(resource.sizeInBytes, resource.keySerializer, resource.valueSerializer, resource.evictionAdvisor); } - @Override - public void start(ServiceProvider serviceProvider) { - this.serviceProvider = serviceProvider; - } - @Override public void stop() { - this.serviceProvider = null; - createdStores.clear(); + try { + createdStores.clear(); + } finally { + super.stop(); + } } @Override - public AuthoritativeTier createAuthoritativeTier(Configuration storeConfig, ServiceConfiguration... serviceConfigs) { + public AuthoritativeTier createAuthoritativeTier(Configuration storeConfig, ServiceConfiguration... serviceConfigs) { OffHeapStore authoritativeTier = createStoreInternal(storeConfig, new ThreadLocalStoreEventDispatcher<>(storeConfig .getDispatcherConcurrency()), serviceConfigs); @@ -259,7 +255,7 @@ public void initAuthoritativeTier(AuthoritativeTier resource) { } @Override - public LowerCachingTier createCachingTier(Configuration storeConfig, ServiceConfiguration... serviceConfigs) { + public LowerCachingTier createCachingTier(Configuration storeConfig, ServiceConfiguration... serviceConfigs) { OffHeapStore lowerCachingTier = createStoreInternal(storeConfig, NullStoreEventDispatcher.nullStoreEventDispatcher(), serviceConfigs); tierOperationStatistics.put(lowerCachingTier, new OperationStatistic[] { diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreProviderFactory.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreProviderFactory.java similarity index 85% rename from impl/src/main/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreProviderFactory.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreProviderFactory.java index 4d3865d91e..8619fd8a13 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreProviderFactory.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreProviderFactory.java @@ -16,21 +16,23 @@ package org.ehcache.impl.internal.store.offheap; -import org.ehcache.spi.service.ServiceCreationConfiguration; import org.ehcache.core.spi.service.ServiceFactory; +import org.ehcache.spi.service.ServiceCreationConfiguration; +import org.osgi.service.component.annotations.Component; /** * OffHeapStoreProviderFactory */ +@Component public class OffHeapStoreProviderFactory implements ServiceFactory { @Override - public OffHeapStore.Provider create(ServiceCreationConfiguration configuration) { + public OffHeapStore.Provider create(ServiceCreationConfiguration configuration) { return new OffHeapStore.Provider(); } @Override - public Class getServiceType() { + public Class getServiceType() { return OffHeapStore.Provider.class; } } diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreUtils.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreUtils.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreUtils.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreUtils.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/offheap/OffHeapValueHolder.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/OffHeapValueHolder.java similarity index 91% rename from impl/src/main/java/org/ehcache/impl/internal/store/offheap/OffHeapValueHolder.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/OffHeapValueHolder.java index daab162d26..66130cf2c2 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/store/offheap/OffHeapValueHolder.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/OffHeapValueHolder.java @@ -26,17 +26,10 @@ */ public abstract class OffHeapValueHolder extends AbstractValueHolder { - public static final TimeUnit TIME_UNIT = TimeUnit.MILLISECONDS; - public OffHeapValueHolder(long id, long creationTime, long expireTime) { super(id, creationTime, expireTime); } - @Override - final protected TimeUnit nativeTimeUnit() { - return TIME_UNIT; - } - @Override public boolean equals(Object other) { if (this == other) return true; diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/offheap/SwitchableEvictionAdvisor.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/SwitchableEvictionAdvisor.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/store/offheap/SwitchableEvictionAdvisor.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/SwitchableEvictionAdvisor.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/offheap/factories/EhcacheSegmentFactory.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/factories/EhcacheSegmentFactory.java similarity index 84% rename from impl/src/main/java/org/ehcache/impl/internal/store/offheap/factories/EhcacheSegmentFactory.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/factories/EhcacheSegmentFactory.java index d1e0cec986..a90d214c53 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/store/offheap/factories/EhcacheSegmentFactory.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/factories/EhcacheSegmentFactory.java @@ -16,6 +16,7 @@ package org.ehcache.impl.internal.store.offheap.factories; +import org.ehcache.core.spi.store.Store; import org.ehcache.impl.internal.store.offheap.SwitchableEvictionAdvisor; import org.terracotta.offheapstore.Metadata; import org.terracotta.offheapstore.ReadWriteLockedOffHeapClockCache; @@ -24,6 +25,9 @@ import org.terracotta.offheapstore.storage.StorageEngine; import org.terracotta.offheapstore.util.Factory; +import java.nio.IntBuffer; +import java.util.Iterator; +import java.util.Set; import java.util.concurrent.locks.Lock; /** @@ -105,8 +109,33 @@ public boolean evict(int index, boolean shrink) { } } + @Override + protected Set> createEntrySet() { + return new EntrySet(); + } + public interface EvictionListener { void onEviction(K key, V value); } + + private class EntrySet extends LockedEntrySet { + @Override + public Iterator> iterator() { + readLock().lock(); + try { + return new LockedEntryIterator() { + + @Override + protected Entry create(IntBuffer entry) { + Entry entryObject = super.create(entry); + ((Store.ValueHolder) entryObject.getValue()).get(); + return entryObject; + } + }; + } finally { + readLock().unlock(); + } + } + } } } diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/offheap/portability/OffHeapValueHolderPortability.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/portability/OffHeapValueHolderPortability.java similarity index 81% rename from impl/src/main/java/org/ehcache/impl/internal/store/offheap/portability/OffHeapValueHolderPortability.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/portability/OffHeapValueHolderPortability.java index e37475dd78..22b97eacb2 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/store/offheap/portability/OffHeapValueHolderPortability.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/portability/OffHeapValueHolderPortability.java @@ -24,6 +24,7 @@ import org.terracotta.offheapstore.storage.portability.WriteContext; import java.nio.ByteBuffer; +import java.util.concurrent.TimeUnit; /** * OffHeapValueHolderPortability @@ -52,9 +53,9 @@ public ByteBuffer encode(OffHeapValueHolder valueHolder) { } ByteBuffer byteBuffer = ByteBuffer.allocate(serialized.remaining() + FIELDS_OVERHEAD); byteBuffer.putLong(valueHolder.getId()); - byteBuffer.putLong(valueHolder.creationTime(OffHeapValueHolder.TIME_UNIT)); - byteBuffer.putLong(valueHolder.lastAccessTime(OffHeapValueHolder.TIME_UNIT)); - byteBuffer.putLong(valueHolder.expirationTime(OffHeapValueHolder.TIME_UNIT)); + byteBuffer.putLong(valueHolder.creationTime()); + byteBuffer.putLong(valueHolder.lastAccessTime()); + byteBuffer.putLong(valueHolder.expirationTime()); byteBuffer.putLong(0L); // represent the hits on previous versions. It is kept for compatibility reasons with previously saved data byteBuffer.put(serialized); byteBuffer.flip(); @@ -78,7 +79,10 @@ public OffHeapValueHolder decode(ByteBuffer byteBuffer, WriteContext writeCon long lastAccessTime = byteBuffer.getLong(); long expireTime = byteBuffer.getLong(); byteBuffer.getLong(); // hits read from disk. It is kept for compatibility reasons with previously saved data - return new LazyOffHeapValueHolder<>(id, byteBuffer.slice(), serializer, - creationTime, expireTime, lastAccessTime, writeContext); + return createLazyOffHeapValueHolder(id, byteBuffer.slice(), serializer, creationTime, expireTime, lastAccessTime, writeContext); + } + + protected OffHeapValueHolder createLazyOffHeapValueHolder(long id, ByteBuffer byteBuffer, Serializer serializer, long creationTime, long expireTime, long lastAccessTime, WriteContext writeContext) { + return new LazyOffHeapValueHolder<>(id, byteBuffer, serializer, creationTime, expireTime, lastAccessTime, writeContext); } } diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/offheap/portability/SerializerPortability.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/portability/SerializerPortability.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/store/offheap/portability/SerializerPortability.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/offheap/portability/SerializerPortability.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/tiering/CompoundCachingTier.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/tiering/CompoundCachingTier.java similarity index 88% rename from impl/src/main/java/org/ehcache/impl/internal/store/tiering/CompoundCachingTier.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/tiering/CompoundCachingTier.java index 72aa322d17..1883026f03 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/store/tiering/CompoundCachingTier.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/tiering/CompoundCachingTier.java @@ -18,18 +18,19 @@ import org.ehcache.config.ResourceType; import org.ehcache.core.CacheConfigurationChangeListener; import org.ehcache.core.collections.ConcurrentWeakIdentityHashMap; +import org.ehcache.core.spi.service.StatisticsService; import org.ehcache.core.spi.store.Store; import org.ehcache.spi.resilience.StoreAccessException; import org.ehcache.core.spi.store.tiering.CachingTier; import org.ehcache.core.spi.store.tiering.HigherCachingTier; import org.ehcache.core.spi.store.tiering.LowerCachingTier; +import org.ehcache.spi.service.OptionalServiceDependencies; import org.ehcache.spi.service.Service; import org.ehcache.spi.service.ServiceConfiguration; import org.ehcache.spi.service.ServiceDependencies; import org.ehcache.spi.service.ServiceProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.terracotta.statistics.StatisticsManager; import java.util.AbstractMap; import java.util.ArrayList; @@ -68,8 +69,6 @@ public CompoundCachingTier(HigherCachingTier higher, final LowerCachingTie } }); - StatisticsManager.associate(higher).withParent(this); - StatisticsManager.associate(lower).withParent(this); } private void notifyInvalidation(K key, Store.ValueHolder p) { @@ -118,6 +117,26 @@ public Store.ValueHolder getOrComputeIfAbsent(K key, final Function getOrDefault(K key, Function> source) throws StoreAccessException { + try { + return higher.getOrDefault(key, keyParam -> { + try { + Store.ValueHolder valueHolder = lower.get(keyParam); + if (valueHolder != null) { + return valueHolder; + } + + return source.apply(keyParam); + } catch (StoreAccessException cae) { + throw new ComputationException(cae); + } + }); + } catch (ComputationException ce) { + throw ce.getStoreAccessException(); + } + } + @Override public void invalidate(final K key) throws StoreAccessException { try { @@ -191,12 +210,13 @@ public List getConfigurationChangeListeners() @ServiceDependencies({HigherCachingTier.Provider.class, LowerCachingTier.Provider.class}) + @OptionalServiceDependencies("org.ehcache.core.spi.service.StatisticsService") public static class Provider implements CachingTier.Provider { private volatile ServiceProvider serviceProvider; private final ConcurrentMap, Map.Entry> providersMap = new ConcurrentWeakIdentityHashMap<>(); @Override - public CachingTier createCachingTier(Store.Configuration storeConfig, ServiceConfiguration... serviceConfigs) { + public CachingTier createCachingTier(Store.Configuration storeConfig, ServiceConfiguration... serviceConfigs) { if (serviceProvider == null) { throw new RuntimeException("ServiceProvider is null."); } @@ -216,6 +236,11 @@ public CachingTier createCachingTier(Store.Configuration stor LowerCachingTier lowerCachingTier = lowerProvider.createCachingTier(storeConfig, serviceConfigs); CompoundCachingTier compoundCachingTier = new CompoundCachingTier<>(higherCachingTier, lowerCachingTier); + StatisticsService statisticsService = serviceProvider.getService(StatisticsService.class); + if (statisticsService != null) { + statisticsService.registerWithParent(higherCachingTier, compoundCachingTier); + statisticsService.registerWithParent(lowerCachingTier, compoundCachingTier); + } providersMap.put(compoundCachingTier, new AbstractMap.SimpleEntry<>(higherProvider, lowerProvider)); return compoundCachingTier; } @@ -245,7 +270,7 @@ public void initCachingTier(CachingTier resource) { } @Override - public int rankCachingTier(Set> resourceTypes, Collection> serviceConfigs) { + public int rankCachingTier(Set> resourceTypes, Collection> serviceConfigs) { return resourceTypes.equals(unmodifiableSet(EnumSet.of(HEAP, OFFHEAP))) ? 2 : 0; } diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/tiering/CompoundCachingTierProviderFactory.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/tiering/CompoundCachingTierProviderFactory.java similarity index 83% rename from impl/src/main/java/org/ehcache/impl/internal/store/tiering/CompoundCachingTierProviderFactory.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/tiering/CompoundCachingTierProviderFactory.java index 4fd20e68f4..7fd64e0d3e 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/store/tiering/CompoundCachingTierProviderFactory.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/tiering/CompoundCachingTierProviderFactory.java @@ -15,20 +15,22 @@ */ package org.ehcache.impl.internal.store.tiering; -import org.ehcache.spi.service.ServiceCreationConfiguration; import org.ehcache.core.spi.service.ServiceFactory; +import org.ehcache.spi.service.ServiceCreationConfiguration; +import org.osgi.service.component.annotations.Component; /** * @author Ludovic Orban */ +@Component public class CompoundCachingTierProviderFactory implements ServiceFactory { @Override - public CompoundCachingTier.Provider create(ServiceCreationConfiguration serviceConfiguration) { + public CompoundCachingTier.Provider create(ServiceCreationConfiguration serviceConfiguration) { return new CompoundCachingTier.Provider(); } @Override - public Class getServiceType() { + public Class getServiceType() { return CompoundCachingTier.Provider.class; } } diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/tiering/TieredStore.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/tiering/TieredStore.java similarity index 83% rename from impl/src/main/java/org/ehcache/impl/internal/store/tiering/TieredStore.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/tiering/TieredStore.java index 6bad9c8e92..c2b3c0bbe2 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/store/tiering/TieredStore.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/tiering/TieredStore.java @@ -21,16 +21,17 @@ import org.ehcache.core.CacheConfigurationChangeListener; import org.ehcache.core.collections.ConcurrentWeakIdentityHashMap; import org.ehcache.core.exceptions.StorePassThroughException; +import org.ehcache.core.spi.service.StatisticsService; import org.ehcache.core.spi.store.Store; import org.ehcache.spi.resilience.StoreAccessException; import org.ehcache.core.spi.store.events.StoreEventSource; import org.ehcache.core.spi.store.tiering.AuthoritativeTier; import org.ehcache.core.spi.store.tiering.CachingTier; +import org.ehcache.spi.service.OptionalServiceDependencies; import org.ehcache.spi.service.Service; import org.ehcache.spi.service.ServiceConfiguration; import org.ehcache.spi.service.ServiceDependencies; import org.ehcache.spi.service.ServiceProvider; -import org.terracotta.statistics.StatisticsManager; import java.util.AbstractMap; import java.util.ArrayList; @@ -39,6 +40,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Set; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicReference; @@ -78,8 +80,6 @@ public void invalidateAllWithHash(long hash) throws StoreAccessException { } }); - StatisticsManager.associate(cachingTier).withParent(this); - StatisticsManager.associate(authoritativeTier).withParent(this); } @Override @@ -111,6 +111,15 @@ public PutStatus put(final K key, final V value) throws StoreAccessException { } } + @Override + public ValueHolder getAndPut(K key, V value) throws StoreAccessException { + try { + return authoritativeTier.getAndPut(key, value); + } finally { + cachingTier().invalidate(key); + } + } + @Override public ValueHolder putIfAbsent(K key, V value, Consumer put) throws StoreAccessException { try { @@ -129,6 +138,15 @@ public boolean remove(K key) throws StoreAccessException { } } + @Override + public ValueHolder getAndRemove(K key) throws StoreAccessException { + try { + return authoritativeTier.getAndRemove(key); + } finally { + cachingTier().invalidate(key); + } + } + @Override public RemoveStatus remove(K key, V value) throws StoreAccessException { try { @@ -213,7 +231,74 @@ public StoreEventSource getStoreEventSource() { @Override public Iterator>> iterator() { - return authoritativeTier.iterator(); + Iterator>> authoritativeIterator = authoritativeTier.iterator(); + + return new Iterator>>() { + + private StoreAccessException prefetchFailure; + private Cache.Entry> prefetched; + + { + try { + prefetched = advance(); + } catch (StoreAccessException sae) { + prefetchFailure = sae; + } + } + + @Override + public boolean hasNext() { + return prefetched != null || prefetchFailure != null; + } + + @Override + public Cache.Entry> next() throws StoreAccessException { + StoreAccessException nextFailure = prefetchFailure; + Cache.Entry> next = prefetched; + + try { + prefetchFailure = null; + prefetched = advance(); + } catch (StoreAccessException sae) { + prefetchFailure = sae; + prefetched = null; + } + if (nextFailure == null) { + if (next == null) { + throw new NoSuchElementException(); + } else { + return next; + } + } else { + throw nextFailure; + } + } + + private Cache.Entry> advance() throws StoreAccessException { + while (authoritativeIterator.hasNext()) { + Cache.Entry> next = authoritativeIterator.next(); + K authKey = next.getKey(); + + ValueHolder checked = cachingTier().getOrDefault(authKey, key -> next.getValue()); + + if (checked != null) { + return new Cache.Entry>() { + @Override + public K getKey() { + return authKey; + } + + @Override + public ValueHolder getValue() { + return checked; + } + }; + } + } + + return null; + } + }; } @Override @@ -309,13 +394,14 @@ private ValueHolder handleStoreAccessException(StoreAccessException ce) throw } @ServiceDependencies({CachingTier.Provider.class, AuthoritativeTier.Provider.class}) + @OptionalServiceDependencies("org.ehcache.core.spi.service.StatisticsService") public static class Provider implements Store.Provider { private volatile ServiceProvider serviceProvider; private final ConcurrentMap, Map.Entry> providersMap = new ConcurrentWeakIdentityHashMap<>(); @Override - public int rank(final Set> resourceTypes, final Collection> serviceConfigs) { + public int rank(final Set> resourceTypes, final Collection> serviceConfigs) { if (resourceTypes.size() == 1) { return 0; } @@ -358,8 +444,8 @@ private ResourceType getAuthorityResource(Set> resourceTypes) } @Override - public Store createStore(Configuration storeConfig, ServiceConfiguration... serviceConfigs) { - final List> enhancedServiceConfigs = new ArrayList<>(Arrays.asList(serviceConfigs)); + public Store createStore(Configuration storeConfig, ServiceConfiguration... serviceConfigs) { + final List> enhancedServiceConfigs = new ArrayList<>(Arrays.asList(serviceConfigs)); final ResourcePools resourcePools = storeConfig.getResourcePools(); if (rank(resourcePools.getResourceTypeSet(), enhancedServiceConfigs) == 0) { @@ -375,17 +461,22 @@ public Store createStore(Configuration storeConfig, ServiceCo CachingTier.Provider cachingTierProvider = getCachingTierProvider(cachingResources, enhancedServiceConfigs); - final ServiceConfiguration[] configurations = - enhancedServiceConfigs.toArray(new ServiceConfiguration[enhancedServiceConfigs.size()]); + final ServiceConfiguration[] configurations = + enhancedServiceConfigs.toArray(new ServiceConfiguration[enhancedServiceConfigs.size()]); CachingTier cachingTier = cachingTierProvider.createCachingTier(storeConfig, configurations); AuthoritativeTier authoritativeTier = authoritativeTierProvider.createAuthoritativeTier(storeConfig, configurations); TieredStore store = new TieredStore<>(cachingTier, authoritativeTier); + StatisticsService statisticsService = serviceProvider.getService(StatisticsService.class); + if (statisticsService != null) { + statisticsService.registerWithParent(cachingTier, store); + statisticsService.registerWithParent(authoritativeTier, store); + } registerStore(store, cachingTierProvider, authoritativeTierProvider); return store; } - private CachingTier.Provider getCachingTierProvider(Set> cachingResources, List> enhancedServiceConfigs) { + private CachingTier.Provider getCachingTierProvider(Set> cachingResources, List> enhancedServiceConfigs) { CachingTier.Provider cachingTierProvider = null; Collection cachingTierProviders = serviceProvider.getServicesOfType(CachingTier.Provider.class); for (CachingTier.Provider provider : cachingTierProviders) { @@ -400,7 +491,7 @@ private CachingTier.Provider getCachingTierProvider(Set> caching return cachingTierProvider; } - AuthoritativeTier.Provider getAuthoritativeTierProvider(ResourceType authorityResource, List> enhancedServiceConfigs) { + AuthoritativeTier.Provider getAuthoritativeTierProvider(ResourceType authorityResource, List> enhancedServiceConfigs) { AuthoritativeTier.Provider authoritativeTierProvider = null; Collection authorityProviders = serviceProvider.getServicesOfType(AuthoritativeTier.Provider.class); int highestRank = 0; @@ -485,6 +576,11 @@ public ValueHolder getOrComputeIfAbsent(final K key, final Function getOrDefault(K key, Function> source) { + return source.apply(key); + } + @Override public void invalidate(final K key) { // noop diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/tiering/TieredStoreProviderFactory.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/tiering/TieredStoreProviderFactory.java similarity index 85% rename from impl/src/main/java/org/ehcache/impl/internal/store/tiering/TieredStoreProviderFactory.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/store/tiering/TieredStoreProviderFactory.java index 0a215b71b1..17bb23a501 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/store/tiering/TieredStoreProviderFactory.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/store/tiering/TieredStoreProviderFactory.java @@ -16,21 +16,23 @@ package org.ehcache.impl.internal.store.tiering; -import org.ehcache.spi.service.ServiceCreationConfiguration; import org.ehcache.core.spi.service.ServiceFactory; +import org.ehcache.spi.service.ServiceCreationConfiguration; +import org.osgi.service.component.annotations.Component; /** * @author Ludovic Orban */ +@Component public class TieredStoreProviderFactory implements ServiceFactory { @Override - public TieredStore.Provider create(ServiceCreationConfiguration configuration) { + public TieredStore.Provider create(ServiceCreationConfiguration configuration) { return new TieredStore.Provider(); } @Override - public Class getServiceType() { + public Class getServiceType() { return TieredStore.Provider.class; } } diff --git a/core/src/main/java/org/ehcache/core/internal/util/Pacer.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/util/Pacer.java similarity index 97% rename from core/src/main/java/org/ehcache/core/internal/util/Pacer.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/util/Pacer.java index 36f91686a1..5f49bf05ae 100644 --- a/core/src/main/java/org/ehcache/core/internal/util/Pacer.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/internal/util/Pacer.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.ehcache.core.internal.util; +package org.ehcache.impl.internal.util; import org.ehcache.core.spi.time.TimeSource; diff --git a/impl/src/main/java/org/ehcache/impl/internal/util/ServiceUtil.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/util/ServiceUtil.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/util/ServiceUtil.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/util/ServiceUtil.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/util/ThreadFactoryUtil.java b/ehcache-impl/src/main/java/org/ehcache/impl/internal/util/ThreadFactoryUtil.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/util/ThreadFactoryUtil.java rename to ehcache-impl/src/main/java/org/ehcache/impl/internal/util/ThreadFactoryUtil.java diff --git a/impl/src/main/java/org/ehcache/impl/persistence/DefaultDiskResourceService.java b/ehcache-impl/src/main/java/org/ehcache/impl/persistence/DefaultDiskResourceService.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/persistence/DefaultDiskResourceService.java rename to ehcache-impl/src/main/java/org/ehcache/impl/persistence/DefaultDiskResourceService.java diff --git a/impl/src/main/java/org/ehcache/impl/persistence/DefaultLocalPersistenceService.java b/ehcache-impl/src/main/java/org/ehcache/impl/persistence/DefaultLocalPersistenceService.java similarity index 95% rename from impl/src/main/java/org/ehcache/impl/persistence/DefaultLocalPersistenceService.java rename to ehcache-impl/src/main/java/org/ehcache/impl/persistence/DefaultLocalPersistenceService.java index f2f85ab871..81f05f4554 100644 --- a/impl/src/main/java/org/ehcache/impl/persistence/DefaultLocalPersistenceService.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/persistence/DefaultLocalPersistenceService.java @@ -24,6 +24,7 @@ import org.ehcache.spi.service.ServiceProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.terracotta.utilities.io.Files; import java.io.File; import java.io.FileNotFoundException; @@ -33,7 +34,6 @@ import java.nio.channels.OverlappingFileLockException; import static org.ehcache.impl.persistence.FileUtils.createLocationIfRequiredAndVerify; -import static org.ehcache.impl.persistence.FileUtils.recursiveDeleteDirectoryContent; import static org.ehcache.impl.persistence.FileUtils.safeIdentifier; import static org.ehcache.impl.persistence.FileUtils.tryRecursiveDelete; import static org.ehcache.impl.persistence.FileUtils.validateName; @@ -121,7 +121,9 @@ public synchronized void stop() { // org.ehcache.internal.persistence.DefaultLocalPersistenceServiceTest.testLocksDirectoryAndUnlocks() // passes on windows rw.close(); - if (!lockFile.delete()) { + try { + Files.delete(lockFile.toPath()); + } catch (IOException e) { LOGGER.debug("Lock file was not deleted {}.", lockFile.getPath()); } } catch (IOException e) { @@ -185,16 +187,14 @@ public void destroySafeSpace(SafeSpaceIdentifier safeSpaceId, boolean verbose) { */ public void destroyAll(String owner) { File ownerDirectory = new File(rootDirectory, owner); - boolean cleared = true; if (ownerDirectory.exists() && ownerDirectory.isDirectory()) { - cleared = false; - if (recursiveDeleteDirectoryContent(ownerDirectory)) { + if (tryRecursiveDelete(ownerDirectory)) { LOGGER.debug("Destroyed all file based persistence contexts owned by {}", owner); - cleared = ownerDirectory.delete(); + } else { + LOGGER.warn("Could not delete all file based persistence contexts owned by {}", owner); } - } - if (!cleared) { - LOGGER.warn("Could not delete all file based persistence contexts owned by {}", owner); + } else { + LOGGER.warn("Could not delete all file based persistence contexts owned by {} - is not a directory!", owner); } } diff --git a/impl/src/main/java/org/ehcache/impl/persistence/FileBasedStateRepository.java b/ehcache-impl/src/main/java/org/ehcache/impl/persistence/FileBasedStateRepository.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/persistence/FileBasedStateRepository.java rename to ehcache-impl/src/main/java/org/ehcache/impl/persistence/FileBasedStateRepository.java diff --git a/impl/src/main/java/org/ehcache/impl/persistence/FileUtils.java b/ehcache-impl/src/main/java/org/ehcache/impl/persistence/FileUtils.java similarity index 71% rename from impl/src/main/java/org/ehcache/impl/persistence/FileUtils.java rename to ehcache-impl/src/main/java/org/ehcache/impl/persistence/FileUtils.java index 33ce48bbb7..4a1ff25b34 100644 --- a/impl/src/main/java/org/ehcache/impl/persistence/FileUtils.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/persistence/FileUtils.java @@ -17,23 +17,22 @@ package org.ehcache.impl.persistence; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; - import org.ehcache.CachePersistenceException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.terracotta.utilities.io.Files; import java.io.File; -import java.nio.charset.Charset; +import java.io.IOException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.util.ArrayDeque; -import java.util.Deque; +import java.time.Duration; import java.util.HashSet; import java.util.Locale; import java.util.Set; import static java.lang.Integer.toHexString; -import static java.nio.charset.Charset.forName; +import static java.nio.charset.StandardCharsets.UTF_8; /** * A bunch of utility functions, mainly used by {@link DefaultLocalPersistenceService} and @@ -41,7 +40,6 @@ */ final class FileUtils { private static final Logger LOGGER = LoggerFactory.getLogger(FileUtils.class); - private static final Charset UTF8 = forName("UTF8"); private static final int DEL = 0x7F; private static final char ESCAPE = '%'; @@ -99,67 +97,19 @@ static void create(File directory) throws CachePersistenceException { } } - static boolean recursiveDeleteDirectoryContent(File file) { - File[] contents = file.listFiles(); - if (contents == null) { - throw new IllegalArgumentException("File " + file.getAbsolutePath() + " is not a directory"); - } else { - boolean deleteSuccessful = true; - for (File f : contents) { - deleteSuccessful &= tryRecursiveDelete(f); - } - return deleteSuccessful; - } - } - - private static boolean recursiveDelete(File file) { - Deque toDelete = new ArrayDeque<>(); - toDelete.push(file); - while (!toDelete.isEmpty()) { - File target = toDelete.pop(); - File[] contents = target.listFiles(); - if (contents == null || contents.length == 0) { - if (target.exists() && !target.delete()) { - return false; - } - } else { - toDelete.push(target); - for (File f : contents) { - toDelete.push(f); - } - } - } - return true; - } - - @SuppressFBWarnings("DM_GC") static boolean tryRecursiveDelete(File file) { - boolean interrupted = false; try { - for (int i = 0; i < 5; i++) { - if (recursiveDelete(file) || !isWindows()) { - return true; - } else { - System.gc(); - System.runFinalization(); - - try { - Thread.sleep(50); - } catch (InterruptedException e) { - interrupted = true; - } - } - } - } finally { - if (interrupted) { - Thread.currentThread().interrupt(); - } + Files.deleteTree(file.toPath(), Duration.ofMillis(250), FileUtils::gc); + return true; + } catch (IOException ioe) { + return false; } - return false; } - private static boolean isWindows() { - return System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("windows"); + @SuppressFBWarnings("DM_GC") + private static void gc() { + System.gc(); + System.runFinalization(); } /** @@ -192,7 +142,7 @@ static String safeIdentifier(String name, boolean withSha1) { private static String sha1(String input) { StringBuilder sb = new StringBuilder(); - for (byte b : getSha1Digest().digest(input.getBytes(UTF8))) { + for (byte b : getSha1Digest().digest(input.getBytes(UTF_8))) { sb.append(toHexString((b & 0xf0) >>> 4)); sb.append(toHexString((b & 0xf))); } diff --git a/impl/src/main/java/org/ehcache/impl/persistence/package-info.java b/ehcache-impl/src/main/java/org/ehcache/impl/persistence/package-info.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/persistence/package-info.java rename to ehcache-impl/src/main/java/org/ehcache/impl/persistence/package-info.java diff --git a/impl/src/main/java/org/ehcache/impl/serialization/ByteArraySerializer.java b/ehcache-impl/src/main/java/org/ehcache/impl/serialization/ByteArraySerializer.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/serialization/ByteArraySerializer.java rename to ehcache-impl/src/main/java/org/ehcache/impl/serialization/ByteArraySerializer.java diff --git a/impl/src/main/java/org/ehcache/impl/serialization/CharSerializer.java b/ehcache-impl/src/main/java/org/ehcache/impl/serialization/CharSerializer.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/serialization/CharSerializer.java rename to ehcache-impl/src/main/java/org/ehcache/impl/serialization/CharSerializer.java diff --git a/impl/src/main/java/org/ehcache/impl/serialization/CompactJavaSerializer.java b/ehcache-impl/src/main/java/org/ehcache/impl/serialization/CompactJavaSerializer.java similarity index 75% rename from impl/src/main/java/org/ehcache/impl/serialization/CompactJavaSerializer.java rename to ehcache-impl/src/main/java/org/ehcache/impl/serialization/CompactJavaSerializer.java index 64d850cffa..8dfbf59df4 100644 --- a/impl/src/main/java/org/ehcache/impl/serialization/CompactJavaSerializer.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/serialization/CompactJavaSerializer.java @@ -28,7 +28,6 @@ import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Map.Entry; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.locks.Lock; @@ -37,10 +36,12 @@ import org.ehcache.spi.persistence.StateHolder; import org.ehcache.spi.persistence.StateRepository; import org.ehcache.spi.serialization.SerializerException; -import org.ehcache.impl.internal.util.ByteBufferInputStream; +import org.ehcache.core.util.ByteBufferInputStream; import org.ehcache.spi.serialization.Serializer; import org.ehcache.spi.serialization.StatefulSerializer; +import static java.lang.Math.max; + /** * A trivially compressed Java serialization based serializer. *

@@ -51,12 +52,13 @@ */ public class CompactJavaSerializer implements StatefulSerializer { - private volatile StateHolder readLookup; - private final ConcurrentMap readLookupLocalCache = new ConcurrentHashMap<>(); - private final ConcurrentMap writeLookup = new ConcurrentHashMap<>(); + private volatile StateHolder persistentState; + private final ConcurrentMap readLookupCache = new ConcurrentHashMap<>(); + private final ConcurrentMap writeLookupCache = new ConcurrentHashMap<>(); private final Lock lock = new ReentrantLock(); private int nextStreamIndex = 0; + private boolean potentiallyInconsistent; private final transient ClassLoader loader; @@ -78,8 +80,8 @@ public static Class> asTypedSerializer() { @Override public void init(final StateRepository stateRepository) { - this.readLookup = stateRepository.getPersistentStateHolder("CompactJavaSerializer-ObjectStreamClassIndex", Integer.class, ObjectStreamClass.class, c -> true, null); - loadMappingsInWriteContext(readLookup.entrySet()); + this.persistentState = stateRepository.getPersistentStateHolder("CompactJavaSerializer-ObjectStreamClassIndex", Integer.class, ObjectStreamClass.class, c -> true, null); + refreshMappingsFromStateRepository(); } /** @@ -130,57 +132,80 @@ public boolean equals(T object, ByteBuffer binary) throws ClassNotFoundException return object.equals(read(binary)); } - private int getOrAddMapping(ObjectStreamClass desc) throws IOException { + private int getOrAddMapping(ObjectStreamClass desc) { SerializableDataKey probe = new SerializableDataKey(desc, false); - Integer rep = writeLookup.get(probe); - if (rep != null) { + Integer rep = writeLookupCache.get(probe); + if (rep == null) { + return addMappingUnderLock(desc, probe); + } else { return rep; } + } - // Install new rep - locking + private int addMappingUnderLock(ObjectStreamClass desc, SerializableDataKey probe) { lock.lock(); try { - return addMappingUnderLock(desc, probe); + if (potentiallyInconsistent) { + refreshMappingsFromStateRepository(); + potentiallyInconsistent = false; + } + while (true) { + Integer rep = writeLookupCache.get(probe); + if (rep != null) { + return rep; + } + rep = nextStreamIndex++; + + try { + ObjectStreamClass disconnected = disconnect(desc); + ObjectStreamClass existingOsc = persistentState.putIfAbsent(rep, disconnected); + if (existingOsc == null) { + cacheMapping(rep, disconnected); + return rep; + } else { + cacheMapping(rep, disconnect(existingOsc)); + } + } catch (Throwable t) { + potentiallyInconsistent = true; + throw t; + } + } } finally { lock.unlock(); } } - private int addMappingUnderLock(ObjectStreamClass desc, SerializableDataKey probe) { - ObjectStreamClass disconnected = disconnect(desc); - SerializableDataKey key = new SerializableDataKey(disconnected, true); - while (true) { - Integer rep = writeLookup.get(probe); - if (rep != null) { - return rep; - } - rep = nextStreamIndex++; - - ObjectStreamClass existingOsc = readLookup.putIfAbsent(rep, disconnected); - if (existingOsc == null) { - writeLookup.put(key, rep); - readLookupLocalCache.put(rep, disconnected); - return rep; - } else { - ObjectStreamClass discOsc = disconnect(existingOsc); - writeLookup.put(new SerializableDataKey(discOsc, true), rep); - readLookupLocalCache.put(rep, discOsc); - } + private void refreshMappingsFromStateRepository() { + int highestIndex = -1; + for (Entry entry : persistentState.entrySet()) { + Integer index = entry.getKey(); + cacheMapping(entry.getKey(), disconnect(entry.getValue())); + highestIndex = max(highestIndex, index); } + nextStreamIndex = highestIndex + 1; } - private void loadMappingsInWriteContext(Set> entries) { - for (Entry entry : entries) { - Integer index = entry.getKey(); - ObjectStreamClass discOsc = disconnect(entry.getValue()); - readLookupLocalCache.put(index, discOsc); - if (writeLookup.putIfAbsent(new SerializableDataKey(discOsc, true), index) != null) { - throw new AssertionError("Corrupted data " + readLookup); + private void cacheMapping(Integer index, ObjectStreamClass disconnectedOsc) { + readLookupCache.merge(index, disconnectedOsc, (existing, update) -> { + if (equals(existing, update)) { + return existing; + } else { + throw new AssertionError("Corrupted data:\n" + + "State Repository: " + persistentState + "\n" + + "Local Write Lookup: " + writeLookupCache + "\n" + + "Local Read Lookup: " + readLookupCache); } - if (nextStreamIndex < index + 1) { - nextStreamIndex = index + 1; + }); + writeLookupCache.merge(new SerializableDataKey(disconnectedOsc, true), index, (existing, update) -> { + if (existing.equals(update)) { + return existing; + } else { + throw new AssertionError("Corrupted data:\n" + + "State Repository: " + persistentState + "\n" + + "Local Write Lookup: " + writeLookupCache + "\n" + + "Local Read Lookup: " + readLookupCache); } - } + }); } class OOS extends ObjectOutputStream { @@ -207,14 +232,11 @@ class OIS extends ObjectInputStream { @Override protected ObjectStreamClass readClassDescriptor() throws IOException { int key = readInt(); - ObjectStreamClass objectStreamClass = readLookupLocalCache.get(key); - if (objectStreamClass != null) { - return objectStreamClass; + ObjectStreamClass objectStreamClass = readLookupCache.get(key); + if (objectStreamClass == null) { + objectStreamClass = persistentState.get(key); + cacheMapping(key, disconnect(objectStreamClass)); } - objectStreamClass = readLookup.get(key); - ObjectStreamClass discOsc = disconnect(objectStreamClass); - readLookupLocalCache.put(key, discOsc); - writeLookup.putIfAbsent(new SerializableDataKey(discOsc, true), key); return objectStreamClass; } diff --git a/impl/src/main/java/org/ehcache/impl/serialization/DoubleSerializer.java b/ehcache-impl/src/main/java/org/ehcache/impl/serialization/DoubleSerializer.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/serialization/DoubleSerializer.java rename to ehcache-impl/src/main/java/org/ehcache/impl/serialization/DoubleSerializer.java diff --git a/impl/src/main/java/org/ehcache/impl/serialization/FloatSerializer.java b/ehcache-impl/src/main/java/org/ehcache/impl/serialization/FloatSerializer.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/serialization/FloatSerializer.java rename to ehcache-impl/src/main/java/org/ehcache/impl/serialization/FloatSerializer.java diff --git a/impl/src/main/java/org/ehcache/impl/serialization/IntegerSerializer.java b/ehcache-impl/src/main/java/org/ehcache/impl/serialization/IntegerSerializer.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/serialization/IntegerSerializer.java rename to ehcache-impl/src/main/java/org/ehcache/impl/serialization/IntegerSerializer.java diff --git a/impl/src/main/java/org/ehcache/impl/serialization/LongSerializer.java b/ehcache-impl/src/main/java/org/ehcache/impl/serialization/LongSerializer.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/serialization/LongSerializer.java rename to ehcache-impl/src/main/java/org/ehcache/impl/serialization/LongSerializer.java diff --git a/impl/src/main/java/org/ehcache/impl/serialization/PlainJavaSerializer.java b/ehcache-impl/src/main/java/org/ehcache/impl/serialization/PlainJavaSerializer.java similarity index 98% rename from impl/src/main/java/org/ehcache/impl/serialization/PlainJavaSerializer.java rename to ehcache-impl/src/main/java/org/ehcache/impl/serialization/PlainJavaSerializer.java index a5c8cb420d..a48c066453 100644 --- a/impl/src/main/java/org/ehcache/impl/serialization/PlainJavaSerializer.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/serialization/PlainJavaSerializer.java @@ -16,7 +16,7 @@ package org.ehcache.impl.serialization; -import org.ehcache.impl.internal.util.ByteBufferInputStream; +import org.ehcache.core.util.ByteBufferInputStream; import org.ehcache.spi.serialization.Serializer; import org.ehcache.spi.serialization.SerializerException; diff --git a/impl/src/main/java/org/ehcache/impl/serialization/StringSerializer.java b/ehcache-impl/src/main/java/org/ehcache/impl/serialization/StringSerializer.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/serialization/StringSerializer.java rename to ehcache-impl/src/main/java/org/ehcache/impl/serialization/StringSerializer.java diff --git a/impl/src/main/java/org/ehcache/impl/serialization/TransientStateHolder.java b/ehcache-impl/src/main/java/org/ehcache/impl/serialization/TransientStateHolder.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/serialization/TransientStateHolder.java rename to ehcache-impl/src/main/java/org/ehcache/impl/serialization/TransientStateHolder.java diff --git a/impl/src/main/java/org/ehcache/impl/serialization/TransientStateRepository.java b/ehcache-impl/src/main/java/org/ehcache/impl/serialization/TransientStateRepository.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/serialization/TransientStateRepository.java rename to ehcache-impl/src/main/java/org/ehcache/impl/serialization/TransientStateRepository.java diff --git a/impl/src/main/java/org/ehcache/impl/serialization/package-info.java b/ehcache-impl/src/main/java/org/ehcache/impl/serialization/package-info.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/serialization/package-info.java rename to ehcache-impl/src/main/java/org/ehcache/impl/serialization/package-info.java diff --git a/ehcache-impl/src/main/java/org/ehcache/impl/store/BaseStore.java b/ehcache-impl/src/main/java/org/ehcache/impl/store/BaseStore.java new file mode 100644 index 0000000000..d3971a68d1 --- /dev/null +++ b/ehcache-impl/src/main/java/org/ehcache/impl/store/BaseStore.java @@ -0,0 +1,134 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.impl.store; + +import org.ehcache.config.ResourceType; +import org.ehcache.core.config.store.StoreStatisticsConfiguration; +import org.ehcache.core.spi.service.StatisticsService; +import org.ehcache.core.statistics.StatisticType; +import org.ehcache.core.spi.store.Store; +import org.ehcache.core.statistics.OperationObserver; +import org.ehcache.core.statistics.OperationStatistic; +import org.ehcache.core.statistics.ZeroOperationStatistic; +import org.ehcache.spi.service.OptionalServiceDependencies; +import org.ehcache.spi.service.Service; +import org.ehcache.spi.service.ServiceProvider; + +import java.io.Serializable; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.Supplier; + +import static java.util.Optional.ofNullable; + +/** + * Base class to most stores. It provides functionality common to stores in general. A given store implementation is not required to extend + * it but the implementor might find it easier to do so. + */ +public abstract class BaseStore implements Store { + + /* Type of the keys stored in this store */ + protected final Class keyType; + /* Type of the values stored in this store */ + protected final Class valueType; + /** Tells if this store is by itself or in a tiered setup */ + protected final boolean operationStatisticsEnabled; + protected final StatisticsService statisticsService; + + public BaseStore(Configuration config, StatisticsService statisticsService) { + this(config.getKeyType(), config.getValueType(), config.isOperationStatisticsEnabled(), statisticsService); + } + + public BaseStore(Class keyType, Class valueType, boolean operationStatisticsEnabled, StatisticsService statisticsService) { + this.keyType = keyType; + this.valueType = valueType; + this.operationStatisticsEnabled = operationStatisticsEnabled; + this.statisticsService = statisticsService; + } + + protected void checkKey(K keyObject) { + if (!keyType.isInstance(Objects.requireNonNull((Object) keyObject))) { + throw new ClassCastException("Invalid key type, expected : " + keyType.getName() + " but was : " + keyObject.getClass().getName()); + } + } + + protected void checkValue(V valueObject) { + if (!valueType.isInstance(Objects.requireNonNull((Object) valueObject))) { + throw new ClassCastException("Invalid value type, expected : " + valueType.getName() + " but was : " + valueObject.getClass().getName()); + } + } + + /** + * Create an {@code OperationObserver} using {@code this} for the context. + * + * @param name name of the statistic + * @param outcome class of the possible outcomes + * @param canBeDisabled if this statistic can be disabled by a {@link StoreStatisticsConfiguration} + * @param type of the outcome + * @return the created observer + */ + protected > OperationObserver createObserver(String name, Class outcome, boolean canBeDisabled) { + if (statisticsService == null || !operationStatisticsEnabled && canBeDisabled) { + return ZeroOperationStatistic.get(); + } else { + return statisticsService.createOperationStatistics(name, outcome, getStatisticsTag(), this); + } + } + + protected void registerStatistic(String name, StatisticType type, Set tags, Supplier valueSupplier) { + if (statisticsService != null) { + statisticsService.registerStatistic(this, name, type, tags, valueSupplier); + } + } + + protected abstract String getStatisticsTag(); + + + @OptionalServiceDependencies("org.ehcache.core.spi.service.StatisticsService") + protected static abstract class BaseStoreProvider implements Store.Provider { + + private volatile ServiceProvider serviceProvider; + + protected , T extends Enum> OperationStatistic createTranslatedStatistic(BaseStore store, String statisticName, Map> translation, String targetName) { + return getStatisticsService() + .map(s -> s.registerStoreStatistics(store, targetName, getResourceType().getTierHeight(), store.getStatisticsTag(), translation, statisticName)) + .orElse(ZeroOperationStatistic.get()); + } + + @Override + public void start(ServiceProvider serviceProvider) { + this.serviceProvider = serviceProvider; + } + + @Override + public void stop() { + this.serviceProvider = null; + } + + protected ServiceProvider getServiceProvider() { + return this.serviceProvider; + } + + protected abstract ResourceType getResourceType(); + + protected Optional getStatisticsService() { + return ofNullable(serviceProvider.getService(StatisticsService.class)); + } + } +} diff --git a/impl/src/main/java/org/ehcache/impl/internal/events/ScopedStoreEventDispatcher.java b/ehcache-impl/src/main/java/org/ehcache/impl/store/DefaultStoreEventDispatcher.java similarity index 71% rename from impl/src/main/java/org/ehcache/impl/internal/events/ScopedStoreEventDispatcher.java rename to ehcache-impl/src/main/java/org/ehcache/impl/store/DefaultStoreEventDispatcher.java index 2fba80a9ab..c194991132 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/events/ScopedStoreEventDispatcher.java +++ b/ehcache-impl/src/main/java/org/ehcache/impl/store/DefaultStoreEventDispatcher.java @@ -14,17 +14,17 @@ * limitations under the License. */ -package org.ehcache.impl.internal.events; +package org.ehcache.impl.store; import org.ehcache.core.events.StoreEventSink; +import org.ehcache.impl.internal.events.AbstractStoreEventDispatcher; /** - * ScopedStoreEventDispatcher + * The default {@link org.ehcache.core.events.StoreEventDispatcher} implementation. */ -public class ScopedStoreEventDispatcher extends AbstractStoreEventDispatcher { +public class DefaultStoreEventDispatcher extends AbstractStoreEventDispatcher { - - public ScopedStoreEventDispatcher(int dispatcherConcurrency) { + public DefaultStoreEventDispatcher(int dispatcherConcurrency) { super(dispatcherConcurrency); } @@ -35,7 +35,7 @@ public StoreEventSink eventSink() { StoreEventSink noOpEventSink = (StoreEventSink) NO_OP_EVENT_SINK; return noOpEventSink; } else { - return new InvocationScopedEventSink<>(getFilters(), isEventOrdering(), getOrderedQueues(), getListeners()); + return super.eventSink(); } } } diff --git a/impl/src/main/java/org/ehcache/impl/store/HashUtils.java b/ehcache-impl/src/main/java/org/ehcache/impl/store/HashUtils.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/store/HashUtils.java rename to ehcache-impl/src/main/java/org/ehcache/impl/store/HashUtils.java diff --git a/impl/src/main/resources/META-INF/services/org.ehcache.core.spi.service.ServiceFactory b/ehcache-impl/src/main/resources/META-INF/services/org.ehcache.core.spi.service.ServiceFactory similarity index 94% rename from impl/src/main/resources/META-INF/services/org.ehcache.core.spi.service.ServiceFactory rename to ehcache-impl/src/main/resources/META-INF/services/org.ehcache.core.spi.service.ServiceFactory index b7a4bc98f6..c2cf30fb9a 100644 --- a/impl/src/main/resources/META-INF/services/org.ehcache.core.spi.service.ServiceFactory +++ b/ehcache-impl/src/main/resources/META-INF/services/org.ehcache.core.spi.service.ServiceFactory @@ -16,5 +16,4 @@ org.ehcache.impl.internal.loaderwriter.writebehind.WriteBehindProviderFactory org.ehcache.impl.internal.events.CacheEventNotificationListenerServiceProviderFactory org.ehcache.impl.internal.spi.copy.DefaultCopyProviderFactory org.ehcache.impl.internal.sizeof.DefaultSizeOfEngineProviderFactory -org.ehcache.impl.internal.statistics.DefaultStatisticsServiceFactory org.ehcache.impl.internal.spi.resilience.DefaultResilienceStrategyProviderFactory diff --git a/impl/src/slow-test/java/org/ehcache/impl/internal/concurrent/ConcurrentHashMapITest.java b/ehcache-impl/src/slow-test/java/org/ehcache/impl/internal/concurrent/ConcurrentHashMapITest.java similarity index 99% rename from impl/src/slow-test/java/org/ehcache/impl/internal/concurrent/ConcurrentHashMapITest.java rename to ehcache-impl/src/slow-test/java/org/ehcache/impl/internal/concurrent/ConcurrentHashMapITest.java index 7932ba1919..84f8356cce 100644 --- a/impl/src/slow-test/java/org/ehcache/impl/internal/concurrent/ConcurrentHashMapITest.java +++ b/ehcache-impl/src/slow-test/java/org/ehcache/impl/internal/concurrent/ConcurrentHashMapITest.java @@ -29,7 +29,7 @@ import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; /** * @author Ludovic Orban diff --git a/impl/src/test/java/org/ehcache/EhcacheRuntimeConfigurationTest.java b/ehcache-impl/src/test/java/org/ehcache/EhcacheRuntimeConfigurationTest.java similarity index 97% rename from impl/src/test/java/org/ehcache/EhcacheRuntimeConfigurationTest.java rename to ehcache-impl/src/test/java/org/ehcache/EhcacheRuntimeConfigurationTest.java index 9a3ae2ad58..570feecd3e 100644 --- a/impl/src/test/java/org/ehcache/EhcacheRuntimeConfigurationTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/EhcacheRuntimeConfigurationTest.java @@ -17,8 +17,8 @@ package org.ehcache; import org.ehcache.config.CacheConfiguration; -import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.config.ResourcePools; +import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.config.builders.ResourcePoolsBuilder; import org.ehcache.config.ResourceType; import org.ehcache.config.builders.CacheManagerBuilder; @@ -26,15 +26,12 @@ import org.ehcache.config.units.EntryUnit; import org.junit.Rule; import org.junit.Test; - -import java.io.File; -import java.io.IOException; +import org.junit.rules.TemporaryFolder; import org.ehcache.config.units.MemoryUnit; -import org.junit.rules.TemporaryFolder; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; /** diff --git a/ehcache-impl/src/test/java/org/ehcache/config/builders/CacheConfigurationBuilderTest.java b/ehcache-impl/src/test/java/org/ehcache/config/builders/CacheConfigurationBuilderTest.java new file mode 100644 index 0000000000..4eaa86f64b --- /dev/null +++ b/ehcache-impl/src/test/java/org/ehcache/config/builders/CacheConfigurationBuilderTest.java @@ -0,0 +1,579 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.config.builders; + +import org.ehcache.config.*; +import org.ehcache.config.units.EntryUnit; +import org.ehcache.config.units.MemoryUnit; +import org.ehcache.core.spi.service.ServiceUtils; +import org.ehcache.expiry.ExpiryPolicy; +import org.ehcache.impl.config.copy.DefaultCopierConfiguration; +import org.ehcache.impl.config.loaderwriter.DefaultCacheLoaderWriterConfiguration; +import org.ehcache.impl.config.resilience.DefaultResilienceStrategyConfiguration; +import org.ehcache.impl.config.serializer.DefaultSerializerConfiguration; +import org.ehcache.impl.config.store.disk.OffHeapDiskStoreConfiguration; +import org.ehcache.impl.config.store.heap.DefaultSizeOfEngineConfiguration; +import org.ehcache.impl.copy.SerializingCopier; +import org.ehcache.impl.internal.classes.ClassInstanceConfiguration; +import org.ehcache.impl.internal.resilience.RobustResilienceStrategy; +import org.ehcache.impl.serialization.JavaSerializer; +import org.ehcache.spi.copy.Copier; +import org.ehcache.spi.loaderwriter.CacheLoaderWriter; +import org.ehcache.spi.loaderwriter.CacheLoaderWriterConfiguration; +import org.ehcache.spi.resilience.RecoveryStore; +import org.ehcache.spi.resilience.ResilienceStrategy; +import org.ehcache.spi.serialization.Serializer; +import org.ehcache.spi.service.Service; +import org.ehcache.spi.service.ServiceConfiguration; +import org.ehcache.test.MockitoUtil; +import org.hamcrest.Matcher; +import org.hamcrest.Matchers; +import org.hamcrest.core.IsSame; +import org.junit.Test; + +import static java.util.function.UnaryOperator.identity; +import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; +import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; +import static org.ehcache.core.spi.service.ServiceUtils.findSingletonAmongst; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.assertEquals; +import static org.ehcache.test.MockitoUtil.mock; +import static org.junit.Assert.fail; + +public class CacheConfigurationBuilderTest { + + @Test + public void testWithEvictionAdvisor() throws Exception { + EvictionAdvisor evictionAdvisor = (key, value) -> false; + + CacheConfiguration cacheConfiguration = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) + .withEvictionAdvisor(evictionAdvisor) + .build(); + + @SuppressWarnings("unchecked") + Matcher> evictionAdvisorMatcher = sameInstance(cacheConfiguration + .getEvictionAdvisor()); + assertThat(evictionAdvisor, evictionAdvisorMatcher); + } + + @Test + public void testWithLoaderWriter() throws Exception { + CacheLoaderWriter loaderWriter = mock(CacheLoaderWriter.class); + + CacheConfiguration cacheConfiguration = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) + .withLoaderWriter(loaderWriter) + .build(); + + CacheLoaderWriterConfiguration cacheLoaderWriterConfiguration = ServiceUtils.findSingletonAmongst(DefaultCacheLoaderWriterConfiguration.class, cacheConfiguration.getServiceConfigurations()); + Object instance = ((ClassInstanceConfiguration) cacheLoaderWriterConfiguration).getInstance(); + assertThat(instance, Matchers.sameInstance(loaderWriter)); + } + + @Test + public void testWithoutLoaderWriter() { + CacheConfiguration cacheConfiguration = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) + .withLoaderWriter(mock(CacheLoaderWriter.class)) + .withoutLoaderWriter() + .build(); + + assertThat(cacheConfiguration.getServiceConfigurations(), not(hasItem(instanceOf(DefaultCacheLoaderWriterConfiguration.class)))); + } + + @Test + public void testWithKeySerializer() throws Exception { + Serializer keySerializer = mock(Serializer.class); + + CacheConfiguration cacheConfiguration = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) + .withKeySerializer(keySerializer) + .build(); + + + DefaultSerializerConfiguration serializerConfiguration = ServiceUtils.findSingletonAmongst(DefaultSerializerConfiguration.class, cacheConfiguration.getServiceConfigurations()); + assertThat(serializerConfiguration.getType(), is(DefaultSerializerConfiguration.Type.KEY)); + Object instance = serializerConfiguration.getInstance(); + assertThat(instance, Matchers.sameInstance(keySerializer)); + } + + @Test + public void testWithKeySerializerClass() throws Exception { + @SuppressWarnings("unchecked") + CacheConfiguration cacheConfiguration = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) + .withKeySerializer((Class) JavaSerializer.class) + .build(); + + + DefaultSerializerConfiguration serializerConfiguration = ServiceUtils.findSingletonAmongst(DefaultSerializerConfiguration.class, cacheConfiguration.getServiceConfigurations()); + assertThat(serializerConfiguration.getType(), is(DefaultSerializerConfiguration.Type.KEY)); + assertThat(serializerConfiguration.getClazz(), sameInstance(JavaSerializer.class)); + } + + @Test + public void testWithoutKeySerializer() throws Exception { + CacheConfiguration cacheConfiguration = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) + .withKeySerializer(MockitoUtil.>mock(Serializer.class)) + .withDefaultKeySerializer() + .build(); + + assertThat(cacheConfiguration.getServiceConfigurations(), not(hasItem(instanceOf(DefaultSerializerConfiguration.class)))); + } + + @Test + public void testWithValueSerializer() throws Exception { + Serializer valueSerializer = mock(Serializer.class); + + CacheConfiguration cacheConfiguration = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) + .withValueSerializer(valueSerializer) + .build(); + + + DefaultSerializerConfiguration serializerConfiguration = ServiceUtils.findSingletonAmongst(DefaultSerializerConfiguration.class, cacheConfiguration.getServiceConfigurations()); + assertThat(serializerConfiguration.getType(), is(DefaultSerializerConfiguration.Type.VALUE)); + Object instance = ((ClassInstanceConfiguration) serializerConfiguration).getInstance(); + assertThat(instance, Matchers.sameInstance(valueSerializer)); + } + + @Test + public void testWithValueSerializerClass() throws Exception { + @SuppressWarnings("unchecked") + CacheConfiguration cacheConfiguration = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) + .withValueSerializer((Class) JavaSerializer.class) + .build(); + + + DefaultSerializerConfiguration serializerConfiguration = ServiceUtils.findSingletonAmongst(DefaultSerializerConfiguration.class, cacheConfiguration.getServiceConfigurations()); + assertThat(serializerConfiguration.getType(), is(DefaultSerializerConfiguration.Type.VALUE)); + assertThat(serializerConfiguration.getClazz(), sameInstance(JavaSerializer.class)); + } + + @Test + public void testWithoutValueSerializer() throws Exception { + CacheConfiguration cacheConfiguration = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) + .withValueSerializer(MockitoUtil.>mock(Serializer.class)) + .withDefaultValueSerializer() + .build(); + + assertThat(cacheConfiguration.getServiceConfigurations(), not(hasItem(instanceOf(DefaultSerializerConfiguration.class)))); + } + + @Test + public void testWithKeyCopier() throws Exception { + Copier keyCopier = mock(Copier.class); + + CacheConfiguration cacheConfiguration = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) + .withKeyCopier(keyCopier) + .build(); + + + DefaultCopierConfiguration copierConfiguration = ServiceUtils.findSingletonAmongst(DefaultCopierConfiguration.class, cacheConfiguration.getServiceConfigurations()); + assertThat(copierConfiguration.getType(), is(DefaultCopierConfiguration.Type.KEY)); + Object instance = copierConfiguration.getInstance(); + assertThat(instance, Matchers.sameInstance(keyCopier)); + } + + @Test + public void testWithKeySerializingCopier() { + CacheConfiguration cacheConfiguration = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) + .withKeySerializingCopier() + .build(); + + + DefaultCopierConfiguration copierConfiguration = ServiceUtils.findSingletonAmongst(DefaultCopierConfiguration.class, cacheConfiguration.getServiceConfigurations()); + assertThat(copierConfiguration.getType(), is(DefaultCopierConfiguration.Type.KEY)); + assertThat(copierConfiguration.getClazz(), Matchers.sameInstance(SerializingCopier.class)); + } + + @Test + public void testWithoutKeyCopier() { + CacheConfiguration cacheConfiguration = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) + .withKeyCopier(MockitoUtil.>mock(Copier.class)) + .withoutKeyCopier() + .build(); + + assertThat(cacheConfiguration.getServiceConfigurations(), not(hasItem(instanceOf(DefaultCopierConfiguration.class)))); + } + + @Test + public void testWithValueCopier() throws Exception { + Copier valueCopier = mock(Copier.class); + + CacheConfiguration cacheConfiguration = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) + .withValueCopier(valueCopier) + .build(); + + + DefaultCopierConfiguration copierConfiguration = ServiceUtils.findSingletonAmongst(DefaultCopierConfiguration.class, cacheConfiguration.getServiceConfigurations()); + assertThat(copierConfiguration.getType(), is(DefaultCopierConfiguration.Type.VALUE)); + Object instance = copierConfiguration.getInstance(); + assertThat(instance, Matchers.sameInstance(valueCopier)); + } + + @Test + public void testWithValueSerializingCopier() { + CacheConfiguration cacheConfiguration = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) + .withValueSerializingCopier() + .build(); + + + DefaultCopierConfiguration copierConfiguration = ServiceUtils.findSingletonAmongst(DefaultCopierConfiguration.class, cacheConfiguration.getServiceConfigurations()); + assertThat(copierConfiguration.getType(), is(DefaultCopierConfiguration.Type.VALUE)); + assertThat(copierConfiguration.getClazz(), Matchers.sameInstance(SerializingCopier.class)); + } + + @Test + public void testWithoutValueCopier() { + CacheConfiguration cacheConfiguration = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) + .withValueCopier(MockitoUtil.>mock(Copier.class)) + .withoutValueCopier() + .build(); + + assertThat(cacheConfiguration.getServiceConfigurations(), not(hasItem(instanceOf(DefaultCopierConfiguration.class)))); + } + + @Test + public void testNothing() { + final CacheConfigurationBuilder builder = newCacheConfigurationBuilder(Long.class, CharSequence.class, heap(10)); + + final ExpiryPolicy expiry = ExpiryPolicyBuilder.timeToIdleExpiration(ExpiryPolicy.INFINITE); + + builder + .withEvictionAdvisor((key, value) -> value.charAt(0) == 'A') + .withExpiry(expiry) + .build(); + } + + @Test + public void testOffheapGetsAddedToCacheConfiguration() { + CacheConfigurationBuilder builder = newCacheConfigurationBuilder(Long.class, CharSequence.class, + ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES) + .offheap(10, MemoryUnit.MB)); + + final ExpiryPolicy expiry = ExpiryPolicyBuilder.timeToIdleExpiration(ExpiryPolicy.INFINITE); + + CacheConfiguration config = builder + .withEvictionAdvisor((key, value) -> value.charAt(0) == 'A') + .withExpiry(expiry) + .build(); + assertThat(config.getResourcePools().getPoolForResource(ResourceType.Core.OFFHEAP).getType(), Matchers.is(ResourceType.Core.OFFHEAP)); + assertThat(config.getResourcePools().getPoolForResource(ResourceType.Core.OFFHEAP).getUnit(), Matchers.is(MemoryUnit.MB)); + } + + @Test + public void testSizeOf() { + CacheConfigurationBuilder builder = newCacheConfigurationBuilder(String.class, String.class, heap(10)); + + builder = builder.withSizeOfMaxObjectSize(10, MemoryUnit.B).withSizeOfMaxObjectGraph(100); + CacheConfiguration configuration = builder.build(); + + DefaultSizeOfEngineConfiguration sizeOfEngineConfiguration = ServiceUtils.findSingletonAmongst(DefaultSizeOfEngineConfiguration.class, configuration.getServiceConfigurations()); + assertThat(sizeOfEngineConfiguration, notNullValue()); + assertEquals(sizeOfEngineConfiguration.getMaxObjectSize(), 10); + assertEquals(sizeOfEngineConfiguration.getUnit(), MemoryUnit.B); + assertEquals(sizeOfEngineConfiguration.getMaxObjectGraphSize(), 100); + + builder = builder.withSizeOfMaxObjectGraph(1000); + configuration = builder.build(); + + sizeOfEngineConfiguration = ServiceUtils.findSingletonAmongst(DefaultSizeOfEngineConfiguration.class, configuration.getServiceConfigurations()); + assertEquals(sizeOfEngineConfiguration.getMaxObjectGraphSize(), 1000); + + } + + @Test + public void testCopyingOfExistingConfiguration() { + Class keyClass = Integer.class; + Class valueClass = String.class; + ClassLoader loader = mock(ClassLoader.class); + @SuppressWarnings("unchecked") + EvictionAdvisor eviction = mock(EvictionAdvisor.class); + @SuppressWarnings("unchecked") + ExpiryPolicy expiry = mock(ExpiryPolicy.class); + ServiceConfiguration service = mock(ServiceConfiguration.class); + + CacheConfiguration configuration = newCacheConfigurationBuilder(Integer.class, String.class, heap(10)) + .withClassLoader(loader) + .withEvictionAdvisor(eviction) + .withExpiry(expiry) + .withService(service) + .build(); + + CacheConfiguration copy = newCacheConfigurationBuilder(configuration).build(); + + assertThat(copy.getKeyType(), equalTo(keyClass)); + assertThat(copy.getValueType(), equalTo(valueClass)); + assertThat(copy.getClassLoader(), equalTo(loader)); + + assertThat(copy.getEvictionAdvisor(), IsSame.sameInstance(eviction)); + assertThat(copy.getExpiryPolicy(), IsSame.sameInstance(expiry)); + assertThat(copy.getServiceConfigurations(), contains(IsSame.sameInstance(service))); + } + + @Test + public void testWithResilienceStrategyInstance() throws Exception { + ResilienceStrategy resilienceStrategy = mock(ResilienceStrategy.class); + + CacheConfiguration cacheConfiguration = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) + .withResilienceStrategy(resilienceStrategy) + .build(); + + DefaultResilienceStrategyConfiguration resilienceStrategyConfiguration = ServiceUtils.findSingletonAmongst(DefaultResilienceStrategyConfiguration.class, cacheConfiguration.getServiceConfigurations()); + Object instance = resilienceStrategyConfiguration.getInstance(); + assertThat(instance, sameInstance(resilienceStrategy)); + } + + @Test + public void testWithResilienceStrategyClass() throws Exception { + CacheConfiguration cacheConfiguration = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) + .withResilienceStrategy(CustomResilience.class, "Hello World") + .build(); + + DefaultResilienceStrategyConfiguration resilienceStrategyConfiguration = ServiceUtils.findSingletonAmongst(DefaultResilienceStrategyConfiguration.class, cacheConfiguration.getServiceConfigurations()); + assertThat(resilienceStrategyConfiguration.getInstance(), nullValue()); + assertThat(resilienceStrategyConfiguration.getClazz(), sameInstance(CustomResilience.class)); + assertThat(resilienceStrategyConfiguration.getArguments(), arrayContaining("Hello World")); + + } + + @Test + public void testWithDefaultResilienceStrategy() { + CacheConfiguration cacheConfiguration = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) + .withResilienceStrategy(mock(ResilienceStrategy.class)) + .withDefaultResilienceStrategy() + .build(); + + assertThat(cacheConfiguration.getServiceConfigurations(), not(hasItem(instanceOf(DefaultResilienceStrategyConfiguration.class)))); + } + + @Test + public void testWithServiceAddsNewConfiguration() { + CacheConfigurationBuilder oldBuilder = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)); + + ServiceConfiguration serviceConfiguration = new IncompatibleServiceConfig(); + + CacheConfigurationBuilder newBuilder = oldBuilder.withService(serviceConfiguration); + + assertThat(oldBuilder.build().getServiceConfigurations(), not(hasItem(sameInstance(serviceConfiguration)))); + assertThat(newBuilder.build().getServiceConfigurations(), hasItem(sameInstance(serviceConfiguration))); + } + + @Test + public void testIncompatibleServiceRemovesExistingConfiguration() { + ServiceConfiguration oldConfig = new IncompatibleServiceConfig(); + ServiceConfiguration newConfig = new IncompatibleServiceConfig(); + + CacheConfigurationBuilder oldBuilder = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)).withService(oldConfig); + + CacheConfigurationBuilder newBuilder = oldBuilder.withService(newConfig); + + assertThat(oldBuilder.build().getServiceConfigurations(), both(hasItem(sameInstance(oldConfig))).and(not(hasItem(sameInstance(newConfig))))); + assertThat(newBuilder.build().getServiceConfigurations(), both(hasItem(sameInstance(newConfig))).and(not(hasItem(sameInstance(oldConfig))))); + } + + @Test + public void testCompatibleServiceJoinsExistingConfiguration() { + ServiceConfiguration oldConfig = new CompatibleServiceConfig(); + ServiceConfiguration newConfig = new CompatibleServiceConfig(); + + CacheConfigurationBuilder oldBuilder = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)).withService(oldConfig); + + CacheConfigurationBuilder newBuilder = oldBuilder.withService(newConfig); + + assertThat(oldBuilder.build().getServiceConfigurations(), both(hasItem(sameInstance(oldConfig))).and(not(hasItem(sameInstance(newConfig))))); + assertThat(newBuilder.build().getServiceConfigurations(), both(hasItem(sameInstance(oldConfig))).and(hasItem(sameInstance(newConfig)))); + } + + @Test + public void testUpdateServicesWithNoServicesThrows() { + CacheConfigurationBuilder oldBuilder = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)); + try { + oldBuilder.updateServices(IncompatibleServiceConfig.class, identity()); + fail("Expected IllegalStateException"); + } catch (IllegalStateException e) { + //expected + } + } + + @Test + public void testUpdateServicesWithNullReturnThrows() { + CacheConfigurationBuilder oldBuilder = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) + .withService(new IncompatibleServiceConfig()); + try { + oldBuilder.updateServices(IncompatibleServiceConfig.class, c -> null); + fail("Expected NullPointerException"); + } catch (NullPointerException e) { + //expected + } + } + + + @Test + public void testUpdateServicesHitsAllServices() { + CompatibleServiceConfig configA = new CompatibleServiceConfig(); + CompatibleServiceConfig configB = new CompatibleServiceConfig(); + CacheConfigurationBuilder oldBuilder = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) + .withService(configA) + .withService(configB); + + CompatibleServiceConfig configA2 = new CompatibleServiceConfig(); + CompatibleServiceConfig configB2 = new CompatibleServiceConfig(); + CacheConfigurationBuilder newBuilder = oldBuilder.updateServices(CompatibleServiceConfig.class, c -> { + if (c == configA) { + return configA2; + } else if (c == configB) { + return configB2; + } else { + throw new AssertionError(); + } + }); + + assertThat(oldBuilder.build().getServiceConfigurations(), hasItems(configA, configB)); + assertThat(newBuilder.build().getServiceConfigurations(), hasItems(configA2, configB2)); + } + + @Test + public void testWithClassLoader() { + ClassLoader classLoader = mock(ClassLoader.class); + + CacheConfiguration cacheConfiguration = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) + .withClassLoader(classLoader) + .build(); + + assertThat(cacheConfiguration.getClassLoader(), sameInstance(classLoader)); + } + + @Test + public void testWithDefaultClassLoader() { + CacheConfiguration cacheConfiguration = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) + .withClassLoader(mock(ClassLoader.class)) + .withDefaultClassLoader() + .build(); + + assertThat(cacheConfiguration.getClassLoader(), nullValue()); + } + + @Test + public void testWithDiskStoreThreadPool() { + CacheConfiguration cacheConfiguration = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) + .withDiskStoreThreadPool("banana", 42) + .build(); + + OffHeapDiskStoreConfiguration config = findSingletonAmongst(OffHeapDiskStoreConfiguration.class, cacheConfiguration.getServiceConfigurations()); + + assertThat(config.getWriterConcurrency(), is(42)); + assertThat(config.getThreadPoolAlias(), is("banana")); + } + + @Test + public void testWithDefaultDiskStoreThreadPool() { + CacheConfiguration cacheConfiguration = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) + .withDiskStoreThreadPool("banana", 42) + .withDefaultDiskStoreThreadPool() + .build(); + + assertThat(cacheConfiguration.getServiceConfigurations(), not(hasItem(instanceOf(OffHeapDiskStoreConfiguration.class)))); + } + + @Test + public void testWithSizeOfConfig() { + CacheConfigurationBuilder builder = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)); + + builder = builder.withSizeOfMaxObjectGraph(42L); + { + CacheConfiguration cacheConfiguration = builder.build(); + DefaultSizeOfEngineConfiguration config = findSingletonAmongst(DefaultSizeOfEngineConfiguration.class, cacheConfiguration.getServiceConfigurations()); + assertThat(config.getMaxObjectGraphSize(), is(42L)); + assertThat(config.getMaxObjectSize(), is(Long.MAX_VALUE)); + assertThat(config.getUnit(), is(MemoryUnit.B)); + } + + builder = builder.withSizeOfMaxObjectSize(1024L, MemoryUnit.KB); + { + CacheConfiguration cacheConfiguration = builder.build(); + DefaultSizeOfEngineConfiguration config = findSingletonAmongst(DefaultSizeOfEngineConfiguration.class, cacheConfiguration.getServiceConfigurations()); + assertThat(config.getMaxObjectGraphSize(), is(42L)); + assertThat(config.getMaxObjectSize(), is(1024L)); + assertThat(config.getUnit(), is(MemoryUnit.KB)); + } + + builder = builder.withSizeOfMaxObjectGraph(43L); + { + CacheConfiguration cacheConfiguration = builder.build(); + DefaultSizeOfEngineConfiguration config = findSingletonAmongst(DefaultSizeOfEngineConfiguration.class, cacheConfiguration.getServiceConfigurations()); + assertThat(config.getMaxObjectGraphSize(), is(43L)); + assertThat(config.getMaxObjectSize(), is(1024L)); + assertThat(config.getUnit(), is(MemoryUnit.KB)); + } + } + + @Test + public void testWithDefaultSizeOfSettings() { + CacheConfiguration cacheConfiguration = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) + .withSizeOfMaxObjectGraph(42L) + .withSizeOfMaxObjectSize(1024L, MemoryUnit.KB) + .withDefaultSizeOfSettings() + .build(); + + assertThat(cacheConfiguration.getServiceConfigurations(), not(hasItem(instanceOf(DefaultSizeOfEngineConfiguration.class)))); + } + + static class CustomResilience extends RobustResilienceStrategy { + + public CustomResilience(RecoveryStore store, String blah) { + super(store); + } + } + + static class IncompatibleServiceConfig implements ServiceConfiguration { + + @Override + public Class getServiceType() { + return Service.class; + } + + @Override + public IncompatibleServiceConfig derive() throws UnsupportedOperationException { + return this; + } + + @Override + public ServiceConfiguration build(IncompatibleServiceConfig representation) throws UnsupportedOperationException { + return representation; + } + } + + static class CompatibleServiceConfig implements ServiceConfiguration { + @Override + public CompatibleServiceConfig derive() throws UnsupportedOperationException { + return this; + } + + @Override + public ServiceConfiguration build(CompatibleServiceConfig representation) throws UnsupportedOperationException { + return representation; + } + + @Override + public Class getServiceType() { + return Service.class; + } + + @Override + public boolean compatibleWith(ServiceConfiguration other) { + return true; + } + } +} diff --git a/impl/src/test/java/org/ehcache/config/builders/CacheManagerBuilderTest.java b/ehcache-impl/src/test/java/org/ehcache/config/builders/CacheManagerBuilderTest.java similarity index 69% rename from impl/src/test/java/org/ehcache/config/builders/CacheManagerBuilderTest.java rename to ehcache-impl/src/test/java/org/ehcache/config/builders/CacheManagerBuilderTest.java index 828d472853..86ef50c253 100644 --- a/impl/src/test/java/org/ehcache/config/builders/CacheManagerBuilderTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/config/builders/CacheManagerBuilderTest.java @@ -25,9 +25,7 @@ import org.ehcache.impl.serialization.CompactJavaSerializer; import org.ehcache.impl.serialization.JavaSerializer; import org.ehcache.spi.serialization.Serializer; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import java.util.concurrent.atomic.AtomicInteger; @@ -37,9 +35,6 @@ public class CacheManagerBuilderTest { - @Rule - public ExpectedException expectedException = ExpectedException.none(); - @Test public void testIsExtensible() { @@ -76,31 +71,41 @@ public void testCanOverrideSerializerConfig() { assertThat(managerBuilder.withSerializer(String.class, serializer2)).isNotNull(); } - @Test(expected = IllegalArgumentException.class) - public void testDuplicateServiceCreationConfigurationFails() { - newCacheManagerBuilder().using(new DefaultCopyProviderConfiguration()) - .using(new DefaultCopyProviderConfiguration()); + @Test + public void testDuplicateServiceCreationConfigurationOk() { + DefaultCopyProviderConfiguration configOne = new DefaultCopyProviderConfiguration(); + DefaultCopyProviderConfiguration configTwo = new DefaultCopyProviderConfiguration(); + CacheManagerBuilder builder = newCacheManagerBuilder() + .using(configOne) + .using(configTwo); + + assertThat(builder.build().getRuntimeConfiguration().getServiceCreationConfigurations()).contains(configTwo).doesNotContain(configOne); } - @Test + @Test @SuppressWarnings("deprecation") public void testDuplicateServiceCreationConfigurationOkWhenExplicit() { - assertThat(newCacheManagerBuilder().using(new DefaultCopyProviderConfiguration()) - .replacing(new DefaultCopyProviderConfiguration())).isNotNull(); + CacheManagerBuilder builder = newCacheManagerBuilder().using(new DefaultCopyProviderConfiguration()) + .replacing(new DefaultCopyProviderConfiguration()); + + assertThat(builder.build()).isNotNull(); } @Test public void testShouldNotBeAllowedToRegisterTwoCachesWithSameAlias() { - String cacheAlias = "cacheAliasSameName"; + String cacheAlias = "alias"; + + CacheConfiguration configOne = CacheConfigurationBuilder + .newCacheConfigurationBuilder(Integer.class, String.class, ResourcePoolsBuilder.heap(10)) + .build(); - CacheConfiguration cacheConfig = CacheConfigurationBuilder + CacheConfiguration configTwo = CacheConfigurationBuilder .newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(10)) .build(); - expectedException.expect(IllegalArgumentException.class); - expectedException.expectMessage("Cache alias 'cacheAliasSameName' already exists"); + CacheManager build = newCacheManagerBuilder() + .withCache(cacheAlias, configOne) + .withCache(cacheAlias, configTwo).build(); - CacheManagerBuilder.newCacheManagerBuilder() - .withCache(cacheAlias, cacheConfig) - .withCache(cacheAlias, cacheConfig); + assertThat(build.getRuntimeConfiguration().getCacheConfigurations().get(cacheAlias).getKeyType()).isEqualTo(Long.class); } } diff --git a/impl/src/test/java/org/ehcache/config/builders/ExpiryPolicyBuilderTest.java b/ehcache-impl/src/test/java/org/ehcache/config/builders/ExpiryPolicyBuilderTest.java similarity index 72% rename from impl/src/test/java/org/ehcache/config/builders/ExpiryPolicyBuilderTest.java rename to ehcache-impl/src/test/java/org/ehcache/config/builders/ExpiryPolicyBuilderTest.java index 5f7b65a0cf..0858c15ef0 100644 --- a/impl/src/test/java/org/ehcache/config/builders/ExpiryPolicyBuilderTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/config/builders/ExpiryPolicyBuilderTest.java @@ -19,6 +19,8 @@ import org.ehcache.expiry.ExpiryPolicy; import org.junit.Test; +import java.time.Duration; + import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.nullValue; @@ -64,12 +66,40 @@ public void testTTLExpiration() { @Test public void testExpiration() { - java.time.Duration creation = java.time.Duration.ofSeconds(1L); - java.time.Duration access = java.time.Duration.ofSeconds(2L); - java.time.Duration update = java.time.Duration.ofSeconds(3L); + Duration creation = Duration.ofSeconds(1L); + Duration access = Duration.ofSeconds(2L); + Duration update = Duration.ofSeconds(3L); ExpiryPolicy expiry = ExpiryPolicyBuilder.expiry().create(creation).access(access).update(update).build(); assertThat(expiry.getExpiryForCreation(this, this), equalTo(creation)); assertThat(expiry.getExpiryForAccess(this, () -> this), equalTo(access)); assertThat(expiry.getExpiryForUpdate(this, () -> this,this), equalTo(update)); } + + @Test + public void testExpirationFunctions() { + Duration creation = Duration.ofSeconds(1L); + Duration access = Duration.ofSeconds(2L); + Duration update = Duration.ofSeconds(3L); + ExpiryPolicy expiry = ExpiryPolicyBuilder.expiry() + .create((k, v) -> { + assertThat(k, equalTo(10L)); + assertThat(v, equalTo(20L)); + return creation; + }) + .access((k, v) -> { + assertThat(k, equalTo(10L)); + assertThat(v.get(), equalTo(20L)); + return access; + }) + .update((k, v1, v2) -> { + assertThat(k, equalTo(10L)); + assertThat(v1.get(), equalTo(20L)); + assertThat(v2, equalTo(30L)); + return update; + }) + .build(); + assertThat(expiry.getExpiryForCreation(10L, 20L), equalTo(creation)); + assertThat(expiry.getExpiryForAccess(10L, () -> 20L), equalTo(access)); + assertThat(expiry.getExpiryForUpdate(10L, () -> 20L,30L), equalTo(update)); + } } diff --git a/impl/src/test/java/org/ehcache/config/builders/PersistentCacheManagerTest.java b/ehcache-impl/src/test/java/org/ehcache/config/builders/PersistentCacheManagerTest.java similarity index 92% rename from impl/src/test/java/org/ehcache/config/builders/PersistentCacheManagerTest.java rename to ehcache-impl/src/test/java/org/ehcache/config/builders/PersistentCacheManagerTest.java index aa6326391e..a2ba2fa045 100644 --- a/impl/src/test/java/org/ehcache/config/builders/PersistentCacheManagerTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/config/builders/PersistentCacheManagerTest.java @@ -23,7 +23,6 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import java.io.File; @@ -33,10 +32,14 @@ import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder; import static org.ehcache.impl.internal.util.FileExistenceMatchers.containsCacheDirectory; import static org.ehcache.impl.internal.util.FileExistenceMatchers.isLocked; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.hasProperty; +import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; -import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; +import static org.terracotta.utilities.io.Files.delete; /** * @author Alex Snaps @@ -45,9 +48,6 @@ public class PersistentCacheManagerTest { private static final String TEST_CACHE_ALIAS = "test123"; - @Rule - public ExpectedException thrown = ExpectedException.none(); - @Rule public final TemporaryFolder folder = new TemporaryFolder(); @@ -57,7 +57,7 @@ public class PersistentCacheManagerTest { @Before public void setup() throws IOException { rootDirectory = folder.newFolder("testInitializesDiskResourceService"); - assertTrue(rootDirectory.delete()); + delete(rootDirectory.toPath()); builder = newCacheManagerBuilder().with(new CacheManagerPersistenceConfiguration(rootDirectory)); } @@ -79,9 +79,8 @@ public void testInitializesLocalPersistenceServiceAndCreateCache() throws IOExce @Test public void testDestroyCache_NullAliasNotAllowed() throws CachePersistenceException { PersistentCacheManager manager = builder.build(true); - thrown.expect(NullPointerException.class); - thrown.expectMessage("Alias cannot be null"); - manager.destroyCache(null); + NullPointerException thrown = assertThrows(NullPointerException.class, () -> manager.destroyCache(null)); + assertThat(thrown, hasProperty("message", is("Alias cannot be null"))); } @Test diff --git a/impl/src/test/java/org/ehcache/config/builders/ResourcePoolsBuilderTest.java b/ehcache-impl/src/test/java/org/ehcache/config/builders/ResourcePoolsBuilderTest.java similarity index 95% rename from impl/src/test/java/org/ehcache/config/builders/ResourcePoolsBuilderTest.java rename to ehcache-impl/src/test/java/org/ehcache/config/builders/ResourcePoolsBuilderTest.java index 8dfcc69c7b..522676ff9c 100644 --- a/impl/src/test/java/org/ehcache/config/builders/ResourcePoolsBuilderTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/config/builders/ResourcePoolsBuilderTest.java @@ -20,16 +20,17 @@ import org.ehcache.config.ResourceUnit; import org.ehcache.config.SizedResourcePool; import org.ehcache.config.units.MemoryUnit; -import org.ehcache.core.config.SizedResourcePoolImpl; +import org.ehcache.impl.config.SizedResourcePoolImpl; import org.hamcrest.Matchers; import org.junit.Test; import static org.ehcache.config.ResourceType.Core.HEAP; import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.*; +import static org.junit.Assert.fail; public class ResourcePoolsBuilderTest { diff --git a/impl/src/test/java/org/ehcache/config/builders/TieringTest.java b/ehcache-impl/src/test/java/org/ehcache/config/builders/TieringTest.java similarity index 100% rename from impl/src/test/java/org/ehcache/config/builders/TieringTest.java rename to ehcache-impl/src/test/java/org/ehcache/config/builders/TieringTest.java diff --git a/impl/src/test/java/org/ehcache/config/builders/UserManagedCacheBuilderTest.java b/ehcache-impl/src/test/java/org/ehcache/config/builders/UserManagedCacheBuilderTest.java similarity index 99% rename from impl/src/test/java/org/ehcache/config/builders/UserManagedCacheBuilderTest.java rename to ehcache-impl/src/test/java/org/ehcache/config/builders/UserManagedCacheBuilderTest.java index 2d0a12014b..b84c23242b 100644 --- a/impl/src/test/java/org/ehcache/config/builders/UserManagedCacheBuilderTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/config/builders/UserManagedCacheBuilderTest.java @@ -22,7 +22,7 @@ import org.ehcache.config.CacheRuntimeConfiguration; import org.ehcache.event.EventType; import org.ehcache.spi.loaderwriter.BulkCacheWritingException; -import org.ehcache.core.internal.service.ServiceLocator; +import org.ehcache.core.spi.ServiceLocator; import org.ehcache.impl.internal.spi.event.DefaultCacheEventListenerProviderTest; import org.junit.Test; diff --git a/impl/src/test/java/org/ehcache/config/builders/WriteBehindConfigurationBuilderTest.java b/ehcache-impl/src/test/java/org/ehcache/config/builders/WriteBehindConfigurationBuilderTest.java similarity index 98% rename from impl/src/test/java/org/ehcache/config/builders/WriteBehindConfigurationBuilderTest.java rename to ehcache-impl/src/test/java/org/ehcache/config/builders/WriteBehindConfigurationBuilderTest.java index 632177f00d..b7f93b975c 100644 --- a/impl/src/test/java/org/ehcache/config/builders/WriteBehindConfigurationBuilderTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/config/builders/WriteBehindConfigurationBuilderTest.java @@ -19,9 +19,9 @@ import java.util.concurrent.TimeUnit; import static org.ehcache.config.builders.WriteBehindConfigurationBuilder.newBatchedWriteBehindConfiguration; import static org.ehcache.config.builders.WriteBehindConfigurationBuilder.newUnBatchedWriteBehindConfiguration; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsNull.nullValue; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import org.junit.Test; diff --git a/impl/src/test/java/org/ehcache/core/events/CacheManagerListenerInteractionsTest.java b/ehcache-impl/src/test/java/org/ehcache/core/events/CacheManagerListenerInteractionsTest.java similarity index 98% rename from impl/src/test/java/org/ehcache/core/events/CacheManagerListenerInteractionsTest.java rename to ehcache-impl/src/test/java/org/ehcache/core/events/CacheManagerListenerInteractionsTest.java index bb67642410..6a51a5fe7c 100644 --- a/impl/src/test/java/org/ehcache/core/events/CacheManagerListenerInteractionsTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/core/events/CacheManagerListenerInteractionsTest.java @@ -27,8 +27,8 @@ import org.junit.Test; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNotNull; diff --git a/impl/src/test/java/org/ehcache/core/events/CacheManagerListenerTest.java b/ehcache-impl/src/test/java/org/ehcache/core/events/CacheManagerListenerTest.java similarity index 96% rename from impl/src/test/java/org/ehcache/core/events/CacheManagerListenerTest.java rename to ehcache-impl/src/test/java/org/ehcache/core/events/CacheManagerListenerTest.java index 19dc820aff..9b09f4fcb6 100644 --- a/impl/src/test/java/org/ehcache/core/events/CacheManagerListenerTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/core/events/CacheManagerListenerTest.java @@ -26,7 +26,6 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import static org.ehcache.config.units.MemoryUnit.MB; @@ -42,9 +41,6 @@ public class CacheManagerListenerTest { @Rule public TemporaryFolder folder = new TemporaryFolder(); - @Rule - public ExpectedException expectedException = ExpectedException.none(); - @Before public void before() { CacheConfigurationBuilder cacheConfiguration = diff --git a/impl/src/test/java/org/ehcache/core/spi/ServiceProviderTest.java b/ehcache-impl/src/test/java/org/ehcache/core/spi/ServiceProviderTest.java similarity index 89% rename from impl/src/test/java/org/ehcache/core/spi/ServiceProviderTest.java rename to ehcache-impl/src/test/java/org/ehcache/core/spi/ServiceProviderTest.java index 339dce04f6..784b5132f4 100644 --- a/impl/src/test/java/org/ehcache/core/spi/ServiceProviderTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/core/spi/ServiceProviderTest.java @@ -16,7 +16,7 @@ package org.ehcache.core.spi; -import org.ehcache.core.internal.service.ServiceLocator; +import org.ehcache.core.spi.service.CacheManagerProviderService; import org.ehcache.core.spi.service.DiskResourceService; import org.ehcache.impl.internal.store.disk.OffHeapDiskStore; import org.ehcache.impl.internal.store.heap.OnHeapStore; @@ -26,9 +26,10 @@ import org.hamcrest.core.IsCollectionContaining; import org.hamcrest.core.IsSame; import org.junit.Test; +import org.mockito.Answers; -import static org.ehcache.core.internal.service.ServiceLocator.dependencySet; -import static org.junit.Assert.assertThat; +import static org.ehcache.core.spi.ServiceLocator.dependencySet; +import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.mock; /** @@ -49,6 +50,7 @@ public void testSupportsMultipleAuthoritativeTierProviders() throws Exception { dependencySet.with(authoritativeTierProvider); dependencySet.with(diskStoreProvider); dependencySet.with(mock(DiskResourceService.class)); + dependencySet.with(mock(CacheManagerProviderService.class, Answers.RETURNS_DEEP_STUBS)); ServiceLocator serviceLocator = dependencySet.build(); serviceLocator.startAllServices(); diff --git a/ehcache-impl/src/test/java/org/ehcache/docs/ConfigurationDerivation.java b/ehcache-impl/src/test/java/org/ehcache/docs/ConfigurationDerivation.java new file mode 100644 index 0000000000..1e74b8bcdd --- /dev/null +++ b/ehcache-impl/src/test/java/org/ehcache/docs/ConfigurationDerivation.java @@ -0,0 +1,227 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.docs; + +import org.ehcache.config.Configuration; +import org.ehcache.config.FluentConfigurationBuilder; +import org.ehcache.config.ResourceType; +import org.ehcache.config.builders.CacheConfigurationBuilder; +import org.ehcache.config.builders.ConfigurationBuilder; +import org.ehcache.config.builders.ExpiryPolicyBuilder; +import org.ehcache.config.builders.ResourcePoolsBuilder; +import org.ehcache.config.units.MemoryUnit; +import org.ehcache.core.internal.resilience.ThrowingResilienceStrategy; +import org.ehcache.core.spi.service.ServiceUtils; +import org.ehcache.core.util.ClassLoading; +import org.ehcache.impl.config.executor.PooledExecutionServiceConfiguration; +import org.ehcache.impl.config.persistence.DefaultPersistenceConfiguration; +import org.ehcache.impl.config.resilience.DefaultResilienceStrategyConfiguration; +import org.ehcache.impl.config.serializer.DefaultSerializationProviderConfiguration; +import org.ehcache.impl.serialization.PlainJavaSerializer; +import org.ehcache.spi.serialization.Serializer; +import org.ehcache.spi.serialization.SerializerException; +import org.ehcache.test.MockitoUtil; +import org.hamcrest.collection.IsEmptyCollection; +import org.hamcrest.collection.IsIterableContainingInAnyOrder; +import org.hamcrest.collection.IsMapContaining; +import org.hamcrest.core.Is; +import org.hamcrest.core.IsCollectionContaining; +import org.hamcrest.core.IsInstanceOf; +import org.hamcrest.core.IsNot; +import org.hamcrest.core.IsNull; +import org.hamcrest.core.IsSame; +import org.junit.Test; + +import java.io.File; +import java.io.Serializable; +import java.nio.ByteBuffer; +import java.time.Duration; +import java.util.Date; + +import static org.hamcrest.MatcherAssert.assertThat; + +public class ConfigurationDerivation { + + @Test + public void identityTransform() { + Configuration configuration = ConfigurationBuilder.newConfigurationBuilder() + .withCache("cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(10)) + .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(10)))) + .build(); + + // tag::deriveContract[] + FluentConfigurationBuilder derivedBuilder = configuration.derive(); // <1> + Configuration configurationCopy = derivedBuilder.build(); // <2> + // end::deriveContract[] + } + + @Test + public void withCustomClassLoader() { + Configuration configuration = ConfigurationBuilder.newConfigurationBuilder() + .withCache("cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(10))) + .build(); + + ClassLoader classLoader = MockitoUtil.mock(ClassLoader.class); + + // tag::customClassLoader[] + Configuration withClassLoader = configuration.derive() + .withClassLoader(classLoader) + .build(); + // end::customClassLoader[] + + assertThat(configuration.getClassLoader(), Is.is(IsSame.sameInstance(ClassLoading.getDefaultClassLoader()))); + assertThat(withClassLoader.getClassLoader(), Is.is(IsSame.sameInstance(classLoader))); + } + + @Test + public void withCache() { + Configuration configuration = ConfigurationBuilder.newConfigurationBuilder().build(); + + //tag::withCache[] + Configuration withCache = configuration.derive() + .withCache("cache", CacheConfigurationBuilder.newCacheConfigurationBuilder( + Long.class, String.class, ResourcePoolsBuilder.heap(10))) + .build(); + //end::withCache[] + + assertThat(configuration.getCacheConfigurations().keySet(), Is.is(IsEmptyCollection.empty())); + assertThat(withCache.getCacheConfigurations().keySet(), IsIterableContainingInAnyOrder.containsInAnyOrder("cache")); + } + + @Test + public void withoutCache() { + Configuration configuration = ConfigurationBuilder.newConfigurationBuilder() + .withCache("cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(10))) + .build(); + + //tag::withoutCache[] + Configuration withoutCache = configuration.derive() + .withoutCache("cache") + .build(); + //end::withoutCache[] + + assertThat(configuration.getCacheConfigurations().keySet(), IsIterableContainingInAnyOrder.containsInAnyOrder("cache")); + assertThat(withoutCache.getCacheConfigurations().keySet(), Is.is(IsEmptyCollection.empty())); + } + + @Test + public void updateCache() { + Configuration configuration = ConfigurationBuilder.newConfigurationBuilder() + .withCache("cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(10))) + .build(); + + //tag::updateCache[] + Configuration withOffHeap = configuration.derive() + .updateCache("cache", cache -> cache.updateResourcePools( + resources -> ResourcePoolsBuilder.newResourcePoolsBuilder(resources) + .offheap(100, MemoryUnit.MB) + .build())) + .build(); + //end::updateCache[] + + assertThat(configuration.getCacheConfigurations().get("cache").getResourcePools().getResourceTypeSet(), IsIterableContainingInAnyOrder.containsInAnyOrder(ResourceType.Core.HEAP)); + assertThat(withOffHeap.getCacheConfigurations().get("cache").getResourcePools().getResourceTypeSet(), IsIterableContainingInAnyOrder.containsInAnyOrder(ResourceType.Core.HEAP, ResourceType.Core.OFFHEAP)); + } + + @Test + public void withServiceCreation() { + Configuration configuration = ConfigurationBuilder.newConfigurationBuilder() + .withCache("cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(10))) + .build(); + + //tag::withServiceCreation[] + Configuration withBoundedThreads = configuration.derive() + .withService(new PooledExecutionServiceConfiguration() + .addDefaultPool("default", 1, 16)) + .build(); + //end::withServiceCreation[] + + assertThat(configuration.getServiceCreationConfigurations(), IsNot.not(IsCollectionContaining.hasItem(IsInstanceOf.instanceOf(PooledExecutionServiceConfiguration.class)))); + PooledExecutionServiceConfiguration serviceCreationConfiguration = ServiceUtils.findSingletonAmongst(PooledExecutionServiceConfiguration.class, withBoundedThreads.getServiceCreationConfigurations()); + assertThat(serviceCreationConfiguration.getDefaultPoolAlias(), Is.is("default")); + assertThat(serviceCreationConfiguration.getPoolConfigurations().keySet(), IsIterableContainingInAnyOrder.containsInAnyOrder("default")); + PooledExecutionServiceConfiguration.PoolConfiguration pool = serviceCreationConfiguration.getPoolConfigurations().get("default"); + assertThat(pool.minSize(), Is.is(1)); + assertThat(pool.maxSize(), Is.is(16)); + } + + @Test + public void updateServiceCreation() { + @SuppressWarnings("unchecked") + Configuration configuration = ConfigurationBuilder.newConfigurationBuilder() + .withService(new DefaultPersistenceConfiguration(new File("temp"))) + .withCache("cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(10))) + .build(); + + //tag::updateServiceCreation[] + Configuration withUpdatedPersistence = configuration.derive() + .updateServices(DefaultPersistenceConfiguration.class, + existing -> new File("/var/persistence/path")) + .build(); + //end::updateServiceCreation[] + + DefaultPersistenceConfiguration initialPersistenceConfiguration = ServiceUtils.findSingletonAmongst(DefaultPersistenceConfiguration.class, configuration.getServiceCreationConfigurations()); + assertThat(initialPersistenceConfiguration.getRootDirectory(), Is.is(new File("temp"))); + + DefaultPersistenceConfiguration revisedPersistenceConfiguration = ServiceUtils.findSingletonAmongst(DefaultPersistenceConfiguration.class, withUpdatedPersistence.getServiceCreationConfigurations()); + assertThat(revisedPersistenceConfiguration.getRootDirectory(), Is.is(new File("/var/persistence/path"))); + } + + @Test + public void withService() { + Configuration configuration = ConfigurationBuilder.newConfigurationBuilder() + .withCache("cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(10))) + .build(); + + //tag::withService[] + Configuration withThrowingStrategy = configuration.derive() + .updateCache("cache", existing -> existing.withService( + new DefaultResilienceStrategyConfiguration(new ThrowingResilienceStrategy<>()) + )) + .build(); + //end::withService[] + + + assertThat(configuration.getServiceCreationConfigurations(), IsNot.not(IsCollectionContaining.hasItem( + IsInstanceOf.instanceOf(DefaultResilienceStrategyConfiguration.class)))); + + DefaultResilienceStrategyConfiguration resilienceStrategyConfiguration = + ServiceUtils.findSingletonAmongst(DefaultResilienceStrategyConfiguration.class, + withThrowingStrategy.getCacheConfigurations().get("cache").getServiceConfigurations()); + assertThat(resilienceStrategyConfiguration.getInstance(), IsInstanceOf.instanceOf(ThrowingResilienceStrategy.class)); + } + + public static final class OptimizedDateSerializer implements Serializer { + + public OptimizedDateSerializer(ClassLoader classLoader) {} + + @Override + public ByteBuffer serialize(Date object) throws SerializerException { + ByteBuffer buffer = ByteBuffer.allocate(8); + return (ByteBuffer) buffer.putLong(object.getTime()).flip(); + } + + @Override + public Date read(ByteBuffer binary) throws ClassNotFoundException, SerializerException { + return new Date(binary.getLong()); + } + + @Override + public boolean equals(Date object, ByteBuffer binary) throws ClassNotFoundException, SerializerException { + return binary.getLong() == object.getTime(); + } + } +} diff --git a/impl/src/test/java/org/ehcache/docs/Ehcache3.java b/ehcache-impl/src/test/java/org/ehcache/docs/Ehcache3.java similarity index 100% rename from impl/src/test/java/org/ehcache/docs/Ehcache3.java rename to ehcache-impl/src/test/java/org/ehcache/docs/Ehcache3.java diff --git a/impl/src/test/java/org/ehcache/docs/GettingStarted.java b/ehcache-impl/src/test/java/org/ehcache/docs/GettingStarted.java similarity index 98% rename from impl/src/test/java/org/ehcache/docs/GettingStarted.java rename to ehcache-impl/src/test/java/org/ehcache/docs/GettingStarted.java index 19e95e3803..f3429094e9 100644 --- a/impl/src/test/java/org/ehcache/docs/GettingStarted.java +++ b/ehcache-impl/src/test/java/org/ehcache/docs/GettingStarted.java @@ -50,9 +50,9 @@ import java.util.function.Supplier; import static java.util.Collections.singletonMap; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; /** * Samples to get started with Ehcache 3 @@ -122,7 +122,7 @@ public void testCacheEventListener() { final CacheManager manager = CacheManagerBuilder.newCacheManagerBuilder() .withCache("foo", CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, String.class, ResourcePoolsBuilder.heap(10)) - .add(cacheEventListenerConfiguration) // <3> + .withService(cacheEventListenerConfiguration) // <3> ).build(true); final Cache cache = manager.getCache("foo", String.class, String.class); @@ -160,7 +160,7 @@ public void writeBehindCache() { Cache writeBehindCache = cacheManager.createCache("writeBehindCache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(10)) .withLoaderWriter(new SampleLoaderWriter<>(singletonMap(41L, "zero"))) // <1> - .add(WriteBehindConfigurationBuilder // <2> + .withService(WriteBehindConfigurationBuilder // <2> .newBatchedWriteBehindConfiguration(1, TimeUnit.SECONDS, 3)// <3> .queueSize(3)// <4> .concurrencyLevel(1) // <5> diff --git a/impl/src/test/java/org/ehcache/docs/ThreadPools.java b/ehcache-impl/src/test/java/org/ehcache/docs/ThreadPools.java similarity index 96% rename from impl/src/test/java/org/ehcache/docs/ThreadPools.java rename to ehcache-impl/src/test/java/org/ehcache/docs/ThreadPools.java index 523dae4700..9a8e07a2ae 100644 --- a/impl/src/test/java/org/ehcache/docs/ThreadPools.java +++ b/ehcache-impl/src/test/java/org/ehcache/docs/ThreadPools.java @@ -98,7 +98,7 @@ public void writeBehind() throws Exception { CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES)) .withLoaderWriter(new SampleLoaderWriter<>(singletonMap(41L, "zero"))) - .add(WriteBehindConfigurationBuilder + .withService(WriteBehindConfigurationBuilder .newBatchedWriteBehindConfiguration(1, TimeUnit.SECONDS, 3) .queueSize(3) .concurrencyLevel(1))) @@ -106,7 +106,7 @@ public void writeBehind() throws Exception { CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES)) .withLoaderWriter(new SampleLoaderWriter<>(singletonMap(41L, "zero"))) - .add(WriteBehindConfigurationBuilder + .withService(WriteBehindConfigurationBuilder .newBatchedWriteBehindConfiguration(1, TimeUnit.SECONDS, 3) .useThreadPool("cache2Pool") // <3> .queueSize(3) @@ -135,12 +135,12 @@ public void events() throws Exception { .withCache("cache1", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES)) - .add(CacheEventListenerConfigurationBuilder + .withService(CacheEventListenerConfigurationBuilder .newEventListenerConfiguration(new ListenerObject(), EventType.CREATED, EventType.UPDATED))) .withCache("cache2", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES)) - .add(CacheEventListenerConfigurationBuilder + .withService(CacheEventListenerConfigurationBuilder .newEventListenerConfiguration(new ListenerObject(), EventType.CREATED, EventType.UPDATED)) .withEventListenersThreadPool("cache2Pool")) // <3> .build(true); diff --git a/impl/src/test/java/org/ehcache/docs/Tiering.java b/ehcache-impl/src/test/java/org/ehcache/docs/Tiering.java similarity index 97% rename from impl/src/test/java/org/ehcache/docs/Tiering.java rename to ehcache-impl/src/test/java/org/ehcache/docs/Tiering.java index fd48c4b711..6d1cb8d3e9 100644 --- a/impl/src/test/java/org/ehcache/docs/Tiering.java +++ b/ehcache-impl/src/test/java/org/ehcache/docs/Tiering.java @@ -38,8 +38,8 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; /** * Tiering @@ -112,7 +112,7 @@ public void diskSegments() throws Exception { .withCache("less-segments", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder().disk(10, MemoryUnit.MB)) - .add(new OffHeapDiskStoreConfiguration(2)) // <1> + .withService(new OffHeapDiskStoreConfiguration(2)) // <1> ) .build(true); @@ -128,7 +128,7 @@ public void updateResourcesAtRuntime() throws InterruptedException { CacheConfiguration cacheConfiguration = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10L, EntryUnit.ENTRIES)) - .add(cacheEventListenerConfiguration) + .withService(cacheEventListenerConfiguration) .build(); CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().withCache("cache", cacheConfiguration) diff --git a/impl/src/test/java/org/ehcache/docs/UserManagedCaches.java b/ehcache-impl/src/test/java/org/ehcache/docs/UserManagedCaches.java similarity index 97% rename from impl/src/test/java/org/ehcache/docs/UserManagedCaches.java rename to ehcache-impl/src/test/java/org/ehcache/docs/UserManagedCaches.java index 1419f344ca..07be4a80f3 100644 --- a/impl/src/test/java/org/ehcache/docs/UserManagedCaches.java +++ b/ehcache-impl/src/test/java/org/ehcache/docs/UserManagedCaches.java @@ -35,13 +35,10 @@ import java.io.File; import java.io.IOException; -import java.net.URISyntaxException; import java.util.concurrent.Executors; -import javax.print.URIException; - +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; /** * UserManagedCaches diff --git a/impl/src/test/java/org/ehcache/docs/plugs/ListenerObject.java b/ehcache-impl/src/test/java/org/ehcache/docs/plugs/ListenerObject.java similarity index 100% rename from impl/src/test/java/org/ehcache/docs/plugs/ListenerObject.java rename to ehcache-impl/src/test/java/org/ehcache/docs/plugs/ListenerObject.java diff --git a/impl/src/test/java/org/ehcache/docs/plugs/LongCopier.java b/ehcache-impl/src/test/java/org/ehcache/docs/plugs/LongCopier.java similarity index 100% rename from impl/src/test/java/org/ehcache/docs/plugs/LongCopier.java rename to ehcache-impl/src/test/java/org/ehcache/docs/plugs/LongCopier.java diff --git a/impl/src/test/java/org/ehcache/docs/plugs/OddKeysEvictionAdvisor.java b/ehcache-impl/src/test/java/org/ehcache/docs/plugs/OddKeysEvictionAdvisor.java similarity index 100% rename from impl/src/test/java/org/ehcache/docs/plugs/OddKeysEvictionAdvisor.java rename to ehcache-impl/src/test/java/org/ehcache/docs/plugs/OddKeysEvictionAdvisor.java diff --git a/impl/src/test/java/org/ehcache/docs/plugs/SampleLoaderWriter.java b/ehcache-impl/src/test/java/org/ehcache/docs/plugs/SampleLoaderWriter.java similarity index 100% rename from impl/src/test/java/org/ehcache/docs/plugs/SampleLoaderWriter.java rename to ehcache-impl/src/test/java/org/ehcache/docs/plugs/SampleLoaderWriter.java diff --git a/impl/src/test/java/org/ehcache/docs/plugs/StringCopier.java b/ehcache-impl/src/test/java/org/ehcache/docs/plugs/StringCopier.java similarity index 100% rename from impl/src/test/java/org/ehcache/docs/plugs/StringCopier.java rename to ehcache-impl/src/test/java/org/ehcache/docs/plugs/StringCopier.java diff --git a/ehcache-impl/src/test/java/org/ehcache/impl/config/BaseCacheConfigurationTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/config/BaseCacheConfigurationTest.java new file mode 100644 index 0000000000..f1d5ab30e7 --- /dev/null +++ b/ehcache-impl/src/test/java/org/ehcache/impl/config/BaseCacheConfigurationTest.java @@ -0,0 +1,56 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.impl.config; + +import org.ehcache.config.ResourcePools; +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasProperty; +import static org.hamcrest.Matchers.startsWith; +import static org.junit.Assert.assertThrows; +import static org.mockito.Mockito.mock; + +/** + * BaseCacheConfigurationTest + */ +public class BaseCacheConfigurationTest { + + @Test + public void testThrowsWithNullKeyType() { + NullPointerException thrown = assertThrows(NullPointerException.class, () -> new BaseCacheConfiguration<>(null, String.class, null, + null, null, mock(ResourcePools.class))); + assertThat(thrown, hasProperty("message", startsWith("keyType"))); + } + + @Test + public void testThrowsWithNullValueType() { + NullPointerException thrown = assertThrows(NullPointerException.class, () -> + new BaseCacheConfiguration<>(Long.class, null, null, + null, null, mock(ResourcePools.class))); + assertThat(thrown, hasProperty("message", startsWith("valueType"))); + } + + @Test + public void testThrowsWithNullResourcePools() { + NullPointerException thrown = assertThrows(NullPointerException.class, () -> + new BaseCacheConfiguration<>(Long.class, String.class, null, + null, null, null)); + assertThat(thrown, hasProperty("message", startsWith("resourcePools"))); + } + +} diff --git a/core/src/test/java/org/ehcache/core/config/ResourcePoolsImplTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/config/ResourcePoolsImplTest.java similarity index 83% rename from core/src/test/java/org/ehcache/core/config/ResourcePoolsImplTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/config/ResourcePoolsImplTest.java index 4c7ca74a7e..d8a1793c6e 100644 --- a/core/src/test/java/org/ehcache/core/config/ResourcePoolsImplTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/config/ResourcePoolsImplTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.ehcache.core.config; +package org.ehcache.impl.config; import org.ehcache.config.ResourcePool; import org.ehcache.config.ResourcePools; @@ -25,19 +25,20 @@ import org.hamcrest.Matchers; import org.junit.Test; -import java.util.Arrays; import java.util.Collection; -import java.util.Collections; -import java.util.List; +import static java.util.Arrays.asList; +import static java.util.Arrays.stream; +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.toMap; import static org.ehcache.config.ResourceType.Core.HEAP; import static org.ehcache.config.ResourceType.Core.OFFHEAP; import static org.ehcache.config.units.EntryUnit.ENTRIES; import static org.ehcache.config.units.MemoryUnit.KB; import static org.ehcache.config.units.MemoryUnit.MB; -import static org.ehcache.core.config.ResourcePoolsImpl.validateResourcePools; +import static org.ehcache.impl.config.ResourcePoolsImpl.validateResourcePools; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; /** @@ -238,10 +239,8 @@ public void testMemoryResourceUnequalUnitInversion() { @Test public void testAddingNewTierWhileUpdating() { - ResourcePools existing = new ResourcePoolsImpl(Collections., ResourcePool>singletonMap( - ResourceType.Core.HEAP, new SizedResourcePoolImpl<>(ResourceType.Core.HEAP, 10L, EntryUnit.ENTRIES, false))); - ResourcePools toBeUpdated = new ResourcePoolsImpl(Collections., ResourcePool>singletonMap( - ResourceType.Core.DISK, new SizedResourcePoolImpl<>(ResourceType.Core.DISK, 10L, MemoryUnit.MB, false))); + ResourcePools existing = resources(new SizedResourcePoolImpl<>(ResourceType.Core.HEAP, 10L, EntryUnit.ENTRIES, false)); + ResourcePools toBeUpdated = resources(new SizedResourcePoolImpl<>(ResourceType.Core.DISK, 10L, MemoryUnit.MB, false)); try { existing.validateAndMerge(toBeUpdated); fail(); @@ -252,8 +251,8 @@ public void testAddingNewTierWhileUpdating() { @Test public void testUpdatingOffHeap() { - ResourcePools existing = ResourcePoolsHelper.createOffheapOnlyPools(10); - ResourcePools toBeUpdated = ResourcePoolsHelper.createOffheapOnlyPools(50); + ResourcePools existing = resources(new SizedResourcePoolImpl<>(ResourceType.Core.OFFHEAP, 10L, MemoryUnit.MB, false)); + ResourcePools toBeUpdated = resources(new SizedResourcePoolImpl<>(ResourceType.Core.OFFHEAP, 50L, MemoryUnit.MB, false)); try { existing.validateAndMerge(toBeUpdated); fail(); @@ -264,8 +263,8 @@ public void testUpdatingOffHeap() { @Test public void testUpdatingDisk() { - ResourcePools existing = ResourcePoolsHelper.createDiskOnlyPools(10, MB); - ResourcePools toBeUpdated = ResourcePoolsHelper.createDiskOnlyPools(50, MB); + ResourcePools existing = resources(new SizedResourcePoolImpl<>(ResourceType.Core.DISK, 10L, MemoryUnit.MB, false)); + ResourcePools toBeUpdated = resources(new SizedResourcePoolImpl<>(ResourceType.Core.DISK, 50L, MemoryUnit.MB, false)); try { existing.validateAndMerge(toBeUpdated); fail(); @@ -276,8 +275,13 @@ public void testUpdatingDisk() { @Test public void testUpdateResourceUnitSuccess() { - ResourcePools existing = ResourcePoolsHelper.createHeapDiskPools(200, MB, 4096); - ResourcePools toBeUpdated = ResourcePoolsHelper.createHeapOnlyPools(2, MemoryUnit.GB); + ResourcePools existing = resources( + new SizedResourcePoolImpl<>(ResourceType.Core.HEAP, 200L, MemoryUnit.MB, false), + new SizedResourcePoolImpl<>(ResourceType.Core.DISK, 4096L, MemoryUnit.MB, false) + ); + ResourcePools toBeUpdated = resources( + new SizedResourcePoolImpl<>(ResourceType.Core.HEAP, 2, MemoryUnit.GB, false) + ); existing = existing.validateAndMerge(toBeUpdated); assertThat(existing.getPoolForResource(ResourceType.Core.HEAP).getSize(), Matchers.is(2L)); @@ -286,8 +290,13 @@ public void testUpdateResourceUnitSuccess() { @Test public void testUpdateResourceUnitFailure() { - ResourcePools existing = ResourcePoolsHelper.createHeapDiskPools(20, MB, 200); - ResourcePools toBeUpdated = ResourcePoolsHelper.createHeapOnlyPools(500, EntryUnit.ENTRIES); + ResourcePools existing = resources( + new SizedResourcePoolImpl<>(ResourceType.Core.HEAP, 20L, MemoryUnit.MB, false), + new SizedResourcePoolImpl<>(ResourceType.Core.DISK, 200, MemoryUnit.MB, false) + ); + ResourcePools toBeUpdated = resources( + new SizedResourcePoolImpl<>(ResourceType.Core.HEAP, 500, EntryUnit.ENTRIES, false) + ); try { existing = existing.validateAndMerge(toBeUpdated); @@ -299,10 +308,7 @@ public void testUpdateResourceUnitFailure() { assertThat(existing.getPoolForResource(ResourceType.Core.HEAP).getUnit(), Matchers.is(MemoryUnit.MB)); } - private Collection asList(T value1, T value2) { - @SuppressWarnings("unchecked") - List list = Arrays.asList(value1, value2); - return list; + private static ResourcePoolsImpl resources(ResourcePool ... resources) { + return new ResourcePoolsImpl(stream(resources).collect(toMap(ResourcePool::getType, identity()))); } - } diff --git a/core/src/test/java/org/ehcache/core/config/SizedResourcePoolImplTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/config/SizedResourcePoolImplTest.java similarity index 93% rename from core/src/test/java/org/ehcache/core/config/SizedResourcePoolImplTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/config/SizedResourcePoolImplTest.java index 433effec13..dd15bbd519 100644 --- a/core/src/test/java/org/ehcache/core/config/SizedResourcePoolImplTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/config/SizedResourcePoolImplTest.java @@ -14,10 +14,9 @@ * limitations under the License. */ -package org.ehcache.core.config; +package org.ehcache.impl.config; import org.ehcache.config.ResourceType; -import org.ehcache.config.SizedResourcePool; import org.ehcache.config.units.EntryUnit; import org.junit.Test; diff --git a/ehcache-impl/src/test/java/org/ehcache/impl/config/copy/DefaultCopyProviderConfigurationTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/config/copy/DefaultCopyProviderConfigurationTest.java new file mode 100644 index 0000000000..d8a31ad627 --- /dev/null +++ b/ehcache-impl/src/test/java/org/ehcache/impl/config/copy/DefaultCopyProviderConfigurationTest.java @@ -0,0 +1,39 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.impl.config.copy; + +import org.ehcache.impl.copy.IdentityCopier; +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNot.not; +import static org.hamcrest.core.IsSame.sameInstance; + +public class DefaultCopyProviderConfigurationTest { + + @Test @SuppressWarnings({"unchecked", "rawtypes"}) + public void testDerivedConfigurationDetachesCorrectly() { + DefaultCopyProviderConfiguration configuration = new DefaultCopyProviderConfiguration(); + configuration.addCopierFor(String.class, (Class) IdentityCopier.class); + + DefaultCopyProviderConfiguration derived = configuration.build(configuration.derive()); + + assertThat(derived, is(not(sameInstance(configuration)))); + assertThat(derived.getDefaults().get(String.class).getClazz(), sameInstance(IdentityCopier.class)); + } +} diff --git a/ehcache-impl/src/test/java/org/ehcache/impl/config/event/CacheEventDispatcherFactoryConfigurationTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/config/event/CacheEventDispatcherFactoryConfigurationTest.java new file mode 100644 index 0000000000..3386e7d447 --- /dev/null +++ b/ehcache-impl/src/test/java/org/ehcache/impl/config/event/CacheEventDispatcherFactoryConfigurationTest.java @@ -0,0 +1,36 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.impl.config.event; + +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNot.not; +import static org.hamcrest.core.IsSame.sameInstance; + +public class CacheEventDispatcherFactoryConfigurationTest { + + @Test + public void testDeriveDetachesCorrectly() { + CacheEventDispatcherFactoryConfiguration configuration = new CacheEventDispatcherFactoryConfiguration("foobar"); + CacheEventDispatcherFactoryConfiguration derived = configuration.build(configuration.derive()); + + assertThat(derived, is(not(sameInstance(configuration)))); + assertThat(derived.getThreadPoolAlias(), is(configuration.getThreadPoolAlias())); + } +} diff --git a/ehcache-impl/src/test/java/org/ehcache/impl/config/event/DefaultCacheEventDispatcherConfigurationTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/config/event/DefaultCacheEventDispatcherConfigurationTest.java new file mode 100644 index 0000000000..71bec44e2b --- /dev/null +++ b/ehcache-impl/src/test/java/org/ehcache/impl/config/event/DefaultCacheEventDispatcherConfigurationTest.java @@ -0,0 +1,36 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.impl.config.event; + +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNot.not; +import static org.hamcrest.core.IsSame.sameInstance; + +public class DefaultCacheEventDispatcherConfigurationTest { + + @Test + public void testDeriveDetachesProperly() { + DefaultCacheEventDispatcherConfiguration configuration = new DefaultCacheEventDispatcherConfiguration("foobar"); + DefaultCacheEventDispatcherConfiguration derived = configuration.build(configuration.derive()); + + assertThat(derived, is(not(sameInstance(configuration)))); + assertThat(derived.getThreadPoolAlias(), is(configuration.getThreadPoolAlias())); + } +} diff --git a/impl/src/test/java/org/ehcache/impl/config/event/DefaultCacheEventListenerConfigurationTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/config/event/DefaultCacheEventListenerConfigurationTest.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/config/event/DefaultCacheEventListenerConfigurationTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/config/event/DefaultCacheEventListenerConfigurationTest.java diff --git a/ehcache-impl/src/test/java/org/ehcache/impl/config/event/DefaultEventSourceConfigurationTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/config/event/DefaultEventSourceConfigurationTest.java new file mode 100644 index 0000000000..0774c54281 --- /dev/null +++ b/ehcache-impl/src/test/java/org/ehcache/impl/config/event/DefaultEventSourceConfigurationTest.java @@ -0,0 +1,36 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.impl.config.event; + +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNot.not; +import static org.hamcrest.core.IsSame.sameInstance; + +public class DefaultEventSourceConfigurationTest { + + @Test + public void testDeriveDetachesProperly() { + DefaultEventSourceConfiguration configuration = new DefaultEventSourceConfiguration(42); + DefaultEventSourceConfiguration derived = configuration.build(configuration.derive()); + + assertThat(derived, is(not(sameInstance(configuration)))); + assertThat(derived.getDispatcherConcurrency(), is(configuration.getDispatcherConcurrency())); + } +} diff --git a/ehcache-impl/src/test/java/org/ehcache/impl/config/executor/PooledExecutionServiceConfigurationTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/config/executor/PooledExecutionServiceConfigurationTest.java new file mode 100644 index 0000000000..a0a7396526 --- /dev/null +++ b/ehcache-impl/src/test/java/org/ehcache/impl/config/executor/PooledExecutionServiceConfigurationTest.java @@ -0,0 +1,37 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.impl.config.executor; + +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNot.not; +import static org.hamcrest.core.IsSame.sameInstance; + +public class PooledExecutionServiceConfigurationTest { + + @Test + public void testDeriveDetachesCorrectly() { + PooledExecutionServiceConfiguration configuration = new PooledExecutionServiceConfiguration(); + configuration.addPool("foo", 1, 2); + + PooledExecutionServiceConfiguration derived = configuration.build(configuration.derive()); + + assertThat(derived, is(not(sameInstance(configuration)))); + } +} diff --git a/ehcache-impl/src/test/java/org/ehcache/impl/config/loaderwriter/DefaultCacheLoaderWriterConfigurationTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/config/loaderwriter/DefaultCacheLoaderWriterConfigurationTest.java new file mode 100644 index 0000000000..02b0641b88 --- /dev/null +++ b/ehcache-impl/src/test/java/org/ehcache/impl/config/loaderwriter/DefaultCacheLoaderWriterConfigurationTest.java @@ -0,0 +1,39 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.impl.config.loaderwriter; + +import org.ehcache.spi.loaderwriter.CacheLoaderWriter; +import org.junit.Test; + +import static org.ehcache.test.MockitoUtil.mock; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNot.not; +import static org.hamcrest.core.IsSame.sameInstance; + +public class DefaultCacheLoaderWriterConfigurationTest { + + @Test + public void testDeriveDetachesCorrectly() { + CacheLoaderWriter mock = mock(CacheLoaderWriter.class); + DefaultCacheLoaderWriterConfiguration configuration = new DefaultCacheLoaderWriterConfiguration(mock); + DefaultCacheLoaderWriterConfiguration derived = configuration.build(configuration.derive()); + + assertThat(derived, is(not(sameInstance(configuration)))); + assertThat(derived.getInstance(), sameInstance(configuration.getInstance())); + } +} diff --git a/ehcache-impl/src/test/java/org/ehcache/impl/config/loaderwriter/DefaultCacheLoaderWriterProviderConfigurationTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/config/loaderwriter/DefaultCacheLoaderWriterProviderConfigurationTest.java new file mode 100644 index 0000000000..a55d57444d --- /dev/null +++ b/ehcache-impl/src/test/java/org/ehcache/impl/config/loaderwriter/DefaultCacheLoaderWriterProviderConfigurationTest.java @@ -0,0 +1,41 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.impl.config.loaderwriter; + +import org.ehcache.spi.loaderwriter.CacheLoaderWriter; +import org.junit.Test; + +import static org.ehcache.test.MockitoUtil.mock; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNot.not; +import static org.hamcrest.core.IsSame.sameInstance; + +public class DefaultCacheLoaderWriterProviderConfigurationTest { + + @Test @SuppressWarnings("unchecked") + public void testDeriveDetachesCorrectly() { + DefaultCacheLoaderWriterProviderConfiguration configuration = new DefaultCacheLoaderWriterProviderConfiguration(); + configuration.addLoaderFor("foo", (Class>) mock(CacheLoaderWriter.class).getClass()); + + DefaultCacheLoaderWriterProviderConfiguration derived = configuration.build(configuration.derive()); + + assertThat(derived, is(not(sameInstance(configuration)))); + assertThat(derived.getDefaults(), is(not(sameInstance(configuration.getDefaults())))); + assertThat(derived.getDefaults(), is(configuration.getDefaults())); + } +} diff --git a/ehcache-impl/src/test/java/org/ehcache/impl/config/loaderwriter/writebehind/WriteBehindProviderConfigurationTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/config/loaderwriter/writebehind/WriteBehindProviderConfigurationTest.java new file mode 100644 index 0000000000..df541d270d --- /dev/null +++ b/ehcache-impl/src/test/java/org/ehcache/impl/config/loaderwriter/writebehind/WriteBehindProviderConfigurationTest.java @@ -0,0 +1,36 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.impl.config.loaderwriter.writebehind; + +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNot.not; +import static org.hamcrest.core.IsSame.sameInstance; + +public class WriteBehindProviderConfigurationTest { + + @Test + public void testDeriveDetachesCorrectly() { + WriteBehindProviderConfiguration configuration = new WriteBehindProviderConfiguration("foobar"); + WriteBehindProviderConfiguration derived = configuration.build(configuration.derive()); + + assertThat(derived, is(not(sameInstance(configuration)))); + assertThat(derived.getThreadPoolAlias(), is(configuration.getThreadPoolAlias())); + } +} diff --git a/ehcache-impl/src/test/java/org/ehcache/impl/config/persistence/DefaultPersistenceConfigurationTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/config/persistence/DefaultPersistenceConfigurationTest.java new file mode 100644 index 0000000000..83a4fc3787 --- /dev/null +++ b/ehcache-impl/src/test/java/org/ehcache/impl/config/persistence/DefaultPersistenceConfigurationTest.java @@ -0,0 +1,38 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.impl.config.persistence; + +import org.junit.Test; + +import java.io.File; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNot.not; +import static org.hamcrest.core.IsSame.sameInstance; + +public class DefaultPersistenceConfigurationTest { + + @Test + public void testDeriveDetachesCorrectly() { + DefaultPersistenceConfiguration configuration = new DefaultPersistenceConfiguration(new File("foo")); + DefaultPersistenceConfiguration derived = configuration.build(configuration.derive()); + + assertThat(derived, is(not(sameInstance(configuration)))); + assertThat(derived.getRootDirectory(), is(configuration.getRootDirectory())); + } +} diff --git a/impl/src/test/java/org/ehcache/impl/config/resilience/DefaultResilienceStrategyConfigurationTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/config/resilience/DefaultResilienceStrategyConfigurationTest.java similarity index 81% rename from impl/src/test/java/org/ehcache/impl/config/resilience/DefaultResilienceStrategyConfigurationTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/config/resilience/DefaultResilienceStrategyConfigurationTest.java index 0e8cccb7ef..044e8b7f09 100644 --- a/impl/src/test/java/org/ehcache/impl/config/resilience/DefaultResilienceStrategyConfigurationTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/config/resilience/DefaultResilienceStrategyConfigurationTest.java @@ -15,30 +15,32 @@ */ package org.ehcache.impl.config.resilience; -import org.ehcache.core.internal.resilience.RobustResilienceStrategy; +import org.ehcache.impl.internal.resilience.RobustResilienceStrategy; import org.ehcache.spi.loaderwriter.CacheLoaderWriter; import org.ehcache.spi.resilience.RecoveryStore; import org.ehcache.spi.resilience.ResilienceStrategy; import org.junit.Test; import static org.ehcache.test.MockitoUtil.mock; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.collection.IsArrayContainingInOrder.arrayContaining; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNot.not; import static org.hamcrest.core.IsNull.nullValue; import static org.hamcrest.core.IsSame.sameInstance; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; public class DefaultResilienceStrategyConfigurationTest { @Test public void testBindOnInstanceConfigurationReturnsSelf() { - DefaultResilienceStrategyConfiguration configuration = new DefaultResilienceStrategyConfiguration(mock(ResilienceStrategy.class)); + DefaultResilienceStrategyConfiguration configuration = new DefaultResilienceStrategyConfiguration((ResilienceStrategy) mock(ResilienceStrategy.class)); assertThat(configuration.bind(null), sameInstance(configuration)); } @Test public void testLoaderWriterBindOnInstanceConfigurationReturnsSelf() { - DefaultResilienceStrategyConfiguration configuration = new DefaultResilienceStrategyConfiguration(mock(ResilienceStrategy.class)); + DefaultResilienceStrategyConfiguration configuration = new DefaultResilienceStrategyConfiguration((ResilienceStrategy) mock(ResilienceStrategy.class)); assertThat(configuration.bind(null, null), sameInstance(configuration)); } @@ -95,4 +97,14 @@ public void testAlreadyBoundLoaderWriterConfigurationCannotBeBound() { //expected } } + + @Test + public void testDeriveDetachesCorrectly() { + ResilienceStrategy resilienceStrategy = mock(ResilienceStrategy.class); + DefaultResilienceStrategyConfiguration configuration = new DefaultResilienceStrategyConfiguration(resilienceStrategy); + DefaultResilienceStrategyConfiguration derived = configuration.build(configuration.derive()); + + assertThat(derived, is(not(sameInstance(configuration)))); + assertThat(derived.getInstance(), sameInstance(configuration.getInstance())); + } } diff --git a/ehcache-impl/src/test/java/org/ehcache/impl/config/resilience/DefaultResilienceStrategyProviderConfigurationTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/config/resilience/DefaultResilienceStrategyProviderConfigurationTest.java new file mode 100644 index 0000000000..e1950302f1 --- /dev/null +++ b/ehcache-impl/src/test/java/org/ehcache/impl/config/resilience/DefaultResilienceStrategyProviderConfigurationTest.java @@ -0,0 +1,45 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.impl.config.resilience; + +import org.ehcache.spi.resilience.ResilienceStrategy; +import org.junit.Test; + +import static org.ehcache.test.MockitoUtil.mock; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNot.not; +import static org.hamcrest.core.IsSame.sameInstance; + +public class DefaultResilienceStrategyProviderConfigurationTest { + + @Test + public void testDeriveDetachesCorrectly() { + DefaultResilienceStrategyProviderConfiguration configuration = new DefaultResilienceStrategyProviderConfiguration(); + configuration.setDefaultResilienceStrategy(mock(ResilienceStrategy.class)); + configuration.setDefaultLoaderWriterResilienceStrategy(mock(ResilienceStrategy.class)); + configuration.addResilienceStrategyFor("foo", mock(ResilienceStrategy.class)); + + DefaultResilienceStrategyProviderConfiguration derived = configuration.build(configuration.derive()); + + assertThat(derived, is(not(sameInstance(configuration)))); + assertThat(derived.getDefaultConfiguration(), is(configuration.getDefaultConfiguration())); + assertThat(derived.getDefaultLoaderWriterConfiguration(), is(configuration.getDefaultLoaderWriterConfiguration())); + assertThat(derived.getDefaults(), is(not(sameInstance(configuration.getDefaults())))); + assertThat(derived.getDefaults(), is(configuration.getDefaults())); + } +} diff --git a/impl/src/test/java/org/ehcache/impl/config/serializer/DefaultSerializationProviderConfigurationTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/config/serializer/DefaultSerializationProviderConfigurationTest.java similarity index 74% rename from impl/src/test/java/org/ehcache/impl/config/serializer/DefaultSerializationProviderConfigurationTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/config/serializer/DefaultSerializationProviderConfigurationTest.java index c4cbb3ae98..cb26efaf69 100644 --- a/impl/src/test/java/org/ehcache/impl/config/serializer/DefaultSerializationProviderConfigurationTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/config/serializer/DefaultSerializationProviderConfigurationTest.java @@ -16,24 +16,28 @@ package org.ehcache.impl.config.serializer; +import org.ehcache.impl.serialization.JavaSerializer; import org.ehcache.spi.persistence.StateRepository; import org.ehcache.spi.serialization.SerializerException; import org.ehcache.spi.serialization.Serializer; import org.ehcache.core.spi.service.FileBasedPersistenceContext; import org.ehcache.spi.serialization.StatefulSerializer; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import java.nio.ByteBuffer; -import static org.junit.Assert.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.endsWith; +import static org.hamcrest.Matchers.hasProperty; +import static org.hamcrest.Matchers.startsWith; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNot.not; +import static org.hamcrest.core.IsSame.sameInstance; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThrows; public class DefaultSerializationProviderConfigurationTest { - @Rule - public ExpectedException expectedException = ExpectedException.none(); - @Test public void testAddSerializerFor() throws Exception { DefaultSerializationProviderConfiguration config = new DefaultSerializationProviderConfiguration(); @@ -46,17 +50,15 @@ public void testAddSerializerFor() throws Exception { public void testAddSerializerForDuplicateThrows() throws Exception { DefaultSerializationProviderConfiguration config = new DefaultSerializationProviderConfiguration(); config.addSerializerFor(Long.class, MinimalSerializer.class); - expectedException.expect(IllegalArgumentException.class); - expectedException.expectMessage("Duplicate serializer for class"); - config.addSerializerFor(Long.class, MinimalSerializer.class); + IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> config.addSerializerFor(Long.class, MinimalSerializer.class)); + assertThat(thrown, hasProperty("message", startsWith("Duplicate serializer for class"))); } @Test public void testAddSerializerForConstructorless() throws Exception { - expectedException.expect(IllegalArgumentException.class); - expectedException.expectMessage("does not have a constructor that takes in a ClassLoader."); DefaultSerializationProviderConfiguration config = new DefaultSerializationProviderConfiguration(); - config.addSerializerFor(Long.class, UnusableSerializer.class); + IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> config.addSerializerFor(Long.class, UnusableSerializer.class)); + assertThat(thrown, hasProperty("message", endsWith("does not have a constructor that takes in a ClassLoader."))); } @Test @@ -68,18 +70,28 @@ public void testAddSerializerForStatefulSerializer() throws Exception { @Test public void testAddSerializerForStatefulConstructorless() throws Exception { - expectedException.expect(IllegalArgumentException.class); - expectedException.expectMessage("does not have a constructor that takes in a ClassLoader."); DefaultSerializationProviderConfiguration config = new DefaultSerializationProviderConfiguration(); - config.addSerializerFor(Long.class, UnusableStatefulSerializer.class); + IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> config.addSerializerFor(Long.class, UnusableStatefulSerializer.class)); + assertThat(thrown, hasProperty("message", endsWith("does not have a constructor that takes in a ClassLoader."))); } @Test public void testAddSerializerForLegacySerializer() throws Exception { - expectedException.expect(IllegalArgumentException.class); - expectedException.expectMessage("does not have a constructor that takes in a ClassLoader."); DefaultSerializationProviderConfiguration config = new DefaultSerializationProviderConfiguration(); - config.addSerializerFor(Long.class, LegacySerializer.class); + IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> config.addSerializerFor(Long.class, LegacySerializer.class)); + assertThat(thrown, hasProperty("message", endsWith("does not have a constructor that takes in a ClassLoader."))); + } + + @Test @SuppressWarnings("unchecked") + public void testDeriveDetachesCorrectly() { + DefaultSerializationProviderConfiguration configuration = new DefaultSerializationProviderConfiguration(); + configuration.addSerializerFor(String.class, (Class) JavaSerializer.class); + + DefaultSerializationProviderConfiguration derived = configuration.build(configuration.derive()); + + assertThat(derived, is(not(sameInstance(configuration)))); + assertThat(derived.getDefaultSerializers(), is(not(sameInstance(configuration.getDefaultSerializers())))); + assertThat(derived.getDefaultSerializers().get(String.class), sameInstance(JavaSerializer.class)); } private static class MinimalSerializer implements Serializer { diff --git a/impl/src/test/java/org/ehcache/impl/config/serializer/SerializerCountingTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/config/serializer/SerializerCountingTest.java similarity index 91% rename from impl/src/test/java/org/ehcache/impl/config/serializer/SerializerCountingTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/config/serializer/SerializerCountingTest.java index 1acc4368b0..0b15b4d954 100644 --- a/impl/src/test/java/org/ehcache/impl/config/serializer/SerializerCountingTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/config/serializer/SerializerCountingTest.java @@ -42,8 +42,8 @@ import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder; import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; /** * SerializerCountingTest @@ -80,8 +80,8 @@ public void tearDown() { public void testOnHeapPutGet() { Cache cache = cacheManager.createCache("onHeap", newCacheConfigurationBuilder(Long.class, String.class, heap(10)) - .add(new DefaultCopierConfiguration<>(SerializingCopier.asCopierClass(), DefaultCopierConfiguration.Type.KEY)) - .add(new DefaultCopierConfiguration<>(SerializingCopier.asCopierClass(), DefaultCopierConfiguration.Type.VALUE)) + .withService(new DefaultCopierConfiguration<>(SerializingCopier.asCopierClass(), DefaultCopierConfiguration.Type.KEY)) + .withService(new DefaultCopierConfiguration<>(SerializingCopier.asCopierClass(), DefaultCopierConfiguration.Type.VALUE)) .build()); cache.put(42L, "TheAnswer!"); @@ -122,8 +122,8 @@ public void testOffHeapPutGet() { public void testOffHeapOnHeapCopyPutGet() { Cache cache = cacheManager.createCache("offHeap", newCacheConfigurationBuilder(Long.class, String.class, newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES).offheap(10, MemoryUnit.MB)) - .add(new DefaultCopierConfiguration<>(SerializingCopier.asCopierClass(), DefaultCopierConfiguration.Type.KEY)) - .add(new DefaultCopierConfiguration<>(SerializingCopier.asCopierClass(), DefaultCopierConfiguration.Type.VALUE)) + .withService(new DefaultCopierConfiguration<>(SerializingCopier.asCopierClass(), DefaultCopierConfiguration.Type.KEY)) + .withService(new DefaultCopierConfiguration<>(SerializingCopier.asCopierClass(), DefaultCopierConfiguration.Type.VALUE)) .build() ); @@ -146,8 +146,8 @@ public void testOffHeapOnHeapCopyPutGet() { public void testDiskOffHeapOnHeapCopyPutGet() { Cache cache = cacheManager.createCache("offHeap", newCacheConfigurationBuilder(Long.class, String.class, newResourcePoolsBuilder().heap(2, EntryUnit.ENTRIES).offheap(10, MemoryUnit.MB).disk(100, MemoryUnit.MB)) - .add(new DefaultCopierConfiguration<>(SerializingCopier.asCopierClass(), DefaultCopierConfiguration.Type.KEY)) - .add(new DefaultCopierConfiguration<>(SerializingCopier.asCopierClass(), DefaultCopierConfiguration.Type.VALUE)) + .withService(new DefaultCopierConfiguration<>(SerializingCopier.asCopierClass(), DefaultCopierConfiguration.Type.KEY)) + .withService(new DefaultCopierConfiguration<>(SerializingCopier.asCopierClass(), DefaultCopierConfiguration.Type.VALUE)) .build() ); diff --git a/ehcache-impl/src/test/java/org/ehcache/impl/config/store/disk/OffHeapDiskStoreConfigurationTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/config/store/disk/OffHeapDiskStoreConfigurationTest.java new file mode 100644 index 0000000000..c22d4c6132 --- /dev/null +++ b/ehcache-impl/src/test/java/org/ehcache/impl/config/store/disk/OffHeapDiskStoreConfigurationTest.java @@ -0,0 +1,38 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.impl.config.store.disk; + +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNot.not; +import static org.hamcrest.core.IsSame.sameInstance; + +public class OffHeapDiskStoreConfigurationTest { + + @Test + public void testDeriveDetachesProperly() { + OffHeapDiskStoreConfiguration configuration = new OffHeapDiskStoreConfiguration("foobar", 16, 42); + OffHeapDiskStoreConfiguration derived = configuration.build(configuration.derive()); + + assertThat(derived, is(not(sameInstance(configuration)))); + assertThat(derived.getThreadPoolAlias(), is(configuration.getThreadPoolAlias())); + assertThat(derived.getDiskSegments(), is(configuration.getDiskSegments())); + assertThat(derived.getWriterConcurrency(), is(configuration.getWriterConcurrency())); + } +} diff --git a/ehcache-impl/src/test/java/org/ehcache/impl/config/store/disk/OffHeapDiskStoreProviderConfigurationTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/config/store/disk/OffHeapDiskStoreProviderConfigurationTest.java new file mode 100644 index 0000000000..aca89ecc60 --- /dev/null +++ b/ehcache-impl/src/test/java/org/ehcache/impl/config/store/disk/OffHeapDiskStoreProviderConfigurationTest.java @@ -0,0 +1,36 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.impl.config.store.disk; + +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNot.not; +import static org.hamcrest.core.IsSame.sameInstance; + +public class OffHeapDiskStoreProviderConfigurationTest { + + @Test + public void testDeriveDetachesCorrectly() { + OffHeapDiskStoreProviderConfiguration configuration = new OffHeapDiskStoreProviderConfiguration("foobar"); + OffHeapDiskStoreProviderConfiguration derived = configuration.build(configuration.derive()); + + assertThat(derived, is(not(sameInstance(configuration)))); + assertThat(derived.getThreadPoolAlias(), is(configuration.getThreadPoolAlias())); + } +} diff --git a/impl/src/test/java/org/ehcache/impl/events/CacheEventDispatcherImplTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/events/CacheEventDispatcherImplTest.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/events/CacheEventDispatcherImplTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/events/CacheEventDispatcherImplTest.java diff --git a/impl/src/test/java/org/ehcache/impl/internal/DefaultTimeSourceServiceTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/DefaultTimeSourceServiceTest.java similarity index 86% rename from impl/src/test/java/org/ehcache/impl/internal/DefaultTimeSourceServiceTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/DefaultTimeSourceServiceTest.java index 7a3d6d7a49..e22c643ef0 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/DefaultTimeSourceServiceTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/DefaultTimeSourceServiceTest.java @@ -19,14 +19,12 @@ import org.ehcache.core.spi.time.SystemTimeSource; import org.ehcache.core.spi.time.TimeSource; import org.ehcache.core.spi.time.TimeSourceService; -import org.ehcache.core.internal.service.ServiceLocator; -import org.ehcache.spi.service.Service; -import org.ehcache.spi.service.ServiceDependencies; +import org.ehcache.core.spi.ServiceLocator; import org.junit.Test; -import static org.ehcache.core.internal.service.ServiceLocator.dependencySet; +import static org.ehcache.core.spi.ServiceLocator.dependencySet; import static org.hamcrest.CoreMatchers.sameInstance; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.mock; /** diff --git a/ehcache-impl/src/test/java/org/ehcache/impl/internal/TimeSourceConfigurationTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/TimeSourceConfigurationTest.java new file mode 100644 index 0000000000..eec92b7ac9 --- /dev/null +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/TimeSourceConfigurationTest.java @@ -0,0 +1,38 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.impl.internal; + +import org.ehcache.core.spi.time.TimeSource; +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNot.not; +import static org.hamcrest.core.IsSame.sameInstance; +import static org.mockito.Mockito.mock; + +public class TimeSourceConfigurationTest { + + @Test + public void testDeriveDetachesCorrectly() { + TimeSourceConfiguration configuration = new TimeSourceConfiguration(mock(TimeSource.class)); + TimeSourceConfiguration derived = configuration.build(configuration.derive()); + + assertThat(derived, is(not(sameInstance(configuration)))); + assertThat(derived.getTimeSource(), sameInstance(configuration.getTimeSource())); + } +} diff --git a/impl/src/test/java/org/ehcache/impl/internal/classes/ClassInstanceProviderConfigurationTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/classes/ClassInstanceProviderConfigurationTest.java similarity index 89% rename from impl/src/test/java/org/ehcache/impl/internal/classes/ClassInstanceProviderConfigurationTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/classes/ClassInstanceProviderConfigurationTest.java index 7983b50c60..001e4154f9 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/classes/ClassInstanceProviderConfigurationTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/classes/ClassInstanceProviderConfigurationTest.java @@ -30,7 +30,7 @@ public class ClassInstanceProviderConfigurationTest { @Test public void testOrdering() throws Exception { - ClassInstanceProviderConfiguration classInstanceProviderFactoryConfig = new ClassInstanceProviderConfiguration<>(); + ClassInstanceProviderConfiguration> classInstanceProviderFactoryConfig = new ClassInstanceProviderConfiguration<>(); for (int i = 0; i < 100; i++) { classInstanceProviderFactoryConfig.getDefaults().put("" + i, new ClassInstanceConfiguration(String.class)); diff --git a/impl/src/test/java/org/ehcache/impl/internal/classes/ClassInstanceProviderTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/classes/ClassInstanceProviderTest.java similarity index 71% rename from impl/src/test/java/org/ehcache/impl/internal/classes/ClassInstanceProviderTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/classes/ClassInstanceProviderTest.java index f529cedf61..5c467fcdde 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/classes/ClassInstanceProviderTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/classes/ClassInstanceProviderTest.java @@ -44,7 +44,7 @@ public class ClassInstanceProviderTest { @Test public void testNewInstanceUsingAliasAndNoArgs() throws Exception { - ClassInstanceProvider classInstanceProvider = new ClassInstanceProvider<>(null, configClass); + ClassInstanceProvider, TestService> classInstanceProvider = new ClassInstanceProvider<>(null, configClass); classInstanceProvider.preconfigured.put("test stuff", new ClassInstanceConfiguration(TestService.class)); TestService obj = classInstanceProvider.newInstance("test stuff", (ServiceConfiguration) null); @@ -54,17 +54,17 @@ public void testNewInstanceUsingAliasAndNoArgs() throws Exception { @Test public void testNewInstanceUsingAliasAndArg() throws Exception { - ClassInstanceProvider classInstanceProvider = new ClassInstanceProvider<>(null, configClass); + ClassInstanceProvider, TestService> classInstanceProvider = new ClassInstanceProvider<>(null, configClass); classInstanceProvider.preconfigured.put("test stuff", new ClassInstanceConfiguration<>(TestService.class, "test string")); - TestService obj = classInstanceProvider.newInstance("test stuff", (ServiceConfiguration) null); + TestService obj = classInstanceProvider.newInstance("test stuff", (ServiceConfiguration) null); assertThat(obj.theString, equalTo("test string")); } @Test public void testNewInstanceUsingServiceConfig() throws Exception { - ClassInstanceProvider classInstanceProvider = new ClassInstanceProvider<>(null, configClass); + ClassInstanceProvider, TestService> classInstanceProvider = new ClassInstanceProvider<>(null, configClass); TestServiceConfiguration config = new TestServiceConfiguration(); TestService obj = classInstanceProvider.newInstance("test stuff", config); @@ -77,7 +77,7 @@ public void testNewInstanceUsingServiceConfigFactory() throws Exception { TestServiceProviderConfiguration factoryConfig = new TestServiceProviderConfiguration(); factoryConfig.getDefaults().put("test stuff", new ClassInstanceConfiguration(TestService.class)); - ClassInstanceProvider classInstanceProvider = new ClassInstanceProvider<>(factoryConfig, configClass); + ClassInstanceProvider, TestService> classInstanceProvider = new ClassInstanceProvider<>(factoryConfig, configClass); classInstanceProvider.start(null); TestService obj = classInstanceProvider.newInstance("test stuff", (ServiceConfiguration) null); @@ -86,14 +86,14 @@ public void testNewInstanceUsingServiceConfigFactory() throws Exception { @Test(expected = IllegalArgumentException.class) public void testReleaseInstanceByAnotherProvider() throws Exception { - ClassInstanceProvider classInstanceProvider = new ClassInstanceProvider<>(null, null); + ClassInstanceProvider, String> classInstanceProvider = new ClassInstanceProvider<>(null, null); classInstanceProvider.releaseInstance("foo"); } @Test(expected = IllegalArgumentException.class) public void testReleaseSameInstanceMultipleTimesThrows() throws Exception { - ClassInstanceProvider classInstanceProvider = new ClassInstanceProvider<>(null, null); + ClassInstanceProvider, String> classInstanceProvider = new ClassInstanceProvider<>(null, null); classInstanceProvider.providedVsCount.put("foo", new AtomicInteger(1)); classInstanceProvider.releaseInstance("foo"); @@ -102,7 +102,7 @@ public void testReleaseSameInstanceMultipleTimesThrows() throws Exception { @Test public void testReleaseCloseableInstance() throws Exception { - ClassInstanceProvider classInstanceProvider = new ClassInstanceProvider<>(null, null); + ClassInstanceProvider, Closeable> classInstanceProvider = new ClassInstanceProvider<>(null, null); Closeable closeable = mock(Closeable.class); classInstanceProvider.providedVsCount.put(closeable, new AtomicInteger(1)); classInstanceProvider.instantiated.add(closeable); @@ -113,7 +113,7 @@ public void testReleaseCloseableInstance() throws Exception { @Test(expected = IOException.class) public void testReleaseCloseableInstanceThrows() throws Exception { - ClassInstanceProvider classInstanceProvider = new ClassInstanceProvider<>(null, null); + ClassInstanceProvider, Closeable> classInstanceProvider = new ClassInstanceProvider<>(null, null); Closeable closeable = mock(Closeable.class); doThrow(IOException.class).when(closeable).close(); classInstanceProvider.providedVsCount.put(closeable, new AtomicInteger(1)); @@ -124,7 +124,7 @@ public void testReleaseCloseableInstanceThrows() throws Exception { @Test public void testNewInstanceWithActualInstanceInServiceConfig() throws Exception { - ClassInstanceProvider classInstanceProvider = new ClassInstanceProvider<>(null, configClass); + ClassInstanceProvider, TestService> classInstanceProvider = new ClassInstanceProvider<>(null, configClass); TestService service = new TestService(); TestServiceConfiguration config = new TestServiceConfiguration(service); @@ -136,7 +136,7 @@ public void testNewInstanceWithActualInstanceInServiceConfig() throws Exception @Test public void testSameInstanceRetrievedMultipleTimesUpdatesTheProvidedCount() throws Exception { - ClassInstanceProvider classInstanceProvider = new ClassInstanceProvider<>(null, configClass); + ClassInstanceProvider, TestService> classInstanceProvider = new ClassInstanceProvider<>(null, configClass); TestService service = new TestService(); TestServiceConfiguration config = new TestServiceConfiguration(service); @@ -152,13 +152,13 @@ public void testSameInstanceRetrievedMultipleTimesUpdatesTheProvidedCount() thro @Test public void testInstancesNotCreatedByProviderDoesNotClose() throws IOException { @SuppressWarnings("unchecked") - Class> configClass = (Class) ClassInstanceConfiguration.class; - ClassInstanceProvider classInstanceProvider = new ClassInstanceProvider<>(null, configClass); + Class> configClass = (Class) ClassInstanceConfiguration.class; + ClassInstanceProvider, TestCloseableService> classInstanceProvider = new ClassInstanceProvider<>(null, configClass); - TestCloaseableService service = mock(TestCloaseableService.class); + TestCloseableService service = mock(TestCloseableService.class); TestCloaseableServiceConfig config = new TestCloaseableServiceConfig(service); - TestCloaseableService newService = classInstanceProvider.newInstance("testClose", config); + TestCloseableService newService = classInstanceProvider.newInstance("testClose", config); assertThat(newService, sameInstance(service)); classInstanceProvider.releaseInstance(newService); verify(service, times(0)).close(); @@ -166,23 +166,23 @@ public void testInstancesNotCreatedByProviderDoesNotClose() throws IOException { } - public static abstract class TestCloaseableService implements Service, Closeable { + public static abstract class TestCloseableService implements Service, Closeable { } - public static class TestCloaseableServiceConfig extends ClassInstanceConfiguration implements ServiceConfiguration { + public static class TestCloaseableServiceConfig extends ClassInstanceConfiguration implements ServiceConfiguration { public TestCloaseableServiceConfig() { - super(TestCloaseableService.class); + super(TestCloseableService.class); } - public TestCloaseableServiceConfig(TestCloaseableService testCloaseableService) { - super(testCloaseableService); + public TestCloaseableServiceConfig(TestCloseableService testCloseableService) { + super(testCloseableService); } @Override - public Class getServiceType() { - return TestCloaseableService.class; + public Class getServiceType() { + return TestCloseableService.class; } } @@ -206,7 +206,7 @@ public void stop() { } } - public static class TestServiceConfiguration extends ClassInstanceConfiguration implements ServiceConfiguration { + public static class TestServiceConfiguration extends ClassInstanceConfiguration implements ServiceConfiguration { public TestServiceConfiguration() { super(TestService.class); } @@ -221,7 +221,7 @@ public Class getServiceType() { } } - public static class TestServiceProviderConfiguration extends ClassInstanceProviderConfiguration implements ServiceConfiguration { + public static class TestServiceProviderConfiguration extends ClassInstanceProviderConfiguration> implements ServiceConfiguration { @Override public Class getServiceType() { return TestService.class; diff --git a/impl/src/test/java/org/ehcache/impl/internal/concurrent/ConcurrentHashMapTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/concurrent/ConcurrentHashMapTest.java similarity index 99% rename from impl/src/test/java/org/ehcache/impl/internal/concurrent/ConcurrentHashMapTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/concurrent/ConcurrentHashMapTest.java index ff3bdb1180..d5c93b27b4 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/concurrent/ConcurrentHashMapTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/concurrent/ConcurrentHashMapTest.java @@ -25,10 +25,10 @@ import static org.ehcache.config.Eviction.noAdvice; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.IsNull.notNullValue; import static org.hamcrest.core.IsNull.nullValue; import static org.hamcrest.number.OrderingComparison.greaterThan; -import static org.junit.Assert.assertThat; /** * @author Alex Snaps diff --git a/impl/src/test/java/org/ehcache/impl/internal/concurrent/otherPackage/V8FeaturesTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/concurrent/otherPackage/V8FeaturesTest.java similarity index 99% rename from impl/src/test/java/org/ehcache/impl/internal/concurrent/otherPackage/V8FeaturesTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/concurrent/otherPackage/V8FeaturesTest.java index be4efb1f09..167e0b3189 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/concurrent/otherPackage/V8FeaturesTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/concurrent/otherPackage/V8FeaturesTest.java @@ -25,10 +25,10 @@ import java.util.concurrent.atomic.AtomicInteger; import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; /** * @author Ludovic Orban diff --git a/impl/src/test/java/org/ehcache/impl/internal/copy/IdentityCopierTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/copy/IdentityCopierTest.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/internal/copy/IdentityCopierTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/copy/IdentityCopierTest.java diff --git a/impl/src/test/java/org/ehcache/impl/internal/copy/SerializingCopierTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/copy/SerializingCopierTest.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/internal/copy/SerializingCopierTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/copy/SerializingCopierTest.java diff --git a/impl/src/test/java/org/ehcache/impl/internal/events/CacheEventDispatcherFactoryImplTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/events/CacheEventDispatcherFactoryImplTest.java similarity index 98% rename from impl/src/test/java/org/ehcache/impl/internal/events/CacheEventDispatcherFactoryImplTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/events/CacheEventDispatcherFactoryImplTest.java index 879cc86633..ee7193de05 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/events/CacheEventDispatcherFactoryImplTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/events/CacheEventDispatcherFactoryImplTest.java @@ -28,8 +28,8 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.instanceOf; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; diff --git a/impl/src/test/java/org/ehcache/impl/internal/events/FudgingInvocationScopedEventSinkTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/events/FudgingInvocationScopedEventSinkTest.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/internal/events/FudgingInvocationScopedEventSinkTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/events/FudgingInvocationScopedEventSinkTest.java diff --git a/impl/src/test/java/org/ehcache/impl/internal/events/InvocationScopedEventSinkTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/events/InvocationScopedEventSinkTest.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/internal/events/InvocationScopedEventSinkTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/events/InvocationScopedEventSinkTest.java diff --git a/impl/src/test/java/org/ehcache/impl/internal/events/TestStoreEventDispatcher.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/events/TestStoreEventDispatcher.java similarity index 95% rename from impl/src/test/java/org/ehcache/impl/internal/events/TestStoreEventDispatcher.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/events/TestStoreEventDispatcher.java index 6292adc84d..51bc63a89e 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/events/TestStoreEventDispatcher.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/events/TestStoreEventDispatcher.java @@ -85,6 +85,11 @@ public void setEventOrdering(boolean ordering) { throw new UnsupportedOperationException("Test impl cannot be made ordered"); } + @Override + public void setSynchronous(boolean synchronous) throws IllegalArgumentException { + throw new UnsupportedOperationException("Test impl cannot be made synchronous"); + } + @Override public boolean isEventOrdering() { return false; diff --git a/impl/src/test/java/org/ehcache/impl/internal/executor/PartitionedOrderedExecutorTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/executor/PartitionedOrderedExecutorTest.java similarity index 99% rename from impl/src/test/java/org/ehcache/impl/internal/executor/PartitionedOrderedExecutorTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/executor/PartitionedOrderedExecutorTest.java index 91005b0c87..2e93bafd50 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/executor/PartitionedOrderedExecutorTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/executor/PartitionedOrderedExecutorTest.java @@ -33,10 +33,11 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MINUTES; import java.util.concurrent.atomic.AtomicInteger; + +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.collection.IsCollectionWithSize.hasSize; import static org.hamcrest.collection.IsEmptyCollection.empty; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; diff --git a/impl/src/test/java/org/ehcache/impl/internal/executor/PartitionedScheduledExecutorTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/executor/PartitionedScheduledExecutorTest.java similarity index 99% rename from impl/src/test/java/org/ehcache/impl/internal/executor/PartitionedScheduledExecutorTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/executor/PartitionedScheduledExecutorTest.java index a736abddc0..fed0fc7cab 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/executor/PartitionedScheduledExecutorTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/executor/PartitionedScheduledExecutorTest.java @@ -35,10 +35,10 @@ import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.SECONDS; import static org.ehcache.impl.internal.executor.ExecutorUtil.waitFor; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.collection.IsCollectionWithSize.hasSize; import static org.hamcrest.collection.IsEmptyCollection.empty; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; public class PartitionedScheduledExecutorTest { diff --git a/impl/src/test/java/org/ehcache/impl/internal/executor/PartitionedUnorderedExecutorTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/executor/PartitionedUnorderedExecutorTest.java similarity index 99% rename from impl/src/test/java/org/ehcache/impl/internal/executor/PartitionedUnorderedExecutorTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/executor/PartitionedUnorderedExecutorTest.java index a1d15ad348..1f787f0684 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/executor/PartitionedUnorderedExecutorTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/executor/PartitionedUnorderedExecutorTest.java @@ -29,10 +29,10 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MINUTES; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.collection.IsCollectionWithSize.hasSize; import static org.hamcrest.collection.IsEmptyCollection.empty; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; diff --git a/impl/src/test/java/org/ehcache/impl/internal/executor/PooledExecutionServiceTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/executor/PooledExecutionServiceTest.java similarity index 68% rename from impl/src/test/java/org/ehcache/impl/internal/executor/PooledExecutionServiceTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/executor/PooledExecutionServiceTest.java index 3c18d4758f..8d5dc1f00f 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/executor/PooledExecutionServiceTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/executor/PooledExecutionServiceTest.java @@ -18,9 +18,7 @@ import org.ehcache.impl.config.executor.PooledExecutionServiceConfiguration; import org.ehcache.impl.internal.util.ThreadFactoryUtil; import org.junit.After; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import java.io.PrintWriter; import java.io.StringWriter; @@ -28,101 +26,95 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; /** * @author Ludovic Orban */ public class PooledExecutionServiceTest { - @Rule - public ExpectedException expectedException = ExpectedException.none(); - - PooledExecutionService pooledExecutionService; - - @After - public void after() { - if(pooledExecutionService != null) { - pooledExecutionService.stop(); - } - } - @Test public void testEmptyConfigThrowsAtStart() throws Exception { PooledExecutionServiceConfiguration configuration = new PooledExecutionServiceConfiguration(); - pooledExecutionService = new PooledExecutionService(configuration); - - expectedException.expectMessage("Pool configuration is empty"); - pooledExecutionService.start(null); + PooledExecutionService pooledExecutionService = new PooledExecutionService(configuration); + assertThatThrownBy(() -> pooledExecutionService.start(null)) + .isInstanceOf(IllegalStateException.class).hasMessage("Pool configuration is empty"); } @Test public void testGetOrderedExecutorFailsOnNonExistentPool() throws Exception { PooledExecutionServiceConfiguration configuration = new PooledExecutionServiceConfiguration(); configuration.addPool("getOrderedExecutorFailsOnNonExistentPool", 0, 1); - pooledExecutionService = new PooledExecutionService(configuration); - + PooledExecutionService pooledExecutionService = new PooledExecutionService(configuration); pooledExecutionService.start(null); - - expectedException.expectMessage("Pool 'abc' is not in the set of available pools [getOrderedExecutorFailsOnNonExistentPool]"); - pooledExecutionService.getOrderedExecutor("abc", new LinkedBlockingDeque<>()); + try { + assertThatThrownBy(() -> pooledExecutionService.getOrderedExecutor("abc", new LinkedBlockingDeque<>())) + .isInstanceOf(IllegalArgumentException.class).hasMessage("Pool 'abc' is not in the set of available pools [getOrderedExecutorFailsOnNonExistentPool]"); + } finally { + pooledExecutionService.stop(); + } } @Test public void testGetOrderedExecutorFailsOnNonExistentDefaultPool() throws Exception { PooledExecutionServiceConfiguration configuration = new PooledExecutionServiceConfiguration(); configuration.addPool("getOrderedExecutorFailsOnNonExistentDefaultPool", 0, 1); - pooledExecutionService = new PooledExecutionService(configuration); - + PooledExecutionService pooledExecutionService = new PooledExecutionService(configuration); pooledExecutionService.start(null); - - expectedException.expectMessage("Null pool alias provided and no default pool configured"); - pooledExecutionService.getOrderedExecutor(null, new LinkedBlockingDeque<>()); + try { + assertThatThrownBy(() -> pooledExecutionService.getOrderedExecutor(null, new LinkedBlockingDeque<>())) + .isInstanceOf(IllegalArgumentException.class).hasMessage("Null pool alias provided and no default pool configured"); + } finally { + pooledExecutionService.stop(); + } } @Test public void testGetOrderedExecutorSucceedsOnExistingPool() throws Exception { PooledExecutionServiceConfiguration configuration = new PooledExecutionServiceConfiguration(); configuration.addPool("getOrderedExecutorSucceedsOnExistingPool", 0, 1); - pooledExecutionService = new PooledExecutionService(configuration); - + PooledExecutionService pooledExecutionService = new PooledExecutionService(configuration); pooledExecutionService.start(null); - - ExecutorService aaa = pooledExecutionService.getOrderedExecutor("getOrderedExecutorSucceedsOnExistingPool", new LinkedBlockingDeque<>()); - aaa.shutdown(); + try { + pooledExecutionService.getOrderedExecutor("getOrderedExecutorSucceedsOnExistingPool", new LinkedBlockingDeque<>()).shutdown(); + } finally { + pooledExecutionService.stop(); + } } @Test public void testGetOrderedExecutorSucceedsOnExistingDefaultPool() throws Exception { PooledExecutionServiceConfiguration configuration = new PooledExecutionServiceConfiguration(); configuration.addDefaultPool("getOrderedExecutorSucceedsOnExistingDefaultPool", 0, 1); - pooledExecutionService = new PooledExecutionService(configuration); - + PooledExecutionService pooledExecutionService = new PooledExecutionService(configuration); pooledExecutionService.start(null); - - ExecutorService dflt = pooledExecutionService.getOrderedExecutor(null, new LinkedBlockingDeque<>()); - dflt.shutdown(); + try { + pooledExecutionService.getOrderedExecutor(null, new LinkedBlockingDeque<>()).shutdown(); + } finally { + pooledExecutionService.stop(); + } } @Test public void testAllThreadsAreStopped() throws Exception { PooledExecutionServiceConfiguration configuration = new PooledExecutionServiceConfiguration(); configuration.addDefaultPool("allThreadsAreStopped", 0, 1); - pooledExecutionService = new PooledExecutionService(configuration); + PooledExecutionService pooledExecutionService = new PooledExecutionService(configuration); pooledExecutionService.start(null); + try { + final CountDownLatch latch = new CountDownLatch(1); - final CountDownLatch latch = new CountDownLatch(1); - - pooledExecutionService.getScheduledExecutor("allThreadsAreStopped") - .execute(latch::countDown); + pooledExecutionService.getScheduledExecutor("allThreadsAreStopped") + .execute(latch::countDown); - assertThat(latch.await(30, TimeUnit.SECONDS)).isTrue(); - - pooledExecutionService.stop(); + assertThat(latch.await(30, TimeUnit.SECONDS)).isTrue(); + } finally { + pooledExecutionService.stop(); + } assertThat(Thread.currentThread().isInterrupted()).isFalse(); diff --git a/impl/src/test/java/org/ehcache/impl/internal/loaderwriter/writebehind/AbstractWriteBehindTestBase.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/loaderwriter/writebehind/AbstractWriteBehindTestBase.java similarity index 92% rename from impl/src/test/java/org/ehcache/impl/internal/loaderwriter/writebehind/AbstractWriteBehindTestBase.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/loaderwriter/writebehind/AbstractWriteBehindTestBase.java index 1cd33a826d..b7d4096761 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/loaderwriter/writebehind/AbstractWriteBehindTestBase.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/loaderwriter/writebehind/AbstractWriteBehindTestBase.java @@ -43,10 +43,10 @@ import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; import static org.ehcache.config.builders.WriteBehindConfigurationBuilder.newBatchedWriteBehindConfiguration; import static org.ehcache.config.builders.WriteBehindConfigurationBuilder.newUnBatchedWriteBehindConfiguration; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.collection.IsIterableContainingInOrder.contains; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; @@ -72,7 +72,7 @@ public void testWriteOrdering() throws Exception { try (CacheManager cacheManager = managerBuilder().using(cacheLoaderWriterProvider).build(true)) { Cache testCache = cacheManager.createCache("testWriteOrdering", configurationBuilder() .withLoaderWriter(loaderWriter) - .add(newBatchedWriteBehindConfiguration(Long.MAX_VALUE, SECONDS, 8).build()) + .withService(newBatchedWriteBehindConfiguration(Long.MAX_VALUE, SECONDS, 8).build()) .build()); CountDownLatch countDownLatch = new CountDownLatch(8); @@ -103,7 +103,7 @@ public void testWrites() throws Exception { try (CacheManager cacheManager = managerBuilder().using(cacheLoaderWriterProvider).build(true)) { Cache testCache = cacheManager.createCache("testWrites", CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, String.class, heap(10)) .withLoaderWriter(loaderWriter) - .add(newUnBatchedWriteBehindConfiguration().concurrencyLevel(3).queueSize(10).build()) + .withService(newUnBatchedWriteBehindConfiguration().concurrencyLevel(3).queueSize(10).build()) .build()); CountDownLatch countDownLatch = new CountDownLatch(4); @@ -129,7 +129,7 @@ public void testBulkWrites() throws Exception { try (CacheManager cacheManager = managerBuilder().using(cacheLoaderWriterProvider).build(true)) { Cache testCache = cacheManager.createCache("testBulkWrites", CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, String.class, heap(100)) .withLoaderWriter(loaderWriter) - .add(newUnBatchedWriteBehindConfiguration().concurrencyLevel(3).queueSize(10).build()) + .withService(newUnBatchedWriteBehindConfiguration().concurrencyLevel(3).queueSize(10).build()) .build()); CountDownLatch countDownLatch = new CountDownLatch(20); @@ -175,7 +175,7 @@ public void testThatAllGetsReturnLatestData() throws Exception { try (CacheManager cacheManager = managerBuilder().using(cacheLoaderWriterProvider).build(true)) { Cache testCache = cacheManager.createCache("testThatAllGetsReturnLatestData", configurationBuilder() .withLoaderWriter(loaderWriter) - .add(newUnBatchedWriteBehindConfiguration().concurrencyLevel(3).queueSize(10).build()) + .withService(newUnBatchedWriteBehindConfiguration().concurrencyLevel(3).queueSize(10).build()) .build()); for (int i = 0; i < 10; i++) { @@ -219,7 +219,7 @@ public void testAllGetsReturnLatestDataWithKeyCollision() { try (CacheManager cacheManager = managerBuilder().using(cacheLoaderWriterProvider).build(true)) { Cache testCache = cacheManager.createCache("testAllGetsReturnLatestDataWithKeyCollision", configurationBuilder() .withLoaderWriter(loaderWriter) - .add(newUnBatchedWriteBehindConfiguration().concurrencyLevel(3).queueSize(10).build()) + .withService(newUnBatchedWriteBehindConfiguration().concurrencyLevel(3).queueSize(10).build()) .build()); Random random = new Random(); @@ -249,7 +249,7 @@ public void testBatchedDeletedKeyReturnsNull() throws Exception { try (CacheManager cacheManager = managerBuilder().using(cacheLoaderWriterProvider).build(true)) { Cache testCache = cacheManager.createCache("testBatchedDeletedKeyReturnsNull", configurationBuilder() .withLoaderWriter(loaderWriter) - .add(newBatchedWriteBehindConfiguration(Long.MAX_VALUE, SECONDS, 2).build()) + .withService(newBatchedWriteBehindConfiguration(Long.MAX_VALUE, SECONDS, 2).build()) .build()); assertThat(testCache.get("key"), is("value")); @@ -277,7 +277,7 @@ public void testUnBatchedDeletedKeyReturnsNull() throws Exception { try { Cache testCache = cacheManager.createCache("testUnBatchedDeletedKeyReturnsNull", configurationBuilder() .withLoaderWriter(loaderWriter) - .add(newUnBatchedWriteBehindConfiguration().build()) + .withService(newUnBatchedWriteBehindConfiguration().build()) .build()); assertThat(testCache.get("key"), is("value")); @@ -301,7 +301,7 @@ public void testBatchedOverwrittenKeyReturnsNewValue() throws Exception { try (CacheManager cacheManager = managerBuilder().using(cacheLoaderWriterProvider).build(true)) { Cache testCache = cacheManager.createCache("testBatchedOverwrittenKeyReturnsNewValue", configurationBuilder() .withLoaderWriter(loaderWriter) - .add(newBatchedWriteBehindConfiguration(Long.MAX_VALUE, SECONDS, 2).build()) + .withService(newBatchedWriteBehindConfiguration(Long.MAX_VALUE, SECONDS, 2).build()) .build()); assertThat(testCache.get("key"), is("value")); @@ -329,7 +329,7 @@ public void testUnBatchedOverwrittenKeyReturnsNewValue() throws Exception { try { Cache testCache = cacheManager.createCache("testUnBatchedOverwrittenKeyReturnsNewValue", configurationBuilder() .withLoaderWriter(loaderWriter) - .add(newUnBatchedWriteBehindConfiguration().build()) + .withService(newUnBatchedWriteBehindConfiguration().build()) .build()); assertThat(testCache.get("key"), is("value")); @@ -351,7 +351,7 @@ public void testCoaslecedWritesAreNotSeen() throws InterruptedException { try (CacheManager cacheManager = managerBuilder().using(cacheLoaderWriterProvider).build(true)) { Cache testCache = cacheManager.createCache("testCoaslecedWritesAreNotSeen", configurationBuilder() .withLoaderWriter(loaderWriter) - .add(newBatchedWriteBehindConfiguration(Long.MAX_VALUE, SECONDS, 2).enableCoalescing().build()) + .withService(newBatchedWriteBehindConfiguration(Long.MAX_VALUE, SECONDS, 2).enableCoalescing().build()) .build()); CountDownLatch latch = new CountDownLatch(2); @@ -376,7 +376,7 @@ public void testUnBatchedWriteBehindStopWaitsForEmptyQueue() { try (CacheManager cacheManager = managerBuilder().using(cacheLoaderWriterProvider).build(true)) { Cache testCache = cacheManager.createCache("testUnBatchedWriteBehindStopWaitsForEmptyQueue", configurationBuilder() .withLoaderWriter(loaderWriter) - .add(newUnBatchedWriteBehindConfiguration().build()) + .withService(newUnBatchedWriteBehindConfiguration().build()) .build()); testCache.put("key", "value"); @@ -392,7 +392,7 @@ public void testBatchedWriteBehindStopWaitsForEmptyQueue() { try (CacheManager cacheManager = managerBuilder().using(cacheLoaderWriterProvider).build(true)) { Cache testCache = cacheManager.createCache("testBatchedWriteBehindStopWaitsForEmptyQueue", configurationBuilder() .withLoaderWriter(loaderWriter) - .add(newBatchedWriteBehindConfiguration(Long.MAX_VALUE, SECONDS, 2).build()) + .withService(newBatchedWriteBehindConfiguration(Long.MAX_VALUE, SECONDS, 2).build()) .build()); testCache.put("key", "value"); @@ -415,7 +415,7 @@ public void testUnBatchedWriteBehindBlocksWhenFull() throws Exception { try (CacheManager cacheManager = managerBuilder().using(cacheLoaderWriterProvider).build(true)) { final Cache testCache = cacheManager.createCache("testUnBatchedWriteBehindBlocksWhenFull", configurationBuilder() .withLoaderWriter(loaderWriter) - .add(newUnBatchedWriteBehindConfiguration().queueSize(1).build()) + .withService(newUnBatchedWriteBehindConfiguration().queueSize(1).build()) .build()); testCache.put("key1", "value"); @@ -455,7 +455,7 @@ public void testBatchedWriteBehindBlocksWhenFull() throws Exception { try (CacheManager cacheManager = managerBuilder().using(cacheLoaderWriterProvider).build(true)) { final Cache testCache = cacheManager.createCache("testBatchedWriteBehindBlocksWhenFull", configurationBuilder() .withLoaderWriter(loaderWriter) - .add(newBatchedWriteBehindConfiguration(Long.MAX_VALUE, SECONDS, 1).queueSize(1).build()) + .withService(newBatchedWriteBehindConfiguration(Long.MAX_VALUE, SECONDS, 1).queueSize(1).build()) .build()); testCache.put("key1", "value"); @@ -488,7 +488,7 @@ public void testFilledBatchedIsWritten() throws Exception { try (CacheManager cacheManager = managerBuilder().using(cacheLoaderWriterProvider).build(true)) { Cache testCache = cacheManager.createCache("testFilledBatchedIsWritten", configurationBuilder() .withLoaderWriter(loaderWriter) - .add(newBatchedWriteBehindConfiguration(Long.MAX_VALUE, SECONDS, 2).build()) + .withService(newBatchedWriteBehindConfiguration(Long.MAX_VALUE, SECONDS, 2).build()) .build()); CountDownLatch latch = new CountDownLatch(2); @@ -514,7 +514,7 @@ public void testAgedBatchedIsWritten() throws Exception { try (CacheManager cacheManager = managerBuilder().using(cacheLoaderWriterProvider).build(true)) { Cache testCache = cacheManager.createCache("testAgedBatchedIsWritten", configurationBuilder() .withLoaderWriter(loaderWriter) - .add(newBatchedWriteBehindConfiguration(1, SECONDS, 2).build()) + .withService(newBatchedWriteBehindConfiguration(1, SECONDS, 2).build()) .build()); CountDownLatch latch = new CountDownLatch(1); @@ -539,7 +539,7 @@ class TestWriteBehindProvider extends WriteBehindProviderFactory.Provider { @Override @SuppressWarnings("unchecked") - public WriteBehind createWriteBehindLoaderWriter(CacheLoaderWriter cacheLoaderWriter, WriteBehindConfiguration configuration) { + public WriteBehind createWriteBehindLoaderWriter(CacheLoaderWriter cacheLoaderWriter, WriteBehindConfiguration configuration) { this.writeBehind = super.createWriteBehindLoaderWriter(cacheLoaderWriter, configuration); return (WriteBehind) writeBehind; } @@ -554,8 +554,8 @@ public WriteBehind createWriteBehindLoaderWriter(CacheLoaderWriter< try (CacheManager cacheManager = managerBuilder().using(writeBehindProvider).build(true)) { Cache testCache = cacheManager.createCache("testAgedBatchedIsWritten", configurationBuilder() - .add(new DefaultCacheLoaderWriterConfiguration(loaderWriter)) - .add(newBatchedWriteBehindConfiguration(5, SECONDS, 2).build()) + .withService(new DefaultCacheLoaderWriterConfiguration(loaderWriter)) + .withService(newBatchedWriteBehindConfiguration(5, SECONDS, 2).build()) .build()); testCache.put("key1", "value1"); diff --git a/impl/src/test/java/org/ehcache/impl/internal/loaderwriter/writebehind/PooledExecutorWriteBehindTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/loaderwriter/writebehind/PooledExecutorWriteBehindTest.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/internal/loaderwriter/writebehind/PooledExecutorWriteBehindTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/loaderwriter/writebehind/PooledExecutorWriteBehindTest.java diff --git a/impl/src/test/java/org/ehcache/impl/internal/loaderwriter/writebehind/WriteBehindEvictionTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/loaderwriter/writebehind/WriteBehindEvictionTest.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/internal/loaderwriter/writebehind/WriteBehindEvictionTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/loaderwriter/writebehind/WriteBehindEvictionTest.java diff --git a/impl/src/test/java/org/ehcache/impl/internal/loaderwriter/writebehind/WriteBehindProviderFactoryTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/loaderwriter/writebehind/WriteBehindProviderFactoryTest.java similarity index 79% rename from impl/src/test/java/org/ehcache/impl/internal/loaderwriter/writebehind/WriteBehindProviderFactoryTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/loaderwriter/writebehind/WriteBehindProviderFactoryTest.java index c5db714bbb..e60ff8b3fa 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/loaderwriter/writebehind/WriteBehindProviderFactoryTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/loaderwriter/writebehind/WriteBehindProviderFactoryTest.java @@ -26,31 +26,29 @@ import org.ehcache.spi.loaderwriter.WriteBehindConfiguration; import org.ehcache.spi.service.ServiceConfiguration; import org.hamcrest.core.IsCollectionContaining; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import java.util.Collection; import java.util.Map; import static java.util.concurrent.TimeUnit.SECONDS; import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasProperty; +import static org.hamcrest.Matchers.is; import static org.hamcrest.core.IsInstanceOf.instanceOf; -import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertThrows; /** * @author rism */ public class WriteBehindProviderFactoryTest { - @Rule - public ExpectedException expectedEx = ExpectedException.none(); - @SuppressWarnings("unchecked") @Test public void testAddingWriteBehindConfigurationAtCacheLevel() { CacheManagerBuilder cacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder(); - WriteBehindConfiguration writeBehindConfiguration = WriteBehindConfigurationBuilder.newBatchedWriteBehindConfiguration(Long.MAX_VALUE, SECONDS, 1) + WriteBehindConfiguration writeBehindConfiguration = WriteBehindConfigurationBuilder.newBatchedWriteBehindConfiguration(Long.MAX_VALUE, SECONDS, 1) .concurrencyLevel(3) .queueSize(10) .build(); @@ -58,22 +56,20 @@ public void testAddingWriteBehindConfigurationAtCacheLevel() { CacheManager cacheManager = cacheManagerBuilder.build(true); final Cache cache = cacheManager.createCache("cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, heap(100)) - .add(writeBehindConfiguration) - .add(new DefaultCacheLoaderWriterConfiguration(klazz)) + .withService(writeBehindConfiguration) + .withService(new DefaultCacheLoaderWriterConfiguration(klazz)) .build()); - Collection> serviceConfiguration = cache.getRuntimeConfiguration() + Collection> serviceConfiguration = cache.getRuntimeConfiguration() .getServiceConfigurations(); - assertThat(serviceConfiguration, IsCollectionContaining.>hasItem(instanceOf(WriteBehindConfiguration.class))); + assertThat(serviceConfiguration, IsCollectionContaining.>hasItem(instanceOf(WriteBehindConfiguration.class))); cacheManager.close(); } @Test public void testWriteBehindWithoutCacheLoaderWriter() { - expectedEx.expect(NullPointerException.class); - expectedEx.expectMessage("WriteBehind requires a non null CacheLoaderWriter"); - WriteBehindProviderFactory factory = new WriteBehindProviderFactory(); - factory.create(null).createWriteBehindLoaderWriter(null, null); + NullPointerException thrown = assertThrows(NullPointerException.class, () -> factory.create(null).createWriteBehindLoaderWriter(null, null)); + assertThat(thrown, hasProperty("message", is("WriteBehind requires a non null CacheLoaderWriter."))); } public static class SampleLoaderWriter implements CacheLoaderWriter { diff --git a/impl/src/test/java/org/ehcache/impl/internal/loaderwriter/writebehind/WriteBehindTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/loaderwriter/writebehind/WriteBehindTest.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/internal/loaderwriter/writebehind/WriteBehindTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/loaderwriter/writebehind/WriteBehindTest.java diff --git a/impl/src/test/java/org/ehcache/impl/internal/loaderwriter/writebehind/WriteBehindTestLoaderWriter.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/loaderwriter/writebehind/WriteBehindTestLoaderWriter.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/internal/loaderwriter/writebehind/WriteBehindTestLoaderWriter.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/loaderwriter/writebehind/WriteBehindTestLoaderWriter.java diff --git a/impl/src/test/java/org/ehcache/impl/internal/persistence/CacheManagerDestroyRemovesPersistenceTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/persistence/CacheManagerDestroyRemovesPersistenceTest.java similarity index 98% rename from impl/src/test/java/org/ehcache/impl/internal/persistence/CacheManagerDestroyRemovesPersistenceTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/persistence/CacheManagerDestroyRemovesPersistenceTest.java index 7502152174..ac5137dfce 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/persistence/CacheManagerDestroyRemovesPersistenceTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/persistence/CacheManagerDestroyRemovesPersistenceTest.java @@ -16,7 +16,6 @@ package org.ehcache.impl.internal.persistence; import org.ehcache.Cache; -import org.ehcache.CachePersistenceException; import org.ehcache.PersistentCacheManager; import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.config.builders.CacheManagerBuilder; @@ -29,7 +28,6 @@ import java.io.File; import java.io.IOException; -import java.net.URISyntaxException; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; import static org.ehcache.impl.internal.util.FileExistenceMatchers.containsCacheDirectory; diff --git a/impl/src/test/java/org/ehcache/impl/internal/persistence/TestDiskResourceService.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/persistence/TestDiskResourceService.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/internal/persistence/TestDiskResourceService.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/persistence/TestDiskResourceService.java diff --git a/core/src/test/java/org/ehcache/core/internal/resilience/RobustLoaderWriterResilienceStrategyTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/resilience/RobustLoaderWriterResilienceStrategyTest.java similarity index 94% rename from core/src/test/java/org/ehcache/core/internal/resilience/RobustLoaderWriterResilienceStrategyTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/resilience/RobustLoaderWriterResilienceStrategyTest.java index 8561840778..e9505f007a 100644 --- a/core/src/test/java/org/ehcache/core/internal/resilience/RobustLoaderWriterResilienceStrategyTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/resilience/RobustLoaderWriterResilienceStrategyTest.java @@ -13,11 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.ehcache.core.internal.resilience; +package org.ehcache.impl.internal.resilience; import org.assertj.core.data.MapEntry; -import org.ehcache.core.internal.util.CollectionUtil; -import org.ehcache.core.spi.store.Store; import org.ehcache.spi.loaderwriter.BulkCacheLoadingException; import org.ehcache.spi.loaderwriter.BulkCacheWritingException; import org.ehcache.spi.loaderwriter.CacheLoaderWriter; @@ -37,9 +35,15 @@ import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collector; +import static java.util.Collections.singletonMap; +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.toMap; import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.doNothing; @@ -68,10 +72,10 @@ public class RobustLoaderWriterResilienceStrategyTest { private final Exception exception = new Exception("failed"); private final BulkCacheLoadingException bulkLoadingException = new BulkCacheLoadingException( - CollectionUtil.map(1, exception), CollectionUtil.map(2, 2L)); + singletonMap(1, exception), singletonMap(2, 2L)); private final BulkCacheWritingException bulkWritingException = new BulkCacheWritingException( - CollectionUtil.map(1, exception), Collections.singleton(2)); + singletonMap(1, exception), Collections.singleton(2)); @After public void noMoreInteractions() { @@ -360,7 +364,8 @@ public void replaceFailure1_writeFails() throws Exception { @Test public void getAllFailure_nothingFound() throws Exception { List keys = Arrays.asList(1, 2); - Map entries = CollectionUtil.map(1, null, 2, null); + Map entries = new HashMap<>(); + keys.forEach(k -> entries.put(k, null)); when(loaderWriter.loadAll(keys)).thenReturn(entries); @@ -376,7 +381,7 @@ public void getAllFailure_nothingFound() throws Exception { @Test public void getAllFailure_allFound() throws Exception { List keys = Arrays.asList(1, 2); - Map entries = CollectionUtil.map(1, 1L, 2, 2L); + Map entries = keys.stream().collect(toMap(identity(), k -> (long) k)); when(loaderWriter.loadAll(keys)).thenReturn(entries); @@ -392,7 +397,8 @@ public void getAllFailure_allFound() throws Exception { @Test public void getAllFailure_partialFound() throws Exception { List keys = Arrays.asList(1, 2); - Map entries = CollectionUtil.map(1, 1L, 2, null); + Map entries = new HashMap<>(); + keys.forEach(k -> entries.put(k, k == 2 ? null : (long) k)); when(loaderWriter.loadAll(keys)).thenReturn(entries); @@ -441,7 +447,7 @@ public void getAllFailure_loadFailsWithBulkException() throws Exception { @Test public void putAllFailure() throws Exception { List> entryList = Arrays.asList(entry(1, 1L), entry(2, 2L)); - Map entryMap = CollectionUtil.map(1, 1L, 2, 2L); + Map entryMap = entryList.stream().collect(toMap(Map.Entry::getKey, Map.Entry::getValue)); doNothing().when(loaderWriter).writeAll(argThat(containsAllMatcher(entryList))); @@ -457,7 +463,7 @@ public void putAllFailure() throws Exception { @Test public void putAllFailure_writeAllFailsWithException() throws Exception { List> entryList = Arrays.asList(entry(1, 1L), entry(2, 2L)); - Map entryMap = CollectionUtil.map(1, 1L, 2, 2L); + Map entryMap = entryList.stream().collect(toMap(Map.Entry::getKey, Map.Entry::getValue)); doThrow(exception).when(loaderWriter).writeAll(argThat(containsAllMatcher(entryList))); @@ -475,7 +481,7 @@ public void putAllFailure_writeAllFailsWithException() throws Exception { @Test public void putAllFailure_writeAllFailsWithBulkException() throws Exception { List> entryList = Arrays.asList(entry(1, 1L), entry(2, 2L)); - Map entryMap = CollectionUtil.map(1, 1L, 2, 2L); + Map entryMap = entryList.stream().collect(toMap(Map.Entry::getKey, Map.Entry::getValue)); doThrow(bulkWritingException).when(loaderWriter).writeAll(argThat(containsAllMatcher(entryList))); diff --git a/core/src/test/java/org/ehcache/core/internal/resilience/RobustResilienceStrategyTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/resilience/RobustResilienceStrategyTest.java similarity index 92% rename from core/src/test/java/org/ehcache/core/internal/resilience/RobustResilienceStrategyTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/resilience/RobustResilienceStrategyTest.java index 06d43fcc87..8c76a68850 100644 --- a/core/src/test/java/org/ehcache/core/internal/resilience/RobustResilienceStrategyTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/resilience/RobustResilienceStrategyTest.java @@ -13,10 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.ehcache.core.internal.resilience; +package org.ehcache.impl.internal.resilience; -import org.ehcache.core.internal.util.CollectionUtil; -import org.ehcache.core.spi.store.Store; import org.ehcache.spi.resilience.RecoveryStore; import org.ehcache.spi.resilience.StoreAccessException; import org.junit.After; @@ -31,6 +29,10 @@ import java.util.Arrays; import static java.util.Arrays.asList; +import static java.util.Arrays.stream; +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.toMap; +import static java.util.stream.Stream.of; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -118,7 +120,7 @@ public void getAllFailure() throws StoreAccessException { @Test public void putAllFailure() throws StoreAccessException { - strategy.putAllFailure(CollectionUtil.map(1, 2L, 2, 2L), accessException); + strategy.putAllFailure(of(1, 2).collect(toMap(identity(), k -> (long) k)), accessException); @SuppressWarnings("unchecked") ArgumentCaptor> captor = ArgumentCaptor.forClass(Iterable.class); verify(store).obliterate(captor.capture()); diff --git a/impl/src/test/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngineConfigurationTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngineConfigurationTest.java similarity index 75% rename from impl/src/test/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngineConfigurationTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngineConfigurationTest.java index 0a4f62a971..b8872af86b 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngineConfigurationTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngineConfigurationTest.java @@ -19,6 +19,9 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNot.not; +import static org.hamcrest.core.IsSame.sameInstance; import static org.junit.Assert.fail; import org.ehcache.config.units.MemoryUnit; @@ -61,4 +64,14 @@ public void testValidArguments() { assertThat(configuration.getUnit(), equalTo(MemoryUnit.B)); } + @Test + public void testDeriveDetachesProperly() { + DefaultSizeOfEngineConfiguration configuration = new DefaultSizeOfEngineConfiguration(42L, MemoryUnit.MB, 123L); + DefaultSizeOfEngineConfiguration derived = configuration.build(configuration.derive()); + + assertThat(derived, is(not(sameInstance(configuration)))); + assertThat(derived.getMaxObjectSize(), is(configuration.getMaxObjectSize())); + assertThat(derived.getUnit(), is(configuration.getUnit())); + assertThat(derived.getMaxObjectGraphSize(), is(configuration.getMaxObjectGraphSize())); + } } diff --git a/impl/src/test/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngineProviderConfigurationTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngineProviderConfigurationTest.java similarity index 75% rename from impl/src/test/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngineProviderConfigurationTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngineProviderConfigurationTest.java index ef6807946b..4d5a3b4cfb 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngineProviderConfigurationTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngineProviderConfigurationTest.java @@ -22,12 +22,15 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNot.not; +import static org.hamcrest.core.IsSame.sameInstance; import static org.junit.Assert.fail; + /** * @author Abhilash * */ - public class DefaultSizeOfEngineProviderConfigurationTest { @Test @@ -59,4 +62,16 @@ public void testValidArguments() { assertThat(configuration.getMaxObjectSize(), equalTo(10l)); assertThat(configuration.getUnit(), equalTo(MemoryUnit.B)); } + + @Test + public void testDeriveDetachesCorrectly() { + DefaultSizeOfEngineProviderConfiguration configuration = new DefaultSizeOfEngineProviderConfiguration(42L, MemoryUnit.B, 100L); + + DefaultSizeOfEngineProviderConfiguration derived = configuration.build(configuration.derive()); + + assertThat(derived, is(not(sameInstance(configuration)))); + assertThat(derived.getMaxObjectGraphSize(), is(configuration.getMaxObjectGraphSize())); + assertThat(derived.getMaxObjectSize(), is(configuration.getMaxObjectSize())); + assertThat(derived.getUnit(), is(configuration.getUnit())); + } } diff --git a/impl/src/test/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngineProviderFactoryTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngineProviderFactoryTest.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngineProviderFactoryTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngineProviderFactoryTest.java diff --git a/impl/src/test/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngineTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngineTest.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngineTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/sizeof/DefaultSizeOfEngineTest.java diff --git a/impl/src/test/java/org/ehcache/impl/internal/spi/TestServiceProvider.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/spi/TestServiceProvider.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/internal/spi/TestServiceProvider.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/spi/TestServiceProvider.java diff --git a/impl/src/test/java/org/ehcache/impl/internal/spi/copy/DefaultCopyProviderTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/spi/copy/DefaultCopyProviderTest.java similarity index 98% rename from impl/src/test/java/org/ehcache/impl/internal/spi/copy/DefaultCopyProviderTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/spi/copy/DefaultCopyProviderTest.java index a7e5dfc595..4784034d91 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/spi/copy/DefaultCopyProviderTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/spi/copy/DefaultCopyProviderTest.java @@ -27,10 +27,10 @@ import java.io.Closeable; import java.io.IOException; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.sameInstance; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; /** diff --git a/impl/src/test/java/org/ehcache/impl/internal/spi/event/DefaultCacheEventListenerProviderTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/spi/event/DefaultCacheEventListenerProviderTest.java similarity index 88% rename from impl/src/test/java/org/ehcache/impl/internal/spi/event/DefaultCacheEventListenerProviderTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/spi/event/DefaultCacheEventListenerProviderTest.java index 2a15bcc7a4..c028c8bccb 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/spi/event/DefaultCacheEventListenerProviderTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/spi/event/DefaultCacheEventListenerProviderTest.java @@ -36,8 +36,8 @@ import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.IsInstanceOf.instanceOf; -import static org.junit.Assert.assertThat; /** * @author rism @@ -55,7 +55,7 @@ public void testCacheConfigUsage() { final CacheManager manager = CacheManagerBuilder.newCacheManagerBuilder() .withCache("foo", CacheConfigurationBuilder.newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) - .add(listenerBuilder) + .withService(listenerBuilder) .build()).build(true); final Collection bar = manager.getCache("foo", Object.class, Object.class).getRuntimeConfiguration().getServiceConfigurations(); assertThat(bar.iterator().next().getClass().toString(), is(ListenerObject.object.toString())); @@ -64,16 +64,16 @@ public void testCacheConfigUsage() { @Test public void testAddingCacheEventListenerConfigurationAtCacheLevel() { CacheManagerBuilder cacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder(); - CacheEventListenerConfiguration cacheEventListenerConfiguration = CacheEventListenerConfigurationBuilder + CacheEventListenerConfiguration cacheEventListenerConfiguration = CacheEventListenerConfigurationBuilder .newEventListenerConfiguration(ListenerObject.class, EventType.CREATED).unordered().asynchronous().build(); CacheManager cacheManager = cacheManagerBuilder.build(true); final Cache cache = cacheManager.createCache("cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, heap(100)) - .add(cacheEventListenerConfiguration) + .withService(cacheEventListenerConfiguration) .build()); - Collection> serviceConfiguration = cache.getRuntimeConfiguration() + Collection> serviceConfiguration = cache.getRuntimeConfiguration() .getServiceConfigurations(); - assertThat(serviceConfiguration, IsCollectionContaining.>hasItem(instanceOf(DefaultCacheEventListenerConfiguration.class))); + assertThat(serviceConfiguration, IsCollectionContaining.>hasItem(instanceOf(DefaultCacheEventListenerConfiguration.class))); cacheManager.close(); } diff --git a/impl/src/test/java/org/ehcache/impl/internal/spi/loaderwriter/DefaultCacheLoaderWriterProviderTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/spi/loaderwriter/DefaultCacheLoaderWriterProviderTest.java similarity index 93% rename from impl/src/test/java/org/ehcache/impl/internal/spi/loaderwriter/DefaultCacheLoaderWriterProviderTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/spi/loaderwriter/DefaultCacheLoaderWriterProviderTest.java index c788884bf7..b1054404d9 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/spi/loaderwriter/DefaultCacheLoaderWriterProviderTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/spi/loaderwriter/DefaultCacheLoaderWriterProviderTest.java @@ -38,8 +38,8 @@ import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.IsInstanceOf.instanceOf; -import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; public class DefaultCacheLoaderWriterProviderTest { @@ -49,7 +49,7 @@ public void testCacheConfigUsage() { final CacheManager manager = CacheManagerBuilder.newCacheManagerBuilder() .withCache("foo", CacheConfigurationBuilder.newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) - .add(new DefaultCacheLoaderWriterConfiguration(MyLoader.class)) + .withService(new DefaultCacheLoaderWriterConfiguration(MyLoader.class)) .build()).build(true); final Object foo = manager.getCache("foo", Object.class, Object.class).get(new Object()); assertThat(foo, is(MyLoader.object)); @@ -74,7 +74,7 @@ public void testCacheManagerConfigUsage() { @Test public void testCacheConfigOverridesCacheManagerConfig() { final CacheConfiguration cacheConfiguration = CacheConfigurationBuilder.newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) - .add(new DefaultCacheLoaderWriterConfiguration(MyOtherLoader.class)) + .withService(new DefaultCacheLoaderWriterConfiguration(MyOtherLoader.class)) .build(); final Map> caches = new HashMap<>(); @@ -95,11 +95,11 @@ public void testAddingCacheLoaderWriterConfigurationAtCacheLevel() { CacheManager cacheManager = cacheManagerBuilder.build(true); final Cache cache = cacheManager.createCache("cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, heap(100)) - .add(new DefaultCacheLoaderWriterConfiguration(klazz)) + .withService(new DefaultCacheLoaderWriterConfiguration(klazz)) .build()); - Collection> serviceConfiguration = cache.getRuntimeConfiguration() + Collection> serviceConfiguration = cache.getRuntimeConfiguration() .getServiceConfigurations(); - assertThat(serviceConfiguration, IsCollectionContaining.>hasItem(instanceOf(DefaultCacheLoaderWriterConfiguration.class))); + assertThat(serviceConfiguration, IsCollectionContaining.>hasItem(instanceOf(DefaultCacheLoaderWriterConfiguration.class))); cacheManager.close(); } diff --git a/impl/src/test/java/org/ehcache/impl/internal/spi/resilience/DefaultResilienceStrategyProviderFactoryTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/spi/resilience/DefaultResilienceStrategyProviderFactoryTest.java similarity index 96% rename from impl/src/test/java/org/ehcache/impl/internal/spi/resilience/DefaultResilienceStrategyProviderFactoryTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/spi/resilience/DefaultResilienceStrategyProviderFactoryTest.java index fc873a6689..cd6c9d118d 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/spi/resilience/DefaultResilienceStrategyProviderFactoryTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/spi/resilience/DefaultResilienceStrategyProviderFactoryTest.java @@ -24,9 +24,10 @@ import org.junit.Test; import static org.ehcache.test.MockitoUtil.mock; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.IsNull.notNullValue; import static org.hamcrest.core.IsSame.sameInstance; -import static org.junit.Assert.*; +import static org.junit.Assert.fail; public class DefaultResilienceStrategyProviderFactoryTest { diff --git a/impl/src/test/java/org/ehcache/impl/internal/spi/resilience/DefaultResilienceStrategyProviderTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/spi/resilience/DefaultResilienceStrategyProviderTest.java similarity index 98% rename from impl/src/test/java/org/ehcache/impl/internal/spi/resilience/DefaultResilienceStrategyProviderTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/spi/resilience/DefaultResilienceStrategyProviderTest.java index ecd3a7eef7..ec62ea981a 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/spi/resilience/DefaultResilienceStrategyProviderTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/spi/resilience/DefaultResilienceStrategyProviderTest.java @@ -16,9 +16,9 @@ package org.ehcache.impl.internal.spi.resilience; import org.ehcache.config.CacheConfiguration; -import org.ehcache.core.internal.resilience.RobustResilienceStrategy; import org.ehcache.impl.config.resilience.DefaultResilienceStrategyConfiguration; import org.ehcache.impl.config.resilience.DefaultResilienceStrategyProviderConfiguration; +import org.ehcache.impl.internal.resilience.RobustResilienceStrategy; import org.ehcache.spi.loaderwriter.CacheLoaderWriter; import org.ehcache.spi.resilience.RecoveryStore; import org.ehcache.spi.resilience.ResilienceStrategy; @@ -27,10 +27,10 @@ import java.util.Collections; import static org.ehcache.test.MockitoUtil.mock; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsInstanceOf.instanceOf; import static org.hamcrest.core.IsSame.sameInstance; -import static org.junit.Assert.assertThat; import static org.mockito.Mockito.when; public class DefaultResilienceStrategyProviderTest { diff --git a/impl/src/test/java/org/ehcache/impl/internal/spi/serialization/DefaultSerializationProviderTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/spi/serialization/DefaultSerializationProviderTest.java similarity index 92% rename from impl/src/test/java/org/ehcache/impl/internal/spi/serialization/DefaultSerializationProviderTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/spi/serialization/DefaultSerializationProviderTest.java index 763cd6dd42..4c07aca184 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/spi/serialization/DefaultSerializationProviderTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/spi/serialization/DefaultSerializationProviderTest.java @@ -39,7 +39,6 @@ import org.hamcrest.Matchers; import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import java.io.Closeable; @@ -53,9 +52,12 @@ import static org.ehcache.impl.internal.spi.TestServiceProvider.providerContaining; import static org.ehcache.test.MockitoUtil.mock; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.endsWith; +import static org.hamcrest.Matchers.hasProperty; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; @@ -70,9 +72,6 @@ public class DefaultSerializationProviderTest { @Rule public TemporaryFolder tempFolder = new TemporaryFolder(); - @Rule - public ExpectedException expectedException = ExpectedException.none(); - @Test public void testCreateSerializerNoConfig() throws Exception { DefaultSerializationProviderConfiguration dspfConfig = new DefaultSerializationProviderConfiguration(); @@ -308,54 +307,50 @@ public void testDefaultByteArraySerializer() throws Exception { @Test public void testCreateTransientSerializerWithoutConstructor() throws Exception { - expectedException.expect(RuntimeException.class); - expectedException.expectMessage("does not have a constructor that takes in a ClassLoader."); DefaultSerializationProvider provider = new DefaultSerializationProvider(null); provider.start(providerContaining()); @SuppressWarnings("unchecked") Class> serializerClass = (Class) BaseSerializer.class; DefaultSerializerConfiguration configuration = new DefaultSerializerConfiguration<>(serializerClass, DefaultSerializerConfiguration.Type.VALUE); - provider.createValueSerializer(Object.class, ClassLoader.getSystemClassLoader(), configuration); + RuntimeException thrown = assertThrows(RuntimeException.class, () -> provider.createValueSerializer(Object.class, getSystemClassLoader(), configuration)); + assertThat(thrown, hasProperty("message", endsWith("does not have a constructor that takes in a ClassLoader."))); } @Test public void testCreatePersistentSerializerWithoutConstructor() throws Exception { - expectedException.expect(RuntimeException.class); - expectedException.expectMessage("does not have a constructor that takes in a ClassLoader."); DefaultSerializationProvider provider = new DefaultSerializationProvider(null); provider.start(providerContaining()); @SuppressWarnings("unchecked") Class> serializerClass = (Class) BaseSerializer.class; DefaultSerializerConfiguration configuration = new DefaultSerializerConfiguration<>(serializerClass, DefaultSerializerConfiguration.Type.VALUE); - provider.createValueSerializer(Object.class, ClassLoader.getSystemClassLoader(), configuration, getPersistenceSpaceIdentifierMock()); + RuntimeException thrown = assertThrows(RuntimeException.class, () -> provider.createValueSerializer(Object.class, getSystemClassLoader(), configuration, getPersistenceSpaceIdentifierMock())); + assertThat(thrown, hasProperty("message", endsWith("does not have a constructor that takes in a ClassLoader."))); } @Test public void testCreateTransientStatefulSerializerWithoutConstructor() throws Exception { - expectedException.expect(RuntimeException.class); - expectedException.expectMessage("does not have a constructor that takes in a ClassLoader."); DefaultSerializationProvider provider = new DefaultSerializationProvider(null); provider.start(providerContaining()); @SuppressWarnings("unchecked") Class> serializerClass = (Class) StatefulBaseSerializer.class; DefaultSerializerConfiguration configuration = new DefaultSerializerConfiguration<>(serializerClass, DefaultSerializerConfiguration.Type.VALUE); - provider.createValueSerializer(Object.class, ClassLoader.getSystemClassLoader(), configuration); + RuntimeException thrown = assertThrows(RuntimeException.class, () -> provider.createValueSerializer(Object.class, getSystemClassLoader(), configuration)); + assertThat(thrown, hasProperty("message", endsWith("does not have a constructor that takes in a ClassLoader."))); } @Test public void testCreatePersistentStatefulSerializerWithoutConstructor() throws Exception { - expectedException.expect(RuntimeException.class); - expectedException.expectMessage("does not have a constructor that takes in a ClassLoader."); DefaultSerializationProvider provider = new DefaultSerializationProvider(null); provider.start(providerContaining()); @SuppressWarnings("unchecked") Class> serializerClass = (Class) StatefulBaseSerializer.class; DefaultSerializerConfiguration configuration = new DefaultSerializerConfiguration<>(serializerClass, DefaultSerializerConfiguration.Type.VALUE); - provider.createValueSerializer(Object.class, ClassLoader.getSystemClassLoader(), configuration, getPersistenceSpaceIdentifierMock()); + RuntimeException thrown = assertThrows(RuntimeException.class, () -> provider.createValueSerializer(Object.class, getSystemClassLoader(), configuration, getPersistenceSpaceIdentifierMock())); + assertThat(thrown, hasProperty("message", endsWith("does not have a constructor that takes in a ClassLoader."))); } @Test @@ -420,15 +415,14 @@ public void testPersistentMinimalStatefulSerializer() throws Exception { @Test public void testTransientLegacySerializer() throws Exception { - expectedException.expect(RuntimeException.class); - expectedException.expectMessage("does not have a constructor that takes in a ClassLoader."); DefaultSerializationProvider provider = new DefaultSerializationProvider(null); provider.start(providerContaining()); @SuppressWarnings("unchecked") Class> serializerClass = (Class) LegacySerializer.class; DefaultSerializerConfiguration configuration = new DefaultSerializerConfiguration<>(serializerClass, DefaultSerializerConfiguration.Type.VALUE); - provider.createValueSerializer(Object.class, ClassLoader.getSystemClassLoader(), configuration); + RuntimeException thrown = assertThrows(RuntimeException.class, () -> provider.createValueSerializer(Object.class, getSystemClassLoader(), configuration)); + assertThat(thrown, hasProperty("message", endsWith("does not have a constructor that takes in a ClassLoader."))); } @Test @@ -439,12 +433,8 @@ public void testPersistentLegacySerializer() throws Exception { @SuppressWarnings("unchecked") Class> serializerClass = (Class) LegacySerializer.class; DefaultSerializerConfiguration configuration = new DefaultSerializerConfiguration<>(serializerClass, DefaultSerializerConfiguration.Type.VALUE); - expectedException.expect(RuntimeException.class); - expectedException.expectMessage("does not have a constructor that takes in a ClassLoader."); - Serializer valueSerializer = - provider.createValueSerializer(Object.class, ClassLoader.getSystemClassLoader(), configuration, getPersistenceSpaceIdentifierMock()); - assertThat(valueSerializer, instanceOf(LegacySerializer.class)); - assertThat(LegacySerializer.legacyConstructorInvoked, is(true)); + RuntimeException thrown = assertThrows(RuntimeException.class, () -> provider.createValueSerializer(Object.class, getSystemClassLoader(), configuration, getPersistenceSpaceIdentifierMock())); + assertThat(thrown, hasProperty("message", endsWith("does not have a constructor that takes in a ClassLoader."))); } @Test @@ -482,28 +472,26 @@ public void testPersistentLegacyComboSerializer() throws Exception { @Test public void testCreateTransientStatefulLegacySerializer() throws Exception { - expectedException.expect(RuntimeException.class); - expectedException.expectMessage("does not have a constructor that takes in a ClassLoader."); DefaultSerializationProvider provider = new DefaultSerializationProvider(null); provider.start(providerContaining()); @SuppressWarnings("unchecked") Class> serializerClass = (Class) StatefulLegacySerializer.class; DefaultSerializerConfiguration configuration = new DefaultSerializerConfiguration<>(serializerClass, DefaultSerializerConfiguration.Type.VALUE); - provider.createValueSerializer(Object.class, ClassLoader.getSystemClassLoader(), configuration); + RuntimeException thrown = assertThrows(RuntimeException.class, () -> provider.createValueSerializer(Object.class, getSystemClassLoader(), configuration)); + assertThat(thrown, hasProperty("message", endsWith("does not have a constructor that takes in a ClassLoader."))); } @Test public void testCreatePersistentStatefulLegacySerializer() throws Exception { - expectedException.expect(RuntimeException.class); - expectedException.expectMessage("does not have a constructor that takes in a ClassLoader."); DefaultSerializationProvider provider = new DefaultSerializationProvider(null); provider.start(providerContaining()); @SuppressWarnings("unchecked") Class> serializerClass = (Class) StatefulLegacySerializer.class; DefaultSerializerConfiguration configuration = new DefaultSerializerConfiguration<>(serializerClass, DefaultSerializerConfiguration.Type.VALUE); - provider.createValueSerializer(Object.class, ClassLoader.getSystemClassLoader(), configuration, getPersistenceSpaceIdentifierMock()); + RuntimeException thrown = assertThrows(RuntimeException.class, () -> provider.createValueSerializer(Object.class, getSystemClassLoader(), configuration, getPersistenceSpaceIdentifierMock())); + assertThat(thrown, hasProperty("message", endsWith("does not have a constructor that takes in a ClassLoader."))); } @Test diff --git a/impl/src/test/java/org/ehcache/impl/internal/statistics/DefaultCacheStatisticsTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/statistics/DefaultCacheStatisticsTest.java similarity index 95% rename from impl/src/test/java/org/ehcache/impl/internal/statistics/DefaultCacheStatisticsTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/statistics/DefaultCacheStatisticsTest.java index 97565385f8..b804590eb4 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/statistics/DefaultCacheStatisticsTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/statistics/DefaultCacheStatisticsTest.java @@ -26,6 +26,8 @@ import org.ehcache.core.InternalCache; import org.ehcache.core.config.store.StoreStatisticsConfiguration; import org.ehcache.core.statistics.CacheOperationOutcomes; +import org.ehcache.core.statistics.ChainedOperationObserver; +import org.ehcache.core.internal.statistics.DefaultCacheStatistics; import org.ehcache.event.CacheEvent; import org.ehcache.event.CacheEventListener; import org.ehcache.event.EventType; @@ -36,7 +38,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import org.terracotta.statistics.observer.ChainedOperationObserver; import java.time.Duration; import java.util.ArrayList; @@ -113,8 +114,8 @@ public void before() { CacheConfiguration cacheConfiguration = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, heap(10)) .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMillis(TIME_TO_EXPIRATION))) - .add(cacheEventListenerConfiguration) - .add(new StoreStatisticsConfiguration(enableStoreStatistics)) + .withService(cacheEventListenerConfiguration) + .withService(new StoreStatisticsConfiguration(enableStoreStatistics)) .build(); cacheManager = CacheManagerBuilder.newCacheManagerBuilder() @@ -212,7 +213,7 @@ public void getExpirations() throws Exception { @Test public void registerDerivedStatistics() { AtomicBoolean endCalled = new AtomicBoolean(); - ChainedOperationObserver derivedStatistic = new ChainedOperationObserver() { + ChainedOperationObserver derivedStatistic = new org.ehcache.core.statistics.ChainedOperationObserver() { @Override public void begin(long time) { diff --git a/impl/src/test/java/org/ehcache/impl/internal/statistics/DefaultStatisticsServiceTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/statistics/DefaultStatisticsServiceTest.java similarity index 85% rename from impl/src/test/java/org/ehcache/impl/internal/statistics/DefaultStatisticsServiceTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/statistics/DefaultStatisticsServiceTest.java index 353d465fe4..7c516027cf 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/statistics/DefaultStatisticsServiceTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/statistics/DefaultStatisticsServiceTest.java @@ -22,22 +22,19 @@ import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.config.builders.CacheManagerBuilder; import org.ehcache.config.builders.ResourcePoolsBuilder; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; import org.ehcache.spi.test.After; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import static org.assertj.core.api.Assertions.assertThat; import static org.ehcache.config.units.MemoryUnit.MB; +import static org.junit.Assert.assertThrows; public class DefaultStatisticsServiceTest { private static final String CACHE = "myCache"; - @Rule - public ExpectedException expectedException = ExpectedException.none(); - private final DefaultStatisticsService service = new DefaultStatisticsService(); private CacheManager cacheManager; @@ -65,20 +62,13 @@ public void after() { public void startStopStart() throws Exception { cacheManager.init(); - assertThat(service.isStarted()).isTrue(); - Cache cache = cacheManager.getCache(CACHE, Long.class, String.class); cache.get(2L); assertThat(service.getCacheStatistics(CACHE).getCacheMisses()).isEqualTo(1); cacheManager.close(); - - assertThat(service.isStarted()).isFalse(); - cacheManager.init(); - assertThat(service.isStarted()).isTrue(); - // We expect the stats to be reinitialized after a stop start assertThat(service.getCacheStatistics(CACHE).getCacheMisses()).isEqualTo(0); cache = cacheManager.getCache(CACHE, Long.class, String.class); @@ -88,8 +78,7 @@ public void startStopStart() throws Exception { @Test public void startInMaintenance() throws Exception { - expectedException.expect(IllegalStateException.class); - service.stateTransition(Status.UNINITIALIZED, Status.MAINTENANCE); + assertThrows(IllegalStateException.class, () -> service.stateTransition(Status.UNINITIALIZED, Status.MAINTENANCE)); } } diff --git a/impl/src/test/java/org/ehcache/impl/internal/statistics/DefaultTierStatisticsDisabledTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/statistics/DefaultTierStatisticsDisabledTest.java similarity index 98% rename from impl/src/test/java/org/ehcache/impl/internal/statistics/DefaultTierStatisticsDisabledTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/statistics/DefaultTierStatisticsDisabledTest.java index 8bbc683176..a29ddd23b5 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/statistics/DefaultTierStatisticsDisabledTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/statistics/DefaultTierStatisticsDisabledTest.java @@ -23,6 +23,7 @@ import org.ehcache.config.builders.CacheManagerBuilder; import org.ehcache.config.builders.ExpiryPolicyBuilder; import org.ehcache.config.units.EntryUnit; +import org.ehcache.core.internal.statistics.DefaultTierStatistics; import org.ehcache.impl.internal.TimeSourceConfiguration; import org.ehcache.internal.TestTimeSource; import org.junit.After; diff --git a/impl/src/test/java/org/ehcache/impl/internal/statistics/DefaultTierStatisticsTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/statistics/DefaultTierStatisticsTest.java similarity index 96% rename from impl/src/test/java/org/ehcache/impl/internal/statistics/DefaultTierStatisticsTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/statistics/DefaultTierStatisticsTest.java index aa73b41f4d..e4bb37aed7 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/statistics/DefaultTierStatisticsTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/statistics/DefaultTierStatisticsTest.java @@ -25,6 +25,7 @@ import org.ehcache.config.builders.ExpiryPolicyBuilder; import org.ehcache.config.units.EntryUnit; import org.ehcache.core.config.store.StoreStatisticsConfiguration; +import org.ehcache.core.internal.statistics.DefaultTierStatistics; import org.ehcache.impl.internal.TimeSourceConfiguration; import org.ehcache.internal.TestTimeSource; import org.junit.After; @@ -51,7 +52,7 @@ public void before() { CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES)) .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMillis(TIME_TO_EXPIRATION))) - .add(new StoreStatisticsConfiguration(true)) // explicitly enable statistics + .withService(new StoreStatisticsConfiguration(true)) // explicitly enable statistics .build(); cacheManager = CacheManagerBuilder.newCacheManagerBuilder() diff --git a/impl/src/test/java/org/ehcache/impl/internal/statistics/StatsUtilsTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/statistics/StatsUtilsTest.java similarity index 88% rename from impl/src/test/java/org/ehcache/impl/internal/statistics/StatsUtilsTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/statistics/StatsUtilsTest.java index 84496ef8ff..6a356d89a6 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/statistics/StatsUtilsTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/statistics/StatsUtilsTest.java @@ -27,9 +27,7 @@ import org.ehcache.core.statistics.TierOperationOutcomes; import org.junit.After; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import org.terracotta.context.ContextManager; import org.terracotta.context.TreeNode; import org.terracotta.context.query.Matchers; @@ -45,7 +43,14 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; -import static org.ehcache.impl.internal.statistics.StatsUtils.*; +import static org.ehcache.core.internal.statistics.StatsUtils.findLowestTier; +import static org.ehcache.core.internal.statistics.StatsUtils.findOperationStatisticOnChildren; +import static org.ehcache.core.internal.statistics.StatsUtils.findStatisticOnDescendants; +import static org.ehcache.core.internal.statistics.StatsUtils.findTiers; +import static org.ehcache.core.internal.statistics.StatsUtils.hasOperationStat; +import static org.ehcache.core.internal.statistics.StatsUtils.hasProperty; +import static org.ehcache.core.internal.statistics.StatsUtils.hasTag; +import static org.junit.Assert.assertThrows; import static org.terracotta.context.query.Matchers.attributes; import static org.terracotta.context.query.Matchers.context; import static org.terracotta.context.query.Matchers.hasAttribute; @@ -55,9 +60,6 @@ public class StatsUtilsTest { - @Rule - public ExpectedException expectedException = ExpectedException.none(); - CacheManager cacheManager; Cache cache; @@ -65,7 +67,7 @@ public class StatsUtilsTest { public void before() { CacheConfiguration cacheConfiguration = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, heap(10)) - .add(new StoreStatisticsConfiguration(true)) // explicitly enable statistics + .withService(new StoreStatisticsConfiguration(true)) // explicitly enable statistics .build(); cacheManager = CacheManagerBuilder.newCacheManagerBuilder() @@ -176,8 +178,7 @@ public void testFindCacheStatistic() { @Test public void testFindCacheStatistic_notExisting() { - expectedException.expect(RuntimeException.class); - findOperationStatisticOnChildren(cache, CacheOperationOutcomes.GetOutcome.class, "xxx"); + assertThrows(RuntimeException.class, () -> findOperationStatisticOnChildren(cache, CacheOperationOutcomes.GetOutcome.class, "xxx")); } @Test @@ -206,8 +207,7 @@ public void testFindLowerTier_three() { @Test public void testFindLowerTier_none() { - expectedException.expect(RuntimeException.class); - findLowestTier(new String[0]); + assertThrows(RuntimeException.class, () -> findLowestTier(new String[0])); } @Test diff --git a/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/basic/DelegatingValueHolder.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/basic/DelegatingValueHolder.java new file mode 100644 index 0000000000..ff3af1fc49 --- /dev/null +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/basic/DelegatingValueHolder.java @@ -0,0 +1,66 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.impl.internal.store.basic; + +import org.ehcache.core.spi.store.Store; + +import java.util.concurrent.TimeUnit; + +import javax.annotation.Nonnull; + +/** + * ValueHolder delegating everything to another ValueHolder. + */ +public class DelegatingValueHolder implements Store.ValueHolder { + + private final Store.ValueHolder valueHolder; + + public DelegatingValueHolder(Store.ValueHolder valueHolder) { + this.valueHolder = valueHolder; + } + + @Override + @Nonnull + public T get() { + return valueHolder.get(); + } + + @Override + public long creationTime() { + return valueHolder.creationTime(); + } + + @Override + public long expirationTime() { + return valueHolder.expirationTime(); + } + + @Override + public boolean isExpired(long expirationTime) { + return valueHolder.isExpired(expirationTime); + } + + @Override + public long lastAccessTime() { + return valueHolder.lastAccessTime(); + } + + @Override + public long getId() { + return valueHolder.getId(); + } +} diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/basic/EmptyValueHolder.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/basic/SimpleValueHolder.java similarity index 61% rename from impl/src/main/java/org/ehcache/impl/internal/store/basic/EmptyValueHolder.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/basic/SimpleValueHolder.java index b6d8b2da7a..78442bc9ee 100644 --- a/impl/src/main/java/org/ehcache/impl/internal/store/basic/EmptyValueHolder.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/basic/SimpleValueHolder.java @@ -13,48 +13,49 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.ehcache.impl.internal.store.basic; import org.ehcache.core.spi.store.Store; import java.util.concurrent.TimeUnit; +import javax.annotation.Nonnull; + /** - * A value holder that always contains null - * - * @author Henri Tremblay + * A really basic value holder that just holds a value. */ -public class EmptyValueHolder implements Store.ValueHolder { +public class SimpleValueHolder implements Store.ValueHolder { - private static final Store.ValueHolder EMPTY = new EmptyValueHolder<>(); + private final T value; - @SuppressWarnings("unchecked") - public static Store.ValueHolder empty() { - return (Store.ValueHolder) EMPTY; + public SimpleValueHolder(T v) { + this.value = v; } @Override - public V get() { - return null; + @Nonnull + public T get() { + return value; } @Override - public long creationTime(TimeUnit unit) { + public long creationTime() { return 0; } @Override - public long expirationTime(TimeUnit unit) { + public long expirationTime() { return 0; } @Override - public boolean isExpired(long expirationTime, TimeUnit unit) { + public boolean isExpired(long expirationTime) { return false; } @Override - public long lastAccessTime(TimeUnit unit) { + public long lastAccessTime() { return 0; } diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/disk/EhcachePersistentConcurrentOffHeapClockCacheTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/disk/EhcachePersistentConcurrentOffHeapClockCacheTest.java similarity index 99% rename from impl/src/test/java/org/ehcache/impl/internal/store/disk/EhcachePersistentConcurrentOffHeapClockCacheTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/disk/EhcachePersistentConcurrentOffHeapClockCacheTest.java index 34babc8d49..132ae1aa9c 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/disk/EhcachePersistentConcurrentOffHeapClockCacheTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/disk/EhcachePersistentConcurrentOffHeapClockCacheTest.java @@ -16,7 +16,6 @@ package org.ehcache.impl.internal.store.disk; -import org.ehcache.config.Eviction; import org.ehcache.config.EvictionAdvisor; import org.ehcache.impl.internal.store.disk.factories.EhcachePersistentSegmentFactory; import org.ehcache.impl.internal.store.offheap.AbstractEhcacheOffHeapBackingMapTest; diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStoreProviderTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStoreProviderTest.java similarity index 93% rename from impl/src/test/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStoreProviderTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStoreProviderTest.java index 106e4f705a..5d3b36b429 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStoreProviderTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStoreProviderTest.java @@ -25,9 +25,11 @@ import org.ehcache.config.SizedResourcePool; import org.ehcache.config.builders.ExpiryPolicyBuilder; import org.ehcache.config.units.MemoryUnit; -import org.ehcache.core.internal.service.ServiceLocator; +import org.ehcache.core.spi.ServiceLocator; +import org.ehcache.core.spi.service.CacheManagerProviderService; import org.ehcache.core.spi.service.DiskResourceService; import org.ehcache.core.spi.store.Store; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; import org.ehcache.expiry.ExpiryPolicy; import org.ehcache.impl.internal.DefaultTimeSourceService; import org.ehcache.impl.serialization.LongSerializer; @@ -46,10 +48,10 @@ import java.util.Set; import static java.util.Collections.singleton; -import static org.ehcache.core.internal.service.ServiceLocator.dependencySet; +import static org.ehcache.core.spi.ServiceLocator.dependencySet; import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.empty; -import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; import static org.terracotta.context.query.Matchers.attributes; import static org.terracotta.context.query.Matchers.context; @@ -65,7 +67,8 @@ public class OffHeapDiskStoreProviderTest { public void testStatisticsAssociations() throws Exception { OffHeapDiskStore.Provider provider = new OffHeapDiskStore.Provider(); - ServiceLocator serviceLocator = dependencySet().with(mock(SerializationProvider.class)) + ServiceLocator serviceLocator = dependencySet().with(mock(SerializationProvider.class)).with(new DefaultStatisticsService()) + .with(mock(CacheManagerProviderService.class)) .with(new DefaultTimeSourceService(null)).with(mock(DiskResourceService.class)).build(); provider.start(serviceLocator); diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStoreSPITest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStoreSPITest.java similarity index 94% rename from impl/src/test/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStoreSPITest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStoreSPITest.java index 08bd778335..755d7d2c51 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStoreSPITest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStoreSPITest.java @@ -20,10 +20,11 @@ import org.ehcache.config.EvictionAdvisor; import org.ehcache.config.ResourcePools; import org.ehcache.config.builders.ExpiryPolicyBuilder; -import org.ehcache.core.internal.store.StoreConfigurationImpl; import org.ehcache.config.SizedResourcePool; import org.ehcache.config.units.MemoryUnit; import org.ehcache.CachePersistenceException; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; +import org.ehcache.core.store.StoreConfigurationImpl; import org.ehcache.expiry.ExpiryPolicy; import org.ehcache.impl.internal.concurrent.ConcurrentHashMap; import org.ehcache.impl.internal.events.TestStoreEventDispatcher; @@ -37,7 +38,7 @@ import org.ehcache.internal.store.StoreFactory; import org.ehcache.internal.tier.AuthoritativeTierFactory; import org.ehcache.internal.tier.AuthoritativeTierSPITest; -import org.ehcache.core.internal.service.ServiceLocator; +import org.ehcache.core.spi.ServiceLocator; import org.ehcache.core.spi.store.Store; import org.ehcache.core.spi.store.tiering.AuthoritativeTier; import org.ehcache.spi.serialization.Serializer; @@ -47,6 +48,7 @@ import org.junit.Before; import org.junit.Rule; import org.junit.rules.TemporaryFolder; +import org.terracotta.statistics.StatisticsManager; import java.io.IOException; import java.util.Arrays; @@ -55,7 +57,7 @@ import static org.ehcache.config.ResourceType.Core.DISK; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; -import static org.ehcache.core.internal.service.ServiceLocator.dependencySet; +import static org.ehcache.core.spi.ServiceLocator.dependencySet; import static org.ehcache.impl.config.store.disk.OffHeapDiskStoreConfiguration.DEFAULT_DISK_SEGMENTS; import static org.ehcache.impl.config.store.disk.OffHeapDiskStoreConfiguration.DEFAULT_WRITER_CONCURRENCY; import static org.ehcache.test.MockitoUtil.mock; @@ -122,7 +124,7 @@ private AuthoritativeTier newStore(Long capacity, EvictionAdviso new OnDemandExecutionService(), null, DEFAULT_WRITER_CONCURRENCY, DEFAULT_DISK_SEGMENTS, config, timeSource, new TestStoreEventDispatcher<>(), - unit.toBytes(diskPool.getSize())); + unit.toBytes(diskPool.getSize()), new DefaultStatisticsService()); OffHeapDiskStore.Provider.init(store); createdStores.put(store, spaceName); return store; @@ -154,13 +156,13 @@ public Class getValueType() { } @Override - public ServiceConfiguration[] getServiceConfigurations() { + public ServiceConfiguration[] getServiceConfigurations() { try { CacheConfiguration cacheConfiguration = mock(CacheConfiguration.class); when(cacheConfiguration.getResourcePools()).thenReturn(newResourcePoolsBuilder().disk(1, MemoryUnit.MB, false).build()); String spaceName = "OffheapDiskStore-" + index.getAndIncrement(); PersistenceSpaceIdentifier space = diskResourceService.getPersistenceSpaceIdentifier(spaceName, cacheConfiguration); - return new ServiceConfiguration[] {space}; + return new ServiceConfiguration[] {space}; } catch (CachePersistenceException e) { throw new RuntimeException(e); } @@ -194,6 +196,7 @@ public void close(final Store store) { String spaceName = createdStores.get(store); try { OffHeapDiskStore.Provider.close((OffHeapDiskStore)store); + StatisticsManager.nodeFor(store).clean(); } catch (IOException ex) { throw new RuntimeException(ex); } diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStoreTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStoreTest.java similarity index 88% rename from impl/src/test/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStoreTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStoreTest.java index bb05437557..5cc5fd54b0 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStoreTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStoreTest.java @@ -23,14 +23,18 @@ import org.ehcache.config.ResourcePool; import org.ehcache.config.ResourceType; import org.ehcache.config.builders.CacheConfigurationBuilder; -import org.ehcache.core.internal.store.StoreConfigurationImpl; import org.ehcache.config.builders.ResourcePoolsBuilder; import org.ehcache.config.units.MemoryUnit; +import org.ehcache.core.spi.service.CacheManagerProviderService; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; +import org.ehcache.core.store.StoreConfigurationImpl; import org.ehcache.spi.resilience.StoreAccessException; import org.ehcache.core.statistics.LowerCachingTierOperationsOutcome; import org.ehcache.CachePersistenceException; import org.ehcache.expiry.ExpiryPolicy; import org.ehcache.impl.config.store.disk.OffHeapDiskStoreConfiguration; +import org.ehcache.impl.internal.store.offheap.portability.AssertingOffHeapValueHolderPortability; +import org.ehcache.impl.internal.store.offheap.portability.OffHeapValueHolderPortability; import org.ehcache.impl.internal.events.TestStoreEventDispatcher; import org.ehcache.impl.internal.executor.OnDemandExecutionService; import org.ehcache.impl.internal.persistence.TestDiskResourceService; @@ -39,7 +43,7 @@ import org.ehcache.impl.internal.spi.serialization.DefaultSerializationProvider; import org.ehcache.core.spi.time.SystemTimeSource; import org.ehcache.core.spi.time.TimeSource; -import org.ehcache.core.internal.service.ServiceLocator; +import org.ehcache.core.spi.ServiceLocator; import org.ehcache.core.spi.store.Store; import org.ehcache.impl.internal.util.UnmatchedResourceType; import org.ehcache.spi.loaderwriter.CacheLoaderWriter; @@ -48,9 +52,11 @@ import org.ehcache.spi.serialization.UnsupportedTypeException; import org.ehcache.core.spi.service.FileBasedPersistenceContext; import org.ehcache.spi.persistence.PersistableResourceService.PersistenceSpaceIdentifier; +import org.ehcache.test.MockitoUtil; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import org.mockito.Answers; import org.terracotta.context.query.Matcher; import org.terracotta.context.query.Query; import org.terracotta.context.query.QueryBuilder; @@ -78,16 +84,16 @@ import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; import static org.ehcache.config.units.MemoryUnit.MB; -import static org.ehcache.core.internal.service.ServiceLocator.dependencySet; +import static org.ehcache.core.spi.ServiceLocator.dependencySet; import static org.ehcache.impl.config.store.disk.OffHeapDiskStoreConfiguration.DEFAULT_DISK_SEGMENTS; import static org.ehcache.impl.config.store.disk.OffHeapDiskStoreConfiguration.DEFAULT_WRITER_CONCURRENCY; import static org.ehcache.impl.internal.spi.TestServiceProvider.providerContaining; -import static org.ehcache.test.MockitoUtil.mock; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.terracotta.context.ContextManager.nodeFor; import static org.terracotta.context.query.Matchers.attributes; @@ -121,16 +127,17 @@ public void testRecovery() throws StoreAccessException, IOException { @Test public void testRecoveryFailureWhenValueTypeChangesToIncompatibleClass() throws Exception { OffHeapDiskStore.Provider provider = new OffHeapDiskStore.Provider(); - ServiceLocator serviceLocator = dependencySet().with(diskResourceService).with(provider).build(); + ServiceLocator serviceLocator = dependencySet().with(diskResourceService).with(provider) + .with(mock(CacheManagerProviderService.class, Answers.RETURNS_DEEP_STUBS)).build(); serviceLocator.startAllServices(); - CacheConfiguration cacheConfiguration = mock(CacheConfiguration.class); + CacheConfiguration cacheConfiguration = MockitoUtil.mock(CacheConfiguration.class); when(cacheConfiguration.getResourcePools()).thenReturn(newResourcePoolsBuilder().disk(1, MemoryUnit.MB, false).build()); PersistenceSpaceIdentifier space = diskResourceService.getPersistenceSpaceIdentifier("cache", cacheConfiguration); { @SuppressWarnings("unchecked") - Store.Configuration storeConfig1 = mock(Store.Configuration.class); + Store.Configuration storeConfig1 = MockitoUtil.mock(Store.Configuration.class); when(storeConfig1.getKeyType()).thenReturn(Long.class); when(storeConfig1.getValueType()).thenReturn(String.class); when(storeConfig1.getResourcePools()).thenReturn(ResourcePoolsBuilder.newResourcePoolsBuilder() @@ -146,7 +153,7 @@ public void testRecoveryFailureWhenValueTypeChangesToIncompatibleClass() throws { @SuppressWarnings("unchecked") - Store.Configuration storeConfig2 = mock(Store.Configuration.class); + Store.Configuration storeConfig2 = MockitoUtil.mock(Store.Configuration.class); when(storeConfig2.getKeyType()).thenReturn(Long.class); when(storeConfig2.getValueType()).thenReturn(Serializable.class); when(storeConfig2.getResourcePools()).thenReturn(ResourcePoolsBuilder.newResourcePoolsBuilder() @@ -171,16 +178,17 @@ public void testRecoveryFailureWhenValueTypeChangesToIncompatibleClass() throws @Test public void testRecoveryWithArrayType() throws Exception { OffHeapDiskStore.Provider provider = new OffHeapDiskStore.Provider(); - ServiceLocator serviceLocator = dependencySet().with(diskResourceService).with(provider).build(); + ServiceLocator serviceLocator = dependencySet().with(diskResourceService).with(provider) + .with(mock(CacheManagerProviderService.class, Answers.RETURNS_DEEP_STUBS)).build(); serviceLocator.startAllServices(); - CacheConfiguration cacheConfiguration = mock(CacheConfiguration.class); + CacheConfiguration cacheConfiguration = MockitoUtil.mock(CacheConfiguration.class); when(cacheConfiguration.getResourcePools()).thenReturn(newResourcePoolsBuilder().disk(1, MemoryUnit.MB, false).build()); PersistenceSpaceIdentifier space = diskResourceService.getPersistenceSpaceIdentifier("cache", cacheConfiguration); { @SuppressWarnings("unchecked") - Store.Configuration storeConfig1 = mock(Store.Configuration.class); + Store.Configuration storeConfig1 = MockitoUtil.mock(Store.Configuration.class); when(storeConfig1.getKeyType()).thenReturn(Long.class); when(storeConfig1.getValueType()).thenReturn(Object[].class); when(storeConfig1.getResourcePools()).thenReturn(ResourcePoolsBuilder.newResourcePoolsBuilder() @@ -196,7 +204,7 @@ public void testRecoveryWithArrayType() throws Exception { { @SuppressWarnings("unchecked") - Store.Configuration storeConfig2 = mock(Store.Configuration.class); + Store.Configuration storeConfig2 = MockitoUtil.mock(Store.Configuration.class); when(storeConfig2.getKeyType()).thenReturn(Long.class); when(storeConfig2.getValueType()).thenReturn(Object[].class); when(storeConfig2.getResourcePools()).thenReturn(ResourcePoolsBuilder.newResourcePoolsBuilder() @@ -216,15 +224,16 @@ public void testRecoveryWithArrayType() throws Exception { @Test public void testProvidingOffHeapDiskStoreConfiguration() throws Exception { OffHeapDiskStore.Provider provider = new OffHeapDiskStore.Provider(); - ServiceLocator serviceLocator = dependencySet().with(diskResourceService).with(provider).build(); + ServiceLocator serviceLocator = dependencySet().with(diskResourceService).with(provider) + .with(mock(CacheManagerProviderService.class, Answers.RETURNS_DEEP_STUBS)).build(); serviceLocator.startAllServices(); - CacheConfiguration cacheConfiguration = mock(CacheConfiguration.class); + CacheConfiguration cacheConfiguration = MockitoUtil.mock(CacheConfiguration.class); when(cacheConfiguration.getResourcePools()).thenReturn(newResourcePoolsBuilder().disk(1, MemoryUnit.MB, false).build()); PersistenceSpaceIdentifier space = diskResourceService.getPersistenceSpaceIdentifier("cache", cacheConfiguration); @SuppressWarnings("unchecked") - Store.Configuration storeConfig1 = mock(Store.Configuration.class); + Store.Configuration storeConfig1 = MockitoUtil.mock(Store.Configuration.class); when(storeConfig1.getKeyType()).thenReturn(Long.class); when(storeConfig1.getValueType()).thenReturn(Object[].class); when(storeConfig1.getResourcePools()).thenReturn(ResourcePoolsBuilder.newResourcePoolsBuilder() @@ -249,12 +258,17 @@ protected OffHeapDiskStore createAndInitStore(final TimeSource t Serializer valueSerializer = serializationProvider.createValueSerializer(String.class, classLoader); StoreConfigurationImpl storeConfiguration = new StoreConfigurationImpl<>(String.class, String.class, null, classLoader, expiry, null, 0, true, keySerializer, valueSerializer, null, false); - OffHeapDiskStore offHeapStore = new OffHeapDiskStore<>( + OffHeapDiskStore offHeapStore = new OffHeapDiskStore( getPersistenceContext(), new OnDemandExecutionService(), null, DEFAULT_WRITER_CONCURRENCY, DEFAULT_DISK_SEGMENTS, storeConfiguration, timeSource, new TestStoreEventDispatcher<>(), - MB.toBytes(1)); + MB.toBytes(1), new DefaultStatisticsService()) { + @Override + protected OffHeapValueHolderPortability createValuePortability(Serializer serializer) { + return new AssertingOffHeapValueHolderPortability<>(serializer); + } + }; OffHeapDiskStore.Provider.init(offHeapStore); return offHeapStore; } catch (UnsupportedTypeException e) { @@ -272,12 +286,17 @@ protected OffHeapDiskStore createAndInitStore(TimeSource timeSou Serializer valueSerializer = serializationProvider.createValueSerializer(byte[].class, classLoader); StoreConfigurationImpl storeConfiguration = new StoreConfigurationImpl<>(String.class, byte[].class, evictionAdvisor, getClass().getClassLoader(), expiry, null, 0, true, keySerializer, valueSerializer, null, false); - OffHeapDiskStore offHeapStore = new OffHeapDiskStore<>( + OffHeapDiskStore offHeapStore = new OffHeapDiskStore( getPersistenceContext(), new OnDemandExecutionService(), null, DEFAULT_WRITER_CONCURRENCY, DEFAULT_DISK_SEGMENTS, storeConfiguration, timeSource, new TestStoreEventDispatcher<>(), - MB.toBytes(1)); + MB.toBytes(1), new DefaultStatisticsService()) { + @Override + protected OffHeapValueHolderPortability createValuePortability(Serializer serializer) { + return new AssertingOffHeapValueHolderPortability<>(serializer); + } + }; OffHeapDiskStore.Provider.init(offHeapStore); return offHeapStore; } catch (UnsupportedTypeException e) { @@ -339,7 +358,7 @@ private void assertRank(final Store.Provider provider, final int expectedRank, f private FileBasedPersistenceContext getPersistenceContext() { try { - CacheConfiguration cacheConfiguration = mock(CacheConfiguration.class); + CacheConfiguration cacheConfiguration = MockitoUtil.mock(CacheConfiguration.class); when(cacheConfiguration.getResourcePools()).thenReturn(newResourcePoolsBuilder().disk(1, MB, false).build()); PersistenceSpaceIdentifier space = diskResourceService.getPersistenceSpaceIdentifier("cache", cacheConfiguration); return diskResourceService.createPersistenceContextWithin(space, "store"); diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/disk/factories/EhcachePersistentSegmentTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/disk/factories/EhcachePersistentSegmentTest.java similarity index 99% rename from impl/src/test/java/org/ehcache/impl/internal/store/disk/factories/EhcachePersistentSegmentTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/disk/factories/EhcachePersistentSegmentTest.java index ab44863c2c..611c846a25 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/disk/factories/EhcachePersistentSegmentTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/disk/factories/EhcachePersistentSegmentTest.java @@ -41,7 +41,7 @@ import static org.ehcache.impl.internal.store.disk.OffHeapDiskStore.persistent; import static org.ehcache.impl.internal.spi.TestServiceProvider.providerContaining; import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.terracotta.offheapstore.util.MemoryUnit.BYTES; diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/heap/BaseOnHeapStoreTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/BaseOnHeapStoreTest.java similarity index 97% rename from impl/src/test/java/org/ehcache/impl/internal/store/heap/BaseOnHeapStoreTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/BaseOnHeapStoreTest.java index 3be124b237..26b538675f 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/heap/BaseOnHeapStoreTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/BaseOnHeapStoreTest.java @@ -225,11 +225,11 @@ public void testAccessTime() throws Exception { OnHeapStore store = newStore(timeSource, ExpiryPolicyBuilder.noExpiration()); store.put("key", "value"); - long first = store.get("key").lastAccessTime(TimeUnit.MILLISECONDS); + long first = store.get("key").lastAccessTime(); assertThat(first, equalTo(timeSource.getTimeMillis())); final long advance = 5; timeSource.advanceTime(advance); - long next = store.get("key").lastAccessTime(TimeUnit.MILLISECONDS); + long next = store.get("key").lastAccessTime(); assertThat(next, equalTo(first + advance)); } @@ -297,7 +297,7 @@ public void testCreateTime() throws Exception { assertThat(store.containsKey("key"), is(false)); store.put("key", "value"); ValueHolder valueHolder = store.get("key"); - assertThat(timeSource.getTimeMillis(), equalTo(valueHolder.creationTime(TimeUnit.MILLISECONDS))); + assertThat(timeSource.getTimeMillis(), equalTo(valueHolder.creationTime())); } @Test @@ -339,9 +339,9 @@ public void testPutIfAbsentUpdatesAccessTime() throws Exception { OnHeapStore store = newStore(timeSource, ExpiryPolicyBuilder.noExpiration()); assertThat(store.get("key"), nullValue()); store.putIfAbsent("key", "value", b -> {}); - long first = store.get("key").lastAccessTime(TimeUnit.MILLISECONDS); + long first = store.get("key").lastAccessTime(); timeSource.advanceTime(1); - long next = store.putIfAbsent("key", "value2", b -> {}).lastAccessTime(TimeUnit.MILLISECONDS); + long next = store.putIfAbsent("key", "value2", b -> {}).lastAccessTime(); assertThat(next - first, equalTo(1L)); } @@ -540,12 +540,9 @@ public void testIteratorExpired() throws Exception { timeSource.advanceTime(1); Map observed = observe(store.iterator()); - assertThat(3, equalTo(observed.size())); - assertThat(observed.get("key1"), equalTo("value1")); - assertThat(observed.get("key2"), equalTo("value2")); - assertThat(observed.get("key3"), equalTo("value3")); + assertThat(0, equalTo(observed.size())); - StatisticsTestUtils.validateStat(store, StoreOperationOutcomes.ExpirationOutcome.SUCCESS, 0L); + StatisticsTestUtils.validateStat(store, StoreOperationOutcomes.ExpirationOutcome.SUCCESS, 3L); } @Test @@ -573,15 +570,15 @@ public void testComputeReplaceTrue() throws Exception { store.put("key", "value"); ValueHolder installedHolder = store.get("key"); - long createTime = installedHolder.creationTime(TimeUnit.MILLISECONDS); - long accessTime = installedHolder.lastAccessTime(TimeUnit.MILLISECONDS); + long createTime = installedHolder.creationTime(); + long accessTime = installedHolder.lastAccessTime(); timeSource.advanceTime(1); ValueHolder newValue = store.computeAndGet("key", (mappedKey, mappedValue) -> mappedValue, () -> true, () -> false); assertThat(newValue.get(), equalTo("value")); - assertThat(createTime + 1, equalTo(newValue.creationTime(TimeUnit.MILLISECONDS))); - assertThat(accessTime + 1, equalTo(newValue.lastAccessTime(TimeUnit.MILLISECONDS))); + assertThat(createTime + 1, equalTo(newValue.creationTime())); + assertThat(accessTime + 1, equalTo(newValue.lastAccessTime())); verify(eventSink).updated(eq("key"), argThat(holding("value")), eq("value")); verifyListenerReleaseEventsInOrder(eventDispatcher); StatisticsTestUtils.validateStats(store, EnumSet.of(StoreOperationOutcomes.ComputeOutcome.PUT)); @@ -594,15 +591,15 @@ public void testComputeReplaceFalse() throws Exception { store.put("key", "value"); ValueHolder installedHolder = store.get("key"); - long createTime = installedHolder.creationTime(TimeUnit.MILLISECONDS); - long accessTime = installedHolder.lastAccessTime(TimeUnit.MILLISECONDS); + long createTime = installedHolder.creationTime(); + long accessTime = installedHolder.lastAccessTime(); timeSource.advanceTime(1); ValueHolder newValue = store.computeAndGet("key", (mappedKey, mappedValue) -> mappedValue, () -> false, () -> false); assertThat(newValue.get(), equalTo("value")); - assertThat(createTime, equalTo(newValue.creationTime(TimeUnit.MILLISECONDS))); - assertThat(accessTime + 1, equalTo(newValue.lastAccessTime(TimeUnit.MILLISECONDS))); + assertThat(createTime, equalTo(newValue.creationTime())); + assertThat(accessTime + 1, equalTo(newValue.lastAccessTime())); StatisticsTestUtils.validateStats(store, EnumSet.of(StoreOperationOutcomes.ComputeOutcome.HIT)); } @@ -945,7 +942,7 @@ public void testGetOfComputeIfAbsentExpiresWithLoaderWriter() throws Exception { @SuppressWarnings("unchecked") final ValueHolder vh = mock(ValueHolder.class); when(vh.get()).thenReturn("newvalue"); - when(vh.expirationTime(TimeUnit.MILLISECONDS)).thenReturn(2L); + when(vh.expirationTime()).thenReturn(2L); ValueHolder newValue = store.getOrComputeIfAbsent("key", s -> vh); @@ -1288,7 +1285,7 @@ private static Map observeAccessTimes(Iterator map = new HashMap<>(); while (iter.hasNext()) { Entry> entry = iter.next(); - map.put(entry.getKey(), entry.getValue().lastAccessTime(TimeUnit.MILLISECONDS)); + map.put(entry.getKey(), entry.getValue().lastAccessTime()); } return map; } diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/heap/ByteSizedOnHeapStoreByRefSPITest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/ByteSizedOnHeapStoreByRefSPITest.java similarity index 90% rename from impl/src/test/java/org/ehcache/impl/internal/store/heap/ByteSizedOnHeapStoreByRefSPITest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/ByteSizedOnHeapStoreByRefSPITest.java index 4070c9a0e8..14af067cb2 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/heap/ByteSizedOnHeapStoreByRefSPITest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/ByteSizedOnHeapStoreByRefSPITest.java @@ -19,8 +19,9 @@ import org.ehcache.config.EvictionAdvisor; import org.ehcache.config.ResourcePools; import org.ehcache.config.builders.ExpiryPolicyBuilder; -import org.ehcache.core.internal.store.StoreConfigurationImpl; import org.ehcache.config.units.MemoryUnit; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; +import org.ehcache.core.store.StoreConfigurationImpl; import org.ehcache.expiry.ExpiryPolicy; import org.ehcache.impl.copy.IdentityCopier; import org.ehcache.impl.internal.events.TestStoreEventDispatcher; @@ -30,13 +31,14 @@ import org.ehcache.core.spi.time.TimeSource; import org.ehcache.internal.store.StoreFactory; import org.ehcache.internal.store.StoreSPITest; -import org.ehcache.core.internal.service.ServiceLocator; +import org.ehcache.core.spi.ServiceLocator; import org.ehcache.core.spi.store.Store; import org.ehcache.spi.service.ServiceConfiguration; import org.junit.Before; +import org.terracotta.statistics.StatisticsManager; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; -import static org.ehcache.core.internal.service.ServiceLocator.dependencySet; +import static org.ehcache.core.spi.ServiceLocator.dependencySet; public class ByteSizedOnHeapStoreByRefSPITest extends StoreSPITest { @@ -78,7 +80,7 @@ private Store newStore(Long capacity, EvictionAdvisor config = new StoreConfigurationImpl<>(getKeyType(), getValueType(), evictionAdvisor, getClass().getClassLoader(), expiry, resourcePools, 0, null, null); return new OnHeapStore<>(config, timeSource, IdentityCopier.identityCopier(), IdentityCopier.identityCopier(), - new DefaultSizeOfEngine(Long.MAX_VALUE, Long.MAX_VALUE), new TestStoreEventDispatcher<>()); + new DefaultSizeOfEngine(Long.MAX_VALUE, Long.MAX_VALUE), new TestStoreEventDispatcher<>(), new DefaultStatisticsService()); } @Override @@ -105,8 +107,8 @@ public Class getValueType() { } @Override - public ServiceConfiguration[] getServiceConfigurations() { - return new ServiceConfiguration[0]; + public ServiceConfiguration[] getServiceConfigurations() { + return new ServiceConfiguration[0]; } @Override @@ -122,6 +124,7 @@ public String createValue(long seed) { @Override public void close(final Store store) { OnHeapStore.Provider.close((OnHeapStore)store); + StatisticsManager.nodeFor(store).clean(); } @Override diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/heap/ByteSizedOnHeapStoreByValueSPITest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/ByteSizedOnHeapStoreByValueSPITest.java similarity index 90% rename from impl/src/test/java/org/ehcache/impl/internal/store/heap/ByteSizedOnHeapStoreByValueSPITest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/ByteSizedOnHeapStoreByValueSPITest.java index 60d60b1fc5..772e2ce7e4 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/heap/ByteSizedOnHeapStoreByValueSPITest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/ByteSizedOnHeapStoreByValueSPITest.java @@ -19,8 +19,9 @@ import org.ehcache.config.EvictionAdvisor; import org.ehcache.config.ResourcePools; import org.ehcache.config.builders.ExpiryPolicyBuilder; -import org.ehcache.core.internal.store.StoreConfigurationImpl; import org.ehcache.config.units.MemoryUnit; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; +import org.ehcache.core.store.StoreConfigurationImpl; import org.ehcache.expiry.ExpiryPolicy; import org.ehcache.impl.copy.SerializingCopier; import org.ehcache.impl.internal.events.TestStoreEventDispatcher; @@ -31,16 +32,17 @@ import org.ehcache.impl.serialization.JavaSerializer; import org.ehcache.internal.store.StoreFactory; import org.ehcache.internal.store.StoreSPITest; -import org.ehcache.core.internal.service.ServiceLocator; +import org.ehcache.core.spi.ServiceLocator; import org.ehcache.core.spi.store.Store; import org.ehcache.spi.copy.Copier; import org.ehcache.spi.serialization.Serializer; import org.ehcache.spi.service.ServiceConfiguration; import org.junit.Before; +import org.terracotta.statistics.StatisticsManager; import static java.lang.ClassLoader.getSystemClassLoader; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; -import static org.ehcache.core.internal.service.ServiceLocator.dependencySet; +import static org.ehcache.core.spi.ServiceLocator.dependencySet; public class ByteSizedOnHeapStoreByValueSPITest extends StoreSPITest { @@ -86,7 +88,7 @@ private Store newStore(Long capacity, EvictionAdvisor(getSystemClassLoader()), new JavaSerializer<>(getSystemClassLoader())); return new OnHeapStore<>(config, timeSource, defaultCopier, defaultCopier, - new DefaultSizeOfEngine(Long.MAX_VALUE, Long.MAX_VALUE), new TestStoreEventDispatcher<>()); + new DefaultSizeOfEngine(Long.MAX_VALUE, Long.MAX_VALUE), new TestStoreEventDispatcher<>(), new DefaultStatisticsService()); } @Override @@ -113,8 +115,8 @@ public Class getValueType() { } @Override - public ServiceConfiguration[] getServiceConfigurations() { - return new ServiceConfiguration[0]; + public ServiceConfiguration[] getServiceConfigurations() { + return new ServiceConfiguration[0]; } @Override @@ -130,6 +132,7 @@ public String createValue(long seed) { @Override public void close(final Store store) { OnHeapStore.Provider.close((OnHeapStore)store); + StatisticsManager.nodeFor(store).clean(); } @Override @@ -147,6 +150,7 @@ public ServiceLocator getServiceProvider() { public static void closeStore(OnHeapStore store) { OnHeapStore.Provider.close(store); + StatisticsManager.nodeFor(store).clean(); } } diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/heap/CountSizedOnHeapStoreByRefTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/CountSizedOnHeapStoreByRefTest.java similarity index 96% rename from impl/src/test/java/org/ehcache/impl/internal/store/heap/CountSizedOnHeapStoreByRefTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/CountSizedOnHeapStoreByRefTest.java index 42076cec32..4a97fddd52 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/heap/CountSizedOnHeapStoreByRefTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/CountSizedOnHeapStoreByRefTest.java @@ -22,6 +22,7 @@ import org.ehcache.config.ResourcePools; import org.ehcache.config.units.EntryUnit; import org.ehcache.core.events.StoreEventDispatcher; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; import org.ehcache.expiry.ExpiryPolicy; import org.ehcache.impl.copy.IdentityCopier; import org.ehcache.impl.internal.sizeof.NoopSizeOfEngine; @@ -98,7 +99,7 @@ public int getDispatcherConcurrency() { public CacheLoaderWriter getCacheLoaderWriter() { return null; } - }, timeSource, IdentityCopier.identityCopier(), IdentityCopier.identityCopier(), new NoopSizeOfEngine(), (StoreEventDispatcher) eventDispatcher); + }, timeSource, IdentityCopier.identityCopier(), IdentityCopier.identityCopier(), new NoopSizeOfEngine(), (StoreEventDispatcher) eventDispatcher, new DefaultStatisticsService()); } } diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/heap/CountSizedOnHeapStoreByValueTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/CountSizedOnHeapStoreByValueTest.java similarity index 96% rename from impl/src/test/java/org/ehcache/impl/internal/store/heap/CountSizedOnHeapStoreByValueTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/CountSizedOnHeapStoreByValueTest.java index 618295b60e..b3cfaca03f 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/heap/CountSizedOnHeapStoreByValueTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/CountSizedOnHeapStoreByValueTest.java @@ -22,6 +22,7 @@ import org.ehcache.config.ResourcePools; import org.ehcache.config.units.EntryUnit; import org.ehcache.core.events.StoreEventDispatcher; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; import org.ehcache.expiry.ExpiryPolicy; import org.ehcache.impl.internal.sizeof.NoopSizeOfEngine; import org.ehcache.core.spi.time.TimeSource; @@ -103,7 +104,7 @@ public int getDispatcherConcurrency() { public CacheLoaderWriter getCacheLoaderWriter() { return null; } - }, timeSource, keyCopier, valueCopier, new NoopSizeOfEngine(), eventDispatcher); + }, timeSource, keyCopier, valueCopier, new NoopSizeOfEngine(), eventDispatcher, new DefaultStatisticsService()); } diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreBulkMethodsTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreBulkMethodsTest.java similarity index 96% rename from impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreBulkMethodsTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreBulkMethodsTest.java index f9a0386304..b065a01694 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreBulkMethodsTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreBulkMethodsTest.java @@ -18,6 +18,7 @@ import org.ehcache.config.builders.ExpiryPolicyBuilder; import org.ehcache.config.units.EntryUnit; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; import org.ehcache.impl.internal.concurrent.ConcurrentHashMap; import org.ehcache.impl.copy.IdentityCopier; import org.ehcache.core.events.NullStoreEventDispatcher; @@ -35,9 +36,9 @@ import java.util.concurrent.ConcurrentMap; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -62,7 +63,7 @@ protected Store.Configuration mockStoreConfig() { protected OnHeapStore newStore() { Store.Configuration configuration = mockStoreConfig(); return new OnHeapStore<>(configuration, SystemTimeSource.INSTANCE, IdentityCopier.identityCopier(), IdentityCopier.identityCopier(), - new NoopSizeOfEngine(), NullStoreEventDispatcher.nullStoreEventDispatcher()); + new NoopSizeOfEngine(), NullStoreEventDispatcher.nullStoreEventDispatcher(), new DefaultStatisticsService()); } @Test @@ -76,7 +77,7 @@ public void testBulkComputeFunctionGetsValuesOfEntries() throws Exception { when(config.getResourcePools()).thenReturn(newResourcePoolsBuilder().heap(Long.MAX_VALUE, EntryUnit.ENTRIES).build()); OnHeapStore store = new OnHeapStore<>(config, SystemTimeSource.INSTANCE, IdentityCopier.identityCopier(), IdentityCopier.identityCopier(), - new NoopSizeOfEngine(), NullStoreEventDispatcher.nullStoreEventDispatcher()); + new NoopSizeOfEngine(), NullStoreEventDispatcher.nullStoreEventDispatcher(), new DefaultStatisticsService()); store.put(1, 2); store.put(2, 3); store.put(3, 4); @@ -155,7 +156,8 @@ public void testBulkComputeStoreRemovesValueWhenFunctionReturnsNullMappings() th Store.Configuration configuration = mockStoreConfig(); @SuppressWarnings("unchecked") - OnHeapStore store = new OnHeapStore<>(configuration, SystemTimeSource.INSTANCE, IdentityCopier.identityCopier(), IdentityCopier.identityCopier(), new NoopSizeOfEngine(), NullStoreEventDispatcher.nullStoreEventDispatcher()); + OnHeapStore store = new OnHeapStore<>(configuration, SystemTimeSource.INSTANCE, IdentityCopier.identityCopier(), + IdentityCopier.identityCopier(), new NoopSizeOfEngine(), NullStoreEventDispatcher.nullStoreEventDispatcher(), new DefaultStatisticsService()); store.put(1, "one"); store.put(2, "two"); store.put(3, "three"); diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreByRefSPITest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreByRefSPITest.java similarity index 88% rename from impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreByRefSPITest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreByRefSPITest.java index a8fe6c36a4..dc7f61bea3 100755 --- a/impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreByRefSPITest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreByRefSPITest.java @@ -19,8 +19,9 @@ import org.ehcache.config.EvictionAdvisor; import org.ehcache.config.ResourcePools; import org.ehcache.config.builders.ExpiryPolicyBuilder; -import org.ehcache.core.internal.store.StoreConfigurationImpl; import org.ehcache.config.units.EntryUnit; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; +import org.ehcache.core.store.StoreConfigurationImpl; import org.ehcache.expiry.ExpiryPolicy; import org.ehcache.impl.copy.IdentityCopier; import org.ehcache.impl.internal.events.TestStoreEventDispatcher; @@ -30,13 +31,14 @@ import org.ehcache.core.spi.time.TimeSource; import org.ehcache.internal.store.StoreFactory; import org.ehcache.internal.store.StoreSPITest; -import org.ehcache.core.internal.service.ServiceLocator; +import org.ehcache.core.spi.ServiceLocator; import org.ehcache.core.spi.store.Store; import org.ehcache.spi.service.ServiceConfiguration; import org.junit.Before; +import org.terracotta.statistics.StatisticsManager; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; -import static org.ehcache.core.internal.service.ServiceLocator.dependencySet; +import static org.ehcache.core.spi.ServiceLocator.dependencySet; /** * Test the {@link org.ehcache.internal.store.heap.OnHeapStore} compliance to the @@ -83,7 +85,8 @@ private Store newStore(Long capacity, EvictionAdvisor config = new StoreConfigurationImpl<>(getKeyType(), getValueType(), evictionAdvisor, getClass().getClassLoader(), expiry, resourcePools, 0, null, null); - return new OnHeapStore<>(config, timeSource, IdentityCopier.identityCopier(), IdentityCopier.identityCopier(), new NoopSizeOfEngine(), new TestStoreEventDispatcher<>()); + return new OnHeapStore<>(config, timeSource, IdentityCopier.identityCopier(), IdentityCopier.identityCopier(), + new NoopSizeOfEngine(), new TestStoreEventDispatcher<>(), new DefaultStatisticsService()); } @Override @@ -111,8 +114,8 @@ public Class getValueType() { } @Override - public ServiceConfiguration[] getServiceConfigurations() { - return new ServiceConfiguration[0]; + public ServiceConfiguration[] getServiceConfigurations() { + return new ServiceConfiguration[0]; } @Override @@ -128,6 +131,7 @@ public String createValue(long seed) { @Override public void close(final Store store) { OnHeapStore.Provider.close((OnHeapStore)store); + StatisticsManager.nodeFor(store).clean(); } @Override diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreByRefTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreByRefTest.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreByRefTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreByRefTest.java diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreByValueSPITest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreByValueSPITest.java similarity index 90% rename from impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreByValueSPITest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreByValueSPITest.java index 6e558ed30e..96b8443734 100755 --- a/impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreByValueSPITest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreByValueSPITest.java @@ -19,8 +19,9 @@ import org.ehcache.config.EvictionAdvisor; import org.ehcache.config.ResourcePools; import org.ehcache.config.builders.ExpiryPolicyBuilder; -import org.ehcache.core.internal.store.StoreConfigurationImpl; import org.ehcache.config.units.EntryUnit; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; +import org.ehcache.core.store.StoreConfigurationImpl; import org.ehcache.expiry.ExpiryPolicy; import org.ehcache.impl.copy.SerializingCopier; import org.ehcache.impl.internal.events.TestStoreEventDispatcher; @@ -31,16 +32,17 @@ import org.ehcache.impl.serialization.JavaSerializer; import org.ehcache.internal.store.StoreFactory; import org.ehcache.internal.store.StoreSPITest; -import org.ehcache.core.internal.service.ServiceLocator; +import org.ehcache.core.spi.ServiceLocator; import org.ehcache.core.spi.store.Store; import org.ehcache.spi.copy.Copier; import org.ehcache.spi.serialization.Serializer; import org.ehcache.spi.service.ServiceConfiguration; import org.junit.Before; +import org.terracotta.statistics.StatisticsManager; import static java.lang.ClassLoader.getSystemClassLoader; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; -import static org.ehcache.core.internal.service.ServiceLocator.dependencySet; +import static org.ehcache.core.spi.ServiceLocator.dependencySet; /** * Test the {@link OnHeapStore} compliance to the @@ -91,7 +93,7 @@ private Store newStore(Long capacity, EvictionAdvisor config = new StoreConfigurationImpl<>(getKeyType(), getValueType(), evictionAdvisor, getClass().getClassLoader(), expiry, resourcePools, 0, new JavaSerializer<>(getSystemClassLoader()), new JavaSerializer<>(getSystemClassLoader())); - return new OnHeapStore<>(config, timeSource, defaultCopier, defaultCopier, new NoopSizeOfEngine(), new TestStoreEventDispatcher<>()); + return new OnHeapStore<>(config, timeSource, defaultCopier, defaultCopier, new NoopSizeOfEngine(), new TestStoreEventDispatcher<>(), new DefaultStatisticsService()); } @Override @@ -118,8 +120,8 @@ public Class getValueType() { } @Override - public ServiceConfiguration[] getServiceConfigurations() { - return new ServiceConfiguration[0]; + public ServiceConfiguration[] getServiceConfigurations() { + return new ServiceConfiguration[0]; } @Override @@ -135,6 +137,7 @@ public String createValue(long seed) { @Override public void close(final Store store) { OnHeapStore.Provider.close((OnHeapStore)store); + StatisticsManager.nodeFor(store).clean(); } @Override @@ -152,6 +155,7 @@ public ServiceLocator getServiceProvider() { public static void closeStore(OnHeapStore store) { OnHeapStore.Provider.close(store); + StatisticsManager.nodeFor(store).clean(); } } diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreByValueTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreByValueTest.java similarity index 97% rename from impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreByValueTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreByValueTest.java index bb9d373b21..73f12cc31f 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreByValueTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreByValueTest.java @@ -39,7 +39,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.concurrent.TimeUnit; import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; import static org.hamcrest.CoreMatchers.instanceOf; @@ -78,11 +77,6 @@ public void testKeyCopierCalledOnGetOrComputeIfAbsent() throws Exception { public Long get() { return key * 1000L; } - - @Override - protected TimeUnit nativeTimeUnit() { - return TimeUnit.MILLISECONDS; - } }); assertThat(computed.get(), is(1000L)); assertThat(keyCopier.copyForWriteCount, is(1)); @@ -163,7 +157,7 @@ public void testStoreByValue() { final Cache cache2 = cacheManager.createCache("cache2", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, heap(1)) - .add(copierConfiguration) + .withService(copierConfiguration) .build()); performAssertions(cache2, false); diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreCachingTierByRefSPITest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreCachingTierByRefSPITest.java similarity index 90% rename from impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreCachingTierByRefSPITest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreCachingTierByRefSPITest.java index ffac6f1dea..765cd2291d 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreCachingTierByRefSPITest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreCachingTierByRefSPITest.java @@ -18,8 +18,9 @@ import org.ehcache.config.ResourcePools; import org.ehcache.config.builders.ExpiryPolicyBuilder; -import org.ehcache.core.internal.store.StoreConfigurationImpl; import org.ehcache.config.units.EntryUnit; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; +import org.ehcache.core.store.StoreConfigurationImpl; import org.ehcache.impl.copy.IdentityCopier; import org.ehcache.core.events.NullStoreEventDispatcher; import org.ehcache.impl.internal.sizeof.NoopSizeOfEngine; @@ -35,7 +36,7 @@ import org.junit.Before; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; -import static org.ehcache.core.internal.service.ServiceLocator.dependencySet; +import static org.ehcache.core.spi.ServiceLocator.dependencySet; /** * This factory instantiates a CachingTier @@ -70,7 +71,8 @@ private CachingTier newCachingTier(Long capacity) { Store.Configuration config = new StoreConfigurationImpl<>(getKeyType(), getValueType(), null, ClassLoader.getSystemClassLoader(), ExpiryPolicyBuilder.noExpiration(), buildResourcePools(capacity), 0, null, null); - return new OnHeapStore<>(config, SystemTimeSource.INSTANCE, IdentityCopier.identityCopier(), IdentityCopier.identityCopier(), new NoopSizeOfEngine(), NullStoreEventDispatcher.nullStoreEventDispatcher()); + return new OnHeapStore<>(config, SystemTimeSource.INSTANCE, IdentityCopier.identityCopier(), IdentityCopier.identityCopier(), + new NoopSizeOfEngine(), NullStoreEventDispatcher.nullStoreEventDispatcher(), new DefaultStatisticsService()); } @Override @@ -102,8 +104,8 @@ public Class getValueType() { } @Override - public ServiceConfiguration[] getServiceConfigurations() { - return new ServiceConfiguration[0]; + public ServiceConfiguration[] getServiceConfigurations() { + return new ServiceConfiguration[0]; } @Override diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreCachingTierByValueSPITest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreCachingTierByValueSPITest.java similarity index 90% rename from impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreCachingTierByValueSPITest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreCachingTierByValueSPITest.java index f9539cb1f5..fd1dba84e4 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreCachingTierByValueSPITest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreCachingTierByValueSPITest.java @@ -18,8 +18,9 @@ import org.ehcache.config.ResourcePools; import org.ehcache.config.builders.ExpiryPolicyBuilder; -import org.ehcache.core.internal.store.StoreConfigurationImpl; import org.ehcache.config.units.EntryUnit; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; +import org.ehcache.core.store.StoreConfigurationImpl; import org.ehcache.impl.copy.SerializingCopier; import org.ehcache.core.events.NullStoreEventDispatcher; import org.ehcache.impl.internal.sizeof.NoopSizeOfEngine; @@ -39,7 +40,7 @@ import static java.lang.ClassLoader.getSystemClassLoader; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; -import static org.ehcache.core.internal.service.ServiceLocator.dependencySet; +import static org.ehcache.core.spi.ServiceLocator.dependencySet; /** * This factory instantiates a CachingTier @@ -77,7 +78,8 @@ private CachingTier newCachingTier(Long capacity) { ClassLoader.getSystemClassLoader(), ExpiryPolicyBuilder.noExpiration(), buildResourcePools(capacity), 0, new JavaSerializer<>(getSystemClassLoader()), new JavaSerializer<>(getSystemClassLoader())); - return new OnHeapStore<>(config, SystemTimeSource.INSTANCE, defaultCopier, defaultCopier, new NoopSizeOfEngine(), NullStoreEventDispatcher.nullStoreEventDispatcher()); + return new OnHeapStore<>(config, SystemTimeSource.INSTANCE, defaultCopier, defaultCopier, new NoopSizeOfEngine(), + NullStoreEventDispatcher.nullStoreEventDispatcher(), new DefaultStatisticsService()); } @Override @@ -111,8 +113,8 @@ public Class getValueType() { } @Override - public ServiceConfiguration[] getServiceConfigurations() { - return new ServiceConfiguration[0]; + public ServiceConfiguration[] getServiceConfigurations() { + return new ServiceConfiguration[0]; } @Override diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreEvictionTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreEvictionTest.java similarity index 93% rename from impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreEvictionTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreEvictionTest.java index 548031f33c..3e951670ba 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreEvictionTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreEvictionTest.java @@ -19,8 +19,9 @@ import org.ehcache.config.ResourcePools; import org.ehcache.config.builders.ExpiryPolicyBuilder; import org.ehcache.config.units.EntryUnit; -import org.ehcache.core.internal.store.StoreConfigurationImpl; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; import org.ehcache.spi.loaderwriter.CacheLoaderWriter; +import org.ehcache.core.store.StoreConfigurationImpl; import org.ehcache.spi.resilience.StoreAccessException; import org.ehcache.event.EventType; import org.ehcache.expiry.ExpiryPolicy; @@ -115,7 +116,7 @@ String.class, String.class, noAdvice(), } }); OnHeapStore store = new OnHeapStore<>(configuration, timeSource, - new IdentityCopier<>(), new IdentityCopier<>(), new NoopSizeOfEngine(), eventDispatcher); + new IdentityCopier<>(), new IdentityCopier<>(), new NoopSizeOfEngine(), eventDispatcher, new DefaultStatisticsService()); timeSource.advanceTime(10000L); store.put(firstKey, "daValue"); timeSource.advanceTime(10000L); @@ -182,11 +183,13 @@ public CacheLoaderWriter getCacheLoaderWriter() { public static class OnHeapStoreForTests extends OnHeapStore { public OnHeapStoreForTests(final Configuration config, final TimeSource timeSource) { - super(config, timeSource, IdentityCopier.identityCopier(), IdentityCopier.identityCopier(), new NoopSizeOfEngine(), NullStoreEventDispatcher.nullStoreEventDispatcher()); + super(config, timeSource, IdentityCopier.identityCopier(), IdentityCopier.identityCopier(), new NoopSizeOfEngine(), + NullStoreEventDispatcher.nullStoreEventDispatcher(), new DefaultStatisticsService()); } public OnHeapStoreForTests(final Configuration config, final TimeSource timeSource, final SizeOfEngine engine) { - super(config, timeSource, IdentityCopier.identityCopier(), IdentityCopier.identityCopier(), engine, NullStoreEventDispatcher.nullStoreEventDispatcher()); + super(config, timeSource, IdentityCopier.identityCopier(), IdentityCopier.identityCopier(), engine, + NullStoreEventDispatcher.nullStoreEventDispatcher(), new DefaultStatisticsService()); } private boolean enforceCapacityWasCalled = false; diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreKeyCopierTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreKeyCopierTest.java similarity index 97% rename from impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreKeyCopierTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreKeyCopierTest.java index 7f179eb96e..fbe6a7b7da 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreKeyCopierTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreKeyCopierTest.java @@ -19,6 +19,7 @@ import org.ehcache.Cache; import org.ehcache.config.builders.ExpiryPolicyBuilder; import org.ehcache.config.units.EntryUnit; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; import org.ehcache.expiry.ExpiryPolicy; import org.ehcache.spi.resilience.StoreAccessException; import org.ehcache.impl.copy.IdentityCopier; @@ -40,11 +41,11 @@ import static java.util.Collections.singleton; import static java.util.Collections.singletonMap; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.sameInstance; -import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -101,7 +102,8 @@ public Key copyForWrite(Key obj) { } }; - store = new OnHeapStore<>(configuration, SystemTimeSource.INSTANCE, keyCopier, IdentityCopier.identityCopier(), new NoopSizeOfEngine(), NullStoreEventDispatcher.nullStoreEventDispatcher()); + store = new OnHeapStore<>(configuration, SystemTimeSource.INSTANCE, keyCopier, IdentityCopier.identityCopier(), + new NoopSizeOfEngine(), NullStoreEventDispatcher.nullStoreEventDispatcher(), new DefaultStatisticsService()); } @Test diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreProviderTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreProviderTest.java similarity index 92% rename from impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreProviderTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreProviderTest.java index 30ed69b897..ebfae715fc 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreProviderTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreProviderTest.java @@ -18,17 +18,21 @@ import org.ehcache.config.ResourcePool; import org.ehcache.config.ResourceType; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; +import org.ehcache.core.spi.ServiceLocator; import org.ehcache.core.spi.store.Store; import org.ehcache.impl.internal.util.UnmatchedResourceType; import org.ehcache.spi.service.ServiceConfiguration; import org.junit.Test; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashSet; import static java.util.Collections.EMPTY_LIST; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; /** @@ -83,7 +87,7 @@ public void testRankCachingTier() throws Exception { private void assertRank(final Store.Provider provider, final int expectedRank, final ResourceType... resources) { assertThat(provider.rank( new HashSet<>(Arrays.asList(resources)), - Collections.>emptyList()), + Collections.>emptyList()), is(expectedRank)); } } diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreValueCopierTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreValueCopierTest.java similarity index 97% rename from impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreValueCopierTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreValueCopierTest.java index 0c1107b609..28566f5bdb 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreValueCopierTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStoreValueCopierTest.java @@ -19,6 +19,7 @@ import org.ehcache.Cache; import org.ehcache.config.builders.ExpiryPolicyBuilder; import org.ehcache.config.units.EntryUnit; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; import org.ehcache.expiry.ExpiryPolicy; import org.ehcache.spi.resilience.StoreAccessException; import org.ehcache.impl.copy.IdentityCopier; @@ -41,10 +42,10 @@ import static java.util.Collections.singletonMap; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; import static org.ehcache.test.MockitoUtil.mock; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.sameInstance; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.Mockito.when; @@ -103,7 +104,8 @@ public Value copyForWrite(Value obj) { } }; - store = new OnHeapStore<>(configuration, SystemTimeSource.INSTANCE, new IdentityCopier<>(), valueCopier, new NoopSizeOfEngine(), NullStoreEventDispatcher.nullStoreEventDispatcher()); + store = new OnHeapStore<>(configuration, SystemTimeSource.INSTANCE, new IdentityCopier<>(), valueCopier, new NoopSizeOfEngine(), + NullStoreEventDispatcher.nullStoreEventDispatcher(), new DefaultStatisticsService()); } @Test diff --git a/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStrategyTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStrategyTest.java new file mode 100644 index 0000000000..a8fec575a1 --- /dev/null +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/OnHeapStrategyTest.java @@ -0,0 +1,170 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.impl.internal.store.heap; + +import org.ehcache.expiry.ExpiryPolicy; +import org.ehcache.impl.internal.store.heap.holders.OnHeapValueHolder; +import org.ehcache.internal.TestTimeSource; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +/** + * @author Henri Tremblay + */ +public class OnHeapStrategyTest { + + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @Mock + private OnHeapStore store; + + @Mock + private ExpiryPolicy policy; + + private OnHeapStrategy strategy; + + private static class TestOnHeapValueHolder extends OnHeapValueHolder { + + long now; + Duration expiration; + + protected TestOnHeapValueHolder(long expirationTime) { + super(1, 0, expirationTime, true); + } + + @Override + public String get() { + return "test"; + } + + @Override + public void accessed(long now, Duration expiration) { + this.now = now; + this.expiration = expiration; + super.accessed(now, expiration); + } + } + + private TestTimeSource timeSource = new TestTimeSource(); + + @Test + public void isExpired_10seconds() { + strategy = OnHeapStrategy.strategy(store, policy, timeSource); + + TestOnHeapValueHolder mapping = new TestOnHeapValueHolder(10); + assertThat(strategy.isExpired(mapping)).isFalse(); + timeSource.advanceTime(10); + assertThat(strategy.isExpired(mapping)).isTrue(); + } + + @Test + public void isExpired_TTL10seconds() { + strategy = OnHeapStrategy.strategy(store, policy, timeSource); + + TestOnHeapValueHolder mapping = new TestOnHeapValueHolder(10); + assertThat(strategy.isExpired(mapping)).isFalse(); + timeSource.advanceTime(10); + assertThat(strategy.isExpired(mapping)).isTrue(); + } + + @Test + public void isExpired_neverExpires() { + strategy = OnHeapStrategy.strategy(store, ExpiryPolicy.NO_EXPIRY, timeSource); + + TestOnHeapValueHolder mapping = new TestOnHeapValueHolder(10); + assertThat(strategy.isExpired(mapping)).isFalse(); + timeSource.advanceTime(10); + assertThat(strategy.isExpired(mapping)).isFalse(); + } + + @Test + public void setAccessTimeAndExpiryThenReturnMappingOutsideLock_nullExpiryForAccess() { + strategy = OnHeapStrategy.strategy(store, ExpiryPolicy.NO_EXPIRY, timeSource); + + TestOnHeapValueHolder mapping = new TestOnHeapValueHolder(10); + when(policy.getExpiryForAccess(1, mapping)).thenReturn(null); + + strategy.setAccessAndExpiryTimeWhenCallerOutsideLock(1, mapping, timeSource.getTimeMillis()); + + assertThat(mapping.expiration).isNull(); + assertThat(mapping.now).isEqualTo(timeSource.getTimeMillis()); + + verifyZeroInteractions(store); + } + + @Test + public void setAccessTimeAndExpiryThenReturnMappingOutsideLock_zeroExpiryOnAccess() { + strategy = OnHeapStrategy.strategy(store, policy, timeSource); + + TestOnHeapValueHolder mapping = new TestOnHeapValueHolder(10); + when(policy.getExpiryForAccess(1, mapping)).thenReturn(Duration.ZERO); + + strategy.setAccessAndExpiryTimeWhenCallerOutsideLock(1, mapping, timeSource.getTimeMillis()); + + verify(store).expireMappingUnderLock(1, mapping); + } + + @Test + public void setAccessTimeAndExpiryThenReturnMappingOutsideLock_infiniteExpiryOnAccess() { + strategy = OnHeapStrategy.strategy(store, policy, timeSource); + + TestOnHeapValueHolder mapping = new TestOnHeapValueHolder(10); + when(policy.getExpiryForAccess(1, mapping)).thenReturn(ExpiryPolicy.INFINITE); + + strategy.setAccessAndExpiryTimeWhenCallerOutsideLock(1, mapping, timeSource.getTimeMillis()); + + assertThat(mapping.expiration).isEqualTo(ExpiryPolicy.INFINITE); + assertThat(mapping.now).isEqualTo(timeSource.getTimeMillis()); + + verifyZeroInteractions(store); + } + + @Test + public void setAccessTimeAndExpiryThenReturnMappingOutsideLock_movingTime() { + strategy = OnHeapStrategy.strategy(store, policy, timeSource); + + TestOnHeapValueHolder mapping = new TestOnHeapValueHolder(10); + when(policy.getExpiryForAccess(1, mapping)).thenReturn(Duration.ofMillis(20)); + + strategy.setAccessAndExpiryTimeWhenCallerOutsideLock(1, mapping, timeSource.getTimeMillis()); + + assertThat(mapping.expiration).isEqualTo(Duration.ofMillis(20)); + assertThat(mapping.now).isEqualTo(timeSource.getTimeMillis()); + + verifyZeroInteractions(store); + + timeSource.advanceTime(30); + + strategy.setAccessAndExpiryTimeWhenCallerOutsideLock(1, mapping, timeSource.getTimeMillis()); + + assertThat(mapping.expiration).isEqualTo(Duration.ofMillis(20)); + assertThat(mapping.now).isEqualTo(timeSource.getTimeMillis()); + + verifyZeroInteractions(store); + } +} diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/ByteAccountingTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/ByteAccountingTest.java similarity index 99% rename from impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/ByteAccountingTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/ByteAccountingTest.java index 7fbb27cce3..88f678b4ed 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/ByteAccountingTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/ByteAccountingTest.java @@ -20,6 +20,7 @@ import org.ehcache.config.ResourcePools; import org.ehcache.config.builders.ExpiryPolicyBuilder; import org.ehcache.config.units.MemoryUnit; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; import org.ehcache.event.EventType; import org.ehcache.core.events.StoreEventDispatcher; import org.ehcache.spi.loaderwriter.CacheLoaderWriter; @@ -538,7 +539,7 @@ static class OnHeapStoreForTests extends OnHeapStore { @SuppressWarnings("unchecked") OnHeapStoreForTests(final Configuration config, final TimeSource timeSource, final SizeOfEngine engine, StoreEventDispatcher eventDispatcher) { - super(config, timeSource, IdentityCopier.identityCopier(), IdentityCopier.identityCopier(), engine, eventDispatcher); + super(config, timeSource, IdentityCopier.identityCopier(), IdentityCopier.identityCopier(), engine, eventDispatcher, new DefaultStatisticsService()); } long getCurrentUsageInBytes() { diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/ByteSizedOnHeapStoreByRefTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/ByteSizedOnHeapStoreByRefTest.java similarity index 94% rename from impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/ByteSizedOnHeapStoreByRefTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/ByteSizedOnHeapStoreByRefTest.java index 45ca0e16c2..7f5bcf62c8 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/ByteSizedOnHeapStoreByRefTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/ByteSizedOnHeapStoreByRefTest.java @@ -23,6 +23,7 @@ import org.ehcache.config.ResourcePools; import org.ehcache.config.units.MemoryUnit; import org.ehcache.core.events.StoreEventDispatcher; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; import org.ehcache.expiry.ExpiryPolicy; import org.ehcache.impl.copy.IdentityCopier; import org.ehcache.impl.internal.sizeof.DefaultSizeOfEngine; @@ -103,7 +104,8 @@ public int getDispatcherConcurrency() { public CacheLoaderWriter getCacheLoaderWriter() { return null; } - }, timeSource, IdentityCopier.identityCopier(), IdentityCopier.identityCopier(), new DefaultSizeOfEngine(Long.MAX_VALUE, Long.MAX_VALUE), (StoreEventDispatcher) eventDispatcher); + }, timeSource, IdentityCopier.identityCopier(), IdentityCopier.identityCopier(), + new DefaultSizeOfEngine(Long.MAX_VALUE, Long.MAX_VALUE), (StoreEventDispatcher) eventDispatcher, new DefaultStatisticsService()); } } diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/ByteSizedOnHeapStoreByValueTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/ByteSizedOnHeapStoreByValueTest.java similarity index 96% rename from impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/ByteSizedOnHeapStoreByValueTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/ByteSizedOnHeapStoreByValueTest.java index 2d305c6f01..04e8218465 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/ByteSizedOnHeapStoreByValueTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/ByteSizedOnHeapStoreByValueTest.java @@ -22,6 +22,7 @@ import org.ehcache.config.ResourcePools; import org.ehcache.config.units.MemoryUnit; import org.ehcache.core.events.StoreEventDispatcher; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; import org.ehcache.expiry.ExpiryPolicy; import org.ehcache.impl.internal.sizeof.DefaultSizeOfEngine; import org.ehcache.impl.internal.store.heap.OnHeapStore; @@ -108,7 +109,7 @@ public int getDispatcherConcurrency() { public CacheLoaderWriter getCacheLoaderWriter() { return null; } - }, timeSource, keyCopier, valueCopier, new DefaultSizeOfEngine(Long.MAX_VALUE, Long.MAX_VALUE), eventDispatcher); + }, timeSource, keyCopier, valueCopier, new DefaultSizeOfEngine(Long.MAX_VALUE, Long.MAX_VALUE), eventDispatcher, new DefaultStatisticsService()); } } diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/OnHeapStoreBulkMethodsTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/OnHeapStoreBulkMethodsTest.java similarity index 95% rename from impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/OnHeapStoreBulkMethodsTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/OnHeapStoreBulkMethodsTest.java index 3c2f30c6d6..e0421375a0 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/OnHeapStoreBulkMethodsTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/OnHeapStoreBulkMethodsTest.java @@ -18,6 +18,7 @@ import org.ehcache.config.builders.ExpiryPolicyBuilder; import org.ehcache.config.units.MemoryUnit; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; import org.ehcache.impl.copy.IdentityCopier; import org.ehcache.impl.internal.concurrent.ConcurrentHashMap; import org.ehcache.core.events.NullStoreEventDispatcher; @@ -35,9 +36,9 @@ import java.util.concurrent.ConcurrentMap; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -58,7 +59,7 @@ protected Store.Configuration mockStoreConfig() { protected OnHeapStore newStore() { Store.Configuration configuration = mockStoreConfig(); return new OnHeapStore<>(configuration, SystemTimeSource.INSTANCE, IdentityCopier.identityCopier(), IdentityCopier.identityCopier(), - new DefaultSizeOfEngine(Long.MAX_VALUE, Long.MAX_VALUE), NullStoreEventDispatcher.nullStoreEventDispatcher()); + new DefaultSizeOfEngine(Long.MAX_VALUE, Long.MAX_VALUE), NullStoreEventDispatcher.nullStoreEventDispatcher(), new DefaultStatisticsService()); } @SuppressWarnings("unchecked") @@ -73,7 +74,7 @@ public void testBulkComputeFunctionGetsValuesOfEntries() throws Exception { Store.Configuration configuration = config; OnHeapStore store = new OnHeapStore<>(configuration, SystemTimeSource.INSTANCE, IdentityCopier.identityCopier(), IdentityCopier.identityCopier(), - new DefaultSizeOfEngine(Long.MAX_VALUE, Long.MAX_VALUE), NullStoreEventDispatcher.nullStoreEventDispatcher()); + new DefaultSizeOfEngine(Long.MAX_VALUE, Long.MAX_VALUE), NullStoreEventDispatcher.nullStoreEventDispatcher(), new DefaultStatisticsService()); store.put(1, 2); store.put(2, 3); store.put(3, 4); diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/OnHeapStoreCachingTierByRefSPITest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/OnHeapStoreCachingTierByRefSPITest.java similarity index 91% rename from impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/OnHeapStoreCachingTierByRefSPITest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/OnHeapStoreCachingTierByRefSPITest.java index aa202e7c93..70d2b5beea 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/OnHeapStoreCachingTierByRefSPITest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/OnHeapStoreCachingTierByRefSPITest.java @@ -18,8 +18,9 @@ import org.ehcache.config.ResourcePools; import org.ehcache.config.builders.ExpiryPolicyBuilder; -import org.ehcache.core.internal.store.StoreConfigurationImpl; import org.ehcache.config.units.MemoryUnit; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; +import org.ehcache.core.store.StoreConfigurationImpl; import org.ehcache.impl.copy.IdentityCopier; import org.ehcache.core.events.NullStoreEventDispatcher; import org.ehcache.impl.internal.sizeof.DefaultSizeOfEngine; @@ -38,7 +39,7 @@ import java.util.Arrays; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; -import static org.ehcache.core.internal.service.ServiceLocator.dependencySet; +import static org.ehcache.core.spi.ServiceLocator.dependencySet; public class OnHeapStoreCachingTierByRefSPITest extends CachingTierSPITest { @@ -69,7 +70,7 @@ private CachingTier newCachingTier(Long capacity) { ClassLoader.getSystemClassLoader(), ExpiryPolicyBuilder.noExpiration(), buildResourcePools(capacity), 0, null, null); return new OnHeapStore<>(config, SystemTimeSource.INSTANCE, IdentityCopier.identityCopier(), IdentityCopier.identityCopier(), - new DefaultSizeOfEngine(Long.MAX_VALUE, Long.MAX_VALUE), NullStoreEventDispatcher.nullStoreEventDispatcher()); + new DefaultSizeOfEngine(Long.MAX_VALUE, Long.MAX_VALUE), NullStoreEventDispatcher.nullStoreEventDispatcher(), new DefaultStatisticsService()); } @Override @@ -100,8 +101,8 @@ public Class getValueType() { } @Override - public ServiceConfiguration[] getServiceConfigurations() { - return new ServiceConfiguration[0]; + public ServiceConfiguration[] getServiceConfigurations() { + return new ServiceConfiguration[0]; } @Override diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/OnHeapStoreCachingTierByValueSPITest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/OnHeapStoreCachingTierByValueSPITest.java similarity index 92% rename from impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/OnHeapStoreCachingTierByValueSPITest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/OnHeapStoreCachingTierByValueSPITest.java index aee64743f5..7b9caa6ec6 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/OnHeapStoreCachingTierByValueSPITest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/OnHeapStoreCachingTierByValueSPITest.java @@ -18,8 +18,9 @@ import org.ehcache.config.ResourcePools; import org.ehcache.config.builders.ExpiryPolicyBuilder; -import org.ehcache.core.internal.store.StoreConfigurationImpl; import org.ehcache.config.units.MemoryUnit; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; +import org.ehcache.core.store.StoreConfigurationImpl; import org.ehcache.impl.copy.SerializingCopier; import org.ehcache.core.events.NullStoreEventDispatcher; import org.ehcache.impl.internal.sizeof.DefaultSizeOfEngine; @@ -42,7 +43,7 @@ import static java.lang.ClassLoader.getSystemClassLoader; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; -import static org.ehcache.core.internal.service.ServiceLocator.dependencySet; +import static org.ehcache.core.spi.ServiceLocator.dependencySet; public class OnHeapStoreCachingTierByValueSPITest extends CachingTierSPITest { @@ -75,7 +76,7 @@ private CachingTier newCachingTier(Long capacity) { ClassLoader.getSystemClassLoader(), ExpiryPolicyBuilder.noExpiration(), buildResourcePools(capacity), 0, new JavaSerializer<>(getSystemClassLoader()), new JavaSerializer<>(getSystemClassLoader())); return new OnHeapStore<>(config, SystemTimeSource.INSTANCE, defaultCopier, defaultCopier, - new DefaultSizeOfEngine(Long.MAX_VALUE, Long.MAX_VALUE), NullStoreEventDispatcher.nullStoreEventDispatcher()); + new DefaultSizeOfEngine(Long.MAX_VALUE, Long.MAX_VALUE), NullStoreEventDispatcher.nullStoreEventDispatcher(), new DefaultStatisticsService()); } @Override @@ -108,8 +109,8 @@ public Class getValueType() { } @Override - public ServiceConfiguration[] getServiceConfigurations() { - return new ServiceConfiguration[0]; + public ServiceConfiguration[] getServiceConfigurations() { + return new ServiceConfiguration[0]; } @Override diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/OnHeapStoreEvictionTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/OnHeapStoreEvictionTest.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/OnHeapStoreEvictionTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/OnHeapStoreEvictionTest.java diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/OversizeMappingTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/OversizeMappingTest.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/OversizeMappingTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/bytesized/OversizeMappingTest.java diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/heap/holders/CopiedOnHeapKeyTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/holders/CopiedOnHeapKeyTest.java similarity index 97% rename from impl/src/test/java/org/ehcache/impl/internal/store/heap/holders/CopiedOnHeapKeyTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/holders/CopiedOnHeapKeyTest.java index c720b66969..8e0840f94a 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/heap/holders/CopiedOnHeapKeyTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/holders/CopiedOnHeapKeyTest.java @@ -19,8 +19,8 @@ import org.ehcache.impl.copy.ReadWriteCopier; import org.junit.Test; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; /** * Created by alsu on 20/08/15. diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/heap/holders/CopiedOnHeapValueHolderTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/holders/CopiedOnHeapValueHolderTest.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/internal/store/heap/holders/CopiedOnHeapValueHolderTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/holders/CopiedOnHeapValueHolderTest.java diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/heap/holders/SerializedOnHeapValueHolderTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/holders/SerializedOnHeapValueHolderTest.java similarity index 98% rename from impl/src/test/java/org/ehcache/impl/internal/store/heap/holders/SerializedOnHeapValueHolderTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/holders/SerializedOnHeapValueHolderTest.java index bf71cd378a..fe4c6d68ee 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/heap/holders/SerializedOnHeapValueHolderTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/heap/holders/SerializedOnHeapValueHolderTest.java @@ -26,13 +26,13 @@ import java.nio.ByteBuffer; import java.util.concurrent.Exchanger; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; /** diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/offheap/AbstractEhcacheOffHeapBackingMapTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/AbstractEhcacheOffHeapBackingMapTest.java similarity index 99% rename from impl/src/test/java/org/ehcache/impl/internal/store/offheap/AbstractEhcacheOffHeapBackingMapTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/AbstractEhcacheOffHeapBackingMapTest.java index 62a7c935d4..89f8f3ffb3 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/offheap/AbstractEhcacheOffHeapBackingMapTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/AbstractEhcacheOffHeapBackingMapTest.java @@ -26,7 +26,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.fail; /** diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/offheap/AbstractOffHeapStoreTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/AbstractOffHeapStoreTest.java similarity index 94% rename from impl/src/test/java/org/ehcache/impl/internal/store/offheap/AbstractOffHeapStoreTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/AbstractOffHeapStoreTest.java index f318ae35d7..ad9bdad37c 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/offheap/AbstractOffHeapStoreTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/AbstractOffHeapStoreTest.java @@ -57,11 +57,11 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.lessThan; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.atLeast; @@ -112,12 +112,15 @@ public void testGetAndRemoveExpiredElementReturnsNull() throws Exception { offHeapStore.put("1", "one"); final AtomicReference> invalidated = new AtomicReference<>(); - offHeapStore.setInvalidationListener((key, valueHolder) -> invalidated.set(valueHolder)); + offHeapStore.setInvalidationListener((key, valueHolder) -> { + valueHolder.get(); + invalidated.set(valueHolder); + }); timeSource.advanceTime(20); assertThat(offHeapStore.getAndRemove("1"), is(nullValue())); assertThat(invalidated.get().get(), equalTo("one")); - assertThat(invalidated.get().isExpired(timeSource.getTimeMillis(), TimeUnit.MILLISECONDS), is(true)); + assertThat(invalidated.get().isExpired(timeSource.getTimeMillis()), is(true)); assertThat(getExpirationStatistic(offHeapStore).count(StoreOperationOutcomes.ExpirationOutcome.SUCCESS), is(1L)); } @@ -158,7 +161,10 @@ public void testInvalidateKeyPresent() throws Exception { offHeapStore.put("1", "one"); final AtomicReference> invalidated = new AtomicReference<>(); - offHeapStore.setInvalidationListener((key, valueHolder) -> invalidated.set(valueHolder)); + offHeapStore.setInvalidationListener((key, valueHolder) -> { + valueHolder.get(); + invalidated.set(valueHolder); + }); offHeapStore.invalidate("1"); assertThat(invalidated.get().get(), equalTo("one")); @@ -188,7 +194,7 @@ public void testWriteBackOfValueHolder() throws StoreAccessException { offHeapStore.put("key1", "value1"); timeSource.advanceTime(10); OffHeapValueHolder valueHolder = (OffHeapValueHolder)offHeapStore.get("key1"); - assertThat(valueHolder.lastAccessTime(TimeUnit.MILLISECONDS), is(10L)); + assertThat(valueHolder.lastAccessTime(), is(10L)); timeSource.advanceTime(10); assertThat(offHeapStore.get("key1"), notNullValue()); timeSource.advanceTime(16); @@ -231,7 +237,7 @@ public void testFlushUpdatesAccessStats() throws StoreAccessException { assertThat(offHeapStore.flush(key, new DelegatingValueHolder<>(firstValueHolder)), is(false)); assertThat(offHeapStore.flush(key, new DelegatingValueHolder<>(secondValueHolder)), is(true)); timeSource.advanceTime(10); // this should NOT affect - assertThat(offHeapStore.getAndFault(key).lastAccessTime(TimeUnit.MILLISECONDS), is(secondValueHolder.creationTime(TimeUnit.MILLISECONDS) + 20)); + assertThat(offHeapStore.getAndFault(key).lastAccessTime(), is(secondValueHolder.creationTime() + 20)); } finally { destroyStore(offHeapStore); } @@ -575,23 +581,23 @@ public T get() { } @Override - public long creationTime(final TimeUnit unit) { - return valueHolder.creationTime(unit); + public long creationTime() { + return valueHolder.creationTime(); } @Override - public long expirationTime(final TimeUnit unit) { - return valueHolder.expirationTime(unit); + public long expirationTime() { + return valueHolder.expirationTime(); } @Override - public boolean isExpired(final long expirationTime, final TimeUnit unit) { - return valueHolder.isExpired(expirationTime, unit); + public boolean isExpired(long expirationTime) { + return valueHolder.isExpired(expirationTime); } @Override - public long lastAccessTime(final TimeUnit unit) { - return valueHolder.lastAccessTime(unit); + public long lastAccessTime() { + return valueHolder.lastAccessTime(); } @Override @@ -609,33 +615,28 @@ public SimpleValueHolder(T v, long creationTime, long expirationTime) { this.value = v; } - @Override - protected TimeUnit nativeTimeUnit() { - return TimeUnit.MILLISECONDS; - } - @Override public T get() { return value; } @Override - public long creationTime(TimeUnit unit) { + public long creationTime() { return 0; } @Override - public long expirationTime(TimeUnit unit) { + public long expirationTime() { return 0; } @Override - public boolean isExpired(long expirationTime, TimeUnit unit) { + public boolean isExpired(long expirationTime) { return false; } @Override - public long lastAccessTime(TimeUnit unit) { + public long lastAccessTime() { return 0; } diff --git a/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/AssertingOffHeapValueHolder.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/AssertingOffHeapValueHolder.java new file mode 100644 index 0000000000..35a9e6df03 --- /dev/null +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/AssertingOffHeapValueHolder.java @@ -0,0 +1,187 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.impl.internal.store.offheap; + +import org.ehcache.spi.serialization.Serializer; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.InstructionAdapter; +import org.objectweb.asm.commons.Method; +import org.terracotta.offheapstore.storage.portability.WriteContext; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.NavigableMap; +import java.util.TreeMap; +import java.util.concurrent.locks.Lock; +import java.util.function.Predicate; + +import static java.util.Arrays.asList; +import static java.util.Arrays.stream; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.objectweb.asm.Opcodes.ASM6; +import static org.objectweb.asm.Type.getObjectType; +import static org.objectweb.asm.Type.getType; +import static org.objectweb.asm.commons.Method.getMethod; + +public class AssertingOffHeapValueHolder extends LazyOffHeapValueHolder { + + /** + * This is a set of 'patterns' that capture a subset of the lock scopes that the `OffHeapValueHolder` can be + * called from. You might end up looking at these patterns for one of three reasons: + * + * 1. (Most Likely) You introduced new code or new testing that access a pre-existing lock scope that is not listed + * here. Find the lock scope in the stack trace of the thrown exception and add an appropriate stanza here. + * 2. (Less Likely) You introduced a new call path that leaks a still serialized, attached value holder that someone + * else tried to access later (outside lock scope). In this case you must locate the source of the value holder + * and either force deserialization or detach the value under the pre-existing lock scope. + * 3. (Least Likely) You introduced a new method to OffHeapStore that needs locking properly. + */ + private static final Collection> LOCK_SCOPES = asList( + className("org.terracotta.offheapstore.AbstractLockedOffHeapHashMap").methodName("shrink"), + className("org.terracotta.offheapstore.AbstractLockedOffHeapHashMap").methodName("computeWithMetadata"), + className("org.terracotta.offheapstore.AbstractLockedOffHeapHashMap").methodName("computeIfPresentWithMetadata"), + className("org.ehcache.impl.internal.store.offheap.factories.EhcacheSegmentFactory$EhcacheSegment$EntrySet").methodName("iterator"), + className("org.ehcache.impl.internal.store.disk.factories.EhcachePersistentSegmentFactory$EhcachePersistentSegment$EntrySet").methodName("iterator"), + className("org.terracotta.offheapstore.AbstractLockedOffHeapHashMap$LockedEntryIterator").methodName("next") + ); + + private static void assertStackTraceContainsLockScope() { + assertThat(stream(Thread.currentThread().getStackTrace()).filter(ste -> LOCK_SCOPES.stream().anyMatch(p -> p.test(ste))).anyMatch(ste -> isLockedInFrame(ste)), is(true)); + } + + private static StePredicateBuilderOne className(String className) { + return new StePredicateBuilderOne() { + @Override + public Predicate methodName(String methodName) { + StePredicateBuilderOne outer = this; + return ste -> outer.test(ste) && methodName.equals(ste.getMethodName()); + } + + @Override + public boolean test(StackTraceElement ste) { + return className.equals(ste.getClassName()); + } + }; + } + + interface StePredicateBuilderOne extends Predicate { + + Predicate methodName(String computeIfPresentWithMetadata); + } + + public AssertingOffHeapValueHolder(long id, ByteBuffer binaryValue, Serializer serializer, long creationTime, long expireTime, long lastAccessTime, WriteContext writeContext) { + super(id, binaryValue, serializer, creationTime, expireTime, lastAccessTime, writeContext); + } + + @Override + void writeBack() { + assertStackTraceContainsLockScope(); + super.writeBack(); + } + + @Override + V deserialize() { + if (!isBinaryValueAvailable()) { + assertStackTraceContainsLockScope(); + } + return super.deserialize(); + } + + @Override + void detach() { + assertStackTraceContainsLockScope(); + super.detach(); + } + + private static final Type LOCK_CLASS; + private static final Method LOCK_METHOD; + private static final Method UNLOCK_METHOD; + + static { + try { + LOCK_CLASS = getType(Lock.class); + LOCK_METHOD = getMethod(Lock.class.getMethod("lock")); + UNLOCK_METHOD = getMethod(Lock.class.getMethod("unlock")); + } catch (NoSuchMethodException e) { + throw new AssertionError(e); + } + } + + private static boolean isLockedInFrame(StackTraceElement ste) { + try { + ClassReader reader = new ClassReader(ste.getClassName()); + + NavigableMap lockLevels = new TreeMap<>(); + + reader.accept(new ClassVisitor(ASM6) { + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { + if (ste.getMethodName().equals(name)) { + return new InstructionAdapter(ASM6, new MethodVisitor(ASM6) {}) { + + private final Map levels = new HashMap<>(); + + private int lockLevel; + + @Override + public void invokeinterface(String owner, String name, String descriptor) { + if (LOCK_CLASS.equals(getObjectType(owner))) { + if (LOCK_METHOD.equals(new Method(name, descriptor))) { + lockLevel++; + } else if (UNLOCK_METHOD.equals(new Method(name, descriptor))) { + lockLevel--; + } + } + } + + @Override + public void visitJumpInsn(int opcode, Label label) { + levels.merge(label, lockLevel, Integer::max); + } + + @Override + public void visitLabel(Label label) { + lockLevel = levels.merge(label, lockLevel, Integer::max); + } + + @Override + public void visitLineNumber(int line, Label start) { + lockLevels.merge(line, levels.get(start), Integer::max); + } + }; + } else { + return null; + } + } + }, 0); + + Map.Entry entry = lockLevels.floorEntry(ste.getLineNumber()); + + return entry.getValue() > 0; + } catch (IOException e) { + throw new AssertionError(e); + } + } +} diff --git a/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/AssertingOffHeapValueHolderTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/AssertingOffHeapValueHolderTest.java new file mode 100644 index 0000000000..f51bd82b3e --- /dev/null +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/AssertingOffHeapValueHolderTest.java @@ -0,0 +1,62 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.impl.internal.store.offheap; + +import org.ehcache.spi.serialization.Serializer; +import org.junit.Test; +import org.terracotta.offheapstore.storage.portability.WriteContext; + +import java.nio.ByteBuffer; + +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; + +public class AssertingOffHeapValueHolderTest { + + @Test @SuppressWarnings("unchecked") + public void testLockingAssertionsOnDetach() { + OffHeapValueHolder valueHolder = new AssertingOffHeapValueHolder<>(1L, ByteBuffer.allocate(1), mock(Serializer.class), 10L, 20L, 15L, mock(WriteContext.class)); + try { + valueHolder.detach(); + fail("Expected AssertionError"); + } catch (AssertionError e) { + //expected + } + } + + @Test @SuppressWarnings("unchecked") + public void testLockingAssertionsOnForceDeserialize() { + OffHeapValueHolder valueHolder = new AssertingOffHeapValueHolder<>(1L, ByteBuffer.allocate(1), mock(Serializer.class), 10L, 20L, 15L, mock(WriteContext.class)); + try { + valueHolder.forceDeserialization(); + fail("Expected AssertionError"); + } catch (AssertionError e) { + //expected + } + } + + @Test @SuppressWarnings("unchecked") + public void testLockingAssertionsOnWriteBack() { + OffHeapValueHolder valueHolder = new AssertingOffHeapValueHolder<>(1L, ByteBuffer.allocate(1), mock(Serializer.class), 10L, 20L, 15L, mock(WriteContext.class)); + try { + valueHolder.writeBack(); + fail("Expected AssertionError"); + } catch (AssertionError e) { + //expected + } + } +} diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/offheap/BasicOffHeapValueHolderTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/BasicOffHeapValueHolderTest.java similarity index 97% rename from impl/src/test/java/org/ehcache/impl/internal/store/offheap/BasicOffHeapValueHolderTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/BasicOffHeapValueHolderTest.java index f3eb983d42..5bbdff41da 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/offheap/BasicOffHeapValueHolderTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/BasicOffHeapValueHolderTest.java @@ -19,8 +19,8 @@ import org.junit.Before; import org.junit.Test; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; /** * BasicOffHeapValueHolderTest diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/offheap/BinaryOffHeapValueHolderTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/BinaryOffHeapValueHolderTest.java similarity index 97% rename from impl/src/test/java/org/ehcache/impl/internal/store/offheap/BinaryOffHeapValueHolderTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/BinaryOffHeapValueHolderTest.java index 06ccb60374..28c858eeb7 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/offheap/BinaryOffHeapValueHolderTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/BinaryOffHeapValueHolderTest.java @@ -22,8 +22,8 @@ import java.nio.ByteBuffer; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; /** * BinaryOffHeapValueHolderTest diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/offheap/EhcacheConcurrentOffHeapClockCacheTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/EhcacheConcurrentOffHeapClockCacheTest.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/internal/store/offheap/EhcacheConcurrentOffHeapClockCacheTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/EhcacheConcurrentOffHeapClockCacheTest.java diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/offheap/LazyOffHeapValueHolderTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/LazyOffHeapValueHolderTest.java similarity index 98% rename from impl/src/test/java/org/ehcache/impl/internal/store/offheap/LazyOffHeapValueHolderTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/LazyOffHeapValueHolderTest.java index f4d9e68254..76f6dc0466 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/offheap/LazyOffHeapValueHolderTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/LazyOffHeapValueHolderTest.java @@ -22,9 +22,9 @@ import java.nio.ByteBuffer; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/offheap/MemorySizeParserTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/MemorySizeParserTest.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/internal/store/offheap/MemorySizeParserTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/MemorySizeParserTest.java diff --git a/transactions/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreLifecycleHelper.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreLifecycleHelper.java similarity index 90% rename from transactions/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreLifecycleHelper.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreLifecycleHelper.java index 781e18cad6..7ba4f0bf90 100644 --- a/transactions/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreLifecycleHelper.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreLifecycleHelper.java @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.ehcache.impl.internal.store.offheap; +import org.terracotta.statistics.StatisticsManager; + /** * @author Ludovic Orban */ @@ -30,6 +31,7 @@ public static void init(OffHeapStore offHeapStore) { public static void close(OffHeapStore offHeapStore) { OffHeapStore.Provider.close(offHeapStore); + StatisticsManager.nodeFor(offHeapStore).clean(); } } diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreSPITest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreSPITest.java similarity index 91% rename from impl/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreSPITest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreSPITest.java index 5c93ac6fe9..60bb1ca087 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreSPITest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreSPITest.java @@ -19,10 +19,11 @@ import org.ehcache.config.EvictionAdvisor; import org.ehcache.config.ResourcePools; import org.ehcache.config.builders.ExpiryPolicyBuilder; -import org.ehcache.core.internal.store.StoreConfigurationImpl; import org.ehcache.config.SizedResourcePool; import org.ehcache.config.builders.ResourcePoolsBuilder; import org.ehcache.config.units.MemoryUnit; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; +import org.ehcache.core.store.StoreConfigurationImpl; import org.ehcache.expiry.ExpiryPolicy; import org.ehcache.impl.internal.events.TestStoreEventDispatcher; import org.ehcache.core.spi.time.SystemTimeSource; @@ -31,17 +32,18 @@ import org.ehcache.internal.store.StoreFactory; import org.ehcache.internal.tier.AuthoritativeTierFactory; import org.ehcache.internal.tier.AuthoritativeTierSPITest; -import org.ehcache.core.internal.service.ServiceLocator; +import org.ehcache.core.spi.ServiceLocator; import org.ehcache.core.spi.store.Store; import org.ehcache.core.spi.store.tiering.AuthoritativeTier; import org.ehcache.spi.serialization.Serializer; import org.ehcache.spi.service.ServiceConfiguration; import org.junit.Before; +import org.terracotta.statistics.StatisticsManager; import java.util.Arrays; import static org.ehcache.config.ResourceType.Core.OFFHEAP; -import static org.ehcache.core.internal.service.ServiceLocator.dependencySet; +import static org.ehcache.core.spi.ServiceLocator.dependencySet; /** * OffHeapStoreSPITest @@ -85,7 +87,7 @@ private AuthoritativeTier newStore(Long capacity, EvictionAdviso Store.Configuration config = new StoreConfigurationImpl<>(getKeyType(), getValueType(), evictionAdvisor, getClass().getClassLoader(), expiry, resourcePools, 0, keySerializer, valueSerializer); OffHeapStore store = new OffHeapStore<>(config, timeSource, new TestStoreEventDispatcher<>(), unit - .toBytes(offheapPool.getSize())); + .toBytes(offheapPool.getSize()), new DefaultStatisticsService()); OffHeapStore.Provider.init(store); return store; } @@ -113,8 +115,8 @@ public Class getValueType() { } @Override - public ServiceConfiguration[] getServiceConfigurations() { - return new ServiceConfiguration[0]; + public ServiceConfiguration[] getServiceConfigurations() { + return new ServiceConfiguration[0]; } @Override @@ -143,6 +145,7 @@ public String createValue(long seed) { @Override public void close(final Store store) { OffHeapStore.Provider.close((OffHeapStore)store); + StatisticsManager.nodeFor(store).clean(); } }; } @@ -163,6 +166,7 @@ public static void initStore(OffHeapStore offHeapStore) { public static void closeStore(OffHeapStore offHeapStore) { OffHeapStore.Provider.close(offHeapStore); + StatisticsManager.nodeFor(offHeapStore).clean(); } } diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreTest.java similarity index 80% rename from impl/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreTest.java index 6c2e7e7b0a..20b2449444 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreTest.java @@ -18,19 +18,23 @@ import org.ehcache.config.EvictionAdvisor; import org.ehcache.config.ResourceType; -import org.ehcache.core.internal.store.StoreConfigurationImpl; import org.ehcache.config.units.MemoryUnit; import org.ehcache.core.spi.store.Store; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; +import org.ehcache.core.store.StoreConfigurationImpl; import org.ehcache.expiry.ExpiryPolicy; import org.ehcache.impl.internal.events.TestStoreEventDispatcher; import org.ehcache.impl.internal.spi.serialization.DefaultSerializationProvider; import org.ehcache.core.spi.time.TimeSource; import org.ehcache.impl.internal.util.UnmatchedResourceType; +import org.ehcache.impl.internal.store.offheap.portability.AssertingOffHeapValueHolderPortability; +import org.ehcache.impl.internal.store.offheap.portability.OffHeapValueHolderPortability; import org.ehcache.spi.serialization.SerializationProvider; import org.ehcache.spi.serialization.Serializer; import org.ehcache.spi.serialization.UnsupportedTypeException; import org.ehcache.spi.service.ServiceConfiguration; import org.junit.Test; +import org.terracotta.statistics.StatisticsManager; import java.util.Arrays; import java.util.Collections; @@ -38,8 +42,8 @@ import static java.util.Collections.EMPTY_LIST; import static org.ehcache.impl.internal.spi.TestServiceProvider.providerContaining; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; public class OffHeapStoreTest extends AbstractOffHeapStoreTest { @@ -53,8 +57,13 @@ protected OffHeapStore createAndInitStore(TimeSource timeSource, Serializer valueSerializer = serializationProvider.createValueSerializer(String.class, classLoader); StoreConfigurationImpl storeConfiguration = new StoreConfigurationImpl<>(String.class, String.class, null, classLoader, expiry, null, 0, keySerializer, valueSerializer); - OffHeapStore offHeapStore = new OffHeapStore<>(storeConfiguration, timeSource, new TestStoreEventDispatcher<>(), MemoryUnit.MB - .toBytes(1)); + OffHeapStore offHeapStore = new OffHeapStore(storeConfiguration, timeSource, new TestStoreEventDispatcher<>(), MemoryUnit.MB + .toBytes(1), new DefaultStatisticsService()) { + @Override + protected OffHeapValueHolderPortability createValuePortability(Serializer serializer) { + return new AssertingOffHeapValueHolderPortability<>(serializer); + } + }; OffHeapStore.Provider.init(offHeapStore); return offHeapStore; } catch (UnsupportedTypeException e) { @@ -72,8 +81,13 @@ protected OffHeapStore createAndInitStore(TimeSource timeSource, Serializer valueSerializer = serializationProvider.createValueSerializer(byte[].class, classLoader); StoreConfigurationImpl storeConfiguration = new StoreConfigurationImpl<>(String.class, byte[].class, evictionAdvisor, getClass().getClassLoader(), expiry, null, 0, keySerializer, valueSerializer); - OffHeapStore offHeapStore = new OffHeapStore<>(storeConfiguration, timeSource, new TestStoreEventDispatcher<>(), MemoryUnit.MB - .toBytes(1)); + OffHeapStore offHeapStore = new OffHeapStore(storeConfiguration, timeSource, new TestStoreEventDispatcher<>(), MemoryUnit.MB + .toBytes(1), new DefaultStatisticsService()) { + @Override + protected OffHeapValueHolderPortability createValuePortability(Serializer serializer) { + return new AssertingOffHeapValueHolderPortability<>(serializer); + } + }; OffHeapStore.Provider.init(offHeapStore); return offHeapStore; } catch (UnsupportedTypeException e) { @@ -109,12 +123,13 @@ public void testRank() throws Exception { private void assertRank(final Store.Provider provider, final int expectedRank, final ResourceType... resources) { assertThat(provider.rank( new HashSet<>(Arrays.asList(resources)), - Collections.>emptyList()), + Collections.>emptyList()), is(expectedRank)); } @Override protected void destroyStore(AbstractOffHeapStore store) { OffHeapStore.Provider.close((OffHeapStore) store); + StatisticsManager.nodeFor(store).clean(); } } diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreUtilsTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreUtilsTest.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreUtilsTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreUtilsTest.java diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapValueHolderPortabilityTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapValueHolderPortabilityTest.java similarity index 88% rename from impl/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapValueHolderPortabilityTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapValueHolderPortabilityTest.java index e33dcd8cf8..0721e250ff 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapValueHolderPortabilityTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapValueHolderPortabilityTest.java @@ -17,7 +17,6 @@ package org.ehcache.impl.internal.store.offheap; import org.ehcache.impl.internal.store.offheap.portability.OffHeapValueHolderPortability; -import org.ehcache.core.spi.store.AbstractValueHolder; import org.ehcache.impl.internal.spi.serialization.DefaultSerializationProvider; import org.ehcache.impl.serialization.StringSerializer; import org.ehcache.spi.serialization.SerializationProvider; @@ -28,17 +27,14 @@ import java.io.IOException; import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.nio.ByteBuffer; -import java.nio.MappedByteBuffer; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.concurrent.TimeUnit; import static org.ehcache.impl.internal.spi.TestServiceProvider.providerContaining; import static org.hamcrest.CoreMatchers.equalTo; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -104,9 +100,9 @@ public void testDecodingAPreviousVersionWithTheHits() { OffHeapValueHolder decoded = valueHolderPortability.decode(byteBuffer); assertThat(decoded.getId(), equalTo(123L)); - assertThat(decoded.creationTime(OffHeapValueHolder.TIME_UNIT), equalTo(time)); - assertThat(decoded.lastAccessTime(OffHeapValueHolder.TIME_UNIT), equalTo(time + 1)); - assertThat(decoded.expirationTime(OffHeapValueHolder.TIME_UNIT), equalTo(time + 2)); + assertThat(decoded.creationTime(), equalTo(time)); + assertThat(decoded.lastAccessTime(), equalTo(time + 1)); + assertThat(decoded.expirationTime(), equalTo(time + 2)); assertThat(decoded.get(), equalTo("test")); } @@ -116,8 +112,8 @@ public void testWriteBackSupport() throws NoSuchMethodException, InvocationTarge WriteContext writeContext = mock(WriteContext.class); OffHeapValueHolder decoded = valueHolderPortability.decode(encoded, writeContext); - decoded.setExpirationTime(4L, TimeUnit.MILLISECONDS); - decoded.setLastAccessTime(6L, TimeUnit.MILLISECONDS); + decoded.setExpirationTime(4L); + decoded.setLastAccessTime(6L); decoded.writeBack(); verify(writeContext).setLong(OffHeapValueHolderPortability.ACCESS_TIME_OFFSET, 6L); diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/offheap/factories/EhcacheSegmentTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/factories/EhcacheSegmentTest.java similarity index 99% rename from impl/src/test/java/org/ehcache/impl/internal/store/offheap/factories/EhcacheSegmentTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/factories/EhcacheSegmentTest.java index bffa481e84..f146989f7a 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/offheap/factories/EhcacheSegmentTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/factories/EhcacheSegmentTest.java @@ -36,7 +36,7 @@ import static org.ehcache.impl.internal.store.offheap.OffHeapStoreUtils.getBufferSource; import static org.ehcache.impl.internal.spi.TestServiceProvider.providerContaining; import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; diff --git a/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/portability/AssertingOffHeapValueHolderPortability.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/portability/AssertingOffHeapValueHolderPortability.java new file mode 100644 index 0000000000..e42c7e0672 --- /dev/null +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/offheap/portability/AssertingOffHeapValueHolderPortability.java @@ -0,0 +1,36 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.impl.internal.store.offheap.portability; + +import org.ehcache.impl.internal.store.offheap.AssertingOffHeapValueHolder; +import org.ehcache.impl.internal.store.offheap.OffHeapValueHolder; +import org.ehcache.spi.serialization.Serializer; +import org.terracotta.offheapstore.storage.portability.WriteContext; + +import java.nio.ByteBuffer; + +public class AssertingOffHeapValueHolderPortability extends OffHeapValueHolderPortability { + + public AssertingOffHeapValueHolderPortability(Serializer serializer) { + super(serializer); + } + + @Override + protected OffHeapValueHolder createLazyOffHeapValueHolder(long id, ByteBuffer byteBuffer, Serializer serializer, long creationTime, long expireTime, long lastAccessTime, WriteContext writeContext) { + return new AssertingOffHeapValueHolder<>(id, byteBuffer, serializer, creationTime, expireTime, lastAccessTime, writeContext); + } +} diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/tiering/CompoundCachingTierSPITest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/tiering/CompoundCachingTierSPITest.java similarity index 92% rename from impl/src/test/java/org/ehcache/impl/internal/store/tiering/CompoundCachingTierSPITest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/tiering/CompoundCachingTierSPITest.java index 1be86824b6..fe073da77f 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/tiering/CompoundCachingTierSPITest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/tiering/CompoundCachingTierSPITest.java @@ -18,10 +18,11 @@ import org.ehcache.config.ResourcePools; import org.ehcache.config.builders.ExpiryPolicyBuilder; -import org.ehcache.core.internal.store.StoreConfigurationImpl; import org.ehcache.config.units.EntryUnit; import org.ehcache.config.units.MemoryUnit; import org.ehcache.core.events.StoreEventDispatcher; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; +import org.ehcache.core.store.StoreConfigurationImpl; import org.ehcache.impl.copy.IdentityCopier; import org.ehcache.core.events.NullStoreEventDispatcher; import org.ehcache.impl.internal.sizeof.NoopSizeOfEngine; @@ -44,7 +45,7 @@ import static java.lang.ClassLoader.getSystemClassLoader; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; -import static org.ehcache.core.internal.service.ServiceLocator.dependencySet; +import static org.ehcache.core.spi.ServiceLocator.dependencySet; /** * This factory instantiates a CachingTier @@ -80,10 +81,10 @@ private CachingTier newCachingTier(Long capacity) { ClassLoader.getSystemClassLoader(), ExpiryPolicyBuilder.noExpiration(), buildResourcePools(capacity), 0, new JavaSerializer<>(getSystemClassLoader()), new JavaSerializer<>(getSystemClassLoader())); StoreEventDispatcher eventDispatcher = NullStoreEventDispatcher.nullStoreEventDispatcher(); - OffHeapStore offHeapStore = new OffHeapStore<>(config, SystemTimeSource.INSTANCE, eventDispatcher, 10 * 1024 * 1024); + OffHeapStore offHeapStore = new OffHeapStore<>(config, SystemTimeSource.INSTANCE, eventDispatcher, 10 * 1024 * 1024, new DefaultStatisticsService()); OffHeapStoreLifecycleHelper.init(offHeapStore); IdentityCopier copier = new IdentityCopier<>(); - OnHeapStore onHeapStore = new OnHeapStore<>(config, SystemTimeSource.INSTANCE, copier, copier, new NoopSizeOfEngine(), eventDispatcher); + OnHeapStore onHeapStore = new OnHeapStore<>(config, SystemTimeSource.INSTANCE, copier, copier, new NoopSizeOfEngine(), eventDispatcher, new DefaultStatisticsService()); CompoundCachingTier compoundCachingTier = new CompoundCachingTier<>(onHeapStore, offHeapStore); map.put(compoundCachingTier, offHeapStore); return compoundCachingTier; @@ -118,8 +119,8 @@ public Class getValueType() { } @Override - public ServiceConfiguration[] getServiceConfigurations() { - return new ServiceConfiguration[0]; + public ServiceConfiguration[] getServiceConfigurations() { + return new ServiceConfiguration[0]; } @Override diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/tiering/CompoundCachingTierTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/tiering/CompoundCachingTierTest.java similarity index 99% rename from impl/src/test/java/org/ehcache/impl/internal/store/tiering/CompoundCachingTierTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/tiering/CompoundCachingTierTest.java index ff5717af85..d94f97bba4 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/tiering/CompoundCachingTierTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/tiering/CompoundCachingTierTest.java @@ -32,9 +32,9 @@ import java.util.function.Function; import static java.util.Collections.EMPTY_LIST; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doAnswer; diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/tiering/TieredStoreFlushWhileShutdownTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/tiering/TieredStoreFlushWhileShutdownTest.java similarity index 92% rename from impl/src/test/java/org/ehcache/impl/internal/store/tiering/TieredStoreFlushWhileShutdownTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/tiering/TieredStoreFlushWhileShutdownTest.java index fe3caffc70..bede68ca2c 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/tiering/TieredStoreFlushWhileShutdownTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/tiering/TieredStoreFlushWhileShutdownTest.java @@ -20,6 +20,7 @@ import org.ehcache.config.EvictionAdvisor; import org.ehcache.config.ResourcePools; import org.ehcache.config.builders.ExpiryPolicyBuilder; +import org.ehcache.core.spi.service.CacheManagerProviderService; import org.ehcache.core.spi.service.DiskResourceService; import org.ehcache.expiry.ExpiryPolicy; import org.ehcache.impl.config.persistence.DefaultPersistenceConfiguration; @@ -28,7 +29,7 @@ import org.ehcache.impl.persistence.DefaultDiskResourceService; import org.ehcache.impl.internal.store.disk.OffHeapDiskStore; import org.ehcache.impl.internal.store.heap.OnHeapStore; -import org.ehcache.core.internal.service.ServiceLocator; +import org.ehcache.core.spi.ServiceLocator; import org.ehcache.core.spi.store.Store; import org.ehcache.impl.persistence.DefaultLocalPersistenceService; import org.ehcache.impl.serialization.JavaSerializer; @@ -38,15 +39,16 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import org.mockito.Answers; +import org.mockito.Mockito; import java.io.File; -import java.util.concurrent.TimeUnit; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; -import static org.ehcache.core.internal.service.ServiceLocator.dependencySet; +import static org.ehcache.core.spi.ServiceLocator.dependencySet; import static org.ehcache.test.MockitoUtil.mock; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; import static org.mockito.Mockito.when; public class TieredStoreFlushWhileShutdownTest { @@ -137,7 +139,7 @@ public CacheLoaderWriter getCacheLoaderWriter() { // Keep the creation time to make sure we have them at restart long[] creationTimes = new long[20]; for (int i = 0; i < 20; i++) { - creationTimes[i] = tieredStore.get(i).creationTime(TimeUnit.MILLISECONDS); + creationTimes[i] = tieredStore.get(i).creationTime(); } tieredStoreProvider.releaseStore(tieredStore); @@ -155,7 +157,7 @@ public CacheLoaderWriter getCacheLoaderWriter() { tieredStoreProvider.initStore(tieredStore); for(int i = 0; i < 20; i++) { - assertThat(tieredStore.get(i).creationTime(TimeUnit.MILLISECONDS), is(creationTimes[i])); + assertThat(tieredStore.get(i).creationTime(), is(creationTimes[i])); } } @@ -168,6 +170,7 @@ private ServiceLocator getServiceLocator(File location) throws Exception { dependencySet.with(diskResourceService); dependencySet.with(new OnHeapStore.Provider()); dependencySet.with(new OffHeapDiskStore.Provider()); + dependencySet.with(Mockito.mock(CacheManagerProviderService.class, Answers.RETURNS_DEEP_STUBS)); return dependencySet.build(); } } diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/tiering/TieredStoreMutatorTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/tiering/TieredStoreMutatorTest.java similarity index 98% rename from impl/src/test/java/org/ehcache/impl/internal/store/tiering/TieredStoreMutatorTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/tiering/TieredStoreMutatorTest.java index 62a345095b..bc3245bbfe 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/tiering/TieredStoreMutatorTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/tiering/TieredStoreMutatorTest.java @@ -19,8 +19,9 @@ import org.ehcache.config.builders.ExpiryPolicyBuilder; import org.ehcache.config.builders.ResourcePoolsBuilder; import org.ehcache.config.units.MemoryUnit; -import org.ehcache.core.internal.store.StoreConfigurationImpl; import org.ehcache.core.spi.store.Store; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; +import org.ehcache.core.store.StoreConfigurationImpl; import org.ehcache.spi.resilience.StoreAccessException; import org.ehcache.core.spi.store.tiering.AuthoritativeTier; import org.ehcache.core.spi.store.tiering.CachingTier; @@ -44,7 +45,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; /** * Tests for {@link TieredStore}. These tests are mainly to validate that @@ -179,7 +180,7 @@ public void setUp() throws Exception { // Here again, all parameters are useless, we only care about the beforeCompletingTheFault implementation CachingTier cachingTier = new OnHeapStore<>(config, SystemTimeSource.INSTANCE, StringCopier.copier(), StringCopier.copier(), new NoopSizeOfEngine(), NullStoreEventDispatcher. - nullStoreEventDispatcher()); + nullStoreEventDispatcher(), new DefaultStatisticsService()); tieredStore = new TieredStore<>(cachingTier, authoritativeTier); } diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/tiering/TieredStoreSPITest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/tiering/TieredStoreSPITest.java similarity index 91% rename from impl/src/test/java/org/ehcache/impl/internal/store/tiering/TieredStoreSPITest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/tiering/TieredStoreSPITest.java index 0a9c0e1830..f3605deb02 100755 --- a/impl/src/test/java/org/ehcache/impl/internal/store/tiering/TieredStoreSPITest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/tiering/TieredStoreSPITest.java @@ -21,12 +21,13 @@ import org.ehcache.config.ResourcePools; import org.ehcache.config.ResourceType; import org.ehcache.config.builders.ExpiryPolicyBuilder; -import org.ehcache.core.internal.store.StoreConfigurationImpl; import org.ehcache.config.SizedResourcePool; import org.ehcache.core.spi.service.DiskResourceService; import org.ehcache.config.units.EntryUnit; import org.ehcache.config.units.MemoryUnit; import org.ehcache.CachePersistenceException; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; +import org.ehcache.core.store.StoreConfigurationImpl; import org.ehcache.expiry.ExpiryPolicy; import org.ehcache.impl.internal.concurrent.ConcurrentHashMap; import org.ehcache.impl.copy.IdentityCopier; @@ -44,7 +45,7 @@ import org.ehcache.impl.serialization.JavaSerializer; import org.ehcache.internal.store.StoreFactory; import org.ehcache.internal.store.StoreSPITest; -import org.ehcache.core.internal.service.ServiceLocator; +import org.ehcache.core.spi.ServiceLocator; import org.ehcache.spi.service.ServiceProvider; import org.ehcache.core.spi.store.Store; import org.ehcache.core.spi.store.tiering.AuthoritativeTier; @@ -64,12 +65,11 @@ import java.util.Collection; import java.util.Map; import java.util.Set; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; import static org.ehcache.config.units.MemoryUnit.MB; -import static org.ehcache.core.internal.service.ServiceLocator.dependencySet; +import static org.ehcache.core.spi.ServiceLocator.dependencySet; import static org.ehcache.impl.config.store.disk.OffHeapDiskStoreConfiguration.DEFAULT_DISK_SEGMENTS; import static org.ehcache.impl.config.store.disk.OffHeapDiskStoreConfiguration.DEFAULT_WRITER_CONCURRENCY; import static org.ehcache.test.MockitoUtil.mock; @@ -130,7 +130,8 @@ private Store newStore(Long capacity, EvictionAdvisor defaultCopier = IdentityCopier.identityCopier(); - OnHeapStore onHeapStore = new OnHeapStore<>(config, timeSource, defaultCopier, defaultCopier, new NoopSizeOfEngine(), NullStoreEventDispatcher.nullStoreEventDispatcher()); + OnHeapStore onHeapStore = new OnHeapStore<>(config, timeSource, defaultCopier, defaultCopier, + new NoopSizeOfEngine(), NullStoreEventDispatcher.nullStoreEventDispatcher(), new DefaultStatisticsService()); try { CacheConfiguration cacheConfiguration = mock(CacheConfiguration.class); when(cacheConfiguration.getResourcePools()).thenReturn(newResourcePoolsBuilder().disk(1, MB, false).build()); @@ -147,11 +148,11 @@ private Store newStore(Long capacity, EvictionAdvisor(), - sizeInBytes); + sizeInBytes, new DefaultStatisticsService()); TieredStore tieredStore = new TieredStore<>(onHeapStore, diskStore); provider.registerStore(tieredStore, new CachingTier.Provider() { @Override - public CachingTier createCachingTier(final Store.Configuration storeConfig, final ServiceConfiguration... serviceConfigs) { + public CachingTier createCachingTier(final Store.Configuration storeConfig, final ServiceConfiguration... serviceConfigs) { throw new UnsupportedOperationException("Implement me!"); } @@ -166,7 +167,7 @@ public void initCachingTier(final CachingTier resource) { } @Override - public int rankCachingTier(Set> resourceTypes, Collection> serviceConfigs) { + public int rankCachingTier(Set> resourceTypes, Collection> serviceConfigs) { throw new UnsupportedOperationException("Implement me!"); } @@ -181,7 +182,7 @@ public void stop() { } }, new AuthoritativeTier.Provider() { @Override - public AuthoritativeTier createAuthoritativeTier(final Store.Configuration storeConfig, final ServiceConfiguration... serviceConfigs) { + public AuthoritativeTier createAuthoritativeTier(final Store.Configuration storeConfig, final ServiceConfiguration... serviceConfigs) { throw new UnsupportedOperationException("Implement me!"); } @@ -196,7 +197,7 @@ public void initAuthoritativeTier(final AuthoritativeTier resource) { } @Override - public int rankAuthority(ResourceType authorityResource, Collection> serviceConfigs) { + public int rankAuthority(ResourceType authorityResource, Collection> serviceConfigs) { throw new UnsupportedOperationException("Implement me!"); } @@ -229,22 +230,22 @@ public String get() { } @Override - public long creationTime(TimeUnit unit) { + public long creationTime() { return creationTime; } @Override - public long expirationTime(TimeUnit unit) { + public long expirationTime() { return 0; } @Override - public boolean isExpired(long expirationTime, TimeUnit unit) { + public boolean isExpired(long expirationTime) { return false; } @Override - public long lastAccessTime(TimeUnit unit) { + public long lastAccessTime() { return 0; } @@ -266,8 +267,8 @@ public Class getValueType() { } @Override - public ServiceConfiguration[] getServiceConfigurations() { - return new ServiceConfiguration[0]; + public ServiceConfiguration[] getServiceConfigurations() { + return new ServiceConfiguration[0]; } @Override @@ -323,7 +324,7 @@ private ResourcePools buildResourcePools(Comparable capacityConstraint) { public static class FakeCachingTierProvider implements CachingTier.Provider { @Override @SuppressWarnings("unchecked") - public CachingTier createCachingTier(Store.Configuration storeConfig, ServiceConfiguration... serviceConfigs) { + public CachingTier createCachingTier(Store.Configuration storeConfig, ServiceConfiguration... serviceConfigs) { return mock(CachingTier.class); } @@ -338,7 +339,7 @@ public void initCachingTier(CachingTier resource) { } @Override - public int rankCachingTier(Set> resourceTypes, Collection> serviceConfigs) { + public int rankCachingTier(Set> resourceTypes, Collection> serviceConfigs) { throw new UnsupportedOperationException(); } @@ -356,7 +357,7 @@ public void stop() { public static class FakeAuthoritativeTierProvider implements AuthoritativeTier.Provider { @Override @SuppressWarnings("unchecked") - public AuthoritativeTier createAuthoritativeTier(Store.Configuration storeConfig, ServiceConfiguration... serviceConfigs) { + public AuthoritativeTier createAuthoritativeTier(Store.Configuration storeConfig, ServiceConfiguration... serviceConfigs) { return mock(AuthoritativeTier.class); } @@ -371,7 +372,7 @@ public void initAuthoritativeTier(AuthoritativeTier resource) { } @Override - public int rankAuthority(ResourceType authorityResource, Collection> serviceConfigs) { + public int rankAuthority(ResourceType authorityResource, Collection> serviceConfigs) { throw new UnsupportedOperationException(); } diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/tiering/TieredStoreTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/tiering/TieredStoreTest.java similarity index 96% rename from impl/src/test/java/org/ehcache/impl/internal/store/tiering/TieredStoreTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/tiering/TieredStoreTest.java index a0b3e79b1f..63b1601e33 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/tiering/TieredStoreTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/tiering/TieredStoreTest.java @@ -20,23 +20,25 @@ import org.ehcache.config.ResourceType; import org.ehcache.config.SizedResourcePool; import org.ehcache.core.exceptions.StorePassThroughException; -import org.ehcache.core.internal.service.ServiceLocator; +import org.ehcache.core.spi.ServiceLocator; +import org.ehcache.core.spi.service.CacheManagerProviderService; import org.ehcache.core.spi.service.DiskResourceService; +import org.ehcache.core.spi.service.StatisticsService; import org.ehcache.core.spi.store.Store; import org.ehcache.core.spi.store.Store.RemoveStatus; import org.ehcache.core.spi.store.Store.ReplaceStatus; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; import org.ehcache.spi.resilience.StoreAccessException; import org.ehcache.core.spi.store.tiering.AuthoritativeTier; import org.ehcache.core.spi.store.tiering.CachingTier; import org.ehcache.impl.internal.store.heap.OnHeapStore; import org.ehcache.impl.internal.store.offheap.OffHeapStore; import org.ehcache.spi.service.Service; -import org.ehcache.spi.service.ServiceConfiguration; import org.ehcache.spi.service.ServiceProvider; import org.hamcrest.Matchers; -import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.mockito.Answers; import org.mockito.ArgumentMatchers; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -54,13 +56,12 @@ import java.util.Set; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; -import java.util.concurrent.TimeUnit; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; -import static org.ehcache.core.internal.service.ServiceLocator.dependencySet; +import static org.ehcache.core.spi.ServiceLocator.dependencySet; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.core.Is.is; @@ -102,7 +103,7 @@ public void testGetHitsCachingTier() throws Exception { TieredStore tieredStore = new TieredStore<>(numberCachingTier, numberAuthoritativeTier); - assertThat(tieredStore.get(1).get(), Matchers.equalTo("one")); + assertThat(tieredStore.get(1).get(), Matchers.equalTo("one")); verify(numberAuthoritativeTier, times(0)).getAndFault(any(Number.class)); } @@ -120,7 +121,7 @@ public void testGetHitsAuthoritativeTier() throws Exception { TieredStore tieredStore = new TieredStore<>(numberCachingTier, numberAuthoritativeTier); - assertThat(tieredStore.get(1).get(), Matchers.equalTo("one")); + assertThat(tieredStore.get(1).get(), Matchers.equalTo("one")); verify(numberCachingTier, times(1)).getOrComputeIfAbsent(eq(1), any(Function.class)); verify(numberAuthoritativeTier, times(1)).getAndFault(any(Number.class)); @@ -561,7 +562,7 @@ public void CachingTierDoesNotSeeAnyOperationDuringClear() throws StoreAccessExc barrier.await(); t.join(); verify(stringCachingTier, never()).getOrComputeIfAbsent( - ArgumentMatchers.any(), ArgumentMatchers.>>any()); + ArgumentMatchers.any(), ArgumentMatchers.any()); } @Test @@ -580,7 +581,7 @@ public void testReleaseStoreFlushes() throws Exception { Set> singleton = Collections.>singleton( ResourceType.Core.HEAP); when(onHeapStoreProvider.rankCachingTier(eq(singleton), any(Collection.class))).thenReturn(1); when(onHeapStoreProvider.createCachingTier(any(Store.Configuration.class), - ArgumentMatchers.[]>any())) + ArgumentMatchers.any())) .thenReturn(stringCachingTier); SizedResourcePool offHeapPool = mock(SizedResourcePool.class); @@ -589,7 +590,7 @@ public void testReleaseStoreFlushes() throws Exception { OffHeapStore.Provider offHeapStoreProvider = mock(OffHeapStore.Provider.class); when(offHeapStoreProvider.rankAuthority(eq(ResourceType.Core.OFFHEAP), any(Collection.class))).thenReturn(1); when(offHeapStoreProvider.createAuthoritativeTier( - any(Store.Configuration.class), ArgumentMatchers.[]>any())) + any(Store.Configuration.class), ArgumentMatchers.any())) .thenReturn(stringAuthoritativeTier); Store.Configuration configuration = mock(Store.Configuration.class); @@ -604,6 +605,7 @@ public void testReleaseStoreFlushes() throws Exception { when(serviceProvider.getService(OffHeapStore.Provider.class)).thenReturn(offHeapStoreProvider); when(serviceProvider.getServicesOfType(AuthoritativeTier.Provider.class)).thenReturn(authorities); when(serviceProvider.getServicesOfType(CachingTier.Provider.class)).thenReturn(cachingTiers); + when(serviceProvider.getService(StatisticsService.class)).thenReturn(new DefaultStatisticsService()); tieredStoreProvider.start(serviceProvider); final Store tieredStore = tieredStoreProvider.createStore(configuration); @@ -615,7 +617,8 @@ public void testReleaseStoreFlushes() throws Exception { @Test public void testRank() throws Exception { TieredStore.Provider provider = new TieredStore.Provider(); - ServiceLocator serviceLocator = dependencySet().with(provider).with(mock(DiskResourceService.class)).build(); + ServiceLocator serviceLocator = dependencySet().with(provider).with(mock(DiskResourceService.class)) + .with(mock(CacheManagerProviderService.class, Answers.RETURNS_DEEP_STUBS)).build(); serviceLocator.startAllServices(); assertRank(provider, 0, ResourceType.Core.DISK); @@ -667,9 +670,9 @@ public void testGetAuthoritativeTierProvider() { } private void assertRank(final Store.Provider provider, final int expectedRank, final ResourceType... resources) { - Assert.assertThat(provider.rank( + assertThat(provider.rank( new HashSet<>(Arrays.asList(resources)), - Collections.>emptyList()), + Collections.emptyList()), Matchers.is(expectedRank)); } @@ -686,22 +689,22 @@ public CharSequence get() { } @Override - public long creationTime(TimeUnit unit) { + public long creationTime() { return 0; } @Override - public long expirationTime(TimeUnit unit) { + public long expirationTime() { return 0; } @Override - public boolean isExpired(long expirationTime, TimeUnit unit) { + public boolean isExpired(long expirationTime) { return false; } @Override - public long lastAccessTime(TimeUnit unit) { + public long lastAccessTime() { return 0; } diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/tiering/TieredStoreWith3TiersSPITest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/tiering/TieredStoreWith3TiersSPITest.java similarity index 92% rename from impl/src/test/java/org/ehcache/impl/internal/store/tiering/TieredStoreWith3TiersSPITest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/store/tiering/TieredStoreWith3TiersSPITest.java index 0ebcd8df15..6d556d805f 100755 --- a/impl/src/test/java/org/ehcache/impl/internal/store/tiering/TieredStoreWith3TiersSPITest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/store/tiering/TieredStoreWith3TiersSPITest.java @@ -21,13 +21,14 @@ import org.ehcache.config.ResourcePools; import org.ehcache.config.ResourceType; import org.ehcache.config.builders.ExpiryPolicyBuilder; -import org.ehcache.core.internal.store.StoreConfigurationImpl; import org.ehcache.config.SizedResourcePool; import org.ehcache.core.spi.service.DiskResourceService; import org.ehcache.config.units.EntryUnit; import org.ehcache.config.units.MemoryUnit; import org.ehcache.core.events.StoreEventDispatcher; import org.ehcache.CachePersistenceException; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; +import org.ehcache.core.store.StoreConfigurationImpl; import org.ehcache.expiry.ExpiryPolicy; import org.ehcache.impl.internal.concurrent.ConcurrentHashMap; import org.ehcache.impl.copy.IdentityCopier; @@ -47,7 +48,7 @@ import org.ehcache.impl.serialization.JavaSerializer; import org.ehcache.internal.store.StoreFactory; import org.ehcache.internal.store.StoreSPITest; -import org.ehcache.core.internal.service.ServiceLocator; +import org.ehcache.core.spi.ServiceLocator; import org.ehcache.spi.service.ServiceProvider; import org.ehcache.core.spi.store.Store; import org.ehcache.core.spi.store.tiering.AuthoritativeTier; @@ -67,11 +68,10 @@ import java.util.Collection; import java.util.Map; import java.util.Set; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; -import static org.ehcache.core.internal.service.ServiceLocator.dependencySet; +import static org.ehcache.core.spi.ServiceLocator.dependencySet; import static org.ehcache.impl.config.store.disk.OffHeapDiskStoreConfiguration.DEFAULT_DISK_SEGMENTS; import static org.ehcache.impl.config.store.disk.OffHeapDiskStoreConfiguration.DEFAULT_WRITER_CONCURRENCY; import static org.ehcache.test.MockitoUtil.mock; @@ -133,12 +133,13 @@ private Store newStore(Long capacity, EvictionAdvisor defaultCopier = new IdentityCopier<>(); StoreEventDispatcher noOpEventDispatcher = NullStoreEventDispatcher.nullStoreEventDispatcher(); - final OnHeapStore onHeapStore = new OnHeapStore<>(config, timeSource, defaultCopier, defaultCopier, new NoopSizeOfEngine(), noOpEventDispatcher); + final OnHeapStore onHeapStore = new OnHeapStore<>(config, timeSource, defaultCopier, defaultCopier, + new NoopSizeOfEngine(), noOpEventDispatcher, new DefaultStatisticsService()); SizedResourcePool offheapPool = config.getResourcePools().getPoolForResource(ResourceType.Core.OFFHEAP); long offheapSize = ((MemoryUnit) offheapPool.getUnit()).toBytes(offheapPool.getSize()); - final OffHeapStore offHeapStore = new OffHeapStore<>(config, timeSource, noOpEventDispatcher, offheapSize); + final OffHeapStore offHeapStore = new OffHeapStore<>(config, timeSource, noOpEventDispatcher, offheapSize, new DefaultStatisticsService()); try { CacheConfiguration cacheConfiguration = mock(CacheConfiguration.class); @@ -155,7 +156,7 @@ private Store newStore(Long capacity, EvictionAdvisor(), - diskSize); + diskSize, new DefaultStatisticsService()); CompoundCachingTier compoundCachingTier = new CompoundCachingTier<>(onHeapStore, offHeapStore); @@ -164,7 +165,7 @@ private Store newStore(Long capacity, EvictionAdvisor CachingTier createCachingTier(final Store.Configuration storeConfig, final ServiceConfiguration... serviceConfigs) { + public CachingTier createCachingTier(final Store.Configuration storeConfig, final ServiceConfiguration... serviceConfigs) { throw new UnsupportedOperationException("Implement me!"); } @@ -181,7 +182,7 @@ public void initCachingTier(final CachingTier resource) { } @Override - public int rankCachingTier(Set> resourceTypes, Collection> serviceConfigs) { + public int rankCachingTier(Set> resourceTypes, Collection> serviceConfigs) { throw new UnsupportedOperationException("Implement me!"); } @@ -196,7 +197,7 @@ public void stop() { } }, new AuthoritativeTier.Provider() { @Override - public AuthoritativeTier createAuthoritativeTier(final Store.Configuration storeConfig, final ServiceConfiguration... serviceConfigs) { + public AuthoritativeTier createAuthoritativeTier(final Store.Configuration storeConfig, final ServiceConfiguration... serviceConfigs) { throw new UnsupportedOperationException("Implement me!"); } @@ -211,7 +212,7 @@ public void initAuthoritativeTier(final AuthoritativeTier resource) { } @Override - public int rankAuthority(ResourceType authorityResource, Collection> serviceConfigs) { + public int rankAuthority(ResourceType authorityResource, Collection> serviceConfigs) { throw new UnsupportedOperationException("Implement me!"); } @@ -244,22 +245,22 @@ public String get() { } @Override - public long creationTime(TimeUnit unit) { + public long creationTime() { return creationTime; } @Override - public long expirationTime(TimeUnit unit) { + public long expirationTime() { return 0; } @Override - public boolean isExpired(long expirationTime, TimeUnit unit) { + public boolean isExpired(long expirationTime) { return false; } @Override - public long lastAccessTime(TimeUnit unit) { + public long lastAccessTime() { return 0; } @@ -281,8 +282,8 @@ public Class getValueType() { } @Override - public ServiceConfiguration[] getServiceConfigurations() { - return new ServiceConfiguration[0]; + public ServiceConfiguration[] getServiceConfigurations() { + return new ServiceConfiguration[0]; } @Override @@ -349,7 +350,7 @@ private ResourcePools buildResourcePools(Long capacityConstraint) { public static class FakeCachingTierProvider implements CachingTier.Provider { @Override @SuppressWarnings("unchecked") - public CachingTier createCachingTier(Store.Configuration storeConfig, ServiceConfiguration... serviceConfigs) { + public CachingTier createCachingTier(Store.Configuration storeConfig, ServiceConfiguration... serviceConfigs) { return mock(CachingTier.class); } @@ -364,7 +365,7 @@ public void initCachingTier(CachingTier resource) { } @Override - public int rankCachingTier(Set> resourceTypes, Collection> serviceConfigs) { + public int rankCachingTier(Set> resourceTypes, Collection> serviceConfigs) { throw new UnsupportedOperationException(); } @@ -382,7 +383,7 @@ public void stop() { public static class FakeAuthoritativeTierProvider implements AuthoritativeTier.Provider { @Override @SuppressWarnings("unchecked") - public AuthoritativeTier createAuthoritativeTier(Store.Configuration storeConfig, ServiceConfiguration... serviceConfigs) { + public AuthoritativeTier createAuthoritativeTier(Store.Configuration storeConfig, ServiceConfiguration... serviceConfigs) { return mock(AuthoritativeTier.class); } @@ -397,7 +398,7 @@ public void initAuthoritativeTier(AuthoritativeTier resource) { } @Override - public int rankAuthority(ResourceType authorityResource, Collection> serviceConfigs) { + public int rankAuthority(ResourceType authorityResource, Collection> serviceConfigs) { throw new UnsupportedOperationException(); } diff --git a/impl/src/test/java/org/ehcache/impl/internal/util/ByteBufferInputStreamTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/util/ByteBufferInputStreamTest.java similarity index 94% rename from impl/src/test/java/org/ehcache/impl/internal/util/ByteBufferInputStreamTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/util/ByteBufferInputStreamTest.java index 6cb6f8eb5b..9ee202286e 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/util/ByteBufferInputStreamTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/util/ByteBufferInputStreamTest.java @@ -18,11 +18,12 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.util.Random; -import org.junit.Assert; + +import org.ehcache.core.util.ByteBufferInputStream; import org.junit.Test; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; public class ByteBufferInputStreamTest { @@ -106,7 +107,7 @@ public void testZeroOffsetAndMaximalLength() { byte[] read = new byte[32]; stream.read(read, 0, 32); for (int i = 0; i < read.length; i++) { - Assert.assertThat(read[i], is((byte) i)); + assertThat(read[i], is((byte) i)); } } @@ -116,7 +117,7 @@ public void testMaximalOffsetAndZeroLength() { byte[] read = new byte[32]; stream.read(read, 32, 0); for (int i = 0; i < read.length; i++) { - Assert.assertThat(read[i], is((byte) 0)); + assertThat(read[i], is((byte) 0)); } } @@ -137,13 +138,13 @@ public void testLegalOffsetAndLegalLength() { byte[] read = new byte[32]; stream.read(read, 4, 16); for (int i = 0; i < 4; i++) { - Assert.assertThat(read[i], is((byte) 0)); + assertThat(read[i], is((byte) 0)); } for (int i = 4; i < 20; i++) { - Assert.assertThat(read[i], is((byte) (i - 4))); + assertThat(read[i], is((byte) (i - 4))); } for (int i = 20; i < read.length; i++) { - Assert.assertThat(read[i], is((byte) 0)); + assertThat(read[i], is((byte) 0)); } } @@ -152,9 +153,9 @@ public void testMinimalOffsetAndMaximalLength() { ByteBufferInputStream stream = createStream(); byte[] read = new byte[32]; stream.read(read, 1, 31); - Assert.assertThat(read[0], is((byte) 0)); + assertThat(read[0], is((byte) 0)); for (int i = 1; i < read.length; i++) { - Assert.assertThat(read[i], is((byte) (i - 1))); + assertThat(read[i], is((byte) (i - 1))); } } @@ -164,7 +165,7 @@ public void testZeroOffsetAndZeroLength() { byte[] read = new byte[32]; stream.read(read, 0, 0); for (int i = 0; i < read.length; i++) { - Assert.assertThat(read[i], is((byte) 0)); + assertThat(read[i], is((byte) 0)); } } diff --git a/impl/src/test/java/org/ehcache/impl/internal/util/FileExistenceMatchers.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/util/FileExistenceMatchers.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/internal/util/FileExistenceMatchers.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/util/FileExistenceMatchers.java diff --git a/impl/src/test/java/org/ehcache/impl/internal/util/FileExistenceMatchersTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/util/FileExistenceMatchersTest.java similarity index 98% rename from impl/src/test/java/org/ehcache/impl/internal/util/FileExistenceMatchersTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/util/FileExistenceMatchersTest.java index 67f03f6464..eb2d8053f0 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/util/FileExistenceMatchersTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/util/FileExistenceMatchersTest.java @@ -26,7 +26,7 @@ import static org.ehcache.impl.internal.util.FileExistenceMatchers.containsCacheDirectory; import static org.ehcache.impl.internal.util.FileExistenceMatchers.isLocked; import static org.hamcrest.CoreMatchers.not; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; /** * @author Henri Tremblay diff --git a/impl/src/test/java/org/ehcache/impl/internal/util/Matchers.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/util/Matchers.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/internal/util/Matchers.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/util/Matchers.java diff --git a/core/src/test/java/org/ehcache/core/internal/util/PacerTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/util/PacerTest.java similarity index 97% rename from core/src/test/java/org/ehcache/core/internal/util/PacerTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/util/PacerTest.java index f026db6bd0..9ee33cd86e 100644 --- a/core/src/test/java/org/ehcache/core/internal/util/PacerTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/util/PacerTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.ehcache.core.internal.util; +package org.ehcache.impl.internal.util; import org.ehcache.core.spi.time.TimeSource; import org.junit.Before; diff --git a/impl/src/test/java/org/ehcache/impl/internal/util/StatisticsTestUtils.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/util/StatisticsTestUtils.java similarity index 94% rename from impl/src/test/java/org/ehcache/impl/internal/util/StatisticsTestUtils.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/util/StatisticsTestUtils.java index 745cbd30cc..6e1da8bf8d 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/util/StatisticsTestUtils.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/util/StatisticsTestUtils.java @@ -21,7 +21,6 @@ import org.hamcrest.Factory; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; -import org.junit.Assert; import org.terracotta.context.ContextManager; import org.terracotta.context.TreeNode; import org.terracotta.statistics.OperationStatistic; @@ -31,6 +30,8 @@ import java.util.EnumSet; import java.util.List; +import static org.hamcrest.MatcherAssert.assertThat; + /** * StatisticsTestUtils */ @@ -62,18 +63,18 @@ public static > void validateStats(final Store store, fi final OperationStatistic operationStatistic = getOperationStatistic(store, statsClass); for (final E statId : changed) { - Assert.assertThat(String.format("Value for %s.%s", statId.getDeclaringClass().getName(), statId.name()), + assertThat(String.format("Value for %s.%s", statId.getDeclaringClass().getName(), statId.name()), getStatistic(operationStatistic, statId), StatisticMatcher.equalTo(1L)); } for (final E statId : unchanged) { - Assert.assertThat(String.format("Value for %s.%s", statId.getDeclaringClass().getName(), statId.name()), + assertThat(String.format("Value for %s.%s", statId.getDeclaringClass().getName(), statId.name()), getStatistic(operationStatistic, statId), StatisticMatcher.equalTo(0L)); } } public static > void validateStat(final Store store, E outcome, long count) { OperationStatistic operationStatistic = getOperationStatistic(store, outcome.getDeclaringClass()); - Assert.assertThat(getStatistic(operationStatistic, outcome), StatisticMatcher.equalTo(count)); + assertThat(getStatistic(operationStatistic, outcome), StatisticMatcher.equalTo(count)); } /** diff --git a/impl/src/test/java/org/ehcache/impl/internal/util/ThreadFactoryUtilTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/util/ThreadFactoryUtilTest.java similarity index 97% rename from impl/src/test/java/org/ehcache/impl/internal/util/ThreadFactoryUtilTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/util/ThreadFactoryUtilTest.java index 33a029737d..e6a14773d7 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/util/ThreadFactoryUtilTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/internal/util/ThreadFactoryUtilTest.java @@ -20,9 +20,9 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicReference; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.not; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; /** * @author Ludovic Orban diff --git a/impl/src/test/java/org/ehcache/impl/internal/util/UnmatchedResourceType.java b/ehcache-impl/src/test/java/org/ehcache/impl/internal/util/UnmatchedResourceType.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/internal/util/UnmatchedResourceType.java rename to ehcache-impl/src/test/java/org/ehcache/impl/internal/util/UnmatchedResourceType.java diff --git a/impl/src/test/java/org/ehcache/impl/persistence/DefaultDiskResourceServiceTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/persistence/DefaultDiskResourceServiceTest.java similarity index 82% rename from impl/src/test/java/org/ehcache/impl/persistence/DefaultDiskResourceServiceTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/persistence/DefaultDiskResourceServiceTest.java index 41486008cb..1825d9e7c6 100644 --- a/impl/src/test/java/org/ehcache/impl/persistence/DefaultDiskResourceServiceTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/persistence/DefaultDiskResourceServiceTest.java @@ -22,13 +22,12 @@ import org.ehcache.spi.service.ServiceProvider; import org.junit.After; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; import org.junit.experimental.runners.Enclosed; -import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -41,9 +40,6 @@ public class DefaultDiskResourceServiceTest { public static abstract class AbstractDefaultDiskResourceServiceTest { - @Rule - public ExpectedException expectedException = ExpectedException.none(); - protected DefaultDiskResourceService service = new DefaultDiskResourceService(); @SuppressWarnings("unchecked") protected ServiceProvider serviceProvider = mock(ServiceProvider.class); @@ -108,9 +104,8 @@ public void testDestroy() throws CachePersistenceException { @Test public void testCreatePersistenceContextWithin() throws CachePersistenceException { - expectedException.expect(CachePersistenceException.class); - expectedException.expectMessage("Unknown space: null"); - service.createPersistenceContextWithin(null, "test"); + assertThatThrownBy(() -> service.createPersistenceContextWithin(null, "test")) + .isInstanceOf(CachePersistenceException.class).withFailMessage("Unknown space: null"); } @Test @@ -121,16 +116,14 @@ public void testGetPersistenceSpaceIdentifier() throws CachePersistenceException @Test public void testGetStateRepositoryWithin() throws CachePersistenceException { - expectedException.expect(CachePersistenceException.class); - expectedException.expectMessage("Unknown space: null"); - assertThat(service.getStateRepositoryWithin(null, "test")).isNull(); + assertThatThrownBy(() -> service.getStateRepositoryWithin(null, "test")) + .isInstanceOf(CachePersistenceException.class).withFailMessage("Unknown space: null"); } @Test public void testReleasePersistenceSpaceIdentifier() throws CachePersistenceException { - expectedException.expect(CachePersistenceException.class); - expectedException.expectMessage("Unknown space: null"); - assertThat(service.getStateRepositoryWithin(null, "test")).isNull(); + assertThatThrownBy(() -> service.getStateRepositoryWithin(null, "test")) + .isInstanceOf(CachePersistenceException.class).withFailMessage("Unknown space: null"); } } diff --git a/impl/src/test/java/org/ehcache/impl/persistence/DefaultLocalPersistenceServiceTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/persistence/DefaultLocalPersistenceServiceTest.java similarity index 94% rename from impl/src/test/java/org/ehcache/impl/persistence/DefaultLocalPersistenceServiceTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/persistence/DefaultLocalPersistenceServiceTest.java index ad22995665..4281cc9345 100644 --- a/impl/src/test/java/org/ehcache/impl/persistence/DefaultLocalPersistenceServiceTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/persistence/DefaultLocalPersistenceServiceTest.java @@ -22,7 +22,6 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import java.io.File; @@ -32,16 +31,15 @@ import static org.ehcache.impl.internal.util.FileExistenceMatchers.isLocked; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasProperty; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.fail; import static org.junit.Assume.assumeTrue; public class DefaultLocalPersistenceServiceTest { - @Rule - public final ExpectedException expectedException = ExpectedException.none(); - @Rule public final TemporaryFolder folder = new TemporaryFolder(); @@ -139,7 +137,7 @@ public void testExclusiveLock() throws IOException { // We should not be able to lock the same directory twice // And we should receive a meaningful exception about it - expectedException.expectMessage("Persistence directory already locked by this process: " + testFolder.getAbsolutePath()); - service2.start(null); + RuntimeException thrown = assertThrows(RuntimeException.class, () -> service2.start(null)); + assertThat(thrown, hasProperty("message", is("Persistence directory already locked by this process: " + testFolder.getAbsolutePath()))); } } diff --git a/impl/src/test/java/org/ehcache/impl/persistence/FileBasedStateRepositoryTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/persistence/FileBasedStateRepositoryTest.java similarity index 98% rename from impl/src/test/java/org/ehcache/impl/persistence/FileBasedStateRepositoryTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/persistence/FileBasedStateRepositoryTest.java index 87ebfce900..55ba86bb69 100644 --- a/impl/src/test/java/org/ehcache/impl/persistence/FileBasedStateRepositoryTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/persistence/FileBasedStateRepositoryTest.java @@ -28,8 +28,8 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; /** * FileBasedStateRepositoryTest diff --git a/impl/src/test/java/org/ehcache/impl/serialization/AddedFieldTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/AddedFieldTest.java similarity index 96% rename from impl/src/test/java/org/ehcache/impl/serialization/AddedFieldTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/serialization/AddedFieldTest.java index 68b3d2369a..6fb918d639 100644 --- a/impl/src/test/java/org/ehcache/impl/serialization/AddedFieldTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/AddedFieldTest.java @@ -18,7 +18,6 @@ import org.ehcache.spi.serialization.StatefulSerializer; import org.hamcrest.core.Is; -import org.junit.Assert; import org.junit.Test; import java.io.Externalizable; @@ -32,6 +31,7 @@ import static org.ehcache.impl.serialization.SerializerTestUtilities.newClassName; import static org.ehcache.impl.serialization.SerializerTestUtilities.popTccl; import static org.ehcache.impl.serialization.SerializerTestUtilities.pushTccl; +import static org.hamcrest.MatcherAssert.assertThat; /** * @@ -51,7 +51,7 @@ public void addingSerializableField() throws Exception { pushTccl(createClassNameRewritingLoader(A_read.class, IncompatibleSerializable_read.class)); try { Serializable out = serializer.read(encodedA); - Assert.assertThat(out.getClass().getField("bar").getInt(out), Is.is(4)); + assertThat(out.getClass().getField("bar").getInt(out), Is.is(4)); } finally { popTccl(); } @@ -69,7 +69,7 @@ public void addingExternalizableField() throws Exception { pushTccl(createClassNameRewritingLoader(B_read.class)); try { Serializable out = serializer.read(encodedA); - Assert.assertThat(out.getClass().getField("bar").getInt(out), Is.is(4)); + assertThat(out.getClass().getField("bar").getInt(out), Is.is(4)); } finally { popTccl(); } diff --git a/impl/src/test/java/org/ehcache/impl/serialization/AddedSuperClassTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/AddedSuperClassTest.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/serialization/AddedSuperClassTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/serialization/AddedSuperClassTest.java diff --git a/impl/src/test/java/org/ehcache/impl/serialization/ArrayPackageScopeTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/ArrayPackageScopeTest.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/serialization/ArrayPackageScopeTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/serialization/ArrayPackageScopeTest.java diff --git a/impl/src/test/java/org/ehcache/impl/serialization/BasicSerializationTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/BasicSerializationTest.java similarity index 93% rename from impl/src/test/java/org/ehcache/impl/serialization/BasicSerializationTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/serialization/BasicSerializationTest.java index b18d69ccc5..2fbbd3755b 100644 --- a/impl/src/test/java/org/ehcache/impl/serialization/BasicSerializationTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/BasicSerializationTest.java @@ -32,6 +32,8 @@ import org.junit.Assert; import org.junit.Test; +import static org.hamcrest.MatcherAssert.assertThat; + /** * * @author cdennis @@ -79,8 +81,8 @@ public void testPrimitiveClasses() throws ClassNotFoundException { Class[] out = (Class[]) s.read(s.serialize(PRIMITIVE_CLASSES)); - Assert.assertThat(out, IsNot.not(IsSame.sameInstance(PRIMITIVE_CLASSES))); - Assert.assertThat(out, IsEqual.equalTo(PRIMITIVE_CLASSES)); + assertThat(out, IsNot.not(IsSame.sameInstance(PRIMITIVE_CLASSES))); + assertThat(out, IsEqual.equalTo(PRIMITIVE_CLASSES)); } @Test @@ -94,8 +96,8 @@ public void testProxyInstance() throws ClassNotFoundException { Object proxy = s.read(s.serialize((Serializable) Proxy.newProxyInstance(BasicSerializationTest.class.getClassLoader(), new Class[]{Foo.class, Bar.class}, new Handler(foo, bar)))); - Assert.assertThat(((Foo) proxy).foo(), Is.is(foo)); - Assert.assertThat(((Bar) proxy).bar(), Is.is(bar)); + assertThat(((Foo) proxy).foo(), Is.is(foo)); + assertThat(((Bar) proxy).bar(), Is.is(bar)); } interface Foo { diff --git a/impl/src/test/java/org/ehcache/impl/serialization/ByteArraySerializerTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/ByteArraySerializerTest.java similarity index 97% rename from impl/src/test/java/org/ehcache/impl/serialization/ByteArraySerializerTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/serialization/ByteArraySerializerTest.java index ff81e9c78f..8a1662781e 100644 --- a/impl/src/test/java/org/ehcache/impl/serialization/ByteArraySerializerTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/ByteArraySerializerTest.java @@ -24,8 +24,8 @@ import java.util.Arrays; import java.util.Random; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; public class ByteArraySerializerTest { @@ -76,4 +76,4 @@ public void testReadThrowsOnNullInput() throws ClassNotFoundException { public void testSerializeThrowsOnNullInput() { new ByteArraySerializer().serialize(null); } -} \ No newline at end of file +} diff --git a/impl/src/test/java/org/ehcache/impl/serialization/CharSerializerTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/CharSerializerTest.java similarity index 96% rename from impl/src/test/java/org/ehcache/impl/serialization/CharSerializerTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/serialization/CharSerializerTest.java index effd7aa568..a2699ff34d 100644 --- a/impl/src/test/java/org/ehcache/impl/serialization/CharSerializerTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/CharSerializerTest.java @@ -22,8 +22,8 @@ import java.util.Random; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; public class CharSerializerTest { @@ -52,4 +52,4 @@ public void testReadThrowsOnNullInput() throws ClassNotFoundException { public void testSerializeThrowsOnNullInput() { new CharSerializer().serialize(null); } -} \ No newline at end of file +} diff --git a/impl/src/test/java/org/ehcache/impl/serialization/CompactJavaSerializerClassLoaderTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/CompactJavaSerializerClassLoaderTest.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/serialization/CompactJavaSerializerClassLoaderTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/serialization/CompactJavaSerializerClassLoaderTest.java diff --git a/impl/src/test/java/org/ehcache/impl/serialization/CompactJavaSerializerClassUnloadingTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/CompactJavaSerializerClassUnloadingTest.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/serialization/CompactJavaSerializerClassUnloadingTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/serialization/CompactJavaSerializerClassUnloadingTest.java diff --git a/ehcache-impl/src/test/java/org/ehcache/impl/serialization/CompactJavaSerializerTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/CompactJavaSerializerTest.java new file mode 100644 index 0000000000..9de204d921 --- /dev/null +++ b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/CompactJavaSerializerTest.java @@ -0,0 +1,86 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.impl.serialization; + +import org.ehcache.spi.persistence.StateHolder; +import org.ehcache.spi.persistence.StateRepository; +import org.junit.Test; +import org.mockito.stubbing.Answer; + +import java.io.ObjectStreamClass; +import java.nio.ByteBuffer; +import java.util.Date; +import java.util.concurrent.atomic.AtomicBoolean; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.collection.IsCollectionWithSize.hasSize; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +public class CompactJavaSerializerTest { + + @Test + public void testStateHolderFailureRereadBehavior() throws ClassNotFoundException { + StateHolder stateMap = spy(new TransientStateHolder<>()); + StateRepository stateRepository = mock(StateRepository.class); + when(stateRepository.getPersistentStateHolder(eq("CompactJavaSerializer-ObjectStreamClassIndex"), eq(Integer.class), eq(ObjectStreamClass.class), any(), any())).thenReturn(stateMap); + + AtomicBoolean failing = new AtomicBoolean(); + Answer optionalFailure = invocation -> { + try { + return invocation.callRealMethod(); + } finally { + if (failing.get()) { + throw new RuntimeException(); + } + } + }; + + doAnswer(optionalFailure).when(stateMap).entrySet(); + doAnswer(optionalFailure).when(stateMap).get(any()); + doAnswer(optionalFailure).when(stateMap).putIfAbsent(any(), any()); + + CompactJavaSerializer serializerA = new CompactJavaSerializer(getClass().getClassLoader()); + serializerA.init(stateRepository); + + Date object = new Date(); + + failing.set(true); + try { + serializerA.serialize(object); + fail("Expected RuntimeException"); + } catch (RuntimeException e) { + //expected + } + + failing.set(false); + ByteBuffer serialized = serializerA.serialize(object); + assertThat(serializerA.read(serialized), is(object)); + + assertThat(stateMap.entrySet(), hasSize(1)); + + CompactJavaSerializer serializerB = new CompactJavaSerializer(getClass().getClassLoader()); + serializerB.init(stateRepository); + + assertThat(serializerB.read(serialized), is(object)); + } +} diff --git a/impl/src/test/java/org/ehcache/impl/serialization/DoubleSerializerTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/DoubleSerializerTest.java similarity index 96% rename from impl/src/test/java/org/ehcache/impl/serialization/DoubleSerializerTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/serialization/DoubleSerializerTest.java index 10c68d0428..ac4d8ea9d3 100644 --- a/impl/src/test/java/org/ehcache/impl/serialization/DoubleSerializerTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/DoubleSerializerTest.java @@ -22,8 +22,8 @@ import java.util.Random; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; public class DoubleSerializerTest { @@ -52,4 +52,4 @@ public void testReadThrowsOnNullInput() throws ClassNotFoundException { public void testSerializeThrowsOnNullInput() { new DoubleSerializer().serialize(null); } -} \ No newline at end of file +} diff --git a/impl/src/test/java/org/ehcache/impl/serialization/DuplicateClassLoader.java b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/DuplicateClassLoader.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/serialization/DuplicateClassLoader.java rename to ehcache-impl/src/test/java/org/ehcache/impl/serialization/DuplicateClassLoader.java diff --git a/impl/src/test/java/org/ehcache/impl/serialization/EnumTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/EnumTest.java similarity index 75% rename from impl/src/test/java/org/ehcache/impl/serialization/EnumTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/serialization/EnumTest.java index 0a704aaa41..e2d0b90255 100644 --- a/impl/src/test/java/org/ehcache/impl/serialization/EnumTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/EnumTest.java @@ -18,7 +18,6 @@ import org.ehcache.spi.serialization.StatefulSerializer; import org.hamcrest.core.IsSame; -import org.junit.Assert; import org.junit.Test; import java.io.Serializable; @@ -27,6 +26,7 @@ import static org.ehcache.impl.serialization.SerializerTestUtilities.newClassName; import static org.ehcache.impl.serialization.SerializerTestUtilities.popTccl; import static org.ehcache.impl.serialization.SerializerTestUtilities.pushTccl; +import static org.hamcrest.MatcherAssert.assertThat; /** * @@ -39,9 +39,9 @@ public void basicInstanceSerialization() throws ClassNotFoundException { StatefulSerializer s = new CompactJavaSerializer<>(null); s.init(new TransientStateRepository()); - Assert.assertThat(s.read(s.serialize(People.Alice)), IsSame.sameInstance(People.Alice)); - Assert.assertThat(s.read(s.serialize(People.Bob)), IsSame.sameInstance(People.Bob)); - Assert.assertThat(s.read(s.serialize(People.Eve)), IsSame.sameInstance(People.Eve)); + assertThat(s.read(s.serialize(People.Alice)), IsSame.sameInstance(People.Alice)); + assertThat(s.read(s.serialize(People.Bob)), IsSame.sameInstance(People.Bob)); + assertThat(s.read(s.serialize(People.Eve)), IsSame.sameInstance(People.Eve)); } @Test @@ -49,10 +49,10 @@ public void classSerialization() throws ClassNotFoundException { StatefulSerializer s = new CompactJavaSerializer<>(null); s.init(new TransientStateRepository()); - Assert.assertThat(s.read(s.serialize(Enum.class)), IsSame.sameInstance(Enum.class)); - Assert.assertThat(s.read(s.serialize(Dogs.Handel.getClass())), IsSame.sameInstance(Dogs.Handel.getClass())); - Assert.assertThat(s.read(s.serialize(Dogs.Cassie.getClass())), IsSame.sameInstance(Dogs.Cassie.getClass())); - Assert.assertThat(s.read(s.serialize(Dogs.Penny.getClass())), IsSame.sameInstance(Dogs.Penny.getClass())); + assertThat(s.read(s.serialize(Enum.class)), IsSame.sameInstance(Enum.class)); + assertThat(s.read(s.serialize(Dogs.Handel.getClass())), IsSame.sameInstance(Dogs.Handel.getClass())); + assertThat(s.read(s.serialize(Dogs.Cassie.getClass())), IsSame.sameInstance(Dogs.Cassie.getClass())); + assertThat(s.read(s.serialize(Dogs.Penny.getClass())), IsSame.sameInstance(Dogs.Penny.getClass())); } @Test @@ -72,7 +72,7 @@ public void shiftingInstanceSerialization() throws ClassNotFoundException { pushTccl(rLoader); try { for (int i = 0; i < wInstances.length; i++) { - Assert.assertThat(s.read(s.serialize((Serializable) wInstances[i])), IsSame.sameInstance(rInstances[i])); + assertThat(s.read(s.serialize((Serializable) wInstances[i])), IsSame.sameInstance(rInstances[i])); } } finally { popTccl(); diff --git a/impl/src/test/java/org/ehcache/impl/serialization/FieldTypeChangeTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/FieldTypeChangeTest.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/serialization/FieldTypeChangeTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/serialization/FieldTypeChangeTest.java diff --git a/impl/src/test/java/org/ehcache/impl/serialization/FloatSerializerTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/FloatSerializerTest.java similarity index 96% rename from impl/src/test/java/org/ehcache/impl/serialization/FloatSerializerTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/serialization/FloatSerializerTest.java index b927433767..83dca80e62 100644 --- a/impl/src/test/java/org/ehcache/impl/serialization/FloatSerializerTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/FloatSerializerTest.java @@ -22,8 +22,8 @@ import java.util.Random; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; public class FloatSerializerTest { @@ -52,4 +52,4 @@ public void testReadThrowsOnNullInput() throws ClassNotFoundException { public void testSerializeThrowsOnNullInput() { new FloatSerializer().serialize(null); } -} \ No newline at end of file +} diff --git a/impl/src/test/java/org/ehcache/impl/serialization/GetFieldTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/GetFieldTest.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/serialization/GetFieldTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/serialization/GetFieldTest.java diff --git a/impl/src/test/java/org/ehcache/impl/serialization/IntegerSerializerTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/IntegerSerializerTest.java similarity index 96% rename from impl/src/test/java/org/ehcache/impl/serialization/IntegerSerializerTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/serialization/IntegerSerializerTest.java index e976a9188b..127bbe552d 100644 --- a/impl/src/test/java/org/ehcache/impl/serialization/IntegerSerializerTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/IntegerSerializerTest.java @@ -22,8 +22,8 @@ import java.util.Random; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; public class IntegerSerializerTest { @@ -52,4 +52,4 @@ public void testReadThrowsOnNullInput() throws ClassNotFoundException { public void testSerializeThrowsOnNullInput() { new IntegerSerializer().serialize(null); } -} \ No newline at end of file +} diff --git a/impl/src/test/java/org/ehcache/impl/serialization/JavaSerializer.java b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/JavaSerializer.java similarity index 98% rename from impl/src/test/java/org/ehcache/impl/serialization/JavaSerializer.java rename to ehcache-impl/src/test/java/org/ehcache/impl/serialization/JavaSerializer.java index 070bfe2627..ebff194cdb 100644 --- a/impl/src/test/java/org/ehcache/impl/serialization/JavaSerializer.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/JavaSerializer.java @@ -16,7 +16,7 @@ package org.ehcache.impl.serialization; import org.ehcache.spi.serialization.SerializerException; -import org.ehcache.impl.internal.util.ByteBufferInputStream; +import org.ehcache.core.util.ByteBufferInputStream; import org.ehcache.spi.serialization.Serializer; import java.io.ByteArrayOutputStream; diff --git a/impl/src/test/java/org/ehcache/impl/serialization/LongSerializerTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/LongSerializerTest.java similarity index 96% rename from impl/src/test/java/org/ehcache/impl/serialization/LongSerializerTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/serialization/LongSerializerTest.java index 0d87007126..87769a44a2 100644 --- a/impl/src/test/java/org/ehcache/impl/serialization/LongSerializerTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/LongSerializerTest.java @@ -22,8 +22,8 @@ import java.util.Random; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; /** * LongSerializerTest @@ -55,4 +55,4 @@ public void testReadThrowsOnNullInput() throws ClassNotFoundException { public void testSerializeThrowsOnNullInput() { new LongSerializer().serialize(null); } -} \ No newline at end of file +} diff --git a/impl/src/test/java/org/ehcache/impl/serialization/PutFieldTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/PutFieldTest.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/serialization/PutFieldTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/serialization/PutFieldTest.java diff --git a/impl/src/test/java/org/ehcache/impl/serialization/ReadObjectNoDataTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/ReadObjectNoDataTest.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/serialization/ReadObjectNoDataTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/serialization/ReadObjectNoDataTest.java diff --git a/impl/src/test/java/org/ehcache/impl/serialization/SerializeAfterEvolutionTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/SerializeAfterEvolutionTest.java similarity index 91% rename from impl/src/test/java/org/ehcache/impl/serialization/SerializeAfterEvolutionTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/serialization/SerializeAfterEvolutionTest.java index 94a197e5b3..33ca475626 100644 --- a/impl/src/test/java/org/ehcache/impl/serialization/SerializeAfterEvolutionTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/SerializeAfterEvolutionTest.java @@ -22,13 +22,14 @@ import org.ehcache.spi.serialization.StatefulSerializer; import org.hamcrest.core.Is; -import org.junit.Assert; import org.junit.Test; import static org.ehcache.impl.serialization.SerializerTestUtilities.createClassNameRewritingLoader; import static org.ehcache.impl.serialization.SerializerTestUtilities.newClassName; import static org.ehcache.impl.serialization.SerializerTestUtilities.popTccl; import static org.ehcache.impl.serialization.SerializerTestUtilities.pushTccl; +import static org.hamcrest.MatcherAssert.assertThat; + public class SerializeAfterEvolutionTest { @Test @@ -45,11 +46,11 @@ public void test() throws Exception { pushTccl(loaderB); try { Serializable outA = s.read(encodedA); - Assert.assertThat((Integer) outA.getClass().getField("integer").get(outA), Is.is(42)); + assertThat((Integer) outA.getClass().getField("integer").get(outA), Is.is(42)); Serializable b = (Serializable) loaderB.loadClass(newClassName(A_new.class)).newInstance(); Serializable outB = s.read(s.serialize(b)); - Assert.assertThat((Integer) outB.getClass().getField("integer").get(outB), Is.is(42)); + assertThat((Integer) outB.getClass().getField("integer").get(outB), Is.is(42)); } finally { popTccl(); } diff --git a/impl/src/test/java/org/ehcache/impl/serialization/SerializerTestUtilities.java b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/SerializerTestUtilities.java similarity index 100% rename from impl/src/test/java/org/ehcache/impl/serialization/SerializerTestUtilities.java rename to ehcache-impl/src/test/java/org/ehcache/impl/serialization/SerializerTestUtilities.java diff --git a/impl/src/test/java/org/ehcache/impl/serialization/StringSerializerTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/StringSerializerTest.java similarity index 98% rename from impl/src/test/java/org/ehcache/impl/serialization/StringSerializerTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/serialization/StringSerializerTest.java index 09df5b15aa..606ac50311 100644 --- a/impl/src/test/java/org/ehcache/impl/serialization/StringSerializerTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/StringSerializerTest.java @@ -21,8 +21,8 @@ import java.util.Random; import org.junit.Test; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; /** * StringSerializerTest @@ -102,4 +102,4 @@ public void testEqualsMismatchOnMissingFinalSurrogateAgainstOldFormat() throws U ByteBuffer bytes = ByteBuffer.wrap(string.getBytes("UTF-8")); assertThat(serializer.equals(trimmed, bytes), is(false)); } -} \ No newline at end of file +} diff --git a/impl/src/test/java/org/ehcache/impl/serialization/TransientStateRepositoryTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/TransientStateRepositoryTest.java similarity index 96% rename from impl/src/test/java/org/ehcache/impl/serialization/TransientStateRepositoryTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/serialization/TransientStateRepositoryTest.java index 8134dda8b2..7424d90bf6 100644 --- a/impl/src/test/java/org/ehcache/impl/serialization/TransientStateRepositoryTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/serialization/TransientStateRepositoryTest.java @@ -19,8 +19,8 @@ import org.ehcache.spi.persistence.StateHolder; import org.junit.Test; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.*; /** * TransientStateRepositoryTest diff --git a/impl/src/test/java/org/ehcache/impl/internal/events/ScopedStoreEventDispatcherTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/store/DefaultStoreEventDispatcherTest.java similarity index 88% rename from impl/src/test/java/org/ehcache/impl/internal/events/ScopedStoreEventDispatcherTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/store/DefaultStoreEventDispatcherTest.java index 00cd7eceff..7ee9c7355a 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/events/ScopedStoreEventDispatcherTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/store/DefaultStoreEventDispatcherTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.ehcache.impl.internal.events; +package org.ehcache.impl.store; import org.ehcache.event.EventType; import org.ehcache.core.events.StoreEventSink; @@ -32,8 +32,8 @@ import java.util.concurrent.CountDownLatch; import static org.ehcache.impl.internal.util.Matchers.eventOfType; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @@ -45,15 +45,15 @@ import static org.mockito.hamcrest.MockitoHamcrest.argThat; /** - * ScopedStoreEventDispatcherTest + * DefaultStoreEventDispatcherTest */ -public class ScopedStoreEventDispatcherTest { +public class DefaultStoreEventDispatcherTest { - private static final Logger LOGGER = LoggerFactory.getLogger(ScopedStoreEventDispatcherTest.class); + private static final Logger LOGGER = LoggerFactory.getLogger(DefaultStoreEventDispatcherTest.class); @Test public void testRegistersOrderingChange() { - ScopedStoreEventDispatcher dispatcher = new ScopedStoreEventDispatcher<>(1); + DefaultStoreEventDispatcher dispatcher = new DefaultStoreEventDispatcher<>(1); assertThat(dispatcher.isEventOrdering(), is(false)); dispatcher.setEventOrdering(true); @@ -65,7 +65,7 @@ public void testRegistersOrderingChange() { @Test @SuppressWarnings("unchecked") public void testListenerNotifiedUnordered() { - ScopedStoreEventDispatcher dispatcher = new ScopedStoreEventDispatcher<>(1); + DefaultStoreEventDispatcher dispatcher = new DefaultStoreEventDispatcher<>(1); @SuppressWarnings("unchecked") StoreEventListener listener = mock(StoreEventListener.class); dispatcher.addEventListener(listener); @@ -80,7 +80,7 @@ public void testListenerNotifiedUnordered() { @Test @SuppressWarnings("unchecked") public void testListenerNotifiedOrdered() { - ScopedStoreEventDispatcher dispatcher = new ScopedStoreEventDispatcher<>(1); + DefaultStoreEventDispatcher dispatcher = new DefaultStoreEventDispatcher<>(1); @SuppressWarnings("unchecked") StoreEventListener listener = mock(StoreEventListener.class); dispatcher.addEventListener(listener); @@ -95,7 +95,7 @@ public void testListenerNotifiedOrdered() { @Test public void testEventFiltering() { - ScopedStoreEventDispatcher dispatcher = new ScopedStoreEventDispatcher<>(1); + DefaultStoreEventDispatcher dispatcher = new DefaultStoreEventDispatcher<>(1); @SuppressWarnings("unchecked") StoreEventListener listener = mock(StoreEventListener.class, withSettings().verboseLogging()); dispatcher.addEventListener(listener); @@ -118,7 +118,7 @@ public void testEventFiltering() { @Test public void testOrderedEventDelivery() throws Exception { - final ScopedStoreEventDispatcher dispatcher = new ScopedStoreEventDispatcher<>(4); + final DefaultStoreEventDispatcher dispatcher = new DefaultStoreEventDispatcher<>(4); dispatcher.setEventOrdering(true); final ConcurrentHashMap map = new ConcurrentHashMap<>(); final long[] keys = new long[] { 1L, 42L, 256L }; diff --git a/impl/src/test/java/org/ehcache/impl/store/HashUtilsTest.java b/ehcache-impl/src/test/java/org/ehcache/impl/store/HashUtilsTest.java similarity index 95% rename from impl/src/test/java/org/ehcache/impl/store/HashUtilsTest.java rename to ehcache-impl/src/test/java/org/ehcache/impl/store/HashUtilsTest.java index e82f763ae8..c74abbe1f3 100644 --- a/impl/src/test/java/org/ehcache/impl/store/HashUtilsTest.java +++ b/ehcache-impl/src/test/java/org/ehcache/impl/store/HashUtilsTest.java @@ -20,8 +20,8 @@ import java.util.Random; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; /** * HashUtilsTest diff --git a/impl/src/test/java/org/ehcache/test/MockitoUtil.java b/ehcache-impl/src/test/java/org/ehcache/test/MockitoUtil.java similarity index 100% rename from impl/src/test/java/org/ehcache/test/MockitoUtil.java rename to ehcache-impl/src/test/java/org/ehcache/test/MockitoUtil.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/concurrent/ConcurrentHashMap.java b/ehcache-impl/src/unsafe/java/org/ehcache/impl/internal/concurrent/ConcurrentHashMap.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/concurrent/ConcurrentHashMap.java rename to ehcache-impl/src/unsafe/java/org/ehcache/impl/internal/concurrent/ConcurrentHashMap.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/concurrent/EvictingConcurrentMap.java b/ehcache-impl/src/unsafe/java/org/ehcache/impl/internal/concurrent/EvictingConcurrentMap.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/concurrent/EvictingConcurrentMap.java rename to ehcache-impl/src/unsafe/java/org/ehcache/impl/internal/concurrent/EvictingConcurrentMap.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/concurrent/ThreadLocalRandomUtil.java b/ehcache-impl/src/unsafe/java/org/ehcache/impl/internal/concurrent/ThreadLocalRandomUtil.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/concurrent/ThreadLocalRandomUtil.java rename to ehcache-impl/src/unsafe/java/org/ehcache/impl/internal/concurrent/ThreadLocalRandomUtil.java diff --git a/impl/src/main/java/org/ehcache/impl/internal/concurrent/package-info.java b/ehcache-impl/src/unsafe/java/org/ehcache/impl/internal/concurrent/package-info.java similarity index 100% rename from impl/src/main/java/org/ehcache/impl/internal/concurrent/package-info.java rename to ehcache-impl/src/unsafe/java/org/ehcache/impl/internal/concurrent/package-info.java diff --git a/management/build.gradle b/ehcache-management/build.gradle similarity index 52% rename from management/build.gradle rename to ehcache-management/build.gradle index 3742560078..4110cbd634 100644 --- a/management/build.gradle +++ b/ehcache-management/build.gradle @@ -14,29 +14,36 @@ * limitations under the License. */ -apply plugin: EhDeploy +plugins { + id 'org.ehcache.build.internal-module' +} + +publishing.publications.withType(MavenPublication) { + pom { + name = 'Ehcache 3 Management and Monitoring module' + description = 'The Management and Monitoring module of Ehcache 3' + } +} dependencies { + implementation "org.terracotta:statistics:$statisticVersion" // optional: if we want xml config - compileOnly project(':xml') + compileOnly project(':ehcache-xml') // optional: if we want to use the clustered management layer - compileOnly project(':clustered:client') + compileOnly project(':clustered:ehcache-client') compileOnly "org.terracotta:entity-client-api:$terracottaApisVersion" - compileOnly "org.terracotta.management.dist:mnm-nms-agent:$terracottaPlatformVersion" + compileOnly ("org.terracotta.management:nms-agent-entity-client:$terracottaPlatformVersion") { + // This is to avoid stats lib being directly used. + exclude group:'org.terracotta', module:'statistics' + } - compileOnly project(':api') - compileOnly project(':core') - compileOnly project(':impl') + compileOnly project(':ehcache-api') + compileOnly project(':ehcache-core') + compileOnly project(':ehcache-impl') testImplementation "org.terracotta.management:management-registry:$terracottaPlatformVersion" - testImplementation project(':xml') - testImplementation project(':impl') + testImplementation project(':ehcache-xml') + testImplementation project(':ehcache-impl') testImplementation "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion" - testCompile project(path: ':xml', configuration: 'testArchives') -} - -test { - if (testJava.javaVersion.isJava9Compatible()) { - jvmArgs += ['--add-modules', 'java.xml.bind'] - } + testImplementation testFixtures(project(':ehcache-xml')) } diff --git a/management/config/checkstyle-suppressions.xml b/ehcache-management/config/checkstyle-suppressions.xml similarity index 100% rename from management/config/checkstyle-suppressions.xml rename to ehcache-management/config/checkstyle-suppressions.xml diff --git a/management/src/main/java/org/ehcache/management/CollectorService.java b/ehcache-management/src/main/java/org/ehcache/management/CollectorService.java similarity index 100% rename from management/src/main/java/org/ehcache/management/CollectorService.java rename to ehcache-management/src/main/java/org/ehcache/management/CollectorService.java diff --git a/ehcache-management/src/main/java/org/ehcache/management/ExtendedStatisticsService.java b/ehcache-management/src/main/java/org/ehcache/management/ExtendedStatisticsService.java new file mode 100644 index 0000000000..b0441c075c --- /dev/null +++ b/ehcache-management/src/main/java/org/ehcache/management/ExtendedStatisticsService.java @@ -0,0 +1,74 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.management; + +import org.ehcache.Cache; +import org.ehcache.core.spi.service.StatisticsService; +import org.ehcache.management.registry.LatencyHistogramConfiguration; +import org.terracotta.management.model.capabilities.descriptors.StatisticDescriptor; +import org.terracotta.management.model.stats.Statistic; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Map; +import java.util.function.LongSupplier; + +public interface ExtendedStatisticsService extends StatisticsService { + + /** + * Create statistics registry + * @param cacheName name (alias) of the cache + * @param cache the {@link Cache} associated with the given alias + * @param timeSource source of time for statistics maintenance + */ + void createCacheRegistry(String cacheName, Cache cache, LongSupplier timeSource); + + /** + * Registers a cache for statistics + * @param cacheName name (alias) of the cache + */ + void registerCacheStatistics(String cacheName); + + /** + * Returns the Statistics descriptor for the cache with the given alias + * @param cacheName name (alias) of the cache + * @return the collection of {@link StatisticDescriptor}s of the cache + */ + Collection getCacheDescriptors(String cacheName); + + /** + * Registers derived statistics for the cache + * @param the generic type of statistics + * @param cacheName name (alias) of the cache + * @param cache the cache associated with the given alias + * @param statName name of the statistic + * @param outcome Class of the type of statistics + * @param derivedName visible name of the statistics + * @param configuration the histogram configuration for statistics + */ + , K, V> void registerDerivedStatistics(String cacheName, Cache cache, String statName, T outcome, String derivedName, LatencyHistogramConfiguration configuration); + + /** + * Returns the statistics for the cache + * @param cacheName name (alias) of the cache + * @param statisticNames names of the statistics + * @param since time since statistics needs to be collected + * @return map of statisticNames and statistics + */ + Map> collectStatistics(String cacheName, Collection statisticNames, long since); + +} diff --git a/management/src/main/java/org/ehcache/management/ManagementRegistryService.java b/ehcache-management/src/main/java/org/ehcache/management/ManagementRegistryService.java similarity index 100% rename from management/src/main/java/org/ehcache/management/ManagementRegistryService.java rename to ehcache-management/src/main/java/org/ehcache/management/ManagementRegistryService.java diff --git a/management/src/main/java/org/ehcache/management/ManagementRegistryServiceConfiguration.java b/ehcache-management/src/main/java/org/ehcache/management/ManagementRegistryServiceConfiguration.java similarity index 92% rename from management/src/main/java/org/ehcache/management/ManagementRegistryServiceConfiguration.java rename to ehcache-management/src/main/java/org/ehcache/management/ManagementRegistryServiceConfiguration.java index 490fb78f59..3a98419716 100644 --- a/management/src/main/java/org/ehcache/management/ManagementRegistryServiceConfiguration.java +++ b/ehcache-management/src/main/java/org/ehcache/management/ManagementRegistryServiceConfiguration.java @@ -15,7 +15,7 @@ */ package org.ehcache.management; -import org.ehcache.management.providers.statistics.LatencyHistogramConfiguration; +import org.ehcache.management.registry.LatencyHistogramConfiguration; import org.ehcache.spi.service.ServiceCreationConfiguration; import org.terracotta.management.model.context.Context; @@ -24,7 +24,7 @@ /** * Configuration interface for a {@link ManagementRegistryService}. */ -public interface ManagementRegistryServiceConfiguration extends ServiceCreationConfiguration { +public interface ManagementRegistryServiceConfiguration extends ServiceCreationConfiguration { /** * The context used to identify this cache manager diff --git a/management/src/main/java/org/ehcache/management/SharedManagementService.java b/ehcache-management/src/main/java/org/ehcache/management/SharedManagementService.java similarity index 100% rename from management/src/main/java/org/ehcache/management/SharedManagementService.java rename to ehcache-management/src/main/java/org/ehcache/management/SharedManagementService.java diff --git a/management/src/main/java/org/ehcache/management/cluster/Clustering.java b/ehcache-management/src/main/java/org/ehcache/management/cluster/Clustering.java similarity index 96% rename from management/src/main/java/org/ehcache/management/cluster/Clustering.java rename to ehcache-management/src/main/java/org/ehcache/management/cluster/Clustering.java index 4b35df0642..d441141ad2 100644 --- a/management/src/main/java/org/ehcache/management/cluster/Clustering.java +++ b/ehcache-management/src/main/java/org/ehcache/management/cluster/Clustering.java @@ -46,7 +46,7 @@ public static boolean isAvailable(ServiceProvider serviceProvider) { /** * Creates a new ${@link ClusteringManagementService} to handle the management integration with the cluster */ - public static ClusteringManagementService newClusteringManagementService(ClusteringManagementServiceConfiguration configuration) { + public static ClusteringManagementService newClusteringManagementService(ClusteringManagementServiceConfiguration configuration) { return new DefaultClusteringManagementService(configuration); } diff --git a/management/src/main/java/org/ehcache/management/cluster/ClusteringManagementService.java b/ehcache-management/src/main/java/org/ehcache/management/cluster/ClusteringManagementService.java similarity index 100% rename from management/src/main/java/org/ehcache/management/cluster/ClusteringManagementService.java rename to ehcache-management/src/main/java/org/ehcache/management/cluster/ClusteringManagementService.java diff --git a/management/src/main/java/org/ehcache/management/cluster/ClusteringManagementServiceConfiguration.java b/ehcache-management/src/main/java/org/ehcache/management/cluster/ClusteringManagementServiceConfiguration.java similarity index 90% rename from management/src/main/java/org/ehcache/management/cluster/ClusteringManagementServiceConfiguration.java rename to ehcache-management/src/main/java/org/ehcache/management/cluster/ClusteringManagementServiceConfiguration.java index a20d72cde9..a18ab82e93 100644 --- a/management/src/main/java/org/ehcache/management/cluster/ClusteringManagementServiceConfiguration.java +++ b/ehcache-management/src/main/java/org/ehcache/management/cluster/ClusteringManagementServiceConfiguration.java @@ -20,7 +20,7 @@ /** * Configuration interface for a {@link ClusteringManagementService}. */ -public interface ClusteringManagementServiceConfiguration extends ServiceCreationConfiguration { +public interface ClusteringManagementServiceConfiguration extends ServiceCreationConfiguration { /** * @return The alias of the executor used to run management call queries. diff --git a/management/src/main/java/org/ehcache/management/cluster/DefaultClusteringManagementService.java b/ehcache-management/src/main/java/org/ehcache/management/cluster/DefaultClusteringManagementService.java similarity index 92% rename from management/src/main/java/org/ehcache/management/cluster/DefaultClusteringManagementService.java rename to ehcache-management/src/main/java/org/ehcache/management/cluster/DefaultClusteringManagementService.java index 60a4a03e33..22d2d14bd8 100644 --- a/management/src/main/java/org/ehcache/management/cluster/DefaultClusteringManagementService.java +++ b/ehcache-management/src/main/java/org/ehcache/management/cluster/DefaultClusteringManagementService.java @@ -36,6 +36,7 @@ import org.terracotta.management.entity.nms.agent.client.DefaultNmsAgentService; import org.terracotta.management.entity.nms.agent.client.NmsAgentEntity; import org.terracotta.management.entity.nms.agent.client.NmsAgentService; +import org.terracotta.management.model.context.Context; import org.terracotta.management.model.notification.ContextualNotification; import org.terracotta.management.model.stats.ContextualStatistics; @@ -49,7 +50,7 @@ @ServiceDependencies({CacheManagerProviderService.class, ExecutionService.class, TimeSourceService.class, ManagementRegistryService.class, EntityService.class, ClusteringService.class}) public class DefaultClusteringManagementService implements ClusteringManagementService, CacheManagerListener, CollectorService.Collector { - private final ClusteringManagementServiceConfiguration configuration; + private final ClusteringManagementServiceConfiguration configuration; private volatile ManagementRegistryService managementRegistryService; private volatile CollectorService collectorService; @@ -63,7 +64,7 @@ public DefaultClusteringManagementService() { this(new DefaultClusteringManagementServiceConfiguration()); } - public DefaultClusteringManagementService(ClusteringManagementServiceConfiguration configuration) { + public DefaultClusteringManagementService(ClusteringManagementServiceConfiguration configuration) { this.configuration = configuration == null ? new DefaultClusteringManagementServiceConfiguration() : configuration; } @@ -92,17 +93,21 @@ public void start(ServiceProvider serviceProvider) { public void stop() { if (collectorService != null) { collectorService.stop(); + collectorService = null; + } + + if (managementCallExecutor != null) { + shutdownNow(managementCallExecutor); + managementCallExecutor = null; } - shutdownNow(managementCallExecutor); // nullify so that no further actions are done with them (see null-checks below) if (nmsAgentService != null) { nmsAgentService.close(); - managementRegistryService = null; nmsAgentService = null; } - managementCallExecutor = null; + managementRegistryService = null; } @Override @@ -140,7 +145,9 @@ public void stateTransition(Status from, Status to) { } private NmsAgentService createNmsAgentService() { - DefaultNmsAgentService nmsAgentService = new DefaultNmsAgentService(() -> { + // root context will contain: instanceId=... and cacheManagerName=... + final Context rootContext = managementRegistryService.getConfiguration().getContext(); + DefaultNmsAgentService nmsAgentService = new DefaultNmsAgentService(rootContext, () -> { try { return nmsAgentFactory.retrieve(); } catch (EntityNotFoundException e) { diff --git a/management/src/main/java/org/ehcache/management/cluster/DefaultClusteringManagementServiceConfiguration.java b/ehcache-management/src/main/java/org/ehcache/management/cluster/DefaultClusteringManagementServiceConfiguration.java similarity index 97% rename from management/src/main/java/org/ehcache/management/cluster/DefaultClusteringManagementServiceConfiguration.java rename to ehcache-management/src/main/java/org/ehcache/management/cluster/DefaultClusteringManagementServiceConfiguration.java index 95c460f4b1..e502abcfeb 100644 --- a/management/src/main/java/org/ehcache/management/cluster/DefaultClusteringManagementServiceConfiguration.java +++ b/ehcache-management/src/main/java/org/ehcache/management/cluster/DefaultClusteringManagementServiceConfiguration.java @@ -15,7 +15,7 @@ */ package org.ehcache.management.cluster; -public class DefaultClusteringManagementServiceConfiguration implements ClusteringManagementServiceConfiguration { +public class DefaultClusteringManagementServiceConfiguration implements ClusteringManagementServiceConfiguration { private String managementCallExecutorAlias = "managementCallExecutor"; private int managementCallQueueSize = 1024; diff --git a/management/src/main/java/org/ehcache/management/cluster/LoggingExecutor.java b/ehcache-management/src/main/java/org/ehcache/management/cluster/LoggingExecutor.java similarity index 100% rename from management/src/main/java/org/ehcache/management/cluster/LoggingExecutor.java rename to ehcache-management/src/main/java/org/ehcache/management/cluster/LoggingExecutor.java diff --git a/management/src/main/java/org/ehcache/management/providers/CacheBinding.java b/ehcache-management/src/main/java/org/ehcache/management/providers/CacheBinding.java similarity index 95% rename from management/src/main/java/org/ehcache/management/providers/CacheBinding.java rename to ehcache-management/src/main/java/org/ehcache/management/providers/CacheBinding.java index b3dcdbf781..d03f86b367 100644 --- a/management/src/main/java/org/ehcache/management/providers/CacheBinding.java +++ b/ehcache-management/src/main/java/org/ehcache/management/providers/CacheBinding.java @@ -24,7 +24,7 @@ /** * Class representing an association between an object and an alias, name, identifier */ -@RequiredContext({@Named("cacheManagerName"), @Named("cacheName")}) +@RequiredContext({@Named("instanceId"), @Named("cacheManagerName"), @Named("cacheName")}) public final class CacheBinding { private final String alias; diff --git a/management/src/main/java/org/ehcache/management/providers/CacheBindingManagementProvider.java b/ehcache-management/src/main/java/org/ehcache/management/providers/CacheBindingManagementProvider.java similarity index 100% rename from management/src/main/java/org/ehcache/management/providers/CacheBindingManagementProvider.java rename to ehcache-management/src/main/java/org/ehcache/management/providers/CacheBindingManagementProvider.java diff --git a/management/src/main/java/org/ehcache/management/providers/EhcacheStatisticCollectorProvider.java b/ehcache-management/src/main/java/org/ehcache/management/providers/EhcacheStatisticCollectorProvider.java similarity index 94% rename from management/src/main/java/org/ehcache/management/providers/EhcacheStatisticCollectorProvider.java rename to ehcache-management/src/main/java/org/ehcache/management/providers/EhcacheStatisticCollectorProvider.java index 9f2dee44e1..dc159e82ef 100644 --- a/management/src/main/java/org/ehcache/management/providers/EhcacheStatisticCollectorProvider.java +++ b/ehcache-management/src/main/java/org/ehcache/management/providers/EhcacheStatisticCollectorProvider.java @@ -20,7 +20,7 @@ import org.terracotta.management.registry.RequiredContext; import org.terracotta.management.registry.collect.StatisticCollectorProvider; -@RequiredContext(@Named("cacheManagerName")) +@RequiredContext({@Named("instanceId"), @Named("cacheManagerName")}) public class EhcacheStatisticCollectorProvider extends StatisticCollectorProvider { public EhcacheStatisticCollectorProvider(ManagementRegistryServiceConfiguration configuration) { super(configuration.getContext()); diff --git a/management/src/main/java/org/ehcache/management/providers/ExposedCacheBinding.java b/ehcache-management/src/main/java/org/ehcache/management/providers/ExposedCacheBinding.java similarity index 97% rename from management/src/main/java/org/ehcache/management/providers/ExposedCacheBinding.java rename to ehcache-management/src/main/java/org/ehcache/management/providers/ExposedCacheBinding.java index 1ab7966668..e43cdcdce5 100644 --- a/management/src/main/java/org/ehcache/management/providers/ExposedCacheBinding.java +++ b/ehcache-management/src/main/java/org/ehcache/management/providers/ExposedCacheBinding.java @@ -15,7 +15,7 @@ */ package org.ehcache.management.providers; -import org.ehcache.core.internal.util.ClassLoading; +import org.ehcache.core.util.ClassLoading; import org.ehcache.management.ManagementRegistryServiceConfiguration; import org.terracotta.management.model.capabilities.descriptors.Descriptor; import org.terracotta.management.model.context.Context; diff --git a/management/src/main/java/org/ehcache/management/providers/actions/EhcacheActionProvider.java b/ehcache-management/src/main/java/org/ehcache/management/providers/actions/EhcacheActionProvider.java similarity index 94% rename from management/src/main/java/org/ehcache/management/providers/actions/EhcacheActionProvider.java rename to ehcache-management/src/main/java/org/ehcache/management/providers/actions/EhcacheActionProvider.java index 32bb963b2a..97234095a6 100644 --- a/management/src/main/java/org/ehcache/management/providers/actions/EhcacheActionProvider.java +++ b/ehcache-management/src/main/java/org/ehcache/management/providers/actions/EhcacheActionProvider.java @@ -23,7 +23,7 @@ import org.terracotta.management.registry.ExposedObject; @Named("ActionsCapability") -@RequiredContext({@Named("cacheManagerName"), @Named("cacheName")}) +@RequiredContext({@Named("instanceId"), @Named("cacheManagerName"), @Named("cacheName")}) public class EhcacheActionProvider extends AbstractActionManagementProvider { private final ManagementRegistryServiceConfiguration registryServiceConfiguration; diff --git a/management/src/main/java/org/ehcache/management/providers/actions/EhcacheActionWrapper.java b/ehcache-management/src/main/java/org/ehcache/management/providers/actions/EhcacheActionWrapper.java similarity index 100% rename from management/src/main/java/org/ehcache/management/providers/actions/EhcacheActionWrapper.java rename to ehcache-management/src/main/java/org/ehcache/management/providers/actions/EhcacheActionWrapper.java diff --git a/management/src/main/java/org/ehcache/management/providers/settings/EhcacheSettingsProvider.java b/ehcache-management/src/main/java/org/ehcache/management/providers/settings/EhcacheSettingsProvider.java similarity index 97% rename from management/src/main/java/org/ehcache/management/providers/settings/EhcacheSettingsProvider.java rename to ehcache-management/src/main/java/org/ehcache/management/providers/settings/EhcacheSettingsProvider.java index d219a30d3c..c34705d2a3 100644 --- a/management/src/main/java/org/ehcache/management/providers/settings/EhcacheSettingsProvider.java +++ b/ehcache-management/src/main/java/org/ehcache/management/providers/settings/EhcacheSettingsProvider.java @@ -29,7 +29,7 @@ import java.util.Collection; @Named("SettingsCapability") -@RequiredContext({@Named("cacheManagerName")}) +@RequiredContext({@Named("instanceId"), @Named("cacheManagerName")}) public class EhcacheSettingsProvider extends CacheBindingManagementProvider { private final ManagementRegistryServiceConfiguration configuration; diff --git a/management/src/main/java/org/ehcache/management/providers/settings/ExposedCacheSettings.java b/ehcache-management/src/main/java/org/ehcache/management/providers/settings/ExposedCacheSettings.java similarity index 100% rename from management/src/main/java/org/ehcache/management/providers/settings/ExposedCacheSettings.java rename to ehcache-management/src/main/java/org/ehcache/management/providers/settings/ExposedCacheSettings.java diff --git a/management/src/main/java/org/ehcache/management/providers/settings/Reflect.java b/ehcache-management/src/main/java/org/ehcache/management/providers/settings/Reflect.java similarity index 100% rename from management/src/main/java/org/ehcache/management/providers/settings/Reflect.java rename to ehcache-management/src/main/java/org/ehcache/management/providers/settings/Reflect.java diff --git a/management/src/main/java/org/ehcache/management/providers/statistics/EhcacheStatisticsProvider.java b/ehcache-management/src/main/java/org/ehcache/management/providers/statistics/EhcacheStatisticsProvider.java similarity index 87% rename from management/src/main/java/org/ehcache/management/providers/statistics/EhcacheStatisticsProvider.java rename to ehcache-management/src/main/java/org/ehcache/management/providers/statistics/EhcacheStatisticsProvider.java index 23d04bd1c1..561df8f700 100644 --- a/management/src/main/java/org/ehcache/management/providers/statistics/EhcacheStatisticsProvider.java +++ b/ehcache-management/src/main/java/org/ehcache/management/providers/statistics/EhcacheStatisticsProvider.java @@ -17,6 +17,7 @@ import org.ehcache.core.spi.service.StatisticsService; import org.ehcache.core.spi.time.TimeSource; +import org.ehcache.management.ExtendedStatisticsService; import org.ehcache.management.ManagementRegistryServiceConfiguration; import org.ehcache.management.providers.CacheBinding; import org.ehcache.management.providers.CacheBindingManagementProvider; @@ -24,10 +25,9 @@ import org.terracotta.management.model.capabilities.descriptors.Descriptor; import org.terracotta.management.model.capabilities.descriptors.StatisticDescriptor; import org.terracotta.management.model.context.Context; -import org.terracotta.management.registry.DefaultStatisticsManagementProvider; +import org.terracotta.management.model.stats.Statistic; import org.terracotta.management.registry.Named; import org.terracotta.management.registry.collect.StatisticProvider; -import org.terracotta.statistics.registry.Statistic; import java.io.Serializable; import java.util.Collection; @@ -41,10 +41,10 @@ @StatisticProvider public class EhcacheStatisticsProvider extends CacheBindingManagementProvider { - private final StatisticsService statisticsService; + private final ExtendedStatisticsService statisticsService; private final TimeSource timeSource; - public EhcacheStatisticsProvider(ManagementRegistryServiceConfiguration configuration, StatisticsService statisticsService, TimeSource timeSource) { + public EhcacheStatisticsProvider(ManagementRegistryServiceConfiguration configuration, ExtendedStatisticsService statisticsService, TimeSource timeSource) { super(configuration); this.statisticsService = Objects.requireNonNull(statisticsService); this.timeSource = Objects.requireNonNull(timeSource); @@ -72,8 +72,7 @@ public Map> collectStatistics(Context if (exposedObject == null) { return Collections.emptyMap(); } - return DefaultStatisticsManagementProvider.collect(exposedObject.getStatisticRegistry(), statisticNames, since); - + return exposedObject.collectStatistics(statisticNames, since); } } diff --git a/ehcache-management/src/main/java/org/ehcache/management/providers/statistics/StandardEhcacheStatistics.java b/ehcache-management/src/main/java/org/ehcache/management/providers/statistics/StandardEhcacheStatistics.java new file mode 100644 index 0000000000..ac27889b52 --- /dev/null +++ b/ehcache-management/src/main/java/org/ehcache/management/providers/statistics/StandardEhcacheStatistics.java @@ -0,0 +1,69 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.management.providers.statistics; + +import org.ehcache.Cache; +import org.ehcache.core.spi.time.TimeSource; +import org.ehcache.core.statistics.CacheOperationOutcomes; +import org.ehcache.management.registry.LatencyHistogramConfiguration; +import org.ehcache.management.ExtendedStatisticsService; +import org.ehcache.management.ManagementRegistryServiceConfiguration; +import org.ehcache.management.providers.CacheBinding; +import org.ehcache.management.providers.ExposedCacheBinding; +import org.terracotta.management.model.capabilities.descriptors.StatisticDescriptor; +import org.terracotta.management.model.stats.Statistic; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Map; + +public class StandardEhcacheStatistics extends ExposedCacheBinding { + + private final String cacheAlias; + private final ExtendedStatisticsService statisticsService; + + StandardEhcacheStatistics(ManagementRegistryServiceConfiguration registryConfiguration, CacheBinding cacheBinding, ExtendedStatisticsService statisticsService, TimeSource timeSource) { + super(registryConfiguration, cacheBinding); + this.cacheAlias = cacheBinding.getAlias(); + this.statisticsService = statisticsService; + + statisticsService.createCacheRegistry(this.cacheAlias, cacheBinding.getCache(), timeSource::getTimeMillis); + + statisticsService.registerCacheStatistics(this.cacheAlias); + + LatencyHistogramConfiguration latencyHistogramConfiguration = registryConfiguration.getLatencyHistogramConfiguration(); + + // We want some latency statistics as well, so let's register them + registerDerivedStatistics(cacheBinding.getCache(), "get", CacheOperationOutcomes.GetOutcome.HIT, "Cache:GetHitLatency", latencyHistogramConfiguration); + registerDerivedStatistics(cacheBinding.getCache(),"get", CacheOperationOutcomes.GetOutcome.MISS, "Cache:GetMissLatency", latencyHistogramConfiguration); + registerDerivedStatistics(cacheBinding.getCache(),"put", CacheOperationOutcomes.PutOutcome.PUT, "Cache:PutLatency", latencyHistogramConfiguration); + registerDerivedStatistics(cacheBinding.getCache(),"remove", CacheOperationOutcomes.RemoveOutcome.SUCCESS, "Cache:RemoveLatency", latencyHistogramConfiguration); + } + + private , K, V> void registerDerivedStatistics(Cache cache, String statName, T outcome, String derivedName, LatencyHistogramConfiguration configuration) { + this.statisticsService.registerDerivedStatistics(this.cacheAlias, cache , statName, outcome, derivedName, configuration); + } + + @Override + public Collection getDescriptors() { + return statisticsService.getCacheDescriptors(cacheAlias); + } + + Map> collectStatistics(Collection statisticNames, long since) { + return this.statisticsService.collectStatistics(this.cacheAlias, statisticNames, since); + } + +} diff --git a/management/src/main/java/org/ehcache/management/registry/DefaultCollectorService.java b/ehcache-management/src/main/java/org/ehcache/management/registry/DefaultCollectorService.java similarity index 91% rename from management/src/main/java/org/ehcache/management/registry/DefaultCollectorService.java rename to ehcache-management/src/main/java/org/ehcache/management/registry/DefaultCollectorService.java index 5a901770df..8444d78192 100644 --- a/management/src/main/java/org/ehcache/management/registry/DefaultCollectorService.java +++ b/ehcache-management/src/main/java/org/ehcache/management/registry/DefaultCollectorService.java @@ -23,7 +23,7 @@ import org.ehcache.core.spi.store.InternalCacheManager; import org.ehcache.core.spi.time.TimeSource; import org.ehcache.core.spi.time.TimeSourceService; -import org.ehcache.impl.internal.statistics.StatsUtils; +import org.ehcache.core.internal.statistics.StatsUtils; import org.ehcache.management.CollectorService; import org.ehcache.management.ManagementRegistryService; import org.ehcache.management.ManagementRegistryServiceConfiguration; @@ -32,11 +32,7 @@ import org.ehcache.spi.service.ServiceProvider; import org.terracotta.management.model.notification.ContextualNotification; import org.terracotta.management.registry.collect.DefaultStatisticCollector; -import org.ehcache.core.statistics.CacheOperationOutcomes.ClearOutcome; -import org.terracotta.statistics.OperationStatistic; -import org.terracotta.statistics.derived.OperationResultFilter; -import java.util.EnumSet; import java.util.concurrent.ScheduledExecutorService; import static org.ehcache.impl.internal.executor.ExecutorUtil.shutdownNow; @@ -129,10 +125,7 @@ private void cacheCleared(String alias) { } private void registerClearNotification(String alias, Cache cache) { - OperationStatistic clear = StatsUtils.findOperationStatisticOnChildren(cache, - ClearOutcome.class, "clear"); - clear.addDerivedStatistic(new OperationResultFilter<>(EnumSet.of(ClearOutcome.SUCCESS), - (time, latency) -> cacheCleared(alias))); + StatsUtils.registerClearNotification(alias, cache, this::cacheCleared); } @Override diff --git a/management/src/main/java/org/ehcache/management/registry/DefaultManagementRegistryConfiguration.java b/ehcache-management/src/main/java/org/ehcache/management/registry/DefaultManagementRegistryConfiguration.java similarity index 93% rename from management/src/main/java/org/ehcache/management/registry/DefaultManagementRegistryConfiguration.java rename to ehcache-management/src/main/java/org/ehcache/management/registry/DefaultManagementRegistryConfiguration.java index e2af78f386..0bdb03c729 100644 --- a/management/src/main/java/org/ehcache/management/registry/DefaultManagementRegistryConfiguration.java +++ b/ehcache-management/src/main/java/org/ehcache/management/registry/DefaultManagementRegistryConfiguration.java @@ -17,7 +17,6 @@ import org.ehcache.management.ManagementRegistryService; import org.ehcache.management.ManagementRegistryServiceConfiguration; -import org.ehcache.management.providers.statistics.LatencyHistogramConfiguration; import org.terracotta.management.model.context.Context; import java.util.Arrays; @@ -33,7 +32,7 @@ public class DefaultManagementRegistryConfiguration implements ManagementRegistr private final Collection tags = new TreeSet<>(); private final String instanceId = UUID.randomUUID().toString(); - private Context context = Context.empty(); + private Context context = Context.empty().with("instanceId", instanceId); private String collectorExecutorAlias = "collectorExecutor"; private LatencyHistogramConfiguration latencyHistogramConfiguration = LatencyHistogramConfiguration.DEFAULT; @@ -49,6 +48,9 @@ public DefaultManagementRegistryConfiguration setContext(Context context) { if (!this.context.contains("cacheManagerName") && !context.contains("cacheManagerName")) { throw new IllegalArgumentException("'cacheManagerName' is missing from context"); } + if (context.contains("instanceId") && !Objects.equals(context.get("instanceId"), instanceId)) { + throw new IllegalArgumentException("Cannot override instanceId in context " + this.context + " by " + context); + } this.context = this.context.with(context); return this; } diff --git a/management/src/main/java/org/ehcache/management/registry/DefaultManagementRegistryFactory.java b/ehcache-management/src/main/java/org/ehcache/management/registry/DefaultManagementRegistryFactory.java similarity index 88% rename from management/src/main/java/org/ehcache/management/registry/DefaultManagementRegistryFactory.java rename to ehcache-management/src/main/java/org/ehcache/management/registry/DefaultManagementRegistryFactory.java index 287e2595cb..8f2ca11868 100644 --- a/management/src/main/java/org/ehcache/management/registry/DefaultManagementRegistryFactory.java +++ b/ehcache-management/src/main/java/org/ehcache/management/registry/DefaultManagementRegistryFactory.java @@ -23,15 +23,15 @@ public class DefaultManagementRegistryFactory implements ServiceFactory { @Override - public ManagementRegistryService create(ServiceCreationConfiguration configuration) { + public ManagementRegistryService create(ServiceCreationConfiguration configuration) { return configuration instanceof ManagementRegistryServiceConfiguration ? new DefaultManagementRegistryService((ManagementRegistryServiceConfiguration) configuration) : new DefaultManagementRegistryService(new DefaultManagementRegistryConfiguration()); } @Override - public Class getServiceType() { - return ManagementRegistryService.class; + public Class getServiceType() { + return DefaultManagementRegistryService.class; } } diff --git a/management/src/main/java/org/ehcache/management/registry/DefaultManagementRegistryService.java b/ehcache-management/src/main/java/org/ehcache/management/registry/DefaultManagementRegistryService.java similarity index 86% rename from management/src/main/java/org/ehcache/management/registry/DefaultManagementRegistryService.java rename to ehcache-management/src/main/java/org/ehcache/management/registry/DefaultManagementRegistryService.java index 0e239171b3..d72208de54 100644 --- a/management/src/main/java/org/ehcache/management/registry/DefaultManagementRegistryService.java +++ b/ehcache-management/src/main/java/org/ehcache/management/registry/DefaultManagementRegistryService.java @@ -21,9 +21,9 @@ import org.ehcache.core.events.CacheManagerListener; import org.ehcache.core.spi.service.CacheManagerProviderService; import org.ehcache.core.spi.service.ExecutionService; -import org.ehcache.core.spi.service.StatisticsService; import org.ehcache.core.spi.store.InternalCacheManager; import org.ehcache.core.spi.time.TimeSourceService; +import org.ehcache.management.ExtendedStatisticsService; import org.ehcache.management.ManagementRegistryService; import org.ehcache.management.ManagementRegistryServiceConfiguration; import org.ehcache.management.cluster.Clustering; @@ -38,15 +38,15 @@ import org.ehcache.spi.service.Service; import org.ehcache.spi.service.ServiceDependencies; import org.ehcache.spi.service.ServiceProvider; +import org.terracotta.management.model.context.Context; import org.terracotta.management.model.context.ContextContainer; import org.terracotta.management.registry.DefaultManagementRegistry; -import org.terracotta.statistics.StatisticsManager; import java.util.ArrayList; import java.util.Collection; import java.util.Map; -@ServiceDependencies({CacheManagerProviderService.class, StatisticsService.class, TimeSourceService.class, ExecutionService.class}) +@ServiceDependencies({CacheManagerProviderService.class, ExtendedStatisticsService.class, TimeSourceService.class, ExecutionService.class}) @OptionalServiceDependencies({ "org.ehcache.clustered.client.service.EntityService", "org.ehcache.clustered.client.service.ClusteringService"}) @@ -55,13 +55,14 @@ public class DefaultManagementRegistryService extends DefaultManagementRegistry private final ManagementRegistryServiceConfiguration configuration; private volatile InternalCacheManager cacheManager; private volatile ClusteringManagementService clusteringManagementService; + private volatile boolean clusteringManagementServiceAutoStarted; + private volatile ExtendedStatisticsService statisticsService; public DefaultManagementRegistryService() { this(new DefaultManagementRegistryConfiguration()); } public DefaultManagementRegistryService(ManagementRegistryServiceConfiguration configuration) { - super(null); // context container creation is overriden here this.configuration = configuration == null ? new DefaultManagementRegistryConfiguration() : configuration; } @@ -69,7 +70,7 @@ public DefaultManagementRegistryService(ManagementRegistryServiceConfiguration c public void start(final ServiceProvider serviceProvider) { this.cacheManager = serviceProvider.getService(CacheManagerProviderService.class).getCacheManager(); - StatisticsService statisticsService = serviceProvider.getService(StatisticsService.class); + this.statisticsService = serviceProvider.getService(ExtendedStatisticsService.class); TimeSourceService timeSourceService = serviceProvider.getService(TimeSourceService.class); // initialize management capabilities (stats, action calls, etc) @@ -86,22 +87,25 @@ public void start(final ServiceProvider serviceProvider) { if (this.clusteringManagementService == null && Clustering.isAvailable(serviceProvider)) { this.clusteringManagementService = Clustering.newClusteringManagementService(new DefaultClusteringManagementServiceConfiguration()); this.clusteringManagementService.start(serviceProvider); + this.clusteringManagementServiceAutoStarted = true; + } else { + this.clusteringManagementServiceAutoStarted = false; } } @Override public void stop() { - if (this.clusteringManagementService != null) { + if (this.clusteringManagementService != null && this.clusteringManagementServiceAutoStarted) { this.clusteringManagementService.stop(); - this.clusteringManagementService = null; } + this.clusteringManagementService = null; super.close(); } @Override public void cacheAdded(String alias, Cache cache) { - StatisticsManager.associate(cache).withParent(cacheManager); + statisticsService.registerWithParent(cache, cacheManager); register(new CacheBinding(alias, cache)); } @@ -110,7 +114,7 @@ public void cacheAdded(String alias, Cache cache) { public void cacheRemoved(String alias, Cache cache) { unregister(new CacheBinding(alias, cache)); - StatisticsManager.dissociate(cache).fromParent(cacheManager); + statisticsService.deRegisterFromParent(cache, cacheManager); } @Override @@ -157,4 +161,9 @@ public ContextContainer getContextContainer() { return new ContextContainer("cacheManagerName", getConfiguration().getContext().get("cacheManagerName"), cacheCtx); } + @Override + public Context getContext() { + // contains instanceId + cacheManagerName keys + return configuration.getContext(); + } } diff --git a/management/src/main/java/org/ehcache/management/registry/DefaultSharedManagementService.java b/ehcache-management/src/main/java/org/ehcache/management/registry/DefaultSharedManagementService.java similarity index 100% rename from management/src/main/java/org/ehcache/management/registry/DefaultSharedManagementService.java rename to ehcache-management/src/main/java/org/ehcache/management/registry/DefaultSharedManagementService.java diff --git a/management/src/main/java/org/ehcache/management/providers/statistics/LatencyHistogramConfiguration.java b/ehcache-management/src/main/java/org/ehcache/management/registry/LatencyHistogramConfiguration.java similarity index 93% rename from management/src/main/java/org/ehcache/management/providers/statistics/LatencyHistogramConfiguration.java rename to ehcache-management/src/main/java/org/ehcache/management/registry/LatencyHistogramConfiguration.java index b15a088044..687660173b 100644 --- a/management/src/main/java/org/ehcache/management/providers/statistics/LatencyHistogramConfiguration.java +++ b/ehcache-management/src/main/java/org/ehcache/management/registry/LatencyHistogramConfiguration.java @@ -13,17 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.ehcache.management.providers.statistics; - -import org.terracotta.statistics.derived.latency.DefaultLatencyHistogramStatistic; +package org.ehcache.management.registry; import java.time.Duration; import java.util.Objects; /** * Configuration of all latency histograms. - * - * @see DefaultLatencyHistogramStatistic */ public class LatencyHistogramConfiguration { diff --git a/management/src/main/java/org/ehcache/management/registry/ManagementRegistryServiceConfigurationParser.java b/ehcache-management/src/main/java/org/ehcache/management/registry/ManagementRegistryServiceConfigurationParser.java similarity index 92% rename from management/src/main/java/org/ehcache/management/registry/ManagementRegistryServiceConfigurationParser.java rename to ehcache-management/src/main/java/org/ehcache/management/registry/ManagementRegistryServiceConfigurationParser.java index c3a9e809b4..12b3e412db 100644 --- a/management/src/main/java/org/ehcache/management/registry/ManagementRegistryServiceConfigurationParser.java +++ b/ehcache-management/src/main/java/org/ehcache/management/registry/ManagementRegistryServiceConfigurationParser.java @@ -19,6 +19,7 @@ import org.ehcache.spi.service.ServiceCreationConfiguration; import org.ehcache.xml.BaseConfigParser; import org.ehcache.xml.CacheManagerServiceConfigurationParser; +import org.ehcache.xml.JaxbParsers; import org.ehcache.xml.exceptions.XmlConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -53,7 +54,7 @@ public URI getNamespace() { } @Override - public ServiceCreationConfiguration parseServiceCreationConfiguration(Element fragment) { + public ServiceCreationConfiguration parseServiceCreationConfiguration(Element fragment, ClassLoader classLoader) { if ("management".equals(fragment.getLocalName())) { DefaultManagementRegistryConfiguration registryConfiguration = new DefaultManagementRegistryConfiguration(); @@ -71,8 +72,8 @@ public ServiceCreationConfiguration parseServiceCreat for (Element tags : NodeListIterable.elements(fragment, NAMESPACE, "tags")) { // tag for (Element tag : NodeListIterable.elements(tags, NAMESPACE, "tag")) { - String val = val(tag); - if (val != null && !val.isEmpty()) { + String val = JaxbParsers.parsePropertyOrString(tag.getTextContent()); + if (!val.isEmpty()) { registryConfiguration.addTag(val); } } @@ -92,17 +93,13 @@ private static String attr(Element element, String name) { return s == null || s.equals("") ? null : s; } - private static String val(Element element) { - return element.hasChildNodes() ? element.getFirstChild().getNodeValue() : null; - } - @Override public Class getServiceType() { return ManagementRegistryService.class; } @Override - public Element unparseServiceCreationConfiguration(ServiceCreationConfiguration serviceCreationConfiguration) { + public Element unparseServiceCreationConfiguration(ServiceCreationConfiguration serviceCreationConfiguration) { return unparseConfig(serviceCreationConfiguration); } diff --git a/management/src/main/java/org/ehcache/management/registry/NodeListIterable.java b/ehcache-management/src/main/java/org/ehcache/management/registry/NodeListIterable.java similarity index 100% rename from management/src/main/java/org/ehcache/management/registry/NodeListIterable.java rename to ehcache-management/src/main/java/org/ehcache/management/registry/NodeListIterable.java diff --git a/ehcache-management/src/main/java/org/ehcache/management/statistics/DefaultCacheStatistics.java b/ehcache-management/src/main/java/org/ehcache/management/statistics/DefaultCacheStatistics.java new file mode 100644 index 0000000000..6ebb39b9d2 --- /dev/null +++ b/ehcache-management/src/main/java/org/ehcache/management/statistics/DefaultCacheStatistics.java @@ -0,0 +1,247 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.management.statistics; + +import org.ehcache.core.InternalCache; +import org.ehcache.core.internal.statistics.DefaultTierStatistics; +import org.ehcache.core.internal.statistics.DelegatingOperationStatistic; +import org.ehcache.core.statistics.BulkOps; +import org.ehcache.core.statistics.CacheOperationOutcomes.GetOutcome; +import org.ehcache.core.statistics.CacheOperationOutcomes.PutOutcome; +import org.ehcache.core.statistics.CacheStatistics; +import org.ehcache.core.statistics.ChainedOperationObserver; +import org.ehcache.core.statistics.OperationStatistic; +import org.ehcache.core.statistics.TierStatistics; +import org.ehcache.core.statistics.ValueStatistic; +import org.terracotta.statistics.ValueStatistics; + +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; + +import static org.ehcache.core.internal.statistics.StatsUtils.findLowestTier; +import static org.ehcache.core.internal.statistics.StatsUtils.findOperationStatisticOnChildren; +import static org.ehcache.core.internal.statistics.StatsUtils.findTiers; +import static org.ehcache.core.statistics.CacheOperationOutcomes.ConditionalRemoveOutcome; +import static org.ehcache.core.statistics.CacheOperationOutcomes.PutIfAbsentOutcome; +import static org.ehcache.core.statistics.CacheOperationOutcomes.RemoveOutcome; +import static org.ehcache.core.statistics.CacheOperationOutcomes.ReplaceOutcome; +import static org.ehcache.core.statistics.SuppliedValueStatistic.counter; + +/** + * Contains usage statistics relative to a given cache. + */ +public class DefaultCacheStatistics implements CacheStatistics { + + private volatile CompensatingCounters compensatingCounters = CompensatingCounters.empty(); + + private final org.terracotta.statistics.OperationStatistic get; + private final org.terracotta.statistics.OperationStatistic put; + private final org.terracotta.statistics.OperationStatistic remove; + private final org.terracotta.statistics.OperationStatistic putIfAbsent; + private final org.terracotta.statistics.OperationStatistic replace; + private final org.terracotta.statistics.OperationStatistic conditionalRemove; + + private final InternalCache cache; + + private final Map tierStatistics; + private final TierStatistics lowestTier; + + private final Map> knownStatistics; + + public DefaultCacheStatistics(InternalCache cache) { + this.cache = cache; + + get = findOperationStatisticOnChildren(cache, GetOutcome.class, "get"); + put = findOperationStatisticOnChildren(cache, PutOutcome.class, "put"); + remove = findOperationStatisticOnChildren(cache, RemoveOutcome.class, "remove"); + putIfAbsent = findOperationStatisticOnChildren(cache, PutIfAbsentOutcome.class, "putIfAbsent"); + replace = findOperationStatisticOnChildren(cache, ReplaceOutcome.class, "replace"); + conditionalRemove = findOperationStatisticOnChildren(cache, ConditionalRemoveOutcome.class, "conditionalRemove"); + + String[] tierNames = findTiers(cache); + + String lowestTierName = findLowestTier(tierNames); + TierStatistics lowestTier = null; + + tierStatistics = new HashMap<>(tierNames.length); + for (String tierName : tierNames) { + DefaultTierStatistics tierStatistics = new DefaultTierStatistics(cache, tierName); + this.tierStatistics.put(tierName, tierStatistics); + if (lowestTierName.equals(tierName)) { + lowestTier = tierStatistics; + } + } + this.lowestTier = lowestTier; + + knownStatistics = createKnownStatistics(); + } + + @Override + public , S extends ChainedOperationObserver> void registerDerivedStatistic(Class outcomeClass, String statName, S derivedStatistic) { + OperationStatistic stat = new DelegatingOperationStatistic<>(findOperationStatisticOnChildren(cache, outcomeClass, statName)); + stat.addDerivedStatistic(derivedStatistic); + } + + private Map> createKnownStatistics() { + Map> knownStatistics = new HashMap<>(30); + knownStatistics.put("Cache:HitCount", ValueStatistics.counter(this::getCacheHits)); + knownStatistics.put("Cache:MissCount", ValueStatistics.counter(this::getCacheMisses)); + knownStatistics.put("Cache:PutCount", ValueStatistics.counter(this::getCachePuts)); + knownStatistics.put("Cache:RemovalCount", ValueStatistics.counter(this::getCacheRemovals)); + knownStatistics.put("Cache:EvictionCount", ValueStatistics.counter(this::getCacheEvictions)); + knownStatistics.put("Cache:ExpirationCount", ValueStatistics.counter(this::getCacheExpirations)); + + for (DefaultTierStatistics tier : tierStatistics.values()) { + knownStatistics.putAll(tier.getKnownStatistics()); + } + + return Collections.unmodifiableMap(knownStatistics); + } + + public Map> getKnownStatistics() { + return knownStatistics; + } + + public Map getTierStatistics() { + return Collections.unmodifiableMap(tierStatistics); + } + + @Override + public void clear() { + compensatingCounters = compensatingCounters.snapshot(this); + for (TierStatistics t : tierStatistics.values()) { + t.clear(); + } + } + + @Override + public long getCacheHits() { + return normalize(getHits() - compensatingCounters.cacheHits); + } + + @Override + public float getCacheHitPercentage() { + long cacheHits = getCacheHits(); + return normalize((float) cacheHits / (cacheHits + getCacheMisses())) * 100.0f; + } + + @Override + public long getCacheMisses() { + return normalize(getMisses() - compensatingCounters.cacheMisses); + } + + @Override + public float getCacheMissPercentage() { + long cacheMisses = getCacheMisses(); + return normalize((float) cacheMisses / (getCacheHits() + cacheMisses)) * 100.0f; + } + + @Override + public long getCacheGets() { + return normalize(getHits() + getMisses() - compensatingCounters.cacheGets); + } + + @Override + public long getCachePuts() { + return normalize(getBulkCount(BulkOps.PUT_ALL) + + put.sum(EnumSet.of(PutOutcome.PUT)) + + putIfAbsent.sum(EnumSet.of(PutIfAbsentOutcome.PUT)) + + replace.sum(EnumSet.of(ReplaceOutcome.HIT)) - + compensatingCounters.cachePuts); + } + + @Override + public long getCacheRemovals() { + return normalize(getBulkCount(BulkOps.REMOVE_ALL) + + remove.sum(EnumSet.of(RemoveOutcome.SUCCESS)) + + conditionalRemove.sum(EnumSet.of(ConditionalRemoveOutcome.SUCCESS)) - + compensatingCounters.cacheRemovals); + } + + @Override + public long getCacheEvictions() { + return normalize(lowestTier.getEvictions()); + } + + @Override + public long getCacheExpirations() { + return normalize(lowestTier.getExpirations()); + } + + private long getMisses() { + return getBulkCount(BulkOps.GET_ALL_MISS) + + get.sum(EnumSet.of(GetOutcome.MISS)) + + putIfAbsent.sum(EnumSet.of(PutIfAbsentOutcome.PUT)) + + replace.sum(EnumSet.of(ReplaceOutcome.MISS_NOT_PRESENT)) + + conditionalRemove.sum(EnumSet.of(ConditionalRemoveOutcome.FAILURE_KEY_MISSING)); + } + + private long getHits() { + return getBulkCount(BulkOps.GET_ALL_HITS) + + get.sum(EnumSet.of(GetOutcome.HIT)) + + putIfAbsent.sum(EnumSet.of(PutIfAbsentOutcome.HIT)) + + replace.sum(EnumSet.of(ReplaceOutcome.HIT, ReplaceOutcome.MISS_PRESENT)) + + conditionalRemove.sum(EnumSet.of(ConditionalRemoveOutcome.SUCCESS, ConditionalRemoveOutcome.FAILURE_KEY_PRESENT)); + } + + private long getBulkCount(BulkOps bulkOps) { + return cache.getBulkMethodEntries().get(bulkOps).longValue(); + } + + private static long normalize(long value) { + return Math.max(0, value); + } + + private static float normalize(float value) { + if (Float.isNaN(value)) { + return 0.0f; + } + return Math.min(1.0f, Math.max(0.0f, value)); + } + + private static class CompensatingCounters { + final long cacheHits; + final long cacheMisses; + final long cacheGets; + final long cachePuts; + final long cacheRemovals; + + private CompensatingCounters(long cacheHits, long cacheMisses, long cacheGets, long cachePuts, long cacheRemovals) { + this.cacheHits = cacheHits; + this.cacheMisses = cacheMisses; + this.cacheGets = cacheGets; + this.cachePuts = cachePuts; + this.cacheRemovals = cacheRemovals; + } + + static CompensatingCounters empty() { + return new CompensatingCounters(0, 0, 0, 0, 0); + } + + CompensatingCounters snapshot(DefaultCacheStatistics statistics) { + return new CompensatingCounters( + cacheHits + statistics.getHits(), + cacheMisses + statistics.getMisses(), + cacheGets + statistics.getCacheGets(), + cachePuts + statistics.getCachePuts(), + cacheRemovals + statistics.getCacheRemovals()); + } + } + +} diff --git a/ehcache-management/src/main/java/org/ehcache/management/statistics/DefaultExtendedStatisticsService.java b/ehcache-management/src/main/java/org/ehcache/management/statistics/DefaultExtendedStatisticsService.java new file mode 100644 index 0000000000..a42566a425 --- /dev/null +++ b/ehcache-management/src/main/java/org/ehcache/management/statistics/DefaultExtendedStatisticsService.java @@ -0,0 +1,241 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.management.statistics; + +import org.ehcache.Cache; +import org.ehcache.Status; +import org.ehcache.config.CacheConfiguration; +import org.ehcache.core.InternalCache; +import org.ehcache.core.events.CacheManagerListener; +import org.ehcache.core.spi.service.CacheManagerProviderService; +import org.ehcache.core.spi.store.InternalCacheManager; +import org.ehcache.core.spi.store.Store; +import org.ehcache.core.statistics.CacheStatistics; +import org.ehcache.core.statistics.StatisticType; +import org.ehcache.core.statistics.OperationObserver; +import org.ehcache.core.statistics.ZeroOperationStatistic; +import org.ehcache.management.ExtendedStatisticsService; +import org.ehcache.management.registry.LatencyHistogramConfiguration; +import org.ehcache.spi.service.Service; +import org.ehcache.spi.service.ServiceDependencies; +import org.ehcache.spi.service.ServiceProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.terracotta.management.model.capabilities.descriptors.StatisticDescriptor; +import org.terracotta.management.model.stats.Statistic; +import org.terracotta.management.model.stats.StatisticRegistry; +import org.terracotta.statistics.MappedOperationStatistic; +import org.terracotta.statistics.OperationStatistic; +import org.terracotta.statistics.StatisticsManager; +import org.terracotta.statistics.derived.OperationResultFilter; +import org.terracotta.statistics.derived.latency.DefaultLatencyHistogramStatistic; + +import java.io.Serializable; +import java.util.Collection; +import java.util.EnumSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.function.LongSupplier; +import java.util.function.Supplier; + +import static org.ehcache.core.internal.statistics.StatsUtils.findOperationStatisticOnChildren; +import static org.terracotta.statistics.StatisticBuilder.operation; + +/** + * Default implementation using the statistics calculated by the observers set on the caches. + */ +@ServiceDependencies(CacheManagerProviderService.class) +public class DefaultExtendedStatisticsService implements ExtendedStatisticsService, CacheManagerListener { + + private static final Logger LOGGER = LoggerFactory.getLogger(DefaultExtendedStatisticsService.class); + + private final ConcurrentMap cacheStatistics = new ConcurrentHashMap<>(); + private final ConcurrentMap statisticRegistries = new ConcurrentHashMap<>(); + + private volatile InternalCacheManager cacheManager; + private volatile boolean started = false; + + public CacheStatistics getCacheStatistics(String cacheName) { + CacheStatistics stats = cacheStatistics.get(cacheName); + if (stats == null) { + throw new IllegalArgumentException("Unknown cache: " + cacheName); + } + return stats; + } + + @Override + public void registerWithParent(Object toAssociate, Object parent) { + StatisticsManager.associate(toAssociate).withParent(parent); + } + + @Override + public , T extends Enum> org.ehcache.core.statistics.OperationStatistic registerStoreStatistics(Store store, String targetName, int tierHeight, String tag, Map> translation, String statisticName) { + + Class outcomeType = getOutcomeType(translation); + + // If the original stat doesn't exist, we do not need to translate it + if (StatsUtils.hasOperationStat(store, outcomeType, targetName)) { + + MappedOperationStatistic operationStatistic = new MappedOperationStatistic<>(store, translation, statisticName, tierHeight, targetName, tag); + StatisticsManager.associate(operationStatistic).withParent(store); + return new DelegatedMappedOperationStatistics<>(operationStatistic); + } + return ZeroOperationStatistic.get(); + } + + /** + * From the Map of translation, we extract one of the items to get the declaring class of the enum. + * + * @param translation translation map + * @param type of the outcome + * @param type of the possible translations + * @return the outcome type + */ + private static , T extends Enum> Class getOutcomeType(Map> translation) { + Map.Entry> first = translation.entrySet().iterator().next(); + return first.getValue().iterator().next().getDeclaringClass(); + } + + @Override + public void deRegisterFromParent(Object toDisassociate, Object parent) { + StatisticsManager.dissociate(toDisassociate).fromParent(parent); + } + + @Override + public void cleanForNode(Object node) { + StatisticsManager.nodeFor(node).clean(); + } + + @Override + public void createCacheRegistry(String cacheName, Cache cache, LongSupplier timeSource) { + statisticRegistries.put(cacheName, new StatisticRegistry(cache, timeSource)); + } + + @Override + public void registerCacheStatistics(String cacheName) { + cacheStatistics.get(cacheName).getKnownStatistics().forEach(statisticRegistries.get(cacheName)::registerStatistic); + } + + @Override + public Collection getCacheDescriptors(String cacheName) { + return statisticRegistries.get(cacheName).getDescriptors(); + } + + @Override + public , K, V> void registerDerivedStatistics(String cacheName, Cache cache, String statName, T outcome, String derivedName, LatencyHistogramConfiguration configuration) { + DefaultLatencyHistogramStatistic histogram = new DefaultLatencyHistogramStatistic(configuration.getPhi(), configuration.getBucketCount(), configuration.getWindow()); + + @SuppressWarnings("unchecked") + Class outcomeClass = (Class) outcome.getClass(); + OperationStatistic stat = findOperationStatisticOnChildren(cache, outcomeClass, statName); + stat.addDerivedStatistic(new OperationResultFilter<>(EnumSet.of(outcome), histogram)); + + statisticRegistries.get(cacheName).registerStatistic(derivedName + "#50", histogram.medianStatistic()); + statisticRegistries.get(cacheName).registerStatistic(derivedName + "#95", histogram.percentileStatistic(0.95)); + statisticRegistries.get(cacheName).registerStatistic(derivedName + "#99", histogram.percentileStatistic(0.99)); + statisticRegistries.get(cacheName).registerStatistic(derivedName + "#100", histogram.maximumStatistic()); + } + + @Override + public Map> collectStatistics(String cacheName, Collection statisticNames, long since) { + return StatisticRegistry.collect(statisticRegistries.get(cacheName), statisticNames, since); + } + + @Override + public void registerStatistic(Object context, String name, StatisticType type, Set tags, Supplier valueSupplier) { + StatisticsManager.createPassThroughStatistic(context, name, tags, convert(type), valueSupplier); + } + + @Override + public > OperationObserver createOperationStatistics(String name, Class outcome, String tag, Object context) { + return new DelegatingOperationObserver<>(operation(outcome).named(name).of(context).tag(tag).build()); + } + + public boolean isStarted() { + return started; + } + + @Override + public void start(ServiceProvider serviceProvider) { + LOGGER.debug("Starting service"); + + CacheManagerProviderService cacheManagerProviderService = serviceProvider.getService(CacheManagerProviderService.class); + cacheManager = cacheManagerProviderService.getCacheManager(); + cacheManager.registerListener(this); + started = true; + } + + @Override + public void stop() { + LOGGER.debug("Stopping service"); + cacheManager.deregisterListener(this); + cacheStatistics.clear(); + started = false; + } + + @Override + public void stateTransition(Status from, Status to) { + LOGGER.debug("Moving from " + from + " to " + to); + switch (to) { + case AVAILABLE: + registerAllCaches(); + break; + case UNINITIALIZED: + cacheManager.deregisterListener(this); + cacheStatistics.clear(); + break; + case MAINTENANCE: + throw new IllegalStateException("Should not be started in maintenance mode"); + default: + throw new AssertionError("Unsupported state: " + to); + } + } + + private void registerAllCaches() { + for (Map.Entry> entry : cacheManager.getRuntimeConfiguration().getCacheConfigurations().entrySet()) { + String alias = entry.getKey(); + CacheConfiguration configuration = entry.getValue(); + Cache cache = cacheManager.getCache(alias, configuration.getKeyType(), configuration.getValueType()); + cacheAdded(alias, cache); + } + } + + @Override + public void cacheAdded(String alias, Cache cache) { + LOGGER.debug("Cache added " + alias); + cacheStatistics.put(alias, new DefaultCacheStatistics((InternalCache) cache)); + } + + @Override + public void cacheRemoved(String alias, Cache cache) { + LOGGER.debug("Cache removed " + alias); + cacheStatistics.remove(alias); + } + + private static org.terracotta.statistics.StatisticType convert(StatisticType type) { + switch (type) { + case COUNTER: + return org.terracotta.statistics.StatisticType.COUNTER; + case GAUGE: + return org.terracotta.statistics.StatisticType.GAUGE; + default: + throw new IllegalArgumentException("Untranslatable statistic type : " + type); + } + } +} diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/internal/messages/SyncMessageType.java b/ehcache-management/src/main/java/org/ehcache/management/statistics/DefaultExtendedStatisticsServiceFactory.java similarity index 50% rename from clustered/server/src/main/java/org/ehcache/clustered/server/internal/messages/SyncMessageType.java rename to ehcache-management/src/main/java/org/ehcache/management/statistics/DefaultExtendedStatisticsServiceFactory.java index c6a60d6155..ae43c72ffb 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/internal/messages/SyncMessageType.java +++ b/ehcache-management/src/main/java/org/ehcache/management/statistics/DefaultExtendedStatisticsServiceFactory.java @@ -14,28 +14,26 @@ * limitations under the License. */ -package org.ehcache.clustered.server.internal.messages; +package org.ehcache.management.statistics; -import org.terracotta.runnel.EnumMapping; +import org.ehcache.core.spi.service.ServiceFactory; +import org.ehcache.management.ExtendedStatisticsService; +import org.ehcache.spi.service.ServiceCreationConfiguration; -import com.tc.classloader.CommonComponent; +public class DefaultExtendedStatisticsServiceFactory implements ServiceFactory { -import static org.terracotta.runnel.EnumMappingBuilder.newEnumMappingBuilder; + @Override + public int rank() { + return 10; + } -/** - * SyncMessageType - */ -@CommonComponent -public enum SyncMessageType { - STATE_REPO, - DATA, - MESSAGE_TRACKER; + @Override + public ExtendedStatisticsService create(ServiceCreationConfiguration configuration) { + return new DefaultExtendedStatisticsService(); + } - public static final String SYNC_MESSAGE_TYPE_FIELD_NAME = "msgType"; - public static final int SYNC_MESSAGE_TYPE_FIELD_INDEX = 10; - public static final EnumMapping SYNC_MESSAGE_TYPE_MAPPING = newEnumMappingBuilder(SyncMessageType.class) - .mapping(STATE_REPO, 1) - .mapping(DATA, 10) - .mapping(MESSAGE_TRACKER, 20) - .build(); + @Override + public Class getServiceType() { + return DefaultExtendedStatisticsService.class; + } } diff --git a/impl/src/main/java/org/ehcache/impl/internal/statistics/DefaultTierStatistics.java b/ehcache-management/src/main/java/org/ehcache/management/statistics/DefaultTierStatistics.java similarity index 85% rename from impl/src/main/java/org/ehcache/impl/internal/statistics/DefaultTierStatistics.java rename to ehcache-management/src/main/java/org/ehcache/management/statistics/DefaultTierStatistics.java index 565811498f..742ee19cd6 100755 --- a/impl/src/main/java/org/ehcache/impl/internal/statistics/DefaultTierStatistics.java +++ b/ehcache-management/src/main/java/org/ehcache/management/statistics/DefaultTierStatistics.java @@ -14,14 +14,16 @@ * limitations under the License. */ -package org.ehcache.impl.internal.statistics; +package org.ehcache.management.statistics; import org.ehcache.Cache; +import org.ehcache.core.internal.statistics.StatsUtils; import org.ehcache.core.statistics.StoreOperationOutcomes; import org.ehcache.core.statistics.TierOperationOutcomes; import org.ehcache.core.statistics.TierStatistics; +import org.ehcache.core.statistics.ValueStatistic; import org.terracotta.statistics.OperationStatistic; -import org.terracotta.statistics.ValueStatistic; +import org.terracotta.statistics.ValueStatistics; import org.terracotta.statistics.ZeroOperationStatistic; import java.util.Collections; @@ -31,18 +33,18 @@ import java.util.Optional; import java.util.function.Supplier; -import static org.ehcache.impl.internal.statistics.StatsUtils.findStatisticOnDescendants; -import static org.terracotta.statistics.ValueStatistics.counter; -import static org.terracotta.statistics.ValueStatistics.gauge; +import static org.ehcache.core.internal.statistics.StatsUtils.findStatisticOnDescendants; +import static org.ehcache.core.statistics.SuppliedValueStatistic.counter; +import static org.ehcache.core.statistics.SuppliedValueStatistic.gauge; /** * Contains usage statistics relative to a given tier. */ -class DefaultTierStatistics implements TierStatistics { +public class DefaultTierStatistics implements TierStatistics { private volatile CompensatingCounters compensatingCounters = CompensatingCounters.empty(); - private final Map> knownStatistics; + private final Map> knownStatistics; private final OperationStatistic get; private final OperationStatistic put; @@ -80,24 +82,24 @@ public DefaultTierStatistics(Cache cache, String tierName) { allocatedMemory = findValueStatistics(cache, tierName, "allocatedMemory"); occupiedMemory = findValueStatistics(cache, tierName, "occupiedMemory"); - Map> knownStatistics = createKnownStatistics(tierName); + Map> knownStatistics = createKnownStatistics(tierName); this.knownStatistics = Collections.unmodifiableMap(knownStatistics); } - private Map> createKnownStatistics(String tierName) { - Map> knownStatistics = new HashMap<>(7); + private Map> createKnownStatistics(String tierName) { + Map> knownStatistics = new HashMap<>(7); addIfPresent(knownStatistics, tierName + ":HitCount", get, this::getHits); addIfPresent(knownStatistics, tierName + ":MissCount", get, this::getMisses); addIfPresent(knownStatistics, tierName + ":PutCount", put, this::getPuts); addIfPresent(knownStatistics, tierName + ":RemovalCount", remove, this::getRemovals); // These two a special because they are used by the cache so they should always be there - knownStatistics.put(tierName + ":EvictionCount", counter(this::getEvictions)); - knownStatistics.put(tierName + ":ExpirationCount", counter(this::getExpirations)); + knownStatistics.put(tierName + ":EvictionCount", ValueStatistics.counter(this::getEvictions)); + knownStatistics.put(tierName + ":ExpirationCount", ValueStatistics.counter(this::getExpirations)); - mapping.ifPresent(longValueStatistic -> knownStatistics.put(tierName + ":MappingCount", gauge(this::getMappings))); - allocatedMemory.ifPresent(longValueStatistic -> knownStatistics.put(tierName + ":AllocatedByteSize", gauge(this::getAllocatedByteSize))); - occupiedMemory.ifPresent(longValueStatistic -> knownStatistics.put(tierName + ":OccupiedByteSize", gauge(this::getOccupiedByteSize))); + mapping.ifPresent(longValueStatistic -> knownStatistics.put(tierName + ":MappingCount", ValueStatistics.gauge(this::getMappings))); + allocatedMemory.ifPresent(longValueStatistic -> knownStatistics.put(tierName + ":AllocatedByteSize", ValueStatistics.gauge(this::getAllocatedByteSize))); + occupiedMemory.ifPresent(longValueStatistic -> knownStatistics.put(tierName + ":OccupiedByteSize", ValueStatistics.gauge(this::getOccupiedByteSize))); return knownStatistics; } @@ -111,14 +113,13 @@ private Map> createKnownStatistics(String tierName) { * @param valueSupplier the supplier that will provide the current value for the statistic * @param type of the supplied value */ - private static void addIfPresent(Map> knownStatistics, String name, OperationStatistic reference, Supplier valueSupplier) { + private static void addIfPresent(Map> knownStatistics, String name, OperationStatistic reference, Supplier valueSupplier) { if(!(reference instanceof ZeroOperationStatistic)) { - knownStatistics.put(name, counter(valueSupplier)); + knownStatistics.put(name, ValueStatistics.counter(valueSupplier)); } } - @Override - public Map> getKnownStatistics() { + public Map> getKnownStatistics() { return knownStatistics; } diff --git a/ehcache-management/src/main/java/org/ehcache/management/statistics/DelegatedMappedOperationStatistics.java b/ehcache-management/src/main/java/org/ehcache/management/statistics/DelegatedMappedOperationStatistics.java new file mode 100644 index 0000000000..cfbb3be33d --- /dev/null +++ b/ehcache-management/src/main/java/org/ehcache/management/statistics/DelegatedMappedOperationStatistics.java @@ -0,0 +1,109 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.management.statistics; + +import org.ehcache.core.statistics.ChainedOperationObserver; +import org.ehcache.core.statistics.OperationStatistic; +import org.terracotta.statistics.MappedOperationStatistic; + +import java.util.Collection; +import java.util.Set; +import java.util.stream.Collectors; + +public class DelegatedMappedOperationStatistics, D extends Enum> implements OperationStatistic { + + private final MappedOperationStatistic delegate; + + public DelegatedMappedOperationStatistics(MappedOperationStatistic operationStatistic) { + this.delegate = operationStatistic; + } + + @Override + public Class type() { + return delegate.type(); + } + + @Override + public long count(D type) { + return delegate.count(type); + } + + @Override + public long sum(Set types) { + return delegate.sum(types); + } + + @Override + public long sum() { + return delegate.sum(); + } + + @Override + public void begin() { + delegate.begin(); + } + + @Override + public void end(D result) { + delegate.end(result); + } + + @Override + public void addDerivedStatistic(ChainedOperationObserver derived) { + delegate.addDerivedStatistic(convert(derived)); + } + + @Override + public void removeDerivedStatistic(ChainedOperationObserver derived) { + delegate.removeDerivedStatistic(convert(derived)); + } + + @Override + public Collection> getDerivedStatistics() { + Collection> derivedStatistics = delegate.getDerivedStatistics(); + return derivedStatistics.stream().map(this::revert).collect(Collectors.toSet()); + } + + private ChainedOperationObserver revert(org.terracotta.statistics.observer.ChainedOperationObserver observer) { + return new ChainedOperationObserver() { + @Override + public void begin(long time) { + observer.begin(time); + } + + @Override + public void end(long time, long latency, D result) { + observer.end(time, latency, result); + } + }; + } + + private org.terracotta.statistics.observer.ChainedOperationObserver convert(ChainedOperationObserver observer) { + return new org.terracotta.statistics.observer.ChainedOperationObserver() { + @Override + public void begin(long time) { + observer.begin(time); + } + + @Override + public void end(long time, long latency, D result) { + observer.end(time, latency, result); + } + }; + } + + +} diff --git a/ehcache-management/src/main/java/org/ehcache/management/statistics/DelegatingOperationObserver.java b/ehcache-management/src/main/java/org/ehcache/management/statistics/DelegatingOperationObserver.java new file mode 100644 index 0000000000..773b28d11a --- /dev/null +++ b/ehcache-management/src/main/java/org/ehcache/management/statistics/DelegatingOperationObserver.java @@ -0,0 +1,37 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.management.statistics; + +import org.ehcache.core.statistics.OperationObserver; + +public class DelegatingOperationObserver> implements OperationObserver { + + private final org.terracotta.statistics.observer.OperationObserver observer; + + public DelegatingOperationObserver(org.terracotta.statistics.observer.OperationObserver operationObserver) { + this.observer = operationObserver; + } + + @Override + public void begin() { + this.observer.begin(); + } + + @Override + public void end(T result) { + this.observer.end(result); + } +} diff --git a/ehcache-management/src/main/java/org/ehcache/management/statistics/StatsUtils.java b/ehcache-management/src/main/java/org/ehcache/management/statistics/StatsUtils.java new file mode 100644 index 0000000000..39f313ce5f --- /dev/null +++ b/ehcache-management/src/main/java/org/ehcache/management/statistics/StatsUtils.java @@ -0,0 +1,270 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.management.statistics; + +import org.ehcache.Cache; +import org.ehcache.core.statistics.CacheOperationOutcomes; +import org.ehcache.core.statistics.StoreOperationOutcomes; +import org.terracotta.context.ContextManager; +import org.terracotta.context.TreeNode; +import org.terracotta.context.query.Matcher; +import org.terracotta.context.query.Matchers; +import org.terracotta.context.query.Query; +import org.terracotta.statistics.OperationStatistic; +import org.terracotta.statistics.derived.OperationResultFilter; + +import java.util.Collections; +import java.util.EnumSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.Consumer; + +import static org.terracotta.context.query.Matchers.attributes; +import static org.terracotta.context.query.Matchers.context; +import static org.terracotta.context.query.Matchers.hasAttribute; +import static org.terracotta.context.query.Matchers.identifier; +import static org.terracotta.context.query.Matchers.subclassOf; +import static org.terracotta.context.query.QueryBuilder.queryBuilder; + +/** + * Class allowing to query cache and tier statistics + */ +public final class StatsUtils { + + private StatsUtils() {} + + public static Matcher> hasTag(final String tag) { + return hasAttribute("tags", new Matcher>() { + @Override + protected boolean matchesSafely(Set object) { + return object.contains(tag); + } + }); + } + + public static Matcher> hasProperty(final String key, final String value) { + return hasAttribute("properties", new Matcher>() { + @Override + protected boolean matchesSafely(Map properties) { + Object val = properties.get(key); + return val != null && value.equals(val); + } + }); + } + + /** + * Search for a statistic on the descendant of the context that matches the tag and statistic name. + * + * @param context the context of the query + * @param discriminator a filter on the discriminator property + * @param tag the tag we are looking for + * @param statName statistic name + * @param type of the statistic that will be returned + * @return the wanted statistic or null if no such statistic is found + * @throws RuntimeException when more than one matching statistic is found + */ + public static Optional findStatisticOnDescendants(Object context, String discriminator, String tag, String statName) { + + @SuppressWarnings("unchecked") + Set statResult = queryBuilder() + .descendants() + .filter(context(attributes(Matchers.allOf( + hasAttribute("name", statName), + hasProperty("discriminator", discriminator), + hasTag(tag))))) + .build().execute(Collections.singleton(ContextManager.nodeFor(context))); + + if (statResult.size() > 1) { + throw new RuntimeException("One stat expected for " + statName + " but found " + statResult.size()); + } + + if (statResult.size() == 1) { + @SuppressWarnings("unchecked") + T result = (T) statResult.iterator().next().getContext().attributes().get("this"); + return Optional.ofNullable(result); + } + + // No such stat in this context + return Optional.empty(); + } + + /** + * Search for a statistic on the descendant of the context that matches the tag and statistic name. + * + * @param context the context of the query + * @param tag the tag we are looking for + * @param statName statistic name + * @param type of the statistic that will be returned + * @return the wanted statistic or null if no such statistic is found + * @throws RuntimeException when more than one matching statistic is found + */ + public static Optional findStatisticOnDescendants(Object context, String tag, String statName) { + + @SuppressWarnings("unchecked") + Set statResult = queryBuilder() + .descendants() + .filter(context(attributes(Matchers.allOf( + hasAttribute("name", statName), + hasTag(tag))))) + .build().execute(Collections.singleton(ContextManager.nodeFor(context))); + + if (statResult.size() > 1) { + throw new RuntimeException("One stat expected for " + statName + " but found " + statResult.size()); + } + + if (statResult.size() == 1) { + @SuppressWarnings("unchecked") + T result = (T) statResult.iterator().next().getContext().attributes().get("this"); + return Optional.ofNullable(result); + } + + // No such stat in this context + return Optional.empty(); + } + + /** + * Find an operation statistic attached (as a children) to this context that matches the statistic name and type + * + * @param context the context of the query + * @param type type of the operation statistic + * @param statName statistic name + * @param type of the operation statistic content + * @return the operation statistic searched for + * @throws RuntimeException if 0 or more than 1 result is found + */ + public static > OperationStatistic findOperationStatisticOnChildren(Object context, Class type, String statName) { + @SuppressWarnings("unchecked") + Query query = queryBuilder() + .children() + .filter(context(attributes(Matchers.allOf(hasAttribute("name", statName), hasAttribute("type", type))))) + .build(); + + Set result = query.execute(Collections.singleton(ContextManager.nodeFor(context))); + if (result.size() > 1) { + throw new RuntimeException("result must be unique"); + } + if (result.isEmpty()) { + throw new RuntimeException("result must not be null"); + } + @SuppressWarnings("unchecked") + OperationStatistic statistic = (OperationStatistic) result.iterator().next().getContext().attributes().get("this"); + return statistic; + } + + /** + * Find the list of tiers of a cache. We assume a lot of things here. + *
    + *
  • The "eviction" statistic is available on the tier
  • + *
  • That the tiers have only one tag attribute
  • + *
  • That this tag contains the tier name
  • + *
  • That the only descendants having an "eviction" statistic are the tiers
  • + *
+ * + * @param cache the context for looking for tiers + * @return an array of tier names + * @throws RuntimeException if not tiers are found or if tiers have multiple tags + */ + public static String[] findTiers(Cache cache) { + // Here I'm randomly taking the eviction observer because it exists on all tiers + @SuppressWarnings("unchecked") + Query statQuery = queryBuilder() + .descendants() + .filter(context(attributes(Matchers.allOf(hasAttribute("name", "eviction"), hasAttribute("type", StoreOperationOutcomes.EvictionOutcome.class))))) + .build(); + + Set statResult = statQuery.execute(Collections.singleton(ContextManager.nodeFor(cache))); + + if (statResult.isEmpty()) { + throw new RuntimeException("Failed to find tiers using the eviction observer, valid result Set sizes must 1 or more"); + } + + String[] tiers = new String[statResult.size()]; + + int i = 0; + for (TreeNode treeNode : statResult) { + Set tags = (Set) treeNode.getContext().attributes().get("tags"); + if (tags.size() != 1) { + throw new RuntimeException("We expect tiers to have only one tag"); + } + + String storeType = tags.iterator().next().toString(); + tiers[i++] = storeType; + } + return tiers; + } + + /** + * Find the lowest tier from a list of tier. We assume a lot of things here that the tiers depth + * magically matches the alphabetical order. + * + * @param tiers all tiers + * @return the lowest tier + */ + public static String findLowestTier(String[] tiers) { + //if only 1 store then you don't need to find the lowest tier + if (tiers.length == 1) { + return tiers[0]; + } + + //we expect at least one tier + if (tiers.length == 0) { + throw new RuntimeException("No existing tier"); + } + + // We rely here on the alphabetical order matching the depth order so from highest to lowest we have + // OnHeap, OffHeap, Disk, Clustered + String lowestTier = tiers[0]; + for (int i = 1; i < tiers.length; i++) { + if (tiers[i].compareTo(lowestTier) < 0) { + lowestTier = tiers[i]; + } + } + + return lowestTier; + } + + public static > boolean hasOperationStat(Object rootNode, Class statisticType, String statName) { + Query q = queryBuilder().descendants() + .filter(context(identifier(subclassOf(OperationStatistic.class)))) + .filter(context(attributes(Matchers.allOf( + hasAttribute("name", statName), + hasAttribute("this", new Matcher>() { + @Override + protected boolean matchesSafely(OperationStatistic object) { + return object.type().equals(statisticType); + } + }) + )))) + .build(); + + Set result = q.execute(Collections.singleton(ContextManager.nodeFor(rootNode))); + + if (result.size() > 1) { + throw new RuntimeException("a zero or a single stat was expected; found " + result.size()); + } + + return !result.isEmpty(); + } + + public static void registerClearNotification(String alias, Cache cache, Consumer cacheClear) { + OperationStatistic clear = StatsUtils.findOperationStatisticOnChildren(cache, + CacheOperationOutcomes.ClearOutcome.class, "clear"); + clear.addDerivedStatistic(new OperationResultFilter<>(EnumSet.of(CacheOperationOutcomes.ClearOutcome.SUCCESS), + (time, latency) -> cacheClear.accept(alias))); + } +} diff --git a/ehcache-management/src/main/resources/META-INF/services/org.ehcache.core.spi.service.ServiceFactory b/ehcache-management/src/main/resources/META-INF/services/org.ehcache.core.spi.service.ServiceFactory new file mode 100644 index 0000000000..fb6fb41c9f --- /dev/null +++ b/ehcache-management/src/main/resources/META-INF/services/org.ehcache.core.spi.service.ServiceFactory @@ -0,0 +1,2 @@ +org.ehcache.management.registry.DefaultManagementRegistryFactory +org.ehcache.management.statistics.DefaultExtendedStatisticsServiceFactory diff --git a/management/src/main/resources/META-INF/services/org.ehcache.xml.CacheManagerServiceConfigurationParser b/ehcache-management/src/main/resources/META-INF/services/org.ehcache.xml.CacheManagerServiceConfigurationParser similarity index 100% rename from management/src/main/resources/META-INF/services/org.ehcache.xml.CacheManagerServiceConfigurationParser rename to ehcache-management/src/main/resources/META-INF/services/org.ehcache.xml.CacheManagerServiceConfigurationParser diff --git a/management/src/main/resources/ehcache-management-ext.xsd b/ehcache-management/src/main/resources/ehcache-management-ext.xsd similarity index 93% rename from management/src/main/resources/ehcache-management-ext.xsd rename to ehcache-management/src/main/resources/ehcache-management-ext.xsd index 13d8130b11..927129c9c9 100644 --- a/management/src/main/resources/ehcache-management-ext.xsd +++ b/ehcache-management/src/main/resources/ehcache-management-ext.xsd @@ -34,7 +34,7 @@ - + diff --git a/management/src/test/java/org/ehcache/docs/ManagementTest.java b/ehcache-management/src/test/java/org/ehcache/docs/ManagementTest.java similarity index 88% rename from management/src/test/java/org/ehcache/docs/ManagementTest.java rename to ehcache-management/src/test/java/org/ehcache/docs/ManagementTest.java index 236378eb8c..53bebb923f 100644 --- a/management/src/test/java/org/ehcache/docs/ManagementTest.java +++ b/ehcache-management/src/test/java/org/ehcache/docs/ManagementTest.java @@ -29,7 +29,6 @@ import org.ehcache.management.registry.DefaultManagementRegistryService; import org.ehcache.management.registry.DefaultSharedManagementService; import org.hamcrest.Matchers; -import org.junit.Assert; import org.junit.Test; import org.terracotta.management.model.call.Parameter; import org.terracotta.management.model.capabilities.Capability; @@ -45,6 +44,8 @@ import java.util.Collection; import java.util.Iterator; +import static org.hamcrest.MatcherAssert.assertThat; + public class ManagementTest { @Test @@ -94,7 +95,7 @@ public void usingManagementRegistry() throws Exception { ContextualStatistics statisticsContext = counters.getResult(context); - Assert.assertThat(counters.size(), Matchers.is(1)); + assertThat(counters.size(), Matchers.is(1)); } finally { if(cacheManager != null) cacheManager.close(); @@ -118,30 +119,33 @@ public void capabilitiesAndContexts() throws Exception { Collection capabilities = managementRegistry.getCapabilities(); // <1> - Assert.assertThat(capabilities.isEmpty(), Matchers.is(false)); + assertThat(capabilities.isEmpty(), Matchers.is(false)); Capability capability = capabilities.iterator().next(); String capabilityName = capability.getName(); // <2> Collection capabilityDescriptions = capability.getDescriptors(); // <3> - Assert.assertThat(capabilityDescriptions.isEmpty(), Matchers.is(false)); + assertThat(capabilityDescriptions.isEmpty(), Matchers.is(false)); CapabilityContext capabilityContext = capability.getCapabilityContext(); Collection attributes = capabilityContext.getAttributes(); // <4> - Assert.assertThat(attributes.size(), Matchers.is(2)); + assertThat(attributes.size(), Matchers.is(3)); Iterator iterator = attributes.iterator(); CapabilityContext.Attribute attribute1 = iterator.next(); - Assert.assertThat(attribute1.getName(), Matchers.equalTo("cacheManagerName")); // <5> - Assert.assertThat(attribute1.isRequired(), Matchers.is(true)); + assertThat(attribute1.getName(), Matchers.equalTo("instanceId")); // <5> + assertThat(attribute1.isRequired(), Matchers.is(true)); CapabilityContext.Attribute attribute2 = iterator.next(); - Assert.assertThat(attribute2.getName(), Matchers.equalTo("cacheName")); // <6> - Assert.assertThat(attribute2.isRequired(), Matchers.is(true)); + assertThat(attribute2.getName(), Matchers.equalTo("cacheManagerName")); // <5> + assertThat(attribute2.isRequired(), Matchers.is(true)); + CapabilityContext.Attribute attribute3 = iterator.next(); + assertThat(attribute3.getName(), Matchers.equalTo("cacheName")); // <6> + assertThat(attribute3.isRequired(), Matchers.is(true)); ContextContainer contextContainer = managementRegistry.getContextContainer(); // <7> - Assert.assertThat(contextContainer.getName(), Matchers.equalTo("cacheManagerName")); // <8> - Assert.assertThat(contextContainer.getValue(), Matchers.startsWith("cache-manager-")); + assertThat(contextContainer.getName(), Matchers.equalTo("cacheManagerName")); // <8> + assertThat(contextContainer.getValue(), Matchers.startsWith("cache-manager-")); Collection subContexts = contextContainer.getSubContexts(); - Assert.assertThat(subContexts.size(), Matchers.is(1)); + assertThat(subContexts.size(), Matchers.is(1)); ContextContainer subContextContainer = subContexts.iterator().next(); - Assert.assertThat(subContextContainer.getName(), Matchers.equalTo("cacheName")); // <9> - Assert.assertThat(subContextContainer.getValue(), Matchers.equalTo("aCache")); + assertThat(subContextContainer.getName(), Matchers.equalTo("cacheName")); // <9> + assertThat(subContextContainer.getValue(), Matchers.equalTo("aCache")); } finally { if(cacheManager != null) cacheManager.close(); @@ -175,7 +179,7 @@ public void actionCall() throws Exception { .build() .execute(); - Assert.assertThat(aCache.get(0L), Matchers.is(Matchers.nullValue())); // <4> + assertThat(aCache.get(0L), Matchers.is(Matchers.nullValue())); // <4> } finally { if(cacheManager != null) cacheManager.close(); diff --git a/management/src/test/java/org/ehcache/management/ManagementRegistryServiceConfigurationParserIT.java b/ehcache-management/src/test/java/org/ehcache/management/ManagementRegistryServiceConfigurationParserIT.java similarity index 64% rename from management/src/test/java/org/ehcache/management/ManagementRegistryServiceConfigurationParserIT.java rename to ehcache-management/src/test/java/org/ehcache/management/ManagementRegistryServiceConfigurationParserIT.java index f30d6969ef..4c33101298 100644 --- a/management/src/test/java/org/ehcache/management/ManagementRegistryServiceConfigurationParserIT.java +++ b/ehcache-management/src/test/java/org/ehcache/management/ManagementRegistryServiceConfigurationParserIT.java @@ -17,16 +17,15 @@ import org.ehcache.config.Configuration; import org.ehcache.xml.XmlConfiguration; -import org.ehcache.xml.XmlConfigurationTest; import org.junit.Test; -import org.xmlunit.builder.Input; -import org.xmlunit.diff.DefaultNodeMatcher; -import org.xmlunit.diff.ElementSelectors; +import javax.xml.namespace.QName; import java.net.URL; -import static org.junit.Assert.assertThat; -import static org.xmlunit.matchers.CompareMatcher.isSimilarTo; +import static org.ehcache.xml.XmlConfigurationMatchers.isSameConfigurationAs; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.xmlunit.diff.ElementSelectors.byNameAndText; +import static org.xmlunit.diff.ElementSelectors.selectorForElementNamed; /** * ManagementRegistryServiceConfigurationParserIT @@ -35,11 +34,9 @@ public class ManagementRegistryServiceConfigurationParserIT { @Test public void testManagementRegistryXmlTranslationToString() { - URL resource = XmlConfigurationTest.class.getResource("/ehcache-management.xml"); + URL resource = ManagementRegistryServiceConfigurationParserIT.class.getResource("/ehcache-management.xml"); Configuration config = new XmlConfiguration(resource); XmlConfiguration xmlConfig = new XmlConfiguration(config); - assertThat(xmlConfig.toString(), isSimilarTo(Input.from(resource)).ignoreComments() - .ignoreWhitespace() - .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText))); + assertThat(xmlConfig.toString(), isSameConfigurationAs(resource, selectorForElementNamed(new QName("http://www.ehcache.org/v3/management", "tag"), byNameAndText))); } } diff --git a/management/src/test/java/org/ehcache/management/providers/actions/EhcacheActionProviderTest.java b/ehcache-management/src/test/java/org/ehcache/management/providers/actions/EhcacheActionProviderTest.java similarity index 95% rename from management/src/test/java/org/ehcache/management/providers/actions/EhcacheActionProviderTest.java rename to ehcache-management/src/test/java/org/ehcache/management/providers/actions/EhcacheActionProviderTest.java index 060fe2edc4..cdf495f79a 100644 --- a/management/src/test/java/org/ehcache/management/providers/actions/EhcacheActionProviderTest.java +++ b/ehcache-management/src/test/java/org/ehcache/management/providers/actions/EhcacheActionProviderTest.java @@ -45,10 +45,11 @@ public class EhcacheActionProviderTest { - Context cmContext = Context.create("cacheManagerName", "myCacheManagerName"); - Context cmContext_0 = Context.create("cacheManagerName", "cache-manager-0"); - ManagementRegistryServiceConfiguration cmConfig = new DefaultManagementRegistryConfiguration().setContext(cmContext); - ManagementRegistryServiceConfiguration cmConfig_0 = new DefaultManagementRegistryConfiguration().setContext(cmContext_0); + ManagementRegistryServiceConfiguration cmConfig = new DefaultManagementRegistryConfiguration().setCacheManagerAlias("myCacheManagerName"); + ManagementRegistryServiceConfiguration cmConfig_0 = new DefaultManagementRegistryConfiguration().setCacheManagerAlias("cache-manager-0"); + + Context cmContext = cmConfig.getContext(); + Context cmContext_0 = cmConfig_0.getContext(); @Test @SuppressWarnings("unchecked") @@ -77,10 +78,13 @@ public void testCapabilityContext() throws Exception { CapabilityContext capabilityContext = ehcacheActionProvider.getCapabilityContext(); - assertThat(capabilityContext.getAttributes().size(), is(2)); + assertThat(capabilityContext.getAttributes().size(), is(3)); Iterator iterator = capabilityContext.getAttributes().iterator(); CapabilityContext.Attribute next = iterator.next(); + assertThat(next.getName(), equalTo("instanceId")); + assertThat(next.isRequired(), is(true)); + next = iterator.next(); assertThat(next.getName(), equalTo("cacheManagerName")); assertThat(next.isRequired(), is(true)); next = iterator.next(); diff --git a/management/src/test/java/org/ehcache/management/providers/settings/EhcacheSettingsProviderTest.java b/ehcache-management/src/test/java/org/ehcache/management/providers/settings/EhcacheSettingsProviderTest.java similarity index 94% rename from management/src/test/java/org/ehcache/management/providers/settings/EhcacheSettingsProviderTest.java rename to ehcache-management/src/test/java/org/ehcache/management/providers/settings/EhcacheSettingsProviderTest.java index 2e97b92a45..0761bf46ea 100644 --- a/management/src/test/java/org/ehcache/management/providers/settings/EhcacheSettingsProviderTest.java +++ b/ehcache-management/src/test/java/org/ehcache/management/providers/settings/EhcacheSettingsProviderTest.java @@ -106,7 +106,9 @@ public void test_standalone_ehcache() throws IOException { String expected = read("/settings-capability.json") .replaceAll("instance-id", serviceConfiguration.getInstanceId()); String actual = mapper.writeValueAsString(getSettingsCapability()) - .replaceAll("\\\"cacheManagerDescription\\\":\\\".*\\\",\\\"instanceId\\\"", "\\\"cacheManagerDescription\\\":\\\"\\\",\\\"instanceId\\\""); + .replaceAll("\\\"cacheManagerDescription\\\":\\\".*\\\",\\\"instanceId\\\"", "\\\"cacheManagerDescription\\\":\\\"\\\",\\\"instanceId\\\"") + .replaceAll("\\\"instanceId\\\":\\\".*\\\",\\\"managementContext\\\"", "\\\"instanceId\\\":\\\"UUID\\\",\\\"managementContext\\\"") + .replaceAll("\\\"instanceId\\\":\\\".*\\\",\\\"cacheManagerName\\\"", "\\\"instanceId\\\":\\\"UUID\\\",\\\"cacheManagerName\\\""); // assertThat for formatted string comparison: ide support is bad assertEquals(expected, actual); diff --git a/management/src/test/java/org/ehcache/management/providers/statistics/EhcacheStatisticsProviderTest.java b/ehcache-management/src/test/java/org/ehcache/management/providers/statistics/EhcacheStatisticsProviderTest.java similarity index 92% rename from management/src/test/java/org/ehcache/management/providers/statistics/EhcacheStatisticsProviderTest.java rename to ehcache-management/src/test/java/org/ehcache/management/providers/statistics/EhcacheStatisticsProviderTest.java index 5e138956f2..67fbc958b0 100644 --- a/management/src/test/java/org/ehcache/management/providers/statistics/EhcacheStatisticsProviderTest.java +++ b/ehcache-management/src/test/java/org/ehcache/management/providers/statistics/EhcacheStatisticsProviderTest.java @@ -16,13 +16,13 @@ package org.ehcache.management.providers.statistics; import org.ehcache.core.Ehcache; -import org.ehcache.core.spi.service.StatisticsService; import org.ehcache.core.spi.time.SystemTimeSource; import org.ehcache.core.spi.time.TimeSource; -import org.ehcache.impl.internal.statistics.DefaultStatisticsService; +import org.ehcache.management.ExtendedStatisticsService; import org.ehcache.management.ManagementRegistryServiceConfiguration; import org.ehcache.management.providers.CacheBinding; import org.ehcache.management.providers.ExposedCacheBinding; +import org.ehcache.management.statistics.DefaultExtendedStatisticsService; import org.ehcache.management.registry.DefaultManagementRegistryConfiguration; import org.hamcrest.Matcher; import org.junit.After; @@ -49,7 +49,7 @@ public class EhcacheStatisticsProviderTest { ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); - StatisticsService statisticsService = new DefaultStatisticsService(); + ExtendedStatisticsService statisticsService = new DefaultExtendedStatisticsService(); Context cmContext_0 = Context.create("cacheManagerName", "cache-manager-0"); ManagementRegistryServiceConfiguration cmConfig_0 = new DefaultManagementRegistryConfiguration() .setContext(cmContext_0); @@ -101,10 +101,13 @@ protected ExposedCacheBinding wrap(CacheBinding cacheBinding) { CapabilityContext capabilityContext = ehcacheStatisticsProvider.getCapabilityContext(); - assertThat(capabilityContext.getAttributes().size(), is(2)); + assertThat(capabilityContext.getAttributes().size(), is(3)); Iterator iterator = capabilityContext.getAttributes().iterator(); CapabilityContext.Attribute next = iterator.next(); + assertThat(next.getName(), equalTo("instanceId")); + assertThat(next.isRequired(), is(true)); + next = iterator.next(); assertThat(next.getName(), equalTo("cacheManagerName")); assertThat(next.isRequired(), is(true)); next = iterator.next(); diff --git a/management/src/test/java/org/ehcache/management/providers/statistics/LatencyHistogramConfigurationTest.java b/ehcache-management/src/test/java/org/ehcache/management/providers/statistics/LatencyHistogramConfigurationTest.java similarity index 94% rename from management/src/test/java/org/ehcache/management/providers/statistics/LatencyHistogramConfigurationTest.java rename to ehcache-management/src/test/java/org/ehcache/management/providers/statistics/LatencyHistogramConfigurationTest.java index 89bba0ce9d..75c97ea12e 100644 --- a/management/src/test/java/org/ehcache/management/providers/statistics/LatencyHistogramConfigurationTest.java +++ b/ehcache-management/src/test/java/org/ehcache/management/providers/statistics/LatencyHistogramConfigurationTest.java @@ -15,6 +15,7 @@ */ package org.ehcache.management.providers.statistics; +import org.ehcache.management.registry.LatencyHistogramConfiguration; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; diff --git a/management/src/test/java/org/ehcache/management/providers/statistics/StandardEhCacheStatisticsQueryTest.java b/ehcache-management/src/test/java/org/ehcache/management/providers/statistics/StandardEhCacheStatisticsQueryTest.java similarity index 96% rename from management/src/test/java/org/ehcache/management/providers/statistics/StandardEhCacheStatisticsQueryTest.java rename to ehcache-management/src/test/java/org/ehcache/management/providers/statistics/StandardEhCacheStatisticsQueryTest.java index 4e811216c7..ff3e70125b 100755 --- a/management/src/test/java/org/ehcache/management/providers/statistics/StandardEhCacheStatisticsQueryTest.java +++ b/ehcache-management/src/test/java/org/ehcache/management/providers/statistics/StandardEhCacheStatisticsQueryTest.java @@ -33,7 +33,6 @@ import org.ehcache.management.registry.DefaultManagementRegistryConfiguration; import org.ehcache.management.registry.DefaultManagementRegistryService; import org.hamcrest.Matchers; -import org.junit.Assert; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -51,7 +50,7 @@ import static org.ehcache.config.units.EntryUnit.ENTRIES; import static org.ehcache.config.units.MemoryUnit.MB; import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; @RunWith(Parameterized.class) public class StandardEhCacheStatisticsQueryTest { @@ -102,7 +101,7 @@ public void test() throws IOException { CacheConfiguration cacheConfiguration = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, resources) .withEvictionAdvisor((key, value) -> key.equals(2L)) - .add(new StoreStatisticsConfiguration(true)) // explicitly enable statistics to make sure they are there even when using only one tier + .withService(new StoreStatisticsConfiguration(true)) // explicitly enable statistics to make sure they are there even when using only one tier .build(); try (CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() @@ -132,7 +131,7 @@ public void test() throws IOException { } long cacheHitCount = getAndAssertExpectedValueFromCounter("Cache:HitCount", context, managementRegistry, cacheExpectedValue); - Assert.assertThat(tierHitCountSum, is(cacheHitCount)); + assertThat(tierHitCountSum, is(cacheHitCount)); } } diff --git a/management/src/test/java/org/ehcache/management/providers/statistics/StandardEhcacheStatisticsTest.java b/ehcache-management/src/test/java/org/ehcache/management/providers/statistics/StandardEhcacheStatisticsTest.java similarity index 75% rename from management/src/test/java/org/ehcache/management/providers/statistics/StandardEhcacheStatisticsTest.java rename to ehcache-management/src/test/java/org/ehcache/management/providers/statistics/StandardEhcacheStatisticsTest.java index e439bee67e..ce8d1749ae 100755 --- a/management/src/test/java/org/ehcache/management/providers/statistics/StandardEhcacheStatisticsTest.java +++ b/ehcache-management/src/test/java/org/ehcache/management/providers/statistics/StandardEhcacheStatisticsTest.java @@ -15,7 +15,6 @@ */ package org.ehcache.management.providers.statistics; -import org.assertj.core.api.AbstractLongAssert; import org.ehcache.Cache; import org.ehcache.CacheManager; import org.ehcache.config.CacheConfiguration; @@ -25,21 +24,23 @@ import org.ehcache.config.units.EntryUnit; import org.ehcache.config.units.MemoryUnit; import org.ehcache.core.statistics.CacheOperationOutcomes; +import org.ehcache.management.registry.LatencyHistogramConfiguration; import org.ehcache.management.ManagementRegistryService; import org.ehcache.management.registry.DefaultManagementRegistryConfiguration; import org.ehcache.management.registry.DefaultManagementRegistryService; import org.ehcache.spi.loaderwriter.CacheLoaderWriter; import org.junit.After; import org.junit.Before; +import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; -import org.junit.rules.Timeout; import org.terracotta.management.model.context.Context; import org.terracotta.management.model.stats.ContextualStatistics; import org.terracotta.statistics.OperationStatistic; import org.terracotta.statistics.derived.OperationResultFilter; import org.terracotta.statistics.derived.latency.LatencyHistogramStatistic; import org.terracotta.statistics.observer.ChainedOperationObserver; +import org.terracotta.utilities.test.rules.TestRetryer; import java.time.Duration; import java.util.Collection; @@ -48,25 +49,24 @@ import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; -import java.util.stream.IntStream; +import static java.time.Duration.ofMillis; +import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; -import static org.ehcache.impl.internal.statistics.StatsUtils.findOperationStatisticOnChildren; +import static org.ehcache.core.internal.statistics.StatsUtils.findOperationStatisticOnChildren; +import static org.terracotta.utilities.test.rules.TestRetryer.tryValues; public class StandardEhcacheStatisticsTest { - private static final int HISTOGRAM_WINDOW_MILLIS = 400; - private static final int NEXT_WINDOW_SLEEP_MILLIS = 500; - - @Rule - public final Timeout globalTimeout = Timeout.seconds(10); + @ClassRule @Rule + public static final TestRetryer TIME_BASE = tryValues(1, 2, 4, 8, 16, 32).map(i -> ofMillis(50).multipliedBy(i)); private CacheManager cacheManager; private Cache cache; private ManagementRegistryService managementRegistry; private Context context; - private long latency; + private Duration latency = Duration.ZERO; private final Map systemOfRecords = new HashMap<>(); @Before @@ -103,7 +103,7 @@ public void delete(Long key) { LatencyHistogramConfiguration latencyHistogramConfiguration = new LatencyHistogramConfiguration( LatencyHistogramConfiguration.DEFAULT_PHI, LatencyHistogramConfiguration.DEFAULT_BUCKET_COUNT, - Duration.ofMillis(HISTOGRAM_WINDOW_MILLIS) + TIME_BASE.get().multipliedBy(8L) ); DefaultManagementRegistryConfiguration registryConfiguration = new DefaultManagementRegistryConfiguration() .setCacheManagerAlias("myCacheManager3") @@ -133,25 +133,19 @@ public void statTest() throws InterruptedException { cache.get(1L); // hit cache.remove(1L); // removal - IntStream.of(50, 95, 99, 100) - .forEach(i -> { - assertStatistic("Cache:MissCount").isEqualTo(1L); - assertStatistic("Cache:GetMissLatency#" + i).isGreaterThan(0); - - assertStatistic("Cache:HitCount").isEqualTo(1L); - assertStatistic("Cache:GetHitLatency#" + i).isGreaterThan(0); - - assertStatistic("Cache:PutCount").isEqualTo(1L); - assertStatistic("Cache:PutLatency#" + i).isGreaterThan(0); - - assertStatistic("Cache:RemovalCount").isEqualTo(1L); - assertStatistic("Cache:RemoveLatency#" + i).isGreaterThan(0); - }); - } - - private AbstractLongAssert assertStatistic(String statName) { - long value = getStatistic(statName); - return assertThat(value); + assertThat(getStatistic("Cache:MissCount")).isEqualTo(1L); + assertThat(getStatistic("Cache:HitCount")).isEqualTo(1L); + assertThat(getStatistic("Cache:PutCount")).isEqualTo(1L); + assertThat(getStatistic("Cache:RemovalCount")).isEqualTo(1L); + + for (String statistic : asList("GetMiss", "GetHit", "Put", "Remove")) { + long last = 0L; + for (String percentile : asList("50", "95", "99", "100")) { + long value = getStatistic("Cache:" + statistic + "Latency#" + percentile); + assertThat(value).isGreaterThanOrEqualTo(last); + last = value; + } + } } private long getStatistic(String statName) { @@ -172,25 +166,25 @@ public void getCacheGetHitMissLatencies() { Consumer verifier = histogram -> { assertThat(histogram.count()).isEqualTo(0L); - latency = 100; + latency = TIME_BASE.get().multipliedBy(2L); cache.get(1L); - latency = 50; + latency = TIME_BASE.get().multipliedBy(1L); cache.get(2L); assertThat(histogram.count()).isEqualTo(2L); - assertThat(histogram.maximum()).isGreaterThanOrEqualTo(TimeUnit.MILLISECONDS.toNanos(100L)); + assertThat(histogram.maximum()).isGreaterThanOrEqualTo(TIME_BASE.get().multipliedBy(2L).toNanos()); - minimumSleep(NEXT_WINDOW_SLEEP_MILLIS); + minimumSleep(TIME_BASE.get().multipliedBy(10)); - latency = 50; + latency = TIME_BASE.get().multipliedBy(1L); cache.get(3L); - latency = 150; + latency = TIME_BASE.get().multipliedBy(3L); cache.get(4L); assertThat(histogram.count()).isEqualTo(2L); - assertThat(histogram.maximum()).isGreaterThanOrEqualTo(TimeUnit.MILLISECONDS.toNanos(150L)); + assertThat(histogram.maximum()).isGreaterThanOrEqualTo(TIME_BASE.get().multipliedBy(3L).toNanos()); }; verifier.accept(getHistogram(CacheOperationOutcomes.GetOutcome.MISS, "get")); @@ -220,26 +214,26 @@ private > LatencyHistogramStatistic getHistogram(T type, Strin return histogram; } - // Java does not provide a guarantee that Thread.sleep will actually sleep long enough + // Java does not provide a guarantee that Thread.sleep will actually sleep long enough. // In fact, on Windows, it does not sleep for long enough. // This method keeps sleeping until the full time has passed. - private void minimumSleep(long millis) { - long start = System.currentTimeMillis(); + // + // Using System.nanoTime (accurate to 1 micro-second or better) in lieu of System.currentTimeMillis (on Windows + // accurate to ~16ms), the inaccuracy of which compounds when invoked multiple times, as in this method. + private void minimumSleep(Duration sleep) { + long end = System.nanoTime() + sleep.toNanos(); while (true) { - long now = System.currentTimeMillis(); - long elapsed = now - start; - long millisLeft = millis - elapsed; + long nanosLeft = end - System.nanoTime(); - if (millisLeft <= 0) { + if (nanosLeft <= 0) { break; } try { - Thread.sleep(millisLeft); + TimeUnit.NANOSECONDS.sleep(nanosLeft); } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - return; + throw new AssertionError(e); } } } diff --git a/management/src/test/java/org/ehcache/management/providers/statistics/StatsUtil.java b/ehcache-management/src/test/java/org/ehcache/management/providers/statistics/StatsUtil.java similarity index 54% rename from management/src/test/java/org/ehcache/management/providers/statistics/StatsUtil.java rename to ehcache-management/src/test/java/org/ehcache/management/providers/statistics/StatsUtil.java index 8a792fc60e..bc9629e138 100755 --- a/management/src/test/java/org/ehcache/management/providers/statistics/StatsUtil.java +++ b/ehcache-management/src/test/java/org/ehcache/management/providers/statistics/StatsUtil.java @@ -24,42 +24,14 @@ import org.terracotta.management.registry.StatisticQuery; import static java.util.Collections.singletonList; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class StatsUtil { public static Context createContext(ManagementRegistryService managementRegistry) { ContextContainer cacheManagerCtx = managementRegistry.getContextContainer(); ContextContainer firstCacheCtx = cacheManagerCtx.getSubContexts().iterator().next(); - return Context.empty() - .with(cacheManagerCtx.getName(), cacheManagerCtx.getValue()) + return managementRegistry.getConfiguration().getContext() .with(firstCacheCtx.getName(), firstCacheCtx.getValue()); } - - /* - NOTE: When using this method in other unit tests, make sure to declare a timeout as it is possible to get an infinite loop. - This should only occur if the stats value is different from your expectedResult, which may happen if the stats calculations - change, the stats value isn't accessible or if you enter the wrong expectedResult. - */ - public static long getAndAssertExpectedValueFromCounter(String statName, Context context, ManagementRegistryService managementRegistry, long expectedResult) { - - StatisticQuery query = managementRegistry.withCapability("StatisticsCapability") - .queryStatistics(singletonList(statName)) - .on(context) - .build(); - - ResultSet counters = query.execute(); - - ContextualStatistics statisticsContext = counters.getResult(context); - - assertThat(counters.size(), Matchers.is(1)); - - Number counter = statisticsContext.getLatestSampleValue(statName).get(); - long value = counter.longValue(); - - assertThat(value, Matchers.is(expectedResult)); - - return value; - } - } diff --git a/management/src/test/java/org/ehcache/management/registry/DefaultCollectorServiceTest.java b/ehcache-management/src/test/java/org/ehcache/management/registry/DefaultCollectorServiceTest.java similarity index 96% rename from management/src/test/java/org/ehcache/management/registry/DefaultCollectorServiceTest.java rename to ehcache-management/src/test/java/org/ehcache/management/registry/DefaultCollectorServiceTest.java index 7366a88b78..551a47e2a8 100644 --- a/management/src/test/java/org/ehcache/management/registry/DefaultCollectorServiceTest.java +++ b/ehcache-management/src/test/java/org/ehcache/management/registry/DefaultCollectorServiceTest.java @@ -41,11 +41,11 @@ import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; import static org.hamcrest.CoreMatchers.equalTo; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class DefaultCollectorServiceTest { - @Test(timeout = 6000) + @Test public void test_collector() throws Exception { final Queue messages = new ConcurrentLinkedQueue<>(); final List notifs = new ArrayList<>(7); @@ -95,7 +95,7 @@ void onEvent(Object event) { .call("startStatisticCollector", new Parameter(1L, long.class.getName()), new Parameter(TimeUnit.SECONDS, TimeUnit.class.getName())) - .on(Context.create("cacheManagerName", "my-cm-1")) + .on(managementRegistry.getConfiguration().getContext()) .build() .execute() .getSingleResult(); @@ -104,7 +104,7 @@ void onEvent(Object event) { cache.put("key", "val"); cache.clear(); - num.await(); + num.await(10, TimeUnit.SECONDS); cacheManager.removeCache("my-cache"); cacheManager.close(); diff --git a/management/src/test/java/org/ehcache/management/registry/DefaultManagementRegistryServiceTest.java b/ehcache-management/src/test/java/org/ehcache/management/registry/DefaultManagementRegistryServiceTest.java similarity index 96% rename from management/src/test/java/org/ehcache/management/registry/DefaultManagementRegistryServiceTest.java rename to ehcache-management/src/test/java/org/ehcache/management/registry/DefaultManagementRegistryServiceTest.java index 5d12a7efc8..9245abef53 100644 --- a/management/src/test/java/org/ehcache/management/registry/DefaultManagementRegistryServiceTest.java +++ b/ehcache-management/src/test/java/org/ehcache/management/registry/DefaultManagementRegistryServiceTest.java @@ -37,7 +37,6 @@ import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import org.junit.rules.Timeout; import org.terracotta.management.model.call.ContextualReturn; @@ -51,6 +50,7 @@ import org.terracotta.management.registry.StatisticQuery.Builder; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; import static org.ehcache.config.units.MemoryUnit.MB; @@ -64,9 +64,6 @@ public class DefaultManagementRegistryServiceTest { private static final Collection DISK_DESCRIPTORS = new ArrayList<>(); private static final Collection CACHE_DESCRIPTORS = new ArrayList<>(); - @Rule - public final ExpectedException expectedException = ExpectedException.none(); - @Rule public final TemporaryFolder diskPath = new TemporaryFolder(); @@ -98,7 +95,7 @@ public void testCanGetContext() { @Test public void descriptorOnHeapTest() { CacheConfiguration cacheConfiguration = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, heap(10)) - .add(new StoreStatisticsConfiguration(true)) + .withService(new StoreStatisticsConfiguration(true)) .build(); ManagementRegistryService managementRegistry = new DefaultManagementRegistryService(new DefaultManagementRegistryConfiguration().setCacheManagerAlias("myCM")); @@ -229,7 +226,7 @@ private String getStoragePath() throws IOException { @Test public void testCanGetCapabilities() { CacheConfiguration cacheConfiguration = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, heap(10)) - .add(new StoreStatisticsConfiguration(true)) + .withService(new StoreStatisticsConfiguration(true)) .build(); ManagementRegistryService managementRegistry = new DefaultManagementRegistryService(new DefaultManagementRegistryConfiguration().setCacheManagerAlias("myCM")); @@ -249,8 +246,8 @@ public void testCanGetCapabilities() { assertThat(capabilities.get(0).getDescriptors()).hasSize(4); assertThat(capabilities.get(3).getDescriptors()).hasSize(ONHEAP_DESCRIPTORS.size() + CACHE_DESCRIPTORS.size()); - assertThat(capabilities.get(0).getCapabilityContext().getAttributes()).hasSize(2); - assertThat(capabilities.get(3).getCapabilityContext().getAttributes()).hasSize(2); + assertThat(capabilities.get(0).getCapabilityContext().getAttributes()).hasSize(3); + assertThat(capabilities.get(3).getCapabilityContext().getAttributes()).hasSize(3); } } @@ -269,12 +266,10 @@ public void testCanGetStats() { .using(managementRegistry) .build(true)) { - Context context1 = Context.empty() - .with("cacheManagerName", "myCM") + Context context1 = managementRegistry.getConfiguration().getContext() .with("cacheName", "aCache1"); - Context context2 = Context.empty() - .with("cacheManagerName", "myCM") + Context context2 = managementRegistry.getConfiguration().getContext() .with("cacheName", "aCache2"); Cache cache1 = cacheManager.getCache("aCache1", Long.class, String.class); @@ -358,8 +353,7 @@ public void testCall() throws ExecutionException { .using(managementRegistry) .build(true)) { - Context context = Context.empty() - .with("cacheManagerName", "myCM") + Context context = managementRegistry.getConfiguration().getContext() .with("cacheName", "aCache1"); cacheManager.getCache("aCache1", Long.class, String.class).put(1L, "1"); @@ -393,9 +387,8 @@ public void testCallOnInexistignContext() throws ExecutionException { .using(managementRegistry) .build(true)) { - Context inexisting = Context.empty() - .with("cacheManagerName", "myCM2") - .with("cacheName", "aCache2"); + Context inexisting = managementRegistry.getConfiguration().getContext() + .with("cacheName", "aCache3"); ResultSet> results = managementRegistry.withCapability("ActionsCapability") .call("clear") @@ -406,8 +399,7 @@ public void testCallOnInexistignContext() throws ExecutionException { assertThat(results.size()).isEqualTo(1); assertThat(results.getSingleResult().hasExecuted()).isFalse(); - expectedException.expect(NoSuchElementException.class); - results.getSingleResult().getValue(); + assertThatThrownBy(() -> results.getSingleResult().getValue()).isInstanceOf(NoSuchElementException.class); } } diff --git a/management/src/test/java/org/ehcache/management/registry/DefaultSharedManagementServiceTest.java b/ehcache-management/src/test/java/org/ehcache/management/registry/DefaultSharedManagementServiceTest.java similarity index 93% rename from management/src/test/java/org/ehcache/management/registry/DefaultSharedManagementServiceTest.java rename to ehcache-management/src/test/java/org/ehcache/management/registry/DefaultSharedManagementServiceTest.java index a05a4bda98..516564259a 100644 --- a/management/src/test/java/org/ehcache/management/registry/DefaultSharedManagementServiceTest.java +++ b/ehcache-management/src/test/java/org/ehcache/management/registry/DefaultSharedManagementServiceTest.java @@ -51,10 +51,10 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.isIn; import static org.hamcrest.collection.IsCollectionWithSize.hasSize; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; @RunWith(JUnit4.class) @@ -158,15 +158,9 @@ public void testStats() { String statisticName = "Cache:MissCount"; List contextList = Arrays.asList( - Context.empty() - .with("cacheManagerName", "myCM1") - .with("cacheName", "aCache1"), - Context.empty() - .with("cacheManagerName", "myCM2") - .with("cacheName", "aCache2"), - Context.empty() - .with("cacheManagerName", "myCM2") - .with("cacheName", "aCache3")); + config1.getContext().with("cacheName", "aCache1"), + config2.getContext().with("cacheName", "aCache2"), + config2.getContext().with("cacheName", "aCache3")); cacheManager1.getCache("aCache1", Long.class, String.class).get(1L); cacheManager2.getCache("aCache2", Long.class, String.class).get(2L); @@ -210,15 +204,9 @@ private static ResultSet getResultSet(StatisticQuery.Build @Test public void testCall() throws ExecutionException { List contextList = Arrays.asList( - Context.empty() - .with("cacheManagerName", "myCM1") - .with("cacheName", "aCache1"), - Context.empty() - .with("cacheManagerName", "myCM1") - .with("cacheName", "aCache4"), - Context.empty() - .with("cacheManagerName", "myCM2") - .with("cacheName", "aCache2"), + config1.getContext().with("cacheName", "aCache1"), + config1.getContext().with("cacheName", "aCache4"), + config2.getContext().with("cacheName", "aCache2"), Context.empty() .with("cacheManagerName", "myCM55") .with("cacheName", "aCache55")); diff --git a/management/src/test/java/org/ehcache/management/registry/ManagementRegistryServiceConfigurationParserTest.java b/ehcache-management/src/test/java/org/ehcache/management/registry/ManagementRegistryServiceConfigurationParserTest.java similarity index 57% rename from management/src/test/java/org/ehcache/management/registry/ManagementRegistryServiceConfigurationParserTest.java rename to ehcache-management/src/test/java/org/ehcache/management/registry/ManagementRegistryServiceConfigurationParserTest.java index 8ab0af7726..afb4751a85 100644 --- a/management/src/test/java/org/ehcache/management/registry/ManagementRegistryServiceConfigurationParserTest.java +++ b/ehcache-management/src/test/java/org/ehcache/management/registry/ManagementRegistryServiceConfigurationParserTest.java @@ -15,16 +15,52 @@ */ package org.ehcache.management.registry; +import org.hamcrest.Matchers; import org.junit.Test; +import org.w3c.dom.Element; import org.w3c.dom.Node; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; -import static org.ehcache.xml.ConfigurationParserTestHelper.assertElement; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import java.io.IOException; +import java.io.StringReader; + +import static org.ehcache.xml.XmlConfigurationMatchers.isSameConfigurationAs; +import static org.hamcrest.MatcherAssert.assertThat; /** * ManagementRegistryServiceConfigurationParserTest */ public class ManagementRegistryServiceConfigurationParserTest { + @Test + public void testParseTagInsideProperty() throws ParserConfigurationException, IOException, SAXException { + String property = ManagementRegistryServiceConfigurationParserTest.class.getName() + ":tag"; + String inputString = "" + + "tag1${" + property + "}"; + + ManagementRegistryServiceConfigurationParser configParser = new ManagementRegistryServiceConfigurationParser(); + + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + documentBuilderFactory.setNamespaceAware(true); + Element node = documentBuilderFactory.newDocumentBuilder() + .parse(new InputSource(new StringReader(inputString))).getDocumentElement(); + + System.setProperty(property, "tag2"); + try { + DefaultManagementRegistryConfiguration configuration = + (DefaultManagementRegistryConfiguration) configParser.parseServiceCreationConfiguration(node, null); + + assertThat(configuration.getTags(), Matchers.hasItems("tag1", "tag2")); + } finally { + System.clearProperty(property); + } + } + @Test public void testTranslateServiceCreationConfiguration() { ManagementRegistryServiceConfigurationParser configTranslator = new ManagementRegistryServiceConfigurationParser(); @@ -37,7 +73,7 @@ public void testTranslateServiceCreationConfiguration() { String inputString = "" + "tag1tag2"; - assertElement(inputString, retElement); + assertThat(retElement, isSameConfigurationAs(inputString)); } @Test @@ -51,7 +87,7 @@ public void testTranslateServiceCreationConfigurationWithoutTags() { Node retElement = configTranslator.unparseServiceCreationConfiguration(defaultManagementRegistryConfiguration); String inputString = ""; - assertElement(inputString, retElement); + assertThat(retElement, isSameConfigurationAs(inputString)); } } diff --git a/management/src/test/java/org/ehcache/management/registry/XmlConfigTest.java b/ehcache-management/src/test/java/org/ehcache/management/registry/XmlConfigTest.java similarity index 92% rename from management/src/test/java/org/ehcache/management/registry/XmlConfigTest.java rename to ehcache-management/src/test/java/org/ehcache/management/registry/XmlConfigTest.java index ee6bd99abb..48e2a097ab 100644 --- a/management/src/test/java/org/ehcache/management/registry/XmlConfigTest.java +++ b/ehcache-management/src/test/java/org/ehcache/management/registry/XmlConfigTest.java @@ -30,7 +30,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.nullValue; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; @RunWith(Parameterized.class) public class XmlConfigTest { @@ -85,7 +85,7 @@ public void test_config_loaded() throws Exception { try { DefaultManagementRegistryConfiguration registryConfiguration = null; - for (ServiceCreationConfiguration configuration : myCacheManager.getRuntimeConfiguration().getServiceCreationConfigurations()) { + for (ServiceCreationConfiguration configuration : myCacheManager.getRuntimeConfiguration().getServiceCreationConfigurations()) { if (configuration instanceof DefaultManagementRegistryConfiguration) { registryConfiguration = (DefaultManagementRegistryConfiguration) configuration; break; @@ -101,7 +101,7 @@ public void test_config_loaded() throws Exception { assertThat(registryConfiguration.getCacheManagerAlias(), equalTo(expectedConfiguration.getCacheManagerAlias())); assertThat(registryConfiguration.getCollectorExecutorAlias(), equalTo(expectedConfiguration.getCollectorExecutorAlias())); - assertThat(registryConfiguration.getContext(), equalTo(expectedConfiguration.getContext())); + assertThat(registryConfiguration.getContext().without("instanceId"), equalTo(expectedConfiguration.getContext().without("instanceId"))); assertThat(registryConfiguration.getTags(), equalTo(expectedConfiguration.getTags())); } finally { diff --git a/ehcache-management/src/test/resources/ehcache-management-1.xml b/ehcache-management/src/test/resources/ehcache-management-1.xml new file mode 100644 index 0000000000..3b6276efd1 --- /dev/null +++ b/ehcache-management/src/test/resources/ehcache-management-1.xml @@ -0,0 +1,16 @@ + + + + + + + + + java.lang.String + java.lang.String + 20 + + + diff --git a/management/src/test/resources/ehcache-management-2.xml b/ehcache-management/src/test/resources/ehcache-management-2.xml similarity index 59% rename from management/src/test/resources/ehcache-management-2.xml rename to ehcache-management/src/test/resources/ehcache-management-2.xml index 112e77d9cc..fd7702f853 100644 --- a/management/src/test/resources/ehcache-management-2.xml +++ b/ehcache-management/src/test/resources/ehcache-management-2.xml @@ -1,9 +1,6 @@ + xmlns:mnm='http://www.ehcache.org/v3/management'> diff --git a/management/src/test/resources/ehcache-management-3.xml b/ehcache-management/src/test/resources/ehcache-management-3.xml similarity index 62% rename from management/src/test/resources/ehcache-management-3.xml rename to ehcache-management/src/test/resources/ehcache-management-3.xml index 15ea08acfb..4be6c11bf9 100644 --- a/management/src/test/resources/ehcache-management-3.xml +++ b/ehcache-management/src/test/resources/ehcache-management-3.xml @@ -1,9 +1,6 @@ + xmlns:mnm='http://www.ehcache.org/v3/management'> diff --git a/management/src/test/resources/ehcache-management-4.xml b/ehcache-management/src/test/resources/ehcache-management-4.xml similarity index 59% rename from management/src/test/resources/ehcache-management-4.xml rename to ehcache-management/src/test/resources/ehcache-management-4.xml index 112e77d9cc..fd7702f853 100644 --- a/management/src/test/resources/ehcache-management-4.xml +++ b/ehcache-management/src/test/resources/ehcache-management-4.xml @@ -1,9 +1,6 @@ + xmlns:mnm='http://www.ehcache.org/v3/management'> diff --git a/management/src/test/resources/ehcache-management-5.xml b/ehcache-management/src/test/resources/ehcache-management-5.xml similarity index 59% rename from management/src/test/resources/ehcache-management-5.xml rename to ehcache-management/src/test/resources/ehcache-management-5.xml index 112e77d9cc..fd7702f853 100644 --- a/management/src/test/resources/ehcache-management-5.xml +++ b/ehcache-management/src/test/resources/ehcache-management-5.xml @@ -1,9 +1,6 @@ + xmlns:mnm='http://www.ehcache.org/v3/management'> diff --git a/management/src/test/resources/ehcache-management.xml b/ehcache-management/src/test/resources/ehcache-management.xml similarity index 80% rename from management/src/test/resources/ehcache-management.xml rename to ehcache-management/src/test/resources/ehcache-management.xml index b9aa21feae..c64d8b5530 100644 --- a/management/src/test/resources/ehcache-management.xml +++ b/ehcache-management/src/test/resources/ehcache-management.xml @@ -15,11 +15,8 @@ ~ limitations under the License. --> + xmlns:mnm='http://www.ehcache.org/v3/management'> diff --git a/management/src/test/resources/settings-capability.json b/ehcache-management/src/test/resources/settings-capability.json similarity index 77% rename from management/src/test/resources/settings-capability.json rename to ehcache-management/src/test/resources/settings-capability.json index 4a87917bfc..f8702c8da7 100644 --- a/management/src/test/resources/settings-capability.json +++ b/ehcache-management/src/test/resources/settings-capability.json @@ -1 +1 @@ -{"name":"SettingsCapability","descriptors":[{"cacheName":"cache-1","keyType":"java.lang.String","valueType":"java.lang.String","resourcePools":{"heap":{"level":10000,"persistent":false,"type":"ENTRY","size":10,"unit":"entries"},"offheap":{"level":1000,"persistent":false,"type":"MEMORY","size":1,"unit":"MB"},"disk":{"level":100,"persistent":true,"type":"MEMORY","size":2,"unit":"MB"}}},{"cacheName":"cache-2","keyType":"java.lang.String","valueType":"java.lang.String","resourcePools":{"heap":{"level":10000,"persistent":false,"type":"ENTRY","size":10,"unit":"entries"},"offheap":{"level":1000,"persistent":false,"type":"MEMORY","size":1,"unit":"MB"},"disk":{"level":100,"persistent":true,"type":"MEMORY","size":2,"unit":"MB"}}},{"cacheManagerDescription":"","instanceId":"instance-id","managementContext":{"cacheManagerName":"my-cm-1"},"tags":["baz","boo","foo"]}],"capabilityContext":{"attributes":[{"name":"cacheManagerName","required":true},{"name":"cacheName","required":true}]}} +{"name":"SettingsCapability","descriptors":[{"cacheName":"cache-1","keyType":"java.lang.String","valueType":"java.lang.String","resourcePools":{"heap":{"level":10000,"persistent":false,"type":"ENTRY","size":10,"unit":"entries"},"offheap":{"level":1000,"persistent":false,"type":"MEMORY","size":1,"unit":"MB"},"disk":{"level":100,"persistent":true,"type":"MEMORY","size":2,"unit":"MB"}}},{"cacheName":"cache-2","keyType":"java.lang.String","valueType":"java.lang.String","resourcePools":{"heap":{"level":10000,"persistent":false,"type":"ENTRY","size":10,"unit":"entries"},"offheap":{"level":1000,"persistent":false,"type":"MEMORY","size":1,"unit":"MB"},"disk":{"level":100,"persistent":true,"type":"MEMORY","size":2,"unit":"MB"}}},{"cacheManagerDescription":"","instanceId":"UUID","cacheManagerName":"my-cm-1"},"tags":["baz","boo","foo"]}],"capabilityContext":{"attributes":[{"name":"instanceId","required":true},{"name":"cacheManagerName","required":true},{"name":"cacheName","required":true}]}} diff --git a/ehcache-transactions/build.gradle b/ehcache-transactions/build.gradle new file mode 100644 index 0000000000..0584802e93 --- /dev/null +++ b/ehcache-transactions/build.gradle @@ -0,0 +1,75 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id 'org.ehcache.build.public-module' + id 'org.ehcache.build.plugins.variant' +} + +publishing.publications.withType(MavenPublication) { + pom { + name = 'Ehcache 3 transactions module' + description = 'The transactions module of Ehcache 3' + } +} + +variants { + variant('module') { + capability "org.ehcache:ehcache-transactions-provider:$version" + capability "org.ehcache:ehcache-transactions-modules:$version" + } +} + +configurations { + [apiElements, runtimeElements]*.outgoing { + capability "org.ehcache:ehcache-transactions-provider:$version" + capability "org.ehcache:ehcache-transactions:$version" + } +} + +dependencies { + api group: 'javax.transaction', name: 'jta', version: '1.1' + api project(':ehcache') + implementation(group: 'org.codehaus.btm', name: 'btm', version: '2.1.4') { + exclude group:'org.slf4j', module:'slf4j-api' + } + compileOnly 'org.osgi:org.osgi.service.component.annotations:1.3.0' + + testImplementation(project(':core-spi-test')) { + exclude group:'org.ehcache.modules' + } + testImplementation testFixtures(project(':ehcache-xml')) { + exclude group:'org.ehcache.modules', module:'ehcache-xml' + } + testImplementation "org.terracotta:statistics:$statisticVersion" + + moduleApi group: 'javax.transaction', name: 'jta', version: '1.1' + moduleApi project(':ehcache-core') + moduleImplementation project(':ehcache-impl') + moduleImplementation project(':ehcache-xml') + + moduleImplementation(group: 'org.codehaus.btm', name: 'btm', version: '2.1.4') { + exclude group:'org.slf4j', module:'slf4j-api' + } +} + +jar { + bnd ( + 'Bundle-SymbolicName': 'org.ehcache.transactions', + 'Export-Package': 'org.ehcache.transactions.xa.*', + 'Import-Package': 'bitronix.tm.*;resolution:=optional, javax.transaction.*;resolution:=optional, org.ehcache.xml.*;resolution:=optional, *', + ) +} diff --git a/ehcache-transactions/config/checkstyle-suppressions.xml b/ehcache-transactions/config/checkstyle-suppressions.xml new file mode 100644 index 0000000000..cb41d0baf7 --- /dev/null +++ b/ehcache-transactions/config/checkstyle-suppressions.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/EhcacheXAException.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/EhcacheXAException.java similarity index 100% rename from transactions/src/main/java/org/ehcache/transactions/xa/EhcacheXAException.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/EhcacheXAException.java diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/XACacheException.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/XACacheException.java similarity index 100% rename from transactions/src/main/java/org/ehcache/transactions/xa/XACacheException.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/XACacheException.java diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/configuration/XAStoreConfiguration.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/configuration/XAStoreConfiguration.java similarity index 83% rename from transactions/src/main/java/org/ehcache/transactions/xa/configuration/XAStoreConfiguration.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/configuration/XAStoreConfiguration.java index c18a72a256..7b30dd7647 100644 --- a/transactions/src/main/java/org/ehcache/transactions/xa/configuration/XAStoreConfiguration.java +++ b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/configuration/XAStoreConfiguration.java @@ -21,7 +21,7 @@ /** * @author Ludovic Orban */ -public class XAStoreConfiguration implements ServiceConfiguration { +public class XAStoreConfiguration implements ServiceConfiguration { private final String uniqueXAResourceId; @@ -37,4 +37,14 @@ public String getUniqueXAResourceId() { public Class getServiceType() { return XAStore.Provider.class; } + + @Override + public String derive() { + return getUniqueXAResourceId(); + } + + @Override + public XAStoreConfiguration build(String xaResourceId) { + return new XAStoreConfiguration(xaResourceId); + } } diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/internal/EhcacheXAResource.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/EhcacheXAResource.java similarity index 100% rename from transactions/src/main/java/org/ehcache/transactions/xa/internal/EhcacheXAResource.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/EhcacheXAResource.java diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/internal/SerializableXid.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/SerializableXid.java similarity index 100% rename from transactions/src/main/java/org/ehcache/transactions/xa/internal/SerializableXid.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/SerializableXid.java diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/internal/SoftLock.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/SoftLock.java similarity index 100% rename from transactions/src/main/java/org/ehcache/transactions/xa/internal/SoftLock.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/SoftLock.java diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/internal/SoftLockSerializer.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/SoftLockSerializer.java similarity index 96% rename from transactions/src/main/java/org/ehcache/transactions/xa/internal/SoftLockSerializer.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/SoftLockSerializer.java index 9e77a44146..3064b950a2 100644 --- a/transactions/src/main/java/org/ehcache/transactions/xa/internal/SoftLockSerializer.java +++ b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/SoftLockSerializer.java @@ -17,7 +17,7 @@ package org.ehcache.transactions.xa.internal; import org.ehcache.spi.serialization.SerializerException; -import org.ehcache.impl.internal.util.ByteBufferInputStream; +import org.ehcache.core.util.ByteBufferInputStream; import org.ehcache.spi.serialization.Serializer; import java.io.ByteArrayOutputStream; @@ -31,7 +31,7 @@ import java.util.HashMap; import java.util.Map; -import static org.ehcache.core.internal.util.TypeUtil.uncheckedCast; +import static org.ehcache.transactions.xa.internal.TypeUtil.uncheckedCast; /** * The stateless {@link Serializer} used to serialize {@link SoftLock}s. diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/internal/SoftLockValueCombinedCopier.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/SoftLockValueCombinedCopier.java similarity index 100% rename from transactions/src/main/java/org/ehcache/transactions/xa/internal/SoftLockValueCombinedCopier.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/SoftLockValueCombinedCopier.java diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/internal/SoftLockValueCombinedSerializer.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/SoftLockValueCombinedSerializer.java similarity index 100% rename from transactions/src/main/java/org/ehcache/transactions/xa/internal/SoftLockValueCombinedSerializer.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/SoftLockValueCombinedSerializer.java diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/internal/StatefulSoftLockValueCombinedSerializer.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/StatefulSoftLockValueCombinedSerializer.java similarity index 100% rename from transactions/src/main/java/org/ehcache/transactions/xa/internal/StatefulSoftLockValueCombinedSerializer.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/StatefulSoftLockValueCombinedSerializer.java diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/internal/StoreEventSourceWrapper.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/StoreEventSourceWrapper.java similarity index 79% rename from transactions/src/main/java/org/ehcache/transactions/xa/internal/StoreEventSourceWrapper.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/StoreEventSourceWrapper.java index 575c453a77..cc8c0ef442 100644 --- a/transactions/src/main/java/org/ehcache/transactions/xa/internal/StoreEventSourceWrapper.java +++ b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/StoreEventSourceWrapper.java @@ -16,18 +16,15 @@ package org.ehcache.transactions.xa.internal; -import org.ehcache.impl.internal.events.StoreEventImpl; import org.ehcache.core.spi.store.events.StoreEvent; import org.ehcache.core.spi.store.events.StoreEventFilter; import org.ehcache.core.spi.store.events.StoreEventListener; import org.ehcache.core.spi.store.events.StoreEventSource; +import org.ehcache.event.EventType; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import static org.ehcache.impl.internal.events.StoreEvents.createEvent; -import static org.ehcache.impl.internal.events.StoreEvents.updateEvent; - /** * StoreEventSourceWrapper */ @@ -86,6 +83,11 @@ public void setEventOrdering(boolean ordering) { underlying.setEventOrdering(ordering); } + @Override + public void setSynchronous(boolean synchronous) throws IllegalArgumentException { + underlying.setSynchronous(synchronous); + } + @Override public boolean isEventOrdering() { return underlying.isEventOrdering(); @@ -104,23 +106,7 @@ private StoreEventListenerWrapper(StoreEventListener wrappedOne) { @Override public void onEvent(StoreEvent> event) { - StoreEvent eventToPropagate = null; - switch (event.getType()) { - case CREATED: - eventToPropagate = createEvent(event.getKey(), event.getNewValue().getOldValue()); - break; - case UPDATED: - eventToPropagate = updateEvent(event.getKey(), event.getOldValue().getOldValue(), event.getNewValue() - .getOldValue()); - break; - case REMOVED: - case EXPIRED: - case EVICTED: - eventToPropagate = new StoreEventImpl<>(event.getType(), event.getKey(), event.getOldValue() - .getOldValue(), null); - break; - } - wrappedOne.onEvent(eventToPropagate); + wrappedOne.onEvent(new XaEvent<>(event)); } @Override @@ -139,4 +125,43 @@ public int hashCode() { return wrappedOne.hashCode(); } } + + static class XaEvent implements StoreEvent { + + private final StoreEvent> delegate; + + XaEvent(StoreEvent> delegate) { + this.delegate = delegate; + } + + @Override + public EventType getType() { + return delegate.getType(); + } + + @Override + public K getKey() { + return delegate.getKey(); + } + + @Override + public V getNewValue() { + SoftLock newValue = delegate.getNewValue(); + if (newValue == null) { + return null; + } else { + return newValue.getOldValue(); + } + } + + @Override + public V getOldValue() { + SoftLock oldValue = delegate.getOldValue(); + if (oldValue == null) { + return null; + } else { + return oldValue.getOldValue(); + } + } + } } diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/internal/TransactionId.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/TransactionId.java similarity index 100% rename from transactions/src/main/java/org/ehcache/transactions/xa/internal/TransactionId.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/TransactionId.java diff --git a/core/src/main/java/org/ehcache/core/internal/util/TypeUtil.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/TypeUtil.java similarity index 95% rename from core/src/main/java/org/ehcache/core/internal/util/TypeUtil.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/TypeUtil.java index b9f2133996..92c635d904 100644 --- a/core/src/main/java/org/ehcache/core/internal/util/TypeUtil.java +++ b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/TypeUtil.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.ehcache.core.internal.util; +package org.ehcache.transactions.xa.internal; /** * Holder for static helper methods related to types and casting. diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/internal/XAStore.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/XAStore.java similarity index 96% rename from transactions/src/main/java/org/ehcache/transactions/xa/internal/XAStore.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/XAStore.java index a6a6588b5a..a9c506820a 100644 --- a/transactions/src/main/java/org/ehcache/transactions/xa/internal/XAStore.java +++ b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/XAStore.java @@ -20,11 +20,12 @@ import org.ehcache.config.ResourceType; import org.ehcache.core.CacheConfigurationChangeListener; import org.ehcache.config.EvictionAdvisor; -import org.ehcache.core.internal.store.StoreConfigurationImpl; -import org.ehcache.core.internal.store.StoreSupport; import org.ehcache.core.spi.service.DiskResourceService; +import org.ehcache.core.spi.service.StatisticsService; import org.ehcache.core.spi.store.WrapperStore; -import org.ehcache.impl.internal.util.CheckerUtil; +import org.ehcache.core.store.StoreConfigurationImpl; +import org.ehcache.core.store.StoreSupport; +import org.ehcache.impl.store.BaseStore; import org.ehcache.spi.resilience.StoreAccessException; import org.ehcache.expiry.ExpiryPolicy; import org.ehcache.impl.config.copy.DefaultCopierConfiguration; @@ -32,6 +33,7 @@ import org.ehcache.core.spi.time.TimeSource; import org.ehcache.core.spi.time.TimeSourceService; import org.ehcache.spi.serialization.StatefulSerializer; +import org.ehcache.spi.service.OptionalServiceDependencies; import org.ehcache.spi.service.ServiceProvider; import org.ehcache.core.spi.store.Store; import org.ehcache.core.spi.store.events.StoreEventSource; @@ -52,7 +54,6 @@ import org.ehcache.core.collections.ConcurrentWeakIdentityHashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.terracotta.context.ContextManager; import java.io.IOException; import java.time.Duration; @@ -79,22 +80,20 @@ import javax.transaction.SystemException; import javax.transaction.Transaction; -import static org.ehcache.core.internal.util.TypeUtil.uncheckedCast; import static org.ehcache.core.spi.service.ServiceUtils.findAmongst; import static org.ehcache.core.spi.service.ServiceUtils.findSingletonAmongst; +import static org.ehcache.transactions.xa.internal.TypeUtil.uncheckedCast; /** * A {@link Store} implementation wrapping another {@link Store} driven by a JTA * {@link javax.transaction.TransactionManager} using the XA 2-phase commit protocol. */ -public class XAStore implements WrapperStore { +public class XAStore extends BaseStore implements WrapperStore { private static final Logger LOGGER = LoggerFactory.getLogger(XAStore.class); private static final Supplier REPLACE_EQUALS_TRUE = () -> Boolean.TRUE; - private final Class keyType; - private final Class valueType; private final Store> underlyingStore; private final TransactionManagerWrapper transactionManagerWrapper; private final Map> xaResources = new ConcurrentHashMap<>(); @@ -106,9 +105,8 @@ public class XAStore implements WrapperStore { private final StoreEventSourceWrapper eventSourceWrapper; public XAStore(Class keyType, Class valueType, Store> underlyingStore, TransactionManagerWrapper transactionManagerWrapper, - TimeSource timeSource, Journal journal, String uniqueXAResourceId) { - this.keyType = keyType; - this.valueType = valueType; + TimeSource timeSource, Journal journal, String uniqueXAResourceId, StatisticsService statisticsService) { + super(keyType, valueType, true, statisticsService); this.underlyingStore = underlyingStore; this.transactionManagerWrapper = transactionManagerWrapper; this.timeSource = timeSource; @@ -118,7 +116,6 @@ public XAStore(Class keyType, Class valueType, Store> under this.recoveryXaResource = new EhcacheXAResource<>(underlyingStore, journal, transactionContextFactory); this.eventSourceWrapper = new StoreEventSourceWrapper<>(underlyingStore.getStoreEventSource()); - ContextManager.associate(underlyingStore).withParent(this); } private static boolean isInDoubt(SoftLock softLock) { @@ -166,14 +163,6 @@ public void afterCompletion(int status) { } } - private void checkKey(K keyObject) { - CheckerUtil.checkKey(keyType, keyObject); - } - - private void checkValue(V valueObject) { - CheckerUtil.checkValue(valueType, valueObject); - } - @Override public ValueHolder get(K key) throws StoreAccessException { checkKey(key); @@ -357,7 +346,7 @@ public ValueHolder replace(K key, V value) throws StoreAccessException { } else { V oldValue = softLock.getOldValue(); currentContext.addCommand(key, new StorePutCommand<>(oldValue, new XAValueHolder<>(value, timeSource.getTimeMillis()))); - return new XAValueHolder<>(oldValue, softLockValueHolder.creationTime(XAValueHolder.NATIVE_TIME_UNIT)); + return new XAValueHolder<>(oldValue, softLockValueHolder.creationTime()); } } else { return null; @@ -419,6 +408,11 @@ public Iterator>> iterator() { return new XAIterator(valueHolderMap, underlyingStore.iterator(), currentContext.getTransactionId()); } + @Override + protected String getStatisticsTag() { + return "XaStore"; + } + class XAIterator implements Iterator>> { private final java.util.Iterator>> iterator; @@ -767,6 +761,7 @@ public CreatedStoreRef(final Store.Provider storeProvider, final SoftLockValueCo } @ServiceDependencies({TimeSourceService.class, JournalProvider.class, CopyProvider.class, TransactionManagerProvider.class}) + @OptionalServiceDependencies("org.ehcache.core.spi.service.StatisticsService") public static class Provider implements WrapperStore.Provider { private volatile ServiceProvider serviceProvider; @@ -774,12 +769,12 @@ public static class Provider implements WrapperStore.Provider { private final Map, CreatedStoreRef> createdStores = new ConcurrentWeakIdentityHashMap<>(); @Override - public int rank(final Set> resourceTypes, final Collection> serviceConfigs) { + public int rank(final Set> resourceTypes, final Collection> serviceConfigs) { throw new UnsupportedOperationException("Its a Wrapper store provider, does not support regular ranking"); } @Override - public Store createStore(Configuration storeConfig, ServiceConfiguration... serviceConfigs) { + public Store createStore(Configuration storeConfig, ServiceConfiguration... serviceConfigs) { Set supportedTypes = EnumSet.allOf(ResourceType.Core.class); Set> configuredTypes = storeConfig.getResourcePools().getResourceTypeSet(); @@ -795,13 +790,13 @@ public Store createStore(Configuration storeConfig, ServiceCo throw new IllegalStateException("XAStore.Provider.createStore called without XAStoreConfiguration"); } - List> serviceConfigList = Arrays.asList(serviceConfigs); + List> serviceConfigList = Arrays.asList(serviceConfigs); Store.Provider underlyingStoreProvider = StoreSupport.selectStoreProvider(serviceProvider, storeConfig.getResourcePools().getResourceTypeSet(), serviceConfigList); String uniqueXAResourceId = xaServiceConfiguration.getUniqueXAResourceId(); - List> underlyingServiceConfigs = new ArrayList<>(serviceConfigList.size() + 5); // pad a bit because we add stuff + List> underlyingServiceConfigs = new ArrayList<>(serviceConfigList.size() + 5); // pad a bit because we add stuff underlyingServiceConfigs.addAll(serviceConfigList); // eviction advisor @@ -940,12 +935,17 @@ public Duration getExpiryForUpdate(K key, Supplier> oldSof Store.Configuration> underlyingStoreConfig = new StoreConfigurationImpl<>(storeConfig.getKeyType(), softLockClass, evictionAdvisor, storeConfig.getClassLoader(), expiry, storeConfig.getResourcePools(), storeConfig.getDispatcherConcurrency(), storeConfig .getKeySerializer(), softLockValueCombinedSerializer); - Store> underlyingStore = underlyingStoreProvider.createStore(underlyingStoreConfig, underlyingServiceConfigs.toArray(new ServiceConfiguration[0])); + Store> underlyingStore = underlyingStoreProvider.createStore(underlyingStoreConfig, underlyingServiceConfigs.toArray(new ServiceConfiguration[0])); // create the XA store + StatisticsService statisticsService = serviceProvider.getService(StatisticsService.class); TransactionManagerWrapper transactionManagerWrapper = transactionManagerProvider.getTransactionManagerWrapper(); Store store = new XAStore<>(storeConfig.getKeyType(), storeConfig.getValueType(), underlyingStore, - transactionManagerWrapper, timeSource, journal, uniqueXAResourceId); + transactionManagerWrapper, timeSource, journal, uniqueXAResourceId, statisticsService); + + if (statisticsService != null) { + statisticsService.registerWithParent(underlyingStore, store); + } // create the softLockSerializer lifecycle helper SoftLockValueCombinedSerializerLifecycleHelper helper = @@ -1019,7 +1019,7 @@ public void stop() { } @Override - public int wrapperStoreRank(Collection> serviceConfigs) { + public int wrapperStoreRank(Collection> serviceConfigs) { XAStoreConfiguration xaServiceConfiguration = findSingletonAmongst(XAStoreConfiguration.class, serviceConfigs); if (xaServiceConfiguration == null) { // An XAStore must be configured for use diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/internal/XATransactionContext.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/XATransactionContext.java similarity index 100% rename from transactions/src/main/java/org/ehcache/transactions/xa/internal/XATransactionContext.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/XATransactionContext.java diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/internal/XATransactionContextFactory.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/XATransactionContextFactory.java similarity index 100% rename from transactions/src/main/java/org/ehcache/transactions/xa/internal/XATransactionContextFactory.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/XATransactionContextFactory.java diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/internal/XAValueHolder.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/XAValueHolder.java similarity index 84% rename from transactions/src/main/java/org/ehcache/transactions/xa/internal/XAValueHolder.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/XAValueHolder.java index 5a76fafe21..db1f629b87 100644 --- a/transactions/src/main/java/org/ehcache/transactions/xa/internal/XAValueHolder.java +++ b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/XAValueHolder.java @@ -23,9 +23,8 @@ import java.io.Serializable; import java.nio.ByteBuffer; -import java.util.concurrent.TimeUnit; -import static org.ehcache.core.internal.util.TypeUtil.uncheckedCast; +import static org.ehcache.transactions.xa.internal.TypeUtil.uncheckedCast; /** * The {@link XAStore} {@link Store.ValueHolder} implementation. @@ -36,13 +35,11 @@ @SuppressWarnings("serial") //this class has writeReplace/readResolve methods public class XAValueHolder extends AbstractValueHolder implements Serializable { - static final TimeUnit NATIVE_TIME_UNIT = TimeUnit.MILLISECONDS; - private final V value; private final byte[] valueSerialized; public XAValueHolder(Store.ValueHolder> valueHolder, V value) { - super(-1, valueHolder.creationTime(TimeUnit.MILLISECONDS), valueHolder.expirationTime(TimeUnit.MILLISECONDS)); + super(-1, valueHolder.creationTime(), valueHolder.expirationTime()); this.value = value; this.valueSerialized = null; } @@ -57,21 +54,21 @@ public XAValueHolder(V value, long creationTime) { } private XAValueHolder(XAValueHolder valueHolder, ByteBuffer serializedValue) { - super(-1, valueHolder.creationTime(TimeUnit.MILLISECONDS), valueHolder.expirationTime(TimeUnit.MILLISECONDS)); + super(-1, valueHolder.creationTime(), valueHolder.expirationTime()); this.value = null; this.valueSerialized = new byte[serializedValue.remaining()]; serializedValue.get(this.valueSerialized); } public XAValueHolder(XAValueHolder valueHolder, V value) { - super(-1, valueHolder.creationTime(TimeUnit.MILLISECONDS), valueHolder.expirationTime(TimeUnit.MILLISECONDS)); + super(-1, valueHolder.creationTime(), valueHolder.expirationTime()); this.value = value; this.valueSerialized = null; } private XAValueHolder(long id, long creationTime, long lastAccessTime, long expirationTime, V value, byte[] valueSerialized) { super(id, creationTime, expirationTime); - setLastAccessTime(lastAccessTime, NATIVE_TIME_UNIT); + setLastAccessTime(lastAccessTime); this.value = value; this.valueSerialized = valueSerialized; } @@ -85,11 +82,6 @@ protected XAValueHolder copyAfterDeserialization(Serializer valueSerialize return new XAValueHolder<>(this, valueSerializer.read(ByteBuffer.wrap(valueSerialized))); } - @Override - protected TimeUnit nativeTimeUnit() { - return NATIVE_TIME_UNIT; - } - @Override public V get() { return value; @@ -115,7 +107,7 @@ public boolean equals(Object other) { } private Object writeReplace() { - return new SerializedXAValueHolder<>(getId(), creationTime(NATIVE_TIME_UNIT), lastAccessTime(NATIVE_TIME_UNIT), expirationTime(NATIVE_TIME_UNIT), + return new SerializedXAValueHolder<>(getId(), creationTime(), lastAccessTime(), expirationTime(), get(), valueSerialized); } diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/internal/commands/Command.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/commands/Command.java similarity index 100% rename from transactions/src/main/java/org/ehcache/transactions/xa/internal/commands/Command.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/commands/Command.java diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/internal/commands/StoreEvictCommand.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/commands/StoreEvictCommand.java similarity index 100% rename from transactions/src/main/java/org/ehcache/transactions/xa/internal/commands/StoreEvictCommand.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/commands/StoreEvictCommand.java diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/internal/commands/StorePutCommand.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/commands/StorePutCommand.java similarity index 100% rename from transactions/src/main/java/org/ehcache/transactions/xa/internal/commands/StorePutCommand.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/commands/StorePutCommand.java diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/internal/commands/StoreRemoveCommand.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/commands/StoreRemoveCommand.java similarity index 100% rename from transactions/src/main/java/org/ehcache/transactions/xa/internal/commands/StoreRemoveCommand.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/commands/StoreRemoveCommand.java diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/internal/configuration/XAStoreProviderFactory.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/configuration/XAStoreProviderFactory.java similarity index 86% rename from transactions/src/main/java/org/ehcache/transactions/xa/internal/configuration/XAStoreProviderFactory.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/configuration/XAStoreProviderFactory.java index 3427f5fa24..dfd7b9ba2a 100644 --- a/transactions/src/main/java/org/ehcache/transactions/xa/internal/configuration/XAStoreProviderFactory.java +++ b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/configuration/XAStoreProviderFactory.java @@ -19,18 +19,20 @@ import org.ehcache.spi.service.ServiceCreationConfiguration; import org.ehcache.core.spi.service.ServiceFactory; import org.ehcache.transactions.xa.internal.XAStore; +import org.osgi.service.component.annotations.Component; /** * @author Ludovic Orban */ +@Component public class XAStoreProviderFactory implements ServiceFactory { @Override - public XAStore.Provider create(ServiceCreationConfiguration configuration) { + public XAStore.Provider create(ServiceCreationConfiguration configuration) { return new XAStore.Provider(); } @Override - public Class getServiceType() { + public Class getServiceType() { return XAStore.Provider.class; } } diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/internal/journal/DefaultJournalProvider.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/journal/DefaultJournalProvider.java similarity index 72% rename from transactions/src/main/java/org/ehcache/transactions/xa/internal/journal/DefaultJournalProvider.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/journal/DefaultJournalProvider.java index 8024a7b418..9b75bf375b 100644 --- a/transactions/src/main/java/org/ehcache/transactions/xa/internal/journal/DefaultJournalProvider.java +++ b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/journal/DefaultJournalProvider.java @@ -19,6 +19,7 @@ import org.ehcache.CachePersistenceException; import org.ehcache.core.spi.service.DiskResourceService; import org.ehcache.spi.persistence.PersistableResourceService; +import org.ehcache.spi.service.OptionalServiceDependencies; import org.ehcache.spi.service.ServiceDependencies; import org.ehcache.spi.service.ServiceProvider; import org.ehcache.spi.serialization.Serializer; @@ -30,7 +31,7 @@ /** * @author Ludovic Orban */ -@ServiceDependencies(DiskResourceService.class) +@OptionalServiceDependencies("org.ehcache.core.spi.service.DiskResourceService") public class DefaultJournalProvider implements JournalProvider { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultJournalProvider.class); @@ -52,14 +53,16 @@ public Journal getJournal(PersistableResourceService.PersistenceSpaceIden if (persistentSpaceId == null) { LOGGER.info("Using transient XAStore journal"); return new TransientJournal<>(); - } - - try { - LOGGER.info("Using persistent XAStore journal"); - FileBasedPersistenceContext persistenceContext = diskResourceService.createPersistenceContextWithin(persistentSpaceId, "XAJournal"); - return new PersistentJournal<>(persistenceContext.getDirectory(), keySerializer); - } catch (CachePersistenceException cpe) { - throw new RuntimeException(cpe); + } else if (diskResourceService == null) { + throw new AssertionError("Null diskResourceService with non-null persistentSpaceId [" + persistentSpaceId + "]"); + } else { + try { + LOGGER.info("Using persistent XAStore journal"); + FileBasedPersistenceContext persistenceContext = diskResourceService.createPersistenceContextWithin(persistentSpaceId, "XAJournal"); + return new PersistentJournal<>(persistenceContext.getDirectory(), keySerializer); + } catch (CachePersistenceException cpe) { + throw new RuntimeException(cpe); + } } } } diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/internal/journal/DefaultJournalProviderFactory.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/journal/DefaultJournalProviderFactory.java similarity index 83% rename from transactions/src/main/java/org/ehcache/transactions/xa/internal/journal/DefaultJournalProviderFactory.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/journal/DefaultJournalProviderFactory.java index 3ae49dee0a..15f777336c 100644 --- a/transactions/src/main/java/org/ehcache/transactions/xa/internal/journal/DefaultJournalProviderFactory.java +++ b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/journal/DefaultJournalProviderFactory.java @@ -18,19 +18,21 @@ import org.ehcache.spi.service.ServiceCreationConfiguration; import org.ehcache.core.spi.service.ServiceFactory; +import org.osgi.service.component.annotations.Component; /** * @author Ludovic Orban */ +@Component public class DefaultJournalProviderFactory implements ServiceFactory { @Override - public JournalProvider create(ServiceCreationConfiguration configuration) { + public JournalProvider create(ServiceCreationConfiguration configuration) { return new DefaultJournalProvider(); } @Override - public Class getServiceType() { - return JournalProvider.class; + public Class getServiceType() { + return DefaultJournalProvider.class; } } diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/internal/journal/Journal.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/journal/Journal.java similarity index 100% rename from transactions/src/main/java/org/ehcache/transactions/xa/internal/journal/Journal.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/journal/Journal.java diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/internal/journal/JournalProvider.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/journal/JournalProvider.java similarity index 100% rename from transactions/src/main/java/org/ehcache/transactions/xa/internal/journal/JournalProvider.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/journal/JournalProvider.java diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/internal/journal/PersistentJournal.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/journal/PersistentJournal.java similarity index 98% rename from transactions/src/main/java/org/ehcache/transactions/xa/internal/journal/PersistentJournal.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/journal/PersistentJournal.java index e47e574213..76c82b8f51 100644 --- a/transactions/src/main/java/org/ehcache/transactions/xa/internal/journal/PersistentJournal.java +++ b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/journal/PersistentJournal.java @@ -34,7 +34,7 @@ import java.util.HashMap; import java.util.Map; -import static org.ehcache.core.internal.util.TypeUtil.uncheckedCast; +import static org.ehcache.transactions.xa.internal.TypeUtil.uncheckedCast; /** * A persistent, but not durable {@link Journal} implementation. diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/internal/journal/TransientJournal.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/journal/TransientJournal.java similarity index 100% rename from transactions/src/main/java/org/ehcache/transactions/xa/internal/journal/TransientJournal.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/journal/TransientJournal.java diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/internal/txmgr/NullXAResourceRegistry.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/txmgr/NullXAResourceRegistry.java similarity index 100% rename from transactions/src/main/java/org/ehcache/transactions/xa/internal/txmgr/NullXAResourceRegistry.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/txmgr/NullXAResourceRegistry.java diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/internal/txmgr/provider/DefaultTransactionManagerProviderFactory.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/txmgr/provider/DefaultTransactionManagerProviderFactory.java similarity index 86% rename from transactions/src/main/java/org/ehcache/transactions/xa/internal/txmgr/provider/DefaultTransactionManagerProviderFactory.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/txmgr/provider/DefaultTransactionManagerProviderFactory.java index 5edb71e685..2cbf253513 100644 --- a/transactions/src/main/java/org/ehcache/transactions/xa/internal/txmgr/provider/DefaultTransactionManagerProviderFactory.java +++ b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/txmgr/provider/DefaultTransactionManagerProviderFactory.java @@ -21,12 +21,14 @@ import org.ehcache.transactions.xa.txmgr.provider.LookupTransactionManagerProvider; import org.ehcache.transactions.xa.txmgr.provider.LookupTransactionManagerProviderConfiguration; import org.ehcache.transactions.xa.txmgr.provider.TransactionManagerProvider; +import org.osgi.service.component.annotations.Component; /** * {@link ServiceFactory} for the default {@link TransactionManagerProvider} * * @see LookupTransactionManagerProvider */ +@Component @ServiceFactory.RequiresConfiguration public class DefaultTransactionManagerProviderFactory implements ServiceFactory { @@ -34,7 +36,7 @@ public class DefaultTransactionManagerProviderFactory implements ServiceFactory< * {@inheritDoc} */ @Override - public TransactionManagerProvider create(ServiceCreationConfiguration configuration) { + public TransactionManagerProvider create(ServiceCreationConfiguration configuration) { return new LookupTransactionManagerProvider((LookupTransactionManagerProviderConfiguration) configuration); } @@ -42,7 +44,7 @@ public TransactionManagerProvider create(ServiceCreationConfiguration getServiceType() { - return TransactionManagerProvider.class; + public Class getServiceType() { + return LookupTransactionManagerProvider.class; } } diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/internal/xml/TxCacheManagerServiceConfigurationParser.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/xml/TxCacheManagerServiceConfigurationParser.java similarity index 84% rename from transactions/src/main/java/org/ehcache/transactions/xa/internal/xml/TxCacheManagerServiceConfigurationParser.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/xml/TxCacheManagerServiceConfigurationParser.java index 9f9b1f934e..1a58b26816 100644 --- a/transactions/src/main/java/org/ehcache/transactions/xa/internal/xml/TxCacheManagerServiceConfigurationParser.java +++ b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/xml/TxCacheManagerServiceConfigurationParser.java @@ -22,8 +22,9 @@ import org.ehcache.xml.BaseConfigParser; import org.ehcache.xml.CacheManagerServiceConfigurationParser; import org.ehcache.spi.service.ServiceCreationConfiguration; -import org.ehcache.core.internal.util.ClassLoading; +import org.ehcache.xml.JaxbParsers; import org.ehcache.xml.exceptions.XmlConfigurationException; +import org.osgi.service.component.annotations.Component; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -34,11 +35,13 @@ import java.net.URI; import java.net.URL; -import static org.ehcache.core.internal.util.TypeUtil.uncheckedCast; +import static org.ehcache.core.util.ClassLoading.delegationChain; +import static org.ehcache.transactions.xa.internal.TypeUtil.uncheckedCast; /** * @author Ludovic Orban */ +@Component public class TxCacheManagerServiceConfigurationParser extends BaseConfigParser implements CacheManagerServiceConfigurationParser { private static final URI NAMESPACE = URI.create("http://www.ehcache.org/v3/tx"); private static final URL XML_SCHEMA = TxCacheManagerServiceConfigurationParser.class.getResource("/ehcache-tx-ext.xsd"); @@ -57,13 +60,16 @@ public URI getNamespace() { } @Override - public ServiceCreationConfiguration parseServiceCreationConfiguration(Element fragment) { + public ServiceCreationConfiguration parseServiceCreationConfiguration(Element fragment, ClassLoader classLoader) { String localName = fragment.getLocalName(); if ("jta-tm".equals(localName)) { - String transactionManagerProviderConfigurationClassName = fragment.getAttribute("transaction-manager-lookup-class"); + String transactionManagerProviderConfigurationClassName = JaxbParsers.parsePropertyOrString(fragment.getAttribute("transaction-manager-lookup-class")); try { - ClassLoader defaultClassLoader = ClassLoading.getDefaultClassLoader(); - Class aClass = Class.forName(transactionManagerProviderConfigurationClassName, true, defaultClassLoader); + Class aClass = Class.forName(transactionManagerProviderConfigurationClassName, true, delegationChain( + () -> Thread.currentThread().getContextClassLoader(), + getClass().getClassLoader(), + classLoader + )); Class clazz = uncheckedCast(aClass); return new LookupTransactionManagerProviderConfiguration(clazz); } catch (Exception e) { @@ -81,7 +87,7 @@ public Class getServiceType() { } @Override - public Element unparseServiceCreationConfiguration(ServiceCreationConfiguration serviceCreationConfiguration) { + public Element unparseServiceCreationConfiguration(ServiceCreationConfiguration serviceCreationConfiguration) { return unparseConfig(serviceCreationConfiguration); } diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/internal/xml/TxCacheServiceConfigurationParser.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/xml/TxCacheServiceConfigurationParser.java similarity index 88% rename from transactions/src/main/java/org/ehcache/transactions/xa/internal/xml/TxCacheServiceConfigurationParser.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/xml/TxCacheServiceConfigurationParser.java index 1e3b054c40..f5b5993385 100644 --- a/transactions/src/main/java/org/ehcache/transactions/xa/internal/xml/TxCacheServiceConfigurationParser.java +++ b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/internal/xml/TxCacheServiceConfigurationParser.java @@ -21,7 +21,9 @@ import org.ehcache.spi.service.ServiceConfiguration; import org.ehcache.transactions.xa.internal.XAStore; import org.ehcache.transactions.xa.configuration.XAStoreConfiguration; +import org.ehcache.xml.JaxbParsers; import org.ehcache.xml.exceptions.XmlConfigurationException; +import org.osgi.service.component.annotations.Component; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -36,6 +38,7 @@ /** * @author Ludovic Orban */ +@Component public class TxCacheServiceConfigurationParser extends BaseConfigParser implements CacheServiceConfigurationParser { private static final URI NAMESPACE = URI.create("http://www.ehcache.org/v3/tx"); @@ -54,10 +57,10 @@ public URI getNamespace() { } @Override - public ServiceConfiguration parseServiceConfiguration(Element fragment) { + public ServiceConfiguration parseServiceConfiguration(Element fragment, ClassLoader classLoader) { String localName = fragment.getLocalName(); if ("xa-store".equals(localName)) { - String uniqueXAResourceId = fragment.getAttribute("unique-XAResource-id"); + String uniqueXAResourceId = JaxbParsers.parsePropertyOrString(fragment.getAttribute("unique-XAResource-id")); return new XAStoreConfiguration(uniqueXAResourceId); } else { throw new XmlConfigurationException(String.format("XML configuration element <%s> in <%s> is not supported", @@ -71,7 +74,7 @@ public Class getServiceType() { } @Override - public Element unparseServiceConfiguration(ServiceConfiguration serviceConfiguration) { + public Element unparseServiceConfiguration(ServiceConfiguration serviceConfiguration) { return unparseConfig(serviceConfiguration); } diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/txmgr/TransactionManagerWrapper.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/txmgr/TransactionManagerWrapper.java similarity index 100% rename from transactions/src/main/java/org/ehcache/transactions/xa/txmgr/TransactionManagerWrapper.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/txmgr/TransactionManagerWrapper.java diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/txmgr/XAResourceRegistry.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/txmgr/XAResourceRegistry.java similarity index 100% rename from transactions/src/main/java/org/ehcache/transactions/xa/txmgr/XAResourceRegistry.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/txmgr/XAResourceRegistry.java diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/txmgr/btm/BitronixTransactionManagerLookup.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/txmgr/btm/BitronixTransactionManagerLookup.java similarity index 100% rename from transactions/src/main/java/org/ehcache/transactions/xa/txmgr/btm/BitronixTransactionManagerLookup.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/txmgr/btm/BitronixTransactionManagerLookup.java diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/txmgr/btm/BitronixXAResourceRegistry.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/txmgr/btm/BitronixXAResourceRegistry.java similarity index 100% rename from transactions/src/main/java/org/ehcache/transactions/xa/txmgr/btm/BitronixXAResourceRegistry.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/txmgr/btm/BitronixXAResourceRegistry.java diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/txmgr/btm/Ehcache3XAResourceHolder.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/txmgr/btm/Ehcache3XAResourceHolder.java similarity index 100% rename from transactions/src/main/java/org/ehcache/transactions/xa/txmgr/btm/Ehcache3XAResourceHolder.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/txmgr/btm/Ehcache3XAResourceHolder.java diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/txmgr/btm/Ehcache3XAResourceProducer.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/txmgr/btm/Ehcache3XAResourceProducer.java similarity index 100% rename from transactions/src/main/java/org/ehcache/transactions/xa/txmgr/btm/Ehcache3XAResourceProducer.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/txmgr/btm/Ehcache3XAResourceProducer.java diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/txmgr/provider/LookupTransactionManagerProvider.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/txmgr/provider/LookupTransactionManagerProvider.java similarity index 100% rename from transactions/src/main/java/org/ehcache/transactions/xa/txmgr/provider/LookupTransactionManagerProvider.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/txmgr/provider/LookupTransactionManagerProvider.java diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/txmgr/provider/LookupTransactionManagerProviderConfiguration.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/txmgr/provider/LookupTransactionManagerProviderConfiguration.java similarity index 76% rename from transactions/src/main/java/org/ehcache/transactions/xa/txmgr/provider/LookupTransactionManagerProviderConfiguration.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/txmgr/provider/LookupTransactionManagerProviderConfiguration.java index c138922816..3a6935512e 100644 --- a/transactions/src/main/java/org/ehcache/transactions/xa/txmgr/provider/LookupTransactionManagerProviderConfiguration.java +++ b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/txmgr/provider/LookupTransactionManagerProviderConfiguration.java @@ -18,12 +18,12 @@ import org.ehcache.spi.service.ServiceCreationConfiguration; -import static org.ehcache.core.internal.util.TypeUtil.uncheckedCast; +import static org.ehcache.transactions.xa.internal.TypeUtil.uncheckedCast; /** * Specialized {@link ServiceCreationConfiguration} for the {@link LookupTransactionManagerProvider}. */ -public class LookupTransactionManagerProviderConfiguration implements ServiceCreationConfiguration { +public class LookupTransactionManagerProviderConfiguration implements ServiceCreationConfiguration> { private final Class lookupClass; @@ -48,4 +48,14 @@ public Class getTransactionManagerLookup() { public Class getServiceType() { return TransactionManagerProvider.class; } + + @Override + public Class derive() { + return getTransactionManagerLookup(); + } + + @Override + public LookupTransactionManagerProviderConfiguration build(Class clazz) { + return new LookupTransactionManagerProviderConfiguration(clazz); + } } diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/txmgr/provider/TransactionManagerLookup.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/txmgr/provider/TransactionManagerLookup.java similarity index 100% rename from transactions/src/main/java/org/ehcache/transactions/xa/txmgr/provider/TransactionManagerLookup.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/txmgr/provider/TransactionManagerLookup.java diff --git a/transactions/src/main/java/org/ehcache/transactions/xa/txmgr/provider/TransactionManagerProvider.java b/ehcache-transactions/src/main/java/org/ehcache/transactions/xa/txmgr/provider/TransactionManagerProvider.java similarity index 100% rename from transactions/src/main/java/org/ehcache/transactions/xa/txmgr/provider/TransactionManagerProvider.java rename to ehcache-transactions/src/main/java/org/ehcache/transactions/xa/txmgr/provider/TransactionManagerProvider.java diff --git a/transactions/src/main/resources/META-INF/services/org.ehcache.core.spi.service.ServiceFactory b/ehcache-transactions/src/main/resources/META-INF/services/org.ehcache.core.spi.service.ServiceFactory similarity index 100% rename from transactions/src/main/resources/META-INF/services/org.ehcache.core.spi.service.ServiceFactory rename to ehcache-transactions/src/main/resources/META-INF/services/org.ehcache.core.spi.service.ServiceFactory diff --git a/transactions/src/main/resources/META-INF/services/org.ehcache.xml.CacheManagerServiceConfigurationParser b/ehcache-transactions/src/main/resources/META-INF/services/org.ehcache.xml.CacheManagerServiceConfigurationParser similarity index 100% rename from transactions/src/main/resources/META-INF/services/org.ehcache.xml.CacheManagerServiceConfigurationParser rename to ehcache-transactions/src/main/resources/META-INF/services/org.ehcache.xml.CacheManagerServiceConfigurationParser diff --git a/transactions/src/main/resources/META-INF/services/org.ehcache.xml.CacheServiceConfigurationParser b/ehcache-transactions/src/main/resources/META-INF/services/org.ehcache.xml.CacheServiceConfigurationParser similarity index 100% rename from transactions/src/main/resources/META-INF/services/org.ehcache.xml.CacheServiceConfigurationParser rename to ehcache-transactions/src/main/resources/META-INF/services/org.ehcache.xml.CacheServiceConfigurationParser diff --git a/transactions/src/main/resources/ehcache-tx-ext.xsd b/ehcache-transactions/src/main/resources/ehcache-tx-ext.xsd similarity index 91% rename from transactions/src/main/resources/ehcache-tx-ext.xsd rename to ehcache-transactions/src/main/resources/ehcache-tx-ext.xsd index 23f5bd6ab6..266c992aa5 100644 --- a/transactions/src/main/resources/ehcache-tx-ext.xsd +++ b/ehcache-transactions/src/main/resources/ehcache-tx-ext.xsd @@ -28,11 +28,11 @@ - + - + - \ No newline at end of file + diff --git a/transactions/src/test/java/org/ehcache/docs/transactions/xa/XAGettingStarted.java b/ehcache-transactions/src/test/java/org/ehcache/docs/transactions/xa/XAGettingStarted.java similarity index 95% rename from transactions/src/test/java/org/ehcache/docs/transactions/xa/XAGettingStarted.java rename to ehcache-transactions/src/test/java/org/ehcache/docs/transactions/xa/XAGettingStarted.java index a92cd9c2c7..5caf90a9d7 100644 --- a/transactions/src/test/java/org/ehcache/docs/transactions/xa/XAGettingStarted.java +++ b/ehcache-transactions/src/test/java/org/ehcache/docs/transactions/xa/XAGettingStarted.java @@ -20,8 +20,8 @@ import org.ehcache.Cache; import org.ehcache.CacheManager; import org.ehcache.PersistentCacheManager; -import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.config.Configuration; +import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.config.builders.ResourcePoolsBuilder; import org.ehcache.config.builders.CacheManagerBuilder; import org.ehcache.impl.config.loaderwriter.DefaultCacheLoaderWriterConfiguration; @@ -49,8 +49,8 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import static java.util.Collections.singletonMap; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; /** @@ -83,7 +83,7 @@ public void testSimpleXACache() throws Exception { .using(new LookupTransactionManagerProviderConfiguration(BitronixTransactionManagerLookup.class)) // <2> .withCache("xaCache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, // <3> ResourcePoolsBuilder.heap(10)) // <4> - .add(new XAStoreConfiguration("xaCache")) // <5> + .withService(new XAStoreConfiguration("xaCache")) // <5> .build() ) .build(true); @@ -111,7 +111,7 @@ public void testNonTransactionalAccess() throws Exception { .using(new LookupTransactionManagerProviderConfiguration(BitronixTransactionManagerLookup.class)) // <2> .withCache("xaCache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, // <3> ResourcePoolsBuilder.heap(10)) // <4> - .add(new XAStoreConfiguration("xaCache")) // <5> + .withService(new XAStoreConfiguration("xaCache")) // <5> .build() ) .build(true); @@ -143,8 +143,8 @@ public void testXACacheWithWriteThrough() throws Exception { .using(new LookupTransactionManagerProviderConfiguration(BitronixTransactionManagerLookup.class)) // <2> .withCache("xaCache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, // <3> ResourcePoolsBuilder.heap(10)) // <4> - .add(new XAStoreConfiguration("xaCache")) // <5> - .add(new DefaultCacheLoaderWriterConfiguration(klazz, singletonMap(1L, "eins"))) // <6> + .withService(new XAStoreConfiguration("xaCache")) // <5> + .withService(new DefaultCacheLoaderWriterConfiguration(klazz, singletonMap(1L, "eins"))) // <6> .build() ) .build(true); @@ -178,7 +178,7 @@ public void testXACacheWithThreeTiers() throws Exception { .offheap(10, MemoryUnit.MB) .disk(20, MemoryUnit.MB, true) ) - .add(new XAStoreConfiguration("xaCache")) // <6> + .withService(new XAStoreConfiguration("xaCache")) // <6> .build() ) .build(true); diff --git a/impl/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreLifecycleHelper.java b/ehcache-transactions/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreLifecycleHelper.java similarity index 75% rename from impl/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreLifecycleHelper.java rename to ehcache-transactions/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreLifecycleHelper.java index 93f26c4a96..7668106c27 100644 --- a/impl/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreLifecycleHelper.java +++ b/ehcache-transactions/src/test/java/org/ehcache/impl/internal/store/offheap/OffHeapStoreLifecycleHelper.java @@ -13,8 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.ehcache.impl.internal.store.offheap; +/* + * This is an import of a shaded class because we depend on the shaded distribution jar. + * This means we have to use the shaded StatisticsManager when digging in to the internals like this. + */ +import org.ehcache.shadow.org.terracotta.statistics.StatisticsManager; + /** * @author Ludovic Orban */ @@ -29,6 +36,7 @@ public static void init(OffHeapStore offHeapStore) { public static void close(OffHeapStore offHeapStore) { OffHeapStore.Provider.close(offHeapStore); + StatisticsManager.nodeFor(offHeapStore).clean(); } } diff --git a/transactions/src/test/java/org/ehcache/transactions/NonXACacheTest.java b/ehcache-transactions/src/test/java/org/ehcache/transactions/NonXACacheTest.java similarity index 72% rename from transactions/src/test/java/org/ehcache/transactions/NonXACacheTest.java rename to ehcache-transactions/src/test/java/org/ehcache/transactions/NonXACacheTest.java index 803fb297d0..81bcae99fb 100644 --- a/transactions/src/test/java/org/ehcache/transactions/NonXACacheTest.java +++ b/ehcache-transactions/src/test/java/org/ehcache/transactions/NonXACacheTest.java @@ -23,18 +23,17 @@ import org.ehcache.config.builders.ResourcePoolsBuilder; import org.ehcache.config.units.EntryUnit; import org.ehcache.config.units.MemoryUnit; -import org.ehcache.core.internal.util.ClassLoading; import org.ehcache.core.spi.service.ServiceFactory; +import org.ehcache.core.util.ClassLoading; import org.ehcache.transactions.xa.internal.XAStore; -import org.ehcache.transactions.xa.txmgr.provider.TransactionManagerProvider; -import org.hamcrest.Matchers; +import org.ehcache.transactions.xa.txmgr.provider.LookupTransactionManagerProvider; import org.junit.Test; -import java.util.HashSet; -import java.util.Set; - -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; +import static java.util.Spliterators.spliterator; +import static java.util.stream.Collectors.toList; +import static java.util.stream.StreamSupport.stream; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsCollectionContaining.hasItems; /** * Ensures that a non-XA {@code CacheManager} can be created when XA classes are @@ -48,17 +47,8 @@ public void testNonXA() throws Exception { /* * Ensure the XA provider classes are loadable through the ServiceLoader mechanism. */ - Set> targetProviders = new HashSet<>(); - targetProviders.add(XAStore.Provider.class); - targetProviders.add(TransactionManagerProvider.class); - for (ServiceFactory factory : ClassLoading.libraryServiceLoaderFor(ServiceFactory.class)) { - if (targetProviders.remove(factory.getServiceType())) { - if (targetProviders.isEmpty()) { - break; - } - } - } - assertThat(targetProviders, is(Matchers.empty())); + assertThat(stream(spliterator(ClassLoading.servicesOfType(ServiceFactory.class).iterator(), Long.MAX_VALUE, 0), false).map(s -> s.getServiceType()).collect(toList()), + hasItems(XAStore.Provider.class, LookupTransactionManagerProvider.class)); CacheConfiguration cacheConfiguration = CacheConfigurationBuilder.newCacheConfigurationBuilder( String.class, diff --git a/transactions/src/test/java/org/ehcache/transactions/TransactionalCacheParserIT.java b/ehcache-transactions/src/test/java/org/ehcache/transactions/TransactionalCacheParserIT.java similarity index 64% rename from transactions/src/test/java/org/ehcache/transactions/TransactionalCacheParserIT.java rename to ehcache-transactions/src/test/java/org/ehcache/transactions/TransactionalCacheParserIT.java index 28bb8f247f..9a1d723fc8 100644 --- a/transactions/src/test/java/org/ehcache/transactions/TransactionalCacheParserIT.java +++ b/ehcache-transactions/src/test/java/org/ehcache/transactions/TransactionalCacheParserIT.java @@ -17,16 +17,12 @@ import org.ehcache.config.Configuration; import org.ehcache.xml.XmlConfiguration; -import org.ehcache.xml.XmlConfigurationTest; import org.junit.Test; -import org.xmlunit.builder.Input; -import org.xmlunit.diff.DefaultNodeMatcher; -import org.xmlunit.diff.ElementSelectors; import java.net.URL; -import static org.junit.Assert.assertThat; -import static org.xmlunit.matchers.CompareMatcher.isSimilarTo; +import static org.ehcache.xml.XmlConfigurationMatchers.isSameConfigurationAs; +import static org.hamcrest.MatcherAssert.assertThat; /** * TransactionalCacheParserIT @@ -35,11 +31,9 @@ public class TransactionalCacheParserIT { @Test public void testTransactionalCacheXmlTranslationToString() { - URL resource = XmlConfigurationTest.class.getResource("/configs/transactional-cache.xml"); + URL resource = TransactionalCacheParserIT.class.getResource("/configs/transactional-cache.xml"); Configuration config = new XmlConfiguration(resource); XmlConfiguration xmlConfig = new XmlConfiguration(config); - assertThat(xmlConfig.toString(), isSimilarTo(Input.from(resource)).ignoreComments() - .ignoreWhitespace() - .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText))); + assertThat(xmlConfig.toString(), isSameConfigurationAs(resource)); } } diff --git a/transactions/src/test/java/org/ehcache/transactions/XmlConfigTest.java b/ehcache-transactions/src/test/java/org/ehcache/transactions/XmlConfigTest.java similarity index 97% rename from transactions/src/test/java/org/ehcache/transactions/XmlConfigTest.java rename to ehcache-transactions/src/test/java/org/ehcache/transactions/XmlConfigTest.java index 46912b8fff..2334523844 100644 --- a/transactions/src/test/java/org/ehcache/transactions/XmlConfigTest.java +++ b/ehcache-transactions/src/test/java/org/ehcache/transactions/XmlConfigTest.java @@ -28,8 +28,8 @@ import java.net.URL; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; /** * @author Ludovic Orban diff --git a/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/configuration/XAStoreConfigurationTest.java b/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/configuration/XAStoreConfigurationTest.java new file mode 100644 index 0000000000..6d05489192 --- /dev/null +++ b/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/configuration/XAStoreConfigurationTest.java @@ -0,0 +1,36 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.transactions.xa.configuration; + +import org.hamcrest.core.IsNot; +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsSame.sameInstance; + +public class XAStoreConfigurationTest { + + @Test + public void testDeriveDetachesProperly() { + XAStoreConfiguration configuration = new XAStoreConfiguration("foobar"); + XAStoreConfiguration derived = configuration.build(configuration.derive()); + + assertThat(derived, is(IsNot.not(sameInstance(configuration)))); + assertThat(derived.getUniqueXAResourceId(), is(configuration.getUniqueXAResourceId())); + } +} diff --git a/transactions/src/test/java/org/ehcache/transactions/xa/integration/StatefulSerializerTest.java b/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/integration/StatefulSerializerTest.java similarity index 96% rename from transactions/src/test/java/org/ehcache/transactions/xa/integration/StatefulSerializerTest.java rename to ehcache-transactions/src/test/java/org/ehcache/transactions/xa/integration/StatefulSerializerTest.java index ef2896ce32..c88b2d279c 100644 --- a/transactions/src/test/java/org/ehcache/transactions/xa/integration/StatefulSerializerTest.java +++ b/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/integration/StatefulSerializerTest.java @@ -63,7 +63,7 @@ public void testXAWithStatefulSerializer() throws Exception { CacheConfigurationBuilder .newCacheConfigurationBuilder(Long.class, Person.class, ResourcePoolsBuilder.heap(5)) - .withExpiry(ExpiryPolicyBuilder.noExpiration()).add(new XAStoreConfiguration("xaCache")) + .withExpiry(ExpiryPolicyBuilder.noExpiration()).withService(new XAStoreConfiguration("xaCache")) .build()) .build(true)) { diff --git a/transactions/src/test/java/org/ehcache/transactions/xa/internal/EhcacheXAResourceTest.java b/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/internal/EhcacheXAResourceTest.java similarity index 99% rename from transactions/src/test/java/org/ehcache/transactions/xa/internal/EhcacheXAResourceTest.java rename to ehcache-transactions/src/test/java/org/ehcache/transactions/xa/internal/EhcacheXAResourceTest.java index 4129ce1c1b..34a17f97bb 100644 --- a/transactions/src/test/java/org/ehcache/transactions/xa/internal/EhcacheXAResourceTest.java +++ b/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/internal/EhcacheXAResourceTest.java @@ -32,8 +32,8 @@ import java.util.Collection; import java.util.Collections; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; diff --git a/transactions/src/test/java/org/ehcache/transactions/xa/internal/UnSupportedResourceTypeTest.java b/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/internal/UnSupportedResourceTypeTest.java similarity index 99% rename from transactions/src/test/java/org/ehcache/transactions/xa/internal/UnSupportedResourceTypeTest.java rename to ehcache-transactions/src/test/java/org/ehcache/transactions/xa/internal/UnSupportedResourceTypeTest.java index c25904ffcc..e87ece536d 100644 --- a/transactions/src/test/java/org/ehcache/transactions/xa/internal/UnSupportedResourceTypeTest.java +++ b/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/internal/UnSupportedResourceTypeTest.java @@ -48,7 +48,7 @@ public void testUnSupportedResourceType() { when(resourcePools.getResourceTypeSet()).thenReturn(resourceTypes); try { - provider.createStore(configuration, (ServiceConfiguration) null); + provider.createStore(configuration, (ServiceConfiguration) null); fail("IllegalStateException expected"); } catch (IllegalStateException e) { diff --git a/transactions/src/test/java/org/ehcache/transactions/xa/internal/XAStoreProviderTest.java b/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/internal/XAStoreProviderTest.java similarity index 94% rename from transactions/src/test/java/org/ehcache/transactions/xa/internal/XAStoreProviderTest.java rename to ehcache-transactions/src/test/java/org/ehcache/transactions/xa/internal/XAStoreProviderTest.java index fb3085a48a..e6546f7f83 100644 --- a/transactions/src/test/java/org/ehcache/transactions/xa/internal/XAStoreProviderTest.java +++ b/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/internal/XAStoreProviderTest.java @@ -18,8 +18,10 @@ import org.ehcache.config.builders.ResourcePoolsBuilder; import org.ehcache.config.units.MemoryUnit; +import org.ehcache.core.spi.service.StatisticsService; import org.ehcache.core.spi.store.Store; import org.ehcache.core.spi.time.TimeSourceService; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; import org.ehcache.impl.internal.DefaultTimeSourceService; import org.ehcache.impl.internal.store.offheap.OffHeapStore; import org.ehcache.spi.persistence.StateRepository; @@ -60,6 +62,7 @@ public void testXAStoreProviderStatefulSerializer() { when(serviceProvider.getService(JournalProvider.class)).thenReturn(journalProvider); when(serviceProvider.getService(TimeSourceService.class)).thenReturn(new DefaultTimeSourceService(null)); when(serviceProvider.getService(TransactionManagerProvider.class)).thenReturn(transactionManagerProvider); + when(serviceProvider.getService(StatisticsService.class)).thenReturn(new DefaultStatisticsService()); when(serviceProvider.getServicesOfType(Store.Provider.class)).thenReturn(Collections.singleton(underlyingStoreProvider)); Store.Configuration configuration = mock(Store.Configuration.class); diff --git a/transactions/src/test/java/org/ehcache/transactions/xa/internal/XAStoreTest.java b/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/internal/XAStoreTest.java similarity index 97% rename from transactions/src/test/java/org/ehcache/transactions/xa/internal/XAStoreTest.java rename to ehcache-transactions/src/test/java/org/ehcache/transactions/xa/internal/XAStoreTest.java index 6218e7147e..dc2c0c2e8d 100644 --- a/transactions/src/test/java/org/ehcache/transactions/xa/internal/XAStoreTest.java +++ b/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/internal/XAStoreTest.java @@ -18,17 +18,17 @@ import org.ehcache.Cache; import org.ehcache.config.EvictionAdvisor; -import org.ehcache.config.ResourcePool; -import org.ehcache.config.ResourceType; import org.ehcache.config.builders.ExpiryPolicyBuilder; import org.ehcache.config.builders.ResourcePoolsBuilder; import org.ehcache.config.units.EntryUnit; import org.ehcache.config.units.MemoryUnit; -import org.ehcache.core.internal.store.StoreConfigurationImpl; import org.ehcache.core.events.StoreEventDispatcher; -import org.ehcache.core.internal.service.ServiceLocator; +import org.ehcache.core.spi.ServiceLocator; +import org.ehcache.core.spi.service.CacheManagerProviderService; import org.ehcache.core.spi.service.DiskResourceService; import org.ehcache.core.spi.store.Store; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; +import org.ehcache.core.store.StoreConfigurationImpl; import org.ehcache.expiry.ExpiryPolicy; import org.ehcache.impl.config.copy.DefaultCopyProviderConfiguration; import org.ehcache.core.events.NullStoreEventDispatcher; @@ -59,6 +59,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; +import org.mockito.Answers; import java.time.Duration; import java.util.Arrays; @@ -91,13 +92,12 @@ import static java.time.Duration.ofSeconds; import static java.util.Collections.emptySet; import static org.ehcache.config.builders.ExpiryPolicyBuilder.timeToLiveExpiration; -import static org.ehcache.core.internal.service.ServiceLocator.dependencySet; +import static org.ehcache.core.spi.ServiceLocator.dependencySet; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; -import static org.hamcrest.Matchers.startsWith; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; @@ -143,7 +143,7 @@ public void setUp() { 0, keySerializer, valueSerializer); testTimeSource = new TestTimeSource(); eventDispatcher = NullStoreEventDispatcher.nullStoreEventDispatcher(); - onHeapStore = new OnHeapStore<>(onHeapConfig, testTimeSource, keyCopier, valueCopier, new NoopSizeOfEngine(), eventDispatcher); + onHeapStore = new OnHeapStore<>(onHeapConfig, testTimeSource, keyCopier, valueCopier, new NoopSizeOfEngine(), eventDispatcher, new DefaultStatisticsService()); journal = new TransientJournal<>(); } @@ -693,7 +693,7 @@ public void testGetAndCompute() throws Exception { .build(), 0, keySerializer, valueSerializer); OffHeapStore> offHeapStore = new OffHeapStore<>(offHeapConfig, testTimeSource, eventDispatcher, MemorySizeParser - .parse("10M")); + .parse("10M"), new DefaultStatisticsService()); OffHeapStoreLifecycleHelper.init(offHeapStore); TieredStore> tieredStore = new TieredStore<>(onHeapStore, offHeapStore); @@ -883,7 +883,7 @@ public void testCompute() throws Exception { .build(), 0, keySerializer, valueSerializer); OffHeapStore> offHeapStore = new OffHeapStore<>(offHeapConfig, testTimeSource, eventDispatcher, MemorySizeParser - .parse("10M")); + .parse("10M"), new DefaultStatisticsService()); OffHeapStoreLifecycleHelper.init(offHeapStore); TieredStore> tieredStore = new TieredStore<>(onHeapStore, offHeapStore); @@ -1059,7 +1059,7 @@ public void testComputeIfAbsent() throws Exception { .build(), 0, keySerializer, valueSerializer); OffHeapStore> offHeapStore = new OffHeapStore<>(offHeapConfig, testTimeSource, eventDispatcher, MemorySizeParser - .parse("10M")); + .parse("10M"), new DefaultStatisticsService()); OffHeapStoreLifecycleHelper.init(offHeapStore); TieredStore> tieredStore = new TieredStore<>(onHeapStore, offHeapStore); @@ -1110,12 +1110,12 @@ public void testExpiry() throws Exception { Store.Configuration> onHeapConfig = new StoreConfigurationImpl<>(Long.class, valueClass, null, classLoader, expiry, ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES).build(), 0, keySerializer, valueSerializer); - onHeapStore = new OnHeapStore<>(onHeapConfig, testTimeSource, keyCopier, valueCopier, new NoopSizeOfEngine(), eventDispatcher); + onHeapStore = new OnHeapStore<>(onHeapConfig, testTimeSource, keyCopier, valueCopier, new NoopSizeOfEngine(), eventDispatcher, new DefaultStatisticsService()); Store.Configuration> offHeapConfig = new StoreConfigurationImpl<>(Long.class, valueClass, null, classLoader, expiry, ResourcePoolsBuilder.newResourcePoolsBuilder().offheap(10, MemoryUnit.MB).build(), 0, keySerializer, valueSerializer); OffHeapStore> offHeapStore = new OffHeapStore<>(offHeapConfig, testTimeSource, eventDispatcher, MemorySizeParser - .parse("10M")); + .parse("10M"), new DefaultStatisticsService()); OffHeapStoreLifecycleHelper.init(offHeapStore); TieredStore> tieredStore = new TieredStore<>(onHeapStore, offHeapStore); @@ -1158,12 +1158,12 @@ public Duration getExpiryForUpdate(Object key, Supplier oldValue, Object newV Store.Configuration> onHeapConfig = new StoreConfigurationImpl<>(Long.class, valueClass, null, classLoader, expiry, ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES).build(), 0, keySerializer, valueSerializer); - OnHeapStore> onHeapStore = new OnHeapStore<>(onHeapConfig, testTimeSource, keyCopier, valueCopier, new NoopSizeOfEngine(), eventDispatcher); + OnHeapStore> onHeapStore = new OnHeapStore<>(onHeapConfig, testTimeSource, keyCopier, valueCopier, new NoopSizeOfEngine(), eventDispatcher, new DefaultStatisticsService()); Store.Configuration> offHeapConfig = new StoreConfigurationImpl<>(Long.class, valueClass, null, classLoader, expiry, ResourcePoolsBuilder.newResourcePoolsBuilder().offheap(10, MemoryUnit.MB).build(), 0, keySerializer, valueSerializer); OffHeapStore> offHeapStore = new OffHeapStore<>(offHeapConfig, testTimeSource, eventDispatcher, MemorySizeParser - .parse("10M")); + .parse("10M"), new DefaultStatisticsService()); OffHeapStoreLifecycleHelper.init(offHeapStore); TieredStore> tieredStore = new TieredStore<>(onHeapStore, offHeapStore); @@ -1201,12 +1201,12 @@ public Duration getExpiryForUpdate(Object key, Supplier oldValue, Object newV Store.Configuration> onHeapConfig = new StoreConfigurationImpl<>(Long.class, valueClass, null, classLoader, expiry, ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES).build(), 0, keySerializer, valueSerializer); - OnHeapStore> onHeapStore = new OnHeapStore<>(onHeapConfig, testTimeSource, keyCopier, valueCopier, new NoopSizeOfEngine(), eventDispatcher); + OnHeapStore> onHeapStore = new OnHeapStore<>(onHeapConfig, testTimeSource, keyCopier, valueCopier, new NoopSizeOfEngine(), eventDispatcher, new DefaultStatisticsService()); Store.Configuration> offHeapConfig = new StoreConfigurationImpl<>(Long.class, valueClass, null, classLoader, expiry, ResourcePoolsBuilder.newResourcePoolsBuilder().offheap(10, MemoryUnit.MB).build(), 0, keySerializer, valueSerializer); OffHeapStore> offHeapStore = new OffHeapStore<>(offHeapConfig, testTimeSource, eventDispatcher, MemorySizeParser - .parse("10M")); + .parse("10M"), new DefaultStatisticsService()); OffHeapStoreLifecycleHelper.init(offHeapStore); TieredStore> tieredStore = new TieredStore<>(onHeapStore, offHeapStore); @@ -1251,12 +1251,12 @@ public Duration getExpiryForUpdate(Object key, Supplier oldValue, Object newV Store.Configuration> onHeapConfig = new StoreConfigurationImpl<>(Long.class, valueClass, null, classLoader, expiry, ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES).build(), 0, keySerializer, valueSerializer); - OnHeapStore> onHeapStore = new OnHeapStore<>(onHeapConfig, testTimeSource, keyCopier, valueCopier, new NoopSizeOfEngine(), eventDispatcher); + OnHeapStore> onHeapStore = new OnHeapStore<>(onHeapConfig, testTimeSource, keyCopier, valueCopier, new NoopSizeOfEngine(), eventDispatcher, new DefaultStatisticsService()); Store.Configuration> offHeapConfig = new StoreConfigurationImpl<>(Long.class, valueClass, null, classLoader, expiry, ResourcePoolsBuilder.newResourcePoolsBuilder().offheap(10, MemoryUnit.MB).build(), 0, keySerializer, valueSerializer); OffHeapStore> offHeapStore = new OffHeapStore<>(offHeapConfig, testTimeSource, eventDispatcher, MemorySizeParser - .parse("10M")); + .parse("10M"), new DefaultStatisticsService()); OffHeapStoreLifecycleHelper.init(offHeapStore); TieredStore> tieredStore = new TieredStore<>(onHeapStore, offHeapStore); @@ -1280,12 +1280,12 @@ public void testBulkCompute() throws Exception { Store.Configuration> onHeapConfig = new StoreConfigurationImpl<>(Long.class, valueClass, null, classLoader, expiry, ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES).build(), 0, keySerializer, valueSerializer); - OnHeapStore> onHeapStore = new OnHeapStore<>(onHeapConfig, testTimeSource, keyCopier, valueCopier, new NoopSizeOfEngine(), eventDispatcher); + OnHeapStore> onHeapStore = new OnHeapStore<>(onHeapConfig, testTimeSource, keyCopier, valueCopier, new NoopSizeOfEngine(), eventDispatcher, new DefaultStatisticsService()); Store.Configuration> offHeapConfig = new StoreConfigurationImpl<>(Long.class, valueClass, null, classLoader, expiry, ResourcePoolsBuilder.newResourcePoolsBuilder().offheap(10, MemoryUnit.MB).build(), 0, keySerializer, valueSerializer); OffHeapStore> offHeapStore = new OffHeapStore<>(offHeapConfig, testTimeSource, eventDispatcher, MemorySizeParser - .parse("10M")); + .parse("10M"), new DefaultStatisticsService()); OffHeapStoreLifecycleHelper.init(offHeapStore); TieredStore> tieredStore = new TieredStore<>(onHeapStore, offHeapStore); @@ -1355,12 +1355,12 @@ public void testBulkComputeIfAbsent() throws Exception { Store.Configuration> onHeapConfig = new StoreConfigurationImpl<>(Long.class, valueClass, null, classLoader, expiry, ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES).build(), 0, keySerializer, valueSerializer); - OnHeapStore> onHeapStore = new OnHeapStore<>(onHeapConfig, testTimeSource, keyCopier, valueCopier, new NoopSizeOfEngine(), eventDispatcher); + OnHeapStore> onHeapStore = new OnHeapStore<>(onHeapConfig, testTimeSource, keyCopier, valueCopier, new NoopSizeOfEngine(), eventDispatcher, new DefaultStatisticsService()); Store.Configuration> offHeapConfig = new StoreConfigurationImpl<>(Long.class, valueClass, null, classLoader, expiry, ResourcePoolsBuilder.newResourcePoolsBuilder().offheap(10, MemoryUnit.MB).build(), 0, keySerializer, valueSerializer); OffHeapStore> offHeapStore = new OffHeapStore<>(offHeapConfig, testTimeSource, eventDispatcher, MemorySizeParser - .parse("10M")); + .parse("10M"), new DefaultStatisticsService()); OffHeapStoreLifecycleHelper.init(offHeapStore); TieredStore> tieredStore = new TieredStore<>(onHeapStore, offHeapStore); @@ -1426,7 +1426,7 @@ public void testCustomEvictionAdvisor() throws Exception { .heap(10, EntryUnit.ENTRIES) .build(), 0, keySerializer, valueSerializer); - OnHeapStore> onHeapStore = new OnHeapStore<>(onHeapConfig, testTimeSource, keyCopier, valueCopier, new NoopSizeOfEngine(), eventDispatcher); + OnHeapStore> onHeapStore = new OnHeapStore<>(onHeapConfig, testTimeSource, keyCopier, valueCopier, new NoopSizeOfEngine(), eventDispatcher, new DefaultStatisticsService()); final XAStore xaStore = getXAStore(onHeapStore); @@ -1453,14 +1453,15 @@ public void testRank() throws Exception { .with(provider) .with(Store.Provider.class) .with(mock(DiskResourceService.class)) + .with(mock(CacheManagerProviderService.class, Answers.RETURNS_DEEP_STUBS)) .with(mock(TransactionManagerProvider.class)).build(); serviceLocator.startAllServices(); - Set> xaStoreConfigs = Collections.singleton(configuration); + Set> xaStoreConfigs = Collections.singleton(configuration); assertThat(provider.wrapperStoreRank(xaStoreConfigs), is(1)); - Set> emptyConfigs = emptySet(); + Set> emptyConfigs = emptySet(); assertThat(provider.wrapperStoreRank(emptyConfigs), is(0)); } @@ -1497,7 +1498,7 @@ private void assertSize(XAStore xaStore, int expectedSize) throws } private XAStore getXAStore(Store> store) { - return new XAStore<>(Long.class, String.class, store, transactionManagerWrapper, testTimeSource, journal, testName.getMethodName()); + return new XAStore<>(Long.class, String.class, store, transactionManagerWrapper, testTimeSource, journal, testName.getMethodName(), new DefaultStatisticsService()); } static class TestTransactionManager implements TransactionManager { diff --git a/transactions/src/test/java/org/ehcache/transactions/xa/internal/XATransactionContextTest.java b/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/internal/XATransactionContextTest.java similarity index 96% rename from transactions/src/test/java/org/ehcache/transactions/xa/internal/XATransactionContextTest.java rename to ehcache-transactions/src/test/java/org/ehcache/transactions/xa/internal/XATransactionContextTest.java index 35d75c83ef..a1989cf3a9 100644 --- a/transactions/src/test/java/org/ehcache/transactions/xa/internal/XATransactionContextTest.java +++ b/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/internal/XATransactionContextTest.java @@ -26,7 +26,6 @@ import org.ehcache.transactions.xa.internal.journal.Journal; import org.ehcache.core.spi.store.Store.ReplaceStatus; import org.ehcache.transactions.xa.utils.TestXid; -import org.junit.Assert; import org.junit.Rule; import org.junit.Test; import org.mockito.Mock; @@ -235,7 +234,7 @@ public void testPrepare() throws Exception { assertThat(xaTransactionContext.prepare(), is(3)); - Assert.assertThat(savedInDoubt.get(), containsInAnyOrder(1L, 2L, 3L)); + assertThat(savedInDoubt.get(), containsInAnyOrder(1L, 2L, 3L)); verify(journal, times(1)).saveInDoubt(eq(new TransactionId(new TestXid(0, 0))), any(Collection.class)); verify(journal, times(0)).saveCommitted(eq(new TransactionId(new TestXid(0, 0))), anyBoolean()); @@ -349,10 +348,6 @@ public void testCommitInOnePhase() throws Exception { public Object get() { return softLock1Ref.get(); } - @Override - protected TimeUnit nativeTimeUnit() { - return TimeUnit.MILLISECONDS; - } }); when(underlyingStore.putIfAbsent(eq(1L), isA(SoftLock.class), any(Consumer.class))).then(invocation -> { softLock1Ref.set((SoftLock) invocation.getArguments()[1]); @@ -370,10 +365,6 @@ protected TimeUnit nativeTimeUnit() { public Object get() { return softLock2Ref.get(); } - @Override - protected TimeUnit nativeTimeUnit() { - return TimeUnit.MILLISECONDS; - } }); when(underlyingStore.replace(eq(2L), isA(SoftLock.class), isA(SoftLock.class))).then(invocation -> { softLock2Ref.set((SoftLock) invocation.getArguments()[2]); @@ -384,7 +375,7 @@ protected TimeUnit nativeTimeUnit() { xaTransactionContext.commitInOnePhase(); - Assert.assertThat(savedInDoubtCollectionRef.get(), containsInAnyOrder(1L, 2L, 3L)); + assertThat(savedInDoubtCollectionRef.get(), containsInAnyOrder(1L, 2L, 3L)); verify(journal, times(1)).saveCommitted(eq(new TransactionId(new TestXid(0, 0))), eq(false)); verify(journal, times(0)).saveRolledBack(eq(new TransactionId(new TestXid(0, 0))), anyBoolean()); @@ -428,20 +419,12 @@ public void testRollbackPhase2() throws Exception { when(journal.getInDoubtKeys(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(Arrays.asList(1L, 2L)); when(underlyingStore.get(1L)).thenReturn(new AbstractValueHolder>(-1, -1) { - @Override - protected TimeUnit nativeTimeUnit() { - return TimeUnit.MILLISECONDS; - } @Override public SoftLock get() { return new SoftLock<>(new TransactionId(new TestXid(0, 0)), "one", new XAValueHolder<>("un", timeSource.getTimeMillis())); } }); when(underlyingStore.get(2L)).thenReturn(new AbstractValueHolder>(-1, -1) { - @Override - protected TimeUnit nativeTimeUnit() { - return TimeUnit.MILLISECONDS; - } @Override public SoftLock get() { return new SoftLock<>(new TransactionId(new TestXid(0, 0)), "two", null); @@ -497,10 +480,6 @@ public void testCommitConflictsEvicts() throws Exception { when(journal.isInDoubt(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(true); when(journal.getInDoubtKeys(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(Arrays.asList(1L, 2L)); when(underlyingStore.get(eq(1L))).thenReturn(new AbstractValueHolder>(-1, -1) { - @Override - protected TimeUnit nativeTimeUnit() { - return TimeUnit.MILLISECONDS; - } @Override public SoftLock get() { return new SoftLock<>(new TransactionId(new TestXid(0, 0)), "old1", new XAValueHolder<>("new1", timeSource @@ -508,10 +487,6 @@ public SoftLock get() { } }); when(underlyingStore.get(eq(2L))).thenReturn(new AbstractValueHolder>(-1, -1) { - @Override - protected TimeUnit nativeTimeUnit() { - return TimeUnit.MILLISECONDS; - } @Override public SoftLock get() { return new SoftLock<>(new TransactionId(new TestXid(0, 0)), "old2", null); @@ -555,10 +530,6 @@ public void testRollbackConflictsEvicts() throws Exception { when(journal.isInDoubt(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(true); when(journal.getInDoubtKeys(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(Arrays.asList(1L, 2L)); when(underlyingStore.get(eq(1L))).thenReturn(new AbstractValueHolder>(-1, -1) { - @Override - protected TimeUnit nativeTimeUnit() { - return TimeUnit.MILLISECONDS; - } @Override public SoftLock get() { return new SoftLock<>(new TransactionId(new TestXid(0, 0)), "old1", new XAValueHolder<>("new1", timeSource @@ -566,10 +537,6 @@ public SoftLock get() { } }); when(underlyingStore.get(eq(2L))).thenReturn(new AbstractValueHolder>(-1, -1) { - @Override - protected TimeUnit nativeTimeUnit() { - return TimeUnit.MILLISECONDS; - } @Override public SoftLock get() { return new SoftLock<>(new TransactionId(new TestXid(0, 0)), "old2", null); diff --git a/transactions/src/test/java/org/ehcache/transactions/xa/internal/XAValueHolderTest.java b/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/internal/XAValueHolderTest.java similarity index 81% rename from transactions/src/test/java/org/ehcache/transactions/xa/internal/XAValueHolderTest.java rename to ehcache-transactions/src/test/java/org/ehcache/transactions/xa/internal/XAValueHolderTest.java index 8cb646f9e4..7419cf27a0 100644 --- a/transactions/src/test/java/org/ehcache/transactions/xa/internal/XAValueHolderTest.java +++ b/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/internal/XAValueHolderTest.java @@ -25,8 +25,8 @@ import java.time.Duration; import java.util.concurrent.TimeUnit; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; /** * XAValueHolderTest @@ -49,9 +49,9 @@ public void testSerialization() throws Exception { XAValueHolder result = (XAValueHolder) new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray())).readObject(); assertThat(result.getId(), is(valueHolder.getId())); - assertThat(result.creationTime(TimeUnit.MILLISECONDS), is(valueHolder.creationTime(TimeUnit.MILLISECONDS))); - assertThat(result.lastAccessTime(TimeUnit.MILLISECONDS), is(valueHolder.lastAccessTime(TimeUnit.MILLISECONDS))); - assertThat(result.expirationTime(TimeUnit.MILLISECONDS), is(valueHolder.expirationTime(TimeUnit.MILLISECONDS))); + assertThat(result.creationTime(), is(valueHolder.creationTime())); + assertThat(result.lastAccessTime(), is(valueHolder.lastAccessTime())); + assertThat(result.expirationTime(), is(valueHolder.expirationTime())); assertThat(result.get(), is(valueHolder.get())); } } diff --git a/transactions/src/test/java/org/ehcache/transactions/xa/internal/journal/AbstractJournalTest.java b/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/internal/journal/AbstractJournalTest.java similarity index 99% rename from transactions/src/test/java/org/ehcache/transactions/xa/internal/journal/AbstractJournalTest.java rename to ehcache-transactions/src/test/java/org/ehcache/transactions/xa/internal/journal/AbstractJournalTest.java index 23db80924b..754f756936 100644 --- a/transactions/src/test/java/org/ehcache/transactions/xa/internal/journal/AbstractJournalTest.java +++ b/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/internal/journal/AbstractJournalTest.java @@ -27,10 +27,10 @@ import java.util.Collection; import java.util.Map; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; /** diff --git a/transactions/src/test/java/org/ehcache/transactions/xa/internal/journal/PersistentJournalTest.java b/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/internal/journal/PersistentJournalTest.java similarity index 97% rename from transactions/src/test/java/org/ehcache/transactions/xa/internal/journal/PersistentJournalTest.java rename to ehcache-transactions/src/test/java/org/ehcache/transactions/xa/internal/journal/PersistentJournalTest.java index acbb274054..0a8685d48f 100644 --- a/transactions/src/test/java/org/ehcache/transactions/xa/internal/journal/PersistentJournalTest.java +++ b/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/internal/journal/PersistentJournalTest.java @@ -25,9 +25,9 @@ import java.util.Arrays; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; /** * @author Ludovic Orban diff --git a/transactions/src/test/java/org/ehcache/transactions/xa/internal/journal/TransientJournalTest.java b/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/internal/journal/TransientJournalTest.java similarity index 100% rename from transactions/src/test/java/org/ehcache/transactions/xa/internal/journal/TransientJournalTest.java rename to ehcache-transactions/src/test/java/org/ehcache/transactions/xa/internal/journal/TransientJournalTest.java diff --git a/transactions/src/test/java/org/ehcache/transactions/xa/internal/xml/TxCacheManagerServiceConfigurationParserTest.java b/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/internal/xml/TxCacheManagerServiceConfigurationParserTest.java similarity index 50% rename from transactions/src/test/java/org/ehcache/transactions/xa/internal/xml/TxCacheManagerServiceConfigurationParserTest.java rename to ehcache-transactions/src/test/java/org/ehcache/transactions/xa/internal/xml/TxCacheManagerServiceConfigurationParserTest.java index 2c0a8291cd..33bcbc3360 100644 --- a/transactions/src/test/java/org/ehcache/transactions/xa/internal/xml/TxCacheManagerServiceConfigurationParserTest.java +++ b/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/internal/xml/TxCacheManagerServiceConfigurationParserTest.java @@ -18,16 +18,48 @@ import org.ehcache.transactions.xa.txmgr.btm.BitronixTransactionManagerLookup; import org.ehcache.transactions.xa.txmgr.provider.LookupTransactionManagerProviderConfiguration; import org.junit.Test; +import org.w3c.dom.Element; import org.w3c.dom.Node; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; +import java.io.StringReader; -import static org.ehcache.xml.ConfigurationParserTestHelper.assertElement; +import static org.ehcache.xml.XmlConfigurationMatchers.isSameConfigurationAs; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.sameInstance; /** * TxCacheManagerServiceConfigurationParserTest */ public class TxCacheManagerServiceConfigurationParserTest { + @Test + public void testParseLookupInsideProperty() throws ParserConfigurationException, IOException, SAXException { + String property = TxCacheManagerServiceConfigurationParserTest.class.getName() + ":lookup"; + String inputString = ""; + + TxCacheManagerServiceConfigurationParser configParser = new TxCacheManagerServiceConfigurationParser(); + + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + documentBuilderFactory.setNamespaceAware(true); + Element node = documentBuilderFactory.newDocumentBuilder() + .parse(new InputSource(new StringReader(inputString))).getDocumentElement(); + + System.setProperty(property, BitronixTransactionManagerLookup.class.getName()); + try { + LookupTransactionManagerProviderConfiguration configuration = + (LookupTransactionManagerProviderConfiguration) configParser.parseServiceCreationConfiguration(node, null); + + assertThat(configuration.getTransactionManagerLookup(), sameInstance(BitronixTransactionManagerLookup.class)); + } finally { + System.clearProperty(property); + } + } + @Test public void testTranslateServiceCreationConfiguration() { TxCacheManagerServiceConfigurationParser configTranslator = new TxCacheManagerServiceConfigurationParser(); @@ -38,7 +70,7 @@ public void testTranslateServiceCreationConfiguration() { String inputString = ""; - assertElement(inputString, retElement); + assertThat(retElement, isSameConfigurationAs(inputString)); } } diff --git a/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/internal/xml/TxCacheServiceConfigurationParserTest.java b/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/internal/xml/TxCacheServiceConfigurationParserTest.java new file mode 100644 index 0000000000..1e9b61c865 --- /dev/null +++ b/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/internal/xml/TxCacheServiceConfigurationParserTest.java @@ -0,0 +1,72 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.transactions.xa.internal.xml; + +import org.ehcache.transactions.xa.configuration.XAStoreConfiguration; +import org.junit.Test; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; +import java.io.StringReader; + +import static org.ehcache.xml.XmlConfigurationMatchers.isSameConfigurationAs; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +/** + * TxCacheServiceConfigurationParserTest + */ +public class TxCacheServiceConfigurationParserTest { + + @Test + public void testParseXaResourceIdInsideProperty() throws ParserConfigurationException, IOException, SAXException { + String property = TxCacheManagerServiceConfigurationParserTest.class.getName() + ":xaResourceId"; + String inputString = ""; + + TxCacheServiceConfigurationParser configParser = new TxCacheServiceConfigurationParser(); + + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + documentBuilderFactory.setNamespaceAware(true); + Element node = documentBuilderFactory.newDocumentBuilder() + .parse(new InputSource(new StringReader(inputString))).getDocumentElement(); + + System.setProperty(property, "Brian"); + try { + XAStoreConfiguration configuration = (XAStoreConfiguration) configParser.parseServiceConfiguration(node, null); + + assertThat(configuration.getUniqueXAResourceId(), is("Brian")); + } finally { + System.clearProperty(property); + } + } + + @Test + public void testTranslateServiceConfiguration() { + TxCacheServiceConfigurationParser configTranslator = new TxCacheServiceConfigurationParser(); + XAStoreConfiguration storeConfiguration = new XAStoreConfiguration("my-unique-resource"); + + Node retElement = configTranslator.unparseServiceConfiguration(storeConfiguration); + String inputString = ""; + assertThat(retElement, isSameConfigurationAs(inputString)); + } + +} diff --git a/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/txmgr/provider/LookupTransactionManagerProviderConfigurationTest.java b/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/txmgr/provider/LookupTransactionManagerProviderConfigurationTest.java new file mode 100644 index 0000000000..8211c1456c --- /dev/null +++ b/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/txmgr/provider/LookupTransactionManagerProviderConfigurationTest.java @@ -0,0 +1,37 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.transactions.xa.txmgr.provider; + +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNot.not; +import static org.hamcrest.core.IsSame.sameInstance; +import static org.mockito.Mockito.mock; + +public class LookupTransactionManagerProviderConfigurationTest { + + @Test + public void testDeriveDetachesCorrectly() { + LookupTransactionManagerProviderConfiguration configuration = new LookupTransactionManagerProviderConfiguration(mock(TransactionManagerLookup.class).getClass()); + LookupTransactionManagerProviderConfiguration derived = configuration.build(configuration.derive()); + + assertThat(derived, is(not(sameInstance(configuration)))); + assertThat(derived.getTransactionManagerLookup(), sameInstance(configuration.getTransactionManagerLookup())); + } +} diff --git a/transactions/src/test/java/org/ehcache/transactions/xa/utils/JavaSerializer.java b/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/utils/JavaSerializer.java similarity index 98% rename from transactions/src/test/java/org/ehcache/transactions/xa/utils/JavaSerializer.java rename to ehcache-transactions/src/test/java/org/ehcache/transactions/xa/utils/JavaSerializer.java index b5d6fdb5ea..a2f80d75f3 100644 --- a/transactions/src/test/java/org/ehcache/transactions/xa/utils/JavaSerializer.java +++ b/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/utils/JavaSerializer.java @@ -16,7 +16,7 @@ package org.ehcache.transactions.xa.utils; import org.ehcache.spi.serialization.SerializerException; -import org.ehcache.impl.internal.util.ByteBufferInputStream; +import org.ehcache.core.util.ByteBufferInputStream; import org.ehcache.spi.serialization.Serializer; import java.io.ByteArrayOutputStream; diff --git a/transactions/src/test/java/org/ehcache/transactions/xa/utils/TestXid.java b/ehcache-transactions/src/test/java/org/ehcache/transactions/xa/utils/TestXid.java similarity index 100% rename from transactions/src/test/java/org/ehcache/transactions/xa/utils/TestXid.java rename to ehcache-transactions/src/test/java/org/ehcache/transactions/xa/utils/TestXid.java diff --git a/transactions/src/test/resources/configs/simple-xa.xml b/ehcache-transactions/src/test/resources/configs/simple-xa.xml similarity index 77% rename from transactions/src/test/resources/configs/simple-xa.xml rename to ehcache-transactions/src/test/resources/configs/simple-xa.xml index 772cdd5475..fcb384f582 100644 --- a/transactions/src/test/resources/configs/simple-xa.xml +++ b/ehcache-transactions/src/test/resources/configs/simple-xa.xml @@ -14,11 +14,8 @@ ~ limitations under the License. --> + xmlns:tx='http://www.ehcache.org/v3/tx'> diff --git a/transactions/src/test/resources/configs/template-xa.xml b/ehcache-transactions/src/test/resources/configs/template-xa.xml similarity index 79% rename from transactions/src/test/resources/configs/template-xa.xml rename to ehcache-transactions/src/test/resources/configs/template-xa.xml index 98dd5912f7..9958139655 100644 --- a/transactions/src/test/resources/configs/template-xa.xml +++ b/ehcache-transactions/src/test/resources/configs/template-xa.xml @@ -15,11 +15,8 @@ --> + xmlns:tx='http://www.ehcache.org/v3/tx'> diff --git a/transactions/src/test/resources/configs/transactional-cache.xml b/ehcache-transactions/src/test/resources/configs/transactional-cache.xml similarity index 78% rename from transactions/src/test/resources/configs/transactional-cache.xml rename to ehcache-transactions/src/test/resources/configs/transactional-cache.xml index ce0bd34d23..65fef5a049 100644 --- a/transactions/src/test/resources/configs/transactional-cache.xml +++ b/ehcache-transactions/src/test/resources/configs/transactional-cache.xml @@ -15,11 +15,8 @@ --> + xmlns:tx='http://www.ehcache.org/v3/tx'> + xmlns:tx='http://www.ehcache.org/v3/tx'> diff --git a/xml/README.adoc b/ehcache-xml/README.adoc similarity index 100% rename from xml/README.adoc rename to ehcache-xml/README.adoc diff --git a/ehcache-xml/build.gradle b/ehcache-xml/build.gradle new file mode 100644 index 0000000000..f60cf4abbc --- /dev/null +++ b/ehcache-xml/build.gradle @@ -0,0 +1,97 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id 'org.ehcache.build.internal-module' + id 'org.unbroken-dome.xjc' + id 'java-test-fixtures' +} + +publishing.publications.withType(MavenPublication) { + pom { + name = 'Ehcache 3 XML Parsing module' + description = 'The module containing all XML parsing logic Ehcache 3' + } +} + +components.java { + withVariantsFromConfiguration(configurations.testFixturesApiElements) { skip() } + withVariantsFromConfiguration(configurations.testFixturesRuntimeElements) { skip() } +} + +sourceSets { + main { + resources.source(xjcSchema) + } +} +tasks.named('sourcesJar') { + filesMatching('*.xsd') { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + } +} + +configurations { + lowerBoundTestRuntimeClasspath { + extendsFrom testRuntimeClasspath + resolutionStrategy.dependencySubstitution { + substitute module('org.glassfish.jaxb:jaxb-runtime') using module('com.sun.xml.bind:jaxb-impl:2.2.8-b01') + } + } +} + +dependencies { + api project(':ehcache-api') + implementation project(':ehcache-core') + implementation project(':ehcache-impl') + + api 'javax.xml.bind:jaxb-api:[2.2,3)' + runtimeOnly 'org.glassfish.jaxb:jaxb-runtime:[2.2,3)' + + testFixturesApi 'org.xmlunit:xmlunit-core:2.6.0', 'org.xmlunit:xmlunit-matchers:2.6.0' + + xjcClasspath 'org.jvnet.jaxb2_commons:jaxb2-fluent-api:3.0' + xjcClasspath 'org.jvnet.jaxb2_commons:jaxb2-basics-annotate:1.1.0' + + lowerBoundTestRuntimeClasspath 'com.sun.activation:javax.activation:1.2.0' +} + +jar { + bnd ( + 'Export-Package': 'org.ehcache.xml, org.ehcache.xml.exceptions, org.ehcache.xml.model', + 'Import-Package': "javax.xml.bind*;version=\"[2.2,3)\", *" + ) +} + +xjc { + extraArgs.add '-Xfluent-api' + extraArgs.add '-Xannotate' + + // ehcache-multi.xsd references ehcache-core.xsd but we cannot control the order they get presented to XJC in. + // Turning off strict checks prevents failing on when seeing the resultant schema parsing issues. + strictCheck = false +} + +tasks.register('lowerBoundTest', Test) { + group = JavaBasePlugin.VERIFICATION_GROUP + //remove the original runtime classpath + classpath -= configurations.testRuntimeClasspath + //add the classpath we want + classpath += configurations.lowerBoundTestRuntimeClasspath +} + +tasks.named('check') { + dependsOn tasks.lowerBoundTest +} diff --git a/ehcache-xml/config/checkstyle-suppressions.xml b/ehcache-xml/config/checkstyle-suppressions.xml new file mode 100644 index 0000000000..c385350074 --- /dev/null +++ b/ehcache-xml/config/checkstyle-suppressions.xml @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/xml/src/main/java/org/ehcache/xml/BaseConfigParser.java b/ehcache-xml/src/main/java/org/ehcache/xml/BaseConfigParser.java similarity index 100% rename from xml/src/main/java/org/ehcache/xml/BaseConfigParser.java rename to ehcache-xml/src/main/java/org/ehcache/xml/BaseConfigParser.java diff --git a/xml/src/main/java/org/ehcache/xml/CacheManagerServiceConfigurationParser.java b/ehcache-xml/src/main/java/org/ehcache/xml/CacheManagerServiceConfigurationParser.java similarity index 87% rename from xml/src/main/java/org/ehcache/xml/CacheManagerServiceConfigurationParser.java rename to ehcache-xml/src/main/java/org/ehcache/xml/CacheManagerServiceConfigurationParser.java index 6151902582..d62e79f5fe 100644 --- a/xml/src/main/java/org/ehcache/xml/CacheManagerServiceConfigurationParser.java +++ b/ehcache-xml/src/main/java/org/ehcache/xml/CacheManagerServiceConfigurationParser.java @@ -33,9 +33,9 @@ public interface CacheManagerServiceConfigurationParser { URI getNamespace(); - ServiceCreationConfiguration parseServiceCreationConfiguration(Element fragment); + ServiceCreationConfiguration parseServiceCreationConfiguration(Element fragment, ClassLoader classLoader); Class getServiceType(); - Element unparseServiceCreationConfiguration(ServiceCreationConfiguration serviceCreationConfiguration); + Element unparseServiceCreationConfiguration(ServiceCreationConfiguration serviceCreationConfiguration); } diff --git a/xml/src/main/java/org/ehcache/xml/CacheResourceConfigurationParser.java b/ehcache-xml/src/main/java/org/ehcache/xml/CacheResourceConfigurationParser.java similarity index 100% rename from xml/src/main/java/org/ehcache/xml/CacheResourceConfigurationParser.java rename to ehcache-xml/src/main/java/org/ehcache/xml/CacheResourceConfigurationParser.java diff --git a/xml/src/main/java/org/ehcache/xml/CacheServiceConfigurationParser.java b/ehcache-xml/src/main/java/org/ehcache/xml/CacheServiceConfigurationParser.java similarity index 84% rename from xml/src/main/java/org/ehcache/xml/CacheServiceConfigurationParser.java rename to ehcache-xml/src/main/java/org/ehcache/xml/CacheServiceConfigurationParser.java index c57c476e80..52d72aaf3a 100644 --- a/xml/src/main/java/org/ehcache/xml/CacheServiceConfigurationParser.java +++ b/ehcache-xml/src/main/java/org/ehcache/xml/CacheServiceConfigurationParser.java @@ -34,9 +34,9 @@ public interface CacheServiceConfigurationParser { URI getNamespace(); - ServiceConfiguration parseServiceConfiguration(Element fragment); + ServiceConfiguration parseServiceConfiguration(Element fragment, ClassLoader classLoader); Class getServiceType(); - Element unparseServiceConfiguration(ServiceConfiguration serviceConfiguration); + Element unparseServiceConfiguration(ServiceConfiguration serviceConfiguration); } diff --git a/xml/src/main/java/org/ehcache/xml/ConfigurationParser.java b/ehcache-xml/src/main/java/org/ehcache/xml/ConfigurationParser.java similarity index 75% rename from xml/src/main/java/org/ehcache/xml/ConfigurationParser.java rename to ehcache-xml/src/main/java/org/ehcache/xml/ConfigurationParser.java index cd27012f19..d0cd669aa1 100644 --- a/xml/src/main/java/org/ehcache/xml/ConfigurationParser.java +++ b/ehcache-xml/src/main/java/org/ehcache/xml/ConfigurationParser.java @@ -18,9 +18,10 @@ import org.ehcache.config.CacheConfiguration; import org.ehcache.config.Configuration; +import org.ehcache.config.FluentConfigurationBuilder; import org.ehcache.config.ResourcePools; import org.ehcache.config.builders.CacheConfigurationBuilder; -import org.ehcache.config.builders.ConfigurationBuilder; +import org.ehcache.core.util.ClassLoading; import org.ehcache.xml.exceptions.XmlConfigurationException; import org.ehcache.xml.model.BaseCacheType; import org.ehcache.xml.model.CacheDefinition; @@ -29,15 +30,12 @@ import org.ehcache.xml.model.CacheTemplateType; import org.ehcache.xml.model.CacheType; import org.ehcache.xml.model.ConfigType; -import org.ehcache.core.internal.util.ClassLoading; import org.ehcache.xml.model.ObjectFactory; 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; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; +import org.xml.sax.SAXNotRecognizedException; import org.xml.sax.SAXParseException; import javax.xml.XMLConstants; @@ -46,11 +44,15 @@ import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; +import javax.xml.bind.helpers.DefaultValidationEventHandler; import javax.xml.namespace.QName; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; @@ -67,32 +69,31 @@ import java.net.URL; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; -import java.security.PrivilegedAction; import java.util.AbstractMap; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Stack; -import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Stream; import java.util.stream.StreamSupport; -import static java.security.AccessController.doPrivileged; +import static java.lang.String.format; import static java.util.Arrays.asList; import static java.util.Spliterators.spliterator; import static java.util.function.Function.identity; +import static java.util.regex.Pattern.quote; import static java.util.stream.Collectors.collectingAndThen; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toMap; import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; import static org.ehcache.config.builders.ConfigurationBuilder.newConfigurationBuilder; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; -import static org.ehcache.core.internal.util.ClassLoading.libraryServiceLoaderFor; +import static org.ehcache.core.util.ClassLoading.servicesOfType; import static org.ehcache.xml.XmlConfiguration.CORE_SCHEMA_URL; import static org.ehcache.xml.XmlConfiguration.getClassForName; @@ -101,12 +102,19 @@ */ public class ConfigurationParser { - private static final Pattern SYSPROP = Pattern.compile("\\$\\{([^}]+)\\}"); - private static final SchemaFactory XSD_SCHEMA_FACTORY = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); - private static Schema newSchema(Source... schemas) throws SAXException { - synchronized (XSD_SCHEMA_FACTORY) { - return XSD_SCHEMA_FACTORY.newSchema(schemas); + public static Schema newSchema(Source... schemas) throws SAXException { + SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + try { + /* + * Our schema is accidentally not XSD 1.1 compatible. Since Saxon incorrectly (imho) defaults to XSD 1.1 for + * `XMLConstants.W3C_XML_SCHEMA_NS_URI` we force it back to 1.0. + */ + schemaFactory.setProperty("http://saxon.sf.net/feature/xsd-version", "1.0"); + } catch (SAXNotRecognizedException e) { + //not saxon } + schemaFactory.setErrorHandler(new FatalErrorHandler()); + return schemaFactory.newSchema(schemas); } private static final TransformerFactory TRANSFORMER_FACTORY = TransformerFactory.newInstance(); @@ -126,23 +134,6 @@ private static Schema newSchema(Source... schemas) throws SAXException { private final ServiceConfigurationParser serviceConfigurationParser; private final ResourceConfigurationParser resourceConfigurationParser; - static String replaceProperties(String originalValue) { - Matcher matcher = SYSPROP.matcher(originalValue); - - StringBuffer sb = new StringBuffer(); - while (matcher.find()) { - final String property = matcher.group(1); - final String value = doPrivileged((PrivilegedAction) () -> System.getProperty(property)); - if (value == null) { - throw new IllegalStateException(String.format("Replacement for ${%s} not found!", property)); - } - matcher.appendReplacement(sb, Matcher.quoteReplacement(value)); - } - matcher.appendTail(sb); - final String resolvedValue = sb.toString(); - return resolvedValue.equals(originalValue) ? null : resolvedValue; - } - @SuppressWarnings("unchecked") private static Stream stream(Iterable iterable) { return StreamSupport.stream(spliterator((Iterator) iterable.iterator(), Long.MAX_VALUE, 0), false); @@ -150,16 +141,16 @@ private static Stream stream(Iterable iterable) { ConfigurationParser() throws IOException, SAXException, JAXBException, ParserConfigurationException { serviceCreationConfigurationParser = ConfigurationParser.>stream( - libraryServiceLoaderFor(CacheManagerServiceConfigurationParser.class)) + servicesOfType(CacheManagerServiceConfigurationParser.class)) .collect(collectingAndThen(toMap(CacheManagerServiceConfigurationParser::getServiceType, identity(), (a, b) -> a.getClass().isInstance(b) ? b : a), ServiceCreationConfigurationParser::new)); serviceConfigurationParser = ConfigurationParser.>stream( - libraryServiceLoaderFor(CacheServiceConfigurationParser.class)) + servicesOfType(CacheServiceConfigurationParser.class)) .collect(collectingAndThen(toMap(CacheServiceConfigurationParser::getServiceType, identity(), (a, b) -> a.getClass().isInstance(b) ? b : a), ServiceConfigurationParser::new)); - resourceConfigurationParser = stream(libraryServiceLoaderFor(CacheResourceConfigurationParser.class)) + resourceConfigurationParser = stream(servicesOfType(CacheResourceConfigurationParser.class)) .flatMap(p -> p.getResourceTypes().stream().map(t -> new AbstractMap.SimpleImmutableEntry<>(t, p))) .collect(collectingAndThen(toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> a.getClass().isInstance(b) ? b : a), m -> new ResourceConfigurationParser(new HashSet<>(m.values())))); @@ -175,36 +166,6 @@ CacheConfigurationBuilder parseServiceConfigurations(CacheConfigura return serviceConfigurationParser.parseConfiguration(cacheDefinition, cacheClassLoader, cacheBuilder); } - private static void substituteSystemProperties(Node node) { - Stack nodeLists = new Stack<>(); - nodeLists.push(node.getChildNodes()); - while (!nodeLists.isEmpty()) { - NodeList nodeList = nodeLists.pop(); - for (int i = 0; i < nodeList.getLength(); ++i) { - Node currentNode = nodeList.item(i); - if (currentNode.hasChildNodes()) { - nodeLists.push(currentNode.getChildNodes()); - } - final NamedNodeMap attributes = currentNode.getAttributes(); - if (attributes != null) { - for (int j = 0; j < attributes.getLength(); ++j) { - final Node attributeNode = attributes.item(j); - final String newValue = replaceProperties(attributeNode.getNodeValue()); - if (newValue != null) { - attributeNode.setNodeValue(newValue); - } - } - } - if (currentNode.getNodeType() == Node.TEXT_NODE) { - final String newValue = replaceProperties(currentNode.getNodeValue()); - if (newValue != null) { - currentNode.setNodeValue(newValue); - } - } - } - } - } - private static Iterable getCacheElements(ConfigType configType) { List cacheCfgs = new ArrayList<>(); final List cacheOrCacheTemplate = configType.getCacheOrCacheTemplate(); @@ -245,8 +206,8 @@ private XmlConfiguration.Template parseTemplate(CacheTemplate template) { return new XmlConfiguration.Template() { @Override public CacheConfigurationBuilder builderFor(ClassLoader classLoader, Class keyType, Class valueType, ResourcePools resources) throws ClassNotFoundException, InstantiationException, IllegalAccessException { - checkTemplateTypeConsistency("key", keyType, template); - checkTemplateTypeConsistency("value", valueType, template); + checkTemplateTypeConsistency("key", classLoader, keyType, template); + checkTemplateTypeConsistency("value", classLoader, valueType, template); if ((resources == null || resources.getResourceTypeSet().isEmpty()) && template.getHeap() == null && template.getResources().isEmpty()) { throw new IllegalStateException("Template defines no resources, and none were provided"); @@ -261,13 +222,12 @@ public CacheConfigurationBuilder builderFor(ClassLoader classLoader }; } - private static void checkTemplateTypeConsistency(String type, Class providedType, CacheTemplate template) throws ClassNotFoundException { - ClassLoader defaultClassLoader = ClassLoading.getDefaultClassLoader(); + private static void checkTemplateTypeConsistency(String type, ClassLoader classLoader, Class providedType, CacheTemplate template) throws ClassNotFoundException { Class templateType; if (type.equals("key")) { - templateType = getClassForName(template.keyType(), defaultClassLoader); + templateType = getClassForName(template.keyType(), classLoader); } else { - templateType = getClassForName(template.valueType(), defaultClassLoader); + templateType = getClassForName(template.valueType(), classLoader); } if(providedType == null || !templateType.isAssignableFrom(providedType)) { @@ -280,8 +240,6 @@ public Document uriToDocument(URI uri) throws IOException, SAXException { } public XmlConfigurationWrapper documentToConfig(Document document, ClassLoader classLoader, Map cacheClassLoaders) throws JAXBException, ClassNotFoundException, InstantiationException, IllegalAccessException { - substituteSystemProperties(document); - Element root = document.getDocumentElement(); QName rootName = new QName(root.getNamespaceURI(), root.getLocalName()); @@ -291,14 +249,15 @@ public XmlConfigurationWrapper documentToConfig(Document document, ClassLoader c Class configTypeClass = ConfigType.class; Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + unmarshaller.setEventHandler(new DefaultValidationEventHandler()); ConfigType jaxbModel = unmarshaller.unmarshal(document, configTypeClass).getValue(); - ConfigurationBuilder managerBuilder = newConfigurationBuilder().withClassLoader(classLoader); + FluentConfigurationBuilder managerBuilder = newConfigurationBuilder().withClassLoader(classLoader); managerBuilder = serviceCreationConfigurationParser.parseServiceCreationConfiguration(jaxbModel, classLoader, managerBuilder); for (CacheDefinition cacheDefinition : getCacheElements(jaxbModel)) { String alias = cacheDefinition.id(); - if(managerBuilder.containsCache(alias)) { + if(managerBuilder.getCache(alias) != null) { throw new XmlConfigurationException("Two caches defined with the same alias: " + alias); } @@ -324,7 +283,7 @@ public XmlConfigurationWrapper documentToConfig(Document document, ClassLoader c } cacheBuilder = parseServiceConfigurations(cacheBuilder, cacheClassLoader, cacheDefinition); - managerBuilder = managerBuilder.addCache(alias, cacheBuilder.build()); + managerBuilder = managerBuilder.withCache(alias, cacheBuilder.build()); } Map templates = getTemplates(jaxbModel); @@ -364,19 +323,41 @@ public Document configToDocument(Configuration configuration) throws JAXBExcepti public static class FatalErrorHandler implements ErrorHandler { + private static final Collection ABSTRACT_TYPE_FAILURES; + static { + ObjectFactory objectFactory = new ObjectFactory(); + List abstractTypes = asList( + objectFactory.createServiceCreationConfiguration(null).getName(), + objectFactory.createServiceConfiguration(null).getName(), + objectFactory.createResource(null).getName()); + + ABSTRACT_TYPE_FAILURES = asList( + //Xerces + abstractTypes.stream().map(element -> quote(format("\"%s\":%s", element.getNamespaceURI(), element.getLocalPart()))) + .collect(collectingAndThen(joining("|", "^\\Qcvc-complex-type.2.4.a\\E.*'\\{.*(?:", ").*\\}'.*$"), Pattern::compile)), + //Saxon + abstractTypes.stream().map(element -> quote(element.getLocalPart())) + .collect(collectingAndThen(joining("|", "^.*\\QThe content model does not allow element\\E.*(?:", ").*"), Pattern::compile))); + } + @Override public void warning(SAXParseException exception) throws SAXException { - throw exception; + fatalError(exception); } @Override public void error(SAXParseException exception) throws SAXException { - throw exception; + fatalError(exception); } @Override public void fatalError(SAXParseException exception) throws SAXException { - throw exception; + if (ABSTRACT_TYPE_FAILURES.stream().anyMatch(pattern -> pattern.matcher(exception.getMessage()).matches())) { + throw new XmlConfigurationException( + "Cannot confirm XML sub-type correctness. You might be missing client side libraries.", exception); + } else { + throw exception; + } } } @@ -400,11 +381,20 @@ public Map getTemplates() { public static String documentToText(Document xml) throws IOException, TransformerException { try (StringWriter writer = new StringWriter()) { - TRANSFORMER_FACTORY.newTransformer().transform(new DOMSource(xml), new StreamResult(writer)); + transformer().transform(new DOMSource(xml), new StreamResult(writer)); return writer.toString(); } } + private static Transformer transformer() throws TransformerConfigurationException { + Transformer transformer = TRANSFORMER_FACTORY.newTransformer(); + transformer.setOutputProperty(OutputKeys.METHOD, "xml"); + transformer.setOutputProperty(OutputKeys.ENCODING, StandardCharsets.UTF_8.name()); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); + return transformer; + } + public static String urlToText(URL url, String encoding) throws IOException { Charset charset = encoding == null ? StandardCharsets.UTF_8 : Charset.forName(encoding); try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), charset))) { @@ -424,16 +414,26 @@ public static DocumentBuilder documentBuilder(Schema schema) throws ParserConfig } public static Schema discoverSchema(Source ... fixedSources) throws SAXException, IOException { - ArrayList schemaSources = new ArrayList<>(asList(fixedSources)); - for (CacheManagerServiceConfigurationParser p : libraryServiceLoaderFor(CacheManagerServiceConfigurationParser.class)) { - schemaSources.add(p.getXmlSchema()); + Map pluginSchemas = new HashMap<>(); + for (CacheManagerServiceConfigurationParser p : servicesOfType(CacheManagerServiceConfigurationParser.class)) { + if (!pluginSchemas.containsKey(p.getNamespace())) { + pluginSchemas.put(p.getNamespace(), p.getXmlSchema()); + } } - for (CacheServiceConfigurationParser p : libraryServiceLoaderFor(CacheServiceConfigurationParser.class)) { - schemaSources.add(p.getXmlSchema()); + for (CacheServiceConfigurationParser p : servicesOfType(CacheServiceConfigurationParser.class)) { + if (!pluginSchemas.containsKey(p.getNamespace())) { + pluginSchemas.put(p.getNamespace(), p.getXmlSchema()); + } } - for (CacheResourceConfigurationParser p : libraryServiceLoaderFor(CacheResourceConfigurationParser.class)) { - schemaSources.add(p.getXmlSchema()); + for (CacheResourceConfigurationParser p : servicesOfType(CacheResourceConfigurationParser.class)) { + if (!pluginSchemas.containsKey(p.getNamespace())) { + pluginSchemas.put(p.getNamespace(), p.getXmlSchema()); + } } + + List schemaSources = new ArrayList<>(asList(fixedSources)); + schemaSources.addAll(pluginSchemas.values()); + return newSchema(schemaSources.toArray(new Source[0])); } } diff --git a/xml/src/main/java/org/ehcache/xml/CoreCacheConfigurationParser.java b/ehcache-xml/src/main/java/org/ehcache/xml/CoreCacheConfigurationParser.java similarity index 91% rename from xml/src/main/java/org/ehcache/xml/CoreCacheConfigurationParser.java rename to ehcache-xml/src/main/java/org/ehcache/xml/CoreCacheConfigurationParser.java index 8c6b23963a..89681e3999 100644 --- a/xml/src/main/java/org/ehcache/xml/CoreCacheConfigurationParser.java +++ b/ehcache-xml/src/main/java/org/ehcache/xml/CoreCacheConfigurationParser.java @@ -27,21 +27,15 @@ import org.ehcache.xml.model.CacheType; import org.ehcache.xml.model.Expiry; import org.ehcache.xml.model.ExpiryType; -import org.ehcache.xml.model.TimeType; -import org.ehcache.xml.model.TimeUnit; +import org.ehcache.xml.model.ObjectFactory; +import org.ehcache.xml.model.TimeTypeWithPropSubst; import java.math.BigInteger; import java.time.Duration; import java.util.stream.Stream; import static java.util.Comparator.comparing; -import static java.util.concurrent.TimeUnit.DAYS; -import static java.util.concurrent.TimeUnit.HOURS; -import static java.util.concurrent.TimeUnit.MICROSECONDS; -import static java.util.concurrent.TimeUnit.MILLISECONDS; -import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.NANOSECONDS; -import static java.util.concurrent.TimeUnit.SECONDS; import static org.ehcache.core.config.ExpiryUtils.jucTimeUnitToTemporalUnit; import static org.ehcache.xml.XmlConfiguration.getClassForName; import static org.ehcache.xml.XmlModel.convertToXmlTimeUnit; @@ -113,12 +107,12 @@ public CacheType unparseConfiguration(CacheConfiguration cacheConfiguratio return cacheType; } - private static TimeType convertToTimeType(Duration duration) { + private static TimeTypeWithPropSubst convertToTimeType(Duration duration) { return Stream.of(java.util.concurrent.TimeUnit.values()) .sorted(comparing(unit -> unit.convert(duration.toNanos(), NANOSECONDS))) .filter(unit -> duration.equals(Duration.of(unit.convert(duration.toNanos(), NANOSECONDS), jucTimeUnitToTemporalUnit(unit)))) .findFirst() - .map(unit -> new TimeType() + .map(unit -> new ObjectFactory().createTimeTypeWithPropSubst() .withValue(BigInteger.valueOf(unit.convert(duration.toNanos(), NANOSECONDS))) .withUnit(convertToXmlTimeUnit(unit)) ).orElseThrow(AssertionError::new); diff --git a/xml/src/main/java/org/ehcache/xml/CoreServiceConfigurationParser.java b/ehcache-xml/src/main/java/org/ehcache/xml/CoreServiceConfigurationParser.java similarity index 100% rename from xml/src/main/java/org/ehcache/xml/CoreServiceConfigurationParser.java rename to ehcache-xml/src/main/java/org/ehcache/xml/CoreServiceConfigurationParser.java diff --git a/xml/src/main/java/org/ehcache/xml/CoreServiceCreationConfigurationParser.java b/ehcache-xml/src/main/java/org/ehcache/xml/CoreServiceCreationConfigurationParser.java similarity index 78% rename from xml/src/main/java/org/ehcache/xml/CoreServiceCreationConfigurationParser.java rename to ehcache-xml/src/main/java/org/ehcache/xml/CoreServiceCreationConfigurationParser.java index 998c3fd01b..a5d951d8b3 100644 --- a/xml/src/main/java/org/ehcache/xml/CoreServiceCreationConfigurationParser.java +++ b/ehcache-xml/src/main/java/org/ehcache/xml/CoreServiceCreationConfigurationParser.java @@ -17,13 +17,12 @@ package org.ehcache.xml; import org.ehcache.config.Configuration; -import org.ehcache.config.builders.ConfigurationBuilder; +import org.ehcache.config.FluentConfigurationBuilder; import org.ehcache.xml.model.ConfigType; public interface CoreServiceCreationConfigurationParser { - ConfigurationBuilder parseServiceCreationConfiguration(ConfigType root, ClassLoader classLoader, ConfigurationBuilder builder) throws ClassNotFoundException; - + FluentConfigurationBuilder parseServiceCreationConfiguration(ConfigType root, ClassLoader classLoader, FluentConfigurationBuilder builder) throws ClassNotFoundException; ConfigType unparseServiceCreationConfiguration(Configuration configuration, ConfigType root); } diff --git a/xml/src/main/java/org/ehcache/xml/DomUtil.java b/ehcache-xml/src/main/java/org/ehcache/xml/DomUtil.java similarity index 89% rename from xml/src/main/java/org/ehcache/xml/DomUtil.java rename to ehcache-xml/src/main/java/org/ehcache/xml/DomUtil.java index 14796f837e..e47f8300d2 100644 --- a/xml/src/main/java/org/ehcache/xml/DomUtil.java +++ b/ehcache-xml/src/main/java/org/ehcache/xml/DomUtil.java @@ -26,26 +26,18 @@ import java.util.Collection; import java.util.List; -import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; -import javax.xml.validation.Schema; -import javax.xml.validation.SchemaFactory; + +import static org.ehcache.xml.ConfigurationParser.newSchema; public class DomUtil { - private static final SchemaFactory XSD_SCHEMA_FACTORY = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); private static final URL CORE_SCHEMA_URL = XmlConfiguration.class.getResource("/ehcache-core.xsd"); - private static Schema newSchema(Source[] schemas) throws SAXException { - synchronized (XSD_SCHEMA_FACTORY) { - return XSD_SCHEMA_FACTORY.newSchema(schemas); - } - } - public static DocumentBuilder createAndGetDocumentBuilder(Collection schemaSources) throws SAXException, ParserConfigurationException { DocumentBuilderFactory factory = createAndGetFactory(schemaSources); DocumentBuilder documentBuilder = factory.newDocumentBuilder(); diff --git a/xml/src/main/java/org/ehcache/xml/JaxbHelper.java b/ehcache-xml/src/main/java/org/ehcache/xml/JaxbHelper.java similarity index 100% rename from xml/src/main/java/org/ehcache/xml/JaxbHelper.java rename to ehcache-xml/src/main/java/org/ehcache/xml/JaxbHelper.java diff --git a/ehcache-xml/src/main/java/org/ehcache/xml/JaxbParsers.java b/ehcache-xml/src/main/java/org/ehcache/xml/JaxbParsers.java new file mode 100644 index 0000000000..3d3e5acfca --- /dev/null +++ b/ehcache-xml/src/main/java/org/ehcache/xml/JaxbParsers.java @@ -0,0 +1,83 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.xml; + +import java.math.BigInteger; +import java.security.PrivilegedAction; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static java.security.AccessController.doPrivileged; + +public class JaxbParsers { + + private static final Pattern SYSPROP = Pattern.compile("\\$\\{(?[^{}]+)}"); + private static final Pattern PADDED_SYSPROP = Pattern.compile("\\s*" + SYSPROP.pattern() + "\\s*"); + + public static String parsePropertyOrString(String s) { + Matcher matcher = PADDED_SYSPROP.matcher(s); + if (matcher.matches()) { + String property = matcher.group("property"); + String value = doPrivileged((PrivilegedAction) () -> System.getProperty(property)); + if (value == null) { + throw new IllegalStateException(String.format("Replacement for ${%s} not found!", property)); + } else { + return value; + } + } else { + return s; + } + } + + public static BigInteger parsePropertyOrInteger(String s) { + return new BigInteger(parsePropertyOrString(s)); + } + + public static BigInteger parsePropertyOrPositiveInteger(String s) { + BigInteger value = parsePropertyOrInteger(s); + if (value.compareTo(BigInteger.ZERO) > 0) { + return value; + } else { + throw new IllegalArgumentException("Value " + value + " is not a positive integer"); + } + } + + public static BigInteger parsePropertyOrNonNegativeInteger(String s) { + BigInteger value = parsePropertyOrInteger(s); + if (value.compareTo(BigInteger.ZERO) >= 0) { + return value; + } else { + throw new IllegalArgumentException("Value " + value + " is not a non-negative integer"); + } + } + + public static String parseStringWithProperties(String s) { + Matcher matcher = SYSPROP.matcher(s); + StringBuffer sb = new StringBuffer(); + while (matcher.find()) { + final String property = matcher.group("property"); + final String value = doPrivileged((PrivilegedAction) () -> System.getProperty(property)); + if (value == null) { + throw new IllegalStateException(String.format("Replacement for ${%s} not found!", property)); + } + matcher.appendReplacement(sb, Matcher.quoteReplacement(value)); + } + matcher.appendTail(sb); + return sb.toString(); + } + +} diff --git a/xml/src/main/java/org/ehcache/xml/ResourceConfigurationParser.java b/ehcache-xml/src/main/java/org/ehcache/xml/ResourceConfigurationParser.java similarity index 80% rename from xml/src/main/java/org/ehcache/xml/ResourceConfigurationParser.java rename to ehcache-xml/src/main/java/org/ehcache/xml/ResourceConfigurationParser.java index f0b1b48d4f..7fb910948c 100644 --- a/xml/src/main/java/org/ehcache/xml/ResourceConfigurationParser.java +++ b/ehcache-xml/src/main/java/org/ehcache/xml/ResourceConfigurationParser.java @@ -23,17 +23,17 @@ import org.ehcache.config.builders.ResourcePoolsBuilder; import org.ehcache.config.units.EntryUnit; import org.ehcache.config.units.MemoryUnit; -import org.ehcache.core.config.SizedResourcePoolImpl; +import org.ehcache.impl.config.SizedResourcePoolImpl; import org.ehcache.xml.exceptions.XmlConfigurationException; import org.ehcache.xml.model.CacheTemplate; import org.ehcache.xml.model.CacheType; import org.ehcache.xml.model.Disk; import org.ehcache.xml.model.Heap; -import org.ehcache.xml.model.MemoryType; +import org.ehcache.xml.model.MemoryTypeWithPropSubst; import org.ehcache.xml.model.ObjectFactory; import org.ehcache.xml.model.Offheap; -import org.ehcache.xml.model.PersistableMemoryType; -import org.ehcache.xml.model.ResourceType; +import org.ehcache.xml.model.PersistableMemoryTypeWithPropSubst; +import org.ehcache.xml.model.ResourceTypeWithPropSubst; import org.ehcache.xml.model.ResourcesType; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -47,33 +47,30 @@ import java.util.Map; import java.util.Set; -import javax.xml.XMLConstants; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; +import javax.xml.bind.helpers.DefaultValidationEventHandler; import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; -import javax.xml.validation.SchemaFactory; +import static org.ehcache.xml.ConfigurationParser.newSchema; import static org.ehcache.xml.XmlConfiguration.CORE_SCHEMA_URL; public class ResourceConfigurationParser { + private static final ObjectFactory OBJECT_FACTORY = new ObjectFactory(); private static final Schema CORE_SCHEMA; static { - SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); try { - CORE_SCHEMA = schemaFactory.newSchema(CORE_SCHEMA_URL); + CORE_SCHEMA = newSchema(new StreamSource(CORE_SCHEMA_URL.toExternalForm())); } catch (Exception e) { throw new AssertionError(e); } } - private static final String CORE_SCHEMA_NS; - static { - ObjectFactory factory = new ObjectFactory(); - CORE_SCHEMA_NS = factory.createResource(factory.createResourceType()).getName().getNamespaceURI(); - } + private static final String CORE_SCHEMA_NS = OBJECT_FACTORY.createResource(OBJECT_FACTORY.createResourceTypeWithPropSubst()).getName().getNamespaceURI(); private final JAXBContext jaxbContext; private final Set extensionParsers; @@ -99,16 +96,16 @@ public ResourcePools parseResourceConfiguration(CacheTemplate cacheTemplate, Res } else { try { Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); - unmarshaller.setSchema(CORE_SCHEMA); + unmarshaller.setEventHandler(new DefaultValidationEventHandler()); Object resource = unmarshaller.unmarshal(element); if (resource instanceof Heap) { resourcePool = parseHeapConfiguration((Heap) resource); } else if (resource instanceof Offheap) { - MemoryType offheapResource = ((Offheap) resource).getValue(); + MemoryTypeWithPropSubst offheapResource = ((Offheap) resource).getValue(); resourcePool = new SizedResourcePoolImpl<>(org.ehcache.config.ResourceType.Core.OFFHEAP, offheapResource.getValue().longValue(), parseMemory(offheapResource), false); } else if (resource instanceof Disk) { - PersistableMemoryType diskResource = ((Disk) resource).getValue(); + PersistableMemoryTypeWithPropSubst diskResource = ((Disk) resource).getValue(); resourcePool = new SizedResourcePoolImpl<>(org.ehcache.config.ResourceType.Core.DISK, diskResource.getValue().longValue(), parseMemory(diskResource), diskResource.isPersistent()); } else { @@ -130,20 +127,20 @@ public ResourcePools parseResourceConfiguration(CacheTemplate cacheTemplate, Res } private ResourcePool parseHeapConfiguration(Heap heap) { - ResourceType heapResource = heap.getValue(); + ResourceTypeWithPropSubst heapResource = heap.getValue(); return new SizedResourcePoolImpl<>(org.ehcache.config.ResourceType.Core.HEAP, heapResource.getValue().longValue(), parseUnit(heapResource), false); } - private static ResourceUnit parseUnit(ResourceType resourceType) { - if (resourceType.getUnit().value().equalsIgnoreCase("entries")) { + private static ResourceUnit parseUnit(ResourceTypeWithPropSubst resourceType) { + if (resourceType.getUnit().equals(org.ehcache.xml.model.ResourceUnit.ENTRIES)) { return EntryUnit.ENTRIES; } else { return org.ehcache.config.units.MemoryUnit.valueOf(resourceType.getUnit().value().toUpperCase()); } } - private static org.ehcache.config.units.MemoryUnit parseMemory(MemoryType memoryType) { + private static org.ehcache.config.units.MemoryUnit parseMemory(MemoryTypeWithPropSubst memoryType) { return MemoryUnit.valueOf(memoryType.getUnit().value().toUpperCase()); } @@ -166,21 +163,12 @@ public CacheType unparseResourceConfiguration(ResourcePools resourcePools, Cache SizedResourcePool pool = (SizedResourcePool) resourcePool; Object resource; if (resourceType == org.ehcache.config.ResourceType.Core.HEAP) { - Heap heap = new Heap(); - ResourceType xmlResourceType = new ResourceType().withValue(BigInteger.valueOf(pool.getSize())).withUnit(unparseUnit(pool.getUnit())); - heap.setValue(xmlResourceType); - resource = heap; + resource = OBJECT_FACTORY.createHeap(OBJECT_FACTORY.createResourceTypeWithPropSubst().withValue(BigInteger.valueOf(pool.getSize())).withUnit(unparseUnit(pool.getUnit()))); } else if (resourceType == org.ehcache.config.ResourceType.Core.OFFHEAP) { - Offheap offheap = new Offheap(); - MemoryType memoryType = new MemoryType().withValue(BigInteger.valueOf(pool.getSize())).withUnit(unparseMemory((MemoryUnit) pool.getUnit())); - offheap.setValue(memoryType); - resource = offheap; + resource = OBJECT_FACTORY.createOffheap(OBJECT_FACTORY.createMemoryTypeWithPropSubst().withValue(BigInteger.valueOf(pool.getSize())).withUnit(unparseMemory((MemoryUnit) pool.getUnit()))); } else if (resourceType == org.ehcache.config.ResourceType.Core.DISK) { - Disk disk = new Disk(); - PersistableMemoryType memoryType = new PersistableMemoryType().withValue(BigInteger.valueOf(pool.getSize())) - .withUnit(unparseMemory((MemoryUnit) pool.getUnit())).withPersistent(pool.isPersistent()); - disk.setValue(memoryType); - resource = disk; + resource = OBJECT_FACTORY.createDisk(OBJECT_FACTORY.createPersistableMemoryTypeWithPropSubst().withValue(BigInteger.valueOf(pool.getSize())) + .withUnit(unparseMemory((MemoryUnit) pool.getUnit())).withPersistent(pool.isPersistent())); } else { throw new AssertionError("Unrecognized core resource type: " + resourceType); } @@ -207,7 +195,7 @@ public CacheType unparseResourceConfiguration(ResourcePools resourcePools, Cache resources.add(element); }); - return cacheType.withResources(new ResourcesType().withResource(resources)); + return cacheType.withResources(OBJECT_FACTORY.createResourcesType().withResource(resources)); } private static org.ehcache.xml.model.ResourceUnit unparseUnit(ResourceUnit resourceUnit) { diff --git a/xml/src/main/java/org/ehcache/xml/ServiceConfigurationParser.java b/ehcache-xml/src/main/java/org/ehcache/xml/ServiceConfigurationParser.java similarity index 97% rename from xml/src/main/java/org/ehcache/xml/ServiceConfigurationParser.java rename to ehcache-xml/src/main/java/org/ehcache/xml/ServiceConfigurationParser.java index 0fe49da50e..5f53219f96 100644 --- a/xml/src/main/java/org/ehcache/xml/ServiceConfigurationParser.java +++ b/ehcache-xml/src/main/java/org/ehcache/xml/ServiceConfigurationParser.java @@ -74,7 +74,7 @@ public CacheConfigurationBuilder parseConfiguration(CacheTemplate c if(xmlConfigurationParser == null) { throw new IllegalArgumentException("Can't find parser for namespace: " + namespace); } - cacheBuilder = cacheBuilder.add(xmlConfigurationParser.parseServiceConfiguration(element)); + cacheBuilder = cacheBuilder.withService(xmlConfigurationParser.parseServiceConfiguration(element, cacheClassLoader)); } return cacheBuilder; diff --git a/xml/src/main/java/org/ehcache/xml/ServiceCreationConfigurationParser.java b/ehcache-xml/src/main/java/org/ehcache/xml/ServiceCreationConfigurationParser.java similarity index 90% rename from xml/src/main/java/org/ehcache/xml/ServiceCreationConfigurationParser.java rename to ehcache-xml/src/main/java/org/ehcache/xml/ServiceCreationConfigurationParser.java index a1a9876409..adc4050533 100644 --- a/xml/src/main/java/org/ehcache/xml/ServiceCreationConfigurationParser.java +++ b/ehcache-xml/src/main/java/org/ehcache/xml/ServiceCreationConfigurationParser.java @@ -17,7 +17,7 @@ package org.ehcache.xml; import org.ehcache.config.Configuration; -import org.ehcache.config.builders.ConfigurationBuilder; +import org.ehcache.config.FluentConfigurationBuilder; import org.ehcache.spi.service.ServiceCreationConfiguration; import org.ehcache.xml.model.ConfigType; import org.ehcache.xml.model.ServiceType; @@ -59,7 +59,7 @@ public ServiceCreationConfigurationParser(Map, CacheManagerServiceConfi this.extensionParsers = extensionParsers; } - ConfigurationBuilder parseServiceCreationConfiguration(ConfigType configRoot, ClassLoader classLoader, ConfigurationBuilder managerBuilder) throws ClassNotFoundException { + FluentConfigurationBuilder parseServiceCreationConfiguration(ConfigType configRoot, ClassLoader classLoader, FluentConfigurationBuilder managerBuilder) throws ClassNotFoundException { for (CoreServiceCreationConfigurationParser parser : CORE_SERVICE_CREATION_CONFIGURATION_PARSERS) { managerBuilder = parser.parseServiceCreationConfiguration(configRoot, classLoader, managerBuilder); } @@ -73,8 +73,8 @@ ConfigurationBuilder parseServiceCreationConfiguration(ConfigType configRoot, Cl if(cacheManagerServiceConfigurationParser == null) { throw new IllegalArgumentException("Can't find parser for namespace: " + namespace); } - ServiceCreationConfiguration serviceConfiguration = cacheManagerServiceConfigurationParser.parseServiceCreationConfiguration(element); - managerBuilder = managerBuilder.addService(serviceConfiguration); + ServiceCreationConfiguration serviceConfiguration = cacheManagerServiceConfigurationParser.parseServiceCreationConfiguration(element, classLoader); + managerBuilder = managerBuilder.withService(serviceConfiguration); } return managerBuilder; diff --git a/xml/src/main/java/org/ehcache/xml/XmlConfiguration.java b/ehcache-xml/src/main/java/org/ehcache/xml/XmlConfiguration.java similarity index 85% rename from xml/src/main/java/org/ehcache/xml/XmlConfiguration.java rename to ehcache-xml/src/main/java/org/ehcache/xml/XmlConfiguration.java index 3a35552477..f44adb566d 100644 --- a/xml/src/main/java/org/ehcache/xml/XmlConfiguration.java +++ b/ehcache-xml/src/main/java/org/ehcache/xml/XmlConfiguration.java @@ -20,20 +20,28 @@ import org.ehcache.config.Configuration; import org.ehcache.config.ResourcePools; import org.ehcache.config.Builder; +import org.ehcache.config.FluentConfigurationBuilder; import org.ehcache.config.builders.CacheConfigurationBuilder; -import org.ehcache.core.internal.util.ClassLoading; +import org.ehcache.core.util.ClassLoading; import org.ehcache.spi.service.ServiceCreationConfiguration; import org.ehcache.xml.exceptions.XmlConfigurationException; import org.w3c.dom.Document; +import java.lang.reflect.Array; import java.net.URL; import java.util.Collection; import java.util.Collections; +import java.util.List; import java.util.Map; +import java.util.function.Predicate; +import static java.lang.Class.forName; +import static java.util.Arrays.asList; import static java.util.Collections.emptyMap; import static java.util.Objects.requireNonNull; +import static org.ehcache.config.builders.ConfigurationBuilder.newConfigurationBuilder; import static org.ehcache.xml.ConfigurationParser.documentToText; +import static org.ehcache.xml.XmlConfiguration.PrettyClassFormat.when; /** * Exposes {@link org.ehcache.config.Configuration} and {@link CacheConfigurationBuilder} expressed @@ -335,17 +343,13 @@ public CacheConfigurationBuilder newCacheConfigurationBuilderFromTe return newCacheConfigurationBuilderFromTemplate(name, keyType, valueType, resourcePoolsBuilder.build()); } - public static Class getClassForName(String name, ClassLoader classLoader) throws ClassNotFoundException { - return Class.forName(name, true, classLoader); - } - @Override public Map> getCacheConfigurations() { return configuration.getCacheConfigurations(); } @Override - public Collection> getServiceCreationConfigurations() { + public Collection> getServiceCreationConfigurations() { return configuration.getServiceCreationConfigurations(); } @@ -354,7 +358,84 @@ public ClassLoader getClassLoader() { return configuration.getClassLoader(); } + @Override + public FluentConfigurationBuilder derive() { + return newConfigurationBuilder(this); + } + public interface Template { CacheConfigurationBuilder builderFor(ClassLoader classLoader, Class keyType, Class valueType, ResourcePools resourcePools) throws ClassNotFoundException, InstantiationException, IllegalAccessException; } + + public static Class getClassForName(String name, ClassLoader classLoader) throws ClassNotFoundException { + String klazz = name.trim(); + return PRETTY_FORMATS.stream().filter(p -> p.applies().test(klazz)).findFirst().map(PrettyClassFormat::lookup).orElseThrow(AssertionError::new).lookup(klazz, classLoader); + } + + private static final List PRETTY_FORMATS = asList( + //Primitive Types + when("boolean"::equals).then((n, l) -> Boolean.TYPE), + when("byte"::equals).then((n, l) -> Byte.TYPE), + when("short"::equals).then((n, l) -> Short.TYPE), + when("int"::equals).then((n, l) -> Integer.TYPE), + when("long"::equals).then((n, l) -> Long.TYPE), + when("char"::equals).then((n, l) -> Character.TYPE), + when("float"::equals).then((n, l) -> Float.TYPE), + when("double"::equals).then((n, l) -> Double.TYPE), + + //Java Language Array Syntax + when(n -> n.endsWith("[]")).then((n, l) -> { + String component = n.split("(\\[\\])+$", 2)[0]; + int dimensions = (n.length() - component.length()) >> 1; + return Array.newInstance(getClassForName(component, l), new int[dimensions]).getClass(); + }), + + //Inner Classes + when(n -> n.contains(".")).then((n, l) -> { + try { + return forName(n, false, l); + } catch (ClassNotFoundException e) { + int innerSeperator = n.lastIndexOf("."); + if (innerSeperator == -1) { + throw e; + } else { + return forName(n.substring(0, innerSeperator) + "$" + n.substring(innerSeperator + 1), false, l); + } + } + }), + + //Everything Else + when(n -> true).then((n, l) -> forName(n, false, l)) + ); + + interface PrettyClassFormat { + + static Builder when(Predicate predicate) { + return lookup -> new PrettyClassFormat() { + @Override + public Predicate applies() { + return predicate; + } + + @Override + public Lookup lookup() { + return lookup; + } + }; + } + + Predicate applies(); + + Lookup lookup(); + + interface Builder { + PrettyClassFormat then(Lookup lookup); + } + } + + private interface Lookup { + + Class lookup(String name, ClassLoader loader) throws ClassNotFoundException; + } + } diff --git a/xml/src/main/java/org/ehcache/xml/XmlModel.java b/ehcache-xml/src/main/java/org/ehcache/xml/XmlModel.java similarity index 100% rename from xml/src/main/java/org/ehcache/xml/XmlModel.java rename to ehcache-xml/src/main/java/org/ehcache/xml/XmlModel.java diff --git a/xml/src/main/java/org/ehcache/xml/exceptions/XmlConfigurationException.java b/ehcache-xml/src/main/java/org/ehcache/xml/exceptions/XmlConfigurationException.java similarity index 100% rename from xml/src/main/java/org/ehcache/xml/exceptions/XmlConfigurationException.java rename to ehcache-xml/src/main/java/org/ehcache/xml/exceptions/XmlConfigurationException.java diff --git a/xml/src/main/java/org/ehcache/xml/model/CacheDefinition.java b/ehcache-xml/src/main/java/org/ehcache/xml/model/CacheDefinition.java similarity index 100% rename from xml/src/main/java/org/ehcache/xml/model/CacheDefinition.java rename to ehcache-xml/src/main/java/org/ehcache/xml/model/CacheDefinition.java diff --git a/xml/src/main/java/org/ehcache/xml/model/CacheSpec.java b/ehcache-xml/src/main/java/org/ehcache/xml/model/CacheSpec.java similarity index 100% rename from xml/src/main/java/org/ehcache/xml/model/CacheSpec.java rename to ehcache-xml/src/main/java/org/ehcache/xml/model/CacheSpec.java diff --git a/xml/src/main/java/org/ehcache/xml/model/CacheTemplate.java b/ehcache-xml/src/main/java/org/ehcache/xml/model/CacheTemplate.java similarity index 100% rename from xml/src/main/java/org/ehcache/xml/model/CacheTemplate.java rename to ehcache-xml/src/main/java/org/ehcache/xml/model/CacheTemplate.java diff --git a/xml/src/main/java/org/ehcache/xml/model/Expiry.java b/ehcache-xml/src/main/java/org/ehcache/xml/model/Expiry.java similarity index 95% rename from xml/src/main/java/org/ehcache/xml/model/Expiry.java rename to ehcache-xml/src/main/java/org/ehcache/xml/model/Expiry.java index 9f7f0011dd..2307708328 100644 --- a/xml/src/main/java/org/ehcache/xml/model/Expiry.java +++ b/ehcache-xml/src/main/java/org/ehcache/xml/model/Expiry.java @@ -45,7 +45,7 @@ public String type() { } public long value() { - final TimeType time; + final TimeTypeWithPropSubst time; if(isTTI()) { time = type.getTti(); } else { @@ -55,7 +55,7 @@ public long value() { } public TemporalUnit unit() { - final TimeType time; + final TimeTypeWithPropSubst time; if(isTTI()) { time = type.getTti(); } else { diff --git a/xml/src/main/java/org/ehcache/xml/model/ListenersConfig.java b/ehcache-xml/src/main/java/org/ehcache/xml/model/ListenersConfig.java similarity index 100% rename from xml/src/main/java/org/ehcache/xml/model/ListenersConfig.java rename to ehcache-xml/src/main/java/org/ehcache/xml/model/ListenersConfig.java diff --git a/xml/src/main/java/org/ehcache/xml/model/SizeOfEngineLimits.java b/ehcache-xml/src/main/java/org/ehcache/xml/model/SizeOfEngineLimits.java similarity index 100% rename from xml/src/main/java/org/ehcache/xml/model/SizeOfEngineLimits.java rename to ehcache-xml/src/main/java/org/ehcache/xml/model/SizeOfEngineLimits.java diff --git a/xml/src/main/java/org/ehcache/xml/multi/XmlMultiConfiguration.java b/ehcache-xml/src/main/java/org/ehcache/xml/multi/XmlMultiConfiguration.java similarity index 89% rename from xml/src/main/java/org/ehcache/xml/multi/XmlMultiConfiguration.java rename to ehcache-xml/src/main/java/org/ehcache/xml/multi/XmlMultiConfiguration.java index ec887ddbc7..c87789b5ce 100644 --- a/xml/src/main/java/org/ehcache/xml/multi/XmlMultiConfiguration.java +++ b/ehcache-xml/src/main/java/org/ehcache/xml/multi/XmlMultiConfiguration.java @@ -23,14 +23,16 @@ import org.ehcache.xml.multi.model.ObjectFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; -import org.w3c.dom.Node; import org.xml.sax.SAXException; import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlSchema; +import javax.xml.bind.helpers.DefaultValidationEventHandler; +import javax.xml.namespace.QName; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; @@ -38,28 +40,14 @@ import javax.xml.validation.Schema; import java.io.IOException; import java.net.URL; -import java.util.AbstractMap.SimpleImmutableEntry; -import java.util.AbstractSet; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; import static java.util.Collections.emptyMap; import static java.util.Collections.emptySet; -import static java.util.Collections.singleton; import static java.util.Collections.unmodifiableSet; -import static java.util.function.Function.identity; -import static java.util.stream.Collectors.collectingAndThen; import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toMap; import static org.ehcache.xml.ConfigurationParser.discoverSchema; @@ -74,6 +62,9 @@ public class XmlMultiConfiguration { private static final URL MULTI_SCHEMA_URL = XmlMultiConfiguration.class.getResource("/ehcache-multi.xsd"); + private static final QName MULTI_SCHEMA_ROOT_NAME = new QName( + Configurations.class.getPackage().getAnnotation(XmlSchema.class).namespace(), + Configurations.class.getAnnotation(XmlRootElement.class).name()); private final Map configurations; @@ -88,21 +79,27 @@ private XmlMultiConfiguration(URL url, BiFunction { - List configOrVariant = c.getConfigOrVariant(); - if (configOrVariant.size() == 1 && configOrVariant.get(0) instanceof Node) { + Element configuration = c.getConfig(); + if (configuration != null) { Document configDoc = domBuilder.newDocument(); - configDoc.appendChild(configDoc.importNode((Element) configOrVariant.get(0), true)); + configDoc.appendChild(configDoc.importNode(configuration, true)); return new SingleConfig(configParser.apply(c.getIdentity(), configDoc)); } else { - return new VariantConfig(configOrVariant.stream() - .map(e -> (JAXBElement) e) - .map(JAXBElement::getValue) + return new VariantConfig(c.getVariant().stream() .collect(toMap(Configurations.Configuration.Variant::getType, v -> { Document configDoc = domBuilder.newDocument(); configDoc.appendChild(configDoc.importNode(v.getConfig(), true)); @@ -123,8 +120,7 @@ private XmlMultiConfiguration(Map configurations) { ObjectFactory objectFactory = new ObjectFactory(); Configurations jaxb = objectFactory.createConfigurations().withConfiguration(configurations.entrySet().stream().map( - entry -> objectFactory.createConfigurationsConfiguration().withIdentity(entry.getKey()).withConfigOrVariant(entry.getValue().unparse(objectFactory)) - ).collect(toList())); + entry -> entry.getValue().unparse(objectFactory, objectFactory.createConfigurationsConfiguration().withIdentity(entry.getKey()))).collect(toList())); JAXBContext jaxbContext = JAXBContext.newInstance(Configurations.class); Marshaller marshaller = jaxbContext.createMarshaller(); @@ -243,7 +239,7 @@ private interface Config { Configuration configuration(String variant); - Collection unparse(ObjectFactory factory); + Configurations.Configuration unparse(ObjectFactory factory, Configurations.Configuration container); Set variants(); } @@ -267,8 +263,8 @@ public Configuration configuration(String variant) { } @Override - public Collection unparse(ObjectFactory factory) { - return singleton(unparseEhcacheConfiguration(config)); + public Configurations.Configuration unparse(ObjectFactory factory, Configurations.Configuration container) { + return container.withConfig(unparseEhcacheConfiguration(config)); } @Override @@ -308,12 +304,12 @@ public Configuration configuration(String variant) { } @Override - public Collection unparse(ObjectFactory factory) { - return configs.entrySet().stream() - .map(v -> factory.createConfigurationsConfigurationVariant().withType(v.getKey()) + public Configurations.Configuration unparse(ObjectFactory factory, Configurations.Configuration container) { + return container.withVariant(configs.entrySet().stream() + .map(v -> factory.createConfigurationsConfigurationVariant() + .withType(v.getKey()) .withConfig(unparseEhcacheConfiguration(v.getValue()))) - .map(factory::createConfigurationsConfigurationVariant) - .collect(toList()); + .collect(toList())); } @Override diff --git a/xml/src/main/java/org/ehcache/xml/provider/CacheEventDispatcherFactoryConfigurationParser.java b/ehcache-xml/src/main/java/org/ehcache/xml/provider/CacheEventDispatcherFactoryConfigurationParser.java similarity index 100% rename from xml/src/main/java/org/ehcache/xml/provider/CacheEventDispatcherFactoryConfigurationParser.java rename to ehcache-xml/src/main/java/org/ehcache/xml/provider/CacheEventDispatcherFactoryConfigurationParser.java diff --git a/xml/src/main/java/org/ehcache/xml/provider/CacheManagerPersistenceConfigurationParser.java b/ehcache-xml/src/main/java/org/ehcache/xml/provider/CacheManagerPersistenceConfigurationParser.java similarity index 100% rename from xml/src/main/java/org/ehcache/xml/provider/CacheManagerPersistenceConfigurationParser.java rename to ehcache-xml/src/main/java/org/ehcache/xml/provider/CacheManagerPersistenceConfigurationParser.java diff --git a/xml/src/main/java/org/ehcache/xml/provider/DefaultCopyProviderConfigurationParser.java b/ehcache-xml/src/main/java/org/ehcache/xml/provider/DefaultCopyProviderConfigurationParser.java similarity index 100% rename from xml/src/main/java/org/ehcache/xml/provider/DefaultCopyProviderConfigurationParser.java rename to ehcache-xml/src/main/java/org/ehcache/xml/provider/DefaultCopyProviderConfigurationParser.java diff --git a/xml/src/main/java/org/ehcache/xml/provider/DefaultSerializationProviderConfigurationParser.java b/ehcache-xml/src/main/java/org/ehcache/xml/provider/DefaultSerializationProviderConfigurationParser.java similarity index 100% rename from xml/src/main/java/org/ehcache/xml/provider/DefaultSerializationProviderConfigurationParser.java rename to ehcache-xml/src/main/java/org/ehcache/xml/provider/DefaultSerializationProviderConfigurationParser.java diff --git a/xml/src/main/java/org/ehcache/xml/provider/DefaultSizeOfEngineProviderConfigurationParser.java b/ehcache-xml/src/main/java/org/ehcache/xml/provider/DefaultSizeOfEngineProviderConfigurationParser.java similarity index 100% rename from xml/src/main/java/org/ehcache/xml/provider/DefaultSizeOfEngineProviderConfigurationParser.java rename to ehcache-xml/src/main/java/org/ehcache/xml/provider/DefaultSizeOfEngineProviderConfigurationParser.java diff --git a/xml/src/main/java/org/ehcache/xml/provider/OffHeapDiskStoreProviderConfigurationParser.java b/ehcache-xml/src/main/java/org/ehcache/xml/provider/OffHeapDiskStoreProviderConfigurationParser.java similarity index 100% rename from xml/src/main/java/org/ehcache/xml/provider/OffHeapDiskStoreProviderConfigurationParser.java rename to ehcache-xml/src/main/java/org/ehcache/xml/provider/OffHeapDiskStoreProviderConfigurationParser.java diff --git a/xml/src/main/java/org/ehcache/xml/provider/PooledExecutionServiceConfigurationParser.java b/ehcache-xml/src/main/java/org/ehcache/xml/provider/PooledExecutionServiceConfigurationParser.java similarity index 100% rename from xml/src/main/java/org/ehcache/xml/provider/PooledExecutionServiceConfigurationParser.java rename to ehcache-xml/src/main/java/org/ehcache/xml/provider/PooledExecutionServiceConfigurationParser.java diff --git a/xml/src/main/java/org/ehcache/xml/provider/SimpleCoreServiceCreationConfigurationParser.java b/ehcache-xml/src/main/java/org/ehcache/xml/provider/SimpleCoreServiceCreationConfigurationParser.java similarity index 88% rename from xml/src/main/java/org/ehcache/xml/provider/SimpleCoreServiceCreationConfigurationParser.java rename to ehcache-xml/src/main/java/org/ehcache/xml/provider/SimpleCoreServiceCreationConfigurationParser.java index 78a5382541..f9e2c01ca7 100644 --- a/xml/src/main/java/org/ehcache/xml/provider/SimpleCoreServiceCreationConfigurationParser.java +++ b/ehcache-xml/src/main/java/org/ehcache/xml/provider/SimpleCoreServiceCreationConfigurationParser.java @@ -17,7 +17,7 @@ package org.ehcache.xml.provider; import org.ehcache.config.Configuration; -import org.ehcache.config.builders.ConfigurationBuilder; +import org.ehcache.config.FluentConfigurationBuilder; import org.ehcache.core.spi.service.ServiceUtils; import org.ehcache.spi.service.ServiceCreationConfiguration; import org.ehcache.xml.CoreServiceCreationConfigurationParser; @@ -27,7 +27,7 @@ import java.util.function.BinaryOperator; import java.util.function.Function; -class SimpleCoreServiceCreationConfigurationParser> implements CoreServiceCreationConfigurationParser { +class SimpleCoreServiceCreationConfigurationParser> implements CoreServiceCreationConfigurationParser { private final Class configType; @@ -68,12 +68,12 @@ class SimpleCoreServiceCreationConfigurationParser parseServiceCreationConfiguration(ConfigType root, ClassLoader classLoader, FluentConfigurationBuilder builder) throws ClassNotFoundException { T config = getter.apply(root); if (config == null) { return builder; } else { - return builder.addService(parser.parse(config, classLoader)); + return builder.withService(parser.parse(config, classLoader)); } } @@ -94,7 +94,7 @@ public ConfigType unparseServiceCreationConfiguration(Configuration configuratio } @FunctionalInterface - interface Parser> { + interface Parser> { U parse(T t, ClassLoader classLoader) throws ClassNotFoundException; } diff --git a/xml/src/main/java/org/ehcache/xml/provider/ThreadPoolServiceCreationConfigurationParser.java b/ehcache-xml/src/main/java/org/ehcache/xml/provider/ThreadPoolServiceCreationConfigurationParser.java similarity index 92% rename from xml/src/main/java/org/ehcache/xml/provider/ThreadPoolServiceCreationConfigurationParser.java rename to ehcache-xml/src/main/java/org/ehcache/xml/provider/ThreadPoolServiceCreationConfigurationParser.java index 6b3d8c6f94..c039471933 100644 --- a/xml/src/main/java/org/ehcache/xml/provider/ThreadPoolServiceCreationConfigurationParser.java +++ b/ehcache-xml/src/main/java/org/ehcache/xml/provider/ThreadPoolServiceCreationConfigurationParser.java @@ -23,7 +23,7 @@ import java.util.function.BiConsumer; import java.util.function.Function; -class ThreadPoolServiceCreationConfigurationParser> extends SimpleCoreServiceCreationConfigurationParser { +class ThreadPoolServiceCreationConfigurationParser> extends SimpleCoreServiceCreationConfigurationParser { ThreadPoolServiceCreationConfigurationParser(Class configType, Function getter, BiConsumer setter, diff --git a/xml/src/main/java/org/ehcache/xml/provider/WriteBehindProviderConfigurationParser.java b/ehcache-xml/src/main/java/org/ehcache/xml/provider/WriteBehindProviderConfigurationParser.java similarity index 100% rename from xml/src/main/java/org/ehcache/xml/provider/WriteBehindProviderConfigurationParser.java rename to ehcache-xml/src/main/java/org/ehcache/xml/provider/WriteBehindProviderConfigurationParser.java diff --git a/xml/src/main/java/org/ehcache/xml/service/DefaultCacheEventDispatcherConfigurationParser.java b/ehcache-xml/src/main/java/org/ehcache/xml/service/DefaultCacheEventDispatcherConfigurationParser.java similarity index 100% rename from xml/src/main/java/org/ehcache/xml/service/DefaultCacheEventDispatcherConfigurationParser.java rename to ehcache-xml/src/main/java/org/ehcache/xml/service/DefaultCacheEventDispatcherConfigurationParser.java diff --git a/xml/src/main/java/org/ehcache/xml/service/DefaultCacheEventListenerConfigurationParser.java b/ehcache-xml/src/main/java/org/ehcache/xml/service/DefaultCacheEventListenerConfigurationParser.java similarity index 82% rename from xml/src/main/java/org/ehcache/xml/service/DefaultCacheEventListenerConfigurationParser.java rename to ehcache-xml/src/main/java/org/ehcache/xml/service/DefaultCacheEventListenerConfigurationParser.java index 72840bbe9a..4cd2eeae5b 100644 --- a/xml/src/main/java/org/ehcache/xml/service/DefaultCacheEventListenerConfigurationParser.java +++ b/ehcache-xml/src/main/java/org/ehcache/xml/service/DefaultCacheEventListenerConfigurationParser.java @@ -24,6 +24,7 @@ import org.ehcache.event.EventOrdering; import org.ehcache.impl.config.event.DefaultCacheEventListenerConfiguration; import org.ehcache.xml.CoreServiceConfigurationParser; +import org.ehcache.xml.exceptions.XmlConfigurationException; import org.ehcache.xml.model.CacheTemplate; import org.ehcache.xml.model.CacheType; import org.ehcache.xml.model.EventFiringType; @@ -33,14 +34,11 @@ import org.ehcache.xml.model.ListenersType; import java.util.Collection; -import java.util.List; import java.util.Set; -import java.util.stream.Collectors; import static java.util.stream.Collectors.toSet; import static org.ehcache.core.spi.service.ServiceUtils.findAmongst; import static org.ehcache.xml.XmlConfiguration.getClassForName; -import static org.ehcache.xml.service.SimpleCoreServiceConfigurationParser.checkNoConcreteInstance; public class DefaultCacheEventListenerConfigurationParser implements CoreServiceConfigurationParser { @@ -58,7 +56,7 @@ public CacheConfigurationBuilder parseServiceConfiguration(CacheTem .newEventListenerConfiguration(cacheEventListenerClass, eventSetToFireOn) .firingMode(EventFiring.valueOf(listener.getEventFiringMode().value())) .eventOrdering(EventOrdering.valueOf(listener.getEventOrderingMode().value())); - cacheBuilder = cacheBuilder.add(listenerBuilder); + cacheBuilder = cacheBuilder.withService(listenerBuilder); } } @@ -79,14 +77,18 @@ public CacheType unparseServiceConfiguration(CacheConfiguration cacheConfi Set listeners = serviceConfigs.stream().map(serviceConfig -> { ListenersType.Listener listener = new ListenersType.Listener(); - checkNoConcreteInstance(serviceConfig); - return listener.withClazz(serviceConfig.getClazz().getName()) - .withEventFiringMode(EventFiringType.fromValue(serviceConfig.firingMode().name())) - .withEventOrderingMode(EventOrderingType.fromValue(serviceConfig.orderingMode().name())) - .withEventsToFireOn(serviceConfig.fireOn() - .stream() - .map(eventType -> EventType.fromValue(eventType.name())) - .collect(toSet())); + if(serviceConfig.getInstance() == null) { + return listener.withClazz(serviceConfig.getClazz().getName()) + .withEventFiringMode(EventFiringType.fromValue(serviceConfig.firingMode().name())) + .withEventOrderingMode(EventOrderingType.fromValue(serviceConfig.orderingMode().name())) + .withEventsToFireOn(serviceConfig.fireOn() + .stream() + .map(eventType -> EventType.fromValue(eventType.name())) + .collect(toSet())); + } else { + throw new XmlConfigurationException("XML translation for instance based initialization for DefaultCacheEventListenerConfiguration is not supported"); + + } }).collect(toSet()); cacheType.withListeners(listenersType.withListener(listeners)); diff --git a/xml/src/main/java/org/ehcache/xml/service/DefaultCacheLoaderWriterConfigurationParser.java b/ehcache-xml/src/main/java/org/ehcache/xml/service/DefaultCacheLoaderWriterConfigurationParser.java similarity index 80% rename from xml/src/main/java/org/ehcache/xml/service/DefaultCacheLoaderWriterConfigurationParser.java rename to ehcache-xml/src/main/java/org/ehcache/xml/service/DefaultCacheLoaderWriterConfigurationParser.java index bfff87a8a9..7144111c85 100644 --- a/xml/src/main/java/org/ehcache/xml/service/DefaultCacheLoaderWriterConfigurationParser.java +++ b/ehcache-xml/src/main/java/org/ehcache/xml/service/DefaultCacheLoaderWriterConfigurationParser.java @@ -18,6 +18,7 @@ import org.ehcache.impl.config.loaderwriter.DefaultCacheLoaderWriterConfiguration; import org.ehcache.spi.loaderwriter.CacheLoaderWriter; +import org.ehcache.xml.exceptions.XmlConfigurationException; import org.ehcache.xml.model.CacheLoaderWriterType; import org.ehcache.xml.model.CacheTemplate; import org.ehcache.xml.model.CacheType; @@ -34,8 +35,11 @@ public DefaultCacheLoaderWriterConfigurationParser() { (config, loader) -> new DefaultCacheLoaderWriterConfiguration((Class>) getClassForName(config, loader)), CacheType::getLoaderWriter, CacheType::setLoaderWriter, config -> { - checkNoConcreteInstance(config); - return new CacheLoaderWriterType().withClazz(config.getClazz().getName()); + if(config.getInstance() == null) { + return new CacheLoaderWriterType().withClazz(config.getClazz().getName()); + } else { + throw new XmlConfigurationException("XML translation for instance based initialization for DefaultCacheLoaderWriterConfiguration is not supported"); + } }); } } diff --git a/xml/src/main/java/org/ehcache/xml/service/DefaultCopierConfigurationParser.java b/ehcache-xml/src/main/java/org/ehcache/xml/service/DefaultCopierConfigurationParser.java similarity index 75% rename from xml/src/main/java/org/ehcache/xml/service/DefaultCopierConfigurationParser.java rename to ehcache-xml/src/main/java/org/ehcache/xml/service/DefaultCopierConfigurationParser.java index ae5bc94b89..8bf44511be 100644 --- a/xml/src/main/java/org/ehcache/xml/service/DefaultCopierConfigurationParser.java +++ b/ehcache-xml/src/main/java/org/ehcache/xml/service/DefaultCopierConfigurationParser.java @@ -20,6 +20,7 @@ import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.impl.config.copy.DefaultCopierConfiguration; import org.ehcache.xml.CoreServiceConfigurationParser; +import org.ehcache.xml.exceptions.XmlConfigurationException; import org.ehcache.xml.model.CacheTemplate; import org.ehcache.xml.model.CacheType; @@ -27,7 +28,6 @@ import static org.ehcache.core.spi.service.ServiceUtils.findAmongst; import static org.ehcache.xml.XmlConfiguration.getClassForName; -import static org.ehcache.xml.service.SimpleCoreServiceConfigurationParser.checkNoConcreteInstance; public class DefaultCopierConfigurationParser implements CoreServiceConfigurationParser { @@ -36,12 +36,12 @@ public CacheConfigurationBuilder parseServiceConfiguration(CacheTem CacheConfigurationBuilder cacheBuilder) throws ClassNotFoundException { if (cacheDefinition.keyCopier() != null) { Class keyCopier = getClassForName(cacheDefinition.keyCopier(), cacheClassLoader); - cacheBuilder = cacheBuilder.add(new DefaultCopierConfiguration(keyCopier, DefaultCopierConfiguration.Type.KEY)); + cacheBuilder = cacheBuilder.withService(new DefaultCopierConfiguration(keyCopier, DefaultCopierConfiguration.Type.KEY)); } if (cacheDefinition.valueCopier() != null) { Class valueCopier = getClassForName(cacheDefinition.valueCopier(), cacheClassLoader); - cacheBuilder = cacheBuilder.add(new DefaultCopierConfiguration(valueCopier, DefaultCopierConfiguration.Type.VALUE)); + cacheBuilder = cacheBuilder.withService(new DefaultCopierConfiguration(valueCopier, DefaultCopierConfiguration.Type.VALUE)); } return cacheBuilder; @@ -53,11 +53,14 @@ public CacheType unparseServiceConfiguration(CacheConfiguration cacheConfi Collection copierConfigs = findAmongst(DefaultCopierConfiguration.class, cacheConfiguration.getServiceConfigurations()); for (DefaultCopierConfiguration copierConfig : copierConfigs) { - checkNoConcreteInstance(copierConfig); - if (copierConfig.getType() == DefaultCopierConfiguration.Type.KEY) { - cacheType.getKeyType().setCopier(copierConfig.getClazz().getName()); + if(copierConfig.getInstance() == null) { + if (copierConfig.getType() == DefaultCopierConfiguration.Type.KEY) { + cacheType.getKeyType().setCopier(copierConfig.getClazz().getName()); + } else { + cacheType.getValueType().setCopier(copierConfig.getClazz().getName()); + } } else { - cacheType.getValueType().setCopier(copierConfig.getClazz().getName()); + throw new XmlConfigurationException("XML translation for instance based initialization for DefaultCopierConfiguration is not supported"); } } return cacheType; diff --git a/xml/src/main/java/org/ehcache/xml/service/DefaultResilienceStrategyConfigurationParser.java b/ehcache-xml/src/main/java/org/ehcache/xml/service/DefaultResilienceStrategyConfigurationParser.java similarity index 81% rename from xml/src/main/java/org/ehcache/xml/service/DefaultResilienceStrategyConfigurationParser.java rename to ehcache-xml/src/main/java/org/ehcache/xml/service/DefaultResilienceStrategyConfigurationParser.java index 6874ed1f91..6aa97a81fc 100644 --- a/xml/src/main/java/org/ehcache/xml/service/DefaultResilienceStrategyConfigurationParser.java +++ b/ehcache-xml/src/main/java/org/ehcache/xml/service/DefaultResilienceStrategyConfigurationParser.java @@ -18,6 +18,7 @@ import org.ehcache.impl.config.resilience.DefaultResilienceStrategyConfiguration; import org.ehcache.spi.resilience.ResilienceStrategy; +import org.ehcache.xml.exceptions.XmlConfigurationException; import org.ehcache.xml.model.CacheTemplate; import org.ehcache.xml.model.CacheType; @@ -32,8 +33,11 @@ public DefaultResilienceStrategyConfigurationParser() { CacheTemplate::resilienceStrategy, (config, loader) -> new DefaultResilienceStrategyConfiguration((Class) getClassForName(config, loader)), CacheType::getResilience, CacheType::setResilience, config -> { - checkNoConcreteInstance(config); - return config.getClazz().getName(); + if(config.getInstance() == null) { + return config.getClazz().getName(); + } else { + throw new XmlConfigurationException("XML translation for instance based initialization for DefaultResilienceStrategyConfiguration is not supported"); + } }); } } diff --git a/xml/src/main/java/org/ehcache/xml/service/DefaultSerializerConfigurationParser.java b/ehcache-xml/src/main/java/org/ehcache/xml/service/DefaultSerializerConfigurationParser.java similarity index 74% rename from xml/src/main/java/org/ehcache/xml/service/DefaultSerializerConfigurationParser.java rename to ehcache-xml/src/main/java/org/ehcache/xml/service/DefaultSerializerConfigurationParser.java index 42537a6c43..1ca1c1bae8 100644 --- a/xml/src/main/java/org/ehcache/xml/service/DefaultSerializerConfigurationParser.java +++ b/ehcache-xml/src/main/java/org/ehcache/xml/service/DefaultSerializerConfigurationParser.java @@ -20,6 +20,7 @@ import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.impl.config.serializer.DefaultSerializerConfiguration; import org.ehcache.xml.CoreServiceConfigurationParser; +import org.ehcache.xml.exceptions.XmlConfigurationException; import org.ehcache.xml.model.CacheTemplate; import org.ehcache.xml.model.CacheType; @@ -27,7 +28,6 @@ import static org.ehcache.core.spi.service.ServiceUtils.findAmongst; import static org.ehcache.xml.XmlConfiguration.getClassForName; -import static org.ehcache.xml.service.SimpleCoreServiceConfigurationParser.checkNoConcreteInstance; public class DefaultSerializerConfigurationParser implements CoreServiceConfigurationParser { @@ -36,12 +36,12 @@ public CacheConfigurationBuilder parseServiceConfiguration(CacheTem CacheConfigurationBuilder cacheBuilder) throws ClassNotFoundException { if (cacheDefinition.keySerializer() != null) { Class keySerializer = getClassForName(cacheDefinition.keySerializer(), cacheClassLoader); - cacheBuilder = cacheBuilder.add(new DefaultSerializerConfiguration(keySerializer, DefaultSerializerConfiguration.Type.KEY)); + cacheBuilder = cacheBuilder.withService(new DefaultSerializerConfiguration(keySerializer, DefaultSerializerConfiguration.Type.KEY)); } if (cacheDefinition.valueSerializer() != null) { Class valueSerializer = getClassForName(cacheDefinition.valueSerializer(), cacheClassLoader); - cacheBuilder = cacheBuilder.add(new DefaultSerializerConfiguration(valueSerializer, DefaultSerializerConfiguration.Type.VALUE)); + cacheBuilder = cacheBuilder.withService(new DefaultSerializerConfiguration(valueSerializer, DefaultSerializerConfiguration.Type.VALUE)); } return cacheBuilder; @@ -52,11 +52,14 @@ public CacheType unparseServiceConfiguration(CacheConfiguration cacheConfi Collection serializerConfigs = findAmongst(DefaultSerializerConfiguration.class, cacheConfiguration.getServiceConfigurations()); for (DefaultSerializerConfiguration serializerConfig : serializerConfigs) { - checkNoConcreteInstance(serializerConfig); - if (serializerConfig.getType() == DefaultSerializerConfiguration.Type.KEY) { - cacheType.getKeyType().setSerializer(serializerConfig.getClazz().getName()); + if(serializerConfig.getInstance() == null) { + if (serializerConfig.getType() == DefaultSerializerConfiguration.Type.KEY) { + cacheType.getKeyType().setSerializer(serializerConfig.getClazz().getName()); + } else { + cacheType.getValueType().setSerializer(serializerConfig.getClazz().getName()); + } } else { - cacheType.getValueType().setSerializer(serializerConfig.getClazz().getName()); + throw new XmlConfigurationException("XML translation for instance based initialization for DefaultSerializerConfiguration is not supported"); } } return cacheType; diff --git a/xml/src/main/java/org/ehcache/xml/service/DefaultSizeOfEngineConfigurationParser.java b/ehcache-xml/src/main/java/org/ehcache/xml/service/DefaultSizeOfEngineConfigurationParser.java similarity index 100% rename from xml/src/main/java/org/ehcache/xml/service/DefaultSizeOfEngineConfigurationParser.java rename to ehcache-xml/src/main/java/org/ehcache/xml/service/DefaultSizeOfEngineConfigurationParser.java diff --git a/xml/src/main/java/org/ehcache/xml/service/DefaultWriteBehindConfigurationParser.java b/ehcache-xml/src/main/java/org/ehcache/xml/service/DefaultWriteBehindConfigurationParser.java similarity index 93% rename from xml/src/main/java/org/ehcache/xml/service/DefaultWriteBehindConfigurationParser.java rename to ehcache-xml/src/main/java/org/ehcache/xml/service/DefaultWriteBehindConfigurationParser.java index 8897fd61cc..a42f0f4da2 100644 --- a/xml/src/main/java/org/ehcache/xml/service/DefaultWriteBehindConfigurationParser.java +++ b/ehcache-xml/src/main/java/org/ehcache/xml/service/DefaultWriteBehindConfigurationParser.java @@ -19,13 +19,11 @@ import org.ehcache.config.builders.WriteBehindConfigurationBuilder; import org.ehcache.config.builders.WriteBehindConfigurationBuilder.BatchedWriteBehindConfigurationBuilder; import org.ehcache.config.builders.WriteBehindConfigurationBuilder.UnBatchedWriteBehindConfigurationBuilder; -import org.ehcache.impl.config.loaderwriter.writebehind.DefaultWriteBehindConfiguration; import org.ehcache.spi.loaderwriter.WriteBehindConfiguration; import org.ehcache.xml.model.BaseCacheType; import org.ehcache.xml.model.CacheLoaderWriterType; import org.ehcache.xml.model.CacheTemplate; -import org.ehcache.xml.model.TimeType; -import org.ehcache.xml.model.TimeUnit; +import org.ehcache.xml.model.TimeTypeWithPropSubst; import java.math.BigInteger; @@ -35,10 +33,11 @@ import static org.ehcache.xml.XmlModel.convertToXmlTimeUnit; public class DefaultWriteBehindConfigurationParser - extends SimpleCoreServiceConfigurationParser { + extends SimpleCoreServiceConfigurationParser> { + @SuppressWarnings("unchecked") public DefaultWriteBehindConfigurationParser() { - super(WriteBehindConfiguration.class, + super((Class>) (Class) WriteBehindConfiguration.class, CacheTemplate::writeBehind, config -> ofNullable(config.getBatching()).map(batching -> { BatchedWriteBehindConfigurationBuilder batchedBuilder = newBatchedWriteBehindConfiguration(batching.getMaxWriteDelay().getValue().longValue(), convertToJUCTimeUnit(batching.getMaxWriteDelay().getUnit()), batching.getBatchSize().intValue()); @@ -63,7 +62,7 @@ public DefaultWriteBehindConfigurationParser() { writeBehind.withBatching(new CacheLoaderWriterType.WriteBehind.Batching() .withBatchSize(BigInteger.valueOf(batchingConfiguration.getBatchSize())) .withCoalesce(batchingConfiguration.isCoalescing()) - .withMaxWriteDelay(new TimeType() + .withMaxWriteDelay(new TimeTypeWithPropSubst() .withValue(BigInteger.valueOf(batchingConfiguration.getMaxDelay())) .withUnit(convertToXmlTimeUnit(batchingConfiguration.getMaxDelayUnit())) ) diff --git a/xml/src/main/java/org/ehcache/xml/service/OffHeapDiskStoreConfigurationParser.java b/ehcache-xml/src/main/java/org/ehcache/xml/service/OffHeapDiskStoreConfigurationParser.java similarity index 100% rename from xml/src/main/java/org/ehcache/xml/service/OffHeapDiskStoreConfigurationParser.java rename to ehcache-xml/src/main/java/org/ehcache/xml/service/OffHeapDiskStoreConfigurationParser.java diff --git a/xml/src/main/java/org/ehcache/xml/service/SimpleCoreServiceConfigurationParser.java b/ehcache-xml/src/main/java/org/ehcache/xml/service/SimpleCoreServiceConfigurationParser.java similarity index 88% rename from xml/src/main/java/org/ehcache/xml/service/SimpleCoreServiceConfigurationParser.java rename to ehcache-xml/src/main/java/org/ehcache/xml/service/SimpleCoreServiceConfigurationParser.java index f2be91e5a6..32bf3be092 100644 --- a/xml/src/main/java/org/ehcache/xml/service/SimpleCoreServiceConfigurationParser.java +++ b/ehcache-xml/src/main/java/org/ehcache/xml/service/SimpleCoreServiceConfigurationParser.java @@ -19,10 +19,8 @@ import org.ehcache.config.CacheConfiguration; import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.core.spi.service.ServiceUtils; -import org.ehcache.impl.internal.classes.ClassInstanceConfiguration; import org.ehcache.spi.service.ServiceConfiguration; import org.ehcache.xml.CoreServiceConfigurationParser; -import org.ehcache.xml.exceptions.XmlConfigurationException; import org.ehcache.xml.model.CacheTemplate; import org.ehcache.xml.model.CacheType; @@ -30,7 +28,7 @@ import java.util.function.BinaryOperator; import java.util.function.Function; -class SimpleCoreServiceConfigurationParser> implements CoreServiceConfigurationParser { +class SimpleCoreServiceConfigurationParser> implements CoreServiceConfigurationParser { private final Function extractor; private final Parser parser; @@ -42,12 +40,6 @@ class SimpleCoreServiceConfigurationParser unparser; private final BinaryOperator merger; - public static void checkNoConcreteInstance(ClassInstanceConfiguration classInstanceConfiguration) { - if(classInstanceConfiguration.getInstance() != null) { - throw new XmlConfigurationException("XML translation for instance based initialization for " + classInstanceConfiguration.getClass().getSimpleName() + - " is not supported"); - } - } SimpleCoreServiceConfigurationParser(Class configType, Function extractor, Function parser, Function getter, BiConsumer setter, Function unparser) { @@ -85,7 +77,7 @@ public final CacheConfigurationBuilder parseServiceConfiguration(Ca if (config != null) { U configuration = parser.parse(config, cacheClassLoader); if (configuration != null) { - return cacheBuilder.add(configuration); + return cacheBuilder.withService(configuration); } } return cacheBuilder; diff --git a/xml/src/main/resources/ehcache-core.xsd b/ehcache-xml/src/main/schema/ehcache-core.xsd similarity index 84% rename from xml/src/main/resources/ehcache-core.xsd rename to ehcache-xml/src/main/schema/ehcache-core.xsd index 721126fef5..987316b871 100644 --- a/xml/src/main/resources/ehcache-core.xsd +++ b/ehcache-xml/src/main/schema/ehcache-core.xsd @@ -134,7 +134,7 @@ - + @@ -171,8 +171,8 @@ - - + + @@ -385,9 +385,9 @@ - + - + @@ -396,8 +396,8 @@ - - + + @@ -435,14 +435,14 @@ - + Entries in the cache should expire if not accessed for the defined time. - + Entries in the cache should expire after the defined time. @@ -460,9 +460,9 @@ - + - + @@ -477,20 +477,32 @@ - - - - The memory unit (see org.ehcache.config.units.MemoryUnit) this value is expressed in. - - - + - + - + + + + + + + + + + + The memory unit (see org.ehcache.config.units.MemoryUnit) this value is expressed in. + + + + + + + + @@ -502,9 +514,9 @@ - + - + @@ -518,7 +530,7 @@ - + @@ -528,7 +540,7 @@ - + @@ -538,7 +550,7 @@ - + @@ -557,8 +569,8 @@ - - + + @@ -617,7 +629,54 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xml/src/main/resources/ehcache-multi.xsd b/ehcache-xml/src/main/schema/ehcache-multi.xsd similarity index 76% rename from xml/src/main/resources/ehcache-multi.xsd rename to ehcache-xml/src/main/schema/ehcache-multi.xsd index 3bc758687d..a0980cf84f 100644 --- a/xml/src/main/resources/ehcache-multi.xsd +++ b/ehcache-xml/src/main/schema/ehcache-multi.xsd @@ -38,7 +38,7 @@ - + @@ -46,22 +46,20 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/xml/src/test/java/com/pany/ehcache/DeprecatedExpiry.java b/ehcache-xml/src/test/java/com/pany/ehcache/DeprecatedExpiry.java similarity index 100% rename from xml/src/test/java/com/pany/ehcache/DeprecatedExpiry.java rename to ehcache-xml/src/test/java/com/pany/ehcache/DeprecatedExpiry.java diff --git a/xml/src/test/java/com/pany/ehcache/MyExpiry.java b/ehcache-xml/src/test/java/com/pany/ehcache/MyExpiry.java similarity index 100% rename from xml/src/test/java/com/pany/ehcache/MyExpiry.java rename to ehcache-xml/src/test/java/com/pany/ehcache/MyExpiry.java diff --git a/xml/src/test/java/com/pany/ehcache/copier/AnotherDescriptionCopier.java b/ehcache-xml/src/test/java/com/pany/ehcache/copier/AnotherDescriptionCopier.java similarity index 100% rename from xml/src/test/java/com/pany/ehcache/copier/AnotherDescriptionCopier.java rename to ehcache-xml/src/test/java/com/pany/ehcache/copier/AnotherDescriptionCopier.java diff --git a/xml/src/test/java/com/pany/ehcache/copier/AnotherPersonCopier.java b/ehcache-xml/src/test/java/com/pany/ehcache/copier/AnotherPersonCopier.java similarity index 100% rename from xml/src/test/java/com/pany/ehcache/copier/AnotherPersonCopier.java rename to ehcache-xml/src/test/java/com/pany/ehcache/copier/AnotherPersonCopier.java diff --git a/xml/src/test/java/com/pany/ehcache/copier/Description.java b/ehcache-xml/src/test/java/com/pany/ehcache/copier/Description.java similarity index 100% rename from xml/src/test/java/com/pany/ehcache/copier/Description.java rename to ehcache-xml/src/test/java/com/pany/ehcache/copier/Description.java diff --git a/xml/src/test/java/com/pany/ehcache/copier/DescriptionCopier.java b/ehcache-xml/src/test/java/com/pany/ehcache/copier/DescriptionCopier.java similarity index 100% rename from xml/src/test/java/com/pany/ehcache/copier/DescriptionCopier.java rename to ehcache-xml/src/test/java/com/pany/ehcache/copier/DescriptionCopier.java diff --git a/xml/src/test/java/com/pany/ehcache/copier/Employee.java b/ehcache-xml/src/test/java/com/pany/ehcache/copier/Employee.java similarity index 100% rename from xml/src/test/java/com/pany/ehcache/copier/Employee.java rename to ehcache-xml/src/test/java/com/pany/ehcache/copier/Employee.java diff --git a/xml/src/test/java/com/pany/ehcache/copier/Person.java b/ehcache-xml/src/test/java/com/pany/ehcache/copier/Person.java similarity index 100% rename from xml/src/test/java/com/pany/ehcache/copier/Person.java rename to ehcache-xml/src/test/java/com/pany/ehcache/copier/Person.java diff --git a/xml/src/test/java/com/pany/ehcache/copier/PersonCopier.java b/ehcache-xml/src/test/java/com/pany/ehcache/copier/PersonCopier.java similarity index 100% rename from xml/src/test/java/com/pany/ehcache/copier/PersonCopier.java rename to ehcache-xml/src/test/java/com/pany/ehcache/copier/PersonCopier.java diff --git a/xml/src/test/java/com/pany/ehcache/integration/TestCacheEventListener.java b/ehcache-xml/src/test/java/com/pany/ehcache/integration/TestCacheEventListener.java similarity index 100% rename from xml/src/test/java/com/pany/ehcache/integration/TestCacheEventListener.java rename to ehcache-xml/src/test/java/com/pany/ehcache/integration/TestCacheEventListener.java diff --git a/xml/src/test/java/com/pany/ehcache/integration/TestCacheLoaderWriter.java b/ehcache-xml/src/test/java/com/pany/ehcache/integration/TestCacheLoaderWriter.java similarity index 100% rename from xml/src/test/java/com/pany/ehcache/integration/TestCacheLoaderWriter.java rename to ehcache-xml/src/test/java/com/pany/ehcache/integration/TestCacheLoaderWriter.java diff --git a/xml/src/test/java/com/pany/ehcache/integration/TestEvictionAdvisor.java b/ehcache-xml/src/test/java/com/pany/ehcache/integration/TestEvictionAdvisor.java similarity index 100% rename from xml/src/test/java/com/pany/ehcache/integration/TestEvictionAdvisor.java rename to ehcache-xml/src/test/java/com/pany/ehcache/integration/TestEvictionAdvisor.java diff --git a/xml/src/test/java/com/pany/ehcache/integration/TestResilienceStrategy.java b/ehcache-xml/src/test/java/com/pany/ehcache/integration/TestResilienceStrategy.java similarity index 100% rename from xml/src/test/java/com/pany/ehcache/integration/TestResilienceStrategy.java rename to ehcache-xml/src/test/java/com/pany/ehcache/integration/TestResilienceStrategy.java diff --git a/xml/src/test/java/com/pany/ehcache/integration/TestSecondCacheEventListener.java b/ehcache-xml/src/test/java/com/pany/ehcache/integration/TestSecondCacheEventListener.java similarity index 100% rename from xml/src/test/java/com/pany/ehcache/integration/TestSecondCacheEventListener.java rename to ehcache-xml/src/test/java/com/pany/ehcache/integration/TestSecondCacheEventListener.java diff --git a/xml/src/test/java/com/pany/ehcache/integration/ThreadRememberingLoaderWriter.java b/ehcache-xml/src/test/java/com/pany/ehcache/integration/ThreadRememberingLoaderWriter.java similarity index 100% rename from xml/src/test/java/com/pany/ehcache/integration/ThreadRememberingLoaderWriter.java rename to ehcache-xml/src/test/java/com/pany/ehcache/integration/ThreadRememberingLoaderWriter.java diff --git a/xml/src/test/java/com/pany/ehcache/serializer/TestSerializer.java b/ehcache-xml/src/test/java/com/pany/ehcache/serializer/TestSerializer.java similarity index 100% rename from xml/src/test/java/com/pany/ehcache/serializer/TestSerializer.java rename to ehcache-xml/src/test/java/com/pany/ehcache/serializer/TestSerializer.java diff --git a/xml/src/test/java/com/pany/ehcache/serializer/TestSerializer2.java b/ehcache-xml/src/test/java/com/pany/ehcache/serializer/TestSerializer2.java similarity index 100% rename from xml/src/test/java/com/pany/ehcache/serializer/TestSerializer2.java rename to ehcache-xml/src/test/java/com/pany/ehcache/serializer/TestSerializer2.java diff --git a/xml/src/test/java/com/pany/ehcache/serializer/TestSerializer3.java b/ehcache-xml/src/test/java/com/pany/ehcache/serializer/TestSerializer3.java similarity index 100% rename from xml/src/test/java/com/pany/ehcache/serializer/TestSerializer3.java rename to ehcache-xml/src/test/java/com/pany/ehcache/serializer/TestSerializer3.java diff --git a/xml/src/test/java/com/pany/ehcache/serializer/TestSerializer4.java b/ehcache-xml/src/test/java/com/pany/ehcache/serializer/TestSerializer4.java similarity index 100% rename from xml/src/test/java/com/pany/ehcache/serializer/TestSerializer4.java rename to ehcache-xml/src/test/java/com/pany/ehcache/serializer/TestSerializer4.java diff --git a/xml/src/test/java/org/ehcache/docs/GettingStarted.java b/ehcache-xml/src/test/java/org/ehcache/docs/GettingStarted.java similarity index 100% rename from xml/src/test/java/org/ehcache/docs/GettingStarted.java rename to ehcache-xml/src/test/java/org/ehcache/docs/GettingStarted.java diff --git a/xml/src/test/java/org/ehcache/docs/MultiGettingStarted.java b/ehcache-xml/src/test/java/org/ehcache/docs/MultiGettingStarted.java similarity index 93% rename from xml/src/test/java/org/ehcache/docs/MultiGettingStarted.java rename to ehcache-xml/src/test/java/org/ehcache/docs/MultiGettingStarted.java index c58a20cb83..397184bf94 100644 --- a/xml/src/test/java/org/ehcache/docs/MultiGettingStarted.java +++ b/ehcache-xml/src/test/java/org/ehcache/docs/MultiGettingStarted.java @@ -23,7 +23,6 @@ import org.hamcrest.collection.IsMapContaining; import org.hamcrest.core.AllOf; import org.hamcrest.core.Is; -import org.junit.Assert; import org.junit.Test; import org.w3c.dom.Document; @@ -32,6 +31,8 @@ import java.util.function.Function; import java.util.stream.Collectors; +import static org.hamcrest.MatcherAssert.assertThat; + public class MultiGettingStarted { @Test @@ -44,7 +45,7 @@ public void multipleConfigurations() { Configuration fooConfiguration = multipleConfiguration.configuration("foo-manager"); // <3> //end::multipleManagers[] - Assert.assertThat(resourceMap(multipleConfiguration.identities().stream().collect( + assertThat(resourceMap(multipleConfiguration.identities().stream().collect( Collectors.toMap(Function.identity(), multipleConfiguration::configuration) )), AllOf.allOf( IsMapContaining.hasEntry(Is.is("foo-manager"), IsMapContaining.hasEntry(Is.is("foo"), IsIterableContainingInAnyOrder.containsInAnyOrder(ResourceType.Core.HEAP, ResourceType.Core.OFFHEAP))), @@ -62,14 +63,14 @@ public void multipleVariants() { Configuration fooConfiguration = variantConfiguration.configuration("foo-manager", "offheap"); // <1> //end::multipleVariants[] - Assert.assertThat(resourceMap(variantConfiguration.identities().stream().collect( + assertThat(resourceMap(variantConfiguration.identities().stream().collect( Collectors.toMap(Function.identity(), i -> variantConfiguration.configuration(i, "offheap")) )), AllOf.allOf( IsMapContaining.hasEntry(Is.is("foo-manager"), IsMapContaining.hasEntry(Is.is("foo"), IsIterableContainingInAnyOrder.containsInAnyOrder(ResourceType.Core.HEAP, ResourceType.Core.OFFHEAP))), IsMapContaining.hasEntry(Is.is("bar-manager"), IsMapContaining.hasEntry(Is.is("bar"), IsIterableContainingInAnyOrder.containsInAnyOrder(ResourceType.Core.HEAP))) )); - Assert.assertThat(resourceMap(variantConfiguration.identities().stream().collect( + assertThat(resourceMap(variantConfiguration.identities().stream().collect( Collectors.toMap(Function.identity(), i -> variantConfiguration.configuration(i, "heap")) )), AllOf.allOf( IsMapContaining.hasEntry(Is.is("foo-manager"), IsMapContaining.hasEntry(Is.is("foo"), IsIterableContainingInAnyOrder.containsInAnyOrder(ResourceType.Core.HEAP))), @@ -93,12 +94,12 @@ public void multipleRetrieval() { .collect(Collectors.toMap(i -> i, i -> variantConfiguration.configuration(i, "offheap"))); // <3> //end::multipleRetrieval[] - Assert.assertThat(resourceMap(allConfigurations), AllOf.allOf( + assertThat(resourceMap(allConfigurations), AllOf.allOf( IsMapContaining.hasEntry(Is.is("foo-manager"), IsMapContaining.hasEntry(Is.is("foo"), IsIterableContainingInAnyOrder.containsInAnyOrder(ResourceType.Core.HEAP, ResourceType.Core.OFFHEAP))), IsMapContaining.hasEntry(Is.is("bar-manager"), IsMapContaining.hasEntry(Is.is("bar"), IsIterableContainingInAnyOrder.containsInAnyOrder(ResourceType.Core.HEAP, ResourceType.Core.OFFHEAP))) )); - Assert.assertThat(resourceMap(offheapConfigurations), AllOf.allOf( + assertThat(resourceMap(offheapConfigurations), AllOf.allOf( IsMapContaining.hasEntry(Is.is("foo-manager"), IsMapContaining.hasEntry(Is.is("foo"), IsIterableContainingInAnyOrder.containsInAnyOrder(ResourceType.Core.HEAP, ResourceType.Core.OFFHEAP))), IsMapContaining.hasEntry(Is.is("bar-manager"), IsMapContaining.hasEntry(Is.is("bar"), IsIterableContainingInAnyOrder.containsInAnyOrder(ResourceType.Core.HEAP))) )); diff --git a/xml/src/test/java/org/ehcache/xml/BarConfiguration.java b/ehcache-xml/src/test/java/org/ehcache/xml/BarConfiguration.java similarity index 97% rename from xml/src/test/java/org/ehcache/xml/BarConfiguration.java rename to ehcache-xml/src/test/java/org/ehcache/xml/BarConfiguration.java index 43de91b5c0..80fedfa68a 100644 --- a/xml/src/test/java/org/ehcache/xml/BarConfiguration.java +++ b/ehcache-xml/src/test/java/org/ehcache/xml/BarConfiguration.java @@ -22,7 +22,7 @@ /** * BarConfiguration */ -public class BarConfiguration implements ServiceCreationConfiguration { +public class BarConfiguration implements ServiceCreationConfiguration { @Override public Class getServiceType() { return Service.class; diff --git a/xml/src/test/java/org/ehcache/xml/BarParser.java b/ehcache-xml/src/test/java/org/ehcache/xml/BarParser.java similarity index 91% rename from xml/src/test/java/org/ehcache/xml/BarParser.java rename to ehcache-xml/src/test/java/org/ehcache/xml/BarParser.java index bbe96740c3..bd38ee2f65 100644 --- a/xml/src/test/java/org/ehcache/xml/BarParser.java +++ b/ehcache-xml/src/test/java/org/ehcache/xml/BarParser.java @@ -50,7 +50,7 @@ public URI getNamespace() { } @Override - public ServiceCreationConfiguration parseServiceCreationConfiguration(Element fragment) { + public ServiceCreationConfiguration parseServiceCreationConfiguration(Element fragment, ClassLoader classLoader) { return new BarConfiguration(); } @@ -60,7 +60,7 @@ public Class getServiceType() { } @Override - public Element unparseServiceCreationConfiguration(ServiceCreationConfiguration serviceCreationConfiguration) { + public Element unparseServiceCreationConfiguration(ServiceCreationConfiguration serviceCreationConfiguration) { try { Document document = DomUtil.createAndGetDocumentBuilder().newDocument(); return document.createElementNS(NAMESPACE.toString(), "bar:bar"); diff --git a/xml/src/test/java/org/ehcache/xml/BazParser.java b/ehcache-xml/src/test/java/org/ehcache/xml/BazParser.java similarity index 100% rename from xml/src/test/java/org/ehcache/xml/BazParser.java rename to ehcache-xml/src/test/java/org/ehcache/xml/BazParser.java diff --git a/xml/src/test/java/org/ehcache/xml/BazResource.java b/ehcache-xml/src/test/java/org/ehcache/xml/BazResource.java similarity index 100% rename from xml/src/test/java/org/ehcache/xml/BazResource.java rename to ehcache-xml/src/test/java/org/ehcache/xml/BazResource.java diff --git a/xml/src/test/java/org/ehcache/xml/CoreCacheConfigurationParserTest.java b/ehcache-xml/src/test/java/org/ehcache/xml/CoreCacheConfigurationParserTest.java similarity index 95% rename from xml/src/test/java/org/ehcache/xml/CoreCacheConfigurationParserTest.java rename to ehcache-xml/src/test/java/org/ehcache/xml/CoreCacheConfigurationParserTest.java index fff46bcfb2..0befa07036 100644 --- a/xml/src/test/java/org/ehcache/xml/CoreCacheConfigurationParserTest.java +++ b/ehcache-xml/src/test/java/org/ehcache/xml/CoreCacheConfigurationParserTest.java @@ -24,7 +24,7 @@ import org.ehcache.expiry.ExpiryPolicy; import org.ehcache.xml.exceptions.XmlConfigurationException; import org.ehcache.xml.model.CacheType; -import org.ehcache.xml.model.TimeType; +import org.ehcache.xml.model.TimeTypeWithPropSubst; import org.ehcache.xml.model.TimeUnit; import org.hamcrest.CoreMatchers; import org.junit.Test; @@ -39,8 +39,8 @@ import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.*; public class CoreCacheConfigurationParserTest { @@ -93,7 +93,7 @@ public void unparseConfigurationCustomExpiry() { public void unparseConfigurationTtiExpiry() { CacheConfiguration cacheConfiguration = buildCacheConfigWith(ExpiryPolicyBuilder.timeToIdleExpiration(Duration.ofMillis(2500))); CacheType cacheType = parser.unparseConfiguration(cacheConfiguration, new CacheType()); - TimeType tti = cacheType.getExpiry().getTti(); + TimeTypeWithPropSubst tti = cacheType.getExpiry().getTti(); assertThat(tti, notNullValue()); assertThat(tti.getValue(), is(BigInteger.valueOf(2500))); assertThat(tti.getUnit(), is(TimeUnit.MILLIS)); @@ -103,7 +103,7 @@ public void unparseConfigurationTtiExpiry() { public void unparseConfigurationTtlExpiry() { CacheConfiguration cacheConfiguration = buildCacheConfigWith(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMinutes(60))); CacheType cacheType = parser.unparseConfiguration(cacheConfiguration, new CacheType()); - TimeType ttl = cacheType.getExpiry().getTtl(); + TimeTypeWithPropSubst ttl = cacheType.getExpiry().getTtl(); assertThat(ttl, notNullValue()); assertThat(ttl.getValue(), is(BigInteger.valueOf(1))); assertThat(ttl.getUnit(), is(TimeUnit.HOURS)); diff --git a/xml/src/test/java/org/ehcache/xml/FancyParser.java b/ehcache-xml/src/test/java/org/ehcache/xml/FancyParser.java similarity index 91% rename from xml/src/test/java/org/ehcache/xml/FancyParser.java rename to ehcache-xml/src/test/java/org/ehcache/xml/FancyParser.java index 2e997fdf20..7f79e3eb55 100644 --- a/xml/src/test/java/org/ehcache/xml/FancyParser.java +++ b/ehcache-xml/src/test/java/org/ehcache/xml/FancyParser.java @@ -42,7 +42,7 @@ public Source getXmlSchema() throws IOException { } @Override - public ServiceConfiguration parseServiceConfiguration(Element fragment) { + public ServiceConfiguration parseServiceConfiguration(Element fragment, ClassLoader classLoader) { return new FooConfiguration(); } @@ -57,7 +57,7 @@ public Class getServiceType() { } @Override - public Element unparseServiceConfiguration(ServiceConfiguration serviceConfiguration) { + public Element unparseServiceConfiguration(ServiceConfiguration serviceConfiguration) { return null; } diff --git a/xml/src/test/java/org/ehcache/xml/FooConfiguration.java b/ehcache-xml/src/test/java/org/ehcache/xml/FooConfiguration.java similarity index 92% rename from xml/src/test/java/org/ehcache/xml/FooConfiguration.java rename to ehcache-xml/src/test/java/org/ehcache/xml/FooConfiguration.java index 00fdd485ca..22cd8611b5 100644 --- a/xml/src/test/java/org/ehcache/xml/FooConfiguration.java +++ b/ehcache-xml/src/test/java/org/ehcache/xml/FooConfiguration.java @@ -23,7 +23,7 @@ * * @author cdennis */ -class FooConfiguration implements ServiceConfiguration { +class FooConfiguration implements ServiceConfiguration { @Override public Class getServiceType() { diff --git a/xml/src/test/java/org/ehcache/xml/FooParser.java b/ehcache-xml/src/test/java/org/ehcache/xml/FooParser.java similarity index 93% rename from xml/src/test/java/org/ehcache/xml/FooParser.java rename to ehcache-xml/src/test/java/org/ehcache/xml/FooParser.java index 21c0184a71..c193618848 100644 --- a/xml/src/test/java/org/ehcache/xml/FooParser.java +++ b/ehcache-xml/src/test/java/org/ehcache/xml/FooParser.java @@ -46,7 +46,7 @@ public Source getXmlSchema() throws IOException { } @Override - public ServiceConfiguration parseServiceConfiguration(Element fragment) { + public ServiceConfiguration parseServiceConfiguration(Element fragment, ClassLoader classLoader) { return new FooConfiguration(); } @@ -56,7 +56,7 @@ public Class getServiceType() { } @Override - public Element unparseServiceConfiguration(ServiceConfiguration serviceConfiguration) { + public Element unparseServiceConfiguration(ServiceConfiguration serviceConfiguration) { try { Document document = DomUtil.createAndGetDocumentBuilder().newDocument(); return document.createElementNS(NAMESPACE.toString(), "foo:foo"); diff --git a/xml/src/test/java/org/ehcache/xml/FromTemplateCacheConfigurationBuilderDefaultTest.java b/ehcache-xml/src/test/java/org/ehcache/xml/FromTemplateCacheConfigurationBuilderDefaultTest.java similarity index 96% rename from xml/src/test/java/org/ehcache/xml/FromTemplateCacheConfigurationBuilderDefaultTest.java rename to ehcache-xml/src/test/java/org/ehcache/xml/FromTemplateCacheConfigurationBuilderDefaultTest.java index b4a4b19917..ee4a219dce 100644 --- a/xml/src/test/java/org/ehcache/xml/FromTemplateCacheConfigurationBuilderDefaultTest.java +++ b/ehcache-xml/src/test/java/org/ehcache/xml/FromTemplateCacheConfigurationBuilderDefaultTest.java @@ -21,8 +21,8 @@ import org.junit.Test; import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; /** * TemplateDefaultTest diff --git a/xml/src/test/java/org/ehcache/xml/IntegrationConfigurationTest.java b/ehcache-xml/src/test/java/org/ehcache/xml/IntegrationConfigurationTest.java similarity index 96% rename from xml/src/test/java/org/ehcache/xml/IntegrationConfigurationTest.java rename to ehcache-xml/src/test/java/org/ehcache/xml/IntegrationConfigurationTest.java index d945d747d3..e77d27dbd2 100644 --- a/xml/src/test/java/org/ehcache/xml/IntegrationConfigurationTest.java +++ b/ehcache-xml/src/test/java/org/ehcache/xml/IntegrationConfigurationTest.java @@ -51,11 +51,11 @@ import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.StringContains.containsString; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; /** @@ -189,7 +189,7 @@ public void testCacheEventListenerThreadPoolName() throws Exception { Configuration configuration = new XmlConfiguration(this.getClass().getResource("/configs/ehcache-cacheEventListener.xml")); CacheConfiguration template1 = configuration.getCacheConfigurations().get("template1"); DefaultCacheEventDispatcherConfiguration eventDispatcherConfig = null; - for (ServiceConfiguration serviceConfiguration : template1.getServiceConfigurations()) { + for (ServiceConfiguration serviceConfiguration : template1.getServiceConfigurations()) { if (serviceConfiguration instanceof DefaultCacheEventDispatcherConfiguration) { eventDispatcherConfig = (DefaultCacheEventDispatcherConfiguration) serviceConfiguration; } @@ -225,8 +225,8 @@ public void testThreadPools() throws Exception { cacheManager.init(); try { Cache cache = cacheManager.createCache("testThreadPools", newCacheConfigurationBuilder(String.class, String.class, heap(10)) - .add(new DefaultCacheLoaderWriterConfiguration(ThreadRememberingLoaderWriter.class)) - .add(newUnBatchedWriteBehindConfiguration().useThreadPool("small")) + .withService(new DefaultCacheLoaderWriterConfiguration(ThreadRememberingLoaderWriter.class)) + .withService(newUnBatchedWriteBehindConfiguration().useThreadPool("small")) .build()); cache.put("foo", "bar"); @@ -246,8 +246,8 @@ public void testThreadPoolsUsingDefaultPool() throws Exception { cacheManager.init(); try { Cache cache = cacheManager.createCache("testThreadPools", newCacheConfigurationBuilder(String.class, String.class, heap(10)) - .add(new DefaultCacheLoaderWriterConfiguration(ThreadRememberingLoaderWriter.class)) - .add(newUnBatchedWriteBehindConfiguration()) + .withService(new DefaultCacheLoaderWriterConfiguration(ThreadRememberingLoaderWriter.class)) + .withService(newUnBatchedWriteBehindConfiguration()) .build()); cache.put("foo", "bar"); diff --git a/ehcache-xml/src/test/java/org/ehcache/xml/JaxbParsersTest.java b/ehcache-xml/src/test/java/org/ehcache/xml/JaxbParsersTest.java new file mode 100644 index 0000000000..a23d1ffe62 --- /dev/null +++ b/ehcache-xml/src/test/java/org/ehcache/xml/JaxbParsersTest.java @@ -0,0 +1,281 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.xml; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; + +import java.math.BigInteger; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertThrows; + +public class JaxbParsersTest { + + private static final String PROPERTY_PREFIX = JaxbParsersTest.class.getName() + ":"; + @Rule public final TestName testName = new TestName(); + + @Test + public void testParsePropertyOrStringFromNullString() { + assertThrows(NullPointerException.class, () -> JaxbParsers.parsePropertyOrString(null)); + } + + @Test + public void testParsePropertyOrStringWithoutProperty() { + assertThat(JaxbParsers.parsePropertyOrString("${foobar"), is("${foobar")); + assertThat(JaxbParsers.parsePropertyOrString("foobar"), is("foobar")); + assertThat(JaxbParsers.parsePropertyOrString("foobar}"), is("foobar}")); + assertThat(JaxbParsers.parsePropertyOrString("$foobar"), is("$foobar")); + } + + @Test + public void testParsePropertyOrStringWithProperty() { + String property = PROPERTY_PREFIX + testName.getMethodName(); + System.setProperty(property, "barfoo"); + try { + assertThat(JaxbParsers.parsePropertyOrString("${" + property + "}"), is("barfoo")); + } finally { + System.clearProperty(property); + } + } + + @Test + public void testParsePropertyOrStringWithMissingProperty() { + String property = PROPERTY_PREFIX + testName.getMethodName(); + assertThrows(IllegalStateException.class, () -> JaxbParsers.parsePropertyOrString("${" + property + "}")); + } + + @Test + public void testParsePropertyOrIntegerFromNullString() { + assertThrows(NullPointerException.class, () -> JaxbParsers.parsePropertyOrInteger(null)); + } + + @Test + public void testParsePropertyOrIntegerValidWithoutProperty() { + assertThat(JaxbParsers.parsePropertyOrInteger("123"), is(BigInteger.valueOf(123))); + assertThat(JaxbParsers.parsePropertyOrInteger("-123"), is(BigInteger.valueOf(-123))); + } + + @Test + public void testParsePropertyOrIntegerInvalidWithoutProperty() { + assertThrows(NumberFormatException.class, () -> JaxbParsers.parsePropertyOrInteger("foobar")); + } + + @Test + public void testParsePropertyOrIntegerValidWithProperty() { + String property = PROPERTY_PREFIX + testName.getMethodName(); + System.setProperty(property, "123"); + try { + assertThat(JaxbParsers.parsePropertyOrInteger("${" + property + "}"), is(BigInteger.valueOf(123))); + } finally { + System.clearProperty(property); + } + System.setProperty(property, "-123"); + try { + assertThat(JaxbParsers.parsePropertyOrInteger("${" + property + "}"), is(BigInteger.valueOf(-123))); + } finally { + System.clearProperty(property); + } + } + + @Test + public void testParsePropertyOrIntegerInvalidWithProperty() { + String property = PROPERTY_PREFIX + testName.getMethodName(); + System.setProperty(property, "barfoo"); + try { + assertThrows(NumberFormatException.class, () -> JaxbParsers.parsePropertyOrInteger("${" + property + "}")); + } finally { + System.clearProperty(property); + } + } + + @Test + public void testParsePropertyOrIntegerWithMissingProperty() { + String property = PROPERTY_PREFIX + testName.getMethodName(); + assertThrows(IllegalStateException.class, () -> JaxbParsers.parsePropertyOrInteger("${" + property + "}")); + } + + + @Test + public void testParsePropertyOrPositiveIntegerFromNullString() { + assertThrows(NullPointerException.class, () -> JaxbParsers.parsePropertyOrPositiveInteger(null)); + } + + @Test + public void testParsePropertyOrPositiveIntegerValidWithoutProperty() { + assertThat(JaxbParsers.parsePropertyOrPositiveInteger("123"), is(BigInteger.valueOf(123))); + } + + @Test + public void testParsePropertyOrPositiveIntegerInvalidWithoutProperty() { + assertThrows(NumberFormatException.class, () -> JaxbParsers.parsePropertyOrPositiveInteger("foobar")); + } + + @Test + public void testParsePropertyOrPositiveIntegerOutOfRangeWithoutProperty() { + assertThrows(IllegalArgumentException.class, () -> JaxbParsers.parsePropertyOrPositiveInteger("0")); + } + + @Test + public void testParsePropertyOrPositiveIntegerValidWithProperty() { + String property = PROPERTY_PREFIX + testName.getMethodName(); + System.setProperty(property, "123"); + try { + assertThat(JaxbParsers.parsePropertyOrPositiveInteger("${" + property + "}"), is(BigInteger.valueOf(123))); + } finally { + System.clearProperty(property); + } + } + + @Test + public void testParsePropertyOrPositiveIntegerInvalidWithProperty() { + String property = PROPERTY_PREFIX + testName.getMethodName(); + System.setProperty(property, "barfoo"); + try { + assertThrows(NumberFormatException.class, () -> JaxbParsers.parsePropertyOrPositiveInteger("${" + property + "}")); + } finally { + System.clearProperty(property); + } + } + + @Test + public void testParsePropertyOrPositiveIntegerOutOfRangeWithProperty() { + String property = PROPERTY_PREFIX + testName.getMethodName(); + System.setProperty(property, "0"); + try { + assertThrows(IllegalArgumentException.class, () -> JaxbParsers.parsePropertyOrPositiveInteger("${" + property + "}")); + } finally { + System.clearProperty(property); + } + } + + @Test + public void testParsePropertyOrPositiveIntegerWithMissingProperty() { + String property = PROPERTY_PREFIX + testName.getMethodName(); + assertThrows(IllegalStateException.class, () -> JaxbParsers.parsePropertyOrPositiveInteger("${" + property + "}")); + } + + @Test + public void parsePropertyOrNonNegativeInteger() { + } + + @Test + public void testParsePropertyOrNonNegativeIntegerFromNullString() { + assertThrows(NullPointerException.class, () -> JaxbParsers.parsePropertyOrNonNegativeInteger(null)); + } + + @Test + public void testParsePropertyOrNonNegativeIntegerValidWithoutProperty() { + assertThat(JaxbParsers.parsePropertyOrNonNegativeInteger("123"), is(BigInteger.valueOf(123))); + } + + @Test + public void testParsePropertyOrNonNegativeIntegerInvalidWithoutProperty() { + assertThrows(NumberFormatException.class, () -> JaxbParsers.parsePropertyOrNonNegativeInteger("foobar")); + } + + @Test + public void testParsePropertyOrNonNegativeIntegerOutOfRangeWithoutProperty() { + assertThrows(IllegalArgumentException.class, () -> JaxbParsers.parsePropertyOrNonNegativeInteger("-1")); + } + + @Test + public void testParsePropertyOrNonNegativeIntegerValidWithProperty() { + String property = PROPERTY_PREFIX + testName.getMethodName(); + System.setProperty(property, "123"); + try { + assertThat(JaxbParsers.parsePropertyOrNonNegativeInteger("${" + property + "}"), is(BigInteger.valueOf(123))); + } finally { + System.clearProperty(property); + } + } + + @Test + public void testParsePropertyOrNonNegativeIntegerInvalidWithProperty() { + String property = PROPERTY_PREFIX + testName.getMethodName(); + System.setProperty(property, "barfoo"); + try { + assertThrows(NumberFormatException.class, () -> JaxbParsers.parsePropertyOrNonNegativeInteger("${" + property + "}")); + } finally { + System.clearProperty(property); + } + } + + @Test + public void testParsePropertyOrNonNegativeIntegerOutOfRangeWithProperty() { + String property = PROPERTY_PREFIX + testName.getMethodName(); + System.setProperty(property, "-1"); + try { + assertThrows(IllegalArgumentException.class, () -> JaxbParsers.parsePropertyOrNonNegativeInteger("${" + property + "}")); + } finally { + System.clearProperty(property); + } + } + + @Test + public void testParsePropertyOrNonNegativeIntegerWithMissingProperty() { + String property = PROPERTY_PREFIX + testName.getMethodName(); + assertThrows(IllegalStateException.class, () -> JaxbParsers.parsePropertyOrNonNegativeInteger("${" + property + "}")); + } + + @Test + public void parseStringWithProperties() { + } + + @Test + public void testParseStringWithPropertiesFromNullString() { + assertThrows(NullPointerException.class, () -> JaxbParsers.parseStringWithProperties(null)); + } + + @Test + public void testParseStringWithPropertiesWithoutProperties() { + assertThat(JaxbParsers.parseStringWithProperties("foo${bar"), is("foo${bar")); + assertThat(JaxbParsers.parseStringWithProperties("foobar"), is("foobar")); + assertThat(JaxbParsers.parseStringWithProperties("foo}bar"), is("foo}bar")); + assertThat(JaxbParsers.parseStringWithProperties("foo$bar"), is("foo$bar")); + } + + @Test + public void testParseStringWithPropertiesWithProperties() { + String foo = PROPERTY_PREFIX + testName.getMethodName() + ":foo"; + String bar = PROPERTY_PREFIX + testName.getMethodName() + ":bar"; + System.setProperty(foo, "foo"); + System.setProperty(bar, "bar"); + try { + assertThat(JaxbParsers.parseStringWithProperties("start:${" + foo + "}:middle:${" + bar + "}:end"), is("start:foo:middle:bar:end")); + } finally { + System.clearProperty(foo); + System.clearProperty(bar); + } + } + + @Test + public void testParseStringWithPropertiesWithMissingProperty() { + String foo = PROPERTY_PREFIX + testName.getMethodName() + ":foo"; + String bar = PROPERTY_PREFIX + testName.getMethodName() + ":bar"; + assertThat(System.getProperty(bar), is(nullValue())); + System.setProperty(foo, "foo"); + try { + assertThrows(IllegalStateException.class, () -> JaxbParsers.parseStringWithProperties("start:${" + foo + "}:middle:${" + bar + "}:end")); + } finally { + System.clearProperty(foo); + } + } +} diff --git a/xml/src/test/java/org/ehcache/xml/NiResilience.java b/ehcache-xml/src/test/java/org/ehcache/xml/NiResilience.java similarity index 100% rename from xml/src/test/java/org/ehcache/xml/NiResilience.java rename to ehcache-xml/src/test/java/org/ehcache/xml/NiResilience.java diff --git a/ehcache-xml/src/test/java/org/ehcache/xml/PropertySubstitutionTest.java b/ehcache-xml/src/test/java/org/ehcache/xml/PropertySubstitutionTest.java new file mode 100644 index 0000000000..6993783696 --- /dev/null +++ b/ehcache-xml/src/test/java/org/ehcache/xml/PropertySubstitutionTest.java @@ -0,0 +1,119 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.xml; + +import org.ehcache.config.CacheConfiguration; +import org.ehcache.config.ResourceType; +import org.ehcache.impl.config.executor.PooledExecutionServiceConfiguration; +import org.ehcache.impl.config.loaderwriter.writebehind.DefaultWriteBehindConfiguration; +import org.ehcache.impl.config.persistence.DefaultPersistenceConfiguration; +import org.ehcache.impl.config.store.disk.OffHeapDiskStoreConfiguration; +import org.ehcache.spi.service.ServiceCreationConfiguration; +import org.ehcache.xml.exceptions.XmlConfigurationException; +import org.hamcrest.CustomMatcher; +import org.hamcrest.CustomTypeSafeMatcher; +import org.junit.Test; + +import javax.xml.bind.UnmarshalException; +import java.net.URL; +import java.time.Duration; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import static org.ehcache.core.spi.service.ServiceUtils.findSingletonAmongst; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.either; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertThrows; +import static org.terracotta.utilities.test.matchers.Matchers.causedBy; + +public class PropertySubstitutionTest { + + @Test + public void testMissingProperties() { + final URL resource = PropertySubstitutionTest.class.getResource("/configs/ehcache-system-props.xml"); + XmlConfigurationException failure = assertThrows(XmlConfigurationException.class, () -> new XmlConfiguration(resource)); + assertThat(failure, causedBy(instanceOf(UnmarshalException.class))); + } + + @Test + public void testSubstitutions() { + Map neededProperties = new HashMap<>(); + neededProperties.put("ehcache.persistence.directory", "foobar"); + neededProperties.put("ehcache.thread-pools.min-size", "0"); + neededProperties.put("ehcache.thread-pools.max-size", "4"); + neededProperties.put("ehcache.expiry.ttl", "10"); + neededProperties.put("ehcache.expiry.tti", "20"); + neededProperties.put("ehcache.loader-writer.write-behind.size", "1000"); + neededProperties.put("ehcache.loader-writer.write-behind.concurrency", "4"); + neededProperties.put("ehcache.loader-writer.write-behind.batching.batch-size", "100"); + neededProperties.put("ehcache.loader-writer.write-behind.batching.max-write-delay", "10"); + neededProperties.put("ehcache.disk-store-settings.writer-concurrency", "8"); + neededProperties.put("ehcache.disk-store-settings.disk-segments", "16"); + neededProperties.put("ehcache.resources.heap", "1024"); + neededProperties.put("ehcache.resources.offheap", "2048"); + neededProperties.put("ehcache.resources.disk", "4096"); + + System.getProperties().putAll(neededProperties); + try { + final URL resource = PropertySubstitutionTest.class.getResource("/configs/ehcache-system-props.xml"); + XmlConfiguration xmlConfig = new XmlConfiguration(new XmlConfiguration(resource)); + + Collection> serviceCreationConfigurations = xmlConfig.getServiceCreationConfigurations(); + + assertThat(findSingletonAmongst(DefaultPersistenceConfiguration.class, serviceCreationConfigurations).getRootDirectory().getAbsolutePath(), either( + is("/dir/path/foobar/tail")).or(new CustomTypeSafeMatcher("matches pattern [A-Z]:\\dir\\path\\foobar\\tail") { + @Override + protected boolean matchesSafely(String item) { + return item.matches("[A-Z]:\\\\dir\\\\path\\\\foobar\\\\tail"); + } + })); + PooledExecutionServiceConfiguration.PoolConfiguration poolConfiguration = findSingletonAmongst(PooledExecutionServiceConfiguration.class, serviceCreationConfigurations).getPoolConfigurations().get("theone"); + assertThat(poolConfiguration.minSize(), is(0)); + assertThat(poolConfiguration.maxSize(), is(4)); + + CacheConfiguration testCacheConfig = xmlConfig.getCacheConfigurations().get("test"); + assertThat(testCacheConfig.getExpiryPolicy().getExpiryForCreation(null, null), is(Duration.ofHours(10))); + assertThat(testCacheConfig.getExpiryPolicy().getExpiryForAccess(null, null), is(nullValue())); + assertThat(testCacheConfig.getExpiryPolicy().getExpiryForUpdate(null, null, null), is(Duration.ofHours(10))); + + DefaultWriteBehindConfiguration writeBehindConfiguration = findSingletonAmongst(DefaultWriteBehindConfiguration.class, testCacheConfig.getServiceConfigurations()); + assertThat(writeBehindConfiguration.getConcurrency(), is(4)); + assertThat(writeBehindConfiguration.getMaxQueueSize(), is(1000)); + assertThat(writeBehindConfiguration.getBatchingConfiguration().getBatchSize(), is(100)); + assertThat(writeBehindConfiguration.getBatchingConfiguration().getMaxDelay(), is(10L)); + + OffHeapDiskStoreConfiguration diskStoreConfiguration = findSingletonAmongst(OffHeapDiskStoreConfiguration.class, testCacheConfig.getServiceConfigurations()); + assertThat(diskStoreConfiguration.getDiskSegments(), is(16)); + assertThat(diskStoreConfiguration.getWriterConcurrency(), is(8)); + + assertThat(testCacheConfig.getResourcePools().getPoolForResource(ResourceType.Core.HEAP).getSize(), is(1024L)); + assertThat(testCacheConfig.getResourcePools().getPoolForResource(ResourceType.Core.OFFHEAP).getSize(), is(2048L)); + assertThat(testCacheConfig.getResourcePools().getPoolForResource(ResourceType.Core.DISK).getSize(), is(4096L)); + + CacheConfiguration anotherTestCacheConfig = xmlConfig.getCacheConfigurations().get("another-test"); + assertThat(anotherTestCacheConfig.getExpiryPolicy().getExpiryForCreation(null, null), is(Duration.ofMillis(20))); + assertThat(anotherTestCacheConfig.getExpiryPolicy().getExpiryForAccess(null, null), is(Duration.ofMillis(20))); + assertThat(anotherTestCacheConfig.getExpiryPolicy().getExpiryForUpdate(null, null, null), is(Duration.ofMillis(20))); + } finally { + neededProperties.keySet().forEach(System::clearProperty); + } + } +} diff --git a/xml/src/test/java/org/ehcache/xml/ShrubberyResilience.java b/ehcache-xml/src/test/java/org/ehcache/xml/ShrubberyResilience.java similarity index 100% rename from xml/src/test/java/org/ehcache/xml/ShrubberyResilience.java rename to ehcache-xml/src/test/java/org/ehcache/xml/ShrubberyResilience.java diff --git a/xml/src/test/java/org/ehcache/xml/XmlConfigurationTest.java b/ehcache-xml/src/test/java/org/ehcache/xml/XmlConfigurationTest.java similarity index 81% rename from xml/src/test/java/org/ehcache/xml/XmlConfigurationTest.java rename to ehcache-xml/src/test/java/org/ehcache/xml/XmlConfigurationTest.java index 5a64851b52..4b50e7a2ff 100644 --- a/xml/src/test/java/org/ehcache/xml/XmlConfigurationTest.java +++ b/ehcache-xml/src/test/java/org/ehcache/xml/XmlConfigurationTest.java @@ -23,7 +23,7 @@ import org.ehcache.config.builders.ExpiryPolicyBuilder; import org.ehcache.config.units.EntryUnit; import org.ehcache.config.units.MemoryUnit; -import org.ehcache.core.internal.util.ClassLoading; +import org.ehcache.core.util.ClassLoading; import org.ehcache.expiry.ExpiryPolicy; import org.ehcache.impl.config.copy.DefaultCopierConfiguration; import org.ehcache.impl.config.copy.DefaultCopyProviderConfiguration; @@ -47,17 +47,13 @@ import org.hamcrest.CoreMatchers; import org.hamcrest.Matchers; import org.hamcrest.core.IsCollectionContaining; +import org.hamcrest.core.IsEqual; import org.hamcrest.core.IsNull; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.xml.sax.SAXParseException; -import org.xmlunit.builder.Input; -import org.xmlunit.diff.DefaultNodeMatcher; -import org.xmlunit.diff.ElementSelectors; import com.pany.ehcache.copier.AnotherPersonCopier; import com.pany.ehcache.copier.Description; @@ -93,10 +89,16 @@ import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; import static org.ehcache.core.spi.service.ServiceUtils.findSingletonAmongst; +import static org.ehcache.core.util.ClassLoading.getDefaultClassLoader; +import static org.ehcache.xml.XmlConfiguration.getClassForName; +import static org.ehcache.xml.XmlConfigurationMatchers.isSameConfigurationAs; +import static org.hamcrest.CoreMatchers.either; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.hasProperty; import static org.hamcrest.Matchers.isIn; import static org.hamcrest.collection.IsCollectionWithSize.hasSize; import static org.hamcrest.collection.IsIterableContainingInOrder.contains; @@ -109,10 +111,9 @@ import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; -import static org.xmlunit.matchers.CompareMatcher.isSimilarTo; /** * @@ -120,9 +121,6 @@ */ public class XmlConfigurationTest { - @Rule - public ExpectedException thrown = ExpectedException.none(); - @Test public void testDefaultTypesConfig() throws Exception { URL resource = XmlConfigurationTest.class.getResource("/configs/defaultTypes-cache.xml"); @@ -258,10 +256,11 @@ public void testInvalidCoreConfiguration() throws Exception { try { new XmlConfiguration(XmlConfigurationTest.class.getResource("/configs/invalid-core.xml")); fail(); - } catch (XmlConfigurationException xce) { - SAXParseException e = (SAXParseException) xce.getCause(); - assertThat(e.getLineNumber(), is(5)); - assertThat(e.getColumnNumber(), is(29)); + } catch (XmlConfigurationException e) { + assertThat(e.getCause().getMessage(), + either(containsString("'ehcache:cach'")) + .or(containsString("'{\"http://www.ehcache.org/v3\":cach}'")) + .or(containsString(""))); } } @@ -270,10 +269,11 @@ public void testInvalidServiceConfiguration() throws Exception { try { new XmlConfiguration(XmlConfigurationTest.class.getResource("/configs/invalid-service.xml")); fail(); - } catch (XmlConfigurationException xce) { - SAXParseException e = (SAXParseException) xce.getCause(); - assertThat(e.getLineNumber(), is(6)); - assertThat(e.getColumnNumber(), is(15)); + } catch (XmlConfigurationException e) { + assertThat(e.getCause().getMessage(), + either(containsString("'foo:bar'")) + .or(containsString("'{\"http://www.example.com/foo\":bar}'")) + .or(containsString(""))); } } @@ -407,7 +407,7 @@ public void testDefaultSerializerConfiguration() throws Exception { assertThat(xmlConfig.getServiceCreationConfigurations().size(), is(1)); - ServiceCreationConfiguration configuration = xmlConfig.getServiceCreationConfigurations().iterator().next(); + ServiceCreationConfiguration configuration = xmlConfig.getServiceCreationConfigurations().iterator().next(); assertThat(configuration, instanceOf(DefaultSerializationProviderConfiguration.class)); @@ -419,12 +419,12 @@ public void testDefaultSerializerConfiguration() throws Exception { assertThat(factoryConfiguration.getDefaultSerializers().get(Integer.class), Matchers.equalTo(TestSerializer4.class)); - List> orderedServiceConfigurations = new ArrayList<>(xmlConfig.getCacheConfigurations() + List> orderedServiceConfigurations = new ArrayList<>(xmlConfig.getCacheConfigurations() .get("baz") .getServiceConfigurations()); // order services by class name so the test can rely on some sort of ordering orderedServiceConfigurations.sort(Comparator.comparing(o -> o.getClass().getName())); - Iterator> it = orderedServiceConfigurations.iterator(); + Iterator> it = orderedServiceConfigurations.iterator(); DefaultSerializerConfiguration keySerializationProviderConfiguration = (DefaultSerializerConfiguration) it.next(); assertThat(keySerializationProviderConfiguration.getType(), isIn(new DefaultSerializerConfiguration.Type[] { DefaultSerializerConfiguration.Type.KEY, DefaultSerializerConfiguration.Type.VALUE })); @@ -459,7 +459,7 @@ public void testCacheCopierConfiguration() throws Exception { assertThat(xmlConfig.getServiceCreationConfigurations().size(), is(1)); - ServiceCreationConfiguration configuration = xmlConfig.getServiceCreationConfigurations().iterator().next(); + ServiceCreationConfiguration configuration = xmlConfig.getServiceCreationConfigurations().iterator().next(); assertThat(configuration, instanceOf(DefaultCopyProviderConfiguration.class)); @@ -471,8 +471,8 @@ public void testCacheCopierConfiguration() throws Exception { Matchers.>>equalTo(PersonCopier.class)); - Collection> configs = xmlConfig.getCacheConfigurations().get("baz").getServiceConfigurations(); - for(ServiceConfiguration config: configs) { + Collection> configs = xmlConfig.getCacheConfigurations().get("baz").getServiceConfigurations(); + for(ServiceConfiguration config: configs) { if(config instanceof DefaultCopierConfiguration) { DefaultCopierConfiguration copierConfig = (DefaultCopierConfiguration) config; if(copierConfig.getType() == DefaultCopierConfiguration.Type.KEY) { @@ -484,7 +484,7 @@ public void testCacheCopierConfiguration() throws Exception { } configs = xmlConfig.getCacheConfigurations().get("bak").getServiceConfigurations(); - for(ServiceConfiguration config: configs) { + for(ServiceConfiguration config: configs) { if(config instanceof DefaultCopierConfiguration) { DefaultCopierConfiguration copierConfig = (DefaultCopierConfiguration) config; if(copierConfig.getType() == DefaultCopierConfiguration.Type.KEY) { @@ -501,7 +501,7 @@ public void testPersistenceConfig() throws Exception { final URL resource = XmlConfigurationTest.class.getResource("/configs/persistence-config.xml"); XmlConfiguration xmlConfig = new XmlConfiguration(new XmlConfiguration(resource)); - ServiceCreationConfiguration serviceConfig = xmlConfig.getServiceCreationConfigurations().iterator().next(); + ServiceCreationConfiguration serviceConfig = xmlConfig.getServiceCreationConfigurations().iterator().next(); assertThat(serviceConfig, instanceOf(DefaultPersistenceConfiguration.class)); DefaultPersistenceConfiguration persistenceConfiguration = (DefaultPersistenceConfiguration)serviceConfig; @@ -528,7 +528,7 @@ public void testWriteBehind() throws Exception { final URL resource = XmlConfigurationTest.class.getResource("/configs/writebehind-cache.xml"); XmlConfiguration xmlConfig = new XmlConfiguration(resource); - Collection> serviceConfiguration = xmlConfig.getCacheConfigurations().get("bar").getServiceConfigurations(); + Collection> serviceConfiguration = xmlConfig.getCacheConfigurations().get("bar").getServiceConfigurations(); assertThat(serviceConfiguration, IsCollectionContaining.hasItem(instanceOf(WriteBehindConfiguration.class))); @@ -536,7 +536,7 @@ public void testWriteBehind() throws Exception { assertThat(serviceConfiguration, IsCollectionContaining.hasItem(instanceOf(WriteBehindConfiguration.class))); - for (ServiceConfiguration configuration : serviceConfiguration) { + for (ServiceConfiguration configuration : serviceConfiguration) { if(configuration instanceof WriteBehindConfiguration) { BatchingConfiguration batchingConfig = ((WriteBehindConfiguration) configuration).getBatchingConfiguration(); assertThat(batchingConfig.getMaxDelay(), is(10L)); @@ -568,7 +568,7 @@ public void testCacheEventListenerThroughTemplate() throws Exception { checkListenerConfigurationExists(cacheConfig.getServiceConfigurations()); CacheConfigurationBuilder templateConfig = xmlConfig.newCacheConfigurationBuilderFromTemplate("example", Number.class, String.class); - assertThat(templateConfig.getExistingServiceConfiguration(DefaultCacheEventListenerConfiguration.class), notNullValue()); + assertThat(templateConfig.getService(DefaultCacheEventListenerConfiguration.class), notNullValue()); } @Test @@ -621,23 +621,20 @@ public void testDiskStoreSettings() throws Exception { @Test public void testNullUrlInConstructorThrowsNPE() throws Exception { - thrown.expect(NullPointerException.class); - thrown.expectMessage("The url can not be null"); - new XmlConfiguration((URL) null, mock(ClassLoader.class), getClassLoaderMapMock()); + NullPointerException thrown = assertThrows(NullPointerException.class, () -> new XmlConfiguration((URL) null, mock(ClassLoader.class), getClassLoaderMapMock())); + assertThat(thrown, hasProperty("message", Matchers.is("The url can not be null"))); } @Test public void testNullClassLoaderInConstructorThrowsNPE() throws Exception { - thrown.expect(NullPointerException.class); - thrown.expectMessage("The classLoader can not be null"); - new XmlConfiguration(XmlConfigurationTest.class.getResource("/configs/one-cache.xml"), null, getClassLoaderMapMock()); + NullPointerException thrown = assertThrows(NullPointerException.class, () -> new XmlConfiguration(XmlConfigurationTest.class.getResource("/configs/one-cache.xml"), null, getClassLoaderMapMock())); + assertThat(thrown, hasProperty("message", Matchers.is("The classLoader can not be null"))); } @Test public void testNullCacheClassLoaderMapInConstructorThrowsNPE() throws Exception { - thrown.expect(NullPointerException.class); - thrown.expectMessage("The cacheClassLoaders map can not be null"); - new XmlConfiguration(XmlConfigurationTest.class.getResource("/configs/one-cache.xml"), mock(ClassLoader.class), null); + NullPointerException thrown = assertThrows(NullPointerException.class, () -> new XmlConfiguration(XmlConfigurationTest.class.getResource("/configs/one-cache.xml"), mock(ClassLoader.class), null)); + assertThat(thrown, hasProperty("message", Matchers.is("The cacheClassLoaders map can not be null"))); } @Test @@ -729,30 +726,6 @@ public void testResilienceStrategyFromTemplate() throws Exception { assertThat(resilienceStrategyConfiguration.getClazz(), sameInstance(ShrubberyResilience.class)); } - @Test - public void testSysPropReplace() { - System.getProperties().setProperty("ehcache.match", Number.class.getName()); - XmlConfiguration xmlConfig = new XmlConfiguration(XmlConfigurationTest.class.getResource("/configs/systemprops.xml")); - - assertThat(xmlConfig.getCacheConfigurations().get("bar").getKeyType(), sameInstance((Class)Number.class)); - - DefaultPersistenceConfiguration persistenceConfiguration = (DefaultPersistenceConfiguration)xmlConfig.getServiceCreationConfigurations().iterator().next(); - assertThat(persistenceConfiguration.getRootDirectory(), is(new File(System.getProperty("user.home") + "/ehcache"))); - } - - @Test - public void testSysPropReplaceRegExp() { - assertThat(ConfigurationParser.replaceProperties("foo${file.separator}"), equalTo("foo" + File.separator)); - assertThat(ConfigurationParser.replaceProperties("${file.separator}foo${file.separator}"), equalTo(File.separator + "foo" + File.separator)); - try { - ConfigurationParser.replaceProperties("${bar}foo"); - fail("Should have thrown!"); - } catch (IllegalStateException e) { - assertThat(e.getMessage().contains("${bar}"), is(true)); - } - assertThat(ConfigurationParser.replaceProperties("foo"), nullValue()); - } - @Test public void testMultithreadedXmlParsing() throws InterruptedException, ExecutionException { Callable parserTask = () -> new XmlConfiguration(XmlConfigurationTest.class.getResource("/configs/one-cache.xml")); @@ -772,7 +745,113 @@ public void testCompleteXmlToString() { URL resource = XmlConfigurationTest.class.getResource("/configs/ehcache-complete.xml"); Configuration config = new XmlConfiguration(resource); XmlConfiguration xmlConfig = new XmlConfiguration(config); - assertThat(xmlConfig.toString(), isSimilarTo(Input.from(resource)).ignoreComments().ignoreWhitespace().withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText))); + assertThat(xmlConfig.toString(), isSameConfigurationAs(resource)); + } + + @Test + public void testPrettyTypeNames() { + URL resource = XmlConfigurationTest.class.getResource("/configs/pretty-typed-caches.xml"); + Configuration config = new XmlConfiguration(new XmlConfiguration(resource)); + + CacheConfiguration byteArray = config.getCacheConfigurations().get("byte-array"); + assertThat(byteArray.getValueType(), equalTo(byte[].class)); + + CacheConfiguration stringArray = config.getCacheConfigurations().get("string-array"); + assertThat(stringArray.getValueType(), equalTo(String[].class)); + + CacheConfiguration string2dArray = config.getCacheConfigurations().get("string-2d-array"); + assertThat(string2dArray.getValueType(), equalTo(String[][].class)); + + CacheConfiguration mapEntry = config.getCacheConfigurations().get("map-entry"); + assertThat(mapEntry.getValueType(), equalTo(Map.Entry.class)); + } + + @Test + public void testPrimitiveNameConversion() throws ClassNotFoundException { + assertThat(getClassForName("boolean", getDefaultClassLoader()), IsEqual.equalTo(Boolean.TYPE)); + assertThat(getClassForName("byte", getDefaultClassLoader()), IsEqual.equalTo(Byte.TYPE)); + assertThat(getClassForName("short", getDefaultClassLoader()), IsEqual.equalTo(Short.TYPE)); + assertThat(getClassForName("int", getDefaultClassLoader()), IsEqual.equalTo(Integer.TYPE)); + assertThat(getClassForName("long", getDefaultClassLoader()), IsEqual.equalTo(Long.TYPE)); + assertThat(getClassForName("char", getDefaultClassLoader()), IsEqual.equalTo(Character.TYPE)); + assertThat(getClassForName("float", getDefaultClassLoader()), IsEqual.equalTo(Float.TYPE)); + assertThat(getClassForName("double", getDefaultClassLoader()), IsEqual.equalTo(Double.TYPE)); + } + + @Test + public void testPrimitiveArrayClassNameConversion() throws ClassNotFoundException { + assertThat(getClassForName("boolean[]", getDefaultClassLoader()), IsEqual.equalTo(boolean[].class)); + assertThat(getClassForName("byte[]", getDefaultClassLoader()), IsEqual.equalTo(byte[].class)); + assertThat(getClassForName("short[]", getDefaultClassLoader()), IsEqual.equalTo(short[].class)); + assertThat(getClassForName("int[]", getDefaultClassLoader()), IsEqual.equalTo(int[].class)); + assertThat(getClassForName("long[]", getDefaultClassLoader()), IsEqual.equalTo(long[].class)); + assertThat(getClassForName("char[]", getDefaultClassLoader()), IsEqual.equalTo(char[].class)); + assertThat(getClassForName("float[]", getDefaultClassLoader()), IsEqual.equalTo(float[].class)); + assertThat(getClassForName("double[]", getDefaultClassLoader()), IsEqual.equalTo(double[].class)); + } + + @Test + public void testMultiDimensionPrimitiveArrayClassNameConversion() throws ClassNotFoundException { + assertThat(getClassForName("byte[][][][]", getDefaultClassLoader()), IsEqual.equalTo(byte[][][][].class)); + assertThat(getClassForName("short[][][][]", getDefaultClassLoader()), IsEqual.equalTo(short[][][][].class)); + assertThat(getClassForName("int[][][][]", getDefaultClassLoader()), IsEqual.equalTo(int[][][][].class)); + assertThat(getClassForName("long[][][][]", getDefaultClassLoader()), IsEqual.equalTo(long[][][][].class)); + assertThat(getClassForName("char[][][][]", getDefaultClassLoader()), IsEqual.equalTo(char[][][][].class)); + assertThat(getClassForName("float[][][][]", getDefaultClassLoader()), IsEqual.equalTo(float[][][][].class)); + assertThat(getClassForName("double[][][][]", getDefaultClassLoader()), IsEqual.equalTo(double[][][][].class)); + } + + @Test + public void testArrayClassNameConversion() throws ClassNotFoundException { + assertThat(getClassForName("java.lang.String[]", getDefaultClassLoader()), IsEqual.equalTo(String[].class)); + } + + @Test + public void testMultiDimensionArrayClassNameConversion() throws ClassNotFoundException { + assertThat(getClassForName("java.lang.String[][][][]", getDefaultClassLoader()), IsEqual.equalTo(String[][][][].class)); + } + + @Test + public void testInnerClassNameConversion() throws ClassNotFoundException { + assertThat(getClassForName("java.util.Map.Entry", getDefaultClassLoader()), IsEqual.equalTo(Map.Entry.class)); + } + + @Test + public void testInnerClassNameArrayConversion() throws ClassNotFoundException { + assertThat(getClassForName("java.util.Map.Entry[]", getDefaultClassLoader()), IsEqual.equalTo(Map.Entry[].class)); + } + + @Test + public void testUnknownServiceCreation() throws Exception { + URL resource = XmlConfigurationTest.class.getResource("/configs/unknown-service-creation.xml"); + try { + new XmlConfiguration(resource); + } catch (XmlConfigurationException e) { + assertThat(e.getMessage(), is("Cannot confirm XML sub-type correctness. You might be missing client side libraries.")); + assertThat(e.getCause(), instanceOf(SAXParseException.class)); + } + } + + @Test + public void testUnknownService() throws Exception { + URL resource = XmlConfigurationTest.class.getResource("/configs/unknown-service.xml"); + try { + new XmlConfiguration(resource); + } catch (XmlConfigurationException e) { + assertThat(e.getMessage(), is("Cannot confirm XML sub-type correctness. You might be missing client side libraries.")); + assertThat(e.getCause(), instanceOf(SAXParseException.class)); + } + } + + @Test + public void testUnknownResource() throws Exception { + URL resource = XmlConfigurationTest.class.getResource("/configs/unknown-resource.xml"); + try { + new XmlConfiguration(resource); + } catch (XmlConfigurationException e) { + assertThat(e.getMessage(), is("Cannot confirm XML sub-type correctness. You might be missing client side libraries.")); + assertThat(e.getCause(), instanceOf(SAXParseException.class)); + } } private void checkListenerConfigurationExists(Collection configuration) { diff --git a/xml/src/test/java/org/ehcache/xml/multi/XmlMultiConfigurationTest.java b/ehcache-xml/src/test/java/org/ehcache/xml/multi/XmlMultiConfigurationTest.java similarity index 86% rename from xml/src/test/java/org/ehcache/xml/multi/XmlMultiConfigurationTest.java rename to ehcache-xml/src/test/java/org/ehcache/xml/multi/XmlMultiConfigurationTest.java index da0fb0a466..d2e9f45be1 100644 --- a/xml/src/test/java/org/ehcache/xml/multi/XmlMultiConfigurationTest.java +++ b/ehcache-xml/src/test/java/org/ehcache/xml/multi/XmlMultiConfigurationTest.java @@ -18,14 +18,17 @@ import org.ehcache.config.Configuration; import org.ehcache.config.builders.ConfigurationBuilder; import org.ehcache.xml.XmlConfiguration; +import org.ehcache.xml.XmlConfigurationMatchers; import org.ehcache.xml.XmlConfigurationTest; +import org.ehcache.xml.exceptions.XmlConfigurationException; import org.junit.Test; -import org.xmlunit.builder.Input; import org.xmlunit.diff.DefaultNodeMatcher; import java.net.URISyntaxException; import java.net.URL; +import static org.ehcache.xml.XmlConfigurationMatchers.isSameConfigurationAs; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.collection.IsEmptyCollection.empty; import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder; @@ -33,12 +36,11 @@ import static org.hamcrest.core.IsNull.notNullValue; import static org.hamcrest.core.IsNull.nullValue; import static org.hamcrest.core.IsSame.sameInstance; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; +import static org.xmlunit.builder.Input.fromURI; import static org.xmlunit.diff.ElementSelectors.and; import static org.xmlunit.diff.ElementSelectors.byNameAndAllAttributes; import static org.xmlunit.diff.ElementSelectors.byNameAndText; -import static org.xmlunit.matchers.CompareMatcher.isSimilarTo; public class XmlMultiConfigurationTest { @@ -53,7 +55,7 @@ public void testEmptyConfigurationFromBuilder() { assertThrows(() -> xmlMultiConfiguration.variants("foo"), IllegalArgumentException.class); assertThat(xmlMultiConfiguration.toString(), - isSimilarTo("").ignoreWhitespace().ignoreComments()); + isSameConfigurationAs("")); } @Test @@ -69,10 +71,9 @@ public void testSimpleConfigurationBuiltFromEmpty() { assertThat(xmlMultiConfiguration.variants("foo"), empty()); assertThat(xmlMultiConfiguration.toString(), - isSimilarTo( - "" + + isSameConfigurationAs("" + "" + - "").ignoreWhitespace().ignoreComments()); + "")); } @Test @@ -90,13 +91,12 @@ public void testVariantConfigurationBuiltFromEmpty() { assertThat(xmlMultiConfiguration.identities(), containsInAnyOrder("foo")); assertThat(xmlMultiConfiguration.variants("foo"), containsInAnyOrder("bar", "baz")); - assertThat(xmlMultiConfiguration.toString(), isSimilarTo( - "" + + assertThat(xmlMultiConfiguration.toString(), isSameConfigurationAs("" + "" + "" + "" + "" + - "").ignoreWhitespace().ignoreComments()); + "")); } @Test @@ -123,8 +123,7 @@ public void testMixedConfigurationBuiltFromEmpty() { assertThat(xmlMultiConfiguration.variants("fum"), containsInAnyOrder("bar")); assertThat(xmlMultiConfiguration.variants("fii"), empty()); - assertThat(xmlMultiConfiguration.toString(), isSimilarTo( - "" + + assertThat(xmlMultiConfiguration.toString(), isSameConfigurationAs("" + "" + "" + "" + @@ -135,7 +134,7 @@ public void testMixedConfigurationBuiltFromEmpty() { "" + "" + "" + - "").ignoreWhitespace().ignoreComments()); + "")); } @Test @@ -150,7 +149,7 @@ public void testEmptyConfigurationFromXml() throws URISyntaxException { assertThat(xmlMultiConfiguration.identities(), empty()); assertThrows(() -> xmlMultiConfiguration.variants("foo"), IllegalArgumentException.class); - assertThat(xmlMultiConfiguration.toString(), isSimilarTo(Input.fromURI(resource.toURI())).ignoreWhitespace().ignoreComments()); + assertThat(xmlMultiConfiguration.toString(), isSameConfigurationAs(fromURI(resource.toURI()))); } @Test @@ -168,7 +167,7 @@ public void testMultipleConfigurationsFromXml() throws URISyntaxException { assertThat(xmlMultiConfiguration.variants("foo"), empty()); assertThat(xmlMultiConfiguration.variants("bar"), empty()); - assertThat(xmlMultiConfiguration.toString(), isSimilarTo(Input.fromURI(resource.toURI())).ignoreWhitespace().ignoreComments()); + assertThat(xmlMultiConfiguration.toString(), isSameConfigurationAs(fromURI(resource.toURI()))); } @Test @@ -188,7 +187,7 @@ public void testMultipleVariantsFromXml() throws URISyntaxException { assertThat(xmlMultiConfiguration.variants("foo"), containsInAnyOrder("development", "production")); assertThat(xmlMultiConfiguration.variants("bar"), containsInAnyOrder("development", "production")); - assertThat(xmlMultiConfiguration.toString(), isSimilarTo(Input.fromURI(resource.toURI())).ignoreWhitespace().ignoreComments()); + assertThat(xmlMultiConfiguration.toString(), isSameConfigurationAs(fromURI(resource.toURI()))); } @Test @@ -205,14 +204,13 @@ public void testManagerRemovedFromXml() throws URISyntaxException { assertThat(xmlMultiConfiguration.identities(), containsInAnyOrder("foo")); assertThat(xmlMultiConfiguration.variants("foo"), empty()); - assertThat(xmlMultiConfiguration.toString(), isSimilarTo( - "" + + assertThat(xmlMultiConfiguration.toString(), isSameConfigurationAs("" + "" + "" + "100" + "" + "" + - "").ignoreWhitespace().ignoreComments()); + "")); } @Test @@ -230,8 +228,7 @@ public void testManagerRemovedFromXmlAndReadded() throws URISyntaxException { assertThat(xmlMultiConfiguration.variants("foo"), empty()); assertThat(xmlMultiConfiguration.variants("bar"), empty()); - assertThat(xmlMultiConfiguration.toString(), isSimilarTo( - "" + + assertThat(xmlMultiConfiguration.toString(), isSameConfigurationAs("" + "" + "" + "" + @@ -240,7 +237,7 @@ public void testManagerRemovedFromXmlAndReadded() throws URISyntaxException { "100" + "" + "" + - "").ignoreWhitespace().ignoreComments()); + "")); } @Test @@ -261,8 +258,7 @@ public void testManagerAddedToXml() throws URISyntaxException { assertThat(xmlMultiConfiguration.variants("bar"), empty()); assertThat(xmlMultiConfiguration.variants("baz"), empty()); - assertThat(xmlMultiConfiguration.toString(), isSimilarTo( - "" + + assertThat(xmlMultiConfiguration.toString(), isSameConfigurationAs("" + "" + "" + "100" + @@ -276,7 +272,7 @@ public void testManagerAddedToXml() throws URISyntaxException { "100" + "" + "" + - "").ignoreWhitespace().ignoreComments()); + "")); } @Test @@ -292,7 +288,7 @@ public void testManagerRemovedFromConfig() throws URISyntaxException { assertThrows(() -> xmlMultiConfiguration.variants("foo"), IllegalArgumentException.class); assertThat(xmlMultiConfiguration.toString(), - isSimilarTo("").ignoreWhitespace().ignoreComments()); + isSameConfigurationAs("")); } @Test @@ -307,10 +303,9 @@ public void testManagerRemovedFromConfigAndReadded() throws URISyntaxException { assertThat(xmlMultiConfiguration.identities(), containsInAnyOrder("foo")); assertThat(xmlMultiConfiguration.variants("foo"), empty()); - assertThat(xmlMultiConfiguration.toString(), isSimilarTo( - "" + + assertThat(xmlMultiConfiguration.toString(), isSameConfigurationAs("" + "" + - "").ignoreWhitespace().ignoreComments()); + "")); } @Test @@ -328,12 +323,10 @@ public void testManagerAddedToConfig() throws URISyntaxException { assertThat(xmlMultiConfiguration.variants("foo"), empty()); assertThat(xmlMultiConfiguration.variants("baz"), empty()); - assertThat(xmlMultiConfiguration.toString(), isSimilarTo( - "" + + assertThat(xmlMultiConfiguration.toString(), isSameConfigurationAs("" + "" + "" + - "").ignoreWhitespace().ignoreComments().withNodeMatcher( - new DefaultNodeMatcher(and(byNameAndText, byNameAndAllAttributes)))); + "")); } @Test @@ -347,8 +340,7 @@ public void testGenerateExtendedConfiguration() throws URISyntaxException { assertThat(xmlMultiConfiguration.identities(), containsInAnyOrder("foo")); assertThat(xmlMultiConfiguration.variants("foo"), empty()); - assertThat(xmlMultiConfiguration.toString(), isSimilarTo( - "" + + assertThat(xmlMultiConfiguration.toString(), isSameConfigurationAs("" + "" + "" + "" + @@ -363,11 +355,11 @@ public void testGenerateExtendedConfiguration() throws URISyntaxException { "" + "" + "" + - "").ignoreWhitespace().ignoreComments()); + "")); } @Test - public void testParseExtendedConfiguration() throws URISyntaxException { + public void testParseExtendedConfiguration() { XmlMultiConfiguration xmlMultiConfiguration = XmlMultiConfiguration.from(getClass().getResource("/configs/multi/extended.xml")).build(); assertThat(xmlMultiConfiguration.configuration("foo"), notNullValue()); @@ -375,8 +367,7 @@ public void testParseExtendedConfiguration() throws URISyntaxException { assertThat(xmlMultiConfiguration.identities(), containsInAnyOrder("foo")); assertThat(xmlMultiConfiguration.variants("foo"), empty()); - assertThat(xmlMultiConfiguration.toString(), isSimilarTo( - "" + + assertThat(xmlMultiConfiguration.toString(), isSameConfigurationAs("" + "" + "" + "" + @@ -391,7 +382,12 @@ public void testParseExtendedConfiguration() throws URISyntaxException { "" + "" + "" + - "").ignoreWhitespace().ignoreComments()); + "")); + } + + @Test(expected = XmlConfigurationException.class) + public void testParseOrdinaryConfiguration() { + XmlMultiConfiguration.from(getClass().getResource("/configs/one-cache.xml")).build(); } private static Configuration emptyConfiguration() { diff --git a/xml/src/test/java/org/ehcache/xml/provider/CacheEventDispatcherFactoryConfigurationParserTest.java b/ehcache-xml/src/test/java/org/ehcache/xml/provider/CacheEventDispatcherFactoryConfigurationParserTest.java similarity index 91% rename from xml/src/test/java/org/ehcache/xml/provider/CacheEventDispatcherFactoryConfigurationParserTest.java rename to ehcache-xml/src/test/java/org/ehcache/xml/provider/CacheEventDispatcherFactoryConfigurationParserTest.java index 1b61d6a604..eb126f095d 100644 --- a/xml/src/test/java/org/ehcache/xml/provider/CacheEventDispatcherFactoryConfigurationParserTest.java +++ b/ehcache-xml/src/test/java/org/ehcache/xml/provider/CacheEventDispatcherFactoryConfigurationParserTest.java @@ -40,7 +40,7 @@ public void parseServiceCreationConfiguration() throws SAXException, JAXBExcepti assertThat(xmlConfig.getServiceCreationConfigurations()).hasSize(1); - ServiceCreationConfiguration configuration = xmlConfig.getServiceCreationConfigurations().iterator().next(); + ServiceCreationConfiguration configuration = xmlConfig.getServiceCreationConfigurations().iterator().next(); assertThat(configuration).isInstanceOf(CacheEventDispatcherFactoryConfiguration.class); CacheEventDispatcherFactoryConfiguration providerConfiguration = (CacheEventDispatcherFactoryConfiguration) configuration; @@ -50,7 +50,7 @@ public void parseServiceCreationConfiguration() throws SAXException, JAXBExcepti @Test public void unparseServiceCreationConfiguration() { Configuration config = ConfigurationBuilder.newConfigurationBuilder() - .addService(new CacheEventDispatcherFactoryConfiguration("foo")).build(); + .withService(new CacheEventDispatcherFactoryConfiguration("foo")).build(); ConfigType configType = new CacheEventDispatcherFactoryConfigurationParser().unparseServiceCreationConfiguration(config, new ConfigType()); assertThat(configType.getEventDispatch().getThreadPool()).isEqualTo("foo"); diff --git a/xml/src/test/java/org/ehcache/xml/provider/CacheManagerPersistenceConfigurationParserTest.java b/ehcache-xml/src/test/java/org/ehcache/xml/provider/CacheManagerPersistenceConfigurationParserTest.java similarity index 96% rename from xml/src/test/java/org/ehcache/xml/provider/CacheManagerPersistenceConfigurationParserTest.java rename to ehcache-xml/src/test/java/org/ehcache/xml/provider/CacheManagerPersistenceConfigurationParserTest.java index f2a3160f32..0cc26057eb 100644 --- a/xml/src/test/java/org/ehcache/xml/provider/CacheManagerPersistenceConfigurationParserTest.java +++ b/ehcache-xml/src/test/java/org/ehcache/xml/provider/CacheManagerPersistenceConfigurationParserTest.java @@ -53,7 +53,7 @@ public void parseServiceCreationConfiguration() throws SAXException, JAXBExcepti @Test public void unparseServiceCreationConfiguration() { Configuration config = ConfigurationBuilder.newConfigurationBuilder() - .addService(new CacheManagerPersistenceConfiguration(new File("foo"))).build(); + .withService(new CacheManagerPersistenceConfiguration(new File("foo"))).build(); ConfigType configType = new CacheManagerPersistenceConfigurationParser().unparseServiceCreationConfiguration(config, new ConfigType()); assertThat(configType.getPersistence().getDirectory()).isEqualTo("foo"); diff --git a/xml/src/test/java/org/ehcache/xml/provider/DefaultCopyProviderConfigurationParserTest.java b/ehcache-xml/src/test/java/org/ehcache/xml/provider/DefaultCopyProviderConfigurationParserTest.java similarity index 89% rename from xml/src/test/java/org/ehcache/xml/provider/DefaultCopyProviderConfigurationParserTest.java rename to ehcache-xml/src/test/java/org/ehcache/xml/provider/DefaultCopyProviderConfigurationParserTest.java index 66ce75719d..c950fc01a3 100644 --- a/xml/src/test/java/org/ehcache/xml/provider/DefaultCopyProviderConfigurationParserTest.java +++ b/ehcache-xml/src/test/java/org/ehcache/xml/provider/DefaultCopyProviderConfigurationParserTest.java @@ -18,9 +18,8 @@ import org.ehcache.config.Configuration; import org.ehcache.config.builders.ConfigurationBuilder; +import org.ehcache.impl.config.copy.DefaultCopierConfiguration; import org.ehcache.impl.config.copy.DefaultCopyProviderConfiguration; -import org.ehcache.impl.internal.classes.ClassInstanceConfiguration; -import org.ehcache.spi.copy.Copier; import org.ehcache.spi.service.ServiceCreationConfiguration; import org.ehcache.xml.XmlConfiguration; import org.ehcache.xml.model.ConfigType; @@ -50,12 +49,12 @@ public void parseServiceCreationConfiguration() throws SAXException, JAXBExcepti assertThat(xmlConfig.getServiceCreationConfigurations()).hasSize(1); - ServiceCreationConfiguration configuration = xmlConfig.getServiceCreationConfigurations().iterator().next(); + ServiceCreationConfiguration configuration = xmlConfig.getServiceCreationConfigurations().iterator().next(); assertThat(configuration).isExactlyInstanceOf(DefaultCopyProviderConfiguration.class); DefaultCopyProviderConfiguration factoryConfiguration = (DefaultCopyProviderConfiguration) configuration; - Map, ClassInstanceConfiguration>> defaults = factoryConfiguration.getDefaults(); + Map, DefaultCopierConfiguration> defaults = factoryConfiguration.getDefaults(); assertThat(defaults).hasSize(2); assertThat(defaults.get(Description.class).getClazz()).isEqualTo(DescriptionCopier.class); assertThat(defaults.get(Person.class).getClazz()).isEqualTo((PersonCopier.class)); @@ -67,7 +66,7 @@ public void unparseServiceCreationConfiguration() { providerConfig.addCopierFor(Description.class, DescriptionCopier.class); providerConfig.addCopierFor(Person.class, PersonCopier.class); - Configuration config = ConfigurationBuilder.newConfigurationBuilder().addService(providerConfig).build(); + Configuration config = ConfigurationBuilder.newConfigurationBuilder().withService(providerConfig).build(); ConfigType configType = new DefaultCopyProviderConfigurationParser().unparseServiceCreationConfiguration(config, new ConfigType()); List copiers = configType.getDefaultCopiers().getCopier(); diff --git a/xml/src/test/java/org/ehcache/xml/provider/DefaultSerializationProviderConfigurationParserTest.java b/ehcache-xml/src/test/java/org/ehcache/xml/provider/DefaultSerializationProviderConfigurationParserTest.java similarity index 95% rename from xml/src/test/java/org/ehcache/xml/provider/DefaultSerializationProviderConfigurationParserTest.java rename to ehcache-xml/src/test/java/org/ehcache/xml/provider/DefaultSerializationProviderConfigurationParserTest.java index b077dc89be..ea08c210c8 100644 --- a/xml/src/test/java/org/ehcache/xml/provider/DefaultSerializationProviderConfigurationParserTest.java +++ b/ehcache-xml/src/test/java/org/ehcache/xml/provider/DefaultSerializationProviderConfigurationParserTest.java @@ -51,7 +51,7 @@ public void parseServiceCreationConfiguration() throws SAXException, JAXBExcepti assertThat(xmlConfig.getServiceCreationConfigurations()).hasSize(1); - ServiceCreationConfiguration configuration = xmlConfig.getServiceCreationConfigurations().iterator().next(); + ServiceCreationConfiguration configuration = xmlConfig.getServiceCreationConfigurations().iterator().next(); assertThat(configuration).isExactlyInstanceOf(DefaultSerializationProviderConfiguration.class); @@ -71,7 +71,7 @@ public void unparseServiceCreationConfiguration() { providerConfig.addSerializerFor(Description.class, (Class) TestSerializer3.class); providerConfig.addSerializerFor(Person.class, (Class) TestSerializer4.class); - Configuration config = ConfigurationBuilder.newConfigurationBuilder().addService(providerConfig).build(); + Configuration config = ConfigurationBuilder.newConfigurationBuilder().withService(providerConfig).build(); ConfigType configType = new DefaultSerializationProviderConfigurationParser().unparseServiceCreationConfiguration(config, new ConfigType()); List serializers = configType.getDefaultSerializers().getSerializer(); diff --git a/xml/src/test/java/org/ehcache/xml/provider/DefaultSizeOfEngineProviderConfigurationParserTest.java b/ehcache-xml/src/test/java/org/ehcache/xml/provider/DefaultSizeOfEngineProviderConfigurationParserTest.java similarity index 92% rename from xml/src/test/java/org/ehcache/xml/provider/DefaultSizeOfEngineProviderConfigurationParserTest.java rename to ehcache-xml/src/test/java/org/ehcache/xml/provider/DefaultSizeOfEngineProviderConfigurationParserTest.java index e5def5da4c..3ad074be31 100644 --- a/xml/src/test/java/org/ehcache/xml/provider/DefaultSizeOfEngineProviderConfigurationParserTest.java +++ b/ehcache-xml/src/test/java/org/ehcache/xml/provider/DefaultSizeOfEngineProviderConfigurationParserTest.java @@ -43,7 +43,7 @@ public void parseServiceCreationConfiguration() throws SAXException, JAXBExcepti assertThat(xmlConfig.getServiceCreationConfigurations()).hasSize(1); - ServiceCreationConfiguration configuration = xmlConfig.getServiceCreationConfigurations().iterator().next(); + ServiceCreationConfiguration configuration = xmlConfig.getServiceCreationConfigurations().iterator().next(); assertThat(configuration).isExactlyInstanceOf(DefaultSizeOfEngineProviderConfiguration.class); DefaultSizeOfEngineProviderConfiguration sizeOfEngineProviderConfig = (DefaultSizeOfEngineProviderConfiguration) configuration; @@ -55,7 +55,7 @@ public void parseServiceCreationConfiguration() throws SAXException, JAXBExcepti public void unparseServiceCreationConfiguration() { ConfigType configType = new ConfigType(); Configuration config = ConfigurationBuilder.newConfigurationBuilder() - .addService(new DefaultSizeOfEngineProviderConfiguration(123, MemoryUnit.MB, 987)).build(); + .withService(new DefaultSizeOfEngineProviderConfiguration(123, MemoryUnit.MB, 987)).build(); configType = new DefaultSizeOfEngineProviderConfigurationParser().unparseServiceCreationConfiguration(config, configType); SizeofType heapStore = configType.getHeapStore(); diff --git a/xml/src/test/java/org/ehcache/xml/provider/OffHeapDiskStoreProviderConfigurationParserTest.java b/ehcache-xml/src/test/java/org/ehcache/xml/provider/OffHeapDiskStoreProviderConfigurationParserTest.java similarity index 92% rename from xml/src/test/java/org/ehcache/xml/provider/OffHeapDiskStoreProviderConfigurationParserTest.java rename to ehcache-xml/src/test/java/org/ehcache/xml/provider/OffHeapDiskStoreProviderConfigurationParserTest.java index d8c23f2b30..e5600e8290 100644 --- a/xml/src/test/java/org/ehcache/xml/provider/OffHeapDiskStoreProviderConfigurationParserTest.java +++ b/ehcache-xml/src/test/java/org/ehcache/xml/provider/OffHeapDiskStoreProviderConfigurationParserTest.java @@ -41,7 +41,7 @@ public void parseServiceCreationConfiguration() throws SAXException, JAXBExcepti assertThat(xmlConfig.getServiceCreationConfigurations()).hasSize(1); - ServiceCreationConfiguration configuration = xmlConfig.getServiceCreationConfigurations().iterator().next(); + ServiceCreationConfiguration configuration = xmlConfig.getServiceCreationConfigurations().iterator().next(); assertThat(configuration).isExactlyInstanceOf(OffHeapDiskStoreProviderConfiguration.class); @@ -53,7 +53,7 @@ public void parseServiceCreationConfiguration() throws SAXException, JAXBExcepti public void unparseServiceCreationConfiguration() { ConfigType configType = new ConfigType(); Configuration config = ConfigurationBuilder.newConfigurationBuilder() - .addService(new OffHeapDiskStoreProviderConfiguration("foo")).build(); + .withService(new OffHeapDiskStoreProviderConfiguration("foo")).build(); configType = new OffHeapDiskStoreProviderConfigurationParser().unparseServiceCreationConfiguration(config, configType); assertThat(configType.getDiskStore().getThreadPool()).isEqualTo("foo"); diff --git a/xml/src/test/java/org/ehcache/xml/provider/PooledExecutionServiceConfigurationParserTest.java b/ehcache-xml/src/test/java/org/ehcache/xml/provider/PooledExecutionServiceConfigurationParserTest.java similarity index 95% rename from xml/src/test/java/org/ehcache/xml/provider/PooledExecutionServiceConfigurationParserTest.java rename to ehcache-xml/src/test/java/org/ehcache/xml/provider/PooledExecutionServiceConfigurationParserTest.java index 92b780781d..2c55ccbd6e 100644 --- a/xml/src/test/java/org/ehcache/xml/provider/PooledExecutionServiceConfigurationParserTest.java +++ b/ehcache-xml/src/test/java/org/ehcache/xml/provider/PooledExecutionServiceConfigurationParserTest.java @@ -42,7 +42,7 @@ public void parseServiceCreationConfiguration() throws SAXException, JAXBExcepti assertThat(xmlConfig.getServiceCreationConfigurations()).hasSize(1); - ServiceCreationConfiguration configuration = xmlConfig.getServiceCreationConfigurations().iterator().next(); + ServiceCreationConfiguration configuration = xmlConfig.getServiceCreationConfigurations().iterator().next(); assertThat(configuration).isExactlyInstanceOf(PooledExecutionServiceConfiguration.class); PooledExecutionServiceConfiguration providerConfiguration = (PooledExecutionServiceConfiguration) configuration; @@ -65,7 +65,7 @@ public void unparseServiceCreationConfiguration() { providerConfig.addDefaultPool("foo", 5, 9); providerConfig.addPool("bar", 2, 6); - Configuration config = ConfigurationBuilder.newConfigurationBuilder().addService(providerConfig).build(); + Configuration config = ConfigurationBuilder.newConfigurationBuilder().withService(providerConfig).build(); ConfigType configType = new ConfigType(); configType = new PooledExecutionServiceConfigurationParser().unparseServiceCreationConfiguration(config, configType); diff --git a/xml/src/test/java/org/ehcache/xml/provider/WriteBehindProviderConfigurationParserTest.java b/ehcache-xml/src/test/java/org/ehcache/xml/provider/WriteBehindProviderConfigurationParserTest.java similarity index 92% rename from xml/src/test/java/org/ehcache/xml/provider/WriteBehindProviderConfigurationParserTest.java rename to ehcache-xml/src/test/java/org/ehcache/xml/provider/WriteBehindProviderConfigurationParserTest.java index 7ab0007314..fd35e5b71b 100644 --- a/xml/src/test/java/org/ehcache/xml/provider/WriteBehindProviderConfigurationParserTest.java +++ b/ehcache-xml/src/test/java/org/ehcache/xml/provider/WriteBehindProviderConfigurationParserTest.java @@ -40,7 +40,7 @@ public void parseServiceCreationConfiguration() throws SAXException, JAXBExcepti assertThat(xmlConfig.getServiceCreationConfigurations()).hasSize(1); - ServiceCreationConfiguration configuration = xmlConfig.getServiceCreationConfigurations().iterator().next(); + ServiceCreationConfiguration configuration = xmlConfig.getServiceCreationConfigurations().iterator().next(); assertThat(configuration).isExactlyInstanceOf(WriteBehindProviderConfiguration.class); @@ -52,7 +52,7 @@ public void parseServiceCreationConfiguration() throws SAXException, JAXBExcepti public void unparseServiceCreationConfiguration() { ConfigType configType = new ConfigType(); Configuration config = ConfigurationBuilder.newConfigurationBuilder() - .addService(new WriteBehindProviderConfiguration("foo")).build(); + .withService(new WriteBehindProviderConfiguration("foo")).build(); configType = new WriteBehindProviderConfigurationParser().unparseServiceCreationConfiguration(config, configType); assertThat(configType.getWriteBehind().getThreadPool()).isEqualTo("foo"); diff --git a/xml/src/test/java/org/ehcache/xml/service/DefaultCacheEventDispatcherConfigurationParserTest.java b/ehcache-xml/src/test/java/org/ehcache/xml/service/DefaultCacheEventDispatcherConfigurationParserTest.java similarity index 96% rename from xml/src/test/java/org/ehcache/xml/service/DefaultCacheEventDispatcherConfigurationParserTest.java rename to ehcache-xml/src/test/java/org/ehcache/xml/service/DefaultCacheEventDispatcherConfigurationParserTest.java index c698a15ada..13e669db7e 100644 --- a/xml/src/test/java/org/ehcache/xml/service/DefaultCacheEventDispatcherConfigurationParserTest.java +++ b/ehcache-xml/src/test/java/org/ehcache/xml/service/DefaultCacheEventDispatcherConfigurationParserTest.java @@ -43,7 +43,7 @@ public void parseServiceConfiguration() throws Exception { @Test public void unparseServiceConfiguration() { CacheConfiguration cacheConfig = - newCacheConfigurationBuilder(Object.class, Object.class, heap(10)).add(new DefaultCacheEventDispatcherConfiguration("foo")).build(); + newCacheConfigurationBuilder(Object.class, Object.class, heap(10)).withService(new DefaultCacheEventDispatcherConfiguration("foo")).build(); CacheType cacheType = new CacheType(); cacheType = new DefaultCacheEventDispatcherConfigurationParser().unparseServiceConfiguration(cacheConfig, cacheType); diff --git a/xml/src/test/java/org/ehcache/xml/service/DefaultCacheEventListenerConfigurationParserTest.java b/ehcache-xml/src/test/java/org/ehcache/xml/service/DefaultCacheEventListenerConfigurationParserTest.java similarity index 96% rename from xml/src/test/java/org/ehcache/xml/service/DefaultCacheEventListenerConfigurationParserTest.java rename to ehcache-xml/src/test/java/org/ehcache/xml/service/DefaultCacheEventListenerConfigurationParserTest.java index a173bc0e24..2ec0aa4b16 100644 --- a/xml/src/test/java/org/ehcache/xml/service/DefaultCacheEventListenerConfigurationParserTest.java +++ b/ehcache-xml/src/test/java/org/ehcache/xml/service/DefaultCacheEventListenerConfigurationParserTest.java @@ -66,7 +66,7 @@ public void unparseServiceConfiguration() { listenerConfig.setEventFiringMode(SYNCHRONOUS); listenerConfig.setEventOrderingMode(UNORDERED); - CacheConfiguration cacheConfig = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)).add(listenerConfig).build(); + CacheConfiguration cacheConfig = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)).withService(listenerConfig).build(); CacheType cacheType = new CacheType(); cacheType = new DefaultCacheEventListenerConfigurationParser().unparseServiceConfiguration(cacheConfig, cacheType); @@ -86,7 +86,7 @@ public void unparseServiceConfigurationWithInstance() { listenerConfig.setEventFiringMode(SYNCHRONOUS); listenerConfig.setEventOrderingMode(UNORDERED); - CacheConfiguration cacheConfig = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)).add(listenerConfig).build(); + CacheConfiguration cacheConfig = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)).withService(listenerConfig).build(); CacheType cacheType = new CacheType(); assertThatExceptionOfType(XmlConfigurationException.class).isThrownBy(() -> new DefaultCacheEventListenerConfigurationParser().unparseServiceConfiguration(cacheConfig, cacheType)) diff --git a/xml/src/test/java/org/ehcache/xml/service/DefaultCacheLoaderWriterConfigurationParserTest.java b/ehcache-xml/src/test/java/org/ehcache/xml/service/DefaultCacheLoaderWriterConfigurationParserTest.java similarity index 93% rename from xml/src/test/java/org/ehcache/xml/service/DefaultCacheLoaderWriterConfigurationParserTest.java rename to ehcache-xml/src/test/java/org/ehcache/xml/service/DefaultCacheLoaderWriterConfigurationParserTest.java index c436650932..740f52d293 100644 --- a/xml/src/test/java/org/ehcache/xml/service/DefaultCacheLoaderWriterConfigurationParserTest.java +++ b/ehcache-xml/src/test/java/org/ehcache/xml/service/DefaultCacheLoaderWriterConfigurationParserTest.java @@ -46,7 +46,7 @@ public void parseServiceConfiguration() throws Exception { @Test public void unparseServiceConfiguration() { CacheConfiguration cacheConfig = - newCacheConfigurationBuilder(Object.class, Object.class, heap(10)).add(new DefaultCacheLoaderWriterConfiguration(TestCacheLoaderWriter.class)).build(); + newCacheConfigurationBuilder(Object.class, Object.class, heap(10)).withService(new DefaultCacheLoaderWriterConfiguration(TestCacheLoaderWriter.class)).build(); CacheType cacheType = new DefaultCacheLoaderWriterConfigurationParser().unparseServiceConfiguration(cacheConfig, new CacheType()); @@ -57,7 +57,7 @@ public void unparseServiceConfiguration() { public void unparseServiceConfigurationWithInstance() { TestCacheLoaderWriter testCacheLoaderWriter = new TestCacheLoaderWriter(); CacheConfiguration cacheConfig = - newCacheConfigurationBuilder(Object.class, Object.class, heap(10)).add(new DefaultCacheLoaderWriterConfiguration(testCacheLoaderWriter)).build(); + newCacheConfigurationBuilder(Object.class, Object.class, heap(10)).withService(new DefaultCacheLoaderWriterConfiguration(testCacheLoaderWriter)).build(); assertThatExceptionOfType(XmlConfigurationException.class).isThrownBy(() -> new DefaultCacheLoaderWriterConfigurationParser().unparseServiceConfiguration(cacheConfig, new CacheType())) .withMessage("%s", "XML translation for instance based initialization for " + diff --git a/xml/src/test/java/org/ehcache/xml/service/DefaultCopierConfigurationParserTest.java b/ehcache-xml/src/test/java/org/ehcache/xml/service/DefaultCopierConfigurationParserTest.java similarity index 94% rename from xml/src/test/java/org/ehcache/xml/service/DefaultCopierConfigurationParserTest.java rename to ehcache-xml/src/test/java/org/ehcache/xml/service/DefaultCopierConfigurationParserTest.java index c165afff9a..00a02cc416 100644 --- a/xml/src/test/java/org/ehcache/xml/service/DefaultCopierConfigurationParserTest.java +++ b/ehcache-xml/src/test/java/org/ehcache/xml/service/DefaultCopierConfigurationParserTest.java @@ -62,8 +62,8 @@ public void parseServiceConfiguration() throws Exception { public void unparseServiceConfiguration() { @SuppressWarnings({"unchecked", "rawtypes"}) CacheConfiguration cacheConfig = newCacheConfigurationBuilder(Description.class, Person.class, heap(10)) - .add(new DefaultCopierConfiguration(DescriptionCopier.class, DefaultCopierConfiguration.Type.KEY)) - .add(new DefaultCopierConfiguration(PersonCopier.class, DefaultCopierConfiguration.Type.VALUE)) + .withService(new DefaultCopierConfiguration(DescriptionCopier.class, DefaultCopierConfiguration.Type.KEY)) + .withService(new DefaultCopierConfiguration(PersonCopier.class, DefaultCopierConfiguration.Type.VALUE)) .build(); CacheType cacheType = new CacheType(); @@ -90,7 +90,7 @@ public void unparseServiceConfigurationWithInstance() { new DefaultCopierConfiguration<>(personCopier, DefaultCopierConfiguration.Type.VALUE); CacheConfiguration cacheConfig = newCacheConfigurationBuilder(Description.class, Person.class, heap(10)) - .add(config1).add(config2).build(); + .withService(config1).withService(config2).build(); CacheType cacheType = new CacheType(); CacheEntryType keyType = new CacheEntryType(); diff --git a/xml/src/test/java/org/ehcache/xml/service/DefaultResilienceStrategyConfigurationParserTest.java b/ehcache-xml/src/test/java/org/ehcache/xml/service/DefaultResilienceStrategyConfigurationParserTest.java similarity index 94% rename from xml/src/test/java/org/ehcache/xml/service/DefaultResilienceStrategyConfigurationParserTest.java rename to ehcache-xml/src/test/java/org/ehcache/xml/service/DefaultResilienceStrategyConfigurationParserTest.java index 5faea257e7..8de81e8c9a 100644 --- a/xml/src/test/java/org/ehcache/xml/service/DefaultResilienceStrategyConfigurationParserTest.java +++ b/ehcache-xml/src/test/java/org/ehcache/xml/service/DefaultResilienceStrategyConfigurationParserTest.java @@ -47,7 +47,7 @@ public void parseServiceConfiguration() throws Exception { @Test public void unparseServiceConfiguration() { CacheConfiguration cacheConfig = - newCacheConfigurationBuilder(Object.class, Object.class, heap(10)).add(new DefaultResilienceStrategyConfiguration(TestResilienceStrategy.class)).build(); + newCacheConfigurationBuilder(Object.class, Object.class, heap(10)).withService(new DefaultResilienceStrategyConfiguration(TestResilienceStrategy.class)).build(); CacheType cacheType = new DefaultResilienceStrategyConfigurationParser().unparseServiceConfiguration(cacheConfig, new CacheType()); assertThat(cacheType.getResilience()).isEqualTo(TestResilienceStrategy.class.getName()); @@ -58,7 +58,7 @@ public void unparseServiceConfiguration() { public void unparseServiceConfigurationWithInstance() { TestResilienceStrategy testObject = new TestResilienceStrategy<>(); CacheConfiguration cacheConfig = - newCacheConfigurationBuilder(Object.class, Object.class, heap(10)).add(new DefaultResilienceStrategyConfiguration(testObject)).build(); + newCacheConfigurationBuilder(Object.class, Object.class, heap(10)).withService(new DefaultResilienceStrategyConfiguration(testObject)).build(); assertThatExceptionOfType(XmlConfigurationException.class).isThrownBy(() -> new DefaultResilienceStrategyConfigurationParser().unparseServiceConfiguration(cacheConfig, new CacheType())) .withMessage("%s", "XML translation for instance based initialization for " + diff --git a/xml/src/test/java/org/ehcache/xml/service/DefaultSerializerConfigurationParserTest.java b/ehcache-xml/src/test/java/org/ehcache/xml/service/DefaultSerializerConfigurationParserTest.java similarity index 93% rename from xml/src/test/java/org/ehcache/xml/service/DefaultSerializerConfigurationParserTest.java rename to ehcache-xml/src/test/java/org/ehcache/xml/service/DefaultSerializerConfigurationParserTest.java index 96263cf315..7afffe6edf 100644 --- a/xml/src/test/java/org/ehcache/xml/service/DefaultSerializerConfigurationParserTest.java +++ b/ehcache-xml/src/test/java/org/ehcache/xml/service/DefaultSerializerConfigurationParserTest.java @@ -17,7 +17,7 @@ package org.ehcache.xml.service; import org.ehcache.config.CacheConfiguration; -import org.ehcache.core.internal.util.ClassLoading; +import org.ehcache.core.util.ClassLoading; import org.ehcache.impl.config.serializer.DefaultSerializerConfiguration; import org.ehcache.xml.XmlConfiguration; import org.ehcache.xml.exceptions.XmlConfigurationException; @@ -61,8 +61,8 @@ public void parseServiceConfiguration() throws Exception { public void unparseServiceConfiguration() { @SuppressWarnings({"unchecked", "rawtypes"}) CacheConfiguration cacheConfig = newCacheConfigurationBuilder(Description.class, Person.class, heap(10)) - .add(new DefaultSerializerConfiguration(TestSerializer3.class, DefaultSerializerConfiguration.Type.KEY)) - .add(new DefaultSerializerConfiguration(TestSerializer4.class, DefaultSerializerConfiguration.Type.VALUE)) + .withService(new DefaultSerializerConfiguration(TestSerializer3.class, DefaultSerializerConfiguration.Type.KEY)) + .withService(new DefaultSerializerConfiguration(TestSerializer4.class, DefaultSerializerConfiguration.Type.VALUE)) .build(); CacheType cacheType = new CacheType(); @@ -87,7 +87,7 @@ public void unparseServiceConfigurationWithInstance() { DefaultSerializerConfiguration config1 = new DefaultSerializerConfiguration<>(testSerializer3, DefaultSerializerConfiguration.Type.KEY); DefaultSerializerConfiguration config2 = new DefaultSerializerConfiguration<>(testSerializer4, DefaultSerializerConfiguration.Type.VALUE); CacheConfiguration cacheConfig = newCacheConfigurationBuilder(Description.class, Person.class, heap(10)) - .add(config1).add(config2).build(); + .withService(config1).withService(config2).build(); CacheType cacheType = new CacheType(); CacheEntryType keyType = new CacheEntryType(); diff --git a/xml/src/test/java/org/ehcache/xml/service/DefaultSizeOfEngineConfigurationParserTest.java b/ehcache-xml/src/test/java/org/ehcache/xml/service/DefaultSizeOfEngineConfigurationParserTest.java similarity index 97% rename from xml/src/test/java/org/ehcache/xml/service/DefaultSizeOfEngineConfigurationParserTest.java rename to ehcache-xml/src/test/java/org/ehcache/xml/service/DefaultSizeOfEngineConfigurationParserTest.java index 023de7234d..07a4f16365 100644 --- a/xml/src/test/java/org/ehcache/xml/service/DefaultSizeOfEngineConfigurationParserTest.java +++ b/ehcache-xml/src/test/java/org/ehcache/xml/service/DefaultSizeOfEngineConfigurationParserTest.java @@ -64,7 +64,7 @@ public void parseServiceConfiguration() throws Exception { @Test public void unparseServiceConfiguration() { CacheConfiguration cacheConfig = - newCacheConfigurationBuilder(Object.class, Object.class, heap(10)).add(new DefaultSizeOfEngineConfiguration(123, MemoryUnit.MB, 987)).build(); + newCacheConfigurationBuilder(Object.class, Object.class, heap(10)).withService(new DefaultSizeOfEngineConfiguration(123, MemoryUnit.MB, 987)).build(); CacheType cacheType = new CacheType(); cacheType = new DefaultSizeOfEngineConfigurationParser().unparseServiceConfiguration(cacheConfig, cacheType); diff --git a/xml/src/test/java/org/ehcache/xml/service/DefaultWriteBehindConfigurationParserTest.java b/ehcache-xml/src/test/java/org/ehcache/xml/service/DefaultWriteBehindConfigurationParserTest.java similarity index 96% rename from xml/src/test/java/org/ehcache/xml/service/DefaultWriteBehindConfigurationParserTest.java rename to ehcache-xml/src/test/java/org/ehcache/xml/service/DefaultWriteBehindConfigurationParserTest.java index 180e37c82c..92e02443ed 100644 --- a/xml/src/test/java/org/ehcache/xml/service/DefaultWriteBehindConfigurationParserTest.java +++ b/ehcache-xml/src/test/java/org/ehcache/xml/service/DefaultWriteBehindConfigurationParserTest.java @@ -65,10 +65,10 @@ public void parseServiceConfigurationBatching() throws Exception { @Test public void unparseServiceConfigurationBatched() { - WriteBehindConfiguration writeBehindConfiguration = + WriteBehindConfiguration writeBehindConfiguration = WriteBehindConfigurationBuilder.newBatchedWriteBehindConfiguration(123, TimeUnit.SECONDS, 987) .enableCoalescing().concurrencyLevel(8).useThreadPool("foo").queueSize(16).build(); - CacheConfiguration cacheConfig = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)).add(writeBehindConfiguration).build(); + CacheConfiguration cacheConfig = newCacheConfigurationBuilder(Object.class, Object.class, heap(10)).withService(writeBehindConfiguration).build(); CacheType cacheType = new CacheType(); cacheType = new DefaultWriteBehindConfigurationParser().unparseServiceConfiguration(cacheConfig, cacheType); diff --git a/xml/src/test/java/org/ehcache/xml/service/OffHeapDiskStoreConfigurationParserTest.java b/ehcache-xml/src/test/java/org/ehcache/xml/service/OffHeapDiskStoreConfigurationParserTest.java similarity index 96% rename from xml/src/test/java/org/ehcache/xml/service/OffHeapDiskStoreConfigurationParserTest.java rename to ehcache-xml/src/test/java/org/ehcache/xml/service/OffHeapDiskStoreConfigurationParserTest.java index 2a52c13955..0cd20ce318 100644 --- a/xml/src/test/java/org/ehcache/xml/service/OffHeapDiskStoreConfigurationParserTest.java +++ b/ehcache-xml/src/test/java/org/ehcache/xml/service/OffHeapDiskStoreConfigurationParserTest.java @@ -44,7 +44,7 @@ public void parseServiceConfiguration() throws Exception { @Test public void unparseServiceConfiguration() { CacheConfiguration cacheConfig = - newCacheConfigurationBuilder(Object.class, Object.class, heap(10)).add(new OffHeapDiskStoreConfiguration("foo", 4, 8)).build(); + newCacheConfigurationBuilder(Object.class, Object.class, heap(10)).withService(new OffHeapDiskStoreConfiguration("foo", 4, 8)).build(); CacheType cacheType = new CacheType(); cacheType = new OffHeapDiskStoreConfigurationParser().unparseServiceConfiguration(cacheConfig, cacheType); diff --git a/xml/src/test/resources/META-INF/services/org.ehcache.xml.CacheManagerServiceConfigurationParser b/ehcache-xml/src/test/resources/META-INF/services/org.ehcache.xml.CacheManagerServiceConfigurationParser similarity index 100% rename from xml/src/test/resources/META-INF/services/org.ehcache.xml.CacheManagerServiceConfigurationParser rename to ehcache-xml/src/test/resources/META-INF/services/org.ehcache.xml.CacheManagerServiceConfigurationParser diff --git a/xml/src/test/resources/META-INF/services/org.ehcache.xml.CacheResourceConfigurationParser b/ehcache-xml/src/test/resources/META-INF/services/org.ehcache.xml.CacheResourceConfigurationParser similarity index 100% rename from xml/src/test/resources/META-INF/services/org.ehcache.xml.CacheResourceConfigurationParser rename to ehcache-xml/src/test/resources/META-INF/services/org.ehcache.xml.CacheResourceConfigurationParser diff --git a/xml/src/test/resources/META-INF/services/org.ehcache.xml.CacheServiceConfigurationParser b/ehcache-xml/src/test/resources/META-INF/services/org.ehcache.xml.CacheServiceConfigurationParser similarity index 100% rename from xml/src/test/resources/META-INF/services/org.ehcache.xml.CacheServiceConfigurationParser rename to ehcache-xml/src/test/resources/META-INF/services/org.ehcache.xml.CacheServiceConfigurationParser diff --git a/xml/src/test/resources/configs/all-extensions.xml b/ehcache-xml/src/test/resources/configs/all-extensions.xml similarity index 80% rename from xml/src/test/resources/configs/all-extensions.xml rename to ehcache-xml/src/test/resources/configs/all-extensions.xml index 9ede9b445f..e0c40e6ec4 100644 --- a/xml/src/test/resources/configs/all-extensions.xml +++ b/ehcache-xml/src/test/resources/configs/all-extensions.xml @@ -15,13 +15,10 @@ --> + xmlns='http://www.ehcache.org/v3'> diff --git a/xml/src/test/resources/configs/bar.xsd b/ehcache-xml/src/test/resources/configs/bar.xsd similarity index 100% rename from xml/src/test/resources/configs/bar.xsd rename to ehcache-xml/src/test/resources/configs/bar.xsd diff --git a/xml/src/test/resources/configs/baz.xsd b/ehcache-xml/src/test/resources/configs/baz.xsd similarity index 100% rename from xml/src/test/resources/configs/baz.xsd rename to ehcache-xml/src/test/resources/configs/baz.xsd diff --git a/xml/src/test/resources/configs/cache-copiers.xml b/ehcache-xml/src/test/resources/configs/cache-copiers.xml similarity index 93% rename from xml/src/test/resources/configs/cache-copiers.xml rename to ehcache-xml/src/test/resources/configs/cache-copiers.xml index a8c15d6e5a..d95315edb2 100644 --- a/xml/src/test/resources/configs/cache-copiers.xml +++ b/ehcache-xml/src/test/resources/configs/cache-copiers.xml @@ -14,9 +14,7 @@ ~ limitations under the License. --> - + com.pany.ehcache.copier.DescriptionCopier diff --git a/xml/src/test/resources/configs/cache-integration.xml b/ehcache-xml/src/test/resources/configs/cache-integration.xml similarity index 87% rename from xml/src/test/resources/configs/cache-integration.xml rename to ehcache-xml/src/test/resources/configs/cache-integration.xml index 73fdb780c8..80e8578c20 100644 --- a/xml/src/test/resources/configs/cache-integration.xml +++ b/ehcache-xml/src/test/resources/configs/cache-integration.xml @@ -14,10 +14,7 @@ ~ limitations under the License. --> - + java.lang.Number java.lang.String diff --git a/xml/src/test/resources/configs/custom-resource.xml b/ehcache-xml/src/test/resources/configs/custom-resource.xml similarity index 78% rename from xml/src/test/resources/configs/custom-resource.xml rename to ehcache-xml/src/test/resources/configs/custom-resource.xml index 08ff612e75..e35d015bb1 100644 --- a/xml/src/test/resources/configs/custom-resource.xml +++ b/ehcache-xml/src/test/resources/configs/custom-resource.xml @@ -15,11 +15,8 @@ --> + xmlns:ehcache='http://www.ehcache.org/v3'> java.lang.String java.lang.String diff --git a/xml/src/test/resources/configs/default-serializer.xml b/ehcache-xml/src/test/resources/configs/default-serializer.xml similarity index 95% rename from xml/src/test/resources/configs/default-serializer.xml rename to ehcache-xml/src/test/resources/configs/default-serializer.xml index d4110ef1c9..1b68d8c363 100644 --- a/xml/src/test/resources/configs/default-serializer.xml +++ b/ehcache-xml/src/test/resources/configs/default-serializer.xml @@ -14,9 +14,7 @@ ~ limitations under the License. --> - + com.pany.ehcache.serializer.TestSerializer diff --git a/xml/src/test/resources/configs/defaultTypes-cache.xml b/ehcache-xml/src/test/resources/configs/defaultTypes-cache.xml similarity index 81% rename from xml/src/test/resources/configs/defaultTypes-cache.xml rename to ehcache-xml/src/test/resources/configs/defaultTypes-cache.xml index ebd36b4c93..02c668a2fb 100644 --- a/xml/src/test/resources/configs/defaultTypes-cache.xml +++ b/ehcache-xml/src/test/resources/configs/defaultTypes-cache.xml @@ -14,10 +14,7 @@ ~ limitations under the License. --> - + diff --git a/xml/src/test/resources/configs/disk-persistent-cache.xml b/ehcache-xml/src/test/resources/configs/disk-persistent-cache.xml similarity index 84% rename from xml/src/test/resources/configs/disk-persistent-cache.xml rename to ehcache-xml/src/test/resources/configs/disk-persistent-cache.xml index 9aa45133e1..7a4f4a4003 100644 --- a/xml/src/test/resources/configs/disk-persistent-cache.xml +++ b/ehcache-xml/src/test/resources/configs/disk-persistent-cache.xml @@ -14,10 +14,7 @@ ~ limitations under the License. --> - + diff --git a/xml/src/test/resources/configs/docs/expiry.xml b/ehcache-xml/src/test/resources/configs/docs/expiry.xml similarity index 83% rename from xml/src/test/resources/configs/docs/expiry.xml rename to ehcache-xml/src/test/resources/configs/docs/expiry.xml index 335745c9e7..93b5b98c9d 100644 --- a/xml/src/test/resources/configs/docs/expiry.xml +++ b/ehcache-xml/src/test/resources/configs/docs/expiry.xml @@ -14,10 +14,7 @@ ~ limitations under the License. --> - + diff --git a/xml/src/test/resources/configs/docs/getting-started.xml b/ehcache-xml/src/test/resources/configs/docs/getting-started.xml similarity index 86% rename from xml/src/test/resources/configs/docs/getting-started.xml rename to ehcache-xml/src/test/resources/configs/docs/getting-started.xml index 6850b517cc..9000aefcd5 100644 --- a/xml/src/test/resources/configs/docs/getting-started.xml +++ b/ehcache-xml/src/test/resources/configs/docs/getting-started.xml @@ -14,10 +14,7 @@ ~ limitations under the License. --> - + java.lang.String diff --git a/xml/src/test/resources/configs/docs/multi/multiple-managers.xml b/ehcache-xml/src/test/resources/configs/docs/multi/multiple-managers.xml similarity index 73% rename from xml/src/test/resources/configs/docs/multi/multiple-managers.xml rename to ehcache-xml/src/test/resources/configs/docs/multi/multiple-managers.xml index bf16bbd5d9..e7bd481a6f 100644 --- a/xml/src/test/resources/configs/docs/multi/multiple-managers.xml +++ b/ehcache-xml/src/test/resources/configs/docs/multi/multiple-managers.xml @@ -1,9 +1,6 @@ + xmlns:multi='http://www.ehcache.org/v3/multi'> diff --git a/xml/src/test/resources/configs/docs/multi/multiple-variants.xml b/ehcache-xml/src/test/resources/configs/docs/multi/multiple-variants.xml similarity index 81% rename from xml/src/test/resources/configs/docs/multi/multiple-variants.xml rename to ehcache-xml/src/test/resources/configs/docs/multi/multiple-variants.xml index 8191a908e4..e04b3c858e 100644 --- a/xml/src/test/resources/configs/docs/multi/multiple-variants.xml +++ b/ehcache-xml/src/test/resources/configs/docs/multi/multiple-variants.xml @@ -1,9 +1,6 @@ + xmlns:multi='http://www.ehcache.org/v3/multi'> diff --git a/xml/src/test/resources/configs/docs/template-sample.xml b/ehcache-xml/src/test/resources/configs/docs/template-sample.xml similarity index 81% rename from xml/src/test/resources/configs/docs/template-sample.xml rename to ehcache-xml/src/test/resources/configs/docs/template-sample.xml index 75bfcae4dd..5248231cee 100644 --- a/xml/src/test/resources/configs/docs/template-sample.xml +++ b/ehcache-xml/src/test/resources/configs/docs/template-sample.xml @@ -14,10 +14,7 @@ ~ limitations under the License. --> - + diff --git a/xml/src/test/resources/configs/docs/thread-pools.xml b/ehcache-xml/src/test/resources/configs/docs/thread-pools.xml similarity index 90% rename from xml/src/test/resources/configs/docs/thread-pools.xml rename to ehcache-xml/src/test/resources/configs/docs/thread-pools.xml index 5d0ad14009..7e6a5dd133 100644 --- a/xml/src/test/resources/configs/docs/thread-pools.xml +++ b/ehcache-xml/src/test/resources/configs/docs/thread-pools.xml @@ -14,10 +14,7 @@ ~ limitations under the License. --> - + diff --git a/xml/src/test/resources/configs/ehcache-cacheEventListener.xml b/ehcache-xml/src/test/resources/configs/ehcache-cacheEventListener.xml similarity index 92% rename from xml/src/test/resources/configs/ehcache-cacheEventListener.xml rename to ehcache-xml/src/test/resources/configs/ehcache-cacheEventListener.xml index 3d7e354971..ec46c1c18f 100644 --- a/xml/src/test/resources/configs/ehcache-cacheEventListener.xml +++ b/ehcache-xml/src/test/resources/configs/ehcache-cacheEventListener.xml @@ -14,10 +14,7 @@ ~ limitations under the License. --> - + diff --git a/xml/src/test/resources/configs/ehcache-complete.xml b/ehcache-xml/src/test/resources/configs/ehcache-complete.xml similarity index 95% rename from xml/src/test/resources/configs/ehcache-complete.xml rename to ehcache-xml/src/test/resources/configs/ehcache-complete.xml index cdab276c61..3744479b4d 100644 --- a/xml/src/test/resources/configs/ehcache-complete.xml +++ b/ehcache-xml/src/test/resources/configs/ehcache-complete.xml @@ -15,10 +15,7 @@ ~ limitations under the License. --> - + diff --git a/xml/src/test/resources/configs/ehcache-multipleCacheEventListener.xml b/ehcache-xml/src/test/resources/configs/ehcache-multipleCacheEventListener.xml similarity index 90% rename from xml/src/test/resources/configs/ehcache-multipleCacheEventListener.xml rename to ehcache-xml/src/test/resources/configs/ehcache-multipleCacheEventListener.xml index 5a8b4fdd10..c18819a6fc 100644 --- a/xml/src/test/resources/configs/ehcache-multipleCacheEventListener.xml +++ b/ehcache-xml/src/test/resources/configs/ehcache-multipleCacheEventListener.xml @@ -14,10 +14,7 @@ ~ limitations under the License. --> - + java.lang.Number diff --git a/ehcache-xml/src/test/resources/configs/ehcache-system-props.xml b/ehcache-xml/src/test/resources/configs/ehcache-system-props.xml new file mode 100644 index 0000000000..152607a540 --- /dev/null +++ b/ehcache-xml/src/test/resources/configs/ehcache-system-props.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + com.pany.ehcache.copier.Description + com.pany.ehcache.copier.Person + + ${ehcache.expiry.ttl} + + + com.pany.ehcache.integration.TestCacheLoaderWriter + + + ${ehcache.loader-writer.write-behind.batching.max-write-delay} + + + + + ${ehcache.resources.heap} + ${ehcache.resources.offheap} + ${ehcache.resources.disk} + + + + + + java.lang.Long + java.lang.String + + ${ehcache.expiry.tti} + + 4096 + + diff --git a/xml/src/test/resources/configs/expiry-caches.xml b/ehcache-xml/src/test/resources/configs/expiry-caches.xml similarity index 91% rename from xml/src/test/resources/configs/expiry-caches.xml rename to ehcache-xml/src/test/resources/configs/expiry-caches.xml index 84b3771ed7..629d0fbc57 100644 --- a/xml/src/test/resources/configs/expiry-caches.xml +++ b/ehcache-xml/src/test/resources/configs/expiry-caches.xml @@ -14,10 +14,7 @@ ~ limitations under the License. --> - + java.lang.String diff --git a/xml/src/test/resources/configs/fancy.xsd b/ehcache-xml/src/test/resources/configs/fancy.xsd similarity index 100% rename from xml/src/test/resources/configs/fancy.xsd rename to ehcache-xml/src/test/resources/configs/fancy.xsd diff --git a/xml/src/test/resources/configs/foo.xsd b/ehcache-xml/src/test/resources/configs/foo.xsd similarity index 100% rename from xml/src/test/resources/configs/foo.xsd rename to ehcache-xml/src/test/resources/configs/foo.xsd diff --git a/xml/src/test/resources/configs/invalid-core.xml b/ehcache-xml/src/test/resources/configs/invalid-core.xml similarity index 84% rename from xml/src/test/resources/configs/invalid-core.xml rename to ehcache-xml/src/test/resources/configs/invalid-core.xml index becc7e392f..35b913aa1d 100644 --- a/xml/src/test/resources/configs/invalid-core.xml +++ b/ehcache-xml/src/test/resources/configs/invalid-core.xml @@ -1,5 +1,4 @@ diff --git a/xml/src/test/resources/configs/invalid-service.xml b/ehcache-xml/src/test/resources/configs/invalid-service.xml similarity index 75% rename from xml/src/test/resources/configs/invalid-service.xml rename to ehcache-xml/src/test/resources/configs/invalid-service.xml index 7fad4a9256..1372dc9511 100644 --- a/xml/src/test/resources/configs/invalid-service.xml +++ b/ehcache-xml/src/test/resources/configs/invalid-service.xml @@ -1,5 +1,4 @@ diff --git a/ehcache-xml/src/test/resources/configs/invalid-two-caches.xml b/ehcache-xml/src/test/resources/configs/invalid-two-caches.xml new file mode 100644 index 0000000000..b9dc49818c --- /dev/null +++ b/ehcache-xml/src/test/resources/configs/invalid-two-caches.xml @@ -0,0 +1,11 @@ + + + + 2000 + + + + 2000 + + + diff --git a/ehcache-xml/src/test/resources/configs/multi/empty.xml b/ehcache-xml/src/test/resources/configs/multi/empty.xml new file mode 100644 index 0000000000..0f2027efef --- /dev/null +++ b/ehcache-xml/src/test/resources/configs/multi/empty.xml @@ -0,0 +1,2 @@ + + diff --git a/xml/src/test/resources/configs/multi/extended.xml b/ehcache-xml/src/test/resources/configs/multi/extended.xml similarity index 92% rename from xml/src/test/resources/configs/multi/extended.xml rename to ehcache-xml/src/test/resources/configs/multi/extended.xml index 4968bb5621..81a028949c 100644 --- a/xml/src/test/resources/configs/multi/extended.xml +++ b/ehcache-xml/src/test/resources/configs/multi/extended.xml @@ -1,5 +1,4 @@ diff --git a/xml/src/test/resources/configs/multi/multiple-variants.xml b/ehcache-xml/src/test/resources/configs/multi/multiple-variants.xml similarity index 95% rename from xml/src/test/resources/configs/multi/multiple-variants.xml rename to ehcache-xml/src/test/resources/configs/multi/multiple-variants.xml index f13baa93b2..c2ba086e56 100644 --- a/xml/src/test/resources/configs/multi/multiple-variants.xml +++ b/ehcache-xml/src/test/resources/configs/multi/multiple-variants.xml @@ -1,6 +1,5 @@ diff --git a/xml/src/test/resources/configs/nonExistentAdvisor-cache.xml b/ehcache-xml/src/test/resources/configs/nonExistentAdvisor-cache.xml similarity index 79% rename from xml/src/test/resources/configs/nonExistentAdvisor-cache.xml rename to ehcache-xml/src/test/resources/configs/nonExistentAdvisor-cache.xml index 52ad6e1c24..30a767611a 100644 --- a/xml/src/test/resources/configs/nonExistentAdvisor-cache.xml +++ b/ehcache-xml/src/test/resources/configs/nonExistentAdvisor-cache.xml @@ -14,10 +14,7 @@ ~ limitations under the License. --> - + com.foo.NonExistentAdvisorInCache diff --git a/xml/src/test/resources/configs/nonExistentAdvisor-template.xml b/ehcache-xml/src/test/resources/configs/nonExistentAdvisor-template.xml similarity index 80% rename from xml/src/test/resources/configs/nonExistentAdvisor-template.xml rename to ehcache-xml/src/test/resources/configs/nonExistentAdvisor-template.xml index 84be19ae24..74abaeed8b 100644 --- a/xml/src/test/resources/configs/nonExistentAdvisor-template.xml +++ b/ehcache-xml/src/test/resources/configs/nonExistentAdvisor-template.xml @@ -14,10 +14,7 @@ ~ limitations under the License. --> - + com.foo.NonExistentAdvisorInTemplate diff --git a/xml/src/test/resources/configs/one-cache.xml b/ehcache-xml/src/test/resources/configs/one-cache.xml similarity index 78% rename from xml/src/test/resources/configs/one-cache.xml rename to ehcache-xml/src/test/resources/configs/one-cache.xml index 2d4d64eb61..98091e81f8 100644 --- a/xml/src/test/resources/configs/one-cache.xml +++ b/ehcache-xml/src/test/resources/configs/one-cache.xml @@ -15,11 +15,8 @@ --> + xmlns:ehcache='http://www.ehcache.org/v3'> java.lang.String java.lang.String diff --git a/xml/src/test/resources/configs/one-service.xml b/ehcache-xml/src/test/resources/configs/one-service.xml similarity index 75% rename from xml/src/test/resources/configs/one-service.xml rename to ehcache-xml/src/test/resources/configs/one-service.xml index ab9e7028bd..fda8541468 100644 --- a/xml/src/test/resources/configs/one-service.xml +++ b/ehcache-xml/src/test/resources/configs/one-service.xml @@ -15,11 +15,8 @@ --> + xmlns:ehcache='http://www.ehcache.org/v3'> diff --git a/ehcache-xml/src/test/resources/configs/persistence-config.xml b/ehcache-xml/src/test/resources/configs/persistence-config.xml new file mode 100644 index 0000000000..bbba65a64b --- /dev/null +++ b/ehcache-xml/src/test/resources/configs/persistence-config.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/ehcache-xml/src/test/resources/configs/pretty-typed-caches.xml b/ehcache-xml/src/test/resources/configs/pretty-typed-caches.xml new file mode 100644 index 0000000000..583b53dce2 --- /dev/null +++ b/ehcache-xml/src/test/resources/configs/pretty-typed-caches.xml @@ -0,0 +1,42 @@ + + + + + + java.lang.Integer + byte[] + 5 + + + + java.lang.String + java.lang.String[] + 5 + + + + java.lang.String + java.lang.String[][] + 5 + + + + java.lang.String + java.util.Map.Entry + 5 + + diff --git a/xml/src/test/resources/configs/resilience-config.xml b/ehcache-xml/src/test/resources/configs/resilience-config.xml similarity index 86% rename from xml/src/test/resources/configs/resilience-config.xml rename to ehcache-xml/src/test/resources/configs/resilience-config.xml index 092eb30243..3902b2ab62 100644 --- a/xml/src/test/resources/configs/resilience-config.xml +++ b/ehcache-xml/src/test/resources/configs/resilience-config.xml @@ -14,10 +14,7 @@ ~ limitations under the License. --> - + java.lang.Number diff --git a/xml/src/test/resources/configs/resources-caches.xml b/ehcache-xml/src/test/resources/configs/resources-caches.xml similarity index 91% rename from xml/src/test/resources/configs/resources-caches.xml rename to ehcache-xml/src/test/resources/configs/resources-caches.xml index 1fd65d4ed3..5a81099b44 100644 --- a/xml/src/test/resources/configs/resources-caches.xml +++ b/ehcache-xml/src/test/resources/configs/resources-caches.xml @@ -14,10 +14,7 @@ ~ limitations under the License. --> - + diff --git a/xml/src/test/resources/configs/resources-templates.xml b/ehcache-xml/src/test/resources/configs/resources-templates.xml similarity index 92% rename from xml/src/test/resources/configs/resources-templates.xml rename to ehcache-xml/src/test/resources/configs/resources-templates.xml index 22d67c644b..2369fbf895 100644 --- a/xml/src/test/resources/configs/resources-templates.xml +++ b/ehcache-xml/src/test/resources/configs/resources-templates.xml @@ -14,10 +14,7 @@ ~ limitations under the License. --> - + java.lang.String diff --git a/xml/src/test/resources/configs/sizeof-engine-cm-defaults-one.xml b/ehcache-xml/src/test/resources/configs/sizeof-engine-cm-defaults-one.xml similarity index 84% rename from xml/src/test/resources/configs/sizeof-engine-cm-defaults-one.xml rename to ehcache-xml/src/test/resources/configs/sizeof-engine-cm-defaults-one.xml index c8aa2fc11d..d78453284a 100644 --- a/xml/src/test/resources/configs/sizeof-engine-cm-defaults-one.xml +++ b/ehcache-xml/src/test/resources/configs/sizeof-engine-cm-defaults-one.xml @@ -13,10 +13,7 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - + 100000 diff --git a/xml/src/test/resources/configs/sizeof-engine-cm-defaults-two.xml b/ehcache-xml/src/test/resources/configs/sizeof-engine-cm-defaults-two.xml similarity index 84% rename from xml/src/test/resources/configs/sizeof-engine-cm-defaults-two.xml rename to ehcache-xml/src/test/resources/configs/sizeof-engine-cm-defaults-two.xml index 1cf04f67ea..a4fbb7517a 100644 --- a/xml/src/test/resources/configs/sizeof-engine-cm-defaults-two.xml +++ b/ehcache-xml/src/test/resources/configs/sizeof-engine-cm-defaults-two.xml @@ -13,10 +13,7 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - + 200 diff --git a/xml/src/test/resources/configs/sizeof-engine.xml b/ehcache-xml/src/test/resources/configs/sizeof-engine.xml similarity index 92% rename from xml/src/test/resources/configs/sizeof-engine.xml rename to ehcache-xml/src/test/resources/configs/sizeof-engine.xml index a8341f0818..95aaa398e9 100644 --- a/xml/src/test/resources/configs/sizeof-engine.xml +++ b/ehcache-xml/src/test/resources/configs/sizeof-engine.xml @@ -13,10 +13,7 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - + 200 diff --git a/xml/src/test/resources/configs/template-cache.xml b/ehcache-xml/src/test/resources/configs/template-cache.xml similarity index 81% rename from xml/src/test/resources/configs/template-cache.xml rename to ehcache-xml/src/test/resources/configs/template-cache.xml index 086470eaf6..38bab168b4 100644 --- a/xml/src/test/resources/configs/template-cache.xml +++ b/ehcache-xml/src/test/resources/configs/template-cache.xml @@ -15,11 +15,8 @@ --> + xmlns:ehcache='http://www.ehcache.org/v3'> java.lang.String diff --git a/xml/src/test/resources/configs/template-defaults.xml b/ehcache-xml/src/test/resources/configs/template-defaults.xml similarity index 69% rename from xml/src/test/resources/configs/template-defaults.xml rename to ehcache-xml/src/test/resources/configs/template-defaults.xml index 6cd5e5a209..62cb319745 100644 --- a/xml/src/test/resources/configs/template-defaults.xml +++ b/ehcache-xml/src/test/resources/configs/template-defaults.xml @@ -14,12 +14,7 @@ ~ limitations under the License. --> - + diff --git a/xml/src/test/resources/configs/thread-pools.xml b/ehcache-xml/src/test/resources/configs/thread-pools.xml similarity index 87% rename from xml/src/test/resources/configs/thread-pools.xml rename to ehcache-xml/src/test/resources/configs/thread-pools.xml index 60bba2ac6e..00376dbb6f 100644 --- a/xml/src/test/resources/configs/thread-pools.xml +++ b/ehcache-xml/src/test/resources/configs/thread-pools.xml @@ -14,9 +14,7 @@ ~ limitations under the License. --> - + diff --git a/ehcache-xml/src/test/resources/configs/unknown-resource.xml b/ehcache-xml/src/test/resources/configs/unknown-resource.xml new file mode 100644 index 0000000000..bf94d680ba --- /dev/null +++ b/ehcache-xml/src/test/resources/configs/unknown-resource.xml @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/ehcache-xml/src/test/resources/configs/unknown-service-creation.xml b/ehcache-xml/src/test/resources/configs/unknown-service-creation.xml new file mode 100644 index 0000000000..254a6dec2b --- /dev/null +++ b/ehcache-xml/src/test/resources/configs/unknown-service-creation.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/ehcache-xml/src/test/resources/configs/unknown-service.xml b/ehcache-xml/src/test/resources/configs/unknown-service.xml new file mode 100644 index 0000000000..2873544bcf --- /dev/null +++ b/ehcache-xml/src/test/resources/configs/unknown-service.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/xml/src/test/resources/configs/writebehind-cache.xml b/ehcache-xml/src/test/resources/configs/writebehind-cache.xml similarity index 90% rename from xml/src/test/resources/configs/writebehind-cache.xml rename to ehcache-xml/src/test/resources/configs/writebehind-cache.xml index 85b7459e85..68925f1e53 100644 --- a/xml/src/test/resources/configs/writebehind-cache.xml +++ b/ehcache-xml/src/test/resources/configs/writebehind-cache.xml @@ -14,10 +14,7 @@ ~ limitations under the License. --> - + diff --git a/ehcache-xml/src/testFixtures/java/org/ehcache/xml/XmlConfigurationMatchers.java b/ehcache-xml/src/testFixtures/java/org/ehcache/xml/XmlConfigurationMatchers.java new file mode 100644 index 0000000000..318ee81ca7 --- /dev/null +++ b/ehcache-xml/src/testFixtures/java/org/ehcache/xml/XmlConfigurationMatchers.java @@ -0,0 +1,57 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehcache.xml; + +import org.xmlunit.diff.DefaultNodeMatcher; +import org.xmlunit.diff.ElementSelector; +import org.xmlunit.matchers.CompareMatcher; +import org.xmlunit.util.Nodes; + +import javax.xml.namespace.QName; + +import java.util.ArrayList; +import java.util.List; + +import static java.util.Arrays.asList; +import static org.xmlunit.diff.ElementSelectors.Default; +import static org.xmlunit.diff.ElementSelectors.byName; +import static org.xmlunit.diff.ElementSelectors.byNameAndAttributes; +import static org.xmlunit.diff.ElementSelectors.byNameAndText; +import static org.xmlunit.diff.ElementSelectors.conditionalSelector; +import static org.xmlunit.diff.ElementSelectors.selectorForElementNamed; + +public class XmlConfigurationMatchers { + + private static final String EHCACHE_NAMESPACE = "http://www.ehcache.org/v3"; + private static final QName CACHE_QNAME = new QName(EHCACHE_NAMESPACE, "cache"); + private static final QName RESOURCES_QNAME = new QName(EHCACHE_NAMESPACE, "resources"); + private static final QName EVENTS_TO_FIRE_ON_QNAME = new QName(EHCACHE_NAMESPACE, "events-to-fire-on"); + + private static final String MULTI_NAMESPACE = "http://www.ehcache.org/v3/multi"; + private static final QName MULTI_CONFIGURATION_QNAME = new QName(MULTI_NAMESPACE, "configuration"); + + public static CompareMatcher isSameConfigurationAs(Object input, ElementSelector... extraElementSelectors) { + List elementSelectors = new ArrayList<>(asList(extraElementSelectors)); + elementSelectors.add(selectorForElementNamed(MULTI_CONFIGURATION_QNAME, byNameAndAttributes("identity"))); + elementSelectors.add(selectorForElementNamed(EVENTS_TO_FIRE_ON_QNAME, byNameAndText)); + elementSelectors.add(selectorForElementNamed(CACHE_QNAME, byNameAndAttributes("alias"))); + elementSelectors.add(conditionalSelector(element -> Nodes.getQName(element.getParentNode()).equals(RESOURCES_QNAME), byName)); + elementSelectors.add(Default); + + return CompareMatcher.isSimilarTo(input).ignoreComments().ignoreWhitespace() + .withNodeMatcher(new DefaultNodeMatcher(elementSelectors.toArray(new ElementSelector[0]))); + } +} diff --git a/ehcache/build.gradle b/ehcache/build.gradle new file mode 100644 index 0000000000..ef5a4436b9 --- /dev/null +++ b/ehcache/build.gradle @@ -0,0 +1,96 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +import aQute.bnd.osgi.Constants + +import static org.gradle.api.attributes.Bundling.EXTERNAL +import static org.gradle.api.attributes.Category.DOCUMENTATION +import static org.gradle.api.attributes.Usage.JAVA_RUNTIME + +plugins { + id 'org.ehcache.build.package' +} + +publishing.publications.withType(MavenPublication) { + pom { + name = 'Ehcache' + description = 'End-user ehcache3 jar artifact' + } +} + +configurations { + contents { + exclude group:'org.glassfish.jaxb' + exclude group:'org.slf4j' + exclude group:'javax.cache' + exclude group:'javax.xml.bind' + } +} + +dependencies { + contents project(':ehcache-api') + contents project(':ehcache-core') + contents project(':ehcache-impl') + contents project(':ehcache-107') + contents project(':ehcache-xml') + + api "javax.cache:cache-api:$parent.jcacheVersion" + implementation "org.slf4j:slf4j-api:$parent.slf4jVersion" + runtimeOnly 'org.glassfish.jaxb:jaxb-runtime:[2.2,3)' +} + +tasks.named('jar') { + osgi { + instruction Constants.BUNDLE_NAME, 'Ehcache 3' + instruction Constants.BUNDLE_SYMBOLICNAME, 'org.ehcache' + instruction Constants.BUNDLE_DESCRIPTION, 'Ehcache is an open-source caching library, compliant with the JSR-107 standard.' + instruction Constants.BUNDLE_ACTIVATOR, 'org.ehcache.core.osgi.EhcacheActivator' + instruction Constants.EXPORT_PACKAGE, '!org.ehcache.jsr107.tck, !org.ehcache.*.internal.*, org.ehcache.*' + instruction Constants.IMPORT_PACKAGE, 'javax.cache.*;resolution:=optional, !javax.annotation, !sun.misc, javax.xml.bind*;version="[2.2,3)", *' + } +} + +tasks.withType(Javadoc).matching({ name.equals('spiJavadoc') }).configureEach { + exclude '**/core/**', '**/impl/**', '**/xml/**', '**/jsr107/**', '**/transactions/**', '**/management/**', '**/tck/**' +} + +TaskProvider spiJavadoc = tasks.register('spiJavadoc', Javadoc) { + title = "$project.archivesBaseName $project.version API & SPI"; + source = tasks.javadoc.source + setClasspath(tasks.javadoc.classpath) + exclude "**/internal/**" + setDestinationDir project.file("$project.buildDir/docs/spi-javadoc") +} + +TaskProvider spiJavadocJar = project.getTasks().register('spiJavadocJar', Jar) { + from(spiJavadoc); + getArchiveClassifier().set("spi-javadoc"); +} +Configuration spiJavadocElements = project.getConfigurations().create("spiJavadocElements", config -> { + config.setDescription("javadoc elements for SPI documentation."); + config.attributes(attributes -> { + attributes.attribute(Usage.USAGE_ATTRIBUTE, project.getObjects().named(Usage.class, JAVA_RUNTIME)); + attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.getObjects().named(Category.class, DOCUMENTATION)); + attributes.attribute(Bundling.BUNDLING_ATTRIBUTE, project.getObjects().named(Bundling.class, EXTERNAL)); + attributes.attribute(DocsType.DOCS_TYPE_ATTRIBUTE, project.getObjects().named(DocsType.class, "spi-javadoc")); + }); + config.getOutgoing().artifact(spiJavadocJar); +}); + +components.named('java', AdhocComponentWithVariants) { + addVariantsFromConfiguration(spiJavadocElements) {} +} diff --git a/ehcache/templates/github-release-issue.md b/ehcache/templates/github-release-issue.md new file mode 100644 index 0000000000..3abcc705c2 --- /dev/null +++ b/ehcache/templates/github-release-issue.md @@ -0,0 +1,11 @@ +- [ ] Tag release +- [ ] Build and verify release + - includes checking javadoc and source jars +- [ ] Prepare release on GitHub +- [ ] Prepare website update + - includes checking XSDs +- [ ] Publish jars to Maven Central +- [ ] Publish release on GitHub +- [ ] Publish website +- [ ] Update readme / bump version on release branch +- [ ] Email announcement diff --git a/ehcache/templates/github-release.md b/ehcache/templates/github-release.md new file mode 100644 index 0000000000..c4b3a17152 --- /dev/null +++ b/ehcache/templates/github-release.md @@ -0,0 +1,48 @@ +## Getting started + +=> Please add what this release is about here + +As usual, it contains numerous [bug fixes and enhancements](https://github.com/ehcache/ehcache3/milestone/%MILESTONE%?closed=1). + +Ehcache ${version} has been released to maven central under the following coordinates: + +### Main module + +``` xml + + org.ehcache + ehcache + %VERSION% + +``` + +### Transactions module + +``` xml + + org.ehcache + ehcache-transactions + %VERSION% + +``` + +### Clustering module + +``` xml + + org.ehcache + ehcache-clustered + %VERSION% + +``` + +Or can be downloaded below. +Note that if you download Ehcache jar you will need one additional jar in your classpath: +- [slf4j-api-1.7.25.jar](http://search.maven.org/#artifactdetails%7Corg.slf4j%7Cslf4j-api%7C1.7.25%7Cjar) + +## Clustering kit + +For clustering a kit is also provided that includes the Terracotta Server component. See below. + +## Further reading +- [Ehcache 3 documentation](http://www.ehcache.org/documentation/%MAJORVERSION%/) diff --git a/gradle.properties b/gradle.properties index f058c020a8..4fa2259dcb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,32 +1,28 @@ # Ehcache version -ehcacheVersion = 3.6-SNAPSHOT +ehcacheVersion = 3.10-SNAPSHOT # Terracotta third parties -offheapVersion = 2.4.0 +offheapVersion = 2.5.2 statisticVersion = 2.1 jcacheVersion = 1.1.0 slf4jVersion = 1.7.25 -sizeofVersion = 0.3.0 +sizeofVersion = 0.4.0 # Terracotta clustered -terracottaPlatformVersion = 5.5.1 -terracottaApisVersion = 1.5.1 -terracottaCoreVersion = 5.5.1 -terracottaPassthroughTestingVersion = 1.5.1 +terracottaPlatformVersion = 5.8.9-pre5 +terracottaApisVersion = 1.8.1 +terracottaCoreVersion = 5.8.4 +terracottaPassthroughTestingVersion = 1.8.2 +terracottaUtilitiesVersion = 0.0.9 # Test lib versions -junitVersion = 4.12 +junitVersion = 4.13.1 assertjVersion = 3.9.0 hamcrestVersion = 1.3 -mockitoVersion = 2.13.0 -jacksonVersion = 2.7.5 +mockitoVersion = 2.23.4 +jacksonVersion = 2.12.4 jcacheTckVersion = 1.1.0 -# Tools -spotbugsVersion = 3.1.3 -checkstyleVersion = 5.9 -jacocoVersion = 0.8.1 - sonatypeUser = OVERRIDE_ME sonatypePwd = OVERRIDE_ME diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 1353677005..cc4fdc293d 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 4e974715fd..a0f7639f7d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index cccdd3d517..2fe81a7d95 100755 --- a/gradlew +++ b/gradlew @@ -1,5 +1,21 @@ #!/usr/bin/env sh +# +# Copyright 2015 the original author or authors. +# +# 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 +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + ############################################################################## ## ## Gradle start up script for UN*X @@ -28,7 +44,7 @@ APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -109,8 +125,8 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` @@ -138,19 +154,19 @@ if $cygwin ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi @@ -159,14 +175,9 @@ save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } -APP_ARGS=$(save "$@") +APP_ARGS=`save "$@"` # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index f9553162f1..9618d8d960 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome diff --git a/impl/.gitignore b/impl/.gitignore deleted file mode 100755 index ae3c172604..0000000000 --- a/impl/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/bin/ diff --git a/impl/build.gradle b/impl/build.gradle deleted file mode 100644 index 7d56bd2513..0000000000 --- a/impl/build.gradle +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -apply plugin: EhDeploy - -dependencies { - api project(':core') - implementation group: 'org.terracotta', name: 'offheap-store', version: parent.offheapVersion - implementation (group: 'org.ehcache', name: 'sizeof', version: parent.sizeofVersion) { - exclude group:'org.slf4j', module:'slf4j-api' - } - implementation ("org.terracotta:statistics:$parent.statisticVersion") - testImplementation project(':core-spi-test') - testImplementation 'org.ow2.asm:asm:6.2' - testImplementation 'org.ow2.asm:asm-commons:6.2' -} - -jar { - from "$rootDir/NOTICE" -} - -compileJava { - //no -Werror due to unsafe - options.compilerArgs = ['-Xlint:all'] -} diff --git a/impl/gradle.properties b/impl/gradle.properties deleted file mode 100644 index a89604d2bc..0000000000 --- a/impl/gradle.properties +++ /dev/null @@ -1,4 +0,0 @@ -subPomName = Ehcache 3 Implementation module -subPomDesc = The implementation module of Ehcache 3 -osgi = {"Export-Package" : ["!org.terracotta.offheapstore.*", "!org.ehcache.impl.internal.*"],\ - "Import-Package" : ["!sun.misc.*", "!sun.security.action.*"]} diff --git a/impl/src/main/java/org/ehcache/config/builders/ConfigurationBuilder.java b/impl/src/main/java/org/ehcache/config/builders/ConfigurationBuilder.java deleted file mode 100644 index d4bafacc76..0000000000 --- a/impl/src/main/java/org/ehcache/config/builders/ConfigurationBuilder.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ehcache.config.builders; - -import org.ehcache.config.Builder; -import org.ehcache.config.CacheConfiguration; -import org.ehcache.config.Configuration; -import org.ehcache.core.config.DefaultConfiguration; -import org.ehcache.spi.service.ServiceCreationConfiguration; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static java.util.Collections.emptyList; -import static java.util.Collections.emptyMap; -import static java.util.Collections.unmodifiableList; -import static java.util.Collections.unmodifiableMap; - -/** - * Companion type to the {@link CacheManagerBuilder} that handles the {@link Configuration} building. - * - * @author Alex Snaps - */ -public class ConfigurationBuilder implements Builder { - - private final Map> caches; - private final List> serviceConfigurations; - private final ClassLoader classLoader; - - public static ConfigurationBuilder newConfigurationBuilder() { - return new ConfigurationBuilder(); - } - - private ConfigurationBuilder() { - this.caches = emptyMap(); - this.serviceConfigurations = emptyList(); - this.classLoader = null; - } - - private ConfigurationBuilder(ConfigurationBuilder builder, Map> caches) { - this.caches = unmodifiableMap(caches); - this.serviceConfigurations = builder.serviceConfigurations; - this.classLoader = builder.classLoader; - } - - private ConfigurationBuilder(ConfigurationBuilder builder, List> serviceConfigurations) { - this.caches = builder.caches; - this.serviceConfigurations = unmodifiableList(serviceConfigurations); - this.classLoader = builder.classLoader; - } - - private ConfigurationBuilder(ConfigurationBuilder builder, ClassLoader classLoader) { - this.caches = builder.caches; - this.serviceConfigurations = builder.serviceConfigurations; - this.classLoader = classLoader; - } - - @Override - public Configuration build() { - return new DefaultConfiguration(caches, classLoader, serviceConfigurations.toArray(new ServiceCreationConfiguration[serviceConfigurations.size()])); - } - - public ConfigurationBuilder addCache(String alias, CacheConfiguration config) { - Map> newCaches = new HashMap<>(caches); - if(newCaches.put(alias, config) != null) { - throw new IllegalArgumentException("Cache alias '" + alias + "' already exists"); - } - return new ConfigurationBuilder(this, newCaches); - } - - public ConfigurationBuilder removeCache(String alias) { - Map> newCaches = new HashMap<>(caches); - newCaches.remove(alias); - return new ConfigurationBuilder(this, newCaches); - } - - public ConfigurationBuilder addService(ServiceCreationConfiguration serviceConfiguration) { - if (findServiceByClass(serviceConfiguration.getClass()) != null) { - throw new IllegalArgumentException("There is already a ServiceCreationConfiguration registered for service " + serviceConfiguration - .getServiceType() + " of type " + serviceConfiguration.getClass()); - } - List> newServiceConfigurations = new ArrayList<>(serviceConfigurations); - newServiceConfigurations.add(serviceConfiguration); - return new ConfigurationBuilder(this, newServiceConfigurations); - } - - T findServiceByClass(Class type) { - for (ServiceCreationConfiguration serviceConfiguration : serviceConfigurations) { - if (serviceConfiguration.getClass().equals(type)) { - return type.cast(serviceConfiguration); - } - } - return null; - } - - public ConfigurationBuilder removeService(ServiceCreationConfiguration serviceConfiguration) { - List> newServiceConfigurations = new ArrayList<>(serviceConfigurations); - newServiceConfigurations.remove(serviceConfiguration); - return new ConfigurationBuilder(this, newServiceConfigurations); - } - - public ConfigurationBuilder withClassLoader(ClassLoader classLoader) { - return new ConfigurationBuilder(this, classLoader); - } - - public boolean containsCache(String alias) { - return caches.containsKey(alias); - } -} diff --git a/impl/src/main/java/org/ehcache/config/builders/ExpiryPolicyBuilder.java b/impl/src/main/java/org/ehcache/config/builders/ExpiryPolicyBuilder.java deleted file mode 100644 index 75c289b179..0000000000 --- a/impl/src/main/java/org/ehcache/config/builders/ExpiryPolicyBuilder.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ehcache.config.builders; - -import org.ehcache.config.Builder; -import org.ehcache.expiry.ExpiryPolicy; - -import java.time.Duration; -import java.util.Objects; -import java.util.function.Supplier; - -/** - * Builder and utilities for getting predefined {@link ExpiryPolicy} instances. - */ -public final class ExpiryPolicyBuilder implements Builder>{ - - /** - * Get an {@link ExpiryPolicy} instance for a non expiring (ie. "eternal") cache. - * - * @return the no expiry instance - */ - public static ExpiryPolicy noExpiration() { - return ExpiryPolicy.NO_EXPIRY; - } - - /** - * Get a time-to-live (TTL) {@link ExpiryPolicy} instance for the given {@link Duration}. - * - * @param timeToLive the TTL duration - * @return a TTL expiry - */ - public static ExpiryPolicy timeToLiveExpiration(Duration timeToLive) { - Objects.requireNonNull(timeToLive, "TTL duration cannot be null"); - if (timeToLive.isNegative()) { - throw new IllegalArgumentException("TTL duration cannot be negative"); - } - return new TimeToLiveExpiryPolicy(timeToLive); - } - - /** - * Get a time-to-idle (TTI) {@link ExpiryPolicy} instance for the given {@link Duration}. - * - * @param timeToIdle the TTI duration - * @return a TTI expiry - */ - public static ExpiryPolicy timeToIdleExpiration(Duration timeToIdle) { - Objects.requireNonNull(timeToIdle, "TTI duration cannot be null"); - if (timeToIdle.isNegative()) { - throw new IllegalArgumentException("TTI duration cannot be negative"); - } - return new TimeToIdleExpiryPolicy(timeToIdle); - } - - /** - * Fluent API for creating an {@link ExpiryPolicy} instance where you can specify constant values for creation, access and update time. - * Unspecified values will be set to {@link ExpiryPolicy#INFINITE INFINITE} for create and {@code null} for access and update, matching - * the {@link #noExpiration()} no expiration} expiry. - * - * @return an {@link ExpiryPolicy} builder - */ - public static ExpiryPolicyBuilder expiry() { - return new ExpiryPolicyBuilder(); - } - - private Duration create = ExpiryPolicy.INFINITE; - private Duration access = null; - private Duration update = null; - - private ExpiryPolicyBuilder() {} - - /** - * Set TTL since creation - * - * @param create TTL since creation - * @return this builder - */ - public ExpiryPolicyBuilder create(Duration create) { - Objects.requireNonNull(create, "Create duration cannot be null"); - if (create.isNegative()) { - throw new IllegalArgumentException("Create duration must be positive"); - } - this.create = create; - return this; - } - - /** - * Set TTL since last access - * - * @param access TTL since last access - * @return this builder - */ - public ExpiryPolicyBuilder access(Duration access) { - if (access != null && access.isNegative()) { - throw new IllegalArgumentException("Access duration must be positive"); - } - this.access = access; - return this; - } - - /** - * Set TTL since last update - * - * @param update TTL since last update - * @return this builder - */ - public ExpiryPolicyBuilder update(Duration update) { - if (update != null && update.isNegative()) { - throw new IllegalArgumentException("Update duration must be positive"); - } - this.update = update; - return this; - } - - /** - * - * @return an {@link ExpiryPolicy} - */ - public ExpiryPolicy build() { - return new BaseExpiryPolicy(create, access, update); - } - - /** - * Simple implementation of the {@link ExpiryPolicy} interface allowing to set constants to each expiry types. - */ - private static class BaseExpiryPolicy implements ExpiryPolicy { - - private final Duration create; - private final Duration access; - private final Duration update; - - BaseExpiryPolicy(Duration create, Duration access, Duration update) { - this.create = create; - this.access = access; - this.update = update; - } - @Override - public Duration getExpiryForCreation(Object key, Object value) { - return create; - } - - @Override - public Duration getExpiryForAccess(Object key, Supplier value) { - return access; - } - - @Override - public Duration getExpiryForUpdate(Object key, Supplier oldValue, Object newValue) { - return update; - } - - @Override - public boolean equals(final Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - final BaseExpiryPolicy that = (BaseExpiryPolicy) o; - - if (!Objects.equals(access, that.access)) return false; - if (!Objects.equals(create, that.create)) return false; - if (!Objects.equals(update, that.update)) return false; - - return true; - } - - @Override - public int hashCode() { - int result = Objects.hashCode(create); - result = 31 * result + Objects.hashCode(access); - result = 31 * result + Objects.hashCode(update); - return result; - } - - @Override - public String toString() { - return this.getClass().getSimpleName() + "{" + - "create=" + create + - ", access=" + access + - ", update=" + update + - '}'; - } - } - - private static final class TimeToLiveExpiryPolicy extends BaseExpiryPolicy { - TimeToLiveExpiryPolicy(Duration ttl) { - super(ttl, null, ttl); - } - } - - private static final class TimeToIdleExpiryPolicy extends BaseExpiryPolicy { - TimeToIdleExpiryPolicy(Duration tti) { - super(tti, tti, tti); - } - } -} diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/basic/BaseStore.java b/impl/src/main/java/org/ehcache/impl/internal/store/basic/BaseStore.java deleted file mode 100644 index 0f39f9ebec..0000000000 --- a/impl/src/main/java/org/ehcache/impl/internal/store/basic/BaseStore.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ehcache.impl.internal.store.basic; - -import org.ehcache.config.ResourceType; -import org.ehcache.core.config.store.StoreStatisticsConfiguration; -import org.ehcache.core.spi.store.Store; -import org.ehcache.impl.internal.statistics.StatsUtils; -import org.ehcache.impl.internal.util.CheckerUtil; -import org.terracotta.statistics.MappedOperationStatistic; -import org.terracotta.statistics.OperationStatistic; -import org.terracotta.statistics.StatisticType; -import org.terracotta.statistics.StatisticsManager; -import org.terracotta.statistics.ZeroOperationStatistic; -import org.terracotta.statistics.observer.OperationObserver; - -import java.io.Serializable; -import java.util.Map; -import java.util.Set; -import java.util.function.Supplier; - -import static org.terracotta.statistics.StatisticBuilder.operation; - -/** - * Base class to most stores. It provides functionality common to stores in general. A given store implementation is not required to extend - * it but the implementor might find it easier to do so. - */ -public abstract class BaseStore implements Store { - - /* Type of the keys stored in this store */ - protected final Class keyType; - /* Type of the values stored in this store */ - protected final Class valueType; - /** Tells if this store is by itself or in a tiered setup */ - protected final boolean operationStatisticsEnabled; - - public BaseStore(Configuration config) { - this.keyType = config.getKeyType(); - this.valueType = config.getValueType(); - this.operationStatisticsEnabled = config.isOperationStatisticsEnabled(); - } - - protected void checkKey(K keyObject) { - CheckerUtil.checkKey(keyType, keyObject); - } - - protected void checkValue(V valueObject) { - CheckerUtil.checkValue(valueType, valueObject); - } - - /** - * Create an {@code OperationObserver} using {@code this} for the context. - * - * @param name name of the statistic - * @param outcome class of the possible outcomes - * @param canBeDisabled if this statistic can be disabled by a {@link StoreStatisticsConfiguration} - * @param type of the outcome - * @return the created observer - */ - protected > OperationObserver createObserver(String name, Class outcome, boolean canBeDisabled) { - if(!operationStatisticsEnabled && canBeDisabled) { - return ZeroOperationStatistic.get(); - } - return operation(outcome).named(name).of(this).tag(getStatisticsTag()).build(); - } - - protected void registerStatistic(String name, StatisticType type, Set tags, Supplier valueSupplier) { - StatisticsManager.createPassThroughStatistic(this, name, tags, type, valueSupplier); - } - - protected abstract String getStatisticsTag(); - - - protected static abstract class BaseStoreProvider implements Store.Provider { - - protected , T extends Enum> OperationStatistic createTranslatedStatistic(BaseStore store, String statisticName, Map> translation, String targetName) { - Class outcomeType = getOutcomeType(translation); - - // If the original stat doesn't exist, we do not need to translate it - if (StatsUtils.hasOperationStat(store, outcomeType, targetName)) { - int tierHeight = getResourceType().getTierHeight(); - OperationStatistic stat = new MappedOperationStatistic<>(store, translation, statisticName, tierHeight, targetName, store - .getStatisticsTag()); - StatisticsManager.associate(stat).withParent(store); - return stat; - } - return ZeroOperationStatistic.get(); - } - - /** - * From the Map of translation, we extract one of the items to get the declaring class of the enum. - * - * @param translation translation map - * @param type of the outcome - * @param type of the possible translations - * @return the outcome type - */ - private static , T extends Enum> Class getOutcomeType(Map> translation) { - Map.Entry> first = translation.entrySet().iterator().next(); - Class outcomeType = first.getValue().iterator().next().getDeclaringClass(); - return outcomeType; - } - - protected abstract ResourceType getResourceType(); - } -} diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/loaderwriter/LoaderWriterStoreProvider.java b/impl/src/main/java/org/ehcache/impl/internal/store/loaderwriter/LoaderWriterStoreProvider.java deleted file mode 100644 index 4fb4e61b58..0000000000 --- a/impl/src/main/java/org/ehcache/impl/internal/store/loaderwriter/LoaderWriterStoreProvider.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ehcache.impl.internal.store.loaderwriter; - -import org.ehcache.config.ResourceType; -import org.ehcache.core.internal.store.StoreSupport; -import org.ehcache.core.spi.store.Store; -import org.ehcache.core.spi.store.WrapperStore; -import org.ehcache.impl.internal.concurrent.ConcurrentHashMap; -import org.ehcache.spi.loaderwriter.CacheLoaderWriterConfiguration; -import org.ehcache.spi.loaderwriter.CacheLoaderWriterProvider; -import org.ehcache.spi.service.Service; -import org.ehcache.spi.service.ServiceConfiguration; -import org.ehcache.spi.service.ServiceDependencies; -import org.ehcache.spi.service.ServiceProvider; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Map; -import java.util.Set; - -import static org.ehcache.core.spi.service.ServiceUtils.findSingletonAmongst; - -@ServiceDependencies({CacheLoaderWriterProvider.class}) -public class LoaderWriterStoreProvider implements WrapperStore.Provider { - - private volatile ServiceProvider serviceProvider; - - private final Map, StoreRef> createdStores = new ConcurrentHashMap<>(); - - @Override - public Store createStore(Store.Configuration storeConfig, ServiceConfiguration... serviceConfigs) { - Store.Provider underlyingStoreProvider = StoreSupport.selectStoreProvider(serviceProvider, - storeConfig.getResourcePools().getResourceTypeSet(), Arrays.asList(serviceConfigs)); - Store store = underlyingStoreProvider.createStore(storeConfig, serviceConfigs); - - LocalLoaderWriterStore loaderWriterStore = new LocalLoaderWriterStore<>(store, storeConfig.getCacheLoaderWriter(), - storeConfig.useLoaderInAtomics(), storeConfig.getExpiry()); - createdStores.put(loaderWriterStore, new StoreRef<>(store, underlyingStoreProvider)); - return loaderWriterStore; - } - - @Override - public void releaseStore(Store resource) { - StoreRef storeRef = createdStores.remove(resource); - storeRef.getUnderlyingStoreProvider().releaseStore(storeRef.getUnderlyingStore()); - } - - @Override - public void initStore(Store resource) { - StoreRef storeRef = createdStores.get(resource); - storeRef.getUnderlyingStoreProvider().initStore(storeRef.getUnderlyingStore()); - } - - @Override - public int rank(Set> resourceTypes, Collection> serviceConfigs) { - throw new UnsupportedOperationException("Its a Wrapper store provider, does not support regular ranking"); - } - - @Override - public void start(ServiceProvider serviceProvider) { - this.serviceProvider = serviceProvider; - } - - @Override - public void stop() { - this.serviceProvider = null; - this.createdStores.clear(); - } - - @Override - public int wrapperStoreRank(Collection> serviceConfigs) { - CacheLoaderWriterConfiguration loaderWriterConfiguration = findSingletonAmongst(CacheLoaderWriterConfiguration.class, serviceConfigs); - if (loaderWriterConfiguration == null) { - return 0; - } - return 2; - } - - public static class StoreRef { - private final Store underlyingStore; - private final Store.Provider underlyingStoreProvider; - - public StoreRef(Store underlyingStore, Store.Provider underlyingStoreProvider) { - this.underlyingStore = underlyingStore; - this.underlyingStoreProvider = underlyingStoreProvider; - } - - public Store.Provider getUnderlyingStoreProvider() { - return underlyingStoreProvider; - } - - public Store getUnderlyingStore() { - return underlyingStore; - } - - } - -} diff --git a/impl/src/main/java/org/ehcache/impl/internal/util/CheckerUtil.java b/impl/src/main/java/org/ehcache/impl/internal/util/CheckerUtil.java deleted file mode 100644 index 7dfdb4178a..0000000000 --- a/impl/src/main/java/org/ehcache/impl/internal/util/CheckerUtil.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ehcache.impl.internal.util; - -import java.util.Objects; - -/** - * @author Henri Tremblay - */ -public final class CheckerUtil { - - public static void checkKey(Class keyType, Object keyObject) { - if (!keyType.isInstance(Objects.requireNonNull(keyObject))) { - throw new ClassCastException("Invalid key type, expected : " + keyType.getName() + " but was : " + keyObject.getClass().getName()); - } - } - - public static void checkValue(Class valueType, Object valueObject) { - if (!valueType.isInstance(Objects.requireNonNull(valueObject))) { - throw new ClassCastException("Invalid value type, expected : " + valueType.getName() + " but was : " + valueObject.getClass().getName()); - } - } - - private CheckerUtil() {} -} diff --git a/impl/src/test/java/org/ehcache/config/builders/CacheConfigurationBuilderTest.java b/impl/src/test/java/org/ehcache/config/builders/CacheConfigurationBuilderTest.java deleted file mode 100644 index 149020b7b1..0000000000 --- a/impl/src/test/java/org/ehcache/config/builders/CacheConfigurationBuilderTest.java +++ /dev/null @@ -1,316 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ehcache.config.builders; - -import org.ehcache.config.*; -import org.ehcache.config.units.EntryUnit; -import org.ehcache.config.units.MemoryUnit; -import org.ehcache.core.internal.resilience.RobustResilienceStrategy; -import org.ehcache.core.spi.service.ServiceUtils; -import org.ehcache.expiry.ExpiryPolicy; -import org.ehcache.impl.config.copy.DefaultCopierConfiguration; -import org.ehcache.impl.config.loaderwriter.DefaultCacheLoaderWriterConfiguration; -import org.ehcache.impl.config.resilience.DefaultResilienceStrategyConfiguration; -import org.ehcache.impl.config.serializer.DefaultSerializerConfiguration; -import org.ehcache.impl.config.store.heap.DefaultSizeOfEngineConfiguration; -import org.ehcache.impl.internal.classes.ClassInstanceConfiguration; -import org.ehcache.spi.copy.Copier; -import org.ehcache.spi.loaderwriter.CacheLoaderWriter; -import org.ehcache.spi.loaderwriter.CacheLoaderWriterConfiguration; -import org.ehcache.spi.resilience.RecoveryStore; -import org.ehcache.spi.resilience.ResilienceStrategy; -import org.ehcache.spi.serialization.Serializer; -import org.ehcache.spi.serialization.SerializerException; -import org.ehcache.spi.service.ServiceConfiguration; -import org.hamcrest.Matcher; -import org.hamcrest.Matchers; -import org.hamcrest.core.IsSame; -import org.junit.Test; - -import java.nio.ByteBuffer; - -import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.ehcache.test.MockitoUtil.mock; - -public class CacheConfigurationBuilderTest { - - @Test - public void testEvictionAdvisor() throws Exception { - EvictionAdvisor evictionAdvisor = (key, value) -> false; - - CacheConfiguration cacheConfiguration = CacheConfigurationBuilder.newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) - .withEvictionAdvisor(evictionAdvisor) - .build(); - - @SuppressWarnings("unchecked") - Matcher> evictionAdvisorMatcher = sameInstance(cacheConfiguration - .getEvictionAdvisor()); - assertThat(evictionAdvisor, evictionAdvisorMatcher); - } - - @Test - public void testLoaderWriter() throws Exception { - CacheLoaderWriter loaderWriter = new CacheLoaderWriter() { - @Override - public Object load(Object key) { - return null; - } - - @Override - public void write(Object key, Object value) { - - } - - @Override - public void delete(Object key) { - - } - }; - - CacheConfiguration cacheConfiguration = CacheConfigurationBuilder.newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) - .withLoaderWriter(loaderWriter) - .build(); - - CacheLoaderWriterConfiguration cacheLoaderWriterConfiguration = ServiceUtils.findSingletonAmongst(DefaultCacheLoaderWriterConfiguration.class, cacheConfiguration.getServiceConfigurations()); - Object instance = ((ClassInstanceConfiguration) cacheLoaderWriterConfiguration).getInstance(); - assertThat(instance, Matchers.sameInstance(loaderWriter)); - } - - @Test - public void testKeySerializer() throws Exception { - Serializer keySerializer = new Serializer() { - @Override - public ByteBuffer serialize(Object object) throws SerializerException { - return null; - } - - @Override - public Object read(ByteBuffer binary) throws ClassNotFoundException, SerializerException { - return null; - } - - @Override - public boolean equals(Object object, ByteBuffer binary) throws ClassNotFoundException, SerializerException { - return false; - } - }; - - CacheConfiguration cacheConfiguration = CacheConfigurationBuilder.newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) - .withKeySerializer(keySerializer) - .build(); - - - DefaultSerializerConfiguration serializerConfiguration = ServiceUtils.findSingletonAmongst(DefaultSerializerConfiguration.class, cacheConfiguration.getServiceConfigurations()); - assertThat(serializerConfiguration.getType(), is(DefaultSerializerConfiguration.Type.KEY)); - Object instance = serializerConfiguration.getInstance(); - assertThat(instance, Matchers.sameInstance(keySerializer)); - } - - @Test - public void testValueSerializer() throws Exception { - Serializer valueSerializer = new Serializer() { - @Override - public ByteBuffer serialize(Object object) throws SerializerException { - return null; - } - - @Override - public Object read(ByteBuffer binary) throws ClassNotFoundException, SerializerException { - return null; - } - - @Override - public boolean equals(Object object, ByteBuffer binary) throws ClassNotFoundException, SerializerException { - return false; - } - }; - - CacheConfiguration cacheConfiguration = CacheConfigurationBuilder.newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) - .withValueSerializer(valueSerializer) - .build(); - - - DefaultSerializerConfiguration serializerConfiguration = ServiceUtils.findSingletonAmongst(DefaultSerializerConfiguration.class, cacheConfiguration.getServiceConfigurations()); - assertThat(serializerConfiguration.getType(), is(DefaultSerializerConfiguration.Type.VALUE)); - Object instance = ((ClassInstanceConfiguration) serializerConfiguration).getInstance(); - assertThat(instance, Matchers.sameInstance(valueSerializer)); - } - - @Test - public void testKeyCopier() throws Exception { - Copier keyCopier = new Copier() { - @Override - public Long copyForRead(Object obj) { - return null; - } - - @Override - public Long copyForWrite(Object obj) { - return null; - } - }; - - CacheConfiguration cacheConfiguration = CacheConfigurationBuilder.newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) - .withKeyCopier(keyCopier) - .build(); - - - DefaultCopierConfiguration copierConfiguration = ServiceUtils.findSingletonAmongst(DefaultCopierConfiguration.class, cacheConfiguration.getServiceConfigurations()); - assertThat(copierConfiguration.getType(), is(DefaultCopierConfiguration.Type.KEY)); - Object instance = copierConfiguration.getInstance(); - assertThat(instance, Matchers.sameInstance(keyCopier)); - } - - @Test - public void testValueCopier() throws Exception { - Copier valueCopier = new Copier() { - @Override - public Long copyForRead(Object obj) { - return null; - } - - @Override - public Long copyForWrite(Object obj) { - return null; - } - }; - - CacheConfiguration cacheConfiguration = CacheConfigurationBuilder.newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) - .withValueCopier(valueCopier) - .build(); - - - DefaultCopierConfiguration copierConfiguration = ServiceUtils.findSingletonAmongst(DefaultCopierConfiguration.class, cacheConfiguration.getServiceConfigurations()); - assertThat(copierConfiguration.getType(), is(DefaultCopierConfiguration.Type.VALUE)); - Object instance = copierConfiguration.getInstance(); - assertThat(instance, Matchers.sameInstance(valueCopier)); - } - - @Test - public void testNothing() { - final CacheConfigurationBuilder builder = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, CharSequence.class, heap(10)); - - final ExpiryPolicy expiry = ExpiryPolicyBuilder.timeToIdleExpiration(ExpiryPolicy.INFINITE); - - builder - .withEvictionAdvisor((key, value) -> value.charAt(0) == 'A') - .withExpiry(expiry) - .build(); - } - - @Test - public void testOffheapGetsAddedToCacheConfiguration() { - CacheConfigurationBuilder builder = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, CharSequence.class, - ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES) - .offheap(10, MemoryUnit.MB)); - - final ExpiryPolicy expiry = ExpiryPolicyBuilder.timeToIdleExpiration(ExpiryPolicy.INFINITE); - - CacheConfiguration config = builder - .withEvictionAdvisor((key, value) -> value.charAt(0) == 'A') - .withExpiry(expiry) - .build(); - assertThat(config.getResourcePools().getPoolForResource(ResourceType.Core.OFFHEAP).getType(), Matchers.is(ResourceType.Core.OFFHEAP)); - assertThat(config.getResourcePools().getPoolForResource(ResourceType.Core.OFFHEAP).getUnit(), Matchers.is(MemoryUnit.MB)); - } - - @Test - public void testSizeOf() { - CacheConfigurationBuilder builder = CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, String.class, heap(10)); - - builder = builder.withSizeOfMaxObjectSize(10, MemoryUnit.B).withSizeOfMaxObjectGraph(100); - CacheConfiguration configuration = builder.build(); - - DefaultSizeOfEngineConfiguration sizeOfEngineConfiguration = ServiceUtils.findSingletonAmongst(DefaultSizeOfEngineConfiguration.class, configuration.getServiceConfigurations()); - assertThat(sizeOfEngineConfiguration, notNullValue()); - assertEquals(sizeOfEngineConfiguration.getMaxObjectSize(), 10); - assertEquals(sizeOfEngineConfiguration.getUnit(), MemoryUnit.B); - assertEquals(sizeOfEngineConfiguration.getMaxObjectGraphSize(), 100); - - builder = builder.withSizeOfMaxObjectGraph(1000); - configuration = builder.build(); - - sizeOfEngineConfiguration = ServiceUtils.findSingletonAmongst(DefaultSizeOfEngineConfiguration.class, configuration.getServiceConfigurations()); - assertEquals(sizeOfEngineConfiguration.getMaxObjectGraphSize(), 1000); - - } - - @Test - public void testCopyingOfExistingConfiguration() { - Class keyClass = Integer.class; - Class valueClass = String.class; - ClassLoader loader = mock(ClassLoader.class); - @SuppressWarnings("unchecked") - EvictionAdvisor eviction = mock(EvictionAdvisor.class); - @SuppressWarnings("unchecked") - ExpiryPolicy expiry = mock(ExpiryPolicy.class); - ServiceConfiguration service = mock(ServiceConfiguration.class); - - CacheConfiguration configuration = CacheConfigurationBuilder.newCacheConfigurationBuilder(Integer.class, String.class, heap(10)) - .withClassLoader(loader) - .withEvictionAdvisor(eviction) - .withExpiry(expiry) - .add(service) - .build(); - - CacheConfiguration copy = CacheConfigurationBuilder.newCacheConfigurationBuilder(configuration).build(); - - assertThat(copy.getKeyType(), equalTo(keyClass)); - assertThat(copy.getValueType(), equalTo(valueClass)); - assertThat(copy.getClassLoader(), equalTo(loader)); - - assertThat(copy.getEvictionAdvisor(), IsSame.sameInstance(eviction)); - assertThat(copy.getExpiryPolicy(), IsSame.sameInstance(expiry)); - assertThat(copy.getServiceConfigurations(), contains(IsSame.sameInstance(service))); - } - - @Test - public void testResilienceStrategyInstance() throws Exception { - ResilienceStrategy resilienceStrategy = mock(ResilienceStrategy.class); - - CacheConfiguration cacheConfiguration = CacheConfigurationBuilder.newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) - .withResilienceStrategy(resilienceStrategy) - .build(); - - DefaultResilienceStrategyConfiguration resilienceStrategyConfiguration = ServiceUtils.findSingletonAmongst(DefaultResilienceStrategyConfiguration.class, cacheConfiguration.getServiceConfigurations()); - Object instance = resilienceStrategyConfiguration.getInstance(); - assertThat(instance, sameInstance(resilienceStrategy)); - } - - @Test - public void testResilienceStrategyClass() throws Exception { - CacheConfiguration cacheConfiguration = CacheConfigurationBuilder.newCacheConfigurationBuilder(Object.class, Object.class, heap(10)) - .withResilienceStrategy(CustomResilience.class, "Hello World") - .build(); - - DefaultResilienceStrategyConfiguration resilienceStrategyConfiguration = ServiceUtils.findSingletonAmongst(DefaultResilienceStrategyConfiguration.class, cacheConfiguration.getServiceConfigurations()); - assertThat(resilienceStrategyConfiguration.getInstance(), nullValue()); - assertThat(resilienceStrategyConfiguration.getClazz(), sameInstance(CustomResilience.class)); - assertThat(resilienceStrategyConfiguration.getArguments(), arrayContaining("Hello World")); - - } - - static class CustomResilience extends RobustResilienceStrategy { - - public CustomResilience(RecoveryStore store, String blah) { - super(store); - } - } -} diff --git a/impl/src/test/java/org/ehcache/impl/internal/util/CheckerUtilTest.java b/impl/src/test/java/org/ehcache/impl/internal/util/CheckerUtilTest.java deleted file mode 100644 index 50da07c920..0000000000 --- a/impl/src/test/java/org/ehcache/impl/internal/util/CheckerUtilTest.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ehcache.impl.internal.util; - -import org.ehcache.spi.loaderwriter.CacheLoadingException; -import org.junit.Test; - -import static org.assertj.core.api.Assertions.*; - -/** - * @author Henri Tremblay - */ -public class CheckerUtilTest { - - @Test - public void checkKey_null() { - assertThatThrownBy(() -> CheckerUtil.checkKey(getClass(), null)) - .isExactlyInstanceOf(NullPointerException.class); - } - - @Test - public void checkKey_invalid() { - assertThatThrownBy(() -> CheckerUtil.checkKey(getClass(), "test")) - .isExactlyInstanceOf(ClassCastException.class) - .hasMessage("Invalid key type, expected : " + getClass().getName() + " but was : " + String.class.getName()); - } - - @Test - public void checkKey_valid() { - CheckerUtil.checkKey(Object.class, "test"); - } - - @Test - public void checkValue() { - assertThatThrownBy(() -> CheckerUtil.checkValue(getClass(), null)) - .isExactlyInstanceOf(NullPointerException.class); - } - - @Test - public void checkValue_invalid() { - assertThatThrownBy(() -> CheckerUtil.checkValue(getClass(), "test")) - .isExactlyInstanceOf(ClassCastException.class) - .hasMessage("Invalid value type, expected : " + getClass().getName() + " but was : " + String.class.getName()); - } - - @Test - public void checkValue_valid() { - CheckerUtil.checkValue(Object.class, "test"); - } -} diff --git a/integration-test/.gitignore b/integration-test/.gitignore deleted file mode 100644 index ae3c172604..0000000000 --- a/integration-test/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/bin/ diff --git a/integration-test/build.gradle b/integration-test/build.gradle index c210ed9d51..876994e798 100644 --- a/integration-test/build.gradle +++ b/integration-test/build.gradle @@ -14,19 +14,21 @@ * limitations under the License. */ +plugins { + id 'org.ehcache.build.conventions.java' +} + dependencies { testImplementation "javax.cache:cache-api:$parent.jcacheVersion" - testImplementation project(':107') - testImplementation project(':impl') - testImplementation project(':transactions') + testImplementation project(':ehcache-107') + testImplementation project(':ehcache-impl') + testImplementation(project(':ehcache-transactions')) { + capabilities { + requireCapability('org.ehcache:ehcache-transactions-modules') + } + } testImplementation (group: 'org.codehaus.btm', name: 'btm', version: '2.1.4') { exclude group:'org.slf4j', module:'slf4j-api' } testImplementation "org.terracotta:statistics:$parent.statisticVersion" } - -test { - if (testJava.javaVersion.isJava9Compatible()) { - jvmArgs += ['--add-modules', 'java.xml.bind'] - } -} diff --git a/integration-test/gradle.properties b/integration-test/gradle.properties deleted file mode 100644 index 8a8b81c0c3..0000000000 --- a/integration-test/gradle.properties +++ /dev/null @@ -1,2 +0,0 @@ -subPomName = Ehcache 3 Integration Tests module -subPomDesc = The integration tests module of Ehcache 3 diff --git a/integration-test/src/test/java/org/ehcache/docs/Performance.java b/integration-test/src/test/java/org/ehcache/docs/Performance.java new file mode 100644 index 0000000000..7ff07712b6 --- /dev/null +++ b/integration-test/src/test/java/org/ehcache/docs/Performance.java @@ -0,0 +1,51 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.docs; + +import org.ehcache.expiry.ExpiryPolicy; +import org.junit.Test; + +import java.time.Duration; +import java.util.function.Supplier; + +/** + * Samples showing performance strategies. + */ +public class Performance { + + @Test + public void expiryAllocation() { + // tag::expiryAllocation[] + new ExpiryPolicy() { + @Override + public Duration getExpiryForCreation(Object key, Object value) { + return null; + } + + @Override + public Duration getExpiryForAccess(Object key, Supplier value) { + return Duration.ofSeconds(10); // <1> + } + + @Override + public Duration getExpiryForUpdate(Object key, Supplier oldValue, Object newValue) { + return null; + } + }; + // end::expiryAllocation[] + } +} diff --git a/integration-test/src/test/java/org/ehcache/integration/CacheCopierTest.java b/integration-test/src/test/java/org/ehcache/integration/CacheCopierTest.java index e775bfbb13..400d032a43 100644 --- a/integration-test/src/test/java/org/ehcache/integration/CacheCopierTest.java +++ b/integration-test/src/test/java/org/ehcache/integration/CacheCopierTest.java @@ -38,10 +38,10 @@ import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertThat; /** * Created by alsu on 01/09/15. @@ -66,7 +66,7 @@ public void tearDown() throws Exception { @Test public void testCopyValueOnRead() throws Exception { CacheConfiguration cacheConfiguration = baseConfig - .add(new DefaultCopierConfiguration<>(PersonOnReadCopier.class, DefaultCopierConfiguration.Type.VALUE)) + .withService(new DefaultCopierConfiguration<>(PersonOnReadCopier.class, DefaultCopierConfiguration.Type.VALUE)) .build(); Cache cache = cacheManager.createCache("cache", cacheConfiguration); @@ -89,7 +89,7 @@ public void testCopyValueOnRead() throws Exception { @Test public void testCopyValueOnWrite() throws Exception { CacheConfiguration cacheConfiguration = baseConfig - .add(new DefaultCopierConfiguration<>(PersonOnWriteCopier.class, DefaultCopierConfiguration.Type.VALUE)) + .withService(new DefaultCopierConfiguration<>(PersonOnWriteCopier.class, DefaultCopierConfiguration.Type.VALUE)) .build(); Cache cache = cacheManager.createCache("cache", cacheConfiguration); @@ -131,8 +131,8 @@ public void testIdentityCopier() throws Exception { @Test public void testSerializingCopier() throws Exception { CacheConfiguration cacheConfiguration = baseConfig - .add(new DefaultCopierConfiguration<>(SerializingCopier.asCopierClass(), DefaultCopierConfiguration.Type.VALUE)) - .add(new DefaultSerializerConfiguration<>(PersonSerializer.class, DefaultSerializerConfiguration.Type.VALUE)) + .withService(new DefaultCopierConfiguration<>(SerializingCopier.asCopierClass(), DefaultCopierConfiguration.Type.VALUE)) + .withService(new DefaultSerializerConfiguration<>(PersonSerializer.class, DefaultSerializerConfiguration.Type.VALUE)) .build(); Cache cache = cacheManager.createCache("cache", cacheConfiguration); @@ -155,7 +155,7 @@ public void testSerializingCopier() throws Exception { @Test public void testReadWriteCopier() throws Exception { CacheConfiguration cacheConfiguration = baseConfig - .add(new DefaultCopierConfiguration<>(PersonCopier.class, DefaultCopierConfiguration.Type.VALUE)) + .withService(new DefaultCopierConfiguration<>(PersonCopier.class, DefaultCopierConfiguration.Type.VALUE)) .build(); Cache cache = cacheManager.createCache("cache", cacheConfiguration); diff --git a/integration-test/src/test/java/org/ehcache/integration/EhcacheBaseTest.java b/integration-test/src/test/java/org/ehcache/integration/EhcacheBaseTest.java new file mode 100644 index 0000000000..829ffaec15 --- /dev/null +++ b/integration-test/src/test/java/org/ehcache/integration/EhcacheBaseTest.java @@ -0,0 +1,324 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.integration; + +import org.ehcache.Cache; +import org.ehcache.CacheManager; +import org.ehcache.config.Configuration; +import org.ehcache.config.builders.CacheConfigurationBuilder; +import org.ehcache.config.builders.CacheManagerBuilder; +import org.ehcache.config.builders.ConfigurationBuilder; +import org.ehcache.config.builders.ExpiryPolicyBuilder; +import org.ehcache.config.builders.ResourcePoolsBuilder; +import org.ehcache.core.EhcacheManager; +import org.ehcache.core.spi.service.StatisticsService; +import org.ehcache.impl.config.persistence.DefaultPersistenceConfiguration; +import org.ehcache.impl.internal.TimeSourceConfiguration; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; +import org.ehcache.integration.statistics.AbstractCacheCalculationTest; +import org.ehcache.spi.loaderwriter.CacheLoaderWriter; +import org.ehcache.spi.service.Service; +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.io.IOException; +import java.time.Duration; +import java.util.Collection; +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNullPointerException; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +/** + * @author Henri Tremblay + */ +@RunWith(Parameterized.class) +public class EhcacheBaseTest extends AbstractCacheCalculationTest { + + private CacheManager cacheManager; + + private Cache cache; + + private final StatisticsService statisticsService = new DefaultStatisticsService(); + + private final TestTimeSource timeSource = new TestTimeSource(); + + public EhcacheBaseTest(ResourcePoolsBuilder poolBuilder) { + super(poolBuilder); + } + + @After + public void after() { + if (cacheManager != null) { + cacheManager.close(); + } + } + + private void createCacheManager(CacheManagerBuilder builder) { + cacheManager = builder + .build(true); + } + + private void createNotAtomicCacheManager() throws IOException { + Configuration config = ConfigurationBuilder.newConfigurationBuilder() + .withService(new TimeSourceConfiguration(timeSource)) + .withService(new DefaultPersistenceConfiguration(diskPath.newFolder())) + .build(); + + Collection services = Collections.singleton(statisticsService); + cacheManager = new EhcacheManager(config, services, false); + cacheManager.init(); + } + + private void createCacheManager() { + createCacheManager(baseCacheManagerConfig()); + } + + private CacheManagerBuilder baseCacheManagerConfig() { + try { + return CacheManagerBuilder.newCacheManagerBuilder() + .using(new DefaultPersistenceConfiguration(diskPath.newFolder())) + .using(statisticsService) + .using(new TimeSourceConfiguration(timeSource)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private Cache createCache() { + return createCache(baseConfig()); + } + + private Cache createCache(CacheConfigurationBuilder config) { + Cache cache = cacheManager.createCache("cache", config); + cacheStatistics = statisticsService.getCacheStatistics("cache"); + return cache; + } + + private CacheConfigurationBuilder baseConfig() { + return CacheConfigurationBuilder.newCacheConfigurationBuilder(Integer.class, String.class, resources); + } + + @Test + public void putIfAbsent_absent() { + createCacheManager(); + + cache = createCache(); + + assertThat(cache.putIfAbsent(1, "a")).isNull(); + + assertThat(cache.get(1)).isEqualTo("a"); + + changesOf(1, 1, 1, 0); + } + + @Test + public void putIfAbsent_present() { + createCacheManager(); + + cache = createCache(); + + cache.put(1, "a"); + + assertThat(cache.putIfAbsent(1, "b")).isEqualTo("a"); + + changesOf(1, 0, 1, 0); + } + + @Test + public void putIfAbsent_presentButExpired() { + createCacheManager(); + + CacheConfigurationBuilder builder = baseConfig() + .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMillis(10))); + cache = createCache(builder); + + cache.put(1, "a"); + + timeSource.advanceTime(15); + + assertThat(cache.putIfAbsent(1, "b")).isNull(); + + assertThat(cache.get(1)).isEqualTo("b"); + + changesOf(1, 1, 2, 0); + } + + @Test + public void putIfAbsent_absentPutNull() { + createCacheManager(); + + cache = createCache(); + + assertThatNullPointerException().isThrownBy(() -> cache.putIfAbsent(1, null)); + + changesOf(0, 0, 0, 0); + } + + @Test + public void putIfAbsentLoaderWriter_absentAndLoaded() throws Exception { + createCacheManager(); + + CacheLoaderWriter loader = mockLoader(); + when(loader.load(1)).thenReturn("a"); + + CacheConfigurationBuilder builder = baseConfig() + .withLoaderWriter(loader); + cache = createCache(builder); + + assertThat(cache.putIfAbsent(1, "b")).isEqualTo("a"); + + assertThat(cache.get(1)).isEqualTo("a"); + + changesOf(2, 0, 0, 0); + } + + @Test + public void putIfAbsentLoaderWriter_absentAndNotLoaded() throws Exception { + createCacheManager(); + + CacheLoaderWriter loader = mockLoader(); + when(loader.load(1)).thenReturn(null); + + CacheConfigurationBuilder builder = baseConfig() + .withLoaderWriter(loader); + cache = createCache(builder); + + assertThat(cache.putIfAbsent(1, "b")).isNull(); + + verify(loader).write(1, "b"); + + assertThat(cache.get(1)).isEqualTo("b"); + + changesOf(1, 1, 1, 0); + } + + @Test + public void putIfAbsentLoaderWriter_present() throws Exception { + createCacheManager(); + + CacheLoaderWriter loader = mockLoader(); + + CacheConfigurationBuilder builder = baseConfig() + .withLoaderWriter(loader); + cache = createCache(builder); + + cache.put(1, "a"); + + assertThat(cache.putIfAbsent(1, "b")).isEqualTo("a"); + + verify(loader).write(1, "a"); + + changesOf(1, 0, 1, 0); + } + + @Test + public void putIfAbsentLoaderWriter_presentButExpiredAndLoaded() throws Exception { + createCacheManager(); + + CacheLoaderWriter loader = mockLoader(); + when(loader.load(1)).thenReturn("c"); + + CacheConfigurationBuilder builder = baseConfig() + .withLoaderWriter(loader) + .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMillis(10))); + cache = createCache(builder); + + cache.put(1, "a"); + + timeSource.advanceTime(15); + + assertThat(cache.putIfAbsent(1, "b")).isEqualTo("c"); + + verify(loader).write(1, "a"); + + changesOf(1, 0, 1, 0); + } + + @Test + public void putIfAbsentLoaderWriter_presentButExpiredAndNotLoaded() throws Exception { + createCacheManager(); + + CacheLoaderWriter loader = mockLoader(); + when(loader.load(1)).thenReturn(null); + + CacheConfigurationBuilder builder = baseConfig() + .withLoaderWriter(loader) + .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMillis(10))); + cache = createCache(builder); + + cache.put(1, "a"); + + timeSource.advanceTime(15); + + assertThat(cache.putIfAbsent(1, "b")).isNull(); + + verify(loader).write(1, "b"); + + changesOf(0, 1, 2, 0); + } + + @Test + public void putIfAbsentLoaderWriterNotAtomic_absent() throws Exception { + createNotAtomicCacheManager(); + + CacheLoaderWriter loader = mockLoader(); + + CacheConfigurationBuilder builder = baseConfig() + .withLoaderWriter(loader); + cache = createCache(builder); + + assertThat(cache.putIfAbsent(1, "a")).isNull(); + + verify(loader).write(1, "a"); + + assertThat(cache.get(1)).isEqualTo("a"); + + changesOf(1, 1, 1, 0); + } + + @Test + public void putIfAbsentLoaderWriterNotAtomic_present() throws Exception { + createNotAtomicCacheManager(); + + CacheLoaderWriter loader = mockLoader(); + + CacheConfigurationBuilder builder = baseConfig() + .withLoaderWriter(loader); + cache = createCache(builder); + + cache.put(1, "a"); + + assertThat(cache.putIfAbsent(1, "b")).isEqualTo("a"); + + verify(loader).write(1, "a"); + verifyNoMoreInteractions(loader); + + changesOf(1, 0, 1, 0); + } + + @SuppressWarnings("unchecked") + private static CacheLoaderWriter mockLoader() { + return mock(CacheLoaderWriter.class); + } +} diff --git a/integration-test/src/test/java/org/ehcache/integration/EhcacheBulkMethodsITest.java b/integration-test/src/test/java/org/ehcache/integration/EhcacheBulkMethodsITest.java index 0c094e5bb2..5784f64acf 100644 --- a/integration-test/src/test/java/org/ehcache/integration/EhcacheBulkMethodsITest.java +++ b/integration-test/src/test/java/org/ehcache/integration/EhcacheBulkMethodsITest.java @@ -21,6 +21,7 @@ import org.ehcache.config.ResourceType; import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.config.builders.CacheManagerBuilder; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; import org.ehcache.spi.loaderwriter.BulkCacheLoadingException; import org.ehcache.spi.loaderwriter.BulkCacheWritingException; import org.ehcache.spi.resilience.StoreAccessException; @@ -29,7 +30,7 @@ import org.ehcache.impl.internal.sizeof.NoopSizeOfEngine; import org.ehcache.impl.internal.store.heap.OnHeapStore; import org.ehcache.core.spi.time.SystemTimeSource; -import org.ehcache.core.internal.service.ServiceLocator; +import org.ehcache.core.spi.ServiceLocator; import org.ehcache.spi.service.ServiceProvider; import org.ehcache.core.spi.store.Store; import org.ehcache.spi.copy.Copier; @@ -51,11 +52,11 @@ import java.util.function.Function; import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; -import static org.ehcache.core.internal.service.ServiceLocator.dependencySet; +import static org.ehcache.core.spi.ServiceLocator.dependencySet; import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsCollectionContaining.hasItems; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doThrow; @@ -519,12 +520,12 @@ public void testRemoveAll_with_store_that_throws() throws Exception { */ private static class CustomStoreProvider implements Store.Provider { @Override - public int rank(final Set> resourceTypes, final Collection> serviceConfigs) { + public int rank(final Set> resourceTypes, final Collection> serviceConfigs) { return Integer.MAX_VALUE; // Ensure this Store.Provider is ranked highest } @Override - public Store createStore(Store.Configuration storeConfig, ServiceConfiguration... serviceConfigs) { + public Store createStore(Store.Configuration storeConfig, ServiceConfiguration... serviceConfigs) { ServiceLocator serviceLocator = dependencySet().with(new DefaultSerializationProvider(null)).build(); try { serviceLocator.startAllServices(); @@ -532,7 +533,7 @@ public Store createStore(Store.Configuration storeConfig, Ser throw new RuntimeException(e); } final Copier defaultCopier = new IdentityCopier(); - return new OnHeapStore(storeConfig, SystemTimeSource.INSTANCE, defaultCopier, defaultCopier, new NoopSizeOfEngine(), NullStoreEventDispatcher.nullStoreEventDispatcher()) { + return new OnHeapStore(storeConfig, SystemTimeSource.INSTANCE, defaultCopier, defaultCopier, new NoopSizeOfEngine(), NullStoreEventDispatcher.nullStoreEventDispatcher(), new DefaultStatisticsService()) { @Override public Map> bulkCompute(Set keys, Function>, Iterable>> remappingFunction) throws StoreAccessException { throw new StoreAccessException("Problem trying to bulk compute"); diff --git a/integration-test/src/test/java/org/ehcache/integration/EventNotificationTest.java b/integration-test/src/test/java/org/ehcache/integration/EventNotificationTest.java index c259f43c01..3d8d4a53b3 100644 --- a/integration-test/src/test/java/org/ehcache/integration/EventNotificationTest.java +++ b/integration-test/src/test/java/org/ehcache/integration/EventNotificationTest.java @@ -30,6 +30,7 @@ import org.ehcache.event.EventOrdering; import org.ehcache.event.EventType; import org.ehcache.impl.internal.TimeSourceConfiguration; +import org.junit.Ignore; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,10 +48,10 @@ import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.lessThan; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; public class EventNotificationTest { private static final TestTimeSource testTimeSource = new TestTimeSource(); diff --git a/integration-test/src/test/java/org/ehcache/integration/EvictionEhcacheTest.java b/integration-test/src/test/java/org/ehcache/integration/EvictionEhcacheTest.java index 29a3a81cb2..9653c387a0 100644 --- a/integration-test/src/test/java/org/ehcache/integration/EvictionEhcacheTest.java +++ b/integration-test/src/test/java/org/ehcache/integration/EvictionEhcacheTest.java @@ -28,8 +28,8 @@ import java.util.Map; import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; /** * @author Ludovic Orban diff --git a/integration-test/src/test/java/org/ehcache/integration/ExpiryEhcacheTestBase.java b/integration-test/src/test/java/org/ehcache/integration/ExpiryEhcacheTestBase.java index 0d88be9df1..f718351015 100644 --- a/integration-test/src/test/java/org/ehcache/integration/ExpiryEhcacheTestBase.java +++ b/integration-test/src/test/java/org/ehcache/integration/ExpiryEhcacheTestBase.java @@ -33,9 +33,9 @@ import java.util.Map; import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; /** * @author Ludovic Orban diff --git a/integration-test/src/test/java/org/ehcache/integration/ExpiryEventsTest.java b/integration-test/src/test/java/org/ehcache/integration/ExpiryEventsTest.java index 6c8c90d383..30fd427980 100644 --- a/integration-test/src/test/java/org/ehcache/integration/ExpiryEventsTest.java +++ b/integration-test/src/test/java/org/ehcache/integration/ExpiryEventsTest.java @@ -60,7 +60,7 @@ public class ExpiryEventsTest { .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(1))); private static final CacheConfigurationBuilder byValueCacheConfigBuilder = - byRefCacheConfigBuilder.add(new DefaultCopierConfiguration<>( + byRefCacheConfigBuilder.withService(new DefaultCopierConfiguration<>( SerializingCopier.asCopierClass(), DefaultCopierConfiguration.Type.VALUE)); private static final TestTimeSource testTimeSource = new TestTimeSource(); diff --git a/integration-test/src/test/java/org/ehcache/integration/LoaderWriterErrorEhcacheTest.java b/integration-test/src/test/java/org/ehcache/integration/LoaderWriterErrorEhcacheTest.java index 58fe1c3c0e..f3c242a608 100644 --- a/integration-test/src/test/java/org/ehcache/integration/LoaderWriterErrorEhcacheTest.java +++ b/integration-test/src/test/java/org/ehcache/integration/LoaderWriterErrorEhcacheTest.java @@ -25,7 +25,6 @@ import org.ehcache.spi.loaderwriter.CacheWritingException; import org.ehcache.spi.loaderwriter.CacheLoaderWriter; import org.ehcache.spi.loaderwriter.CacheLoaderWriterProvider; -import org.ehcache.spi.service.ServiceConfiguration; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -39,10 +38,10 @@ import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder; import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.lessThan; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsNull.notNullValue; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; diff --git a/integration-test/src/test/java/org/ehcache/integration/LoaderWriterSimpleEhcacheTest.java b/integration-test/src/test/java/org/ehcache/integration/LoaderWriterSimpleEhcacheTest.java index 366928007b..6e942ec5b1 100644 --- a/integration-test/src/test/java/org/ehcache/integration/LoaderWriterSimpleEhcacheTest.java +++ b/integration-test/src/test/java/org/ehcache/integration/LoaderWriterSimpleEhcacheTest.java @@ -29,9 +29,9 @@ import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder; import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; diff --git a/integration-test/src/test/java/org/ehcache/integration/OnHeapEvictionStrategyTest.java b/integration-test/src/test/java/org/ehcache/integration/OnHeapEvictionStrategyTest.java new file mode 100644 index 0000000000..d160090ef9 --- /dev/null +++ b/integration-test/src/test/java/org/ehcache/integration/OnHeapEvictionStrategyTest.java @@ -0,0 +1,174 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.integration; + +import org.ehcache.Cache; +import org.ehcache.CacheManager; +import org.ehcache.config.builders.CacheConfigurationBuilder; +import org.ehcache.config.builders.CacheManagerBuilder; +import org.ehcache.config.builders.ExpiryPolicyBuilder; +import org.ehcache.config.builders.ResourcePoolsBuilder; +import org.ehcache.expiry.ExpiryPolicy; +import org.ehcache.impl.internal.TimeSourceConfiguration; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Henri Tremblay + */ +public class OnHeapEvictionStrategyTest { + + private final TestTimeSource timeSource = new TestTimeSource(); + private final TimeSourceConfiguration timeSourceConfiguration = new TimeSourceConfiguration(timeSource); + + private CacheManager cacheManager; + + @Before + public void before() { + cacheManager = CacheManagerBuilder.newCacheManagerBuilder() + .using(timeSourceConfiguration) + .build(true); + } + + @After + public void after() { + cacheManager.close(); + } + + @Test + public void noExpiryGet() { + Cache cache = createCache(ExpiryPolicyBuilder.noExpiration()); + + cache.put(1, "a"); + + timeSource.setTimeMillis(Long.MAX_VALUE); + + assertThat(cache.get(1)).isEqualTo("a"); + } + + @Test + public void ttlExpiryGet() { + Cache cache = createCache(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMillis(100))); + + cache.put(1, "a"); + + assertThat(cache.get(1)).isEqualTo("a"); + + timeSource.setTimeMillis(100); + + assertThat(cache.get(1)).isNull(); + } + + @Test + public void ttiExpiryGet() { + Cache cache = createCache(ExpiryPolicyBuilder.timeToIdleExpiration(Duration.ofMillis(100))); + + cache.put(1, "a"); + + assertThat(cache.get(1)).isEqualTo("a"); + + timeSource.setTimeMillis(100); + + assertThat(cache.get(1)).isNull(); + } + + @Test + public void customExpiryGet() { + Cache cache = createCache( + ExpiryPolicyBuilder.expiry() + .create(ExpiryPolicy.INFINITE) + .update(Duration.ofMillis(100)) + .access((Duration) null) + .build()); + + cache.put(1, "a"); + + assertThat(cache.get(1)).isEqualTo("a"); + + cache.put(1, "b"); + + timeSource.setTimeMillis(100); + + assertThat(cache.get(1)).isNull(); + } + + @Test + public void noExpiryPut() { + Cache cache = createCache(ExpiryPolicyBuilder.noExpiration()); + + cache.put(1, "a"); + + timeSource.setTimeMillis(Long.MAX_VALUE); + + assertThat(cache.putIfAbsent(1, "b")).isEqualTo("a"); + } + + @Test + public void ttlExpiryPut() { + Cache cache = createCache(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMillis(100))); + + cache.put(1, "a"); + + assertThat(cache.putIfAbsent(1, "b")).isEqualTo("a"); + + timeSource.setTimeMillis(100); + + assertThat(cache.putIfAbsent(1, "c")).isNull(); + } + + @Test + public void ttiExpiryPut() { + Cache cache = createCache(ExpiryPolicyBuilder.timeToIdleExpiration(Duration.ofMillis(100))); + + cache.put(1, "a"); + + assertThat(cache.putIfAbsent(1, "b")).isEqualTo("a"); + + timeSource.setTimeMillis(100); + + assertThat(cache.putIfAbsent(1, "c")).isNull(); + } + + @Test + public void customExpiryPut() { + Cache cache = createCache( + ExpiryPolicyBuilder.expiry() + .create(ExpiryPolicy.INFINITE) + .update(Duration.ofMillis(100)) + .access((Duration) null) + .build()); + + cache.put(1, "a"); // create + cache.put(1, "b"); // update that will expire + + timeSource.setTimeMillis(100); + + assertThat(cache.putIfAbsent(1, "d")).isNull(); // expires since update + } + + private Cache createCache(ExpiryPolicy expiryPolicy) { + return cacheManager.createCache("cache", + CacheConfigurationBuilder.newCacheConfigurationBuilder(Integer.class, String.class, + ResourcePoolsBuilder.heap(10)) + .withExpiry(expiryPolicy)); + } +} diff --git a/integration-test/src/test/java/org/ehcache/integration/OsgiSafetyTest.java b/integration-test/src/test/java/org/ehcache/integration/OsgiSafetyTest.java new file mode 100644 index 0000000000..3728086e0b --- /dev/null +++ b/integration-test/src/test/java/org/ehcache/integration/OsgiSafetyTest.java @@ -0,0 +1,42 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.integration; + +import org.ehcache.core.osgi.SafeOsgi; +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.fail; + +public class OsgiSafetyTest { + + @Test + public void testOsgiIsNotHere() { + try { + Class.forName("org.osgi.framework.Bundle"); + fail("Expected ClassNotFoundException"); + } catch (ClassNotFoundException e) { + //expected + } + } + + @Test + public void testSafeOsgiIsSafe() { + assertThat(SafeOsgi.useOSGiServiceLoading(), is(false)); + } +} diff --git a/integration-test/src/test/java/org/ehcache/integration/PersistentCacheTest.java b/integration-test/src/test/java/org/ehcache/integration/PersistentCacheTest.java index 21fcb7ca57..b1e72ca780 100644 --- a/integration-test/src/test/java/org/ehcache/integration/PersistentCacheTest.java +++ b/integration-test/src/test/java/org/ehcache/integration/PersistentCacheTest.java @@ -26,8 +26,8 @@ import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; -import org.junit.rules.TemporaryFolder; import org.junit.rules.TestName; +import org.junit.rules.TemporaryFolder; import java.io.File; import java.io.Serializable; diff --git a/integration-test/src/test/java/org/ehcache/integration/PersistentUserManagedCacheTest.java b/integration-test/src/test/java/org/ehcache/integration/PersistentUserManagedCacheTest.java index 73f5827fe1..c3921d46c1 100644 --- a/integration-test/src/test/java/org/ehcache/integration/PersistentUserManagedCacheTest.java +++ b/integration-test/src/test/java/org/ehcache/integration/PersistentUserManagedCacheTest.java @@ -32,8 +32,8 @@ import java.io.File; import java.io.Serializable; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.notNullValue; -import static org.junit.Assert.assertThat; /** * PersistentUserManagedCacheTest diff --git a/integration-test/src/test/java/org/ehcache/integration/SerializersTest.java b/integration-test/src/test/java/org/ehcache/integration/SerializersTest.java index 6d022b0ff4..0fa71ab5a6 100644 --- a/integration-test/src/test/java/org/ehcache/integration/SerializersTest.java +++ b/integration-test/src/test/java/org/ehcache/integration/SerializersTest.java @@ -29,7 +29,6 @@ import org.ehcache.spi.serialization.StatefulSerializer; import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import java.nio.ByteBuffer; @@ -38,15 +37,13 @@ import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder; import static org.ehcache.config.builders.CacheManagerBuilder.persistence; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; public class SerializersTest { @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); - @Rule - public ExpectedException expectedException = ExpectedException.none(); @Test public void testStatefulSerializer() throws Exception { diff --git a/integration-test/src/test/java/org/ehcache/integration/SimpleEhcacheTest.java b/integration-test/src/test/java/org/ehcache/integration/SimpleEhcacheTest.java index a60598073d..2ff0433ba4 100644 --- a/integration-test/src/test/java/org/ehcache/integration/SimpleEhcacheTest.java +++ b/integration-test/src/test/java/org/ehcache/integration/SimpleEhcacheTest.java @@ -30,11 +30,11 @@ import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; /** * @author Ludovic Orban diff --git a/integration-test/src/test/java/org/ehcache/integration/StatefulSerializerWithStateRepositoryTest.java b/integration-test/src/test/java/org/ehcache/integration/StatefulSerializerWithStateRepositoryTest.java index 5ec4928041..5b8f076b00 100644 --- a/integration-test/src/test/java/org/ehcache/integration/StatefulSerializerWithStateRepositoryTest.java +++ b/integration-test/src/test/java/org/ehcache/integration/StatefulSerializerWithStateRepositoryTest.java @@ -30,8 +30,8 @@ import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder; import static org.ehcache.config.builders.CacheManagerBuilder.persistence; import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; public class StatefulSerializerWithStateRepositoryTest { diff --git a/integration-test/src/test/java/org/ehcache/integration/StoreStatisticsTest.java b/integration-test/src/test/java/org/ehcache/integration/StoreStatisticsTest.java index 68d4d77026..e086204646 100644 --- a/integration-test/src/test/java/org/ehcache/integration/StoreStatisticsTest.java +++ b/integration-test/src/test/java/org/ehcache/integration/StoreStatisticsTest.java @@ -45,9 +45,9 @@ import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; import static org.terracotta.context.query.Matchers.attributes; import static org.terracotta.context.query.Matchers.context; import static org.terracotta.context.query.Matchers.hasAttribute; @@ -69,7 +69,7 @@ public void test1TierStoreStatsAvailableInContextManager() throws Exception { try(CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() .withCache("threeTieredCache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, heap(1)) - .add(new StoreStatisticsConfiguration(true)) // explicitly enable statistics + .withService(new StoreStatisticsConfiguration(true)) // explicitly enable statistics ).build(true)) { Cache cache = cacheManager.getCache("threeTieredCache", Long.class, String.class); diff --git a/integration-test/src/test/java/org/ehcache/integration/TieringTest.java b/integration-test/src/test/java/org/ehcache/integration/TieringTest.java index 33cedcba2e..8ee077ba78 100644 --- a/integration-test/src/test/java/org/ehcache/integration/TieringTest.java +++ b/integration-test/src/test/java/org/ehcache/integration/TieringTest.java @@ -24,8 +24,8 @@ import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; /** diff --git a/integration-test/src/test/java/org/ehcache/integration/UserManagedCacheEvictionTest.java b/integration-test/src/test/java/org/ehcache/integration/UserManagedCacheEvictionTest.java index e5e47cdaeb..7cf3387d73 100644 --- a/integration-test/src/test/java/org/ehcache/integration/UserManagedCacheEvictionTest.java +++ b/integration-test/src/test/java/org/ehcache/integration/UserManagedCacheEvictionTest.java @@ -24,7 +24,7 @@ import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; /** * @author Anthony Dahanne diff --git a/integration-test/src/test/java/org/ehcache/integration/UserManagedCacheLoaderWriterTest.java b/integration-test/src/test/java/org/ehcache/integration/UserManagedCacheLoaderWriterTest.java index 1cfb3e0d2e..438a639649 100644 --- a/integration-test/src/test/java/org/ehcache/integration/UserManagedCacheLoaderWriterTest.java +++ b/integration-test/src/test/java/org/ehcache/integration/UserManagedCacheLoaderWriterTest.java @@ -20,10 +20,10 @@ import org.ehcache.config.units.EntryUnit; import org.ehcache.spi.loaderwriter.CacheLoaderWriter; import org.hamcrest.Matchers; -import org.junit.Assert; import org.junit.Test; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; +import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; @@ -47,6 +47,6 @@ public void testLoaderWriterWithUserManagedCache() throws Exception { verify(cacheLoaderWriter, times(1)).write(eq(1L), eq(1L)); when(cacheLoaderWriter.load(anyLong())).thenReturn(2L); - Assert.assertThat(userManagedCache.get(2L), Matchers.is(2L)); + assertThat(userManagedCache.get(2L), Matchers.is(2L)); } } diff --git a/integration-test/src/test/java/org/ehcache/integration/statistics/AbstractCacheCalculationTest.java b/integration-test/src/test/java/org/ehcache/integration/statistics/AbstractCacheCalculationTest.java index 3bab93e7da..e5cfc61d2b 100644 --- a/integration-test/src/test/java/org/ehcache/integration/statistics/AbstractCacheCalculationTest.java +++ b/integration-test/src/test/java/org/ehcache/integration/statistics/AbstractCacheCalculationTest.java @@ -18,13 +18,13 @@ import java.util.Arrays; import java.util.Collection; +import org.assertj.core.api.SoftAssertions; import org.ehcache.config.builders.ResourcePoolsBuilder; import org.ehcache.core.statistics.CacheStatistics; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import static org.assertj.core.api.Assertions.assertThat; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; import static org.ehcache.config.units.EntryUnit.ENTRIES; import static org.ehcache.config.units.MemoryUnit.MB; @@ -79,10 +79,13 @@ public static Collection data() { * @param remove how many removes should have happened */ protected void changesOf(long hit, long miss, long put, long remove) { - assertThat(cacheStatistics.getCacheHits() - hitCount).as("Hits").isEqualTo(hit); - assertThat(cacheStatistics.getCacheMisses() - missCount).as("Misses").isEqualTo(miss); - assertThat(cacheStatistics.getCachePuts() - putCount).as("Puts").isEqualTo(put); - assertThat(cacheStatistics.getCacheRemovals() - removalCount).as("Removals").isEqualTo(remove); + SoftAssertions softly = new SoftAssertions(); + softly.assertThat(cacheStatistics.getCacheHits() - hitCount).as("Hits").isEqualTo(hit); + softly.assertThat(cacheStatistics.getCacheMisses() - missCount).as("Misses").isEqualTo(miss); + softly.assertThat(cacheStatistics.getCachePuts() - putCount).as("Puts").isEqualTo(put); + softly.assertThat(cacheStatistics.getCacheRemovals() - removalCount).as("Removals").isEqualTo(remove); + softly.assertAll(); + hitCount += hit; missCount += miss; putCount += put; diff --git a/integration-test/src/test/java/org/ehcache/integration/statistics/CacheCalculationTest.java b/integration-test/src/test/java/org/ehcache/integration/statistics/CacheCalculationTest.java index dd8b108a63..be63a91bb2 100644 --- a/integration-test/src/test/java/org/ehcache/integration/statistics/CacheCalculationTest.java +++ b/integration-test/src/test/java/org/ehcache/integration/statistics/CacheCalculationTest.java @@ -30,7 +30,7 @@ import org.ehcache.config.builders.ResourcePoolsBuilder; import org.ehcache.core.spi.service.StatisticsService; import org.ehcache.impl.config.persistence.DefaultPersistenceConfiguration; -import org.ehcache.impl.internal.statistics.DefaultStatisticsService; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -131,10 +131,10 @@ public void iterator() { changesOf(0, 0, 2, 0); Iterator> iterator = cache.iterator(); - changesOf(1, 0, 0, 0); // FIXME Why one?!? + changesOf(0, 0, 0, 0); iterator.next().getKey(); - changesOf(2, 0, 0, 0); // FIXME Why two?!? + changesOf(1, 0, 0, 0); expect(iterator.hasNext()).isTrue(); changesOf(0, 0, 0, 0); @@ -157,7 +157,7 @@ public void foreach() { changesOf(0, 0, 3, 0); cache.forEach(e -> {}); - changesOf(6, 0, 0, 0); // FIXME counted twice but works for JCache + changesOf(3, 0, 0, 0); } @Test @@ -168,7 +168,7 @@ public void spliterator() { changesOf(0, 0, 3, 0); StreamSupport.stream(cache.spliterator(), false).forEach(e -> {}); - changesOf(6, 0, 0, 0); // FIXME counted twice but works for JCache + changesOf(3, 0, 0, 0); } @Test diff --git a/integration-test/src/test/java/org/ehcache/integration/statistics/TierCalculationTest.java b/integration-test/src/test/java/org/ehcache/integration/statistics/TierCalculationTest.java index a6b8d5c03a..c590a6096d 100644 --- a/integration-test/src/test/java/org/ehcache/integration/statistics/TierCalculationTest.java +++ b/integration-test/src/test/java/org/ehcache/integration/statistics/TierCalculationTest.java @@ -33,7 +33,7 @@ import org.ehcache.core.spi.service.StatisticsService; import org.ehcache.impl.config.persistence.DefaultPersistenceConfiguration; import org.ehcache.impl.internal.TimeSourceConfiguration; -import org.ehcache.impl.internal.statistics.DefaultStatisticsService; +import org.ehcache.core.internal.statistics.DefaultStatisticsService; import org.ehcache.integration.TestTimeSource; import org.junit.After; import org.junit.Before; @@ -66,7 +66,7 @@ public void before() throws Exception { CacheConfigurationBuilder .newCacheConfigurationBuilder(Integer.class, String.class, resources) .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMillis(TIME_TO_EXPIRATION))) - .add(new StoreStatisticsConfiguration(true)) // explicitly enable statistics + .withService(new StoreStatisticsConfiguration(true)) // explicitly enable statistics .build(); StatisticsService statisticsService = new DefaultStatisticsService(); @@ -149,16 +149,16 @@ public void iterator() { changesOf(0, 0, 2, 0); Iterator> iterator = cache.iterator(); - changesOf(1, 0, 0, 0); // FIXME Why one?!? + changesOf(0, 0, 0, 0); iterator.next().getKey(); - changesOf(1, 0, 0, 0); // FIXME One hit and on the cache we have two + changesOf(0, 0, 0, 0); expect(iterator.hasNext()).isTrue(); changesOf(0, 0, 0, 0); iterator.next().getKey(); - changesOf(0, 0, 0, 0); // FIXME No hit on a next + changesOf(0, 0, 0, 0); expect(iterator.hasNext()).isFalse(); changesOf(0, 0, 0, 0); diff --git a/integration-test/src/test/java/org/ehcache/integration/transactions/xa/XACacheTest.java b/integration-test/src/test/java/org/ehcache/integration/transactions/xa/XACacheTest.java index 8be7ad3201..5d5f159fd1 100644 --- a/integration-test/src/test/java/org/ehcache/integration/transactions/xa/XACacheTest.java +++ b/integration-test/src/test/java/org/ehcache/integration/transactions/xa/XACacheTest.java @@ -45,7 +45,6 @@ import javax.transaction.Status; import javax.transaction.Transaction; import java.io.File; -import java.io.IOException; import java.time.Duration; import java.util.HashMap; import java.util.Iterator; @@ -55,11 +54,11 @@ import java.util.concurrent.atomic.AtomicReference; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; /** @@ -114,8 +113,8 @@ public void testEndToEnd() throws Exception { cacheManager = CacheManagerBuilder.newCacheManagerBuilder() .using(new LookupTransactionManagerProviderConfiguration(BitronixTransactionManagerLookup.class)) - .withCache("txCache1", cacheConfigurationBuilder.add(new XAStoreConfiguration("txCache1")).build()) - .withCache("txCache2", cacheConfigurationBuilder.add(new XAStoreConfiguration("txCache2")).build()) + .withCache("txCache1", cacheConfigurationBuilder.withService(new XAStoreConfiguration("txCache1")).build()) + .withCache("txCache2", cacheConfigurationBuilder.withService(new XAStoreConfiguration("txCache2")).build()) .withCache("nonTxCache", cacheConfigurationBuilder.build()) .build(true); @@ -177,8 +176,8 @@ public void testRecoveryWithInflightTx() throws Exception { .offheap(10, MemoryUnit.MB)); cacheManager = CacheManagerBuilder.newCacheManagerBuilder() - .withCache("txCache1", cacheConfigurationBuilder.add(new XAStoreConfiguration("txCache1")).build()) - .withCache("txCache2", cacheConfigurationBuilder.add(new XAStoreConfiguration("txCache2")).build()) + .withCache("txCache1", cacheConfigurationBuilder.withService(new XAStoreConfiguration("txCache1")).build()) + .withCache("txCache2", cacheConfigurationBuilder.withService(new XAStoreConfiguration("txCache2")).build()) .using(new LookupTransactionManagerProviderConfiguration(BitronixTransactionManagerLookup.class)) .build(true); @@ -219,8 +218,8 @@ public void testRecoveryAfterCrash() throws Exception { cacheManager = CacheManagerBuilder.newCacheManagerBuilder() .with(new CacheManagerPersistenceConfiguration(getStoragePath())) - .withCache("txCache1", cacheConfigurationBuilder.add(new XAStoreConfiguration("txCache1")).build()) - .withCache("txCache2", cacheConfigurationBuilder.add(new XAStoreConfiguration("txCache2")).build()) + .withCache("txCache1", cacheConfigurationBuilder.withService(new XAStoreConfiguration("txCache1")).build()) + .withCache("txCache2", cacheConfigurationBuilder.withService(new XAStoreConfiguration("txCache2")).build()) .using(new LookupTransactionManagerProviderConfiguration(BitronixTransactionManagerLookup.class)) .build(true); @@ -277,8 +276,8 @@ public void testExpiry() throws Exception { .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(1))); cacheManager = CacheManagerBuilder.newCacheManagerBuilder() - .withCache("txCache1", cacheConfigurationBuilder.add(new XAStoreConfiguration("txCache1")).build()) - .withCache("txCache2", cacheConfigurationBuilder.add(new XAStoreConfiguration("txCache2")).build()) + .withCache("txCache1", cacheConfigurationBuilder.withService(new XAStoreConfiguration("txCache1")).build()) + .withCache("txCache2", cacheConfigurationBuilder.withService(new XAStoreConfiguration("txCache2")).build()) .using(new DefaultTimeSourceService(new TimeSourceConfiguration(testTimeSource))) .using(new LookupTransactionManagerProviderConfiguration(BitronixTransactionManagerLookup.class)) .build(true); @@ -322,15 +321,15 @@ public void testCopiers() throws Exception { cacheManager = CacheManagerBuilder.newCacheManagerBuilder() .with(new CacheManagerPersistenceConfiguration(getStoragePath())) .withCache("txCache1", cacheConfigurationBuilder - .add(new XAStoreConfiguration("txCache1")) - .add(new DefaultCopierConfiguration<>(LongCopier.class, DefaultCopierConfiguration.Type.KEY)) - .add(new DefaultCopierConfiguration<>(StringCopier.class, DefaultCopierConfiguration.Type.VALUE)) + .withService(new XAStoreConfiguration("txCache1")) + .withService(new DefaultCopierConfiguration<>(LongCopier.class, DefaultCopierConfiguration.Type.KEY)) + .withService(new DefaultCopierConfiguration<>(StringCopier.class, DefaultCopierConfiguration.Type.VALUE)) .build() ) .withCache("txCache2", cacheConfigurationBuilder - .add(new XAStoreConfiguration("txCache2")) - .add(new DefaultCopierConfiguration<>(LongCopier.class, DefaultCopierConfiguration.Type.KEY)) - .add(new DefaultCopierConfiguration<>(StringCopier.class, DefaultCopierConfiguration.Type.VALUE)) + .withService(new XAStoreConfiguration("txCache2")) + .withService(new DefaultCopierConfiguration<>(LongCopier.class, DefaultCopierConfiguration.Type.KEY)) + .withService(new DefaultCopierConfiguration<>(StringCopier.class, DefaultCopierConfiguration.Type.VALUE)) .build()) .using(new DefaultTimeSourceService(new TimeSourceConfiguration(testTimeSource))) .using(new LookupTransactionManagerProviderConfiguration(BitronixTransactionManagerLookup.class)) @@ -373,15 +372,15 @@ public void testTimeout() throws Exception { cacheManager = CacheManagerBuilder.newCacheManagerBuilder() .with(new CacheManagerPersistenceConfiguration(getStoragePath())) .withCache("txCache1", cacheConfigurationBuilder - .add(new XAStoreConfiguration("txCache1")) - .add(new DefaultCopierConfiguration<>(LongCopier.class, DefaultCopierConfiguration.Type.KEY)) - .add(new DefaultCopierConfiguration<>(StringCopier.class, DefaultCopierConfiguration.Type.VALUE)) + .withService(new XAStoreConfiguration("txCache1")) + .withService(new DefaultCopierConfiguration<>(LongCopier.class, DefaultCopierConfiguration.Type.KEY)) + .withService(new DefaultCopierConfiguration<>(StringCopier.class, DefaultCopierConfiguration.Type.VALUE)) .build() ) .withCache("txCache2", cacheConfigurationBuilder - .add(new XAStoreConfiguration("txCache2")) - .add(new DefaultCopierConfiguration<>(LongCopier.class, DefaultCopierConfiguration.Type.KEY)) - .add(new DefaultCopierConfiguration<>(StringCopier.class, DefaultCopierConfiguration.Type.VALUE)) + .withService(new XAStoreConfiguration("txCache2")) + .withService(new DefaultCopierConfiguration<>(LongCopier.class, DefaultCopierConfiguration.Type.KEY)) + .withService(new DefaultCopierConfiguration<>(StringCopier.class, DefaultCopierConfiguration.Type.VALUE)) .build()) .using(new DefaultTimeSourceService(new TimeSourceConfiguration(testTimeSource))) .using(new LookupTransactionManagerProviderConfiguration(BitronixTransactionManagerLookup.class)) @@ -435,8 +434,8 @@ public void testConcurrentTx() throws Exception { .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(1))); cacheManager = CacheManagerBuilder.newCacheManagerBuilder() - .withCache("txCache1", cacheConfigurationBuilder.add(new XAStoreConfiguration("txCache1")).build()) - .withCache("txCache2", cacheConfigurationBuilder.add(new XAStoreConfiguration("txCache2")).build()) + .withCache("txCache1", cacheConfigurationBuilder.withService(new XAStoreConfiguration("txCache1")).build()) + .withCache("txCache2", cacheConfigurationBuilder.withService(new XAStoreConfiguration("txCache2")).build()) .using(new DefaultTimeSourceService(new TimeSourceConfiguration(testTimeSource))) .using(new LookupTransactionManagerProviderConfiguration(BitronixTransactionManagerLookup.class)) .build(true); @@ -500,7 +499,7 @@ public void testAtomicsWithoutLoaderWriter() throws Exception { .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(1))); cacheManager = CacheManagerBuilder.newCacheManagerBuilder() - .withCache("txCache1", cacheConfigurationBuilder.add(new XAStoreConfiguration("txCache1")).build()) + .withCache("txCache1", cacheConfigurationBuilder.withService(new XAStoreConfiguration("txCache1")).build()) .using(new DefaultTimeSourceService(new TimeSourceConfiguration(testTimeSource))) .using(new LookupTransactionManagerProviderConfiguration(BitronixTransactionManagerLookup.class)) .build(true); @@ -529,7 +528,7 @@ public void testAtomicsWithLoaderWriter() throws Exception { .withLoaderWriter(loaderWriter); cacheManager = CacheManagerBuilder.newCacheManagerBuilder() - .withCache("txCache1", cacheConfigurationBuilder.add(new XAStoreConfiguration("txCache1")).build()) + .withCache("txCache1", cacheConfigurationBuilder.withService(new XAStoreConfiguration("txCache1")).build()) .using(new DefaultTimeSourceService(new TimeSourceConfiguration(testTimeSource))) .using(new LookupTransactionManagerProviderConfiguration(BitronixTransactionManagerLookup.class)) .build(true); @@ -678,7 +677,7 @@ public void testIterate() throws Throwable { .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(1))); cacheManager = CacheManagerBuilder.newCacheManagerBuilder() - .withCache("txCache1", cacheConfigurationBuilder.add(new XAStoreConfiguration("txCache1")).build()) + .withCache("txCache1", cacheConfigurationBuilder.withService(new XAStoreConfiguration("txCache1")).build()) .using(new DefaultTimeSourceService(new TimeSourceConfiguration(testTimeSource))) .using(new LookupTransactionManagerProviderConfiguration(BitronixTransactionManagerLookup.class)) .build(true); diff --git a/integration-test/src/test/resources/configs/simple-xa.xml b/integration-test/src/test/resources/configs/simple-xa.xml index 4efbc3e835..ba09416aba 100644 --- a/integration-test/src/test/resources/configs/simple-xa.xml +++ b/integration-test/src/test/resources/configs/simple-xa.xml @@ -14,11 +14,8 @@ ~ limitations under the License. --> + xmlns:tx='http://www.ehcache.org/v3/tx'> diff --git a/management/.gitignore b/management/.gitignore deleted file mode 100755 index ae3c172604..0000000000 --- a/management/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/bin/ diff --git a/management/gradle.properties b/management/gradle.properties deleted file mode 100644 index 226794f83c..0000000000 --- a/management/gradle.properties +++ /dev/null @@ -1,3 +0,0 @@ -subPomName = Ehcache 3 Management and Monitoring module -subPomDesc = The Management and Monitoring module of Ehcache 3 -osgi = {"Import-Package" : ["!sun.misc.*", "!sun.security.action.*"]} diff --git a/management/src/main/java/org/ehcache/management/providers/statistics/StandardEhcacheStatistics.java b/management/src/main/java/org/ehcache/management/providers/statistics/StandardEhcacheStatistics.java deleted file mode 100644 index 481dac90fc..0000000000 --- a/management/src/main/java/org/ehcache/management/providers/statistics/StandardEhcacheStatistics.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ehcache.management.providers.statistics; - -import org.ehcache.core.spi.service.StatisticsService; -import org.ehcache.core.spi.time.TimeSource; -import org.ehcache.core.statistics.CacheOperationOutcomes; -import org.ehcache.core.statistics.CacheStatistics; -import org.ehcache.management.ManagementRegistryServiceConfiguration; -import org.ehcache.management.providers.CacheBinding; -import org.ehcache.management.providers.ExposedCacheBinding; -import org.terracotta.management.model.capabilities.descriptors.StatisticDescriptor; -import org.terracotta.management.registry.collect.StatisticRegistry; -import org.terracotta.statistics.derived.OperationResultFilter; -import org.terracotta.statistics.derived.latency.DefaultLatencyHistogramStatistic; - -import java.util.Collection; -import java.util.EnumSet; - -public class StandardEhcacheStatistics extends ExposedCacheBinding { - - private final StatisticRegistry statisticRegistry; - - StandardEhcacheStatistics(ManagementRegistryServiceConfiguration registryConfiguration, CacheBinding cacheBinding, StatisticsService statisticsService, TimeSource timeSource) { - super(registryConfiguration, cacheBinding); - this.statisticRegistry = new StatisticRegistry(cacheBinding.getCache(), timeSource::getTimeMillis); - - String cacheName = cacheBinding.getAlias(); - CacheStatistics cacheStatistics = statisticsService.getCacheStatistics(cacheName); - - cacheStatistics.getKnownStatistics().forEach(statisticRegistry::registerStatistic); - - LatencyHistogramConfiguration latencyHistogramConfiguration = registryConfiguration.getLatencyHistogramConfiguration(); - - // We want some latency statistics as well, so let's register them - registerDerivedStatistics(cacheStatistics, "get", CacheOperationOutcomes.GetOutcome.HIT, "Cache:GetHitLatency", latencyHistogramConfiguration); - registerDerivedStatistics(cacheStatistics, "get", CacheOperationOutcomes.GetOutcome.MISS, "Cache:GetMissLatency", latencyHistogramConfiguration); - registerDerivedStatistics(cacheStatistics, "put", CacheOperationOutcomes.PutOutcome.PUT, "Cache:PutLatency", latencyHistogramConfiguration); - registerDerivedStatistics(cacheStatistics, "remove", CacheOperationOutcomes.RemoveOutcome.SUCCESS, "Cache:RemoveLatency", latencyHistogramConfiguration); - } - - private > void registerDerivedStatistics(CacheStatistics cacheStatistics, String statName, T outcome, String derivedName, LatencyHistogramConfiguration configuration) { - @SuppressWarnings("unchecked") - Class outcomeClass = (Class) outcome.getClass(); - DefaultLatencyHistogramStatistic histogram = new DefaultLatencyHistogramStatistic(configuration.getPhi(), configuration.getBucketCount(), configuration.getWindow()); - cacheStatistics.registerDerivedStatistic(outcomeClass, statName, new OperationResultFilter<>(EnumSet.of(outcome), histogram)); - - statisticRegistry.registerStatistic(derivedName + "#50", histogram.medianStatistic()); - statisticRegistry.registerStatistic(derivedName + "#95", histogram.percentileStatistic(0.95)); - statisticRegistry.registerStatistic(derivedName + "#99", histogram.percentileStatistic(0.99)); - statisticRegistry.registerStatistic(derivedName + "#100", histogram.maximumStatistic()); - } - - @Override - public Collection getDescriptors() { - return statisticRegistry.getDescriptors(); - } - - StatisticRegistry getStatisticRegistry() { - return statisticRegistry; - } - -} diff --git a/management/src/main/resources/META-INF/services/org.ehcache.core.spi.service.ServiceFactory b/management/src/main/resources/META-INF/services/org.ehcache.core.spi.service.ServiceFactory deleted file mode 100644 index e014208625..0000000000 --- a/management/src/main/resources/META-INF/services/org.ehcache.core.spi.service.ServiceFactory +++ /dev/null @@ -1 +0,0 @@ -org.ehcache.management.registry.DefaultManagementRegistryFactory \ No newline at end of file diff --git a/management/src/test/resources/ehcache-management-1.xml b/management/src/test/resources/ehcache-management-1.xml deleted file mode 100644 index 6dc090cfd3..0000000000 --- a/management/src/test/resources/ehcache-management-1.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - java.lang.String - java.lang.String - 20 - - - diff --git a/osgi-test/build.gradle b/osgi-test/build.gradle index bd096c3548..666463b594 100644 --- a/osgi-test/build.gradle +++ b/osgi-test/build.gradle @@ -14,51 +14,121 @@ * limitations under the License. */ +plugins { + id 'org.ehcache.build.conventions.java-library' +} + +configurations { + modularOsgiModule + osgiModule + lowerBoundOsgiModule.extendsFrom osgiModule + testCompileOnly.extendsFrom osgiModule +} + dependencies { - ext { - paxExamVersion = '4.11.0' - felixVersion = '5.6.10' + api ('org.ops4j.pax.exam:pax-exam-junit4:4.12.0') { + exclude group:'org.slf4j', module:'slf4j-api' } - - testImplementation project(':impl') - testImplementation project(':xml') - testImplementation project(':107') - testImplementation "javax.cache:cache-api:$parent.jcacheVersion" - testImplementation "org.apache.felix:org.apache.felix.framework:$felixVersion" - testImplementation ("org.ops4j.pax.exam:pax-exam-junit4:$paxExamVersion") { + implementation 'org.apache.felix:org.apache.felix.framework:6.0.3' + runtimeOnly ('org.ops4j.pax.exam:pax-exam-link-mvn:4.12.0') { exclude group:'org.slf4j', module:'slf4j-api' } - - testRuntimeOnly "org.slf4j:slf4j-simple:$parent.slf4jVersion", - testRuntimeOnly ("org.ops4j.pax.exam:pax-exam-container-native:$paxExamVersion") { + runtimeOnly ("org.ops4j.pax.url:pax-url-wrap:2.6.1") { exclude group:'org.slf4j', module:'slf4j-api' } - testRuntimeOnly ("org.ops4j.pax.exam:pax-exam-link-mvn:$paxExamVersion") { + runtimeOnly ('org.ops4j.pax.exam:pax-exam-container-native:4.12.0') { exclude group:'org.slf4j', module:'slf4j-api' } + + modularOsgiModule project(':ehcache-api') + modularOsgiModule project(':ehcache-core') + modularOsgiModule project(':ehcache-impl') + modularOsgiModule project(':ehcache-xml') + modularOsgiModule project(':ehcache-107') + + osgiModule project(':ehcache-transactions') + osgiModule "javax.cache:cache-api:$parent.jcacheVersion" + osgiModule ('org.codehaus.btm:btm:2.1.4') { + exclude group:'org.slf4j', module:'slf4j-api' + } + + osgiModule project(':ehcache') + + osgiModule "org.slf4j:slf4j-simple:$parent.slf4jVersion" + osgiModule 'org.apache.felix:org.apache.felix.scr:2.1.6' + + osgiModule 'com.sun.activation:javax.activation:1.2.0' + osgiModule 'org.glassfish.hk2:osgi-resource-locator:1.0.2' } configurations.all { - resolutionStrategy { - force 'org.ops4j.base:ops4j-base-lang:1.5.0' - force 'org.ops4j.base:ops4j-base-util-property:1.5.0' - } + resolutionStrategy { + dependencySubstitution { + substitute(module('org.ops4j.pax.url:pax-url-aether:2.4.5')) + .because('https://github.com/codehaus-plexus/plexus-utils/issues/3' + + ' and https://github.com/codehaus-plexus/plexus-utils/issues/4') + .with(module('org.ops4j.pax.url:pax-url-aether:2.6.3')) + substitute(module('org.ops4j.pax.url:pax-url-classpath:2.4.5')) + .because('https://ops4j1.jira.com/browse/PAXURL-341') + .with(module('org.ops4j.pax.url:pax-url-classpath:2.6.1')) + substitute(module('org.ops4j.pax.url:pax-url-link:2.4.5')) + .because('https://ops4j1.jira.com/browse/PAXURL-341') + .with(module('org.ops4j.pax.url:pax-url-link:2.6.1')) + + substitute(module('biz.aQute.bnd:bndlib:2.4.0')) + .because('Java 9 Stuff') + .with(module('biz.aQute.bnd:biz.aQute.bndlib:5.2.0')) + substitute(module('junit:junit:4.12')) + .because('CVE-2020-15250') + .with(module('junit:junit:4.13.1')) + } + } } sourceSets { test { - // Needed for PaxExam which makes the dynamic bundle load content of a single dir - // matching the package of the test class + // Needed to allow PaxExam to see the test resources output.resourcesDir = java.outputDir } } +if (testJava.javaVersion.isJava9Compatible()) { + tasks.withType(Test) { + //https://issues.apache.org/jira/browse/FELIX-5727 - framework extensions in Java 9 are ugly + jvmArgs += '--add-opens=java.base/jdk.internal.loader=ALL-UNNAMED' + } +} + test { - systemProperty 'ehcache.osgi.jar', project(':dist').jar.archivePath.getPath() - systemProperty 'ehcache.osgi.jcache.version', parent.jcacheVersion - systemProperty 'ehcache.osgi.slf4j.version', parent.slf4jVersion -}.doFirst { - if (testJava.javaVersion.isJava9Compatible()) throw new StopExecutionException("OSGi Tests Not Working in Java 9") + dependsOn configurations.osgiModule, configurations.modularOsgiModule + doFirst { + [configurations.modularOsgiModule, configurations.osgiModule]*.resolvedConfiguration*.resolvedArtifacts*.forEach({ + systemProperty "$it.moduleVersion.id.module:osgi-path", it.file + }) + } +} + +configurations { + lowerBoundOsgiModule { + resolutionStrategy.dependencySubstitution { + substitute module('org.glassfish.jaxb:jaxb-runtime') with module('com.sun.xml.bind:jaxb-osgi:2.2.8-b01') + } + } +} +dependencies { + lowerBoundOsgiModule 'javax.xml.bind:jaxb-api:2.2.9' } -test.dependsOn ':dist:jar' +tasks.register('lowerBoundTest', Test) { test -> + group = JavaBasePlugin.VERIFICATION_GROUP + dependsOn configurations.lowerBoundOsgiModule, configurations.modularOsgiModule + doFirst { + [configurations.modularOsgiModule, configurations.lowerBoundOsgiModule]*.resolvedConfiguration*.resolvedArtifacts*.forEach { + systemProperty "$it.moduleVersion.id.module:osgi-path", it.file + } + } +} + +tasks.named('check') { + dependsOn tasks.lowerBoundTest +} diff --git a/osgi-test/src/main/java/org/ehcache/osgi/OsgiTestUtils.java b/osgi-test/src/main/java/org/ehcache/osgi/OsgiTestUtils.java new file mode 100644 index 0000000000..90fc0623e5 --- /dev/null +++ b/osgi-test/src/main/java/org/ehcache/osgi/OsgiTestUtils.java @@ -0,0 +1,115 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.osgi; + +import org.ops4j.pax.exam.Option; +import org.ops4j.pax.exam.options.ProvisionOption; +import org.ops4j.pax.exam.options.WrappedUrlProvisionOption; +import org.osgi.framework.Constants; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Optional; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +import static java.lang.String.join; +import static java.lang.System.getProperty; +import static org.ops4j.pax.exam.CoreOptions.bundle; +import static org.ops4j.pax.exam.CoreOptions.cleanCaches; +import static org.ops4j.pax.exam.CoreOptions.composite; +import static org.ops4j.pax.exam.CoreOptions.junitBundles; +import static org.ops4j.pax.exam.CoreOptions.systemProperty; +import static org.ops4j.pax.exam.CoreOptions.workingDirectory; +import static org.ops4j.pax.exam.CoreOptions.wrappedBundle; +import static org.ops4j.pax.exam.options.WrappedUrlProvisionOption.OverwriteMode.MERGE; + +public class OsgiTestUtils { + + public static Option baseConfiguration(String ... path) { + return composite( + gradleBundle("org.slf4j:slf4j-api"), + gradleBundle("org.slf4j:slf4j-simple").noStart(), + gradleBundle("org.apache.felix:org.apache.felix.scr"), + systemProperty("pax.exam.osgi.unresolved.fail").value("true"), + cleanCaches(true), + workingDirectory(join(File.separator, "build", "osgi-container", join(File.separator, path))), + junitBundles() + ); + } + + public static Option jaxbConfiguration() { + return optionalGradleBundle("com.sun.xml.bind:jaxb-osgi") + .map(jaxb -> composite(jaxb, + gradleBundle("javax.xml.bind:jaxb-api"), + gradleBundle("com.sun.activation:javax.activation"), + gradleBundle("org.glassfish.hk2:osgi-resource-locator")) + ).orElseGet(() -> optionalGradleBundle("org.glassfish.jaxb:jaxb-runtime") + .map(jaxb -> composite(jaxb, + wrappedGradleBundle("javax.xml.bind:jaxb-api").instructions("-removeheaders=Require-Capability"), + gradleBundle("com.sun.istack:istack-commons-runtime"), + gradleBundle("com.sun.activation:javax.activation"), + gradleBundle("org.glassfish.hk2:osgi-resource-locator")) + ).orElseThrow(AssertionError::new)); + } + + public static Option jtaConfiguration() { + return composite( + wrappedGradleBundle("javax.transaction:jta").instructions("Fragment-Host=org.apache.felix.framework"), + gradleBundle("org.codehaus.btm:btm") + ); + } + + public static ProvisionOption gradleBundle(String module) { + return optionalGradleBundle(module).orElseThrow(() -> new IllegalArgumentException("Cannot find '" + module + "'")); + } + + private static final Attributes.Name BUNDLE_SYMBOLICNAME = new Attributes.Name(Constants.BUNDLE_SYMBOLICNAME); + + public static Optional> optionalGradleBundle(String module) { + return artifact(module).map(artifact -> { + try (JarFile jar = new JarFile(artifact.toFile())) { + Manifest manifest = jar.getManifest(); + if (manifest != null && manifest.getMainAttributes().containsKey(BUNDLE_SYMBOLICNAME)) { + return bundle(artifact.toUri().toString()); + } else { + return wrappedBundle(artifact.toUri().toString()); + } + } catch (IOException e) { + throw new IllegalArgumentException("Module '" + module + "' artifact " + artifact + " is broken?"); + } + }); + } + + public static WrappedUrlProvisionOption wrappedGradleBundle(String module) { + ProvisionOption provisionOption = gradleBundle(module); + if (provisionOption instanceof WrappedUrlProvisionOption) { + return (WrappedUrlProvisionOption) provisionOption; + } else { + return wrappedBundle(provisionOption.getURL()).overwriteManifest(MERGE); + } + } + + private static Optional artifact(String module) { + return Optional.ofNullable(getProperty(module + ":osgi-path")).map(Paths::get).filter(Files::isRegularFile); + } +} + diff --git a/osgi-test/src/test/java/org/ehcache/osgi/ByteSizedOnHeapOsgiTest.java b/osgi-test/src/test/java/org/ehcache/osgi/ByteSizedOnHeapOsgiTest.java index 52976e5883..67591f89d3 100644 --- a/osgi-test/src/test/java/org/ehcache/osgi/ByteSizedOnHeapOsgiTest.java +++ b/osgi-test/src/test/java/org/ehcache/osgi/ByteSizedOnHeapOsgiTest.java @@ -16,13 +16,6 @@ package org.ehcache.osgi; -import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; -import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; -import static org.ops4j.pax.exam.CoreOptions.bundle; -import static org.ops4j.pax.exam.CoreOptions.junitBundles; -import static org.ops4j.pax.exam.CoreOptions.mavenBundle; -import static org.ops4j.pax.exam.CoreOptions.options; - import org.ehcache.Cache; import org.ehcache.CacheManager; import org.ehcache.config.builders.CacheManagerBuilder; @@ -35,36 +28,59 @@ import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy; import org.ops4j.pax.exam.spi.reactors.PerMethod; -/** - * - */ +import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; +import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; +import static org.ehcache.osgi.OsgiTestUtils.baseConfiguration; +import static org.ehcache.osgi.OsgiTestUtils.gradleBundle; +import static org.ehcache.osgi.OsgiTestUtils.jaxbConfiguration; +import static org.ops4j.pax.exam.CoreOptions.options; + @RunWith(PaxExam.class) @ExamReactorStrategy(PerMethod.class) public class ByteSizedOnHeapOsgiTest { @Configuration - public Option[] config() { - String slf4jVersion = VersionUtil.version("ehcache.osgi.slf4j.version", "slf4jVersion"); + public Option[] individualModules() { return options( - mavenBundle("org.slf4j", "slf4j-api", slf4jVersion), - mavenBundle("org.slf4j", "slf4j-simple", slf4jVersion).noStart(), - bundle("file:" + VersionUtil.ehcacheOsgiJar()), - junitBundles() + gradleBundle("org.ehcache.modules:ehcache-impl"), + gradleBundle("org.ehcache.modules:ehcache-core"), + gradleBundle("org.ehcache.modules:ehcache-api"), + + gradleBundle("org.terracotta:statistics"), + gradleBundle("org.ehcache:sizeof"), + gradleBundle("org.terracotta:offheap-store"), + gradleBundle("org.terracotta:terracotta-utilities-tools"), + + baseConfiguration("ByteSizedOnHeapOsgiTest", "individualModules") + ); + } + + @Configuration + public Option[] uberJar() { + return options( + gradleBundle("org.ehcache:ehcache"), jaxbConfiguration(), + + baseConfiguration("ByteSizedOnHeapOsgiTest", "uberJar") ); } @Test public void testByteSizedOnHeapInOsgi() { - CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() + TestMethods.testByteSizedOnHeapInOsgi(); + } + + private static class TestMethods { + public static void testByteSizedOnHeapInOsgi() { + CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() .withCache("myCache", newCacheConfigurationBuilder(Long.class, String.class, newResourcePoolsBuilder().heap(10, MemoryUnit.KB)) - .build()) + .build()) .build(true); - Cache cache = cacheManager.getCache("myCache", Long.class, String.class); + Cache cache = cacheManager.getCache("myCache", Long.class, String.class); - cache.put(42L, "I am out of heap!!"); + cache.put(42L, "I am out of heap!!"); - cache.get(42L); + cache.get(42L); + } } - } diff --git a/osgi-test/src/test/java/org/ehcache/osgi/Jsr107OsgiTest.java b/osgi-test/src/test/java/org/ehcache/osgi/Jsr107OsgiTest.java index 50260b0dfa..f96c601241 100644 --- a/osgi-test/src/test/java/org/ehcache/osgi/Jsr107OsgiTest.java +++ b/osgi-test/src/test/java/org/ehcache/osgi/Jsr107OsgiTest.java @@ -16,7 +16,9 @@ package org.ehcache.osgi; -import org.junit.Ignore; +import org.ehcache.core.osgi.EhcacheActivator; +import org.ehcache.core.osgi.OsgiServiceLoader; +import org.ehcache.core.spi.service.ServiceFactory; import org.junit.Test; import org.junit.runner.RunWith; import org.ops4j.pax.exam.Configuration; @@ -24,16 +26,25 @@ import org.ops4j.pax.exam.junit.PaxExam; import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy; import org.ops4j.pax.exam.spi.reactors.PerMethod; +import org.osgi.framework.wiring.BundleWiring; import javax.cache.Cache; import javax.cache.CacheManager; import javax.cache.Caching; import javax.cache.spi.CachingProvider; +import java.util.ServiceLoader; +import java.util.Set; +import static java.util.Spliterators.spliterator; +import static java.util.stream.Collectors.toSet; +import static java.util.stream.Stream.of; +import static java.util.stream.StreamSupport.stream; +import static org.ehcache.osgi.OsgiTestUtils.baseConfiguration; +import static org.ehcache.osgi.OsgiTestUtils.gradleBundle; +import static org.ehcache.osgi.OsgiTestUtils.jaxbConfiguration; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsCollectionContaining.hasItems; import static org.junit.Assert.assertEquals; -import static org.ops4j.pax.exam.CoreOptions.bundle; -import static org.ops4j.pax.exam.CoreOptions.junitBundles; -import static org.ops4j.pax.exam.CoreOptions.mavenBundle; import static org.ops4j.pax.exam.CoreOptions.options; /** @@ -44,25 +55,67 @@ public class Jsr107OsgiTest { @Configuration - public Option[] config() { - String slf4jVersion = VersionUtil.version("ehcache.osgi.slf4j.version", "slf4jVersion"); + public Option[] individualModules() { return options( - mavenBundle("org.slf4j", "slf4j-api", slf4jVersion), - mavenBundle("org.slf4j", "slf4j-simple", slf4jVersion).noStart(), - bundle("file:" + VersionUtil.ehcacheOsgiJar()), - mavenBundle("javax.cache", "cache-api", VersionUtil.version("ehcache.osgi.jcache.version", "jcacheVersion")), - junitBundles() + gradleBundle("org.ehcache.modules:ehcache-impl"), + gradleBundle("org.ehcache.modules:ehcache-xml"), jaxbConfiguration(), + gradleBundle("org.ehcache.modules:ehcache-107"), + gradleBundle("org.ehcache.modules:ehcache-core"), + gradleBundle("org.ehcache.modules:ehcache-api"), + gradleBundle("javax.cache:cache-api"), + + gradleBundle("org.terracotta:statistics"), + gradleBundle("org.ehcache:sizeof"), + gradleBundle("org.terracotta:offheap-store"), + gradleBundle("org.terracotta:terracotta-utilities-tools"), + + baseConfiguration("Jsr107OsgiTest", "individualModules") + ); + } + + @Configuration + public Option[] uberJar() { + return options( + gradleBundle("org.ehcache:ehcache"), jaxbConfiguration(), + gradleBundle("javax.cache:cache-api"), + + baseConfiguration("Jsr107OsgiTest", "uberJar") ); } @Test - @Ignore("Needs https://github.com/jsr107/jsr107spec/issues/326 to be fixed and so will wait on javax.cache:cache-api:1.0.1 only") - @SuppressWarnings("unchecked") public void testJsr107EhcacheOsgi() throws Exception { - CachingProvider cachingProvider = Caching.getCachingProvider("org.ehcache.jsr107.EhcacheCachingProvider", getClass().getClassLoader()); - CacheManager cacheManager = cachingProvider.getCacheManager(getClass().getResource("/org/ehcache/osgi/ehcache-107-osgi.xml").toURI(), getClass().getClassLoader()); - Cache personCache = cacheManager.getCache("personCache", Long.class, Person.class); - assertEquals(Person.class, personCache.getConfiguration(javax.cache.configuration.Configuration.class).getValueType()); + TestMethods.testJsr107EhcacheOsgi(); } + @Test + public void testAllServicesAreAvailable() { + TestMethods.testAllServicesAreAvailable(); + } + + private static class TestMethods { + @SuppressWarnings("unchecked") + public static void testJsr107EhcacheOsgi() throws Exception { + CachingProvider cachingProvider = Caching.getCachingProvider("org.ehcache.jsr107.EhcacheCachingProvider", TestMethods.class.getClassLoader()); + CacheManager cacheManager = cachingProvider.getCacheManager(TestMethods.class.getResource("/org/ehcache/osgi/ehcache-107-osgi.xml").toURI(), TestMethods.class.getClassLoader()); + Cache personCache = cacheManager.getCache("personCache", Long.class, Person.class); + assertEquals(Person.class, personCache.getConfiguration(javax.cache.configuration.Configuration.class).getValueType()); + } + + public static void testAllServicesAreAvailable() { + Set osgiAvailableClasses = + stream(spliterator(OsgiServiceLoader.load(ServiceFactory.class).iterator(), Long.MAX_VALUE, 0), false) + .map(f -> f.getClass().getName()) + .collect(toSet()); + + Set jdkAvailableClasses = of(EhcacheActivator.getCoreBundle().getBundles()) + .map(b -> b.adapt(BundleWiring.class).getClassLoader()) + .flatMap(cl -> + stream(spliterator(ServiceLoader.load(ServiceFactory.class, cl).iterator(), Long.MAX_VALUE, 0), false) + .map(f -> f.getClass().getName())) + .collect(toSet()); + + assertThat(osgiAvailableClasses, hasItems(jdkAvailableClasses.toArray(new String[0]))); + } + } } diff --git a/osgi-test/src/test/java/org/ehcache/osgi/OffHeapOsgiTest.java b/osgi-test/src/test/java/org/ehcache/osgi/OffHeapOsgiTest.java index 35ea3f9c36..4143fbc681 100644 --- a/osgi-test/src/test/java/org/ehcache/osgi/OffHeapOsgiTest.java +++ b/osgi-test/src/test/java/org/ehcache/osgi/OffHeapOsgiTest.java @@ -33,86 +33,123 @@ import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; +import static org.ehcache.core.osgi.EhcacheActivator.OSGI_LOADING; +import static org.ehcache.osgi.OsgiTestUtils.baseConfiguration; +import static org.ehcache.osgi.OsgiTestUtils.gradleBundle; +import static org.ehcache.osgi.OsgiTestUtils.jaxbConfiguration; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.ops4j.pax.exam.CoreOptions.bundle; -import static org.ops4j.pax.exam.CoreOptions.junitBundles; -import static org.ops4j.pax.exam.CoreOptions.mavenBundle; +import static org.ops4j.pax.exam.CoreOptions.frameworkProperty; import static org.ops4j.pax.exam.CoreOptions.options; -/** - * OffHeapOsgiTest - */ @RunWith(PaxExam.class) @ExamReactorStrategy(PerMethod.class) public class OffHeapOsgiTest { @Configuration - public Option[] config() { - String slf4jVersion = VersionUtil.version("ehcache.osgi.slf4j.version", "slf4jVersion"); + public Option[] individualModules() { + return options( + gradleBundle("org.ehcache.modules:ehcache-api"), + gradleBundle("org.ehcache.modules:ehcache-core"), + gradleBundle("org.ehcache.modules:ehcache-impl"), + + gradleBundle("org.terracotta:statistics"), + gradleBundle("org.ehcache:sizeof"), + gradleBundle("org.terracotta:offheap-store"), + gradleBundle("org.terracotta:terracotta-utilities-tools"), + + baseConfiguration("OffHeapOsgiTest", "individualModules") + ); + } + + @Configuration + public Option[] uberJarWithOsgiServiceLoading() { + return options( + gradleBundle("org.ehcache:ehcache"), jaxbConfiguration(), + + baseConfiguration("OffHeapOsgiTest", "uberJarWithOsgiServiceLoading") + ); + } + + @Configuration + public Option[] uberJarWithJdkServiceLoading() { return options( - mavenBundle("org.slf4j", "slf4j-api", slf4jVersion), - mavenBundle("org.slf4j", "slf4j-simple", slf4jVersion).noStart(), - bundle("file:" + VersionUtil.ehcacheOsgiJar()), - junitBundles() + frameworkProperty(OSGI_LOADING).value("false"), + + gradleBundle("org.ehcache:ehcache"), jaxbConfiguration(), + + baseConfiguration("OffHeapOsgiTest", "uberJarWithJdkServiceLoading") ); } @Test public void testOffHeapInOsgi() { - CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() + TestMethods.testOffHeapInOsgi(); + } + + @Test + public void testOffHeapClientClass() { + TestMethods.testOffHeapClientClass(); + } + + private static class TestMethods { + + public static void testOffHeapInOsgi() { + CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() .withCache("myCache", newCacheConfigurationBuilder(Long.class, String.class, newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES).offheap(10, MemoryUnit.MB)) - .build()) + .build()) .build(true); - Cache cache = cacheManager.getCache("myCache", Long.class, String.class); + Cache cache = cacheManager.getCache("myCache", Long.class, String.class); - cache.put(42L, "I am out of heap!!"); + cache.put(42L, "I am out of heap!!"); - cache.get(42L); - } + cache.get(42L); + } - @Test - public void testOffHeapClientClass() { - CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() - .withClassLoader(getClass().getClassLoader()) + public static void testOffHeapClientClass() { + CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() + .withClassLoader(TestMethods.class.getClassLoader()) .withCache("myCache", newCacheConfigurationBuilder(Long.class, Order.class, newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES).offheap(2, MemoryUnit.MB)) - .build()) + .build()) .build(true); - Cache cache = cacheManager.getCache("myCache", Long.class, Order.class); + Cache cache = cacheManager.getCache("myCache", Long.class, Order.class); - Order order = new Order(42L); - cache.put(42L, order); + Order order = new Order(42L); + cache.put(42L, order); - assertTrue(cache.get(42L) instanceof Order); + assertTrue(cache.get(42L) instanceof Order); - cache.replace(42L, order, new Order(-1L)); + cache.replace(42L, order, new Order(-1L)); - assertEquals(-1L, cache.get(42L).id); - } + assertEquals(-1L, cache.get(42L).id); + } - private static class Order implements Serializable { + private static class Order implements Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - final long id; + final long id; - Order(long id) { - this.id = id; - } + Order(long id) { + this.id = id; + } - @Override - public int hashCode() { - return (int) id; - } + @Override + public int hashCode() { + return (int) id; + } - @Override - public boolean equals(Object obj) { - if (obj instanceof Order) { - return ((Order) obj).id == this.id; + @Override + public boolean equals(Object obj) { + if (obj instanceof Order) { + return ((Order) obj).id == this.id; + } + return false; } - return false; } + + } } diff --git a/osgi-test/src/test/java/org/ehcache/osgi/SimpleOsgiTest.java b/osgi-test/src/test/java/org/ehcache/osgi/SimpleOsgiTest.java index 74460dfce7..7498a7e22d 100644 --- a/osgi-test/src/test/java/org/ehcache/osgi/SimpleOsgiTest.java +++ b/osgi-test/src/test/java/org/ehcache/osgi/SimpleOsgiTest.java @@ -19,6 +19,9 @@ import org.ehcache.Cache; import org.ehcache.CacheManager; import org.ehcache.config.builders.CacheManagerBuilder; +import org.ehcache.core.osgi.EhcacheActivator; +import org.ehcache.core.osgi.OsgiServiceLoader; +import org.ehcache.core.spi.service.ServiceFactory; import org.ehcache.impl.config.copy.DefaultCopierConfiguration; import org.ehcache.impl.copy.ReadWriteCopier; import org.ehcache.impl.copy.SerializingCopier; @@ -30,90 +33,165 @@ import org.ops4j.pax.exam.junit.PaxExam; import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy; import org.ops4j.pax.exam.spi.reactors.PerMethod; +import org.osgi.framework.wiring.BundleWiring; +import java.util.ServiceLoader; +import java.util.Set; + +import static java.util.Spliterators.spliterator; +import static java.util.stream.Collectors.toSet; +import static java.util.stream.Stream.of; +import static java.util.stream.StreamSupport.stream; import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; +import static org.ehcache.core.osgi.EhcacheActivator.OSGI_LOADING; +import static org.ehcache.osgi.OsgiTestUtils.baseConfiguration; +import static org.ehcache.osgi.OsgiTestUtils.gradleBundle; +import static org.ehcache.osgi.OsgiTestUtils.jaxbConfiguration; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsCollectionContaining.hasItems; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.ops4j.pax.exam.CoreOptions.bundle; -import static org.ops4j.pax.exam.CoreOptions.junitBundles; -import static org.ops4j.pax.exam.CoreOptions.mavenBundle; +import static org.ops4j.pax.exam.CoreOptions.frameworkProperty; import static org.ops4j.pax.exam.CoreOptions.options; -/** - * SimpleOsgiTest - */ @RunWith(PaxExam.class) @ExamReactorStrategy(PerMethod.class) public class SimpleOsgiTest { @Configuration - public Option[] config() { - String slf4jVersion = VersionUtil.version("ehcache.osgi.slf4j.version", "slf4jVersion"); + public Option[] individualModules() { + return options( + gradleBundle("org.ehcache.modules:ehcache-api"), + gradleBundle("org.ehcache.modules:ehcache-core"), + gradleBundle("org.ehcache.modules:ehcache-impl"), + gradleBundle("org.ehcache.modules:ehcache-xml"), jaxbConfiguration(), + + gradleBundle("org.terracotta:statistics"), + gradleBundle("org.ehcache:sizeof"), + gradleBundle("org.terracotta:offheap-store"), + gradleBundle("org.terracotta:terracotta-utilities-tools"), + + baseConfiguration("SimpleOsgiTest", "individualModules") + ); + } + + @Configuration + public Option[] uberJarWithOsgiServiceLoading() { + return options( + gradleBundle("org.ehcache:ehcache"), jaxbConfiguration(), + + baseConfiguration("SimpleOsgiTest", "uberJarWithOsgiServiceLoading") + ); + } + + @Configuration + public Option[] uberJarWithJdkServiceLoading() { return options( - mavenBundle("org.slf4j", "slf4j-api", slf4jVersion), - mavenBundle("org.slf4j", "slf4j-simple", slf4jVersion).noStart(), - bundle("file:" + VersionUtil.ehcacheOsgiJar()), - junitBundles() + frameworkProperty(OSGI_LOADING).value("false"), + + gradleBundle("org.ehcache:ehcache"), jaxbConfiguration(), + + baseConfiguration("SimpleOsgiTest", "uberJarWithJdkServiceLoading") ); } @Test public void testEhcache3AsBundle() { - CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() - .withCache("myCache", newCacheConfigurationBuilder(Long.class, String.class, heap(10)) - .build()) - .build(true); + TestMethods.testEhcache3AsBundle(); + } - Cache myCache = cacheManager.getCache("myCache", Long.class, String.class); + @Test + public void testEhcache3WithSerializationAndClientClass() { + TestMethods.testEhcache3WithSerializationAndClientClass(); + } - myCache.put(42L, "DaAnswer!"); - assertEquals("DaAnswer!", myCache.get(42L)); + @Test + public void testCustomCopier() { + TestMethods.testCustomCopier(); } @Test - public void testEhcache3WithSerializationAndClientClass() { - CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() + public void testEhcacheXMLConfig() throws Exception { + TestMethods.testEhcacheXMLConfig(); + } + + @Test + public void testAllServicesAreAvailable() { + TestMethods.testAllServicesAreAvailable(); + } + + private static class TestMethods { + + public static void testEhcache3AsBundle() { + CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() + .withCache("myCache", newCacheConfigurationBuilder(Long.class, String.class, heap(10)) + .build()) + .build(true); + + Cache myCache = cacheManager.getCache("myCache", Long.class, String.class); + + myCache.put(42L, "DaAnswer!"); + assertEquals("DaAnswer!", myCache.get(42L)); + } + + public static void testEhcache3WithSerializationAndClientClass() { + CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() .withCache("myCache", newCacheConfigurationBuilder(Long.class, Person.class, heap(10)) - .add(new DefaultCopierConfiguration<>(SerializingCopier.asCopierClass(), DefaultCopierConfiguration.Type.VALUE)) - .withClassLoader(getClass().getClassLoader()) - .build()) + .withService(new DefaultCopierConfiguration<>(SerializingCopier.asCopierClass(), DefaultCopierConfiguration.Type.VALUE)) + .withClassLoader(TestMethods.class.getClassLoader()) + .build()) .build(true); - Cache myCache = cacheManager.getCache("myCache", Long.class, Person.class); + Cache myCache = cacheManager.getCache("myCache", Long.class, Person.class); - myCache.put(42L, new Person("Arthur")); - assertTrue(myCache.get(42L) instanceof Person); - } + myCache.put(42L, new Person("Arthur")); + assertTrue(myCache.get(42L) instanceof Person); + } - @Test - public void testCustomCopier() { - CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() + public static void testCustomCopier() { + CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() .withCache("myCache", newCacheConfigurationBuilder(Long.class, String.class, heap(10)) - .add(new DefaultCopierConfiguration<>(StringCopier.class, DefaultCopierConfiguration.Type.VALUE)) - .withClassLoader(getClass().getClassLoader()) - .build()) + .withService(new DefaultCopierConfiguration<>(StringCopier.class, DefaultCopierConfiguration.Type.VALUE)) + .withClassLoader(TestMethods.class.getClassLoader()) + .build()) .build(true); - Cache cache = cacheManager.getCache("myCache", Long.class, String.class); + Cache cache = cacheManager.getCache("myCache", Long.class, String.class); - cache.put(42L, "What's the question again?"); - cache.get(42L); - } + cache.put(42L, "What's the question again?"); + cache.get(42L); + } - @Test - public void testEhcacheXMLConfig() throws Exception { - XmlConfiguration configuration = new XmlConfiguration(getClass().getResource("/org/ehcache/osgi/ehcache-osgi.xml").toURI().toURL(), getClass().getClassLoader()); + public static void testEhcacheXMLConfig() throws Exception { + XmlConfiguration configuration = new XmlConfiguration(TestMethods.class.getResource("/org/ehcache/osgi/ehcache-osgi.xml").toURI().toURL(), TestMethods.class.getClassLoader()); - assertEquals(Person.class, configuration.getCacheConfigurations().get("bar").getValueType()); - } + assertEquals(Person.class, configuration.getCacheConfigurations().get("bar").getValueType()); + } - public static class StringCopier extends ReadWriteCopier { + public static void testAllServicesAreAvailable() { + Set osgiAvailableClasses = + stream(spliterator(OsgiServiceLoader.load(ServiceFactory.class).iterator(), Long.MAX_VALUE, 0), false) + .map(f -> f.getClass().getName()) + .collect(toSet()); - @Override - public String copy(String obj) { - return new String(obj); + Set jdkAvailableClasses = of(EhcacheActivator.getCoreBundle().getBundles()) + .map(b -> b.adapt(BundleWiring.class).getClassLoader()) + .flatMap(cl -> + stream(spliterator(ServiceLoader.load(ServiceFactory.class, cl).iterator(), Long.MAX_VALUE, 0), false) + .map(f -> f.getClass().getName())) + .collect(toSet()); + + assertThat(osgiAvailableClasses, hasItems(jdkAvailableClasses.toArray(new String[0]))); } - } + public static class StringCopier extends ReadWriteCopier { + + @Override + public String copy(String obj) { + return new String(obj); + } + } + + } } diff --git a/osgi-test/src/test/java/org/ehcache/osgi/TransactionalOsgiTest.java b/osgi-test/src/test/java/org/ehcache/osgi/TransactionalOsgiTest.java new file mode 100644 index 0000000000..e5966016f8 --- /dev/null +++ b/osgi-test/src/test/java/org/ehcache/osgi/TransactionalOsgiTest.java @@ -0,0 +1,147 @@ +/* + * Copyright Terracotta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ehcache.osgi; + +import bitronix.tm.BitronixTransactionManager; +import bitronix.tm.TransactionManagerServices; +import org.ehcache.Cache; +import org.ehcache.CacheManager; +import org.ehcache.config.builders.CacheConfigurationBuilder; +import org.ehcache.config.builders.CacheManagerBuilder; +import org.ehcache.transactions.xa.configuration.XAStoreConfiguration; +import org.ehcache.transactions.xa.txmgr.btm.BitronixTransactionManagerLookup; +import org.ehcache.transactions.xa.txmgr.provider.LookupTransactionManagerProviderConfiguration; +import org.ehcache.xml.XmlConfiguration; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.ops4j.pax.exam.Configuration; +import org.ops4j.pax.exam.Option; +import org.ops4j.pax.exam.junit.PaxExam; +import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy; +import org.ops4j.pax.exam.spi.reactors.PerMethod; + +import static bitronix.tm.TransactionManagerServices.getTransactionManager; +import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManager; +import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; +import static org.ehcache.osgi.OsgiTestUtils.baseConfiguration; +import static org.ehcache.osgi.OsgiTestUtils.gradleBundle; +import static org.ehcache.osgi.OsgiTestUtils.jaxbConfiguration; +import static org.ehcache.osgi.OsgiTestUtils.jtaConfiguration; +import static org.ops4j.pax.exam.CoreOptions.options; + +@RunWith(PaxExam.class) +@ExamReactorStrategy(PerMethod.class) +public class TransactionalOsgiTest { + + @Configuration + public Option[] individualModules() { + return options( + gradleBundle("org.ehcache.modules:ehcache-api"), + gradleBundle("org.ehcache.modules:ehcache-core"), + gradleBundle("org.ehcache.modules:ehcache-impl"), + gradleBundle("org.ehcache.modules:ehcache-xml"), jaxbConfiguration(), + gradleBundle("org.ehcache:ehcache-transactions"), jtaConfiguration(), + + gradleBundle("org.terracotta:statistics"), + gradleBundle("org.ehcache:sizeof"), + gradleBundle("org.terracotta:offheap-store"), + gradleBundle("org.terracotta:terracotta-utilities-tools"), + + baseConfiguration("TransactionalOsgiTest", "individualModules") + ); + } + + @Configuration + public Option[] uberJar() { + return options( + gradleBundle("org.ehcache:ehcache"), jaxbConfiguration(), + gradleBundle("org.ehcache:ehcache-transactions"), jtaConfiguration(), + + baseConfiguration("TransactionalOsgiTest", "uberJar") + ); + } + + @Before + public void setUp() throws Exception { + TransactionManagerServices.getConfiguration().setJournal("null").setServerId(getClass().getSimpleName()); + } + + @After + public void tearDown() throws Exception { + if (TransactionManagerServices.isTransactionManagerRunning()) { + TransactionManagerServices.getTransactionManager().shutdown(); + } + } + + @Test + public void testProgrammaticConfiguration() throws Exception { + TestMethods.testProgrammaticConfiguration(); + } + + @Test + public void testXmlConfiguration() throws Exception { + TestMethods.testXmlConfiguration(); + } + + private static class TestMethods { + + public static void testProgrammaticConfiguration() throws Exception { + BitronixTransactionManager transactionManager = getTransactionManager(); + + try (CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() + .withClassLoader(TestMethods.class.getClassLoader()) + .using(new LookupTransactionManagerProviderConfiguration(BitronixTransactionManagerLookup.class)) + .withCache("xaCache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, heap(10)) + .withService(new XAStoreConfiguration("xaCache")).build()).build(true)) { + + Cache xaCache = cacheManager.getCache("xaCache", Long.class, String.class); + + transactionManager.begin(); + try { + xaCache.put(1L, "one"); + } catch (Throwable t) { + transactionManager.rollback(); + } + transactionManager.commit(); + } + transactionManager.shutdown(); + } + + public static void testXmlConfiguration() throws Exception { + BitronixTransactionManager transactionManager = getTransactionManager(); + + try (CacheManager cacheManager = newCacheManager( + new XmlConfiguration(TestMethods.class.getResource("ehcache-xa-osgi.xml"), TestMethods.class.getClassLoader()) + )) { + cacheManager.init(); + + Cache xaCache = cacheManager.getCache("xaCache", Long.class, String.class); + + transactionManager.begin(); + try { + xaCache.put(1L, "one"); + } catch (Throwable t) { + transactionManager.rollback(); + } + transactionManager.commit(); + } + transactionManager.shutdown(); + } + } +} diff --git a/osgi-test/src/test/java/org/ehcache/osgi/VersionUtil.java b/osgi-test/src/test/java/org/ehcache/osgi/VersionUtil.java deleted file mode 100644 index 9746899fbb..0000000000 --- a/osgi-test/src/test/java/org/ehcache/osgi/VersionUtil.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.ehcache.osgi; - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.Properties; - -/** - * Used to retrieve version of dependencies. Made to work with Gradle and in an IDE. - */ -public final class VersionUtil { - - private static Properties properties; - - private VersionUtil() { - } - - private static Properties get() { - // No need to support multithreading - if(properties == null) { - properties = new Properties(); - try { - properties.load(Files.newBufferedReader(Paths.get("../gradle.properties"), StandardCharsets.UTF_8)); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - return properties; - } - - public static String version(String systemProperty, String gradleProperty) { - String version = System.getProperty(systemProperty); - if(version != null) { - return version; - } - return get().getProperty(gradleProperty); - } - - public static String ehcacheOsgiJar() { - String version = System.getProperty("ehcache.osgi.jar"); - if(version != null) { - return version; - } - return "../dist/build/libs/ehcache-" + properties.getProperty("ehcacheVersion") + ".jar"; - } -} diff --git a/osgi-test/src/test/resources/org/ehcache/osgi/ehcache-107-osgi.xml b/osgi-test/src/test/resources/org/ehcache/osgi/ehcache-107-osgi.xml index 4a6c31aec9..5c757fccc5 100644 --- a/osgi-test/src/test/resources/org/ehcache/osgi/ehcache-107-osgi.xml +++ b/osgi-test/src/test/resources/org/ehcache/osgi/ehcache-107-osgi.xml @@ -15,11 +15,8 @@ --> + xmlns:jsr107='http://www.ehcache.org/v3/jsr107'> @@ -40,4 +37,4 @@ 20 - \ No newline at end of file + diff --git a/osgi-test/src/test/resources/org/ehcache/osgi/ehcache-osgi.xml b/osgi-test/src/test/resources/org/ehcache/osgi/ehcache-osgi.xml index 704738aa26..8155f7476e 100644 --- a/osgi-test/src/test/resources/org/ehcache/osgi/ehcache-osgi.xml +++ b/osgi-test/src/test/resources/org/ehcache/osgi/ehcache-osgi.xml @@ -30,9 +30,7 @@ ~ limitations under the License. --> - + java.lang.String org.ehcache.osgi.Person diff --git a/xml/src/test/resources/configs/systemprops.xml b/osgi-test/src/test/resources/org/ehcache/osgi/ehcache-xa-osgi.xml similarity index 50% rename from xml/src/test/resources/configs/systemprops.xml rename to osgi-test/src/test/resources/org/ehcache/osgi/ehcache-xa-osgi.xml index bf18c771d0..97624b7556 100644 --- a/xml/src/test/resources/configs/systemprops.xml +++ b/osgi-test/src/test/resources/org/ehcache/osgi/ehcache-xa-osgi.xml @@ -13,23 +13,19 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> + - + + + - + + java.lang.Long + java.lang.String + 10 + + - - - - - 5 - - - - ${ehcache.match} - 5 - - + diff --git a/settings.gradle b/settings.gradle index 3c3b31c74e..1117a15add 100644 --- a/settings.gradle +++ b/settings.gradle @@ -14,10 +14,23 @@ * limitations under the License. */ -include "api", "spi-tester", "core", "core-spi-test", "impl", "management", "transactions", "107", "xml", - "clustered", "clustered:common", "clustered:client", "clustered:server", "clustered:integration-test", "clustered:clustered-dist", "clustered:ops-tool", - "integration-test", "dist", "osgi-test", "demos", "demos:00-NoCache", "demos:01-CacheAside", "docs" +pluginManagement { + plugins { + id 'io.codearte.nexus-staging' version '0.30.0' + id 'org.owasp.dependencycheck' version '6.2.2' + id 'org.gretty' version '3.0.6' + id 'org.asciidoctor.jvm.base' version '3.3.2' + id 'org.unbroken-dome.xjc' version '2.0.0' + } -// This is recommended to make sure publishing works correctly with the new default to be established -// in Gradle 5. It can be removed when moving to Gradle 5. -enableFeaturePreview('STABLE_PUBLISHING') + includeBuild 'build-logic' +} + +include "ehcache-api", "ehcache-core", "ehcache-impl", "ehcache-107", "ehcache-xml", + "ehcache-management", "ehcache-transactions", "ehcache", + "spi-tester", "core-spi-test", + "clustered:ehcache-common-api", "clustered:ehcache-common", + "clustered:server:ehcache-service-api", "clustered:server:ehcache-service", "clustered:server:ehcache-entity", + "clustered:ehcache-client", "clustered:ehcache-clustered", "clustered:ops-tool", + "clustered:test-utils", "clustered:integration-test", + "integration-test", "osgi-test", "clustered:osgi-test", "demos", "demos:00-NoCache", "demos:01-CacheAside", "docs" diff --git a/spi-tester/.gitignore b/spi-tester/.gitignore deleted file mode 100755 index ae3c172604..0000000000 --- a/spi-tester/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/bin/ diff --git a/spi-tester/build.gradle b/spi-tester/build.gradle new file mode 100644 index 0000000000..f119623938 --- /dev/null +++ b/spi-tester/build.gradle @@ -0,0 +1,3 @@ +plugins { + id 'org.ehcache.build.conventions.java-library' +} diff --git a/spi-tester/gradle.properties b/spi-tester/gradle.properties deleted file mode 100644 index 607a9322c8..0000000000 --- a/spi-tester/gradle.properties +++ /dev/null @@ -1,2 +0,0 @@ -subPomName = Ehcache 3 SPI Tester module -subPomDesc = SPI Tester diff --git a/start_next_version.sh b/start_next_version.sh new file mode 100755 index 0000000000..325a68cc63 --- /dev/null +++ b/start_next_version.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash + +### +# Call this script when starting a new major or minor version. +# It will create the branch for the previous version and update all the required files to start the new version. +# +# See https://github.com/ehcache/ehcache3/wiki/dev.release for details. +# +### + +# to exit in case of error +set -e +# to see what's going on +set -v + +function pause { + echo + read -p "Press [enter] to continue" +} + +echo 'Welcome to the Ehcache next version wizard' +echo +echo 'This wizard will guide you through moving from a major.minor version to another. Some steps will be performed automatically, some will require your help' +echo + +read -e -p "Enter the next version (x.y): " version + +short_version=${version//[.]/} + +echo "Upgrading gradle.properties to ${version}" +sed -i '' "s/ehcacheVersion = .*/ehcacheVersion = ${version}-SNAPSHOT/" gradle.properties + +echo "Update docs sourcedir to sourcedir${short_version}" +find docs -type f -name '*.adoc' -exec sed -i '' "s/sourcedir[0-9][0-9]/sourcedir${short_version}/g" {} \; + +echo "Update version in site content to ${version}" +find docs -type f -name '*.adoc' -exec sed -i '' "s/version: [0-9]\.[0-9]/version: ${version}/" {} \; + +echo "Add new XSDs for ${version}" +sed -i '' "s/\/\/ needle_for_core_xsd/** Location for ${version}: \`http:\/\/www.ehcache.org\/schema\/ehcache-core-${version}.xsd\`\\ +\/\/ needle_for_core_xsd/" docs/src/docs/asciidoc/user/xsds.adoc +sed -i '' "s/\/\/ needle_for_107_xsd/** Location for ${version}: \`http:\/\/www.ehcache.org\/schema\/ehcache-107-ext-${version}.xsd\`\\ +\/\/ needle_for_107_xsd/" docs/src/docs/asciidoc/user/xsds.adoc +sed -i '' "s/\/\/ needle_for_transactions_xsd/** Location for ${version}: \`http:\/\/www.ehcache.org\/schema\/ehcache-tx-ext-${version}.xsd\`\\ +\/\/ needle_for_transactions_xsd/" docs/src/docs/asciidoc/user/xsds.adoc +sed -i '' "s/\/\/ needle_for_clustered_xsd/** Location for ${version}: \`http:\/\/www.ehcache.org\/schema\/ehcache-clustered-ext-${version}.xsd\`\\ +\/\/ needle_for_clustered_xsd/" docs/src/docs/asciidoc/user/xsds.adoc diff --git a/transactions/build.gradle b/transactions/build.gradle deleted file mode 100644 index a257a3e46a..0000000000 --- a/transactions/build.gradle +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -group = 'org.ehcache' - -apply plugin: EhOsgi -apply plugin: EhPomMangle - -dependencies { - api group: 'javax.transaction', name: 'jta', version: '1.1' - api project(':core') - implementation project(':impl') - implementation project(':xml') - implementation "org.terracotta:statistics:$parent.statisticVersion" - implementation (group: 'org.codehaus.btm', name: 'btm', version: '2.1.4') { - exclude group:'org.slf4j', module:'slf4j-api' - } - testImplementation project(':core-spi-test') - testCompile project(path: ':xml', configuration: 'testArchives') -} - -// For EhPomMangle -dependencies { - pomOnlyCompile "org.ehcache:ehcache:$parent.baseVersion" - pomOnlyProvided 'javax.transaction:jta:1.1', 'org.codehaus.btm:btm:2.1.4' -} - -test { - if (testJava.javaVersion.isJava9Compatible()) { - jvmArgs += ['--add-modules', 'java.xml.bind'] - } -} - -project.signing { - required { project.isReleaseVersion && project.gradle.taskGraph.hasTask("uploadArchives") } - sign project.configurations.getByName('archives') -} diff --git a/transactions/gradle.properties b/transactions/gradle.properties deleted file mode 100644 index e685b39fac..0000000000 --- a/transactions/gradle.properties +++ /dev/null @@ -1,5 +0,0 @@ -subPomName = Ehcache 3 transactions module -subPomDesc = The transactions module of Ehcache 3 -osgi = {"Export-Package" : ["!org.ehcache.transactions.xa.internal.*"],\ - "Import-Package" : ["bitronix.tm.*;resolution:=optional", "javax.transaction.*;resolution:=optional",\ - "!sun.misc.*", "!sun.security.action.*", "!org.ehcache.transactions.*"]} diff --git a/transactions/src/test/java/org/ehcache/transactions/xa/internal/xml/TxCacheServiceConfigurationParserTest.java b/transactions/src/test/java/org/ehcache/transactions/xa/internal/xml/TxCacheServiceConfigurationParserTest.java deleted file mode 100644 index 4136512cf6..0000000000 --- a/transactions/src/test/java/org/ehcache/transactions/xa/internal/xml/TxCacheServiceConfigurationParserTest.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ehcache.transactions.xa.internal.xml; - -import org.ehcache.transactions.xa.configuration.XAStoreConfiguration; -import org.junit.Test; -import org.w3c.dom.Node; - -import static org.ehcache.xml.ConfigurationParserTestHelper.assertElement; - -/** - * TxCacheServiceConfigurationParserTest - */ -public class TxCacheServiceConfigurationParserTest { - - @Test - public void testTranslateServiceConfiguration() { - TxCacheServiceConfigurationParser configTranslator = new TxCacheServiceConfigurationParser(); - XAStoreConfiguration storeConfiguration = new XAStoreConfiguration("my-unique-resource"); - - Node retElement = configTranslator.unparseServiceConfiguration(storeConfiguration); - String inputString = ""; - assertElement(inputString, retElement); - } - -} diff --git a/xml/.gitignore b/xml/.gitignore deleted file mode 100755 index ae3c172604..0000000000 --- a/xml/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/bin/ diff --git a/xml/build.gradle b/xml/build.gradle deleted file mode 100644 index 966c2747a6..0000000000 --- a/xml/build.gradle +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -plugins { - id 'org.unbroken-dome.xjc' version '1.1.3' - id 'com.github.hauner.jarTest' version '1.0.1' -} - -apply plugin: EhDeploy -apply plugin: 'com.github.hauner.jarTest' - -dependencies { - api project(':api') - implementation project(':core') - implementation project(':impl') - testImplementation 'org.xmlunit:xmlunit-core:2.6.0', 'org.xmlunit:xmlunit-matchers:2.6.0' - xjcClasspath 'org.jvnet.jaxb2_commons:jaxb2-fluent-api:3.0' - xjcClasspath 'org.jvnet.jaxb2_commons:jaxb2-basics-annotate:1.1.0' -} - -test { - if (testJava.javaVersion.isJava9Compatible()) { - jvmArgs += ['--add-modules', 'java.xml.bind'] - } -} - -xjcGenerate { - source = ['src/main/resources/ehcache-core.xsd', 'src/main/resources/ehcache-multi.xsd'] - extension = true - extraArgs = ['-Xfluent-api', '-Xannotate'] -} diff --git a/xml/gradle.properties b/xml/gradle.properties deleted file mode 100644 index ce44fae982..0000000000 --- a/xml/gradle.properties +++ /dev/null @@ -1,20 +0,0 @@ -# -# Copyright Terracotta, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -subPomName = Ehcache 3 XML Parsing module -subPomDesc = The module containing all XML parsing logic Ehcache 3 -osgi = {"Export-Package" : ["!org.ehcache.xml.model.*"],\ - "Import-Package" : ["!sun.misc.*", "!sun.security.action.*"]} diff --git a/xml/src/test/java/org/ehcache/xml/ConfigurationParserTestHelper.java b/xml/src/test/java/org/ehcache/xml/ConfigurationParserTestHelper.java deleted file mode 100644 index 45d40ae19e..0000000000 --- a/xml/src/test/java/org/ehcache/xml/ConfigurationParserTestHelper.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Terracotta, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ehcache.xml; - -import org.w3c.dom.Node; -import org.xmlunit.builder.Input; -import org.xmlunit.diff.DefaultNodeMatcher; -import org.xmlunit.diff.ElementSelectors; - -import static org.junit.Assert.assertThat; -import static org.xmlunit.matchers.CompareMatcher.isSimilarTo; - -/** - * ConfigurationParserTestHelper - */ -public class ConfigurationParserTestHelper { - public static void assertElement(String inputString, Node rootElement) { - assertThat(Input.from(rootElement), isSimilarTo(Input.from(inputString)).ignoreComments() - .ignoreWhitespace() - .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText))); - } -} diff --git a/xml/src/test/resources/configs/invalid-two-caches.xml b/xml/src/test/resources/configs/invalid-two-caches.xml deleted file mode 100644 index cbefbca7b9..0000000000 --- a/xml/src/test/resources/configs/invalid-two-caches.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - 2000 - - - - 2000 - - - diff --git a/xml/src/test/resources/configs/multi/empty.xml b/xml/src/test/resources/configs/multi/empty.xml deleted file mode 100644 index f5f0961bf1..0000000000 --- a/xml/src/test/resources/configs/multi/empty.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/xml/src/test/resources/configs/persistence-config.xml b/xml/src/test/resources/configs/persistence-config.xml deleted file mode 100644 index 7a4692ad43..0000000000 --- a/xml/src/test/resources/configs/persistence-config.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - \ No newline at end of file