From 28c4fc001c98a29e706703ba586a135d73617516 Mon Sep 17 00:00:00 2001
From: Louis Jacomet
Date: Fri, 9 Sep 2016 11:29:19 +0200
Subject: [PATCH 001/218] :snowflake: #1427 Bump version to 3.2.0-SNAPSHOT and
update README
---
README.adoc | 9 +++++----
build.gradle | 2 +-
2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/README.adoc b/README.adoc
index 01b173a7ce..0f77cfb325 100644
--- a/README.adoc
+++ b/README.adoc
@@ -12,16 +12,17 @@ For samples, documentation, and usage information, please see http://ehcache.org
== Current release
-We released 3.1.1 on July 18th 2016.
+We released 3.1.2 on September 9th 2016.
-The https://github.com/ehcache/ehcache3/releases/tag/v3.1.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.1.2[release notes] contain the links to the artifacts and the documentation to help you get started.
You should consider upgrading to 3.1.x as it does all 3.0.x does and more with a fully compatible API.
The only thing to note is that transactional support has been moved to a separate jar.
== Current development & next release
-We are now working on the missing features of the clustering tier of Ehcache 3 which will be included in upcoming 3.1.x releases.
-We may still do a last 3.0.x release to include all fixes that have been made on it, but this is now less a priority.
+We are now working on the missing features of the clustering tier of Ehcache 3 which will be included in upcoming 3.2.x releases.
+We may still do 3.1.x release to include all fixes that have been made on it, but this is now less a priority.
+There is no longer any plan for a 3.0.x release.
See the https://github.com/ehcache/ehcache3/milestones[milestones on GitHub] for more details on the current status.
diff --git a/build.gradle b/build.gradle
index b850391e31..70a2ad6539 100644
--- a/build.gradle
+++ b/build.gradle
@@ -16,7 +16,7 @@
import scripts.*
ext {
- baseVersion = '3.1.2-SNAPSHOT'
+ baseVersion = '3.2.0-SNAPSHOT'
// Third parties
offheapVersion = '2.2.2'
From 41f55674245ba9d35033bc23ffef1172c39382c2 Mon Sep 17 00:00:00 2001
From: Mathieu Carbou
Date: Wed, 7 Sep 2016 16:12:46 -0400
Subject: [PATCH 002/218] :heavy_plus_sign: ClusteringService.isConnected()
clustered management scheduled task crashes at cache manager close sevral
times because they are not aware of the closing of the underlying connection.
So this addition is to enable dependant services to check the connection
status.
---
.../service/DefaultClusteringService.java | 44 +++++++++----------
.../client/service/ClusteringService.java | 5 +++
.../service/DefaultClusteringServiceTest.java | 4 ++
3 files changed, 31 insertions(+), 22 deletions(-)
diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/DefaultClusteringService.java b/clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/DefaultClusteringService.java
index 77f46ff091..694e3500bc 100644
--- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/DefaultClusteringService.java
+++ b/clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/DefaultClusteringService.java
@@ -81,7 +81,7 @@ class DefaultClusteringService implements ClusteringService, EntityService {
private final ConcurrentMap knownPersistenceSpaces = new ConcurrentHashMap();
private final EhcacheClientEntity.Timeouts operationTimeouts;
- private Connection clusterConnection;
+ private volatile Connection clusterConnection;
private EhcacheClientEntityFactory entityFactory;
private EhcacheClientEntity entity;
@@ -125,7 +125,7 @@ public ClientEntityFactory newClientEntityFactory(St
return new AbstractClientEntityFactory(entityIdentifier, entityType, entityVersion, configuration) {
@Override
protected Connection getConnection() {
- if (clusterConnection == null) {
+ if (!isConnected()) {
throw new IllegalStateException(getClass().getSimpleName() + " not started.");
}
return clusterConnection;
@@ -133,6 +133,11 @@ protected Connection getConnection() {
};
}
+ @Override
+ public boolean isConnected() {
+ return clusterConnection != null;
+ }
+
@Override
public void start(final ServiceProvider serviceProvider) {
initClusterConnection();
@@ -153,12 +158,7 @@ public void start(final ServiceProvider serviceProvider) {
}
} catch (RuntimeException e) {
entityFactory = null;
- try {
- clusterConnection.close();
- clusterConnection = null;
- } catch (IOException ex) {
- LOGGER.warn("Error closing cluster connection: " + ex);
- }
+ closeConnection();
throw e;
}
}
@@ -211,12 +211,7 @@ public void startForMaintenance(ServiceProvider serviceProv
if (!entityFactory.acquireLeadership(entityIdentifier)) {
entityFactory = null;
- try {
- clusterConnection.close();
- clusterConnection = null;
- } catch (IOException e) {
- LOGGER.warn("Error closing cluster connection: " + e);
- }
+ closeConnection();
throw new IllegalStateException("Couldn't acquire cluster-wide maintenance lease");
}
inMaintenance = true;
@@ -238,14 +233,7 @@ public void stop() {
entity = null;
- try {
- if (clusterConnection != null) {
- clusterConnection.close();
- clusterConnection = null;
- }
- } catch (IOException ex) {
- throw new RuntimeException(ex);
- }
+ closeConnection();
}
@Override
@@ -444,6 +432,18 @@ public void releaseServerStoreProxy(ServerStoreProxy storeProxy) {
}
}
+ private void closeConnection() {
+ Connection conn = clusterConnection;
+ clusterConnection = null;
+ if(conn != null) {
+ try {
+ conn.close();
+ } catch (IOException e) {
+ LOGGER.warn("Error closing cluster connection: " + e);
+ }
+ }
+ }
+
/**
* Supplies the identifier to use for identifying a client-side cache to its server counterparts.
*/
diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/service/ClusteringService.java b/clustered/client/src/main/java/org/ehcache/clustered/client/service/ClusteringService.java
index bb89c24456..b0cc5ed2f6 100644
--- a/clustered/client/src/main/java/org/ehcache/clustered/client/service/ClusteringService.java
+++ b/clustered/client/src/main/java/org/ehcache/clustered/client/service/ClusteringService.java
@@ -31,6 +31,11 @@ public interface ClusteringService extends PersistableResourceService {
ClusteringServiceConfiguration getConfiguration();
+ /**
+ * @return true if a connection to a cluster exists
+ */
+ boolean isConnected();
+
/**
* Gets a {@link ServerStoreProxy} though which a server-resident {@code ServerStore} is accessed.
*
diff --git a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/DefaultClusteringServiceTest.java b/clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/DefaultClusteringServiceTest.java
index f33cf67af7..b1169a3c72 100644
--- a/clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/DefaultClusteringServiceTest.java
+++ b/clustered/client/src/test/java/org/ehcache/clustered/client/internal/service/DefaultClusteringServiceTest.java
@@ -216,7 +216,9 @@ public void testStartStopAutoCreate() throws Exception {
.autoCreate()
.build();
DefaultClusteringService service = new DefaultClusteringService(configuration);
+ assertThat(service.isConnected(), is(false));
service.start(null);
+ assertThat(service.isConnected(), is(true));
assertThat(UnitTestConnectionService.getConnectionProperties(clusterUri).size(), is(1));
List activeEntities = observableEhcacheServerEntityService.getServedActiveEntities();
@@ -326,7 +328,9 @@ public void testStartForMaintenanceAutoStart() throws Exception {
.autoCreate()
.build();
DefaultClusteringService service = new DefaultClusteringService(configuration);
+ assertThat(service.isConnected(), is(false));
service.startForMaintenance(null);
+ assertThat(service.isConnected(), is(true));
assertThat(UnitTestConnectionService.getConnectionProperties(clusterUri).size(), is(1));
List activeEntities = observableEhcacheServerEntityService.getServedActiveEntities();
From 16a484620b248017a8bddecfe0ca4d308ac61216 Mon Sep 17 00:00:00 2001
From: Mathieu Carbou
Date: Wed, 7 Sep 2016 16:21:14 -0400
Subject: [PATCH 003/218] :art: Moving ClusteredManagementServiceTest from
management to clustered
---
build.gradle | 2 +-
clustered/integration-test/build.gradle | 8 ++
.../AbstractClusteringManagementTest.java | 108 +++++++---------
.../ClusteringManagementServiceTest.java | 86 ++++---------
.../EhcacheManagerToStringTest.java | 120 +++++-------------
.../test/resources/clusteredConfiguration.txt | 4 +-
.../test/resources/simpleConfiguration.txt | 6 +-
management/build.gradle | 26 +---
.../DefaultClusteringManagementService.java | 9 +-
.../registry/DefaultCollectorService.java | 10 +-
.../registry/DefaultCollectorServiceTest.java | 4 +-
...rg.terracotta.connection.ConnectionService | 1 -
.../ehcache-management-clustered.xml | 46 -------
13 files changed, 138 insertions(+), 292 deletions(-)
rename {management/src/test/java/org/ehcache/management/cluster => clustered/integration-test/src/test/java/org/ehcache/clustered/management}/AbstractClusteringManagementTest.java (58%)
rename {management/src/test/java/org/ehcache/management/cluster => clustered/integration-test/src/test/java/org/ehcache/clustered/management}/ClusteringManagementServiceTest.java (78%)
rename {management/src/test/java/org/ehcache/core => clustered/integration-test/src/test/java/org/ehcache/clustered/management}/EhcacheManagerToStringTest.java (56%)
rename {management => clustered/integration-test}/src/test/resources/clusteredConfiguration.txt (90%)
rename {management => clustered/integration-test}/src/test/resources/simpleConfiguration.txt (81%)
delete mode 100644 management/src/test/resources/META-INF/services/org.terracotta.connection.ConnectionService
delete mode 100644 management/src/test/resources/ehcache-management-clustered.xml
diff --git a/build.gradle b/build.gradle
index 70a2ad6539..f9282a4797 100644
--- a/build.gradle
+++ b/build.gradle
@@ -26,7 +26,7 @@ ext {
sizeofVersion = '0.3.0'
// Clustered
- terracottaPlatformVersion = '5.0.6.beta5'
+ terracottaPlatformVersion = '5.0.6.beta6'
managementVersion = terracottaPlatformVersion
terracottaApisVersion = '1.0.6.beta'
terracottaCoreVersion = '5.0.6-beta2'
diff --git a/clustered/integration-test/build.gradle b/clustered/integration-test/build.gradle
index cf833e2461..1537eeed1f 100644
--- a/clustered/integration-test/build.gradle
+++ b/clustered/integration-test/build.gradle
@@ -17,10 +17,16 @@
dependencies {
testCompile project(':dist')
testCompile project(':clustered:clustered-dist')
+ testCompile project(':management')
+ testCompile "org.terracotta.management:management-entity-client:$parent.managementVersion"
testCompile group:'org.terracotta', name:'galvan-support', version: galvanVersion
testCompile group:'com.google.code.tempus-fugit', name:'tempus-fugit', version:'1.1'
testCompile group: 'javax.cache', name: 'cache-api', version: jcacheVersion
+
+ testCompile "org.terracotta.management:management-entity-server:$parent.managementVersion:plugin"
+ testCompile "org.terracotta.management:monitoring-service:$parent.managementVersion:plugin"
+ testCompile "org.terracotta.management:monitoring-service-entity:$parent.managementVersion:plugin"
}
task unzipKit(type: Copy) {
@@ -42,9 +48,11 @@ test {
dependsOn unzipKit
executable = MavenToolchain.javaExecutable(JavaVersion.VERSION_1_8, 'java')
environment 'JAVA_HOME', MavenToolchain.javaHome(JavaVersion.VERSION_1_8)
+ // If you want to see all mutations of the voltron monitoring tree, add to JAVA_OPTS: -Dorg.terracotta.management.service.monitoring.VoltronMonitoringService.DEBUG=true
environment 'JAVA_OPTS', '-Dcom.tc.l2.lockmanager.greedy.locks.enabled=false'
//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 'managementPlugins', ["management-model", "management-entity-server", "monitoring-service", "monitoring-service-entity"].collect { String artifact -> project.configurations.testCompile.find { it.name.startsWith("$artifact-$parent.managementVersion") } }.join(':')
// Uncomment to include client logging in console output
// testLogging.showStandardStreams = true
}
diff --git a/management/src/test/java/org/ehcache/management/cluster/AbstractClusteringManagementTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/management/AbstractClusteringManagementTest.java
similarity index 58%
rename from management/src/test/java/org/ehcache/management/cluster/AbstractClusteringManagementTest.java
rename to clustered/integration-test/src/test/java/org/ehcache/clustered/management/AbstractClusteringManagementTest.java
index 2aab98bbf8..225c589d08 100644
--- a/management/src/test/java/org/ehcache/management/cluster/AbstractClusteringManagementTest.java
+++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/management/AbstractClusteringManagementTest.java
@@ -13,43 +13,33 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.ehcache.management.cluster;
+package org.ehcache.clustered.management;
-import org.ehcache.clustered.client.internal.EhcacheClientEntityService;
-import org.ehcache.clustered.client.internal.lock.VoltronReadWriteLockEntityClientService;
-import org.ehcache.clustered.lock.server.VoltronReadWriteLockServerEntityService;
-import org.ehcache.clustered.server.EhcacheServerEntityService;
import org.junit.After;
-import org.junit.AfterClass;
+import org.junit.Assert;
import org.junit.BeforeClass;
+import org.junit.ClassRule;
import org.terracotta.connection.Connection;
import org.terracotta.connection.ConnectionFactory;
import org.terracotta.management.entity.management.ManagementAgentConfig;
import org.terracotta.management.entity.management.client.ContextualReturnListener;
-import org.terracotta.management.entity.management.client.ManagementAgentEntityClientService;
import org.terracotta.management.entity.management.client.ManagementAgentEntityFactory;
import org.terracotta.management.entity.management.client.ManagementAgentService;
-import org.terracotta.management.entity.management.server.ManagementAgentEntityServerService;
import org.terracotta.management.entity.monitoring.client.MonitoringServiceEntity;
-import org.terracotta.management.entity.monitoring.client.MonitoringServiceEntityClientService;
import org.terracotta.management.entity.monitoring.client.MonitoringServiceEntityFactory;
-import org.terracotta.management.entity.monitoring.server.MonitoringServiceEntityServerService;
import org.terracotta.management.model.call.ContextualReturn;
import org.terracotta.management.model.call.Parameter;
import org.terracotta.management.model.cluster.ClientIdentifier;
import org.terracotta.management.model.context.Context;
import org.terracotta.management.model.stats.ContextualStatistics;
-import org.terracotta.offheapresource.OffHeapResourcesConfiguration;
-import org.terracotta.offheapresource.OffHeapResourcesProvider;
-import org.terracotta.offheapresource.config.OffheapResourcesType;
-import org.terracotta.offheapresource.config.ResourceType;
-import org.terracotta.passthrough.PassthroughClusterControl;
-import org.terracotta.passthrough.PassthroughServer;
+import org.terracotta.testing.rules.BasicExternalCluster;
+import org.terracotta.testing.rules.Cluster;
+import java.io.File;
import java.io.Serializable;
-import java.math.BigInteger;
-import java.net.URI;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
import java.util.Properties;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
@@ -66,53 +56,31 @@
public abstract class AbstractClusteringManagementTest {
+ private static final String RESOURCE_CONFIG =
+ ""
+ + ""
+ + "64"
+ + "" +
+ "\n";
+
protected static MonitoringServiceEntity consumer;
- private static PassthroughClusterControl stripeControl;
+ @ClassRule
+ public static Cluster CLUSTER = new BasicExternalCluster(new File("build/cluster"), 1, getManagementPlugins(), "", RESOURCE_CONFIG, "");
@BeforeClass
public static void beforeClass() throws Exception {
- PassthroughServer activeServer = new PassthroughServer();
- activeServer.setServerName("server-1");
- activeServer.setBindPort(9510);
- activeServer.setGroupPort(9610);
-
- // management agent entity
- activeServer.registerServerEntityService(new ManagementAgentEntityServerService());
- activeServer.registerClientEntityService(new ManagementAgentEntityClientService());
-
- // ehcache entity
- activeServer.registerServerEntityService(new EhcacheServerEntityService());
- activeServer.registerClientEntityService(new EhcacheClientEntityService());
-
- // RW lock entity (required by ehcache)
- activeServer.registerServerEntityService(new VoltronReadWriteLockServerEntityService());
- activeServer.registerClientEntityService(new VoltronReadWriteLockEntityClientService());
-
- activeServer.registerServerEntityService(new MonitoringServiceEntityServerService());
- activeServer.registerClientEntityService(new MonitoringServiceEntityClientService());
-
- // off-heap service
- OffheapResourcesType offheapResourcesType = new OffheapResourcesType();
- ResourceType resourceType = new ResourceType();
- resourceType.setName("primary-server-resource");
- resourceType.setUnit(org.terracotta.offheapresource.config.MemoryUnit.MB);
- resourceType.setValue(BigInteger.TEN);
- offheapResourcesType.getResource().add(resourceType);
- activeServer.registerServiceProvider(new OffHeapResourcesProvider(), new OffHeapResourcesConfiguration(offheapResourcesType));
-
- stripeControl = new PassthroughClusterControl("server-1", activeServer);
-
- consumer = new MonitoringServiceEntityFactory(ConnectionFactory.connect(URI.create("passthrough://server-1:9510/cluster-1"), new Properties())).retrieveOrCreate("MonitoringConsumerEntity");
+ CLUSTER.getClusterControl().waitForActive();
+
+ consumer = new MonitoringServiceEntityFactory(ConnectionFactory.connect(CLUSTER.getConnectionURI(), new Properties())).retrieveOrCreate("MonitoringConsumerEntity");
+ // buffer for client-side notifications
consumer.createBestEffortBuffer("client-notifications", 1024, Serializable[].class);
+ // buffer for client-side stats
consumer.createBestEffortBuffer("client-statistics", 1024, Serializable[].class);
- }
-
- @AfterClass
- public static void afterClass() throws Exception {
- if (stripeControl != null) {
- stripeControl.tearDown();
- }
+ // buffer for platform topology changes
+ consumer.createBestEffortBuffer("platform-notifications", 1024, Serializable[].class);
+ // buffer for entity notifications
+ consumer.createBestEffortBuffer("entity-notifications", 1024, Serializable[].class);
}
@After
@@ -126,8 +94,9 @@ protected final void clear() {
}
protected static void sendManagementCallToCollectStats(String... statNames) throws Exception {
- try (Connection managementConsole = ConnectionFactory.connect(URI.create("passthrough://server-1:9510/"), new Properties())) {
- ManagementAgentService agent = new ManagementAgentService(new ManagementAgentEntityFactory(managementConsole).retrieveOrCreate(new ManagementAgentConfig()));
+ Connection managementConnection = CLUSTER.newConnection();
+ try {
+ ManagementAgentService agent = new ManagementAgentService(new ManagementAgentEntityFactory(managementConnection).retrieveOrCreate(new ManagementAgentConfig()));
assertThat(agent.getManageableClients().size(), equalTo(2));
@@ -143,15 +112,15 @@ protected static void sendManagementCallToCollectStats(String... statNames) thro
assertThat(client, is(notNullValue()));
final ClientIdentifier ehcacheClientIdentifier = client;
- CountDownLatch callCompleted = new CountDownLatch(1);
- AtomicReference managementCallId = new AtomicReference<>();
- BlockingQueue> returns = new LinkedBlockingQueue<>();
+ final CountDownLatch callCompleted = new CountDownLatch(1);
+ final AtomicReference managementCallId = new AtomicReference();
+ final BlockingQueue> returns = new LinkedBlockingQueue>();
agent.setContextualReturnListener(new ContextualReturnListener() {
@Override
public void onContextualReturn(ClientIdentifier from, String id, ContextualReturn> aReturn) {
try {
- assertEquals(ehcacheClientIdentifier, from);
+ Assert.assertEquals(ehcacheClientIdentifier, from);
// make sure the call completed
callCompleted.await(10, TimeUnit.SECONDS);
assertEquals(managementCallId.get(), id);
@@ -176,6 +145,8 @@ public void onContextualReturn(ClientIdentifier from, String id, ContextualRetur
// ensure the call is made
returns.take();
+ } finally {
+ managementConnection.close();
}
}
@@ -186,4 +157,13 @@ protected static ContextualStatistics[] waitForNextStats() {
return (ContextualStatistics[]) serializables[1];
}
+ private static List getManagementPlugins() {
+ String[] paths = System.getProperty("managementPlugins").split(":");
+ List plugins = new ArrayList(paths.length);
+ for (String path : paths) {
+ plugins.add(new File(path));
+ }
+ return plugins;
+ }
+
}
diff --git a/management/src/test/java/org/ehcache/management/cluster/ClusteringManagementServiceTest.java b/clustered/integration-test/src/test/java/org/ehcache/clustered/management/ClusteringManagementServiceTest.java
similarity index 78%
rename from management/src/test/java/org/ehcache/management/cluster/ClusteringManagementServiceTest.java
rename to clustered/integration-test/src/test/java/org/ehcache/clustered/management/ClusteringManagementServiceTest.java
index 34627247d9..75386e89a4 100644
--- a/management/src/test/java/org/ehcache/management/cluster/ClusteringManagementServiceTest.java
+++ b/clustered/integration-test/src/test/java/org/ehcache/clustered/management/ClusteringManagementServiceTest.java
@@ -13,12 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.ehcache.management.cluster;
+package org.ehcache.clustered.management;
import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.ehcache.Status;
-import org.ehcache.ValueSupplier;
import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder;
import org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder;
import org.ehcache.config.builders.CacheConfigurationBuilder;
@@ -27,14 +26,11 @@
import org.ehcache.config.units.MemoryUnit;
import org.ehcache.management.config.EhcacheStatisticsProviderConfiguration;
import org.ehcache.management.registry.DefaultManagementRegistryConfiguration;
-import org.ehcache.xml.XmlConfiguration;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.Timeout;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
import org.terracotta.management.entity.management.ManagementAgentConfig;
import org.terracotta.management.entity.management.client.ManagementAgentEntityFactory;
import org.terracotta.management.model.capabilities.Capability;
@@ -45,11 +41,10 @@
import org.terracotta.management.model.stats.primitive.Counter;
import java.io.Serializable;
-import java.net.URI;
import java.util.Arrays;
-import java.util.Collection;
import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder;
import static org.hamcrest.CoreMatchers.equalTo;
@@ -59,68 +54,41 @@
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
import static org.junit.Assert.assertThat;
-@RunWith(Parameterized.class)
public class ClusteringManagementServiceTest extends AbstractClusteringManagementTest {
- @Parameterized.Parameters
- public static Collection
*
- * When used within the default serialization provider, there are additional requirements.
- * The implementations must define either or both of the two constructors:
- *
- *
Serializer(ClassLoader loader)
- *
This constructor is used to initialize the serializer for transient caches.
- *
This constructor is used to initialize the serializer for persistent caches and allows them to store any relevant
- * state in the provided repository.
- *
+ * When used within the default serialization provider, there is an additional requirement.
+ * The implementations must define a constructor that takes in a {@code ClassLoader}.
* The {@code ClassLoader} value may be {@code null}. If not {@code null}, the class loader
* instance provided should be used during deserialization to load classes needed by the deserialized objects.
*
diff --git a/api/src/main/java/org/ehcache/spi/serialization/StatefulSerializer.java b/api/src/main/java/org/ehcache/spi/serialization/StatefulSerializer.java
new file mode 100644
index 0000000000..3b3d0e0ff0
--- /dev/null
+++ b/api/src/main/java/org/ehcache/spi/serialization/StatefulSerializer.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.spi.serialization;
+
+import org.ehcache.spi.persistence.StateRepository;
+
+/**
+ * Implementations of this interface can have their state maintained in a {@code StateRepository}.
+ * The state will be maintained by the authoritative tier of the cache for which this is configured.
+ *
+ * Implementations must be thread-safe.
+ *
+ *
+ * When used within the default serialization provider, there is an additional constructor requirement.
+ * The implementations must define a constructor that takes in a {@code ClassLoader}.
+ * Post instantiation, the state repository will be injected with the {@code init} method invocation.
+ * This is guaranteed to happen before any serialization/deserialization interaction.
+ *
+ *
+ * @param the type of the instances to serialize
+ *
+ * @see Serializer
+ */
+public interface StatefulSerializer extends Serializer {
+
+ /**
+ * This method is used to inject a {@code StateRepository} to the serializer
+ * by the authoritative tier of a cache during the cache initialization.
+ * The passed in state repository will have the persistent properties of the injecting tier.
+ *
+ * @param stateRepository the state repository
+ */
+ void init(StateRepository stateRepository);
+}
diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/ClusteredStateRepository.java b/clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/ClusteredStateRepository.java
index 06b20fcb92..5885ee523b 100644
--- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/ClusteredStateRepository.java
+++ b/clustered/client/src/main/java/org/ehcache/clustered/client/internal/service/ClusteredStateRepository.java
@@ -18,7 +18,6 @@
import org.ehcache.clustered.client.internal.EhcacheClientEntity;
import org.ehcache.clustered.client.service.ClusteringService;
-import org.ehcache.impl.internal.concurrent.ConcurrentHashMap;
import org.ehcache.spi.persistence.StateRepository;
import java.io.Serializable;
diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ClusteredStore.java b/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ClusteredStore.java
index 17a8b29ddf..a7fc5fb5e5 100644
--- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ClusteredStore.java
+++ b/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/ClusteredStore.java
@@ -51,6 +51,9 @@
import org.ehcache.core.spi.time.TimeSourceService;
import org.ehcache.impl.config.loaderwriter.DefaultCacheLoaderWriterConfiguration;
import org.ehcache.impl.internal.events.NullStoreEventDispatcher;
+import org.ehcache.spi.persistence.StateRepository;
+import org.ehcache.spi.serialization.Serializer;
+import org.ehcache.spi.serialization.StatefulSerializer;
import org.ehcache.spi.service.ServiceProvider;
import org.ehcache.spi.service.Service;
import org.ehcache.spi.service.ServiceConfiguration;
@@ -574,11 +577,34 @@ public void initStore(final Store, ?> resource) {
throw new IllegalArgumentException("Given clustered tier is not managed by this provider : " + resource);
}
final ClusteredStore clusteredStore = (ClusteredStore) resource;
+ ClusteredCacheIdentifier cacheIdentifier = storeConfig.getCacheIdentifier();
try {
- clusteredStore.storeProxy = clusteringService.getServerStoreProxy(storeConfig.getCacheIdentifier(), storeConfig.getStoreConfig(), storeConfig.getConsistency());
+ clusteredStore.storeProxy = clusteringService.getServerStoreProxy(cacheIdentifier, storeConfig.getStoreConfig(), storeConfig.getConsistency());
} catch (CachePersistenceException e) {
- throw new RuntimeException("Unable to create clustered tier proxy - " + storeConfig.getCacheIdentifier(), e);
+ throw new RuntimeException("Unable to create clustered tier proxy - " + cacheIdentifier, e);
}
+
+ Serializer keySerializer = clusteredStore.codec.getKeySerializer();
+ if (keySerializer instanceof StatefulSerializer) {
+ StateRepository stateRepository = null;
+ try {
+ stateRepository = clusteringService.getStateRepositoryWithin(cacheIdentifier, cacheIdentifier.getId() + "-Key");
+ } catch (CachePersistenceException e) {
+ throw new RuntimeException(e);
+ }
+ ((StatefulSerializer)keySerializer).init(stateRepository);
+ }
+ Serializer valueSerializer = clusteredStore.codec.getValueSerializer();
+ if (valueSerializer instanceof StatefulSerializer) {
+ StateRepository stateRepository = null;
+ try {
+ stateRepository = clusteringService.getStateRepositoryWithin(cacheIdentifier, cacheIdentifier.getId() + "-Value");
+ } catch (CachePersistenceException e) {
+ throw new RuntimeException(e);
+ }
+ ((StatefulSerializer)valueSerializer).init(stateRepository);
+ }
+
clusteredStore.storeProxy.addInvalidationListener(new ServerStoreProxy.InvalidationListener() {
@Override
public void onInvalidateHash(long hash) {
diff --git a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/operations/codecs/OperationsCodec.java b/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/operations/codecs/OperationsCodec.java
index c6868312b6..16bbf347f9 100644
--- a/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/operations/codecs/OperationsCodec.java
+++ b/clustered/client/src/main/java/org/ehcache/clustered/client/internal/store/operations/codecs/OperationsCodec.java
@@ -42,4 +42,12 @@ public Operation decode(ByteBuffer buffer) {
buffer.rewind();
return opCode.decode(buffer, keySerializer, valueSerializer);
}
+
+ public Serializer getKeySerializer() {
+ return keySerializer;
+ }
+
+ public Serializer getValueSerializer() {
+ return valueSerializer;
+ }
}
diff --git a/docs/src/docs/asciidoc/user/serializers-copiers.adoc b/docs/src/docs/asciidoc/user/serializers-copiers.adoc
index 27be820b30..db867df01e 100644
--- a/docs/src/docs/asciidoc/user/serializers-copiers.adoc
+++ b/docs/src/docs/asciidoc/user/serializers-copiers.adoc
@@ -125,40 +125,38 @@ of the serialized types as they might not be available in the current class load
[[persistent-vs-transient-caches]]
==== Persistent vs. transient caches
-When configured on a persistent cache, serializers may need to persist and restore their state across restarts.
-For clustered caches there might be an additional requirement that the state of the serializer must be visible to all clients using the same cache(configured with the same serializer).
-To address these requirement you have to implement a constructor with the following signature:
+All custom serializers must have a constructor with the following signature:
```java
-public MySerializer(ClassLoader classLoader, StateRepository stateRepository) {
+public MySerializer(ClassLoader classLoader) {
}
```
-otherwise persistent caches won't be able to use your serializer.
-
-The `StateRepository.getPersistentConcurrentMap()` provides a `ConcurrentMap` that you can use to store any relevant state.
-The users don't have to worry about the persistence aspects of this map as it is taken care by `Ehcache`.
-In the case of a disk persistent cache, the contents of the map will be persisted locally on to the disk.
-For clustered caches the contents are persisted in the cluster itself so that other clients using the same cache can also access the contents of the map.
-
-Attempting to configure a serializer that lacks such constructor on a persistent cache using either of
+Attempting to configure a serializer that lacks such constructor on a cache using either of
`CacheConfigurationBuilder.withKeySerializer(Class extends Serializer> keySerializerClass)` or
`CacheConfigurationBuilder.withValueSerializer(Class extends Serializer> valueSerializerClass)`
will be sanctioned with an exception upon cache initialization.
-Configuring a serializer that lacks such constructor by instance on a persistent cache using either of
-`CacheConfigurationBuilder.withKeySerializer(Serializer keySerializer)` or `CacheConfigurationBuilder.withValueSerializer(Serializer valueSerializer)`
-will work, but the responsibility of persisting and restoring the serializer's state across restarts lies on you.
+But if an instance of the serializer is configured using either of
+`CacheConfigurationBuilder.withKeySerializer(Serializer keySerializer)` or
+`CacheConfigurationBuilder.withValueSerializer(Serializer valueSerializer)`
+it will work since the instantiation is done by the user code itself.
+
+Registering a serializer that lacks such constructor at the cache manager level will prevent it from being chosen for caches.
+
+Custom serializer implementations could have some state that is used in the serialization/deserialization process.
+When configured on a persistent cache, the state of such serializers needs to be persisted across restarts.
-On caches that have no persistent capable store, serializers must have a constructor:
+To address these requirements you can have a `StatefulSerializer` implementation.
+`StatefulSerializer` is a specialized `Serializer` with an additional _init_ method with the following signature:
```java
-public MySerializer(ClassLoader classLoader) {
+public void init(StateRepository repository) {
}
```
-Attempting to configure a serializer that lacks such constructor on a transient cache using either of
-`CacheConfigurationBuilder.withKeySerializer(Class extends Serializer> keySerializerClass)` or
-`CacheConfigurationBuilder.withValueSerializer(Class extends Serializer> valueSerializerClass)`
-will be sanctioned with an exception upon cache initialization.
+The `StateRepository.getPersistentConcurrentMap(String, Class, Class)` provides a `ConcurrentMap` that you can use to store any relevant state.
+The `StateRepository` is provided by the authoritative tier of the cache and hence will have the same persistence properties of that tier.
+For persistent caches it is highly recommended that all state is stored in these maps as the users won't have to worry about the persistence aspects of this map as it is taken care by `Ehcache`.
-Registering a serializer that lacks such constructor at the cache manager level will prevent it from being chosen for persistent caches.
+* In the case of a disk persistent cache, the contents of the map will be persisted locally on to the disk.
+* For clustered caches the contents are persisted in the cluster itself so that other clients using the same cache can also access the contents of the map.
NOTE: The constructor with the signature `(ClassLoader classLoader, FileBasedPersistenceContext persistenceContext)`
that existed in v3.0 is still supported to respect backward compatibility but the usage is limited to disk based caches.
diff --git a/impl/src/main/java/org/ehcache/impl/config/serializer/DefaultSerializationProviderConfiguration.java b/impl/src/main/java/org/ehcache/impl/config/serializer/DefaultSerializationProviderConfiguration.java
index 55dc152642..e0cda53697 100644
--- a/impl/src/main/java/org/ehcache/impl/config/serializer/DefaultSerializationProviderConfiguration.java
+++ b/impl/src/main/java/org/ehcache/impl/config/serializer/DefaultSerializationProviderConfiguration.java
@@ -20,7 +20,6 @@
import java.util.LinkedHashMap;
import java.util.Map;
-import org.ehcache.spi.persistence.StateRepository;
import org.ehcache.spi.serialization.SerializationProvider;
import org.ehcache.spi.serialization.Serializer;
import org.ehcache.core.spi.service.FileBasedPersistenceContext;
@@ -94,31 +93,27 @@ public DefaultSerializationProviderConfiguration addSerializerFor(Class s
throw new NullPointerException("Serializer class cannot be null");
}
- boolean transientConstructorPresent;
- boolean persistentConstructorPresent;
-
- if(transientConstructorPresent = isConstructorPresent(serializerClass, ClassLoader.class)) {
- if (!overwrite && transientSerializers.containsKey(serializableClass)) {
- throw new IllegalArgumentException("Duplicate transient serializer for class : " + serializableClass.getName());
- } else {
- transientSerializers.put(serializableClass, serializerClass);
+ boolean baseConstructorPresent = isConstructorPresent(serializerClass, ClassLoader.class);
+ if (baseConstructorPresent) {
+ if (!overwrite) {
+ if (transientSerializers.containsKey(serializableClass)) {
+ throw new IllegalArgumentException("Duplicate transient serializer for class : " + serializableClass.getName());
+ }
+ if (persistentSerializers.containsKey(serializableClass)) {
+ throw new IllegalArgumentException("Duplicate persistent serializer for class : " + serializableClass.getName());
+ }
}
+ transientSerializers.put(serializableClass, serializerClass);
+ persistentSerializers.put(serializableClass, serializerClass);
}
- if (persistentConstructorPresent = isConstructorPresent(serializerClass, ClassLoader.class, StateRepository.class)) {
- if (!overwrite && persistentSerializers.containsKey(serializableClass)) {
- throw new IllegalArgumentException("Duplicate persistent serializer for class : " + serializableClass.getName());
- } else {
- persistentSerializers.put(serializableClass, serializerClass);
- }
+ boolean legacyConstructorPresent = isConstructorPresent(serializerClass, ClassLoader.class, FileBasedPersistenceContext.class);
+ if(!baseConstructorPresent && !legacyConstructorPresent) {
+ throw new IllegalArgumentException("The serializer: " + serializerClass.getName()
+ + " does not meet the constructor requirements for either transient or persistent caches.");
}
- if (isConstructorPresent(serializerClass, ClassLoader.class, FileBasedPersistenceContext.class)) {
- if (persistentConstructorPresent) {
- throw new IllegalArgumentException("Serializer cannot have constructors taking (ClassLoader, StateRepository) and (ClassLoader, FileBasedPersistenceContext)" +
- " - you should remove the second one as it is deprecated since version 3.1.0");
- }
- persistentConstructorPresent = true;
+ if (!baseConstructorPresent && legacyConstructorPresent) {
if (!overwrite && persistentSerializers.containsKey(serializableClass)) {
throw new IllegalArgumentException("Duplicate persistent serializer for class : " + serializableClass.getName());
} else {
@@ -126,10 +121,6 @@ public DefaultSerializationProviderConfiguration addSerializerFor(Class s
}
}
- if(!transientConstructorPresent && !persistentConstructorPresent) {
- throw new IllegalArgumentException("The serializer: " + serializerClass.getName()
- + " does not meet the constructor requirements for either transient or persistent caches.");
- }
return this;
}
diff --git a/impl/src/main/java/org/ehcache/impl/internal/spi/serialization/DefaultSerializationProvider.java b/impl/src/main/java/org/ehcache/impl/internal/spi/serialization/DefaultSerializationProvider.java
index 35ec028cd2..d654b3af58 100644
--- a/impl/src/main/java/org/ehcache/impl/internal/spi/serialization/DefaultSerializationProvider.java
+++ b/impl/src/main/java/org/ehcache/impl/internal/spi/serialization/DefaultSerializationProvider.java
@@ -23,16 +23,14 @@
import org.ehcache.impl.serialization.CharSerializer;
import org.ehcache.core.internal.service.ServiceLocator;
import org.ehcache.impl.serialization.CompactJavaSerializer;
-import org.ehcache.impl.serialization.CompactPersistentJavaSerializer;
import org.ehcache.impl.serialization.DoubleSerializer;
import org.ehcache.impl.serialization.FloatSerializer;
import org.ehcache.impl.serialization.IntegerSerializer;
import org.ehcache.impl.serialization.LongSerializer;
-import org.ehcache.impl.serialization.PlainJavaSerializer;
import org.ehcache.impl.serialization.StringSerializer;
-import org.ehcache.spi.persistence.StateRepository;
import org.ehcache.spi.persistence.PersistableResourceService;
import org.ehcache.spi.persistence.PersistableResourceService.PersistenceSpaceIdentifier;
+import org.ehcache.spi.serialization.StatefulSerializer;
import org.ehcache.spi.service.ServiceProvider;
import org.ehcache.spi.serialization.SerializationProvider;
import org.ehcache.spi.serialization.Serializer;
@@ -163,11 +161,11 @@ public TransientProvider(Map, Class extends Serializer>>> serialize
@Override
protected Serializer createSerializer(String suffix, Class clazz, ClassLoader classLoader, DefaultSerializerConfiguration config, ServiceConfiguration>... configs) throws UnsupportedTypeException {
+ Class extends Serializer> klazz = getSerializerClassFor(clazz, config, classLoader);
try {
- Class extends Serializer> klazz = getClassFor(clazz, config, classLoader);
return constructSerializer(clazz, klazz.getConstructor(ClassLoader.class), classLoader);
} catch (NoSuchMethodException e) {
- throw new RuntimeException(e);
+ throw new RuntimeException(klazz + " does not meet the constructor requirement for transient caches", e);
}
}
@@ -193,32 +191,44 @@ private PersistentProvider(Map, Class extends Serializer>>> seriali
@Override
protected Serializer createSerializer(String suffix, Class clazz, ClassLoader classLoader, DefaultSerializerConfiguration config, ServiceConfiguration>... configs) throws UnsupportedTypeException {
- Class extends Serializer> klazz = getClassFor(clazz, config, classLoader);
- PersistenceSpaceIdentifier extends PersistableResourceService> space = findSingletonAmongst(PersistenceSpaceIdentifier.class, (Object[]) configs);
- PersistableResourceService service = serviceProvider.getService(space.getServiceType());
-
- String subSpaceName = DefaultSerializationProvider.class.getSimpleName() + suffix;
+ Class extends Serializer> klazz = getSerializerClassFor(clazz, config, classLoader);
+ String errorMessage = klazz + " does not meet the constructor requirements for persistent caches";
+
+ if (StatefulSerializer.class.isAssignableFrom(klazz)) {
+ try {
+ Constructor extends Serializer> constructor = klazz.getConstructor(ClassLoader.class);
+ return constructSerializer(clazz, constructor, classLoader);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(errorMessage, e);
+ }
+ } else {
+ PersistenceSpaceIdentifier extends PersistableResourceService> space = findSingletonAmongst(PersistenceSpaceIdentifier.class, (Object[]) configs);
+ PersistableResourceService service = serviceProvider.getService(space.getServiceType());
- try {
- Constructor extends Serializer> constructor = klazz.getConstructor(ClassLoader.class, StateRepository.class);
- StateRepository stateRepository = service.getStateRepositoryWithin(space, subSpaceName);
- return constructSerializer(clazz, constructor, classLoader, stateRepository);
- } catch (NoSuchMethodException e) {
if (service instanceof LocalPersistenceService) {
try {
Constructor extends Serializer> constructor = klazz.getConstructor(ClassLoader.class, FileBasedPersistenceContext.class);
+ String subSpaceName = DefaultSerializationProvider.class.getSimpleName() + suffix;
FileBasedPersistenceContext context = ((LocalPersistenceService) service).createPersistenceContextWithin(space, subSpaceName);
return constructSerializer(clazz, constructor, classLoader, context);
} catch (NoSuchMethodException nsmex) {
- throw new RuntimeException(nsmex);
+ try {
+ Constructor extends Serializer> constructor = klazz.getConstructor(ClassLoader.class);
+ return constructSerializer(clazz, constructor, classLoader);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(errorMessage, e);
+ }
} catch (CachePersistenceException cpex) {
throw new RuntimeException(cpex);
}
} else {
- throw new RuntimeException(e);
+ try {
+ Constructor extends Serializer> constructor = klazz.getConstructor(ClassLoader.class);
+ return constructSerializer(clazz, constructor, classLoader);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(errorMessage, e);
+ }
}
- } catch (CachePersistenceException e) {
- throw new RuntimeException(e);
}
}
@@ -256,7 +266,7 @@ public Serializer createValueSerializer(Class clazz, ClassLoader class
protected abstract Serializer createSerializer(String suffix, Class clazz, ClassLoader classLoader, DefaultSerializerConfiguration config, ServiceConfiguration>... configs) throws UnsupportedTypeException;
- protected Class extends Serializer> getClassFor(Class clazz, DefaultSerializerConfiguration config, ClassLoader classLoader) throws UnsupportedTypeException {
+ protected Class extends Serializer> getSerializerClassFor(Class clazz, DefaultSerializerConfiguration config, ClassLoader classLoader) throws UnsupportedTypeException {
if (config != null) {
Class extends Serializer> configured = config.getClazz();
if (configured != null) {
diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStore.java b/impl/src/main/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStore.java
index c7684a3ce5..b07d050bb8 100644
--- a/impl/src/main/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStore.java
+++ b/impl/src/main/java/org/ehcache/impl/internal/store/disk/OffHeapDiskStore.java
@@ -37,6 +37,8 @@
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.ServiceProvider;
import org.ehcache.core.spi.store.Store;
import org.ehcache.core.spi.store.tiering.AuthoritativeTier;
@@ -72,6 +74,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@@ -297,9 +300,10 @@ private File getMetadataFile() {
@ServiceDependencies({TimeSourceService.class, SerializationProvider.class, ExecutionService.class})
public static class Provider implements Store.Provider, AuthoritativeTier.Provider {
- private final Set> createdStores = Collections.newSetFromMap(new ConcurrentWeakIdentityHashMap, Boolean>());
+ private final Map, PersistenceSpaceIdentifier> createdStores = new ConcurrentWeakIdentityHashMap, PersistenceSpaceIdentifier>();
private final String defaultThreadPool;
private volatile ServiceProvider serviceProvider;
+ private volatile LocalPersistenceService localPersistenceService;
public Provider() {
this(null);
@@ -337,7 +341,7 @@ private OffHeapDiskStore createStoreInternal(Configuration st
}
MemoryUnit unit = (MemoryUnit)diskPool.getUnit();
- LocalPersistenceService localPersistenceService = serviceProvider.getService(LocalPersistenceService.class);
+ this.localPersistenceService = serviceProvider.getService(LocalPersistenceService.class);
if (localPersistenceService == null) {
throw new IllegalStateException("No LocalPersistenceService could be found - did you configure it at the CacheManager level?");
}
@@ -359,7 +363,7 @@ private OffHeapDiskStore createStoreInternal(Configuration st
OffHeapDiskStore offHeapStore = new OffHeapDiskStore(persistenceContext,
executionService, threadPoolAlias, writerConcurrency,
storeConfig, timeSource, eventDispatcher, unit.toBytes(diskPool.getSize()));
- createdStores.add(offHeapStore);
+ createdStores.put(offHeapStore, space);
return offHeapStore;
} catch (CachePersistenceException cpex) {
throw new RuntimeException("Unable to create persistence context in " + space, cpex);
@@ -368,7 +372,7 @@ private OffHeapDiskStore createStoreInternal(Configuration st
@Override
public void releaseStore(Store, ?> resource) {
- if (!createdStores.contains(resource)) {
+ if (createdStores.remove(resource) == null) {
throw new IllegalArgumentException("Given store is not managed by this provider : " + resource);
}
try {
@@ -397,10 +401,34 @@ static void close(final OffHeapDiskStore resource) throws IOExcepti
@Override
public void initStore(Store, ?> resource) {
- if (!createdStores.contains(resource)) {
+ PersistenceSpaceIdentifier identifier = createdStores.get(resource);
+ if (identifier == null) {
throw new IllegalArgumentException("Given store is not managed by this provider : " + resource);
}
- init((OffHeapDiskStore)resource);
+ OffHeapDiskStore diskStore = (OffHeapDiskStore) resource;
+
+ Serializer keySerializer = diskStore.keySerializer;
+ if (keySerializer instanceof StatefulSerializer) {
+ StateRepository stateRepository = null;
+ try {
+ stateRepository = localPersistenceService.getStateRepositoryWithin(identifier, "key-serializer");
+ } catch (CachePersistenceException e) {
+ throw new RuntimeException(e);
+ }
+ ((StatefulSerializer)keySerializer).init(stateRepository);
+ }
+ Serializer valueSerializer = diskStore.valueSerializer;
+ if (valueSerializer instanceof StatefulSerializer) {
+ StateRepository stateRepository = null;
+ try {
+ stateRepository = localPersistenceService.getStateRepositoryWithin(identifier, "value-serializer");
+ } catch (CachePersistenceException e) {
+ throw new RuntimeException(e);
+ }
+ ((StatefulSerializer)valueSerializer).init(stateRepository);
+ }
+
+ init(diskStore);
}
static void init(final OffHeapDiskStore resource) {
diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/heap/OnHeapStore.java b/impl/src/main/java/org/ehcache/impl/internal/store/heap/OnHeapStore.java
index c90fce7434..19987f6dd3 100644
--- a/impl/src/main/java/org/ehcache/impl/internal/store/heap/OnHeapStore.java
+++ b/impl/src/main/java/org/ehcache/impl/internal/store/heap/OnHeapStore.java
@@ -46,7 +46,10 @@
import org.ehcache.impl.internal.store.heap.holders.SerializedOnHeapValueHolder;
import org.ehcache.core.spi.time.TimeSource;
import org.ehcache.core.spi.time.TimeSourceService;
+import org.ehcache.impl.serialization.TransientStateRepository;
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;
@@ -413,7 +416,7 @@ public OnHeapValueHolder apply(K mappedKey, OnHeapValueHolder mappedValue)
case MISS:
return false;
default:
- throw new AssertionError("Unknow enum value " + outcome);
+ throw new AssertionError("Unknown enum value " + outcome);
}
} catch (RuntimeException re) {
storeEventDispatcher.releaseEventSinkAfterFailure(eventSink, re);
@@ -1684,6 +1687,16 @@ static void close(final OnHeapStore onHeapStore) {
@Override
public void initStore(Store, ?> resource) {
checkResource(resource);
+
+ List copiers = createdStores.get(resource);
+ for (Copier copier : copiers) {
+ if(copier instanceof SerializingCopier) {
+ Serializer serializer = ((SerializingCopier)copier).getSerializer();
+ if(serializer instanceof StatefulSerializer) {
+ ((StatefulSerializer)serializer).init(new TransientStateRepository());
+ }
+ }
+ }
}
private void checkResource(Object resource) {
@@ -1721,7 +1734,7 @@ public void releaseCachingTier(CachingTier, ?> resource) {
@Override
public void initCachingTier(CachingTier, ?> resource) {
- initStore((Store, ?>) resource);
+ checkResource(resource);
}
@Override
@@ -1736,7 +1749,7 @@ public void releaseHigherCachingTier(HigherCachingTier, ?> resource) {
@Override
public void initHigherCachingTier(HigherCachingTier, ?> resource) {
- initStore((Store, ?>) resource);
+ checkResource(resource);
}
}
diff --git a/impl/src/main/java/org/ehcache/impl/internal/store/offheap/OffHeapStore.java b/impl/src/main/java/org/ehcache/impl/internal/store/offheap/OffHeapStore.java
index e3a5e6c8e2..c098eb445e 100644
--- a/impl/src/main/java/org/ehcache/impl/internal/store/offheap/OffHeapStore.java
+++ b/impl/src/main/java/org/ehcache/impl/internal/store/offheap/OffHeapStore.java
@@ -31,6 +31,9 @@
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.impl.serialization.TransientStateRepository;
+import org.ehcache.spi.persistence.PersistableResourceService;
+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.tiering.AuthoritativeTier;
@@ -55,6 +58,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import static org.ehcache.impl.internal.store.offheap.OffHeapStoreUtils.getBufferSource;
@@ -179,6 +183,17 @@ public void initStore(Store, ?> resource) {
if (!createdStores.contains(resource)) {
throw new IllegalArgumentException("Given store is not managed by this provider : " + resource);
}
+
+ OffHeapStore offHeapStore = (OffHeapStore) resource;
+ Serializer keySerializer = offHeapStore.keySerializer;
+ if (keySerializer instanceof StatefulSerializer) {
+ ((StatefulSerializer)keySerializer).init(new TransientStateRepository());
+ }
+ Serializer valueSerializer = offHeapStore.valueSerializer;
+ if (valueSerializer instanceof StatefulSerializer) {
+ ((StatefulSerializer)valueSerializer).init(new TransientStateRepository());
+ }
+
init((OffHeapStore)resource);
}
@@ -251,7 +266,7 @@ public void initCachingTier(LowerCachingTier, ?> resource) {
if (!createdStores.contains(resource)) {
throw new IllegalArgumentException("Given caching tier is not managed by this provider : " + resource);
}
- initStore((Store, ?>) resource);
+ init((OffHeapStore, ?>) resource);
}
}
}
diff --git a/impl/src/main/java/org/ehcache/impl/serialization/ByteArraySerializer.java b/impl/src/main/java/org/ehcache/impl/serialization/ByteArraySerializer.java
index d350339ea6..8f9dbb1e6a 100644
--- a/impl/src/main/java/org/ehcache/impl/serialization/ByteArraySerializer.java
+++ b/impl/src/main/java/org/ehcache/impl/serialization/ByteArraySerializer.java
@@ -16,7 +16,6 @@
package org.ehcache.impl.serialization;
-import org.ehcache.spi.persistence.StateRepository;
import org.ehcache.spi.serialization.SerializerException;
import org.ehcache.spi.serialization.Serializer;
@@ -51,21 +50,6 @@ public ByteArraySerializer() {
public ByteArraySerializer(ClassLoader classLoader) {
}
- /**
- * Constructor to enable this serializer as a persistent one.
- *
- * Parameters are ignored as {@code byte[]} is a base java type and this implementation requires no state.
- *
- *
- * @param classLoader the classloader to use
- * @param stateRepository the state repository
- *
- * @see Serializer
- */
- public ByteArraySerializer(ClassLoader classLoader, StateRepository stateRepository) {
-
- }
-
/**
* {@inheritDoc}
*/
diff --git a/impl/src/main/java/org/ehcache/impl/serialization/CharSerializer.java b/impl/src/main/java/org/ehcache/impl/serialization/CharSerializer.java
index cd05711d41..c72ba3b82f 100644
--- a/impl/src/main/java/org/ehcache/impl/serialization/CharSerializer.java
+++ b/impl/src/main/java/org/ehcache/impl/serialization/CharSerializer.java
@@ -16,7 +16,6 @@
package org.ehcache.impl.serialization;
-import org.ehcache.spi.persistence.StateRepository;
import org.ehcache.spi.serialization.Serializer;
import java.nio.ByteBuffer;
@@ -46,21 +45,6 @@ public CharSerializer() {
public CharSerializer(ClassLoader classLoader) {
}
- /**
- * Constructor to enable this serializer as a persistent one.
- *
- * Parameters are ignored as {@link Character} is a base java type and this implementation requires no state.
- *
- *
- * @param classLoader the classloader to use
- * @param stateRepository the state repository
- *
- * @see Serializer
- */
- public CharSerializer(ClassLoader classLoader, StateRepository stateRepository) {
-
- }
-
/**
* {@inheritDoc}
*/
diff --git a/impl/src/main/java/org/ehcache/impl/serialization/CompactJavaSerializer.java b/impl/src/main/java/org/ehcache/impl/serialization/CompactJavaSerializer.java
index de287192ba..4fa0612bf2 100644
--- a/impl/src/main/java/org/ehcache/impl/serialization/CompactJavaSerializer.java
+++ b/impl/src/main/java/org/ehcache/impl/serialization/CompactJavaSerializer.java
@@ -18,7 +18,6 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
-import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
@@ -35,15 +34,14 @@
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
-import org.ehcache.core.spi.function.NullaryFunction;
import org.ehcache.spi.persistence.StateRepository;
import org.ehcache.spi.serialization.SerializerException;
import org.ehcache.impl.internal.util.ByteBufferInputStream;
import org.ehcache.spi.serialization.Serializer;
+import org.ehcache.spi.serialization.StatefulSerializer;
/**
* A trivially compressed Java serialization based serializer.
@@ -53,9 +51,9 @@
* {@code Class} and the integer representation are stored in a single on-heap
* map.
*/
-public class CompactJavaSerializer implements Serializer {
+public class CompactJavaSerializer implements StatefulSerializer {
- private final ConcurrentMap readLookup;
+ private volatile ConcurrentMap readLookup;
private final ConcurrentMap readLookupLocalCache = new ConcurrentHashMap();
private final ConcurrentMap writeLookup = new ConcurrentHashMap();
@@ -72,13 +70,8 @@ public class CompactJavaSerializer implements Serializer {
* @see Serializer
*/
public CompactJavaSerializer(ClassLoader loader) {
- this(loader, new TransientStateRepository());
- }
-
- public CompactJavaSerializer(ClassLoader loader, StateRepository stateRepository) {
this.loader = loader;
- this.readLookup = stateRepository.getPersistentConcurrentMap("CompactJavaSerializer-ObjectStreamClassIndex", Integer.class, ObjectStreamClass.class);
- loadMappingsInWriteContext(readLookup.entrySet(), true);
+ init(new TransientStateRepository());
}
CompactJavaSerializer(ClassLoader loader, Map mappings) {
@@ -97,6 +90,12 @@ public CompactJavaSerializer(ClassLoader loader, StateRepository stateRepository
}
}
+ @Override
+ public void init(final StateRepository stateRepository) {
+ this.readLookup = stateRepository.getPersistentConcurrentMap("CompactJavaSerializer-ObjectStreamClassIndex", Integer.class, ObjectStreamClass.class);
+ loadMappingsInWriteContext(readLookup.entrySet(), true);
+ }
+
Map getSerializationMappings() {
return Collections.unmodifiableMap(new HashMap(readLookup));
}
diff --git a/impl/src/main/java/org/ehcache/impl/serialization/DoubleSerializer.java b/impl/src/main/java/org/ehcache/impl/serialization/DoubleSerializer.java
index e3e578d8be..9ded987569 100644
--- a/impl/src/main/java/org/ehcache/impl/serialization/DoubleSerializer.java
+++ b/impl/src/main/java/org/ehcache/impl/serialization/DoubleSerializer.java
@@ -16,7 +16,6 @@
package org.ehcache.impl.serialization;
-import org.ehcache.spi.persistence.StateRepository;
import org.ehcache.spi.serialization.Serializer;
import java.nio.ByteBuffer;
@@ -46,21 +45,6 @@ public DoubleSerializer() {
public DoubleSerializer(ClassLoader classLoader) {
}
- /**
- * Constructor to enable this serializer as a persistent one.
- *
- * Parameters are ignored as {@link Double} is a base java type and this implementation requires no state.
- *
- *
- * @param classLoader the classloader to use
- * @param stateRepository the state repository
- *
- * @see Serializer
- */
- public DoubleSerializer(ClassLoader classLoader, StateRepository stateRepository) {
-
- }
-
/**
* {@inheritDoc}
*/
diff --git a/impl/src/main/java/org/ehcache/impl/serialization/FloatSerializer.java b/impl/src/main/java/org/ehcache/impl/serialization/FloatSerializer.java
index 73760d6cab..a15cf7382c 100644
--- a/impl/src/main/java/org/ehcache/impl/serialization/FloatSerializer.java
+++ b/impl/src/main/java/org/ehcache/impl/serialization/FloatSerializer.java
@@ -16,7 +16,6 @@
package org.ehcache.impl.serialization;
-import org.ehcache.spi.persistence.StateRepository;
import org.ehcache.spi.serialization.Serializer;
import java.nio.ByteBuffer;
@@ -46,21 +45,6 @@ public FloatSerializer() {
public FloatSerializer(ClassLoader classLoader) {
}
- /**
- * Constructor to enable this serializer as a persistent one.
- *
- * Parameters are ignored as {@link Float} is a base java type and this implementation requires no state.
- *
- *
- * @param classLoader the classloader to use
- * @param stateRepository the state repository
- *
- * @see Serializer
- */
- public FloatSerializer(ClassLoader classLoader, StateRepository stateRepository) {
-
- }
-
/**
* {@inheritDoc}
*/
diff --git a/impl/src/main/java/org/ehcache/impl/serialization/IntegerSerializer.java b/impl/src/main/java/org/ehcache/impl/serialization/IntegerSerializer.java
index 659c0ac8c3..f4efe01892 100644
--- a/impl/src/main/java/org/ehcache/impl/serialization/IntegerSerializer.java
+++ b/impl/src/main/java/org/ehcache/impl/serialization/IntegerSerializer.java
@@ -16,7 +16,6 @@
package org.ehcache.impl.serialization;
-import org.ehcache.spi.persistence.StateRepository;
import org.ehcache.spi.serialization.Serializer;
import java.nio.ByteBuffer;
@@ -46,21 +45,6 @@ public IntegerSerializer() {
public IntegerSerializer(ClassLoader classLoader) {
}
- /**
- * Constructor to enable this serializer as a persistent one.
- *
- * Parameters are ignored as {@link Integer} is a base java type and this implementation requires no state.
- *
- *
- * @param classLoader the classloader to use
- * @param stateRepository the state repository
- *
- * @see Serializer
- */
- public IntegerSerializer(ClassLoader classLoader, StateRepository stateRepository) {
-
- }
-
/**
* {@inheritDoc}
*/
diff --git a/impl/src/main/java/org/ehcache/impl/serialization/LongSerializer.java b/impl/src/main/java/org/ehcache/impl/serialization/LongSerializer.java
index 2c983daee9..ce7fd97477 100644
--- a/impl/src/main/java/org/ehcache/impl/serialization/LongSerializer.java
+++ b/impl/src/main/java/org/ehcache/impl/serialization/LongSerializer.java
@@ -16,7 +16,6 @@
package org.ehcache.impl.serialization;
-import org.ehcache.spi.persistence.StateRepository;
import org.ehcache.spi.serialization.Serializer;
import java.nio.ByteBuffer;
@@ -46,21 +45,6 @@ public LongSerializer() {
public LongSerializer(ClassLoader classLoader) {
}
- /**
- * Constructor to enable this serializer as a persistent one.
- *
- * Parameters are ignored as {@link Long} is a base java type and this implementation requires no state.
- *
- *
- * @param classLoader the classloader to use
- * @param stateRepository the state repository
- *
- * @see Serializer
- */
- public LongSerializer(ClassLoader classLoader, StateRepository stateRepository) {
-
- }
-
/**
* {@inheritDoc}
*/
diff --git a/impl/src/main/java/org/ehcache/impl/serialization/PlainJavaSerializer.java b/impl/src/main/java/org/ehcache/impl/serialization/PlainJavaSerializer.java
index a1c02e1ab8..b72abb4df2 100644
--- a/impl/src/main/java/org/ehcache/impl/serialization/PlainJavaSerializer.java
+++ b/impl/src/main/java/org/ehcache/impl/serialization/PlainJavaSerializer.java
@@ -17,7 +17,6 @@
package org.ehcache.impl.serialization;
import org.ehcache.impl.internal.util.ByteBufferInputStream;
-import org.ehcache.spi.persistence.StateRepository;
import org.ehcache.spi.serialization.Serializer;
import org.ehcache.spi.serialization.SerializerException;
@@ -43,10 +42,6 @@ public PlainJavaSerializer(ClassLoader classLoader) {
this.classLoader = classLoader;
}
- public PlainJavaSerializer(ClassLoader classLoader, StateRepository stateRepository) throws IOException, ClassNotFoundException {
- this(classLoader);
- }
-
@Override
public ByteBuffer serialize(T object) {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
diff --git a/impl/src/main/java/org/ehcache/impl/serialization/StringSerializer.java b/impl/src/main/java/org/ehcache/impl/serialization/StringSerializer.java
index add9eb458d..cc4f84e5a3 100644
--- a/impl/src/main/java/org/ehcache/impl/serialization/StringSerializer.java
+++ b/impl/src/main/java/org/ehcache/impl/serialization/StringSerializer.java
@@ -20,7 +20,6 @@
import java.io.IOException;
import java.nio.ByteBuffer;
-import org.ehcache.spi.persistence.StateRepository;
import org.ehcache.spi.serialization.Serializer;
import org.ehcache.spi.serialization.SerializerException;
@@ -49,20 +48,6 @@ public StringSerializer() {
public StringSerializer(ClassLoader classLoader) {
}
- /**
- * Constructor to enable this serializer as a persistent one.
- *
- * Parameters are ignored as {@link String} is a base java type and this implementation requires no state.
- *
- *
- * @param classLoader the classloader to use
- * @param stateRepository the state repository
- *
- * @see Serializer
- */
- public StringSerializer(ClassLoader classLoader, StateRepository stateRepository) {
- }
-
/**
* {@inheritDoc}
*/
diff --git a/impl/src/main/java/org/ehcache/impl/serialization/TransientStateRepository.java b/impl/src/main/java/org/ehcache/impl/serialization/TransientStateRepository.java
index 004f1cee73..12519df360 100644
--- a/impl/src/main/java/org/ehcache/impl/serialization/TransientStateRepository.java
+++ b/impl/src/main/java/org/ehcache/impl/serialization/TransientStateRepository.java
@@ -25,7 +25,7 @@
/**
* TransientStateRepository
*/
-class TransientStateRepository implements StateRepository {
+public class TransientStateRepository implements StateRepository {
private ConcurrentMap> knownMaps = new ConcurrentHashMap>();
diff --git a/impl/src/test/java/org/ehcache/impl/config/serializer/DefaultSerializationProviderConfigurationTest.java b/impl/src/test/java/org/ehcache/impl/config/serializer/DefaultSerializationProviderConfigurationTest.java
index 3421a2cf35..130e15a43f 100644
--- a/impl/src/test/java/org/ehcache/impl/config/serializer/DefaultSerializationProviderConfigurationTest.java
+++ b/impl/src/test/java/org/ehcache/impl/config/serializer/DefaultSerializationProviderConfigurationTest.java
@@ -16,26 +16,30 @@
package org.ehcache.impl.config.serializer;
+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.*;
-/**
- * Created by alsu on 30/09/15.
- */
public class DefaultSerializationProviderConfigurationTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
@Test
public void testAddSerializerForTransient() throws Exception {
DefaultSerializationProviderConfiguration config = new DefaultSerializationProviderConfiguration();
config.addSerializerFor(Long.class, TransientSerializer.class);
- assertTrue(config.getPersistentSerializers().isEmpty());
+ assertSame(TransientSerializer.class, config.getPersistentSerializers().get(Long.class));
assertSame(TransientSerializer.class, config.getTransientSerializers().get(Long.class));
}
@@ -48,6 +52,15 @@ public void testAddSerializerForPersistent() throws Exception {
assertSame(PersistentSerializer.class, config.getPersistentSerializers().get(Long.class));
}
+ @Test
+ public void testAddSerializerForTransientPersistentLegacyCombo() throws Exception {
+ DefaultSerializationProviderConfiguration config = new DefaultSerializationProviderConfiguration();
+ config.addSerializerFor(Long.class, LegacyComboSerializer.class);
+
+ assertSame(LegacyComboSerializer.class, config.getPersistentSerializers().get(Long.class));
+ assertSame(LegacyComboSerializer.class, config.getTransientSerializers().get(Long.class));
+ }
+
@Test
public void testAddSerializerForTransientPersistentCombo() throws Exception {
DefaultSerializationProviderConfiguration config = new DefaultSerializationProviderConfiguration();
@@ -57,12 +70,20 @@ public void testAddSerializerForTransientPersistentCombo() throws Exception {
assertSame(ComboSerializer.class, config.getTransientSerializers().get(Long.class));
}
- @Test(expected = IllegalArgumentException.class)
- public void testAddSerializerForUnusable() throws Exception {
+ @Test
+ public void testAddSerializerForConstructorless() throws Exception {
+ expectedException.expectMessage("does not meet the constructor requirements for either transient or persistent caches");
DefaultSerializationProviderConfiguration config = new DefaultSerializationProviderConfiguration();
config.addSerializerFor(Long.class, UnusableSerializer.class);
}
+ @Test
+ public void testAddSerializerForStatefulOnly() throws Exception {
+ expectedException.expectMessage("does not meet the constructor requirements for either transient or persistent caches");
+ DefaultSerializationProviderConfiguration config = new DefaultSerializationProviderConfiguration();
+ config.addSerializerFor(Long.class, YetAnotherUnusableSerializer.class);
+ }
+
private static class TransientSerializer implements Serializer {
public TransientSerializer(ClassLoader loader) {
@@ -105,12 +126,12 @@ public boolean equals(final Long object, final ByteBuffer binary) throws ClassNo
}
}
- private static class ComboSerializer implements Serializer {
+ private static class LegacyComboSerializer implements Serializer {
- public ComboSerializer(ClassLoader loader) {
+ public LegacyComboSerializer(ClassLoader loader) {
}
- public ComboSerializer(ClassLoader loader, FileBasedPersistenceContext context) {
+ public LegacyComboSerializer(ClassLoader loader, FileBasedPersistenceContext context) {
}
@Override
@@ -146,4 +167,79 @@ public boolean equals(final Long object, final ByteBuffer binary) throws ClassNo
throw new UnsupportedOperationException("Implement me!");
}
}
-}
\ No newline at end of file
+
+ private static class ComboSerializer implements StatefulSerializer {
+
+ public ComboSerializer(ClassLoader loader) {
+ }
+
+ @Override
+ public void init(final StateRepository stateRepository) {
+ throw new UnsupportedOperationException("Implement me!");
+ }
+
+ @Override
+ public ByteBuffer serialize(final Long object) throws SerializerException {
+ throw new UnsupportedOperationException("Implement me!");
+ }
+
+ @Override
+ public Long read(final ByteBuffer binary) throws ClassNotFoundException, SerializerException {
+ throw new UnsupportedOperationException("Implement me!");
+ }
+
+ @Override
+ public boolean equals(final Long object, final ByteBuffer binary) throws ClassNotFoundException, SerializerException {
+ throw new UnsupportedOperationException("Implement me!");
+ }
+ }
+
+ private static class AnotherUnusableSerializer implements StatefulSerializer {
+
+ public AnotherUnusableSerializer(ClassLoader loader, FileBasedPersistenceContext context) {
+ }
+
+ @Override
+ public void init(final StateRepository stateRepository) {
+ throw new UnsupportedOperationException("Implement me!");
+ }
+
+ @Override
+ public ByteBuffer serialize(final Long object) throws SerializerException {
+ throw new UnsupportedOperationException("Implement me!");
+ }
+
+ @Override
+ public Long read(final ByteBuffer binary) throws ClassNotFoundException, SerializerException {
+ throw new UnsupportedOperationException("Implement me!");
+ }
+
+ @Override
+ public boolean equals(final Long object, final ByteBuffer binary) throws ClassNotFoundException, SerializerException {
+ throw new UnsupportedOperationException("Implement me!");
+ }
+ }
+
+ private static class YetAnotherUnusableSerializer implements StatefulSerializer {
+
+ @Override
+ public void init(final StateRepository stateRepository) {
+ throw new UnsupportedOperationException("Implement me!");
+ }
+
+ @Override
+ public ByteBuffer serialize(final Long object) throws SerializerException {
+ throw new UnsupportedOperationException("Implement me!");
+ }
+
+ @Override
+ public Long read(final ByteBuffer binary) throws ClassNotFoundException, SerializerException {
+ throw new UnsupportedOperationException("Implement me!");
+ }
+
+ @Override
+ public boolean equals(final Long object, final ByteBuffer binary) throws ClassNotFoundException, SerializerException {
+ throw new UnsupportedOperationException("Implement me!");
+ }
+ }
+}
diff --git a/impl/src/test/java/org/ehcache/impl/internal/spi/serialization/DefaultSerializationProviderTest.java b/impl/src/test/java/org/ehcache/impl/internal/spi/serialization/DefaultSerializationProviderTest.java
index da0ed8b45d..883dddfb06 100644
--- a/impl/src/test/java/org/ehcache/impl/internal/spi/serialization/DefaultSerializationProviderTest.java
+++ b/impl/src/test/java/org/ehcache/impl/internal/spi/serialization/DefaultSerializationProviderTest.java
@@ -20,26 +20,25 @@
import org.ehcache.core.spi.service.LocalPersistenceService;
import org.ehcache.impl.config.serializer.DefaultSerializationProviderConfiguration;
import org.ehcache.impl.config.serializer.DefaultSerializerConfiguration;
-import org.ehcache.impl.internal.concurrent.ConcurrentHashMap;
import org.ehcache.impl.serialization.ByteArraySerializer;
import org.ehcache.impl.serialization.CharSerializer;
import org.ehcache.impl.serialization.CompactJavaSerializer;
-import org.ehcache.impl.serialization.CompactPersistentJavaSerializer;
import org.ehcache.impl.serialization.DoubleSerializer;
import org.ehcache.impl.serialization.FloatSerializer;
import org.ehcache.impl.serialization.IntegerSerializer;
import org.ehcache.impl.serialization.LongSerializer;
-import org.ehcache.impl.serialization.PlainJavaSerializer;
import org.ehcache.impl.serialization.StringSerializer;
import org.ehcache.spi.persistence.PersistableResourceService;
import org.ehcache.spi.persistence.StateRepository;
import org.ehcache.spi.serialization.Serializer;
import org.ehcache.spi.serialization.SerializerException;
+import org.ehcache.spi.serialization.StatefulSerializer;
import org.ehcache.spi.serialization.UnsupportedTypeException;
import org.ehcache.spi.service.ServiceProvider;
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;
@@ -61,8 +60,6 @@
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
@@ -73,6 +70,9 @@ 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();
@@ -291,6 +291,205 @@ public void testDefaultByteArraySerializer() throws Exception {
assertThat(keySerializer, instanceOf(ByteArraySerializer.class));
}
+ @Test
+ public void testCreateTransientSerializerWithoutConstructor() throws Exception {
+ expectedException.expect(RuntimeException.class);
+ expectedException.expectMessage("does not meet the constructor requirement for transient caches");
+ DefaultSerializationProvider provider = new DefaultSerializationProvider(null);
+ provider.start(providerContaining());
+
+ DefaultSerializerConfiguration configuration = new DefaultSerializerConfiguration((Class) BaseSerializer.class, DefaultSerializerConfiguration.Type.VALUE);
+ provider.createValueSerializer(Object.class, ClassLoader.getSystemClassLoader(), configuration);
+ }
+
+ @Test
+ public void testCreatePersistentSerializerWithoutConstructor() throws Exception {
+ expectedException.expect(RuntimeException.class);
+ expectedException.expectMessage("does not meet the constructor requirements for persistent caches");
+ DefaultSerializationProvider provider = new DefaultSerializationProvider(null);
+ provider.start(providerContaining());
+
+ DefaultSerializerConfiguration configuration = new DefaultSerializerConfiguration((Class) BaseSerializer.class, DefaultSerializerConfiguration.Type.VALUE);
+ provider.createValueSerializer(Object.class, ClassLoader.getSystemClassLoader(), configuration, getPersistenceSpaceIdentifierMock());
+ }
+
+ @Test
+ public void testCreateTransientStatefulSerializerWithoutConstructor() throws Exception {
+ expectedException.expect(RuntimeException.class);
+ expectedException.expectMessage("does not meet the constructor requirement for transient caches");
+ DefaultSerializationProvider provider = new DefaultSerializationProvider(null);
+ provider.start(providerContaining());
+
+ DefaultSerializerConfiguration configuration = new DefaultSerializerConfiguration((Class) StatefulBaseSerializer.class, DefaultSerializerConfiguration.Type.VALUE);
+ provider.createValueSerializer(Object.class, ClassLoader.getSystemClassLoader(), configuration);
+ }
+
+ @Test
+ public void testCreatePersistentStatefulSerializerWithoutConstructor() throws Exception {
+ expectedException.expect(RuntimeException.class);
+ expectedException.expectMessage("does not meet the constructor requirements for persistent caches");
+ DefaultSerializationProvider provider = new DefaultSerializationProvider(null);
+ provider.start(providerContaining());
+
+ DefaultSerializerConfiguration configuration = new DefaultSerializerConfiguration((Class) StatefulBaseSerializer.class, DefaultSerializerConfiguration.Type.VALUE);
+ provider.createValueSerializer(Object.class, ClassLoader.getSystemClassLoader(), configuration, getPersistenceSpaceIdentifierMock());
+ }
+
+ @Test
+ public void testCreateTransientMinimalSerializer() throws Exception {
+ DefaultSerializationProvider provider = new DefaultSerializationProvider(null);
+ provider.start(providerContaining());
+
+ MinimalSerializer.baseConstructorInvoked = false;
+ DefaultSerializerConfiguration configuration = new DefaultSerializerConfiguration(MinimalSerializer.class, DefaultSerializerConfiguration.Type.VALUE);
+ Serializer