diff --git a/pom.xml b/pom.xml
index c52f8e332..0a218f01b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,7 +9,7 @@
io.ebean
ebean
- 10.2.4-SNAPSHOT
+ 10.3.1-SNAPSHOT
jar
ebean
diff --git a/src/main/java/io/ebean/cache/TenantAwareKey.java b/src/main/java/io/ebean/cache/TenantAwareKey.java
new file mode 100644
index 000000000..96ebcbc0e
--- /dev/null
+++ b/src/main/java/io/ebean/cache/TenantAwareKey.java
@@ -0,0 +1,77 @@
+package io.ebean.cache;
+
+import io.ebean.config.CurrentTenantProvider;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+/**
+ * Tenant aware handling for caching.
+ */
+public class TenantAwareKey {
+
+ private final CurrentTenantProvider tenantProvider;
+
+ /**
+ * Construct with a tenant provider than can be null.
+ */
+ public TenantAwareKey(CurrentTenantProvider tenantProvider) {
+ this.tenantProvider = tenantProvider;
+ }
+
+ /**
+ * Return a tenant aware key.
+ */
+ public Object key(Object key) {
+ if (tenantProvider != null) {
+ return new CacheKey(key, tenantProvider.currentId());
+ } else {
+ return key;
+ }
+ }
+
+ /**
+ * We use a combined key, if this serverCache is per tenant.
+ */
+ public static final class CacheKey implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ final Object key;
+ final Object tenantId;
+
+ /**
+ * Create with optional tenantId.
+ */
+ public CacheKey(Object key, Object tenantId) {
+ this.key = key;
+ this.tenantId = tenantId;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = key.hashCode();
+ result = 92821 * result + Objects.hashCode(tenantId);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof CacheKey) {
+ CacheKey that = (CacheKey) obj;
+ return Objects.equals(that.key, this.key)
+ && Objects.equals(that.tenantId, this.tenantId);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(key.toString());
+ if (tenantId != null) {
+ sb.append(":").append(tenantId);
+ }
+ return sb.toString();
+ }
+ }
+}
diff --git a/src/main/java/io/ebeaninternal/server/cache/DefaultServerCache.java b/src/main/java/io/ebeaninternal/server/cache/DefaultServerCache.java
index f9187db0c..060e27701 100644
--- a/src/main/java/io/ebeaninternal/server/cache/DefaultServerCache.java
+++ b/src/main/java/io/ebeaninternal/server/cache/DefaultServerCache.java
@@ -4,6 +4,7 @@ import io.ebean.BackgroundExecutor;
import io.ebean.cache.ServerCache;
import io.ebean.cache.ServerCacheOptions;
import io.ebean.cache.ServerCacheStatistics;
+import io.ebean.cache.TenantAwareKey;
import io.ebean.config.CurrentTenantProvider;
import org.slf4j.Logger;
@@ -35,44 +36,6 @@ public class DefaultServerCache implements ServerCache {
*/
public static final CompareByLastAccess BY_LAST_ACCESS = new CompareByLastAccess();
-/**
- * We use a combined key, if this serverCache is per tenant.
- */
- public static final class CacheKey implements Serializable {
- private static final long serialVersionUID = 1L;
-
- final Object tenantId;
- final Object key;
-
- CacheKey(Object tenantId, Object key) {
- super();
- this.tenantId = tenantId;
- this.key = key;
- }
-
- @Override
- public int hashCode() {
- int result = key.hashCode();
- result = 31 * result + ((tenantId == null) ? 0 : tenantId.hashCode());
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof CacheKey) {
- CacheKey other = (CacheKey) obj;
- if (other.key.equals(this.key)) {
- if (other.tenantId == null && this.tenantId == null) {
- return true;
- } else if (this.tenantId != null) {
- return this.tenantId.equals(other.tenantId);
- }
- }
- }
- return false;
- }
- }
-
/**
* The underlying map (ConcurrentHashMap or similar)
*/
@@ -91,13 +54,6 @@ public class DefaultServerCache implements ServerCache {
protected final LongAdder evictCount = new LongAdder();
protected final LongAdder evictMicros = new LongAdder();
- /**
- * if tenantContext is available, use it to generate cacheKey.
- */
- protected final CurrentTenantProvider tenantProvider;
-
- protected final Object monitor = new Object();
-
protected final String name;
protected int maxSize;
@@ -108,6 +64,8 @@ public class DefaultServerCache implements ServerCache {
protected int maxSecsToLive;
+ protected TenantAwareKey tenantAwareKey;
+
/**
* Construct using a ConcurrentHashMap and cache options.
*/
@@ -129,7 +87,7 @@ public class DefaultServerCache implements ServerCache {
this.name = name;
this.map = map;
this.maxSize = maxSize;
- this.tenantProvider = tenantProvider;
+ this.tenantAwareKey = new TenantAwareKey(tenantProvider);
this.maxIdleSecs = maxIdleSecs;
this.maxSecsToLive = maxSecsToLive;
this.trimFrequency = determineTrim(maxIdleSecs, maxSecsToLive, trimFrequency);
@@ -233,22 +191,20 @@ public class DefaultServerCache implements ServerCache {
map.clear();
}
- private Object convertKey(Object key) {
- if (tenantProvider != null) {
- return new CacheKey(tenantProvider.currentId(), key);
- } else {
- return key;
- }
+ /**
+ * Return the tenant aware key.
+ */
+ private Object key(Object id) {
+ return tenantAwareKey.key(id);
}
+
/**
* Return a value from the cache.
*/
@Override
- public Object get(Object key) {
+ public Object get(Object id) {
- key = convertKey(key);
-
- CacheEntry entry = map.get(key);
+ CacheEntry entry = map.get(key(id));
if (entry == null) {
missCount.increment();
return null;
@@ -265,10 +221,9 @@ public class DefaultServerCache implements ServerCache {
* Put a value into the cache.
*/
@Override
- public Object put(Object key, Object value) {
-
- key = convertKey(key);
-
+ public Object put(Object id, Object value) {
+
+ Object key = key(id);
CacheEntry entry = map.put(key, new CacheEntry(key, value));
if (entry == null) {
insertCount.increment();
@@ -283,11 +238,9 @@ public class DefaultServerCache implements ServerCache {
* Remove an entry from the cache.
*/
@Override
- public Object remove(Object key) {
-
- key = convertKey(key);
-
- CacheEntry entry = map.remove(key);
+ public Object remove(Object id) {
+
+ CacheEntry entry = map.remove(key(id));
if (entry == null) {
return null;
} else {
diff --git a/src/test/java/io/ebean/cache/CacheKeyTest.java b/src/test/java/io/ebean/cache/CacheKeyTest.java
new file mode 100644
index 000000000..98225a6e6
--- /dev/null
+++ b/src/test/java/io/ebean/cache/CacheKeyTest.java
@@ -0,0 +1,72 @@
+package io.ebean.cache;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class CacheKeyTest {
+
+ @Test
+ public void equals_when_null_tenantId() {
+
+ TenantAwareKey.CacheKey key0 = key("12", null);
+ TenantAwareKey.CacheKey key1 = key("12", null);
+
+ assertMatchEquals(key0, key1);
+ }
+
+ private void assertMatchEquals(TenantAwareKey.CacheKey key0, TenantAwareKey.CacheKey key1) {
+ assertThat(key0.hashCode()).isEqualTo(key1.hashCode());
+ assertThat(key0.equals(key1)).isTrue();
+ assertThat(key0.toString()).isEqualTo(key1.toString());
+ }
+
+ @Test
+ public void equals_when_same_tenantId() {
+
+ TenantAwareKey.CacheKey key0 = key(42L, "1");
+ TenantAwareKey.CacheKey key1 = key(42L, "1");
+
+ assertMatchEquals(key0, key1);
+ }
+
+ @Test
+ public void not_equal_when_diff_both() {
+
+ TenantAwareKey.CacheKey key0 = key(42L, "1");
+ TenantAwareKey.CacheKey key1 = key(43L, "2");
+
+ assertThat(key0.equals(key1)).isFalse();
+ }
+
+ @Test
+ public void not_equal_when_diff_key() {
+
+ TenantAwareKey.CacheKey key0 = key(42L, "1");
+ TenantAwareKey.CacheKey key1 = key(43L, "1");
+
+ assertThat(key0.equals(key1)).isFalse();
+ }
+
+ @Test
+ public void not_equal_when_diff_key_andNoTenantId() {
+
+ TenantAwareKey.CacheKey key0 = key(42L, null);
+ TenantAwareKey.CacheKey key1 = key(43L, null);
+
+ assertThat(key0.equals(key1)).isFalse();
+ }
+
+ @Test
+ public void not_equal_when_diff_tenantId() {
+
+ TenantAwareKey.CacheKey key0 = key(42L, "1");
+ TenantAwareKey.CacheKey key1 = key(42L, "2");
+
+ assertThat(key0.equals(key1)).isFalse();
+ }
+
+ private TenantAwareKey.CacheKey key(Object key, Object tenantId) {
+ return new TenantAwareKey.CacheKey(key, tenantId);
+ }
+}