diff --git a/src/main/java/com/avanza/gs/test/MirrorPu.java b/src/main/java/com/avanza/gs/test/MirrorPu.java
index aa8f7b6..9c22115 100644
--- a/src/main/java/com/avanza/gs/test/MirrorPu.java
+++ b/src/main/java/com/avanza/gs/test/MirrorPu.java
@@ -20,6 +20,7 @@
import org.openspaces.pu.container.integrated.IntegratedProcessingUnitContainer;
import org.openspaces.pu.container.integrated.IntegratedProcessingUnitContainerProvider;
import org.springframework.context.ApplicationContext;
+import org.springframework.core.io.Resource;
import java.io.IOException;
import java.util.Properties;
@@ -29,6 +30,7 @@ public class MirrorPu implements PuRunner {
private IntegratedProcessingUnitContainer container;
private final String gigaSpaceBeanName = "gigaSpace";
private final String puXmlPath;
+ private final Resource puConfigResource;
private Properties contextProperties = new Properties();
private final String lookupGroupName;
private final boolean autostart;
@@ -36,6 +38,7 @@ public class MirrorPu implements PuRunner {
public MirrorPu(MirrorPuConfigurer config) {
this.puXmlPath = config.puXmlPath;
+ this.puConfigResource = config.puConfigResource;
this.contextProperties = config.properties;
this.lookupGroupName = config.lookupGroupName;
this.autostart = true;
@@ -56,7 +59,12 @@ public void run() throws IOException {
private void startContainers() throws IOException {
IntegratedProcessingUnitContainerProvider provider = new IntegratedProcessingUnitContainerProvider();
provider.setBeanLevelProperties(createBeanLevelProperties());
- provider.addConfigLocation(puXmlPath);
+ if (puXmlPath != null) {
+ provider.addConfigLocation(puXmlPath);
+ }
+ if (puConfigResource != null) {
+ provider.addConfigLocation(puConfigResource);
+ }
if (parentContext != null) {
provider.setParentContext(parentContext);
}
diff --git a/src/main/java/com/avanza/gs/test/MirrorPuConfigurer.java b/src/main/java/com/avanza/gs/test/MirrorPuConfigurer.java
index 577ce43..d8a433a 100644
--- a/src/main/java/com/avanza/gs/test/MirrorPuConfigurer.java
+++ b/src/main/java/com/avanza/gs/test/MirrorPuConfigurer.java
@@ -18,16 +18,24 @@
import java.util.Properties;
import org.springframework.context.ApplicationContext;
+import org.springframework.core.io.Resource;
public class MirrorPuConfigurer {
final String puXmlPath;
+ final Resource puConfigResource;
Properties properties = new Properties();
ApplicationContext parentContext;
String lookupGroupName = JVMGlobalLus.getLookupGroupName();
public MirrorPuConfigurer(String puXmlPath) {
this.puXmlPath = puXmlPath;
+ this.puConfigResource = null;
+ }
+
+ public MirrorPuConfigurer(Resource puConfigResource) {
+ this.puXmlPath = null;
+ this.puConfigResource = puConfigResource;
}
/**
diff --git a/src/main/java/com/avanza/gs/test/PartitionedPu.java b/src/main/java/com/avanza/gs/test/PartitionedPu.java
index 20d0b86..7d068ed 100644
--- a/src/main/java/com/avanza/gs/test/PartitionedPu.java
+++ b/src/main/java/com/avanza/gs/test/PartitionedPu.java
@@ -27,6 +27,8 @@
import org.openspaces.pu.container.integrated.IntegratedProcessingUnitContainerProvider;
import org.openspaces.pu.container.support.CompoundProcessingUnitContainer;
import org.springframework.context.ApplicationContext;
+import org.springframework.core.io.Resource;
+
import com.gigaspaces.security.directory.DefaultCredentialsProvider;
/**
@@ -39,6 +41,7 @@ public final class PartitionedPu implements PuRunner {
private CompoundProcessingUnitContainer container;
private final String gigaSpaceBeanName = "gigaSpace";
private final String puXmlPath;
+ private final Resource puConfigResource;
private final Integer numberOfPrimaries;
private final Integer numberOfBackups;
private final Properties contextProperties = new Properties();
@@ -50,6 +53,7 @@ public final class PartitionedPu implements PuRunner {
public PartitionedPu(PartitionedPuConfigurer configurer) {
this.puXmlPath = configurer.puXmlPath;
+ this.puConfigResource = configurer.puConfigResource;
this.numberOfBackups = configurer.numberOfBackups;
this.numberOfPrimaries = configurer.numberOfPrimaries;
this.contextProperties.putAll(configurer.contextProperties);
@@ -76,7 +80,12 @@ private void startContainers() throws IOException {
IntegratedProcessingUnitContainerProvider provider = new IntegratedProcessingUnitContainerProvider();
provider.setBeanLevelProperties(createBeanLevelProperties());
provider.setClusterInfo(createClusterInfo());
- provider.addConfigLocation(puXmlPath);
+ if (puXmlPath != null) {
+ provider.addConfigLocation(puXmlPath);
+ }
+ if (puConfigResource != null) {
+ provider.addConfigLocation(puConfigResource);
+ }
if (parentContext != null) {
provider.setParentContext(parentContext);
}
diff --git a/src/main/java/com/avanza/gs/test/PartitionedPuConfigurer.java b/src/main/java/com/avanza/gs/test/PartitionedPuConfigurer.java
index 5e455b7..9ca7a82 100644
--- a/src/main/java/com/avanza/gs/test/PartitionedPuConfigurer.java
+++ b/src/main/java/com/avanza/gs/test/PartitionedPuConfigurer.java
@@ -20,10 +20,12 @@
import java.util.Properties;
import org.springframework.context.ApplicationContext;
+import org.springframework.core.io.Resource;
public final class PartitionedPuConfigurer {
- String puXmlPath;
+ final String puXmlPath;
+ final Resource puConfigResource;
int numberOfPrimaries = 1;
int numberOfBackups = 0;
boolean startAsync = false;
@@ -37,6 +39,12 @@ public final class PartitionedPuConfigurer {
public PartitionedPuConfigurer(String puXmlPath) {
this.puXmlPath = puXmlPath;
+ this.puConfigResource = null;
+ }
+
+ public PartitionedPuConfigurer(Resource puConfigResource) {
+ this.puXmlPath = null;
+ this.puConfigResource = puConfigResource;
}
public PartitionedPuConfigurer parentContext(ApplicationContext parentContext) {
diff --git a/src/main/java/com/avanza/gs/test/PartitionedSpace.java b/src/main/java/com/avanza/gs/test/PartitionedSpace.java
new file mode 100644
index 0000000..7b4e987
--- /dev/null
+++ b/src/main/java/com/avanza/gs/test/PartitionedSpace.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2017 Avanza Bank AB
+ *
+ * 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 com.avanza.gs.test;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+import org.openspaces.core.GigaSpace;
+import org.openspaces.core.space.UrlSpaceConfigurer;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.util.StreamUtils;
+
+/**
+ * Utility class to create a partitioned space without backups.
+ *
+ * Individual primary instances can be retrieved by using the {@code partition(ID)} method. An url to the
+ * cluster can be retrieved through the {@code getUrl()} method.
+ *
+ * @deprecated This class is provided for backwards compatibility.
+ * For a simple test with no need for partitions, {@link EmbeddedSpace} can be used instead.
+ * For more complex tests, use {@link PuConfigurers} to set up a {@code partitionedPu}.
+ */
+@Deprecated
+public class PartitionedSpace implements TestRule {
+
+ private final GigaSpace[] instances;
+ private final RunningPu partitionedPu;
+ private final Path puXml;
+ private final String spaceName;
+ private final String groupName;
+ private final int numberOfPartitions;
+
+ private boolean started = false;
+
+ /**
+ * Creates a partitioned space with a given number of partitions.
+ *
+ * @param numberOfPartitions (must be at least 2).
+ * @throws IllegalArgumentException if numberOfPartitions is less than 2.
+ */
+ public PartitionedSpace(int numberOfPartitions) {
+ this(numberOfPartitions, "testSpace");
+ }
+
+ /**
+ * Creates a partitioned space with a given number of partitions.
+ *
+ * @param numberOfPartitions (must be at least 2).
+ * @param spaceName name of the space to create.
+ *
+ * @throws IllegalArgumentException if numberOfPartitions is less than 2.
+ * @throws IllegalArgumentException if spaceName is empty string.
+ * @throws NullPointerException if spaceName is null.
+ */
+ public PartitionedSpace(int numberOfPartitions, String spaceName) {
+ this(numberOfPartitions, spaceName, JVMGlobalLus.getLookupGroupName());
+ }
+
+ public PartitionedSpace(int numberOfPartitions, String spaceName, String lookupGroupName) {
+ if (numberOfPartitions < 2) {
+ throw new IllegalArgumentException("Number of partitions must be at least 2, was " + numberOfPartitions);
+ }
+ if (spaceName.isEmpty()) {
+ throw new IllegalArgumentException("Space name must not be empty");
+ }
+ this.numberOfPartitions = numberOfPartitions;
+ this.groupName = lookupGroupName;
+ this.spaceName = spaceName;
+ this.instances = new GigaSpace[numberOfPartitions];
+ this.puXml = writePuXml(spaceName);
+ this.partitionedPu = PuConfigurers.partitionedPu(new FileSystemResource(puXml.toFile()))
+ .lookupGroup(lookupGroupName)
+ .spaceName(spaceName)
+ .numberOfPrimaries(numberOfPartitions)
+ .numberOfBackups(0)
+ .configure();
+
+ start();
+ }
+
+ private static Path writePuXml(String spaceName) {
+ try (InputStream in = PartitionedSpace.class.getResourceAsStream("/partitioned_space/simple-pu.xml.template")) {
+ Path tempFile = Files.createTempFile("partitioned-space-", ".xml");
+ String content = StreamUtils.copyToString(in, UTF_8)
+ .replace("##SPACE_NAME##", spaceName);
+ return Files.write(tempFile, content.getBytes(UTF_8));
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ public void start() {
+ if (started) {
+ return;
+ }
+ try {
+ partitionedPu.start();
+ } catch (Exception e) {
+ throw new RuntimeException("Could not start partitionedPu", e);
+ }
+ for (int instanceId = 1; instanceId <= numberOfPartitions; instanceId ++) {
+ instances[instanceId - 1] = partitionedPu.getPrimaryInstanceApplicationContext(instanceId - 1)
+ .getBean(GigaSpace.class);
+ }
+ started = true;
+ }
+
+ @Deprecated
+ protected void configure(UrlSpaceConfigurer spaceConfigurer) {
+ throw new UnsupportedOperationException("This method is not supported");
+ }
+
+ /**
+ * Returns the primary with the given instanceId.
+ *
+ * NOTE: Primaries are numbered from 1!
+ */
+ public GigaSpace primary(int instanceId) {
+ if (instanceId < 1) {
+ throw new IllegalArgumentException("Primaries are numbered from 1! Requested instance was " + instanceId);
+ }
+ if (instanceId > instances.length) {
+ throw new IllegalArgumentException("No such primary: " + instanceId + ". Space contains " + this.instances.length + " partitions.");
+ }
+ return instances[instanceId - 1];
+ }
+
+ private static final Object ALL_OBJECTS = null; /* Intentional NULL */
+
+ /**
+ * Cleans all space's in this cluster.
+ */
+ public void clean() {
+ for (GigaSpace instance : instances) {
+ instance.clear(ALL_OBJECTS);
+ }
+ }
+
+ /**
+ * Returns an url to this space.
+ */
+ public String getUrl() {
+ return "jini://*/*/" + this.spaceName + "?groups=" + this.groupName;
+ }
+
+ /**
+ * Creates a clustered proxy against this space.
+ */
+ public GigaSpace createClusteredProxy() {
+ return partitionedPu.getClusteredGigaSpace();
+ }
+
+ /**
+ * Destroys this space.
+ */
+ public void destroy() {
+ try {
+ partitionedPu.stop();
+ Files.deleteIfExists(puXml);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Returns the name of this space.
+ */
+ public String getName() {
+ return this.spaceName;
+ }
+
+ /**
+ * Returns the lookup group name
+ */
+ public String getLookupGroupName() {
+ return this.groupName;
+ }
+
+ @Override
+ public Statement apply(final Statement base, Description description) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ try {
+ start();
+ base.evaluate();
+ } finally {
+ destroy();
+ }
+ }
+ };
+ }
+
+}
diff --git a/src/main/java/com/avanza/gs/test/PuConfigurers.java b/src/main/java/com/avanza/gs/test/PuConfigurers.java
index 500bdad..dc9d4b6 100644
--- a/src/main/java/com/avanza/gs/test/PuConfigurers.java
+++ b/src/main/java/com/avanza/gs/test/PuConfigurers.java
@@ -15,14 +15,24 @@
*/
package com.avanza.gs.test;
+import org.springframework.core.io.Resource;
+
public class PuConfigurers {
public static PartitionedPuConfigurer partitionedPu(String puXmlPath) {
return new PartitionedPuConfigurer(puXmlPath);
}
+ public static PartitionedPuConfigurer partitionedPu(Resource puConfigResource) {
+ return new PartitionedPuConfigurer(puConfigResource);
+ }
+
public static MirrorPuConfigurer mirrorPu(String puXmlPath) {
return new MirrorPuConfigurer(puXmlPath);
}
+ public static MirrorPuConfigurer mirrorPu(Resource puConfigResource) {
+ return new MirrorPuConfigurer(puConfigResource);
+ }
+
}
diff --git a/src/main/resources/partitioned_space/simple-pu.xml.template b/src/main/resources/partitioned_space/simple-pu.xml.template
new file mode 100644
index 0000000..a5190c3
--- /dev/null
+++ b/src/main/resources/partitioned_space/simple-pu.xml.template
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+ 1
+ read-only
+ true
+ true
+
+
+
+
+
diff --git a/src/test/java/com/avanza/gs/test/PartitionedSpaceTest.java b/src/test/java/com/avanza/gs/test/PartitionedSpaceTest.java
new file mode 100644
index 0000000..e71cb27
--- /dev/null
+++ b/src/test/java/com/avanza/gs/test/PartitionedSpaceTest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2017 Avanza Bank AB
+ *
+ * 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 com.avanza.gs.test;
+
+import static org.hamcrest.CoreMatchers.startsWith;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.openspaces.core.GigaSpace;
+import org.openspaces.core.space.UrlSpaceFactoryBean;
+
+import com.gigaspaces.annotation.pojo.SpaceId;
+import com.gigaspaces.annotation.pojo.SpaceRouting;
+import com.j_spaces.core.IJSpace;
+
+@Ignore("requires a valid gs license to be set with -Dcom.gs.licensekey to run")
+public class PartitionedSpaceTest {
+
+ private static final String SPACE_NAME = "the_space_name";
+
+ @ClassRule
+ public static final PartitionedSpace space = new PartitionedSpace(2, SPACE_NAME);
+
+ private GigaSpace clustered;
+ private GigaSpace partition1;
+ private GigaSpace partition2;
+
+ @Before
+ public void setup() {
+ clustered = space.createClusteredProxy();
+ partition1 = space.primary(1);
+ partition2 = space.primary(2);
+ }
+
+ @After
+ public void cleanSpace() {
+ space.clean();
+ }
+
+ @AfterClass
+ public static void destroySpace() {
+ space.destroy();
+ }
+
+ @Test
+ public void writeToPartition1() {
+ assertEquals("expected space to be empty before insert", 0, clustered.count(null));
+
+ clustered.write(createMessage("foo", 0));
+ assertEquals(1, partition1.count(null));
+ assertEquals(0, partition2.count(null));
+ assertEquals(1, clustered.count(null));
+ }
+
+ @Test
+ public void writeToPartition2() {
+ assertEquals("expected space to be empty before insert", 0, clustered.count(null));
+
+ clustered.write(createMessage("foo", 1));
+ assertEquals(0, partition1.count(null));
+ assertEquals(1, partition2.count(null));
+ assertEquals(1, clustered.count(null));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void throwsIllegalArgumentExceptionWhenAskingForNonexistingPrimary1() {
+ space.primary(0);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void throwsIllegalArgumentExceptionWhenAskingForNonexistingPrimary2() {
+ space.primary(3);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void throwsIllegalArgumentExceptionIfNumberOfPartitionsIsLessThan2() {
+ new PartitionedSpace(1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void throwsIllegalArgumentExceptionIfSpaceNameIsEmpty() {
+ new PartitionedSpace(1, "");
+ }
+
+ @Test
+ public void cleansSpace() {
+ clustered.write(createMessage("foo", 0));
+ clustered.write(createMessage("foo", 1));
+ assertEquals(2, clustered.count(null));
+
+ space.clean();
+ assertEquals(0, clustered.count(null));
+ }
+
+ @Test
+ public void spaceName() {
+ assertEquals("gigaSpace", partition1.getName());
+ assertEquals(SPACE_NAME, space.getName());
+ }
+
+ @Test
+ public void lookupGroupname() {
+ assertNotNull(space.getLookupGroupName());
+ }
+
+ @Test
+ public void connectToSpaceWithUrl() {
+ UrlSpaceFactoryBean urlSpaceFactoryBean = new UrlSpaceFactoryBean(space.getUrl());
+ urlSpaceFactoryBean.afterPropertiesSet();
+ IJSpace obj = (IJSpace) urlSpaceFactoryBean.getObject();
+ assertThat(obj.getContainerName(), startsWith(SPACE_NAME));
+ }
+
+ private Message createMessage(String message, int routingId) {
+ Message m = new Message();
+ m.setMessage(message);
+ m.setRoutingId(routingId);
+ return m;
+ }
+
+ public static class Message {
+
+ private String message;
+ private int routingId;
+ private String id;
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ @SpaceRouting
+ public int getRoutingId() {
+ return routingId;
+ }
+
+ public void setRoutingId(int routingId) {
+ this.routingId = routingId;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ @SpaceId(autoGenerate = true)
+ public String getId() {
+ return id;
+ }
+
+ }
+
+}