Repository: crucial-project/dso (language: python)

Issue:
Infinispan Server integration
Make DSO work as an extension to a standard Infinispan Server distribution.


Discussion / hints:

Fix patch:
diff --git a/core/pom.xml b/core/pom.xml
index 14ae7e2..c282ccb 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -5,7 +5,7 @@
         <groupId>org.crucial</groupId>
         <artifactId>dso</artifactId>
         <version>2.0</version>
-        <relativePath>../</relativePath>
+        <relativePath>../pom.xml</relativePath>
     </parent>
 
     <artifactId>dso-core</artifactId>
diff --git a/core/src/main/java/org/crucial/dso/Factory.java b/core/src/main/java/org/crucial/dso/Factory.java
index 9d03676..512902d 100644
--- a/core/src/main/java/org/crucial/dso/Factory.java
+++ b/core/src/main/java/org/crucial/dso/Factory.java
@@ -114,8 +114,8 @@ public static Factory get(String server) {
                 .host(host)
                 .port(port)
                 .forceReturnValues(true)
-                .addJavaSerialWhiteList(".*")
-                .marshaller(new JavaSerializationMarshaller()).addJavaSerialWhiteList(".*")
+                .addJavaSerialAllowList(".*")
+                .marshaller(new JavaSerializationMarshaller())
                 .connectionTimeout(3000)
                 .maxRetries(5);
         RemoteCacheManager manager = new RemoteCacheManager(cb.build());
diff --git a/core/src/main/java/org/crucial/dso/container/AbstractContainer.java b/core/src/main/java/org/crucial/dso/container/AbstractContainer.java
index b28d595..4430c7f 100644
--- a/core/src/main/java/org/crucial/dso/container/AbstractContainer.java
+++ b/core/src/main/java/org/crucial/dso/container/AbstractContainer.java
@@ -4,7 +4,6 @@
 import org.crucial.dso.object.Call;
 import org.crucial.dso.object.CallResponse;
 import org.crucial.dso.object.Reference;
-import org.crucial.dso.utils.Reflection;
 import org.infinispan.commons.logging.Log;
 import org.infinispan.commons.logging.LogFactory;
 
diff --git a/core/src/main/java/org/crucial/dso/container/BaseContainer.java b/core/src/main/java/org/crucial/dso/container/BaseContainer.java
index e870353..5705001 100644
--- a/core/src/main/java/org/crucial/dso/container/BaseContainer.java
+++ b/core/src/main/java/org/crucial/dso/container/BaseContainer.java
@@ -3,16 +3,11 @@
 import javassist.util.proxy.MethodHandler;
 import javassist.util.proxy.ProxyFactory;
 import javassist.util.proxy.ProxyObject;
+import org.crucial.dso.object.*;
 import org.crucial.dso.utils.Context;
 import org.crucial.dso.utils.ContextManager;
 import org.crucial.dso.utils.Reflection;
 import org.infinispan.commons.api.BasicCache;
-import org.crucial.dso.object.BoxedReference;
-import org.crucial.dso.object.Call;
-import org.crucial.dso.object.CallConstruct;
-import org.crucial.dso.object.CallInvoke;
-import org.crucial.dso.object.CallResponse;
-import org.crucial.dso.object.Reference;
 
 import javax.persistence.Entity;
 import java.lang.reflect.InvocationTargetException;
@@ -154,7 +149,7 @@ private synchronized void open()
         if (!isOpen) {
 
             if (log.isTraceEnabled())
-                log.trace(" Opening - "+this.toString());
+                log.trace(" Opening - " + this);
 
             CallConstruct construct = new CallConstruct(reference,
                     generateCallID(), forceNew, initArgs, readOptimization, isIdempotent);
@@ -164,7 +159,7 @@ private synchronized void open()
             isOpen = true;
 
             if (log.isTraceEnabled())
-                log.trace(" Opened - "+this.toString());
+                log.trace(" Opened - " + this);
 
         }
 
diff --git a/pom.xml b/pom.xml
index f8e38a7..fc1f2e9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,12 +20,13 @@
         <version.java-uuid-generator>3.1.3</version.java-uuid-generator>
         <version.javax-persistance-api>2.2</version.javax-persistance-api>
         <version.args4j>2.32</version.args4j>
-        <version.javassist>3.23.1-GA</version.javassist>
+        <version.javassist>3.28.0-GA</version.javassist>
         <version.testng>6.14.3</version.testng>
-        <version.infinispan>11.0.4.Final</version.infinispan>
+        <version.infinispan>13.0.6.Final</version.infinispan>
         <version.io-kubernetes-client-java>4.0.0</version.io-kubernetes-client-java>
         <version.picocli>4.0.4</version.picocli>
-        <version.aspectj>1.9.</version.aspectj>
+        <version.aspectj>1.9.7</version.aspectj>
+        <version.javax.annotation>1.3.2</version.javax.annotation>
         <log4j.configurationFile>log4j2.xml</log4j.configurationFile>
     </properties>
 
@@ -77,6 +78,22 @@
                 <version>${version.infinispan}</version>
             </dependency>
 
+            <dependency>
+                <groupId>org.infinispan</groupId>
+                <artifactId>infinispan-component-annotations</artifactId>
+                <version>${version.infinispan}</version>
+                <!-- compile-only dependency -->
+                <scope>provided</scope>
+            </dependency>
+
+            <dependency>
+                <groupId>org.infinispan</groupId>
+                <artifactId>infinispan-component-processor</artifactId>
+                <version>${version.infinispan}</version>
+                <!-- compile-only dependency -->
+                <scope>provided</scope>
+            </dependency>
+
             <dependency>
                 <groupId>org.infinispan</groupId>
                 <artifactId>infinispan-jboss-marshalling</artifactId>
@@ -107,6 +124,13 @@
                 <version>${version.javax-persistance-api}</version>
             </dependency>
 
+            <dependency>
+                <groupId>org.kohsuke.metainf-services</groupId>
+                <artifactId>metainf-services</artifactId>
+                <version>1.8</version>
+                <scope>provided</scope>
+            </dependency>
+
             <!-- fixme -->
             <dependency>
                 <groupId>args4j</groupId>
@@ -131,6 +155,7 @@
                 <groupId>org.infinispan</groupId>
                 <artifactId>infinispan-commons-test</artifactId>
                 <version>${version.infinispan}</version>
+                <scope>test</scope>
             </dependency>
 
             <dependency>
@@ -158,6 +183,13 @@
                 <version>${aspectj.version}</version>
             </dependency>
 
+            <dependency>
+                <groupId>javax.annotation</groupId>
+                <artifactId>javax.annotation-api</artifactId>
+                <version>${version.javax.annotation}</version>
+                <scope>provided</scope>
+            </dependency>
+
             <!-- TEST -->
 
             <dependency>
@@ -196,23 +228,14 @@
     </profiles>
 
     <build>
-        <plugins>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-compiler-plugin</artifactId>
-                <version>3.8.0</version>
-                <configuration>
-                    <release>11</release>
-                </configuration>
-            </plugin>
-        </plugins>
         <pluginManagement>
             <plugins>
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-compiler-plugin</artifactId>
-                    <version>3.3</version>
+                    <version>${maven-compiler-plugin-version}</version>
                     <configuration>
+                        <release>${maven.compiler.source-target}</release>
                         <source>${maven.compiler.source-target}</source>
                         <target>${maven.compiler.source-target}</target>
                         <!-- IMPORTANT -->
diff --git a/server/pom.xml b/server/pom.xml
index f92a69e..69e1223 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -1,11 +1,12 @@
-<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+         xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
     <modelVersion>4.0.0</modelVersion>
 
     <parent>
         <groupId>org.crucial</groupId>
         <artifactId>dso</artifactId>
         <version>2.0</version>
-        <relativePath>../</relativePath>
+        <relativePath>../pom.xml</relativePath>
     </parent>
 
     <artifactId>dso-server</artifactId>
@@ -30,6 +31,16 @@
             <artifactId>infinispan-core</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>org.infinispan</groupId>
+            <artifactId>infinispan-component-annotations</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.infinispan</groupId>
+            <artifactId>infinispan-component-processor</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>org.infinispan</groupId>
             <artifactId>infinispan-commons</artifactId>
@@ -40,6 +51,11 @@
             <artifactId>infinispan-server-hotrod</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>org.infinispan</groupId>
             <artifactId>infinispan-server-hotrod</artifactId>
@@ -65,9 +81,9 @@
         </dependency>
 
         <dependency>
-          <groupId>org.jgroups.kubernetes</groupId>
-          <artifactId>jgroups-kubernetes</artifactId>
-          <version>1.0.9.Final</version>
+            <groupId>org.jgroups.kubernetes</groupId>
+            <artifactId>jgroups-kubernetes</artifactId>
+            <version>1.0.9.Final</version>
         </dependency>
 
         <dependency>
@@ -75,15 +91,12 @@
             <artifactId>java-uuid-generator</artifactId>
         </dependency>
 
-        <!-- TEST -->
-
         <dependency>
-            <groupId>org.infinispan</groupId>
-            <artifactId>infinispan-commons-test</artifactId>
-            <type>test-jar</type>
-            <version>${version.infinispan}</version>
+            <groupId>org.kohsuke.metainf-services</groupId>
+            <artifactId>metainf-services</artifactId>
         </dependency>
 
+        <!-- TEST -->
         <dependency>
             <groupId>${project.groupId}</groupId>
             <artifactId>dso-core</artifactId>
@@ -99,13 +112,6 @@
             <scope>test</scope>
         </dependency>
 
-        <dependency>
-            <groupId>org.infinispan</groupId>
-            <artifactId>infinispan-component-annotations</artifactId>
-            <version>${version.infinispan}</version>
-            <scope>test</scope>
-        </dependency>
-
         <dependency>
             <groupId>org.testng</groupId>
             <artifactId>testng</artifactId>
@@ -129,51 +135,95 @@
         </resources>
         <plugins>
             <plugin>
-                <artifactId>maven-dependency-plugin</artifactId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <version>3.2.0</version>
                 <executions>
                     <execution>
-                        <phase>package</phase>
                         <goals>
-                            <goal>copy-dependencies</goal>
+                            <goal>test-jar</goal>
                         </goals>
-                        <configuration>
-                            <outputDirectory>${project.build.directory}/lib</outputDirectory>
-                        </configuration>
                     </execution>
                 </executions>
             </plugin>
             <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-assembly-plugin</artifactId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <version>3.2.0</version>
                 <executions>
                     <execution>
-                        <id>assembly</id>
                         <phase>package</phase>
                         <goals>
-                            <goal>single</goal>
+                            <goal>copy-dependencies</goal>
                         </goals>
                         <configuration>
-                            <appendAssemblyId>false</appendAssemblyId>
-                            <descriptor>src/main/assembly/bin.xml</descriptor>
-                            <finalName>${project.name}-${project.version}</finalName>
+                            <outputDirectory>${project.build.directory}/lib</outputDirectory>
+                            <includeScope>compile</includeScope>
                         </configuration>
                     </execution>
                 </executions>
             </plugin>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-jar-plugin</artifactId>
-                <version>2.2</version>
-                <executions>
-                    <execution>
-                        <goals>
-                            <goal>test-jar</goal>
-                        </goals>
-                    </execution>
-                </executions>
-            </plugin>
         </plugins>
     </build>
 
+    <profiles>
+        <profile>
+            <id>docker</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>io.fabric8</groupId>
+                        <artifactId>docker-maven-plugin</artifactId>
+                        <version>0.37.0</version>
+                        <executions>
+                            <execution>
+                                <phase>package</phase>
+                                <goals>
+                                    <goal>build</goal>
+                                </goals>
+                                <configuration>
+                                    <images>
+                                        <image>
+                                            <name>otrack/dso-server</name>
+                                            <build>
+                                                <dockerFile>${project.basedir}/src/main/docker/Dockerfile</dockerFile>
+                                                <contextDir>${project.basedir}</contextDir>
+                                            </build>
+                                        </image>
+                                    </images>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>standalone</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>assembly</id>
+                                <phase>package</phase>
+                                <goals>
+                                    <goal>single</goal>
+                                </goals>
+                                <configuration>
+                                    <appendAssemblyId>false</appendAssemblyId>
+                                    <finalName>${project.name}-${project.version}</finalName>
+                                    <descriptors>
+                                        <descriptor>src/main/assembly/bin.xml</descriptor>
+                                    </descriptors>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
 </project>
     
diff --git a/server/src/main/docker/Dockerfile b/server/src/main/docker/Dockerfile
index 8e05ec8..11b2d07 100644
--- a/server/src/main/docker/Dockerfile
+++ b/server/src/main/docker/Dockerfile
@@ -1,30 +1,11 @@
-FROM openjdk:11-slim
+FROM quay.io/infinispan/server:${version.infinispan}
 
-MAINTAINER otrack
+ENV VERSION=${project.version}
 
-ENV NAME=dso-server
-ENV VERSION=2.0
-ENV CLOUD=local
-ENV BUCKET=undefined
-ENV BUCKET_KEY=undefined
-ENV BUCKET_SECRET=undefined
-ENV IP=127.0.0.1
-ENV PORT=11222
-ENV JAR=$NAME-${VERSION}.jar
-ENV EXTRA="-rf 1"
-ENV JVM_EXTRA="-XX:+UseG1GC -Xms64m -Xmx1024m"
+LABEL version=$VERSION
+LABEL description="DSO Server"
 
-WORKDIR $NAME
-
-ADD target/$JAR .
-ADD target/lib lib
-ADD src/main/resources/jgroups-dso-tcp.xml  .
-ADD src/main/resources/jgroups-dso-ec2.xml  .
-ADD src/main/resources/jgroups-dso-gcp.xml  .
-ADD src/main/resources/jgroups-dso-k8s.xml  .
-ADD src/main/resources/log4j.properties .
-ADD src/main/resources/log4j-debug.properties .
-ADD src/main/bin/server.sh  .
-RUN chmod +x .
-
-CMD ["bash", "/dso-server/server.sh"]
+ADD target/dso-server-$VERSION.jar server/lib
+ADD target/lib/dso-core-$VERSION.jar server/lib
+ADD target/lib/guava-*jar server/lib
+ADD target/lib/java-uuid-generator-*.jar server/lib
diff --git a/server/src/main/java/org/crucial/dso/Server.java b/server/src/main/java/org/crucial/dso/Server.java
index 9fc6791..fd4e096 100644
--- a/server/src/main/java/org/crucial/dso/Server.java
+++ b/server/src/main/java/org/crucial/dso/Server.java
@@ -1,5 +1,16 @@
 package org.crucial.dso;
 
+import static org.crucial.dso.Factory.DSO_CACHE_NAME;
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
 import org.crucial.dso.utils.ConfigurationHelper;
 import org.infinispan.commons.marshall.JavaSerializationMarshaller;
 import org.infinispan.configuration.cache.CacheMode;
@@ -12,25 +23,13 @@
 import org.infinispan.test.AbstractCacheTest;
 import org.infinispan.util.logging.Log;
 import org.infinispan.util.logging.LogFactory;
-import org.jboss.marshalling.serial.Serial;
 import org.kohsuke.args4j.CmdLineException;
 import org.kohsuke.args4j.CmdLineParser;
 import org.kohsuke.args4j.Option;
+
 import sun.misc.Signal;
 import sun.misc.SignalHandler;
 
-import java.io.File;
-import java.lang.reflect.Method;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-
-import static org.crucial.dso.Factory.DSO_CACHE_NAME;
-
 /**
  * @author Pierre Sutra
  */
@@ -140,7 +139,7 @@ public void doMain(String[] args) {
         // marshalling
         gbuilder.serialization()
                 .marshaller(new JavaSerializationMarshaller())
-                .whiteList()
+                .allowList()
                 .addRegexps(".*");
 
         ConfigurationBuilder cBuilder
@@ -160,7 +159,6 @@ public void doMain(String[] args) {
                 false);
 
         HotRodServerConfigurationBuilder hbuilder = new HotRodServerConfigurationBuilder();
-        hbuilder.topologyStateTransfer(true);
         hbuilder.host(host);
         hbuilder.port(port);
         hbuilder.recvBufSize(1000000);
diff --git a/server/src/main/java/org/crucial/dso/server/Attribute.java b/server/src/main/java/org/crucial/dso/server/Attribute.java
new file mode 100644
index 0000000..febc166
--- /dev/null
+++ b/server/src/main/java/org/crucial/dso/server/Attribute.java
@@ -0,0 +1,54 @@
+package org.crucial.dso.server;
+
+import org.infinispan.commons.configuration.io.NamingStrategy;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @since 14.0
+ **/
+public enum Attribute {
+    UNKNOWN(null),
+    IDEMPOTENT;
+
+    private static final Map<String, Attribute> MAP;
+
+    static {
+        final Map<String, Attribute> map = new HashMap<>(8);
+        for (Attribute element : values()) {
+            final String name = element.getLocalName();
+            if (name != null) map.put(name, element);
+        }
+        MAP = map;
+    }
+
+    private final String name;
+
+    Attribute(final String name) {
+        this.name = name;
+    }
+
+    Attribute() {
+        this.name = NamingStrategy.KEBAB_CASE.convert(name()).toLowerCase();
+    }
+
+    public static Attribute forName(String localName) {
+        final Attribute element = MAP.get(localName);
+        return element == null ? UNKNOWN : element;
+    }
+
+    /**
+     * Get the local name of this element.
+     *
+     * @return the local name
+     */
+    public String getLocalName() {
+        return name;
+    }
+
+    @Override
+    public String toString() {
+        return name;
+    }
+}
diff --git a/server/src/main/java/org/crucial/dso/server/DSOConfiguration.java b/server/src/main/java/org/crucial/dso/server/DSOConfiguration.java
new file mode 100644
index 0000000..1f4f737
--- /dev/null
+++ b/server/src/main/java/org/crucial/dso/server/DSOConfiguration.java
@@ -0,0 +1,24 @@
+package org.crucial.dso.server;
+
+import org.infinispan.commons.configuration.BuiltBy;
+import org.infinispan.commons.configuration.attributes.AttributeDefinition;
+import org.infinispan.commons.configuration.attributes.AttributeSet;
+import org.infinispan.commons.configuration.attributes.ConfigurationElement;
+
+@BuiltBy(DSOConfigurationBuilder.class)
+public class DSOConfiguration extends ConfigurationElement<DSOConfiguration> {
+    static final AttributeDefinition<Boolean> IDEMPOTENT =
+            AttributeDefinition.builder("idempotent", false).immutable().build();
+
+    public DSOConfiguration(AttributeSet attributes) {
+        super(Element.DSO, attributes);
+    }
+
+    public static AttributeSet attributeDefinitionSet() {
+        return new AttributeSet(DSOConfiguration.class, IDEMPOTENT);
+    }
+
+    public boolean idempotent() {
+        return attributes.attribute(IDEMPOTENT).get();
+    }
+}
diff --git a/server/src/main/java/org/crucial/dso/server/DSOConfigurationBuilder.java b/server/src/main/java/org/crucial/dso/server/DSOConfigurationBuilder.java
new file mode 100644
index 0000000..54622d7
--- /dev/null
+++ b/server/src/main/java/org/crucial/dso/server/DSOConfigurationBuilder.java
@@ -0,0 +1,35 @@
+package org.crucial.dso.server;
+
+import org.infinispan.commons.configuration.Builder;
+import org.infinispan.commons.configuration.attributes.AttributeSet;
+import org.infinispan.configuration.global.GlobalConfigurationBuilder;
+
+import static org.crucial.dso.server.Attribute.IDEMPOTENT;
+
+public class DSOConfigurationBuilder implements Builder<DSOConfiguration> {
+    private final AttributeSet attributes;
+
+    public DSOConfigurationBuilder() {
+        this(null);
+    }
+
+    public DSOConfigurationBuilder(GlobalConfigurationBuilder builder) {
+        this.attributes = DSOConfiguration.attributeDefinitionSet();
+    }
+
+    @Override
+    public DSOConfiguration create() {
+        return new DSOConfiguration(attributes.protect());
+    }
+
+    @Override
+    public Builder<?> read(DSOConfiguration template) {
+        this.read(template);
+        return this;
+    }
+
+    public DSOConfigurationBuilder idempotent(boolean idempotent) {
+        attributes.attribute(IDEMPOTENT).set(idempotent);
+        return this;
+    }
+}
diff --git a/server/src/main/java/org/crucial/dso/server/DSOConfigurationParser.java b/server/src/main/java/org/crucial/dso/server/DSOConfigurationParser.java
new file mode 100644
index 0000000..9d7b2be
--- /dev/null
+++ b/server/src/main/java/org/crucial/dso/server/DSOConfigurationParser.java
@@ -0,0 +1,63 @@
+package org.crucial.dso.server;
+
+
+import org.infinispan.commons.configuration.io.ConfigurationReader;
+import org.infinispan.configuration.cache.ConfigurationBuilder;
+import org.infinispan.configuration.parsing.*;
+import org.kohsuke.MetaInfServices;
+
+/**
+ * Anchored keys parser extension.
+ * <p>
+ * This extension parses elements in the "urn:crucial:dso" namespace
+ *
+ * @author Tristan Tarrant
+ */
+@MetaInfServices
+@Namespace(root = "dso")
+@Namespace(uri = "urn:crucial:dso:*", root = "dso", since = "13.0")
+public class DSOConfigurationParser implements ConfigurationParser {
+
+    @Override
+    public void readElement(ConfigurationReader reader, ConfigurationBuilderHolder holder) {
+        if (!holder.inScope(ParserScope.CACHE) && !holder.inScope(ParserScope.CACHE_TEMPLATE)) {
+            throw new IllegalStateException("WRONG SCOPE");
+        }
+        ConfigurationBuilder builder = holder.getCurrentConfigurationBuilder();
+
+        Element element = Element.forName(reader.getLocalName());
+        switch (element) {
+            case DSO: {
+                DSOConfigurationBuilder dsoBuilder = builder.addModule(DSOConfigurationBuilder.class);
+                parseDso(reader, dsoBuilder);
+                break;
+            }
+            default: {
+                throw ParseUtils.unexpectedElement(reader);
+            }
+        }
+    }
+
+    private void parseDso(ConfigurationReader reader, DSOConfigurationBuilder builder) {
+        for (int i = 0; i < reader.getAttributeCount(); i++) {
+            ParseUtils.requireNoNamespaceAttribute(reader, i);
+            String value = reader.getAttributeValue(i);
+            Attribute attribute = Attribute.forName(reader.getAttributeName(i));
+            switch (attribute) {
+                case IDEMPOTENT: {
+                    builder.idempotent(Boolean.parseBoolean(value));
+                    break;
+                }
+                default: {
+                    throw ParseUtils.unexpectedAttribute(reader, i);
+                }
+            }
+        }
+        ParseUtils.requireNoContent(reader);
+    }
+
+    @Override
+    public Namespace[] getNamespaces() {
+        return ParseUtils.getNamespaceAnnotations(getClass());
+    }
+}
diff --git a/server/src/main/java/org/crucial/dso/server/DSOLifecycle.java b/server/src/main/java/org/crucial/dso/server/DSOLifecycle.java
new file mode 100644
index 0000000..61f9b4f
--- /dev/null
+++ b/server/src/main/java/org/crucial/dso/server/DSOLifecycle.java
@@ -0,0 +1,57 @@
+package org.crucial.dso.server;
+
+import org.crucial.dso.Factory;
+import org.infinispan.configuration.cache.CacheMode;
+import org.infinispan.configuration.cache.Configuration;
+import org.infinispan.configuration.cache.ConfigurationBuilder;
+import org.infinispan.configuration.global.GlobalConfiguration;
+import org.infinispan.factories.ComponentRegistry;
+import org.infinispan.factories.GlobalComponentRegistry;
+import org.infinispan.factories.annotations.InfinispanModule;
+import org.infinispan.factories.impl.BasicComponentRegistry;
+import org.infinispan.interceptors.AsyncInterceptorChain;
+import org.infinispan.interceptors.impl.CallInterceptor;
+import org.infinispan.lifecycle.ModuleLifecycle;
+import org.infinispan.registry.InternalCacheRegistry;
+import org.infinispan.util.logging.Log;
+
+import java.util.EnumSet;
+
+@InfinispanModule(name = "crucial-dso", requiredModules = "core")
+public class DSOLifecycle implements ModuleLifecycle {
+    private DSOConfiguration configuration;
+    private StateMachineInterceptor stateMachineInterceptor;
+
+    @Override
+    public void cacheManagerStarting(GlobalComponentRegistry gcr, GlobalConfiguration globalConfiguration) {
+        BasicComponentRegistry registry = gcr.getComponent(BasicComponentRegistry.class);
+        InternalCacheRegistry internalCacheRegistry = registry.getComponent(InternalCacheRegistry.class).running();
+        ConfigurationBuilder builder = new ConfigurationBuilder();
+        builder.clustering().cacheMode(CacheMode.DIST_ASYNC);
+        internalCacheRegistry.registerInternalCache(Factory.DSO_CACHE_NAME, builder.build(), EnumSet.of(InternalCacheRegistry.Flag.USER));
+        configuration = globalConfiguration.module(DSOConfiguration.class);
+        if (configuration == null) {
+            configuration = new DSOConfigurationBuilder().create();
+        }
+    }
+
+    @Override
+    public void cacheStarting(ComponentRegistry cr, Configuration configuration, String cacheName) {
+        if (Factory.DSO_CACHE_NAME.equals(cacheName) && configuration.clustering().cacheMode().isClustered()) {
+            BasicComponentRegistry bcr = cr.getComponent(BasicComponentRegistry.class);
+            stateMachineInterceptor = new StateMachineInterceptor();
+            bcr.registerComponent(StateMachineInterceptor.class, stateMachineInterceptor, true);
+            bcr.addDynamicDependency(AsyncInterceptorChain.class.getName(), StateMachineInterceptor.class.getName());
+            bcr.getComponent(AsyncInterceptorChain.class).wired()
+                    .addInterceptorBefore(stateMachineInterceptor, CallInterceptor.class);
+        }
+    }
+
+    @Override
+    public void cacheStarted(ComponentRegistry cr, String cacheName) {
+        if (Factory.DSO_CACHE_NAME.equals(cacheName)) {
+            stateMachineInterceptor.setup(Factory.forCache(cr.getCache().running()), this.configuration.idempotent());
+            Log.CONTAINER.info("DSO cache initialized");
+        }
+    }
+}
diff --git a/server/src/main/java/org/crucial/dso/server/Element.java b/server/src/main/java/org/crucial/dso/server/Element.java
new file mode 100644
index 0000000..187020c
--- /dev/null
+++ b/server/src/main/java/org/crucial/dso/server/Element.java
@@ -0,0 +1,55 @@
+package org.crucial.dso.server;
+
+import org.infinispan.commons.configuration.io.NamingStrategy;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @since 14.0
+ **/
+public enum Element {
+    // Must be first
+    UNKNOWN(null),
+    DSO;
+
+    private static final Map<String, Element> MAP;
+
+    static {
+        final Map<String, Element> map = new HashMap<>(8);
+        for (Element element : values()) {
+            final String name = element.getLocalName();
+            if (name != null) map.put(name, element);
+        }
+        MAP = map;
+    }
+
+    private final String name;
+
+    Element(final String name) {
+        this.name = name;
+    }
+
+    Element() {
+        this.name = NamingStrategy.KEBAB_CASE.convert(name()).toLowerCase();
+    }
+
+    public static Element forName(String localName) {
+        final Element element = MAP.get(localName);
+        return element == null ? UNKNOWN : element;
+    }
+
+    /**
+     * Get the local name of this element.
+     *
+     * @return the local name
+     */
+    public String getLocalName() {
+        return name;
+    }
+
+    @Override
+    public String toString() {
+        return name;
+    }
+}
diff --git a/server/src/main/java/org/crucial/dso/server/StateMachineInterceptor.java b/server/src/main/java/org/crucial/dso/server/StateMachineInterceptor.java
index 7ada9cb..afe25eb 100644
--- a/server/src/main/java/org/crucial/dso/server/StateMachineInterceptor.java
+++ b/server/src/main/java/org/crucial/dso/server/StateMachineInterceptor.java
@@ -1,25 +1,21 @@
 package org.crucial.dso.server;
 
 import com.fasterxml.uuid.impl.RandomBasedGenerator;
+import org.crucial.dso.Factory;
+import org.crucial.dso.object.*;
 import org.crucial.dso.utils.Context;
 import org.crucial.dso.utils.ContextManager;
 import org.crucial.dso.utils.Reflection;
 import org.infinispan.commands.write.ClearCommand;
 import org.infinispan.commands.write.PutKeyValueCommand;
 import org.infinispan.context.InvocationContext;
-import org.crucial.dso.Factory;
-import org.crucial.dso.object.Call;
-import org.crucial.dso.object.CallConstruct;
-import org.crucial.dso.object.CallInvoke;
-import org.crucial.dso.object.CallResponse;
-import org.crucial.dso.object.Reference;
-import org.infinispan.interceptors.impl.ClusteringInterceptor;
+import org.infinispan.interceptors.BaseCustomAsyncInterceptor;
 import org.infinispan.util.logging.Log;
 import org.infinispan.util.logging.LogFactory;
 
 import java.util.Random;
 
-public class StateMachineInterceptor extends ClusteringInterceptor {
+public class StateMachineInterceptor extends BaseCustomAsyncInterceptor {
 
     private static final Log log = LogFactory.getLog(StateMachineInterceptor.class);
 
@@ -144,11 +140,4 @@ public void setup(Factory factory, boolean useIdempotence){
         this.factory = factory;
         this.withIdempotence = useIdempotence;
     }
-
-    // utils
-
-    @Override
-    protected Log getLog() {
-        return log;
-    }
 }
diff --git a/server/src/main/java/org/crucial/dso/utils/ConfigurationHelper.java b/server/src/main/java/org/crucial/dso/utils/ConfigurationHelper.java
index 16f2bc1..bf9f241 100644
--- a/server/src/main/java/org/crucial/dso/utils/ConfigurationHelper.java
+++ b/server/src/main/java/org/crucial/dso/utils/ConfigurationHelper.java
@@ -1,11 +1,10 @@
 package org.crucial.dso.utils;
 
+import org.crucial.dso.Factory;
+import org.crucial.dso.server.StateMachineInterceptor;
 import org.infinispan.configuration.cache.CacheMode;
 import org.infinispan.configuration.cache.ConfigurationBuilder;
-import org.infinispan.configuration.cache.Index;
 import org.infinispan.configuration.cache.SingleFileStoreConfigurationBuilder;
-import org.crucial.dso.Factory;
-import org.crucial.dso.server.StateMachineInterceptor;
 import org.infinispan.interceptors.impl.CallInterceptor;
 import org.infinispan.manager.EmbeddedCacheManager;
 import org.infinispan.transaction.TransactionMode;
diff --git a/server/src/main/resources/crucial-dso.xsd b/server/src/main/resources/crucial-dso.xsd
new file mode 100644
index 0000000..0ba92c7
--- /dev/null
+++ b/server/src/main/resources/crucial-dso.xsd
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<xs:schema elementFormDefault="qualified" version="1.0"
+           targetNamespace="urn:crucial:dso::13.0"
+           xmlns:tns="urn:crucial:dso::13.0"
+           xmlns:xs="http://www.w3.org/2001/XMLSchema">
+
+    <xs:element name="dso" type="tns:dso"/>
+
+    <xs:complexType name="dso">
+        <xs:attribute name="idempotent" type="xs:boolean" default="false">
+            <xs:annotation>
+                <xs:documentation>
+                    Enables idempotency.
+                </xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+    </xs:complexType>
+</xs:schema>


Test patch:
diff --git a/server/src/test/java/org/crucial/dso/test/AbstractTest.java b/server/src/test/java/org/crucial/dso/test/AbstractTest.java
index 8b3ddac..e6533f7 100644
--- a/server/src/test/java/org/crucial/dso/test/AbstractTest.java
+++ b/server/src/test/java/org/crucial/dso/test/AbstractTest.java
@@ -2,7 +2,6 @@
 
 import com.fasterxml.uuid.Generators;
 import com.fasterxml.uuid.impl.RandomBasedGenerator;
-import javassist.util.proxy.Proxy;
 import org.crucial.dso.*;
 import org.crucial.dso.utils.Context;
 import org.crucial.dso.utils.ContextManager;
@@ -39,7 +38,7 @@ public abstract class AbstractTest extends MultipleCacheManagersTest {
     protected static final String PERSISTENT_STORAGE_DIR = "/tmp/dso";
 
     @Test(groups = {"dso"})
-    public void baseUsage() throws Exception {
+    public void baseUsage() {
 
         BasicCacheContainer cacheManager = containers().iterator().next();
         BasicCache<Object, Object> cache = cacheManager.getCache(DSO_CACHE_NAME);
@@ -57,7 +56,7 @@ public void baseUsage() throws Exception {
     }
 
     @Test(groups = {"dso", "stress"})
-    public void basePerformance() throws Exception {
+    public void basePerformance() {
 
         BasicCacheContainer cacheManager = containers().iterator().next();
         BasicCache<Object, Object> cache = cacheManager.getCache(DSO_CACHE_NAME);
@@ -77,7 +76,7 @@ public void basePerformance() throws Exception {
     }
 
     @Test(groups = {"dso"})
-    public void persistence() throws Exception {
+    public void persistence() {
 
         assertTrue(containers().size() >= 2);
 
@@ -129,7 +128,7 @@ public void persistence() throws Exception {
     }
 
     @Test(groups = {"dso"})
-    public void baseCacheTest() throws Exception {
+    public void baseCacheTest() {
 
         Iterator<BasicCacheContainer> it = containers().iterator();
         BasicCacheContainer container1 = it.next();
@@ -178,7 +177,7 @@ public void concurrentUpdates() throws Exception {
     }
 
     @Test(groups = {"dso"})
-    public void multipleCreation() throws Exception {
+    public void multipleCreation() {
 
         assertTrue(containers().size() >= 2);
 
@@ -206,7 +205,7 @@ public void multipleCreation() throws Exception {
     }
 
     @Test(groups = {"dso"})
-    public void baseComposition() throws Exception {
+    public void baseComposition() {
         assert ShardedObject.class.isAssignableFrom(ShardedObject.class);
 
         Iterator<BasicCacheContainer> it = containers().iterator();
@@ -312,7 +311,7 @@ public void memoryUsage(){
     }
 
     @Test(groups = {"dso"})
-    public void idempotence() throws IllegalAccessException {
+    public void idempotence() {
 
         Iterator<BasicCacheContainer> it = containers().iterator();
         BasicCacheContainer container1 = it.next();
@@ -371,9 +370,9 @@ protected void assertOnAllCaches(Object key, String value) {
         for (Cache c : caches()) {
             Object realVal = c.get(key);
             if (value == null) {
-                assert realVal == null : "Expecting [" + key + "] to equal [" + value + "] on cache " + c.toString();
+                assert realVal == null : "Expecting [" + key + "] to equal [" + value + "] on cache " + c;
             } else {
-                assert value.equals(realVal) : "Expecting [" + key + "] to equal [" + value + "] on cache " + c.toString();
+                assert value.equals(realVal) : "Expecting [" + key + "] to equal [" + value + "] on cache " + c;
             }
         }
         // Allow some time for all ClusteredGetCommands to finish executing
@@ -391,7 +390,7 @@ public SetTask(Set set, int n) {
         }
 
         @Override
-        public Integer call() throws Exception {
+        public Integer call() {
 
             int ret = 0;
             for (int i = 0; i < ncalls; i++) {
diff --git a/server/src/test/java/org/crucial/dso/test/BaseTest.java b/server/src/test/java/org/crucial/dso/test/BaseTest.java
index b8a0ba8..556fc67 100644
--- a/server/src/test/java/org/crucial/dso/test/BaseTest.java
+++ b/server/src/test/java/org/crucial/dso/test/BaseTest.java
@@ -1,9 +1,8 @@
 package org.crucial.dso.test;
 
-import org.crucial.dso.utils.ConfigurationHelper;
+import org.crucial.dso.Factory;
 import org.infinispan.Cache;
 import org.infinispan.commons.api.BasicCacheContainer;
-import org.crucial.dso.Factory;
 import org.infinispan.manager.EmbeddedCacheManager;
 import org.infinispan.test.fwk.TransportFlags;
 import org.testng.annotations.Test;
@@ -39,15 +38,6 @@ public synchronized boolean addContainer() {
         TransportFlags flags = new TransportFlags();
         flags.withFD(true).withMerge(true);
         EmbeddedCacheManager cm = addClusterEnabledCacheManager(flags);
-        ConfigurationHelper.installCache(
-                cm,
-                CACHE_MODE,
-                REPLICATION_FACTOR, MAX_ENTRIES,
-                PASSIVATION,
-                PERSISTENT_STORAGE_DIR + "/" + containers().size(),
-                true,
-                false,
-                true);
         waitForClusterToForm(DSO_CACHE_NAME);
         Cache cache = cm.getCache(DSO_CACHE_NAME);
         cache.start();
diff --git a/server/src/test/java/org/crucial/dso/test/RemoteTest.java b/server/src/test/java/org/crucial/dso/test/RemoteTest.java
index a9a0fe7..914bb9f 100644
--- a/server/src/test/java/org/crucial/dso/test/RemoteTest.java
+++ b/server/src/test/java/org/crucial/dso/test/RemoteTest.java
@@ -1,12 +1,10 @@
 package org.crucial.dso.test;
 
-import org.crucial.dso.utils.ConfigurationHelper;
+import org.crucial.dso.Factory;
+import org.crucial.dso.server.DSOConfigurationBuilder;
 import org.infinispan.client.hotrod.RemoteCacheManager;
 import org.infinispan.commons.api.BasicCacheContainer;
 import org.infinispan.commons.marshall.JavaSerializationMarshaller;
-import org.infinispan.commons.marshall.Marshaller;
-import org.crucial.dso.Factory;
-import org.infinispan.configuration.cache.ConfigurationBuilder;
 import org.infinispan.configuration.global.GlobalConfigurationBuilder;
 import org.infinispan.lifecycle.ComponentStatus;
 import org.infinispan.manager.EmbeddedCacheManager;
@@ -52,26 +50,17 @@ public boolean addContainer() {
         GlobalConfigurationBuilder gbuilder = GlobalConfigurationBuilder.defaultClusteredBuilder();
         gbuilder.serialization()
                 .marshaller(new JavaSerializationMarshaller())
-                .whiteList()
+                .allowList()
                 .addRegexps(".*");
+        gbuilder.addModule(DSOConfigurationBuilder.class).idempotent(true);
         TransportFlags flags = new TransportFlags();
         flags.withFD(true).withMerge(true);
         EmbeddedCacheManager cm = TestCacheManagerFactory.createClusteredCacheManager(
                 false,
                 gbuilder,
-                (ConfigurationBuilder)null,
+                null,
                 flags);
         this.cacheManagers.add(cm);
-        ConfigurationHelper.installCache(
-                cm,
-                CACHE_MODE,
-                REPLICATION_FACTOR,
-                MAX_ENTRIES,
-                PASSIVATION,
-                PERSISTENT_STORAGE_DIR + "/" + index,
-                true,
-                false,
-                true);
         waitForClusterToForm(DSO_CACHE_NAME);
 
         // hotrod server
@@ -84,7 +73,7 @@ public boolean addContainer() {
         RemoteCacheManager manager = new RemoteCacheManager(
                 new org.infinispan.client.hotrod.configuration.ConfigurationBuilder()
                         .addServers(server.getHost() + ":" + server.getPort())
-                        .marshaller(new JavaSerializationMarshaller()).addJavaSerialWhiteList(".*")
+                        .marshaller(new JavaSerializationMarshaller()).addJavaSerialAllowList(".*")
                         .forceReturnValues(true)
                         .build());
         remoteCacheManagers.add(manager);