Index: docs/reference/src/main/docbook/en-US/content/connectors/infinispan.xml
===================================================================
--- docs/reference/src/main/docbook/en-US/content/connectors/infinispan.xml (revision 1847)
+++ docs/reference/src/main/docbook/en-US/content/connectors/infinispan.xml (working copy)
@@ -158,6 +158,21 @@ config.repositorySource("Infinispan Store")
+
+ Considerations for Distributed Sources
+
+ The &InfinispanSource; can be used to provide access to an Infinispan cluster, but be sure to use the DIST_SYNC
+ cache mode. Using other modes will likely lead to data inconsistency.
+
+
+ Additionally, some operating systems (e.g., OS X) require you to set either the java.net.preferIPv4Stack
+ or the java.net.preferIPv6Addresses
system property to true
. These properties are used by
+ JGroups, the communications library that underlies Infinispan, to help determine which address type to use.
+
+
+ The rootNodeUuid
property must be set to the same value for all Infinispan sources in the cluster.
+
+
Index: extensions/modeshape-connector-infinispan/pom.xml
===================================================================
--- extensions/modeshape-connector-infinispan/pom.xml (revision 1847)
+++ extensions/modeshape-connector-infinispan/pom.xml (working copy)
@@ -45,7 +45,7 @@
test
org.infinispan
Index: extensions/modeshape-connector-infinispan/src/main/java/org/modeshape/connector/infinispan/InfinispanSource.java
===================================================================
--- extensions/modeshape-connector-infinispan/src/main/java/org/modeshape/connector/infinispan/InfinispanSource.java (revision 1847)
+++ extensions/modeshape-connector-infinispan/src/main/java/org/modeshape/connector/infinispan/InfinispanSource.java (working copy)
@@ -107,7 +107,7 @@ public class InfinispanSource implements BaseRepositorySource, ObjectFactory {
protected static final String UPDATES_ALLOWED = "updatesAllowed";
private volatile String name;
- private volatile UUID rootNodeUuid = UUID.randomUUID();
+ private volatile UUID rootNodeUuid = UUID.fromString("cafebabe-cafe-babe-cafe-babecafebabe");
private volatile CachePolicy defaultCachePolicy;
private volatile String cacheConfigurationName;
private volatile String cacheManagerJndiName;
@@ -380,6 +380,46 @@ public class InfinispanSource implements BaseRepositorySource, ObjectFactory {
capabilities.supportsReferences());
}
+ private CacheManager createCacheManager() {
+ CacheManager cacheManager;
+
+ String configName = getCacheConfigurationName();
+ if (configName == null) {
+ cacheManager = new DefaultCacheManager();
+ } else {
+ /*
+ * First try treating the config name as a classpath resource, then as a file name.
+ */
+ InputStream configStream = getClass().getResourceAsStream(configName);
+ try {
+ if (configStream == null) {
+ configStream = new FileInputStream(configName);
+ }
+ } catch (IOException ioe) {
+ I18n msg = InfinispanConnectorI18n.configFileNotFound;
+ throw new RepositorySourceException(this.name, msg.text(configName), ioe);
+ }
+
+ try {
+ cacheManager = new DefaultCacheManager(configStream);
+ } catch (IOException ioe) {
+ I18n msg = InfinispanConnectorI18n.configFileNotValid;
+ throw new RepositorySourceException(this.name, msg.text(configName), ioe);
+ } finally {
+ try {
+ configStream.close();
+ } catch (IOException ioe) {
+ }
+ }
+ }
+
+ return cacheManager;
+ }
+
+ final CacheManager cacheManager() {
+ return repository.getCacheManager();
+ }
+
/**
* {@inheritDoc}
*
@@ -418,36 +458,7 @@ public class InfinispanSource implements BaseRepositorySource, ObjectFactory {
}
}
if (cacheManager == null) {
- String configName = getCacheConfigurationName();
- if (configName == null) {
- cacheManager = new DefaultCacheManager();
- } else {
- /*
- * First try treating the config name as a classpath resource, then as a file name.
- */
- InputStream configStream = getClass().getResourceAsStream(configName);
- try {
- if (configStream == null) {
- configStream = new FileInputStream(configName);
- }
- } catch (IOException ioe) {
- I18n msg = InfinispanConnectorI18n.configFileNotFound;
- throw new RepositorySourceException(this.name, msg.text(configName), ioe);
- }
-
- try {
- cacheManager = new DefaultCacheManager(configStream);
- } catch (IOException ioe) {
- I18n msg = InfinispanConnectorI18n.configFileNotValid;
- throw new RepositorySourceException(this.name, msg.text(configName), ioe);
- } finally {
- try {
- configStream.close();
- } catch (IOException ioe) {
- }
- }
-
- }
+ cacheManager = createCacheManager();
}
// Now create the repository ...
Index: extensions/modeshape-connector-infinispan/src/test/java/org/modeshape/connector/infinispan/InfinispanClusterTest.java
new file mode 100644
===================================================================
--- /dev/null (revision 1847)
+++ extensions/modeshape-connector-infinispan/src/test/java/org/modeshape/connector/infinispan/InfinispanClusterTest.java (working copy)
@@ -0,0 +1,107 @@
+package org.modeshape.connector.infinispan;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import java.util.UUID;
+import org.infinispan.Cache;
+import org.junit.Test;
+import org.modeshape.graph.ExecutionContext;
+import org.modeshape.graph.Graph;
+import org.modeshape.graph.Node;
+import org.modeshape.graph.Subgraph;
+import org.modeshape.graph.connector.RepositoryConnection;
+import org.modeshape.graph.connector.RepositoryConnectionFactory;
+import org.modeshape.graph.connector.RepositoryContext;
+import org.modeshape.graph.connector.RepositorySource;
+import org.modeshape.graph.connector.RepositorySourceException;
+import org.modeshape.graph.observe.Observer;
+import org.modeshape.graph.property.PathNotFoundException;
+
+/*
+ * Quick test that two clustered InfinispanSources can share data.
+ */
+public class InfinispanClusterTest {
+
+ private static final String CONFIG_FILE = "./src/test/resources/infinispan_clustered_config.xml";
+
+
+ private final ExecutionContext context = new ExecutionContext();
+
+ @Test
+ public void shouldDistributeGraphNodes() {
+ InfinispanSource source1 = new InfinispanSource();
+ source1.setName("source1");
+ source1.setCacheConfigurationName(CONFIG_FILE);
+ source1.initialize(repositoryContextFor(source1));
+
+ InfinispanSource source2 = new InfinispanSource();
+ source2.setName("source2");
+ source2.setCacheConfigurationName(CONFIG_FILE);
+ source2.initialize(repositoryContextFor(source2));
+
+ Graph graph1 = Graph.create(source1, context);
+ Graph graph2 = Graph.create(source2, context);
+
+ assertThat(source1.getRootNodeUuid(), is(source2.getRootNodeUuid()));
+
+ Node root1 = graph1.getNodeAt("/");
+ Node root2 = graph2.getNodeAt("/");
+
+ assertThat(root1.getLocation(), is(root2.getLocation()));
+
+ graph1.create("/foo").and();
+
+ Cache cache1 = source1.cacheManager().getCache("");
+ Cache cache2 = source2.cacheManager().getCache("");
+ // System.out.println(cache1 + " -> " + cache1.entrySet());
+ // System.out.println(cache2 + " -> " + cache2.entrySet());
+
+ assertThat(cache1.size(), is(2));
+ assertThat(cache2.size(), is(2));
+
+ graph2.getNodeAt("/foo");
+
+ graph2.delete("/foo");
+
+ try {
+ graph1.getNodeAt("/foo");
+ fail("/foo was deleted by the other source and should no longer exist");
+ } catch (PathNotFoundException expected) {
+
+ }
+ }
+
+ private final RepositoryContext repositoryContextFor( final RepositorySource source ) {
+ return new RepositoryContext() {
+
+ @Override
+ public Subgraph getConfiguration( int depth ) {
+ return null;
+ }
+
+ @Override
+ public ExecutionContext getExecutionContext() {
+ return context;
+ }
+
+ @Override
+ public Observer getObserver() {
+ return null;
+ }
+
+ @Override
+ public RepositoryConnectionFactory getRepositoryConnectionFactory() {
+ return new RepositoryConnectionFactory() {
+
+ @Override
+ public RepositoryConnection createConnection( String sourceName ) throws RepositorySourceException {
+ return source.getConnection();
+ }
+
+ };
+ }
+
+ };
+ }
+}
Index: extensions/modeshape-connector-infinispan/src/test/resources/infinispan_clustered_config.xml
new file mode 100644
===================================================================
--- /dev/null (revision 1847)
+++ extensions/modeshape-connector-infinispan/src/test/resources/infinispan_clustered_config.xml (working copy)
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
Index: modeshape-integration-tests/pom.xml
===================================================================
--- modeshape-integration-tests/pom.xml (revision 1847)
+++ modeshape-integration-tests/pom.xml (working copy)
@@ -80,6 +80,12 @@
modeshape-connector-jbosscache
${project.version}
test
+
+
+ jgroups
+ jgroups
+
+
org.modeshape
Index: modeshape-integration-tests/src/test/java/org/modeshape/test/integration/infinispan/InfinispanClusterTest.java
new file mode 100644
===================================================================
--- /dev/null (revision 1847)
+++ modeshape-integration-tests/src/test/java/org/modeshape/test/integration/infinispan/InfinispanClusterTest.java (working copy)
@@ -0,0 +1,95 @@
+package org.modeshape.test.integration.infinispan;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import java.util.concurrent.TimeUnit;
+import javax.jcr.Item;
+import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.Repository;
+import javax.jcr.Session;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.modeshape.common.collection.Problem;
+import org.modeshape.jcr.JcrConfiguration;
+import org.modeshape.jcr.JcrEngine;
+
+public class InfinispanClusterTest {
+
+ private JcrEngine engine1;
+ private JcrEngine engine2;
+
+ @Before
+ public void beforeEach() throws Exception {
+ engine1 = new JcrConfiguration().loadFrom("./src/test/resources/infinispan/configRepository.xml").build();
+ engine2 = new JcrConfiguration().loadFrom("./src/test/resources/infinispan/configRepository.xml").build();
+
+ engine1.start();
+ if (engine1.getProblems().hasProblems()) {
+ for (Problem problem : engine1.getProblems()) {
+ System.err.println(problem.getMessageString());
+ }
+ }
+
+ engine2.start();
+ if (engine2.getProblems().hasProblems()) {
+ for (Problem problem : engine2.getProblems()) {
+ System.err.println(problem.getMessageString());
+ }
+ }
+ }
+
+ @After
+ public void afterEach() throws Exception {
+ if (engine1 != null) {
+ engine1.shutdown();
+ engine1.awaitTermination(5, TimeUnit.SECONDS);
+ }
+
+ if (engine2 != null) {
+ engine2.shutdown();
+ engine2.awaitTermination(5, TimeUnit.SECONDS);
+ }
+ }
+
+ @Test
+ public void shouldShareContent() throws Exception {
+ assertThat(engine1, is(notNullValue()));
+ assertThat(engine2, is(notNullValue()));
+
+ Repository repo1 = engine1.getRepository("Test Repository Source");
+ Repository repo2 = engine2.getRepository("Test Repository Source");
+
+ Session session1 = repo1.login();
+ Session session2 = repo2.login();
+
+ Node fooNode = session1.getRootNode().addNode("foo");
+ fooNode.addMixin("mix:referenceable");
+
+ checkNoNodeAt(session2, "/foo");
+
+ session1.save();
+
+ session2.refresh(false);
+ Item foo2 = session2.getItem("/foo");
+ foo2.remove();
+ session2.save();
+
+ session1.refresh(false);
+ checkNoNodeAt(session1, "/foo");
+
+ }
+
+ private void checkNoNodeAt( Session session,
+ String path ) throws Exception {
+ try {
+ session.getItem(path);
+ fail("No node should exist at path: " + path);
+ } catch (PathNotFoundException expected) {
+
+ }
+ }
+}
Index: modeshape-integration-tests/src/test/resources/infinispan/configRepository.xml
new file mode 100644
===================================================================
--- /dev/null (revision 1847)
+++ modeshape-integration-tests/src/test/resources/infinispan/configRepository.xml (working copy)
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+ default
+ otherWorkspace
+
+
+
+
+
+ Standard extension-based MIME type detector
+
+ org.modeshape.graph.mimetype.ExtensionBasedMimeTypeDetector
+
+
+
+
+
+
+
+
+
+ Store
+
+
+
+
+
+
+
+
+
+
+
+
Index: modeshape-integration-tests/src/test/resources/infinispan/infinispan_clustered_config.xml
new file mode 100644
===================================================================
--- /dev/null (revision 1847)
+++ modeshape-integration-tests/src/test/resources/infinispan/infinispan_clustered_config.xml (working copy)
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
Index: pom.xml
===================================================================
--- pom.xml (revision 1847)
+++ pom.xml (working copy)
@@ -822,6 +822,10 @@
java.io.tmpdir
${basedir}/target
+
+ java.net.preferIPv4Stack
+ true
+