diff --git a/offheap-resource/pom.xml b/offheap-resource/pom.xml
new file mode 100644
index 0000000000..9011ddad39
--- /dev/null
+++ b/offheap-resource/pom.xml
@@ -0,0 +1,100 @@
+
+
+ 4.0.0
+
+
+ org.terracotta
+ platform-root
+ 1.0-SNAPSHOT
+ ../pom.xml
+
+
+ offheap-resource
+
+
+
+
+ org.slf4j
+ slf4j-api
+ 1.7.18
+
+
+ org.terracotta.internal
+ tc-config-parser
+ 10.0-SNAPSHOT
+
+
+ junit
+ junit
+ test
+
+
+ org.hamcrest
+ hamcrest-all
+ 1.3
+
+
+ org.mockito
+ mockito-all
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-toolchains-plugin
+ 1.1
+
+
+
+ toolchain
+
+
+
+
+
+
+ 1.8
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 2.10.1
+
+
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+ org.codehaus.mojo
+ jaxb2-maven-plugin
+ 2.2
+
+
+ xjc
+
+ xjc
+
+
+
+
+ org.terracotta.offheapresource.config
+
+
+
+
+
+
+
+
diff --git a/offheap-resource/src/main/java/org/terracotta/offheapresource/OffHeapResource.java b/offheap-resource/src/main/java/org/terracotta/offheapresource/OffHeapResource.java
new file mode 100644
index 0000000000..bafe4ac843
--- /dev/null
+++ b/offheap-resource/src/main/java/org/terracotta/offheapresource/OffHeapResource.java
@@ -0,0 +1,93 @@
+/*
+ * The contents of this file are subject to the Terracotta Public 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://terracotta.org/legal/terracotta-public-license.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
+ * the specific language governing rights and limitations under the License.
+ *
+ * The Covered Software is OffHeap Resource.
+ *
+ * The Initial Developer of the Covered Software is
+ * Terracotta, Inc., a Software AG company
+ */
+
+package org.terracotta.offheapresource;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Represents an offheap resource, providing a reservation system that can be
+ * used to control the combined memory usage of participating consumers.
+ *
+ * Reservation and release calls perform no allocations, and therefore rely on
+ * the cooperation of callers to achieve control over the 'real' resource usage.
+ */
+public class OffHeapResource {
+
+ private final AtomicLong remaining;
+
+ /**
+ * Creates a resource of the given initial size.
+ *
+ * @param size size of the resource
+ * @throws IllegalArgumentException if the size is negative
+ */
+ OffHeapResource(long size) throws IllegalArgumentException {
+ if (size < 0) {
+ throw new IllegalArgumentException("Resource size cannot be negative");
+ } else {
+ this.remaining = new AtomicLong(size);
+ }
+ }
+
+ /**
+ * Reserves the given amount of this resource.
+ *
+ * This method performs no allocation. It is simply a reservation
+ * that the consumer agrees to bind by the result of. A {@code false} return
+ * should mean the caller refrains from performing any associated allocation.
+ *
+ * @param size reservation size
+ * @return {@code true} if the reservation succeeded
+ * @throws IllegalArgumentException if the reservation size is negative
+ */
+ public boolean reserve(long size) throws IllegalArgumentException {
+ if (size < 0) {
+ throw new IllegalArgumentException("Reservation size cannot be negative");
+ } else {
+ for (long current = remaining.get(); current >= size; current = remaining.get()) {
+ if (remaining.compareAndSet(current, current - size)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Releases the given amount of resource back to this pool.
+ *
+ * @param size release size
+ * @throws IllegalArgumentException if the release size is negative
+ */
+ public void release(long size) throws IllegalArgumentException {
+ if (size < 0) {
+ throw new IllegalArgumentException("Released size cannot be negative");
+ } else {
+ remaining.addAndGet(size);
+ }
+ }
+
+ /**
+ * Returns the size of the remaining resource that can be reserved.
+ *
+ * @return the remaining resource size
+ */
+ public long available() {
+ return remaining.get();
+ }
+}
diff --git a/offheap-resource/src/main/java/org/terracotta/offheapresource/OffHeapResourceConfigurationParser.java b/offheap-resource/src/main/java/org/terracotta/offheapresource/OffHeapResourceConfigurationParser.java
new file mode 100644
index 0000000000..c33a2292c8
--- /dev/null
+++ b/offheap-resource/src/main/java/org/terracotta/offheapresource/OffHeapResourceConfigurationParser.java
@@ -0,0 +1,70 @@
+/*
+ * The contents of this file are subject to the Terracotta Public 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://terracotta.org/legal/terracotta-public-license.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
+ * the specific language governing rights and limitations under the License.
+ *
+ * The Covered Software is OffHeap Resource.
+ *
+ * The Initial Developer of the Covered Software is
+ * Terracotta, Inc., a Software AG company
+ */
+
+package org.terracotta.offheapresource;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URL;
+import javax.xml.XMLConstants;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.SchemaFactory;
+import org.terracotta.config.service.ServiceConfigParser;
+import org.terracotta.entity.ServiceProviderConfiguration;
+import org.w3c.dom.Element;
+
+import org.terracotta.offheapresource.config.OffheapResourcesType;
+import org.xml.sax.SAXException;
+
+public class OffHeapResourceConfigurationParser implements ServiceConfigParser {
+
+ private static final URL XML_SCHEMA = OffHeapResourceConfigurationParser.class.getResource("/offheap-resource.xsd");
+ private static final URI NAMESPACE = URI.create("http://www.terracotta.org/config/offheap-resource");
+
+ @Override
+ public Source getXmlSchema() throws IOException {
+ return new StreamSource(XML_SCHEMA.openStream());
+ }
+
+ @Override
+ public URI getNamespace() {
+ return NAMESPACE;
+ }
+
+ @Override
+ public ServiceProviderConfiguration parse(Element elmnt, String string) {
+ try {
+ JAXBContext jaxbContext = JAXBContext.newInstance(OffheapResourcesType.class.getPackage().getName(), OffHeapResourceConfigurationParser.class.getClassLoader());
+ Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
+ unmarshaller.setSchema(SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(getXmlSchema()));
+ JAXBElement parsed = (JAXBElement) unmarshaller.unmarshal(elmnt);
+ return new OffHeapResourcesConfiguration(parsed.getValue());
+ } catch (JAXBException e) {
+ throw new IllegalArgumentException(e);
+ } catch (SAXException e) {
+ throw new AssertionError(e);
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+}
diff --git a/offheap-resource/src/main/java/org/terracotta/offheapresource/OffHeapResourceIdentifier.java b/offheap-resource/src/main/java/org/terracotta/offheapresource/OffHeapResourceIdentifier.java
new file mode 100644
index 0000000000..64ca428e7e
--- /dev/null
+++ b/offheap-resource/src/main/java/org/terracotta/offheapresource/OffHeapResourceIdentifier.java
@@ -0,0 +1,56 @@
+/*
+ * The contents of this file are subject to the Terracotta Public 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://terracotta.org/legal/terracotta-public-license.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
+ * the specific language governing rights and limitations under the License.
+ *
+ * The Covered Software is OffHeap Resource.
+ *
+ * The Initial Developer of the Covered Software is
+ * Terracotta, Inc., a Software AG company
+ */
+
+package org.terracotta.offheapresource;
+
+import org.terracotta.entity.ServiceConfiguration;
+
+/**
+ *
+ * @author cdennis
+ */
+public final class OffHeapResourceIdentifier implements ServiceConfiguration {
+
+ private final String name;
+
+ public static OffHeapResourceIdentifier identifier(String name) {
+ return new OffHeapResourceIdentifier(name);
+ }
+
+ private OffHeapResourceIdentifier(String name) {
+ if (name == null) {
+ throw new NullPointerException("Name cannot be null");
+ } else {
+ this.name = name;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return name.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return (obj instanceof OffHeapResourceIdentifier) && name.equals(((OffHeapResourceIdentifier) obj).name);
+ }
+
+ @Override
+ public Class getServiceType() {
+ return OffHeapResource.class;
+ }
+}
diff --git a/offheap-resource/src/main/java/org/terracotta/offheapresource/OffHeapResourcesConfiguration.java b/offheap-resource/src/main/java/org/terracotta/offheapresource/OffHeapResourcesConfiguration.java
new file mode 100644
index 0000000000..dd30573ab6
--- /dev/null
+++ b/offheap-resource/src/main/java/org/terracotta/offheapresource/OffHeapResourcesConfiguration.java
@@ -0,0 +1,43 @@
+/*
+ * The contents of this file are subject to the Terracotta Public 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://terracotta.org/legal/terracotta-public-license.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
+ * the specific language governing rights and limitations under the License.
+ *
+ * The Covered Software is OffHeap Resource.
+ *
+ * The Initial Developer of the Covered Software is
+ * Terracotta, Inc., a Software AG company
+ */
+
+package org.terracotta.offheapresource;
+
+import java.util.Collection;
+import org.terracotta.entity.ServiceProvider;
+import org.terracotta.entity.ServiceProviderConfiguration;
+import org.terracotta.offheapresource.config.OffheapResourcesType;
+import org.terracotta.offheapresource.config.ResourceType;
+
+class OffHeapResourcesConfiguration implements ServiceProviderConfiguration {
+
+ private final OffheapResourcesType xmlConfig;
+
+ OffHeapResourcesConfiguration(OffheapResourcesType xmlConfig) {
+ this.xmlConfig = xmlConfig;
+ }
+
+ public Collection getResources() {
+ return xmlConfig.getResource();
+ }
+
+ @Override
+ public Class extends ServiceProvider> getServiceProviderType() {
+ return OffHeapResourcesProvider.class;
+ }
+
+}
diff --git a/offheap-resource/src/main/java/org/terracotta/offheapresource/OffHeapResourcesProvider.java b/offheap-resource/src/main/java/org/terracotta/offheapresource/OffHeapResourcesProvider.java
new file mode 100644
index 0000000000..f4273132f2
--- /dev/null
+++ b/offheap-resource/src/main/java/org/terracotta/offheapresource/OffHeapResourcesProvider.java
@@ -0,0 +1,109 @@
+/*
+ * The contents of this file are subject to the Terracotta Public 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://terracotta.org/legal/terracotta-public-license.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
+ * the specific language governing rights and limitations under the License.
+ *
+ * The Covered Software is OffHeap Resource.
+ *
+ * The Initial Developer of the Covered Software is
+ * Terracotta, Inc., a Software AG company
+ */
+
+package org.terracotta.offheapresource;
+
+import java.math.BigInteger;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.terracotta.entity.ServiceConfiguration;
+import org.terracotta.entity.ServiceProvider;
+import org.terracotta.entity.ServiceProviderConfiguration;
+import org.terracotta.offheapresource.config.MemoryUnit;
+import org.terracotta.offheapresource.config.ResourceType;
+
+/**
+ * A provider of {@link OffHeapResource} instances.
+ *
+ * This service allows for the configuration of a multitude of virtual offheap
+ * resource pools from which participating entities can reserve space. This
+ * allows for the partitioning and control of memory usage by entities
+ * consuming this service.
+ */
+public class OffHeapResourcesProvider implements ServiceProvider {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(OffHeapResourcesProvider.class);
+
+ private final Map resources = new HashMap();
+
+ @Override
+ public synchronized boolean initialize(ServiceProviderConfiguration unknownConfig) {
+ if (unknownConfig instanceof OffHeapResourcesConfiguration) {
+ OffHeapResourcesConfiguration configuration = (OffHeapResourcesConfiguration) unknownConfig;
+ if (resources.isEmpty()) {
+ long totalSize = 0;
+ for (ResourceType r : configuration.getResources()) {
+ long size = convert(r.getValue(), r.getUnit()).longValueExact();
+ totalSize += size;
+ resources.put(OffHeapResourceIdentifier.identifier(r.getName()), new OffHeapResource(size));
+ }
+ Long physicalMemory = PhysicalMemory.totalPhysicalMemory();
+ if (physicalMemory != null && totalSize > physicalMemory) {
+ LOGGER.warn("More offheap configured than there is physical memory [{} > {}]", totalSize, physicalMemory);
+ }
+ return true;
+ } else {
+ throw new IllegalStateException("Resources already initialized");
+ }
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public T getService(long consumerID, ServiceConfiguration unknownConfiguration) {
+ if (unknownConfiguration instanceof OffHeapResourceIdentifier) {
+ OffHeapResourceIdentifier identifier = (OffHeapResourceIdentifier) unknownConfiguration;
+ return (T) identifier.getServiceType().cast(resources.get(identifier));
+ } else {
+ throw new IllegalArgumentException("Unexpected configuration type " + unknownConfiguration.getClass());
+ }
+ }
+
+ @Override
+ public Collection> getProvidedServiceTypes() {
+ return Collections.>singleton(OffHeapResource.class);
+ }
+
+ @Override
+ public void close() {
+ clear();
+ }
+
+ @Override
+ public void clear() {
+ resources.clear();
+ }
+
+ private static BigInteger convert(BigInteger value, MemoryUnit unit) {
+ switch (unit) {
+ case B: return value.shiftLeft(0);
+ case K_B: return value.shiftLeft(10);
+ case MB: return value.shiftLeft(20);
+ case GB: return value.shiftLeft(30);
+ case TB: return value.shiftLeft(40);
+ case PB: return value.shiftLeft(50);
+ }
+ throw new IllegalArgumentException("Unknown unit " + unit);
+ }
+}
diff --git a/offheap-resource/src/main/java/org/terracotta/offheapresource/PhysicalMemory.java b/offheap-resource/src/main/java/org/terracotta/offheapresource/PhysicalMemory.java
new file mode 100644
index 0000000000..3ca8a9a06d
--- /dev/null
+++ b/offheap-resource/src/main/java/org/terracotta/offheapresource/PhysicalMemory.java
@@ -0,0 +1,82 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.terracotta.offheapresource;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.OperatingSystemMXBean;
+import java.lang.reflect.InvocationTargetException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ * @author cdennis
+ */
+class PhysicalMemory {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(PhysicalMemory.class);
+ private static final OperatingSystemMXBean OS_BEAN = ManagementFactory.getOperatingSystemMXBean();
+
+ public static Long totalPhysicalMemory() {
+ return getAttribute("getTotalPhysicalMemorySize");
+ }
+
+ public static Long freePhysicalMemory() {
+ return getAttribute("getFreePhysicalMemorySize");
+ }
+
+ public static Long totalSwapSpace() {
+ return getAttribute("getTotalSwapSpaceSize");
+ }
+
+ public static Long freeSwapSpace() {
+ return getAttribute("getFreeSwapSpaceSize");
+ }
+
+ public static Long ourCommittedVirtualMemory() {
+ return getAttribute("getCommittedVirtualMemorySize");
+ }
+
+ private static T getAttribute(String name) {
+ LOGGER.trace("Bean lookup for {}", name);
+ for (Class> s = OS_BEAN.getClass(); s != null; s = s.getSuperclass()) {
+ try {
+ T result = (T) s.getMethod(name).invoke(OS_BEAN);
+ LOGGER.trace("Bean lookup successful using {}, got {}", s, result);
+ return result;
+ } catch (SecurityException e) {
+ LOGGER.trace("Bean lookup failed on {}", s, e);
+ } catch (NoSuchMethodException e) {
+ LOGGER.trace("Bean lookup failed on {}", s, e);
+ } catch (IllegalAccessException e) {
+ LOGGER.trace("Bean lookup failed on {}", s, e);
+ } catch (IllegalArgumentException e) {
+ LOGGER.trace("Bean lookup failed on {}", s, e);
+ } catch (InvocationTargetException e) {
+ LOGGER.trace("Bean lookup failed on {}", s, e);
+ }
+ }
+ for (Class> i : OS_BEAN.getClass().getInterfaces()) {
+ try {
+ T result = (T) i.getMethod(name).invoke(OS_BEAN);
+ LOGGER.trace("Bean lookup successful using {}, got {}", i, result);
+ return result;
+ } catch (SecurityException e) {
+ LOGGER.trace("Bean lookup failed on {}", i, e);
+ } catch (NoSuchMethodException e) {
+ LOGGER.trace("Bean lookup failed on {}", i, e);
+ } catch (IllegalAccessException e) {
+ LOGGER.trace("Bean lookup failed on {}", i, e);
+ } catch (IllegalArgumentException e) {
+ LOGGER.trace("Bean lookup failed on {}", i, e);
+ } catch (InvocationTargetException e) {
+ LOGGER.trace("Bean lookup failed on {}", i, e);
+ }
+ }
+ LOGGER.trace("Returning null for {}", name);
+ return null;
+ }
+}
diff --git a/offheap-resource/src/main/resources/META-INF/services/org.terracotta.config.service.ServiceConfigParser b/offheap-resource/src/main/resources/META-INF/services/org.terracotta.config.service.ServiceConfigParser
new file mode 100644
index 0000000000..04744c0181
--- /dev/null
+++ b/offheap-resource/src/main/resources/META-INF/services/org.terracotta.config.service.ServiceConfigParser
@@ -0,0 +1 @@
+org.terracotta.offheapresource.OffHeapResourceConfigurationParser
diff --git a/offheap-resource/src/main/resources/offheap-resource.xsd b/offheap-resource/src/main/resources/offheap-resource.xsd
new file mode 100644
index 0000000000..1467323ce9
--- /dev/null
+++ b/offheap-resource/src/main/resources/offheap-resource.xsd
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+ An enumeration of the resources that will be made available by this service.
+
+
+
+
+
+
+
+
+
+ Definition of an offheap resource that can be consumed by Voltron entities.
+
+
+
+
+
+
+
+
+
+
+
+
+ Name by which the pool can be referenced from an entity.
+
+
+
+
+
+
+ The unit the resource size is expressed in.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/offheap-resource/src/test/java/org/terracotta/offheapresource/OffHeapResourceConfigurationParserTest.java b/offheap-resource/src/test/java/org/terracotta/offheapresource/OffHeapResourceConfigurationParserTest.java
new file mode 100644
index 0000000000..d51b80d277
--- /dev/null
+++ b/offheap-resource/src/test/java/org/terracotta/offheapresource/OffHeapResourceConfigurationParserTest.java
@@ -0,0 +1,84 @@
+/*
+ * The contents of this file are subject to the Terracotta Public 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://terracotta.org/legal/terracotta-public-license.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
+ * the specific language governing rights and limitations under the License.
+ *
+ * The Covered Software is OffHeap Resource.
+ *
+ * The Initial Developer of the Covered Software is
+ * Terracotta, Inc., a Software AG company
+ */
+
+package org.terracotta.offheapresource;
+
+import java.math.BigInteger;
+import javax.xml.XMLConstants;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.validation.SchemaFactory;
+import org.hamcrest.Matcher;
+import org.hamcrest.core.IsCollectionContaining;
+import org.junit.Test;
+import org.terracotta.offheapresource.config.MemoryUnit;
+import org.terracotta.offheapresource.config.ResourceType;
+import org.w3c.dom.Document;
+
+import static org.hamcrest.beans.HasPropertyWithValue.hasProperty;
+import static org.hamcrest.core.AllOf.allOf;
+import static org.hamcrest.core.Is.is;
+import org.junit.Assert;
+import static org.junit.Assert.assertThat;
+
+/**
+ *
+ * @author cdennis
+ */
+public class OffHeapResourceConfigurationParserTest {
+
+ @Test
+ public void testValidParse() throws Exception {
+ OffHeapResourceConfigurationParser parser = new OffHeapResourceConfigurationParser();
+
+ DocumentBuilderFactory domBuilderFactory = DocumentBuilderFactory.newInstance();
+ domBuilderFactory.setNamespaceAware(true);
+ domBuilderFactory.setSchema(SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(parser.getXmlSchema()));
+
+ Document dom = domBuilderFactory.newDocumentBuilder().parse(getClass().getResourceAsStream("/configs/valid.xml"));
+
+ OffHeapResourcesConfiguration config = (OffHeapResourcesConfiguration) parser.parse(dom.getDocumentElement(), "what is this thing?");
+
+ assertThat(config.getResources(), IsCollectionContaining.hasItems(
+ resource("primary", 128, MemoryUnit.GB),
+ resource("secondary", 1024, MemoryUnit.MB)));
+ }
+
+ @Test
+ public void testNoResources() throws Exception {
+ OffHeapResourceConfigurationParser parser = new OffHeapResourceConfigurationParser();
+
+ DocumentBuilderFactory domBuilderFactory = DocumentBuilderFactory.newInstance();
+ domBuilderFactory.setNamespaceAware(true);
+ domBuilderFactory.setValidating(true);
+ domBuilderFactory.setSchema(SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(parser.getXmlSchema()));
+
+ Document dom = domBuilderFactory.newDocumentBuilder().parse(getClass().getResourceAsStream("/configs/no-resources.xml"));
+
+ try {
+ parser.parse(dom.getDocumentElement(), "what is this thing?");
+ Assert.fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ //expected
+ }
+ }
+
+
+ private static Matcher resource(String name, long size, MemoryUnit unit) {
+ return allOf(hasProperty("name", is(name)), hasProperty("unit", is(unit)),
+ hasProperty("value", is(BigInteger.valueOf(size))));
+ }
+}
diff --git a/offheap-resource/src/test/java/org/terracotta/offheapresource/OffHeapResourceIdentifierTest.java b/offheap-resource/src/test/java/org/terracotta/offheapresource/OffHeapResourceIdentifierTest.java
new file mode 100644
index 0000000000..662b28b41f
--- /dev/null
+++ b/offheap-resource/src/test/java/org/terracotta/offheapresource/OffHeapResourceIdentifierTest.java
@@ -0,0 +1,53 @@
+/*
+ * The contents of this file are subject to the Terracotta Public 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://terracotta.org/legal/terracotta-public-license.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
+ * the specific language governing rights and limitations under the License.
+ *
+ * The Covered Software is OffHeap Resource.
+ *
+ * The Initial Developer of the Covered Software is
+ * Terracotta, Inc., a Software AG company
+ */
+package org.terracotta.offheapresource;
+
+import static org.hamcrest.core.Is.is;
+import org.junit.Test;
+
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+public class OffHeapResourceIdentifierTest {
+
+ @Test
+ public void testNullName() {
+ try {
+ OffHeapResourceIdentifier.identifier(null);
+ fail("Expected NullPointerException");
+ } catch (NullPointerException e) {
+ //expected
+ }
+ }
+
+ @Test
+ public void testServiceType() {
+ assertThat(OffHeapResourceIdentifier.identifier("foo").getServiceType(), equalTo(OffHeapResource.class));
+ }
+
+ @Test
+ public void testEquals() {
+ assertThat(OffHeapResourceIdentifier.identifier("foo").equals(OffHeapResourceIdentifier.identifier("foo")), is(true));
+ assertThat(OffHeapResourceIdentifier.identifier("foo").equals(OffHeapResourceIdentifier.identifier("bar")), is(false));
+ }
+
+ @Test
+ public void testHashcode() {
+ assertThat(OffHeapResourceIdentifier.identifier("foo").hashCode(), is(OffHeapResourceIdentifier.identifier("foo").hashCode()));
+ }
+}
diff --git a/offheap-resource/src/test/java/org/terracotta/offheapresource/OffHeapResourceTest.java b/offheap-resource/src/test/java/org/terracotta/offheapresource/OffHeapResourceTest.java
new file mode 100644
index 0000000000..325517b250
--- /dev/null
+++ b/offheap-resource/src/test/java/org/terracotta/offheapresource/OffHeapResourceTest.java
@@ -0,0 +1,91 @@
+/*
+ * The contents of this file are subject to the Terracotta Public 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://terracotta.org/legal/terracotta-public-license.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
+ * the specific language governing rights and limitations under the License.
+ *
+ * The Covered Software is OffHeap Resource.
+ *
+ * The Initial Developer of the Covered Software is
+ * Terracotta, Inc., a Software AG company
+ */
+
+package org.terracotta.offheapresource;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+
+public class OffHeapResourceTest {
+
+ @Test
+ public void testNegativeResourceSize() {
+ try {
+ new OffHeapResource(-1);
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ //expected;
+ }
+ }
+
+ @Test
+ public void testZeroSizeResourceIsUseless() {
+ OffHeapResource ohr = new OffHeapResource(0);
+ assertThat(ohr.reserve(1), is(false));
+ assertThat(ohr.available(), is(0L));
+ }
+
+ @Test
+ public void testAllocationReducesSize() {
+ OffHeapResource ohr = new OffHeapResource(20);
+ assertThat(ohr.available(), is(20L));
+ assertThat(ohr.reserve(10), is(true));
+ assertThat(ohr.available(), is(10L));
+ }
+
+ @Test
+ public void testNegativeAllocationFails() {
+ OffHeapResource ohr = new OffHeapResource(20);
+ try {
+ ohr.reserve(-1);
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ //expected
+ }
+ }
+
+ @Test
+ public void testAllocationWhenExhaustedFails() {
+ OffHeapResource ohr = new OffHeapResource(20);
+ ohr.reserve(20);
+ assertThat(ohr.reserve(1), is(false));
+ assertThat(ohr.available(), is(0L));
+ }
+
+ @Test
+ public void testFreeIncreasesSize() {
+ OffHeapResource ohr = new OffHeapResource(20);
+ ohr.reserve(20);
+ assertThat(ohr.available(), is(0L));
+ ohr.release(10);
+ assertThat(ohr.available(), is(10L));
+ }
+
+ @Test
+ public void testNegativeFreeFails() {
+ OffHeapResource ohr = new OffHeapResource(20);
+ ohr.reserve(10);
+ try {
+ ohr.release(-10);
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ //expected
+ }
+ }
+}
diff --git a/offheap-resource/src/test/java/org/terracotta/offheapresource/OffHeapResourcesProviderTest.java b/offheap-resource/src/test/java/org/terracotta/offheapresource/OffHeapResourcesProviderTest.java
new file mode 100644
index 0000000000..831ff6dc48
--- /dev/null
+++ b/offheap-resource/src/test/java/org/terracotta/offheapresource/OffHeapResourcesProviderTest.java
@@ -0,0 +1,120 @@
+/*
+ * The contents of this file are subject to the Terracotta Public 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://terracotta.org/legal/terracotta-public-license.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
+ * the specific language governing rights and limitations under the License.
+ *
+ * The Covered Software is OffHeap Resource.
+ *
+ * The Initial Developer of the Covered Software is
+ * Terracotta, Inc., a Software AG company
+ */
+
+package org.terracotta.offheapresource;
+
+import java.math.BigInteger;
+import java.util.Collections;
+import org.junit.Test;
+import org.terracotta.entity.ServiceProvider;
+import org.terracotta.entity.ServiceProviderConfiguration;
+
+import org.terracotta.offheapresource.config.MemoryUnit;
+import org.terracotta.offheapresource.config.ResourceType;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class OffHeapResourcesProviderTest {
+
+ @Test
+ public void testInitializeWithWrongConfig() {
+ OffHeapResourcesProvider provider = new OffHeapResourcesProvider();
+
+ assertThat(provider.initialize(new ServiceProviderConfiguration() {
+ @Override
+ public Class extends ServiceProvider> getServiceProviderType() {
+ return OffHeapResourcesProvider.class;
+ }
+ }), is(false));
+ }
+
+ @Test
+ public void testInitializeWithValidConfig() {
+ ResourceType resourceConfig = mock(ResourceType.class);
+ when(resourceConfig.getName()).thenReturn("foo");
+ when(resourceConfig.getUnit()).thenReturn(MemoryUnit.MB);
+ when(resourceConfig.getValue()).thenReturn(BigInteger.valueOf(2));
+
+ OffHeapResourcesConfiguration config = mock(OffHeapResourcesConfiguration.class);
+ when(config.getResources()).thenReturn(Collections.singleton(resourceConfig));
+
+ OffHeapResourcesProvider provider = new OffHeapResourcesProvider();
+ provider.initialize(config);
+
+ assertThat(provider.getService(42L, OffHeapResourceIdentifier.identifier("foo")), notNullValue());
+ assertThat(provider.getService(42L, OffHeapResourceIdentifier.identifier("foo")).available(), is(2L * 1024 * 1024));
+ }
+
+ @Test
+ public void testDoubleInitialize() {
+ ResourceType resourceConfig = mock(ResourceType.class);
+ when(resourceConfig.getName()).thenReturn("foo");
+ when(resourceConfig.getUnit()).thenReturn(MemoryUnit.MB);
+ when(resourceConfig.getValue()).thenReturn(BigInteger.valueOf(2));
+
+ OffHeapResourcesConfiguration config = mock(OffHeapResourcesConfiguration.class);
+ when(config.getResources()).thenReturn(Collections.singleton(resourceConfig));
+
+ OffHeapResourcesProvider provider = new OffHeapResourcesProvider();
+ provider.initialize(config);
+ try {
+ provider.initialize(config);
+ fail("Expected IllegalStateException");
+ } catch (IllegalStateException e) {
+ //expected
+ }
+ }
+
+ @Test
+ public void testNullReturnOnInvalidResource() {
+ ResourceType resourceConfig = mock(ResourceType.class);
+ when(resourceConfig.getName()).thenReturn("foo");
+ when(resourceConfig.getUnit()).thenReturn(MemoryUnit.MB);
+ when(resourceConfig.getValue()).thenReturn(BigInteger.valueOf(2));
+
+ OffHeapResourcesConfiguration config = mock(OffHeapResourcesConfiguration.class);
+ when(config.getResources()).thenReturn(Collections.singleton(resourceConfig));
+
+ OffHeapResourcesProvider provider = new OffHeapResourcesProvider();
+ provider.initialize(config);
+
+ assertThat(provider.getService(42L, OffHeapResourceIdentifier.identifier("bar")), nullValue());
+ }
+
+
+ @Test
+ public void testNullReturnAfterClose() {
+ ResourceType resourceConfig = mock(ResourceType.class);
+ when(resourceConfig.getName()).thenReturn("foo");
+ when(resourceConfig.getUnit()).thenReturn(MemoryUnit.MB);
+ when(resourceConfig.getValue()).thenReturn(BigInteger.valueOf(2));
+
+ OffHeapResourcesConfiguration config = mock(OffHeapResourcesConfiguration.class);
+ when(config.getResources()).thenReturn(Collections.singleton(resourceConfig));
+
+ OffHeapResourcesProvider provider = new OffHeapResourcesProvider();
+ provider.initialize(config);
+ provider.close();
+ assertThat(provider.getService(42L, OffHeapResourceIdentifier.identifier("foo")), nullValue());
+ }
+}
diff --git a/offheap-resource/src/test/resources/configs/no-resources.xml b/offheap-resource/src/test/resources/configs/no-resources.xml
new file mode 100644
index 0000000000..001a63d886
--- /dev/null
+++ b/offheap-resource/src/test/resources/configs/no-resources.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
diff --git a/offheap-resource/src/test/resources/configs/valid.xml b/offheap-resource/src/test/resources/configs/valid.xml
new file mode 100644
index 0000000000..2ce63d9096
--- /dev/null
+++ b/offheap-resource/src/test/resources/configs/valid.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ 128
+ 1024
+
diff --git a/pom.xml b/pom.xml
index c5bbaef1e4..2be42b2cf9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -34,6 +34,7 @@
+ offheap-resourceproxycoordinator-entity/commoncoordinator-entity/client