From 3b82c9ad36fe89546e4e0a488737f74255e65433 Mon Sep 17 00:00:00 2001 From: Chris Dennis Date: Wed, 16 Mar 2016 13:49:54 -0400 Subject: [PATCH 1/4] Issue #49 Add offheap-resource project --- offheap-resource/pom.xml | 70 ++++++++++++++++++++++++++++++++++++++++ pom.xml | 1 + 2 files changed, 71 insertions(+) create mode 100644 offheap-resource/pom.xml diff --git a/offheap-resource/pom.xml b/offheap-resource/pom.xml new file mode 100644 index 0000000000..f37c9ac5ba --- /dev/null +++ b/offheap-resource/pom.xml @@ -0,0 +1,70 @@ + + + 4.0.0 + + + org.terracotta + platform-root + 1.0-SNAPSHOT + ../pom.xml + + + offheap-resource + + + + org.terracotta + standard-cluster-services + provided + + + junit + junit + test + + + 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 + + + + + + + diff --git a/pom.xml b/pom.xml index c5bbaef1e4..2be42b2cf9 100644 --- a/pom.xml +++ b/pom.xml @@ -34,6 +34,7 @@ + offheap-resource proxy coordinator-entity/common coordinator-entity/client From 27479eb630a4c4f0325bd6b536a1862dc30b1d62 Mon Sep 17 00:00:00 2001 From: Chris Dennis Date: Thu, 17 Mar 2016 16:18:01 -0400 Subject: [PATCH 2/4] Issue #49 XSD for offheap resource configuration --- .../src/main/resources/offheap-resource.xsd | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 offheap-resource/src/main/resources/offheap-resource.xsd 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. + + + + + + + + + + + + + + + + + + From 093cc129ad514de7b70c3337f46ccb1191a5060c Mon Sep 17 00:00:00 2001 From: Chris Dennis Date: Fri, 18 Mar 2016 14:34:25 -0400 Subject: [PATCH 3/4] Issue #50 Add configuration parsing logic --- offheap-resource/pom.xml | 30 ++++++- .../OffHeapResourceConfigurationParser.java | 70 ++++++++++++++++ .../OffHeapResourcesConfiguration.java | 43 ++++++++++ .../OffHeapResourcesProvider.java | 45 ++++++++++ ...racotta.config.service.ServiceConfigParser | 1 + ...ffHeapResourceConfigurationParserTest.java | 84 +++++++++++++++++++ .../test/resources/configs/no-resources.xml | 23 +++++ .../src/test/resources/configs/valid.xml | 25 ++++++ 8 files changed, 318 insertions(+), 3 deletions(-) create mode 100644 offheap-resource/src/main/java/org/terracotta/offheapresource/OffHeapResourceConfigurationParser.java create mode 100644 offheap-resource/src/main/java/org/terracotta/offheapresource/OffHeapResourcesConfiguration.java create mode 100644 offheap-resource/src/main/java/org/terracotta/offheapresource/OffHeapResourcesProvider.java create mode 100644 offheap-resource/src/main/resources/META-INF/services/org.terracotta.config.service.ServiceConfigParser create mode 100644 offheap-resource/src/test/java/org/terracotta/offheapresource/OffHeapResourceConfigurationParserTest.java create mode 100644 offheap-resource/src/test/resources/configs/no-resources.xml create mode 100644 offheap-resource/src/test/resources/configs/valid.xml diff --git a/offheap-resource/pom.xml b/offheap-resource/pom.xml index f37c9ac5ba..f0482ff71d 100644 --- a/offheap-resource/pom.xml +++ b/offheap-resource/pom.xml @@ -13,15 +13,20 @@ - org.terracotta - standard-cluster-services - provided + org.terracotta.internal + tc-config-parser + 10.0-SNAPSHOT junit junit test + + org.hamcrest + hamcrest-all + 1.3 + org.mockito mockito-all @@ -65,6 +70,25 @@ + + org.codehaus.mojo + jaxb2-maven-plugin + 2.2 + + + xjc + + xjc + + + + + org.terracotta.offheapresource.config + + src/main/resources/offheap-resource.xsd + + + 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/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 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..280ac7fc5d --- /dev/null +++ b/offheap-resource/src/main/java/org/terracotta/offheapresource/OffHeapResourcesProvider.java @@ -0,0 +1,45 @@ +/* + * 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.io.IOException; +import java.util.Collection; +import org.terracotta.entity.ServiceConfiguration; +import org.terracotta.entity.ServiceProvider; +import org.terracotta.entity.ServiceProviderCleanupException; +import org.terracotta.entity.ServiceProviderConfiguration; + +/** + * + * @author cdennis + */ +class OffHeapResourcesProvider implements ServiceProvider { + + @Override + public boolean initialize(ServiceProviderConfiguration configuration) { + throw new UnsupportedOperationException(); + } + + @Override + public T getService(long consumerID, ServiceConfiguration configuration) { + throw new UnsupportedOperationException(); + } + + @Override + public Collection> getProvidedServiceTypes() { + throw new UnsupportedOperationException(); + } + + @Override + public void close() throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() throws ServiceProviderCleanupException { + throw new UnsupportedOperationException(); + } +} 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/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/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 + From b2c3f222252243b1ee079aba27922ba1a40d050a Mon Sep 17 00:00:00 2001 From: Chris Dennis Date: Fri, 18 Mar 2016 14:35:38 -0400 Subject: [PATCH 4/4] Issue #50 Implement offheap-resource provider --- offheap-resource/pom.xml | 6 + .../offheapresource/OffHeapResource.java | 93 ++++++++++++++ .../OffHeapResourceIdentifier.java | 56 ++++++++ .../OffHeapResourcesProvider.java | 98 +++++++++++--- .../offheapresource/PhysicalMemory.java | 82 ++++++++++++ .../OffHeapResourceIdentifierTest.java | 53 ++++++++ .../offheapresource/OffHeapResourceTest.java | 91 +++++++++++++ .../OffHeapResourcesProviderTest.java | 120 ++++++++++++++++++ 8 files changed, 582 insertions(+), 17 deletions(-) create mode 100644 offheap-resource/src/main/java/org/terracotta/offheapresource/OffHeapResource.java create mode 100644 offheap-resource/src/main/java/org/terracotta/offheapresource/OffHeapResourceIdentifier.java create mode 100644 offheap-resource/src/main/java/org/terracotta/offheapresource/PhysicalMemory.java create mode 100644 offheap-resource/src/test/java/org/terracotta/offheapresource/OffHeapResourceIdentifierTest.java create mode 100644 offheap-resource/src/test/java/org/terracotta/offheapresource/OffHeapResourceTest.java create mode 100644 offheap-resource/src/test/java/org/terracotta/offheapresource/OffHeapResourcesProviderTest.java diff --git a/offheap-resource/pom.xml b/offheap-resource/pom.xml index f0482ff71d..9011ddad39 100644 --- a/offheap-resource/pom.xml +++ b/offheap-resource/pom.xml @@ -12,6 +12,12 @@ offheap-resource + + + org.slf4j + slf4j-api + 1.7.18 + org.terracotta.internal tc-config-parser 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/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/OffHeapResourcesProvider.java b/offheap-resource/src/main/java/org/terracotta/offheapresource/OffHeapResourcesProvider.java index 280ac7fc5d..f4273132f2 100644 --- a/offheap-resource/src/main/java/org/terracotta/offheapresource/OffHeapResourcesProvider.java +++ b/offheap-resource/src/main/java/org/terracotta/offheapresource/OffHeapResourcesProvider.java @@ -1,45 +1,109 @@ /* - * 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. + * 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.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.ServiceProviderCleanupException; import org.terracotta.entity.ServiceProviderConfiguration; +import org.terracotta.offheapresource.config.MemoryUnit; +import org.terracotta.offheapresource.config.ResourceType; /** - * - * @author cdennis + * 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. */ -class OffHeapResourcesProvider implements ServiceProvider { +public class OffHeapResourcesProvider implements ServiceProvider { + + private static final Logger LOGGER = LoggerFactory.getLogger(OffHeapResourcesProvider.class); + + private final Map resources = new HashMap(); @Override - public boolean initialize(ServiceProviderConfiguration configuration) { - throw new UnsupportedOperationException(); + 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 configuration) { - throw new UnsupportedOperationException(); + 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() { - throw new UnsupportedOperationException(); + return Collections.>singleton(OffHeapResource.class); } @Override - public void close() throws IOException { - throw new UnsupportedOperationException(); + public void close() { + clear(); } @Override - public void clear() throws ServiceProviderCleanupException { - throw new UnsupportedOperationException(); + 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/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 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()); + } +}