Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

ISPN-2767 Add defensive copying option for storeAsBinary functionality

* This enables true, store-as-value, functionality within Infinispan.
* It's required by the JSR-107 specification.
  • Loading branch information...
commit cf494947577debac84e47f21392aa99c1353a158 1 parent 7889c2d
Galder Zamarreño galderz authored maniksurtani committed
14 core/src/main/java/org/infinispan/configuration/cache/StoreAsBinaryConfiguration.java
View
@@ -34,11 +34,13 @@
private boolean enabled;
private final boolean storeKeysAsBinary;
private final boolean storeValuesAsBinary;
+ private final boolean defensive;
- StoreAsBinaryConfiguration(boolean enabled, boolean storeKeysAsBinary, boolean storeValuesAsBinary) {
+ StoreAsBinaryConfiguration(boolean enabled, boolean storeKeysAsBinary, boolean storeValuesAsBinary, boolean defensive) {
this.enabled = enabled;
this.storeKeysAsBinary = storeKeysAsBinary;
this.storeValuesAsBinary = storeValuesAsBinary;
+ this.defensive = defensive;
}
/**
@@ -67,12 +69,20 @@ public boolean storeValuesAsBinary() {
return storeValuesAsBinary;
}
+ /**
+ * Enables defensive copies.
+ */
+ public boolean defensive() {
+ return defensive;
+ }
+
@Override
public String toString() {
return "StoreAsBinaryConfiguration{" +
"enabled=" + enabled +
", storeKeysAsBinary=" + storeKeysAsBinary +
", storeValuesAsBinary=" + storeValuesAsBinary +
+ ", defensive=" + defensive +
'}';
}
@@ -86,6 +96,7 @@ public boolean equals(Object o) {
if (enabled != that.enabled) return false;
if (storeKeysAsBinary != that.storeKeysAsBinary) return false;
if (storeValuesAsBinary != that.storeValuesAsBinary) return false;
+ if (defensive != that.defensive) return false;
return true;
}
@@ -95,6 +106,7 @@ public int hashCode() {
int result = (enabled ? 1 : 0);
result = 31 * result + (storeKeysAsBinary ? 1 : 0);
result = 31 * result + (storeValuesAsBinary ? 1 : 0);
+ result = 31 * result + (defensive ? 1 : 0);
return result;
}
29 core/src/main/java/org/infinispan/configuration/cache/StoreAsBinaryConfigurationBuilder.java
View
@@ -36,6 +36,7 @@
private boolean enabled = false;
private boolean storeKeysAsBinary = true;
private boolean storeValuesAsBinary = true;
+ private boolean defensive = false;
StoreAsBinaryConfigurationBuilder(ConfigurationBuilder builder) {
super(builder);
@@ -83,6 +84,30 @@ public StoreAsBinaryConfigurationBuilder storeValuesAsBinary(boolean storeValues
return this;
}
+ /**
+ * When defensive copying is disabled, Infinispan keeps object references
+ * around and marshalls keys lazily. So clients can modify entries via
+ * original object references, and marshalling only happens when entries
+ * are to be replicated/distributed, or stored in a cache store.
+ *
+ * Since client references are valid, clients can make changes to entries
+ * in the cache using those references, but these modifications are only
+ * local and you still need to call one of the cache's put/replace...
+ * methods in order for changes to replicate.
+ *
+ * When defensive copies are enabled, Infinispan marshalls objects the
+ * moment they're stored, hence changes made to object references are
+ * not stored in the cache, not even for local caches.
+ *
+ * @param defensive boolean indicating whether defensive copies
+ * should be enabled cache wide
+ * @return a configuration builder for fluent programmatic configuration
+ */
+ public StoreAsBinaryConfigurationBuilder defensive(boolean defensive) {
+ this.defensive = defensive;
+ return this;
+ }
+
@Override
public void validate() {
// Nothing to validate.
@@ -90,7 +115,8 @@ public void validate() {
@Override
public StoreAsBinaryConfiguration create() {
- return new StoreAsBinaryConfiguration(enabled, storeKeysAsBinary, storeValuesAsBinary);
+ return new StoreAsBinaryConfiguration(
+ enabled, storeKeysAsBinary, storeValuesAsBinary, defensive);
}
@Override
@@ -98,6 +124,7 @@ public StoreAsBinaryConfigurationBuilder read(StoreAsBinaryConfiguration templat
this.enabled = template.enabled();
this.storeKeysAsBinary = template.storeKeysAsBinary();
this.storeValuesAsBinary = template.storeValuesAsBinary();
+ this.defensive = template.defensive();
return this;
}
1  core/src/main/java/org/infinispan/configuration/parsing/Attribute.java
View
@@ -115,6 +115,7 @@
SPIN_DURATION("spinDuration"),
STORE_KEYS_AS_BINARY("storeKeysAsBinary"),
STORE_VALUES_AS_BINARY("storeValuesAsBinary"),
+ DEFENSIVE("defensive"),
STRATEGY("strategy"),
STREAM_BUFFER_SIZE("streamBufferSize"),
SYNC_COMMIT_PHASE("syncCommitPhase"),
3  core/src/main/java/org/infinispan/configuration/parsing/Parser53.java
View
@@ -497,6 +497,9 @@ private void parseStoreAsBinary(final XMLExtendedStreamReader reader, final Conf
case STORE_VALUES_AS_BINARY:
builder.storeAsBinary().storeValuesAsBinary(Boolean.parseBoolean(value));
break;
+ case DEFENSIVE:
+ builder.storeAsBinary().defensive(Boolean.parseBoolean(value));
+ break;
default:
throw ParseUtils.unexpectedAttribute(reader, i);
}
15 core/src/main/java/org/infinispan/factories/InterceptorChainFactory.java
View
@@ -134,8 +134,19 @@ public InterceptorChain buildInterceptorChain() {
if (configuration.transaction().transactionMode().isTransactional())
interceptorChain.appendInterceptor(createInterceptor(new TxInterceptor(), TxInterceptor.class), false);
- if (isUsingMarshalledValues(configuration))
- interceptorChain.appendInterceptor(createInterceptor(new MarshalledValueInterceptor(), MarshalledValueInterceptor.class), false);
+ if (isUsingMarshalledValues(configuration)) {
+ CommandInterceptor interceptor;
+ if (configuration.storeAsBinary().defensive()) {
+ interceptor = createInterceptor(
+ new DefensiveMarshalledValueInterceptor(), DefensiveMarshalledValueInterceptor.class);
+ } else {
+ interceptor = createInterceptor(
+ new MarshalledValueInterceptor(), MarshalledValueInterceptor.class);
+ }
+
+ interceptorChain.appendInterceptor(interceptor, false);
+ }
+
interceptorChain.appendInterceptor(createInterceptor(new NotificationInterceptor(), NotificationInterceptor.class), false);
46 core/src/main/java/org/infinispan/interceptors/DefensiveMarshalledValueInterceptor.java
View
@@ -0,0 +1,46 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other
+ * contributors as indicated by the @author tags. All rights reserved.
+ * See the copyright.txt in the distribution for a full listing of
+ * individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.infinispan.interceptors;
+
+import org.infinispan.marshall.MarshalledValue;
+
+/**
+ * A marshalled value interceptor which forces defensive copies to be made
+ * proactively. By doing so, clients are no longer able to make any changes
+ * via direct object references, so any changes require a cache modification
+ * call via put/replace...etc methods.
+ *
+ * @author Galder Zamarreño
+ * @since 5.3
+ */
+public class DefensiveMarshalledValueInterceptor extends MarshalledValueInterceptor {
+
+ @Override
+ protected void compact(MarshalledValue mv) {
+ // Force marshalled version to be stored
+ if (mv != null)
+ mv.compact(true, true);
+ }
+
+}
2  core/src/main/java/org/infinispan/interceptors/MarshalledValueInterceptor.java
View
@@ -315,7 +315,7 @@ private Object compactAndProcessRetVal(Set<MarshalledValue> marshalledValues, Ob
return processRetVal(retVal, ctx);
}
- private void compact(MarshalledValue mv) {
+ protected void compact(MarshalledValue mv) {
if (mv == null) return;
mv.compact(false, false);
}
31 core/src/test/java/org/infinispan/configuration/XmlFileParsingTest.java
View
@@ -251,7 +251,38 @@ public void call() {
cm.getDefaultCacheConfiguration();
}
});
+ }
+
+ public void testDefensive() throws IOException {
+ String config = INFINISPAN_START_TAG_NO_SCHEMA +
+ "<default>\n" +
+ "<storeAsBinary enabled=\"true\" defensive=\"true\" />\n" +
+ "</default>\n" +
+ INFINISPAN_END_TAG;
+ InputStream is = new ByteArrayInputStream(config.getBytes());
+ withCacheManager(new CacheManagerCallable(TestCacheManagerFactory.fromStream(is)) {
+ @Override
+ public void call() {
+ Configuration cfg = cm.getDefaultCacheConfiguration();
+ assert cfg.storeAsBinary().enabled();
+ assert cfg.storeAsBinary().defensive();
+ }
+ });
+ config = INFINISPAN_START_TAG_NO_SCHEMA +
+ "<default>\n" +
+ "<storeAsBinary enabled=\"true\" defensive=\"false\" />\n" +
+ "</default>\n" +
+ INFINISPAN_END_TAG;
+ is = new ByteArrayInputStream(config.getBytes());
+ withCacheManager(new CacheManagerCallable(TestCacheManagerFactory.fromStream(is)) {
+ @Override
+ public void call() {
+ Configuration cfg = cm.getDefaultCacheConfiguration();
+ assert cfg.storeAsBinary().enabled();
+ assert !cfg.storeAsBinary().defensive();
+ }
+ });
}
private void assertNamedCacheFile(EmbeddedCacheManager cm, boolean deprecated) {
63 core/src/test/java/org/infinispan/marshall/DefensiveCopyTest.java
View
@@ -0,0 +1,63 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other
+ * contributors as indicated by the @author tags. All rights reserved.
+ * See the copyright.txt in the distribution for a full listing of
+ * individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.infinispan.marshall;
+
+import org.infinispan.configuration.cache.ConfigurationBuilder;
+import org.infinispan.manager.EmbeddedCacheManager;
+import org.infinispan.test.SingleCacheManagerTest;
+import org.infinispan.test.data.Person;
+import org.infinispan.test.fwk.TestCacheManagerFactory;
+import org.testng.annotations.Test;
+
+import static org.testng.AssertJUnit.assertEquals;
+
+/**
+ * Tests defensive copy logic.
+ *
+ * @author Galder Zamarreño
+ * @since 5.3
+ */
+@Test(groups = "functional", testName = "marshall.DefensiveCopyTest")
+public class DefensiveCopyTest extends SingleCacheManagerTest {
+
+ @Override
+ protected EmbeddedCacheManager createCacheManager() throws Exception {
+ ConfigurationBuilder builder = new ConfigurationBuilder();
+ builder.storeAsBinary().enable().defensive(true);
+ return TestCacheManagerFactory.createCacheManager(builder);
+ }
+
+ public void testStoreByValue() {
+ Integer k = 1;
+ Person person = new Person("Mr Infinispan");
+ cache.put(k, person);
+ assertEquals(person, cache.get(k));
+ // Change referenced object
+ person.setName("Ms Hibernate");
+ // If defensive copies are working as expected,
+ // it should be same as before
+ assertEquals(new Person("Mr Infinispan"), cache.get(k));
+ }
+
+}
8 core/src/test/java/org/infinispan/test/data/Person.java
View
@@ -31,6 +31,14 @@
String name = null;
Address address;
+ public Person() {
+ // Needed for serialization
+ }
+
+ public Person(String name) {
+ this.name = name;
+ }
+
public String getName() {
return name;
}
3  jcache/src/main/java/org/infinispan/jcache/ConfigurationAdapter.java
View
@@ -42,7 +42,8 @@ public ConfigurationAdapter(javax.cache.Configuration<K, V> configuration) {
public org.infinispan.configuration.cache.Configuration build() {
ConfigurationBuilder cb = new ConfigurationBuilder();
- cb.storeAsBinary().enabled(c.isStoreByValue());
+ if (c.isStoreByValue())
+ cb.storeAsBinary().enable().defensive(true);
switch (c.getTransactionMode()) {
case NONE:
Please sign in to comment.
Something went wrong with that request. Please try again.