Adding new TLP as per INFRA-5163
git-svn-id: https://svn.apache.org/repos/asf/directmemory/tags/directmemory-0.1-incubating@1375467 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..96f31a1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+target
+*.iml
+.idea
+.classpath
+.project
+.settings
+.DS_Store
+out
diff --git a/DISCLAIMER b/DISCLAIMER
new file mode 100644
index 0000000..33d4abd
--- /dev/null
+++ b/DISCLAIMER
@@ -0,0 +1,10 @@
+Apache DirectMemory is an effort undergoing incubation at the Apache Software
+Foundation (ASF), sponsored by the Apache Incubator PMC.
+
+Incubation is required of all newly accepted projects until a further review
+indicates that the infrastructure, communications, and decision making process
+have stabilized in a manner consistent with other successful ASF projects.
+
+While incubation status is not necessarily a reflection of the completeness
+or stability of the code, it does indicate that the project has yet to be
+fully endorsed by the ASF.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..66de955
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,247 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
+
+
+APPENDIX II: Licenses for Incorporated Works.
+
+------
+jQuery, Jquery tmpl, jQuery JSON Plugin
+
+Copyright (c) 2011 John Resig, http://jquery.com/
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+------
+Bootstrap
+
+Copyright 2012 Twitter, Inc.
+
+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.
+
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..19f0ba0
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,11 @@
+Apache DirectMemory
+Copyright 2001-2012 The Apache Software Foundation
+
+This product includes software developed by
+The Apache Software Foundation (http://www.apache.org/).
+
+This product includes software (bootstrap) developed by
+Twitter (http://twitter.github.com/bootstrap/).
+
+This product includes software (jquery) developed by
+Jquery (http://jquery.org/).
diff --git a/README b/README
new file mode 100644
index 0000000..7099295
--- /dev/null
+++ b/README
@@ -0,0 +1,13 @@
+Apache DirectMemory is a multi layered cache implementation featuring off-heap memory storage (a-la BigMemory)
+to enable caching of java objects without degrading jvm performance.
+Its main purpose is to act as a second level cache (after a heap based one)
+to collect large amounts of data without filling up the java heap and thus avoiding long garbage collection cycles.
+Included in the box is a small set of utility classes to easily handle off-heap memory buffers.
+
+------------
+Build
+------------
+You need Apache Maven to build the project. Just use: mvn clean install (you will have jars installed locally in your
+local Maven repository.
+
+NOTE: the buildnumber maven plugin which add a maven property with the current svn revision has been configured to work with svn 1.7
diff --git a/directmemory-cache/bench.bat b/directmemory-cache/bench.bat
new file mode 100644
index 0000000..77fa9fd
--- /dev/null
+++ b/directmemory-cache/bench.bat
@@ -0,0 +1,15 @@
+rem Licensed to the Apache Software Foundation (ASF) under one or more
+rem contributor license agreements. See the NOTICE file distributed with
+rem this work for additional information regarding copyright ownership.
+rem The ASF licenses this file to You under the Apache License, Version 2.0
+rem (the "License"); you may not use this file except in compliance with
+rem the License. You may obtain a copy of the License at
+rem
+rem http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem Unless required by applicable law or agreed to in writing, software
+rem distributed under the License is distributed on an "AS IS" BASIS,
+rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+rem See the License for the specific language governing permissions and
+rem limitations under the License.
+mvn test -Dtest=SerializerTest -Djub.customkey=%1 -Djub.consumers=CONSOLE,XML,H2 -Djub.db.file=data/benchmarks/database -Djub.xml.file=data/benchmarks/benchmarks.xml -Djub.charts.dir=data/benchmarks/graphs
diff --git a/directmemory-cache/bench.sh b/directmemory-cache/bench.sh
new file mode 100644
index 0000000..6abc326
--- /dev/null
+++ b/directmemory-cache/bench.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+mvn test -Djub.customkey=$1 -Dtest=MicroBenchmarks -Djub.consumers=CONSOLE,XML,H2 -Djub.db.file=target/benchmarks/database -Djub.xml.file=target/logs/benchmarks.xml -Djub.charts.dir=target/data/benchmarks/graphs
diff --git a/directmemory-cache/pom.xml b/directmemory-cache/pom.xml
new file mode 100644
index 0000000..42529d3
--- /dev/null
+++ b/directmemory-cache/pom.xml
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.directmemory</groupId>
+ <artifactId>directmemory</artifactId>
+ <version>0.1-incubating</version>
+ <relativePath>../</relativePath>
+ </parent>
+
+ <artifactId>directmemory-cache</artifactId>
+ <name>Apache DirectMemory :: Cache</name>
+ <packaging>bundle</packaging>
+ <description>DirectMemory Cache is a multi layered cache implementation featuring off-heap memory management (a-la
+ BigMemory) to enable efficient handling of a large number of java objects without affecting jvm garbage collection
+ performance
+ </description>
+
+ <properties>
+ <osgi.import>
+ !org.apache.directmemory*,
+ com.google.common.collect;version="[9.0,11)",
+ com.google.common.base;version="[9.0,11)",
+ org.aspectj*;version="[1.6,2)",
+ org.slf4j*
+ </osgi.import>
+ <osgi.export>org.apache.directmemory*;version="${project.version}</osgi.export>
+ </properties>
+
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-clean-plugin</artifactId>
+ <configuration>
+ <filesets>
+ <fileset>
+ <directory>logs</directory>
+ <followSymlinks>false</followSymlinks>
+ </fileset>
+ </filesets>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>aspectj-maven-plugin</artifactId>
+ <version>1.4</version>
+ <configuration>
+ <source>${maven.compile.source}</source>
+ <target>${maven.compile.target}</target>
+ <complianceLevel>${java.version}</complianceLevel>
+ </configuration>
+ <executions>
+ <execution>
+ <goals>
+ <goal>compile</goal>
+ <!-- use this goal to weave all your main classes -->
+ <goal>test-compile</goal>
+ <!-- use this goal to weave all your test classes -->
+ </goals>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>org.aspectj</groupId>
+ <artifactId>aspectjtools</artifactId>
+ <version>${aspectj.version}</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.aspectj</groupId>
+ <artifactId>aspectjrt</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.carrotsearch</groupId>
+ <artifactId>junit-benchmarks</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/directmemory-cache/src/main/aspect/org/apache/directmemory/monitoring/Performance.aj b/directmemory-cache/src/main/aspect/org/apache/directmemory/monitoring/Performance.aj
new file mode 100644
index 0000000..110ec78
--- /dev/null
+++ b/directmemory-cache/src/main/aspect/org/apache/directmemory/monitoring/Performance.aj
@@ -0,0 +1,196 @@
+package org.apache.directmemory.monitoring;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import static java.lang.String.format;
+
+import org.apache.directmemory.cache.Cache;
+import org.apache.directmemory.measures.Monitor;
+import org.apache.directmemory.measures.MonitorService;
+import org.apache.directmemory.memory.Pointer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public aspect Performance
+{
+
+ public static final String cache_prefix = "cache";
+
+ public static final String cache_putByteArray = cache_prefix + ".putByteArray";
+
+ public static final String cache_retrieveByteArray = cache_prefix + ".retrieveByteArray";
+
+ public static final String cache_getPointer = cache_prefix + ".getPointer";
+
+ public static final String cache_putObject = cache_prefix + ".put";
+
+ public static final String cache_retrieveObject = cache_prefix + ".get";
+
+ public static final String cache_collectLFU = cache_prefix + ".collectLFU";
+
+ public static final String cache_collectExpired = cache_prefix + ".collectExpired";
+
+ public static final String cache_serialize = cache_prefix + ".serializer.serialize";
+
+ public static final String cache_deserialize = cache_prefix + ".serializer.deserialize";
+
+ private static Logger logger = LoggerFactory.getLogger( Cache.class );
+
+ pointcut putByteArrayPointcut( String key, byte[] payload ):
+ execution(Pointer org.apache.directmemory.cache.Cache.putByteArray(java.lang.String, byte[])) &&
+ args(key, payload);
+
+ pointcut putObjectPointcut( String key, Object object, int expiresIn ):
+ execution(Pointer org.apache.directmemory.cache.Cache.put(java.lang.String, java.lang.Object, int)) &&
+ args(key, object, expiresIn);
+
+ pointcut retrieveByteArrayPointcut( String key ):
+ execution(byte[] org.apache.directmemory.cache.Cache.retrieveByteArray(java.lang.String)) &&
+ args(key);
+
+ pointcut retrieveObjectPointcut( String key ):
+ execution(java.lang.Object org.apache.directmemory.cache.Cache.retrieve(java.lang.String)) &&
+ args(key);
+
+ pointcut getPointcut( String key ):
+ execution(Pointer org.apache.directmemory.cache.Cache.getPointer(java.lang.String)) &&
+ args(key);
+
+ pointcut collectLFUPointcut():
+ execution(void org.apache.directmemory.cache.Cache.collectLFU());
+
+ pointcut collectExpiredPointcut():
+ execution(void org.apache.directmemory.cache.Cache.collectExpired());
+
+ pointcut serializePointcut( Object obj, @SuppressWarnings( "rawtypes" ) Class clazz ):
+ execution(byte[] org.apache.directmemory.serialization.ProtoStuffSerializerV1.serialize(java.lang.Object, java.lang.Class)) &&
+ args(obj, clazz);
+
+ pointcut deserializePointcut( byte[] source, @SuppressWarnings( "rawtypes" ) Class clazz ):
+ execution(java.lang.Object org.apache.directmemory.serialization.ProtoStuffSerializerV1.deserialize(byte[], java.lang.Class)) &&
+ args(source, clazz);
+
+ Pointer around( String key, byte[] payload ): putByteArrayPointcut(key, payload) {
+ MonitorService mon = Monitor.get( cache_putByteArray );
+ final long startedAt = mon.start();
+ Pointer entry = proceed( key, payload );
+ if ( logger.isDebugEnabled() )
+ {
+ logger.debug( format( "put: [%s] %d bytes", key, payload.length ) );
+ }
+ mon.stop( startedAt );
+ return entry;
+ }
+
+ Pointer around( String key, Object object, int expiresIn ): putObjectPointcut(key, object, expiresIn) {
+ MonitorService mon = Monitor.get( cache_putObject );
+ final long startedAt = mon.start();
+ Pointer entry = proceed( key, object, expiresIn );
+ if ( logger.isDebugEnabled() )
+ {
+ logger.debug( format( "put object: [%s]", key ) );
+ }
+ mon.stop( startedAt );
+ return entry;
+ }
+
+ byte[] around( String key ): retrieveByteArrayPointcut(key) {
+ MonitorService mon = Monitor.get( cache_retrieveByteArray );
+ final long startedAt = mon.start();
+ byte[] payload = proceed( key );
+ if ( logger.isDebugEnabled() )
+ {
+ logger.debug( format( "retrieve: [%s]", key ) );
+ }
+ mon.stop( startedAt );
+ return payload;
+ }
+
+ Object around( String key ): retrieveObjectPointcut(key) {
+ MonitorService mon = Monitor.get( cache_retrieveObject );
+ final long startedAt = mon.start();
+ Object payload = proceed( key );
+ if ( logger.isDebugEnabled() )
+ {
+ logger.debug( format( "retrieve object: [%s]", key ) );
+ }
+ mon.stop( startedAt );
+ return payload;
+ }
+
+ Pointer around( String key ): getPointcut(key) {
+ MonitorService mon = Monitor.get( cache_getPointer );
+ final long startedAt = mon.start();
+ Pointer pointer = proceed( key );
+ if ( logger.isDebugEnabled() )
+ {
+ logger.debug( format( "get: [%s]", key ) );
+ }
+ mon.stop( startedAt );
+ return pointer;
+ }
+
+ void around(): collectLFUPointcut() {
+ MonitorService mon = Monitor.get( cache_collectLFU );
+ final long startedAt = mon.start();
+ proceed();
+ if ( logger.isDebugEnabled() )
+ {
+ logger.debug( "collect LFU" );
+ }
+ mon.stop( startedAt );
+ }
+
+ void around(): collectExpiredPointcut() {
+ MonitorService mon = Monitor.get( cache_collectExpired );
+ final long startedAt = mon.start();
+ proceed();
+ if ( logger.isDebugEnabled() )
+ {
+ logger.debug( "collect expired" );
+ }
+ mon.stop( startedAt );
+ }
+
+ byte[] around( Object obj, @SuppressWarnings( "rawtypes" ) Class clazz ): serializePointcut(obj, clazz) {
+ MonitorService mon = Monitor.get( cache_serialize );
+ final long startedAt = mon.start();
+ byte[] payload = proceed( obj, clazz );
+ if ( logger.isDebugEnabled() )
+ {
+ logger.debug( format( "serialize: [%s]", clazz.getSimpleName() ) );
+ }
+ mon.stop( startedAt );
+ return payload;
+ }
+
+ Object around( byte[] source, @SuppressWarnings( "rawtypes" ) Class clazz ): deserializePointcut(source, clazz) {
+ MonitorService mon = Monitor.get( cache_deserialize );
+ final long startedAt = mon.start();
+ Object obj = proceed( source, clazz );
+ if ( logger.isDebugEnabled() )
+ {
+ logger.debug( format( "deserialize: [%s]", clazz.getSimpleName() ) );
+ }
+ mon.stop( startedAt );
+ return obj;
+ }
+
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/DirectMemory.java b/directmemory-cache/src/main/java/org/apache/directmemory/DirectMemory.java
new file mode 100644
index 0000000..f6c91df
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/DirectMemory.java
@@ -0,0 +1,180 @@
+package org.apache.directmemory;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.lang.String.format;
+import static org.apache.directmemory.measures.In.seconds;
+import static org.apache.directmemory.serialization.SerializerFactory.createNewSerializer;
+
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.directmemory.cache.CacheService;
+import org.apache.directmemory.cache.CacheServiceImpl;
+import org.apache.directmemory.measures.Ram;
+import org.apache.directmemory.memory.MemoryManagerService;
+import org.apache.directmemory.memory.MemoryManagerServiceImpl;
+import org.apache.directmemory.memory.Pointer;
+import org.apache.directmemory.serialization.Serializer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.MapMaker;
+
+public final class DirectMemory<K, V>
+{
+
+ public static final int DEFAULT_CONCURRENCY_LEVEL = 4;
+
+ public static final int DEFAULT_INITIAL_CAPACITY = 100000;
+
+ public static final int DEFAULT_DISPOSAL_TIME = 10; // seconds
+
+ private final Logger logger = LoggerFactory.getLogger( getClass() );
+
+ private int numberOfBuffers;
+
+ private int size;
+
+ private int initialCapacity = DEFAULT_INITIAL_CAPACITY;
+
+ private int concurrencyLevel = DEFAULT_CONCURRENCY_LEVEL;
+
+ private long disposalTime = seconds( DEFAULT_DISPOSAL_TIME );
+
+ private ConcurrentMap<K, Pointer<V>> map;
+
+ private Serializer serializer;
+
+ private MemoryManagerService<V> memoryManager;
+
+ public DirectMemory()
+ {
+ // does nothing
+ }
+
+ public DirectMemory( DirectMemory<K, V> prototype )
+ {
+ checkArgument( prototype != null, "Impossible to create a DirectMemory instance from a null prototype" );
+
+ numberOfBuffers = prototype.numberOfBuffers;
+ size = prototype.size;
+ initialCapacity = prototype.initialCapacity;
+ concurrencyLevel = prototype.concurrencyLevel;
+ disposalTime = prototype.disposalTime;
+
+ map = prototype.map;
+ serializer = prototype.serializer;
+ memoryManager = prototype.memoryManager;
+ }
+
+ public DirectMemory<K, V> setNumberOfBuffers( int numberOfBuffers )
+ {
+ checkArgument( numberOfBuffers > 0, "Impossible to create a CacheService with a number of buffers lesser than 1" );
+ this.numberOfBuffers = numberOfBuffers;
+ return this;
+ }
+
+ public DirectMemory<K, V> setSize( int size )
+ {
+ checkArgument( size > 0, "Impossible to create a CacheService with a size lesser than 1" );
+ this.size = size;
+ return this;
+ }
+
+ public DirectMemory<K, V> setInitialCapacity( int initialCapacity )
+ {
+ checkArgument( initialCapacity > 0, "Impossible to create a CacheService with an initialCapacity lesser than 1" );
+ this.initialCapacity = initialCapacity;
+ return this;
+ }
+
+ public DirectMemory<K, V> setConcurrencyLevel( int concurrencyLevel )
+ {
+ checkArgument( concurrencyLevel > 0, "Impossible to create a CacheService with a concurrencyLevel lesser than 1" );
+ this.concurrencyLevel = concurrencyLevel;
+ return this;
+ }
+
+ public DirectMemory<K, V> setDisposalTime( long disposalTime )
+ {
+ checkArgument( disposalTime > 0, "Impossible to create a CacheService with a disposalTime lesser than 1" );
+ this.disposalTime = disposalTime;
+ return this;
+ }
+
+ public DirectMemory<K, V> setMap( ConcurrentMap<K, Pointer<V>> map )
+ {
+ checkArgument( map != null, "Impossible to create a CacheService with a null map" );
+ this.map = map;
+ return this;
+ }
+
+ public DirectMemory<K, V> setSerializer( Serializer serializer )
+ {
+ checkArgument( serializer != null, "Impossible to create a CacheService with a null serializer" );
+ this.serializer = serializer;
+ return this;
+ }
+
+ public DirectMemory<K, V> setMemoryManager( MemoryManagerService<V> memoryManager )
+ {
+ checkArgument( memoryManager != null, "Impossible to create a CacheService with a null memoryManager" );
+ this.memoryManager = memoryManager;
+ return this;
+ }
+
+ public CacheService<K, V> newCacheService()
+ {
+ if ( map == null )
+ {
+ map = new MapMaker().concurrencyLevel( concurrencyLevel ).initialCapacity( initialCapacity ).makeMap();
+ }
+ if ( memoryManager == null )
+ {
+ memoryManager = new MemoryManagerServiceImpl<V>();
+ }
+ if ( serializer == null )
+ {
+ serializer = createNewSerializer();
+ }
+
+ logger.info( "******************************** initializing *******************************" );
+ logger.info( " ____ _ __ __ ___" );
+ logger.info( " / __ \\(_)________ _____/ /_/ |/ /___ ____ ___ ____ _______ __" );
+ logger.info( " / / / / // ___/ _ \\/ ___/ __/ /|_/ // _ \\/ __ `__ \\/ __ \\/ ___/ / / /" );
+ logger.info( " / /_/ / // / / __/ /__/ /_/ / / // __/ / / / / / /_/ / / / /_/ / " );
+ logger.info( " /_____/_//_/ \\___/\\___/\\__/_/ /_/ \\___/_/ /_/ /_/\\____/_/ \\__, /" );
+ logger.info( " /____/ " );
+ logger.info( "********************************************************************************" );
+
+ memoryManager.init( numberOfBuffers, size );
+
+ logger.info( "initialized" );
+ logger.info( format( "number of buffer(s): \t%1d with %2s each", numberOfBuffers, Ram.inMb( size ) ) );
+ logger.info( format( "initial capacity: \t%1d", initialCapacity ) );
+ logger.info( format( "concurrency level: \t%1d", concurrencyLevel ) );
+
+ CacheService<K, V> cacheService = new CacheServiceImpl<K, V>( map, memoryManager, serializer );
+ cacheService.scheduleDisposalEvery( disposalTime );
+ return cacheService;
+ }
+
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/cache/Cache.java b/directmemory-cache/src/main/java/org/apache/directmemory/cache/Cache.java
new file mode 100644
index 0000000..f097238
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/cache/Cache.java
@@ -0,0 +1,154 @@
+package org.apache.directmemory.cache;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+
+import org.apache.directmemory.DirectMemory;
+import org.apache.directmemory.memory.MemoryManagerService;
+import org.apache.directmemory.memory.Pointer;
+import org.apache.directmemory.serialization.Serializer;
+
+public class Cache
+{
+
+ private static final DirectMemory<String, Object> builder = new DirectMemory<String, Object>();
+
+ private static CacheService<String, Object> cacheService = builder.newCacheService();
+
+ // olamy chicken and eggs isssue
+ // private static CacheService cacheService = new CacheServiceImpl( getMemoryManager());
+
+ private Cache()
+ {
+ // not instantiable
+ }
+
+ public static void scheduleDisposalEvery( long l )
+ {
+ // store to builder
+ builder.setDisposalTime( l );
+
+ cacheService.scheduleDisposalEvery( l );
+ }
+
+ public static void init( int numberOfBuffers, int size, int initialCapacity, int concurrencyLevel )
+ {
+ cacheService =
+ builder.setNumberOfBuffers( numberOfBuffers ).setInitialCapacity( initialCapacity ).setConcurrencyLevel(
+ concurrencyLevel ).setSize( size ).newCacheService();
+ }
+
+ public static void init( int numberOfBuffers, int size )
+ {
+ init( numberOfBuffers, size, DirectMemory.DEFAULT_INITIAL_CAPACITY, DirectMemory.DEFAULT_CONCURRENCY_LEVEL );
+ }
+
+ public static Pointer<Object> putByteArray( String key, byte[] payload, int expiresIn )
+ {
+ return cacheService.putByteArray( key, payload, expiresIn );
+ }
+
+ public static Pointer<Object> putByteArray( String key, byte[] payload )
+ {
+ return cacheService.putByteArray( key, payload );
+ }
+
+ public static Pointer<Object> put( String key, Object object )
+ {
+ return cacheService.put( key, object );
+ }
+
+ public static Pointer<Object> put( String key, Object object, int expiresIn )
+ {
+ return cacheService.put( key, object, expiresIn );
+ }
+
+ public static byte[] retrieveByteArray( String key )
+ {
+ return cacheService.retrieveByteArray( key );
+ }
+
+ public static Object retrieve( String key )
+ {
+ return cacheService.retrieve( key );
+ }
+
+ public static Pointer<Object> getPointer( String key )
+ {
+ return cacheService.getPointer( key );
+ }
+
+ public static void free( String key )
+ {
+ cacheService.free( key );
+ }
+
+ public static void free( Pointer<Object> pointer )
+ {
+ cacheService.free( pointer );
+ }
+
+ public static void collectExpired()
+ {
+ cacheService.collectExpired();
+ }
+
+ public static void collectLFU()
+ {
+ cacheService.collectLFU();
+ }
+
+ public static void collectAll()
+ {
+ cacheService.collectAll();
+ }
+
+
+ public static void clear()
+ {
+ cacheService.clear();
+ }
+
+ public static long entries()
+ {
+ return cacheService.entries();
+ }
+
+ public static void dump()
+ {
+ cacheService.dump();
+ }
+
+ public static Serializer getSerializer()
+ {
+ return cacheService.getSerializer();
+ }
+
+ public static MemoryManagerService<Object> getMemoryManager()
+ {
+ return cacheService.getMemoryManager();
+ }
+
+ public static Pointer<Object> allocate( String key, int size )
+ {
+ return cacheService.allocate( key, Object.class, size );
+ }
+
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/cache/CacheService.java b/directmemory-cache/src/main/java/org/apache/directmemory/cache/CacheService.java
new file mode 100644
index 0000000..bd96a71
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/cache/CacheService.java
@@ -0,0 +1,93 @@
+package org.apache.directmemory.cache;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+
+import org.apache.directmemory.memory.MemoryManagerService;
+import org.apache.directmemory.memory.Pointer;
+import org.apache.directmemory.serialization.Serializer;
+
+import java.util.concurrent.ConcurrentMap;
+
+public interface CacheService<K, V>
+{
+
+ void scheduleDisposalEvery( long l );
+
+ /**
+ *
+ * @param key
+ * @param payload
+ * @param expiresIn in ms
+ * @return
+ */
+ Pointer<V> putByteArray( K key, byte[] payload, int expiresIn );
+
+ Pointer<V> putByteArray( K key, byte[] payload );
+
+ Pointer<V> put( K key, V value );
+
+ /**
+ *
+ * @param key
+ * @param value
+ * @param expiresIn in ms
+ * @return
+ */
+ Pointer<V> put( K key, V value, int expiresIn );
+
+ byte[] retrieveByteArray( K key );
+
+ V retrieve( K key );
+
+ Pointer<V> getPointer( K key );
+
+ void free( K key );
+
+ void free( Pointer<V> pointer );
+
+ void collectExpired();
+
+ void collectLFU();
+
+ void collectAll();
+
+
+ void clear();
+
+ long entries();
+
+ void dump();
+
+ ConcurrentMap<K, Pointer<V>> getMap();
+
+ void setMap( ConcurrentMap<K, Pointer<V>> map );
+
+ Serializer getSerializer();
+
+ MemoryManagerService<V> getMemoryManager();
+
+ void setMemoryManager( MemoryManagerService<V> memoryManager );
+
+ void setSerializer( Serializer serializer );
+
+ <T extends V> Pointer<V> allocate( K key, Class<T> type, int size );
+
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/cache/CacheServiceImpl.java b/directmemory-cache/src/main/java/org/apache/directmemory/cache/CacheServiceImpl.java
new file mode 100644
index 0000000..946f26c
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/cache/CacheServiceImpl.java
@@ -0,0 +1,357 @@
+package org.apache.directmemory.cache;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import org.apache.directmemory.measures.Ram;
+import org.apache.directmemory.memory.MemoryManagerService;
+import org.apache.directmemory.memory.Pointer;
+import org.apache.directmemory.serialization.Serializer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.ConcurrentMap;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.lang.String.format;
+
+public class CacheServiceImpl<K, V>
+ implements CacheService<K, V>
+{
+
+ private static Logger logger = LoggerFactory.getLogger( CacheServiceImpl.class );
+
+ private ConcurrentMap<K, Pointer<V>> map;
+
+ private Serializer serializer;
+
+ private MemoryManagerService<V> memoryManager;
+
+ private final Timer timer = new Timer();
+
+ /**
+ * Constructor
+ */
+ public CacheServiceImpl( ConcurrentMap<K, Pointer<V>> map, MemoryManagerService<V> memoryManager,
+ Serializer serializer )
+ {
+ checkArgument( map != null, "Impossible to initialize the CacheService with a null map" );
+ checkArgument( memoryManager != null, "Impossible to initialize the CacheService with a null memoryManager" );
+ checkArgument( serializer != null, "Impossible to initialize the CacheService with a null serializer" );
+
+ this.map = map;
+ this.memoryManager = memoryManager;
+ this.serializer = serializer;
+ }
+
+ @Override
+ public void scheduleDisposalEvery( long l )
+ {
+ timer.schedule( new TimerTask()
+ {
+ public void run()
+ {
+ logger.info( "begin scheduled disposal" );
+
+ collectExpired();
+ collectLFU();
+
+ logger.info( "scheduled disposal complete" );
+ }
+ }, l, l );
+
+ logger.info( "disposal scheduled every {} milliseconds", l );
+ }
+
+ @Override
+ public Pointer<V> putByteArray( K key, byte[] payload )
+ {
+ return store( key, payload, 0 );
+ }
+
+ @Override
+ public Pointer<V> putByteArray( K key, byte[] payload, int expiresIn )
+ {
+ return store( key, payload, expiresIn );
+ }
+
+ @Override
+ public Pointer<V> put( K key, V value )
+ {
+ return put( key, value, 0 );
+ }
+
+ @Override
+ public Pointer<V> put( K key, V value, int expiresIn )
+ {
+ try
+ {
+ byte[] payload = serializer.serialize( value );
+ Pointer<V> ptr = store( key, payload, expiresIn );
+ if ( ptr != null )
+ {
+ @SuppressWarnings( "unchecked" ) // type driven by the compiler
+ Class<? extends V> clazz = (Class<? extends V>) value.getClass();
+
+ ptr.setClazz( clazz );
+ }
+ return ptr;
+ }
+ catch ( IOException e )
+ {
+
+ if ( logger.isDebugEnabled() )
+ {
+ logger.debug( "IOException put object in cache:{}", e.getMessage(), e );
+ }
+ else
+ {
+ logger.error( "IOException put object in cache:{}", e.getMessage() );
+ }
+ return null;
+ }
+ }
+
+ private Pointer<V> store( K key, byte[] payload, int expiresIn )
+ {
+ Pointer<V> pointer = map.get( key );
+ if ( pointer != null )
+ {
+ return memoryManager.update( pointer, payload );
+ }
+ else
+ {
+ pointer = memoryManager.store( payload, expiresIn );
+ if ( pointer != null )
+ {
+ map.put( key, pointer );
+ }
+ return pointer;
+ }
+ }
+
+ @Override
+ public byte[] retrieveByteArray( K key )
+ {
+ Pointer<V> ptr = getPointer( key );
+ if ( ptr == null )
+ {
+ return null;
+ }
+ if ( ptr.isExpired() || ptr.isFree() )
+ {
+ map.remove( key );
+ if ( !ptr.isFree() )
+ {
+ memoryManager.free( ptr );
+ }
+ return null;
+ }
+ else
+ {
+ return memoryManager.retrieve( ptr );
+ }
+ }
+
+ @Override
+ public V retrieve( K key )
+ {
+ Pointer<V> ptr = getPointer( key );
+ if ( ptr == null )
+ {
+ return null;
+ }
+ if ( ptr.isExpired() || ptr.isFree() )
+ {
+ map.remove( key );
+ if ( !ptr.isFree() )
+ {
+ memoryManager.free( ptr );
+ }
+ return null;
+ }
+ else
+ {
+ try
+ {
+ return serializer.deserialize( memoryManager.retrieve( ptr ), ptr.getClazz() );
+ }
+ catch ( EOFException e )
+ {
+ logger.error( e.getMessage() );
+ }
+ catch ( IOException e )
+ {
+ logger.error( e.getMessage() );
+ }
+ catch ( ClassNotFoundException e )
+ {
+ logger.error( e.getMessage() );
+ }
+ catch ( InstantiationException e )
+ {
+ logger.error( e.getMessage() );
+ }
+ catch ( IllegalAccessException e )
+ {
+ logger.error( e.getMessage() );
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public Pointer<V> getPointer( K key )
+ {
+ return map.get( key );
+ }
+
+ @Override
+ public void free( K key )
+ {
+ Pointer<V> p = map.remove( key );
+ if ( p != null )
+ {
+ memoryManager.free( p );
+ }
+ }
+
+ @Override
+ public void free( Pointer<V> pointer )
+ {
+ memoryManager.free( pointer );
+ }
+
+ @Override
+ public void collectExpired()
+ {
+ memoryManager.collectExpired();
+ // still have to look for orphan (storing references to freed pointers) map entries
+ }
+
+ @Override
+ public void collectLFU()
+ {
+ memoryManager.collectLFU();
+ // can possibly clear one whole buffer if it's too fragmented - investigate
+ }
+
+ @Override
+ public void collectAll()
+ {
+ Thread thread = new Thread()
+ {
+ public void run()
+ {
+ logger.info( "begin disposal" );
+ collectExpired();
+ collectLFU();
+ logger.info( "disposal complete" );
+ }
+ };
+ thread.start();
+ }
+
+
+ @Override
+ public void clear()
+ {
+ map.clear();
+ memoryManager.clear();
+ logger.info( "Cache cleared" );
+ }
+
+ @Override
+ public long entries()
+ {
+ return map.size();
+ }
+
+ public void dump( MemoryManagerService<V> mms )
+ {
+ logger.info( format( "off-heap - allocated: \t%1s", Ram.inMb( mms.capacity() ) ) );
+ logger.info( format( "off-heap - used: \t%1s", Ram.inMb( mms.used() ) ) );
+ logger.info( format( "heap - max: \t%1s", Ram.inMb( Runtime.getRuntime().maxMemory() ) ) );
+ logger.info( format( "heap - allocated: \t%1s", Ram.inMb( Runtime.getRuntime().totalMemory() ) ) );
+ logger.info( format( "heap - free : \t%1s", Ram.inMb( Runtime.getRuntime().freeMemory() ) ) );
+ logger.info( "************************************************" );
+ }
+
+ @Override
+ public void dump()
+ {
+ if ( !logger.isInfoEnabled() )
+ {
+ return;
+ }
+
+ logger.info( "*** DirectMemory statistics ********************" );
+
+ dump( memoryManager );
+ }
+
+ @Override
+ public ConcurrentMap<K, Pointer<V>> getMap()
+ {
+ return map;
+ }
+
+ @Override
+ public void setMap( ConcurrentMap<K, Pointer<V>> map )
+ {
+ this.map = map;
+ }
+
+ @Override
+ public Serializer getSerializer()
+ {
+ return serializer;
+ }
+
+ @Override
+ public void setSerializer( Serializer serializer )
+ {
+ this.serializer = serializer;
+ }
+
+ @Override
+ public MemoryManagerService<V> getMemoryManager()
+ {
+ return memoryManager;
+ }
+
+ @Override
+ public void setMemoryManager( MemoryManagerService<V> memoryManager )
+ {
+ this.memoryManager = memoryManager;
+ }
+
+ @Override
+ public <T extends V> Pointer<V> allocate( K key, Class<T> type, int size )
+ {
+ Pointer<V> ptr = memoryManager.allocate( type, size, -1, -1 );
+ map.put( key, ptr );
+ ptr.setClazz( type );
+ return ptr;
+ }
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/cache/package-info.java b/directmemory-cache/src/main/java/org/apache/directmemory/cache/package-info.java
new file mode 100644
index 0000000..0fcdf8a
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/cache/package-info.java
@@ -0,0 +1,23 @@
+/**
+ * Apache DirectMemory cache APIs.
+ */
+package org.apache.directmemory.cache;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/measures/Every.java b/directmemory-cache/src/main/java/org/apache/directmemory/measures/Every.java
new file mode 100644
index 0000000..a94dcd8
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/measures/Every.java
@@ -0,0 +1,31 @@
+package org.apache.directmemory.measures;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+public class Every
+ extends In
+{
+
+ public Every( double measure )
+ {
+ super( measure );
+ }
+
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/measures/Expires.java b/directmemory-cache/src/main/java/org/apache/directmemory/measures/Expires.java
new file mode 100644
index 0000000..84f5773
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/measures/Expires.java
@@ -0,0 +1,40 @@
+package org.apache.directmemory.measures;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+public class Expires
+ extends In
+{
+
+ public Expires( double measure )
+ {
+ super( measure );
+ }
+
+ public static In in( double measure )
+ {
+ return new In( measure );
+ }
+
+ public static long never()
+ {
+ return -1L;
+ }
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/measures/For.java b/directmemory-cache/src/main/java/org/apache/directmemory/measures/For.java
new file mode 100644
index 0000000..0474015
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/measures/For.java
@@ -0,0 +1,31 @@
+package org.apache.directmemory.measures;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+public class For
+ extends In
+{
+
+ public For( double measure )
+ {
+ super( measure );
+ }
+
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/measures/Heap.java b/directmemory-cache/src/main/java/org/apache/directmemory/measures/Heap.java
new file mode 100644
index 0000000..c6285f5
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/measures/Heap.java
@@ -0,0 +1,26 @@
+package org.apache.directmemory.measures;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+public class Heap
+ extends Sizing
+{
+
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/measures/In.java b/directmemory-cache/src/main/java/org/apache/directmemory/measures/In.java
new file mode 100644
index 0000000..c26cb06
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/measures/In.java
@@ -0,0 +1,92 @@
+package org.apache.directmemory.measures;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+public class In
+{
+
+ private static final int MILLISECONDS_IN_SECOND = 1000;
+
+ private static final int SECONDS_IN_MINUTE_OR_MINUTES_IN_HOUR = 60;
+
+ private static final int HOUR_IN_DAY = 24;
+
+ private double measure;
+
+ public In( double measure )
+ {
+ this.measure = measure;
+ }
+
+ public long seconds()
+ {
+ return seconds( measure );
+ }
+
+ public long minutes()
+ {
+ return minutes( measure );
+ }
+
+ public long hours()
+ {
+ return hours( measure );
+ }
+
+ public long days()
+ {
+ return days( measure );
+ }
+
+ public static long seconds( double seconds )
+ {
+ return (long) seconds * MILLISECONDS_IN_SECOND;
+ }
+
+ public static long minutes( double minutes )
+ {
+ return seconds( minutes * SECONDS_IN_MINUTE_OR_MINUTES_IN_HOUR );
+ }
+
+ public static long hours( double hours )
+ {
+ return minutes( hours * SECONDS_IN_MINUTE_OR_MINUTES_IN_HOUR );
+ }
+
+ public static long days( double days )
+ {
+ return hours( days * HOUR_IN_DAY );
+ }
+
+ public static In just( double measure )
+ {
+ return new In( measure );
+ }
+
+ public static In exactly( double measure )
+ {
+ return new In( measure );
+ }
+
+ public static In only( double measure )
+ {
+ return new In( measure );
+ }
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/measures/Memory.java b/directmemory-cache/src/main/java/org/apache/directmemory/measures/Memory.java
new file mode 100644
index 0000000..860d029
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/measures/Memory.java
@@ -0,0 +1,26 @@
+package org.apache.directmemory.measures;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+public class Memory
+ extends Sizing
+{
+
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/measures/Monitor.java b/directmemory-cache/src/main/java/org/apache/directmemory/measures/Monitor.java
new file mode 100644
index 0000000..5aa65ff
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/measures/Monitor.java
@@ -0,0 +1,118 @@
+package org.apache.directmemory.measures;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import static java.lang.String.format;
+
+import java.text.DecimalFormat;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Monitor
+{
+
+ private static final Logger LOG = LoggerFactory.getLogger( Monitor.class );
+
+ //TODO: MONITORS looks like a good candidate to become a private field
+ public static final Map<String, MonitorService> MONITORS = new HashMap<String, MonitorService>();
+
+ private MonitorService monitorService;
+
+ public static MonitorService get( String key )
+ {
+ MonitorService mon = MONITORS.get( key );
+ if ( mon == null )
+ {
+ mon = new MonitorServiceImpl( key );
+ MONITORS.put( key, mon );
+ }
+ return mon;
+ }
+
+ public Monitor( String name )
+ {
+ this.monitorService = new MonitorServiceImpl( name );
+ MONITORS.put( name, monitorService );
+ }
+
+ public long start()
+ {
+ return System.nanoTime();
+ }
+
+ public long stop( long begunAt )
+ {
+ monitorService.getHits().incrementAndGet();
+ final long lastAccessed = System.nanoTime();
+ final long elapsed = lastAccessed - begunAt;
+ monitorService.addToTotalTime( elapsed );
+ if ( elapsed > monitorService.getMax() && monitorService.getHits().get() > 0 )
+ {
+ monitorService.setMax( elapsed );
+ }
+ if ( elapsed < monitorService.getMin() && monitorService.getHits().get() > 0 )
+ {
+ monitorService.setMin( elapsed );
+ }
+ return elapsed;
+ }
+
+ public long hits()
+ {
+ return monitorService.getHits().get();
+ }
+
+ public long totalTime()
+ {
+ return monitorService.totalTime();
+ }
+
+ public long average()
+ {
+ return monitorService.getHits().get() > 0 ? monitorService.getTotalTime() / monitorService.getHits().get() : 0;
+ }
+
+ public String toString()
+ {
+ return format( "%1$s hits: %2$d, avg: %3$s ms, tot: %4$s seconds", monitorService.getName(),
+ monitorService.getHits().get(),
+ new DecimalFormat( "####.###" ).format( (double) average() / 1000000 ),
+ new DecimalFormat( "####.###" ).format( (double) monitorService.getTotalTime() / 1000000000 ) );
+ }
+
+ public static void dump( String prefix )
+ {
+ for ( MonitorService monitor : MONITORS.values() )
+ {
+ if ( monitor.getName().startsWith( prefix ) )
+ {
+ LOG.info( monitor.toString() );
+ }
+ }
+ }
+
+ public static void dump()
+ {
+ dump( "" );
+ }
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/measures/MonitorService.java b/directmemory-cache/src/main/java/org/apache/directmemory/measures/MonitorService.java
new file mode 100644
index 0000000..44a677a
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/measures/MonitorService.java
@@ -0,0 +1,58 @@
+package org.apache.directmemory.measures;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+
+import java.util.concurrent.atomic.AtomicLong;
+
+public interface MonitorService
+{
+
+ long start();
+
+ long stop( long begunAt );
+
+ long hits();
+
+ long totalTime();
+
+ long average();
+
+ void dump( String prefix );
+
+ void dump();
+
+ AtomicLong getHits();
+
+ long getTotalTime();
+
+ void addToTotalTime( long time );
+
+ long getMin();
+
+ void setMin( long min );
+
+ long getMax();
+
+ void setMax( long max );
+
+ String getName();
+
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/measures/MonitorServiceImpl.java b/directmemory-cache/src/main/java/org/apache/directmemory/measures/MonitorServiceImpl.java
new file mode 100644
index 0000000..f2cc6d1
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/measures/MonitorServiceImpl.java
@@ -0,0 +1,158 @@
+package org.apache.directmemory.measures;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import static java.lang.String.format;
+
+import java.text.DecimalFormat;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MonitorServiceImpl
+ implements MonitorService
+{
+
+ private AtomicLong hits = new AtomicLong( 0 );
+
+ private long totalTime = 0;
+
+ private long min = -1;
+
+ private long max = -1;
+
+ public String name;
+
+ private static final Logger LOG = LoggerFactory.getLogger( MonitorServiceImpl.class );
+
+ //TODO: MONITORS looks like a good candidate to become a private field
+ public static final Map<String, MonitorServiceImpl> MONITORS = new HashMap<String, MonitorServiceImpl>();
+
+ public MonitorServiceImpl( String name )
+ {
+ this.name = name;
+ }
+
+ public long start()
+ {
+ return System.nanoTime();
+ }
+
+ public long stop( long begunAt )
+ {
+ hits.incrementAndGet();
+ final long lastAccessed = System.nanoTime();
+ final long elapsed = lastAccessed - begunAt;
+ totalTime += elapsed;
+ if ( elapsed > max && hits.get() > 0 )
+ {
+ max = elapsed;
+ }
+ if ( elapsed < min && hits.get() > 0 )
+ {
+ min = elapsed;
+ }
+ return elapsed;
+ }
+
+ public long hits()
+ {
+ return hits.get();
+ }
+
+ public long totalTime()
+ {
+ return totalTime;
+ }
+
+ public long average()
+ {
+ return hits.get() > 0 ? totalTime / hits.get() : 0;
+ }
+
+ public String toString()
+ {
+ return format( "%1$s hits: %2$d, avg: %3$s ms, tot: %4$s seconds", name, hits.get(),
+ new DecimalFormat( "####.###" ).format( (double) average() / 1000000 ),
+ new DecimalFormat( "####.###" ).format( (double) totalTime / 1000000000 ) );
+ }
+
+ public void dump( String prefix )
+ {
+ for ( MonitorServiceImpl monitor : MonitorServiceImpl.MONITORS.values() )
+ {
+ if ( monitor.name.startsWith( prefix ) )
+ {
+ LOG.info( monitor.toString() );
+ }
+ }
+ }
+
+ public void dump()
+ {
+ dump( "" );
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public AtomicLong getHits()
+ {
+ return hits;
+ }
+
+ public long getTotalTime()
+ {
+ return totalTime;
+ }
+
+ @Override
+ public void addToTotalTime( long time )
+ {
+ totalTime += time;
+ }
+
+ public long getMin()
+ {
+ return min;
+ }
+
+ public void setMin( long min )
+ {
+ this.min = min;
+ }
+
+ public long getMax()
+ {
+ return max;
+ }
+
+ public void setMax( long max )
+ {
+ this.max = max;
+ }
+
+
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/measures/Ram.java b/directmemory-cache/src/main/java/org/apache/directmemory/measures/Ram.java
new file mode 100644
index 0000000..2a9786e
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/measures/Ram.java
@@ -0,0 +1,26 @@
+package org.apache.directmemory.measures;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+public class Ram
+ extends Sizing
+{
+
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/measures/Sizing.java b/directmemory-cache/src/main/java/org/apache/directmemory/measures/Sizing.java
new file mode 100644
index 0000000..980a545
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/measures/Sizing.java
@@ -0,0 +1,64 @@
+package org.apache.directmemory.measures;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import static java.lang.String.format;
+
+public class Sizing
+{
+
+ private static final int KILOBYTE_UNIT = 1024;
+
+ public static int Gb( double giga )
+ {
+ return (int) giga * KILOBYTE_UNIT * KILOBYTE_UNIT * KILOBYTE_UNIT;
+ }
+
+ public static int Mb( double mega )
+ {
+ return (int) mega * KILOBYTE_UNIT * KILOBYTE_UNIT;
+ }
+
+ public static int Kb( double kilo )
+ {
+ return (int) kilo * KILOBYTE_UNIT;
+ }
+
+ public static int unlimited()
+ {
+ return -1;
+ }
+
+ public static String inKb( long bytes )
+ {
+ return format( "%(,.1fKb", (double) bytes / KILOBYTE_UNIT );
+ }
+
+ public static String inMb( long bytes )
+ {
+ return format( "%(,.1fMb", (double) bytes / KILOBYTE_UNIT / KILOBYTE_UNIT );
+ }
+
+ public static String inGb( long bytes )
+ {
+ return format( "%(,.1fGb", (double) bytes / KILOBYTE_UNIT / KILOBYTE_UNIT / KILOBYTE_UNIT );
+ }
+
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/measures/Space.java b/directmemory-cache/src/main/java/org/apache/directmemory/measures/Space.java
new file mode 100644
index 0000000..3e8e633
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/measures/Space.java
@@ -0,0 +1,26 @@
+package org.apache.directmemory.measures;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+public class Space
+ extends Sizing
+{
+
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/measures/package-info.java b/directmemory-cache/src/main/java/org/apache/directmemory/measures/package-info.java
new file mode 100644
index 0000000..003f77c
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/measures/package-info.java
@@ -0,0 +1,23 @@
+/**
+ * Fluent APIs for manipulate unit measures, such as time and space memory.
+ */
+package org.apache.directmemory.measures;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/memory/AllocationPolicy.java b/directmemory-cache/src/main/java/org/apache/directmemory/memory/AllocationPolicy.java
new file mode 100644
index 0000000..3b98043
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/memory/AllocationPolicy.java
@@ -0,0 +1,59 @@
+package org.apache.directmemory.memory;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import java.util.List;
+
+import org.apache.directmemory.memory.allocator.ByteBufferAllocator;
+
+/**
+ * Interface describing the buffer allocation policy.
+ * The implementations will be initialized by setting the list of buffers {@link #init(List)},
+ * and every allocation will call {@link #getActiveBuffer(OffHeapMemoryBuffer, int)},
+ * passing the previously (possibly null) buffer that failed to allocate and the number of the current allocation
+ *
+ * @author bperroud
+ *
+ */
+public interface AllocationPolicy
+{
+
+ /**
+ * Initialization function.
+ *
+ * @param buffers
+ */
+ void init( List<ByteBufferAllocator> allocators );
+
+ /**
+ * Returns the {@link ByteBufferAllocator} to use to allocate.
+ *
+ * @param previousAllocator : the previously used {@link ByteBufferAllocator}, or null if it's the first allocation
+ * @param allocationNumber : the number of time the allocation has already failed.
+ * @return the {@link ByteBufferAllocator} to use, or null if allocation has failed.
+ */
+ ByteBufferAllocator getActiveAllocator( ByteBufferAllocator previousAllocator, int allocationNumber );
+
+ /**
+ * Reset internal state
+ */
+ void reset();
+
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/memory/MemoryManager.java b/directmemory-cache/src/main/java/org/apache/directmemory/memory/MemoryManager.java
new file mode 100644
index 0000000..90a30ef
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/memory/MemoryManager.java
@@ -0,0 +1,90 @@
+package org.apache.directmemory.memory;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+public class MemoryManager
+{
+ private static MemoryManagerService<Object> memoryManager = new MemoryManagerServiceImpl<Object>();
+
+ private MemoryManager()
+ {
+ //static class
+ }
+
+ public static void init( int numberOfBuffers, int size )
+ {
+ memoryManager.init( numberOfBuffers, size );
+ }
+
+ public static Pointer<Object> store( byte[] payload, int expiresIn )
+ {
+ return memoryManager.store( payload, expiresIn );
+ }
+
+ public static Pointer<Object> store( byte[] payload )
+ {
+ return store( payload, 0 );
+ }
+
+ public static Pointer<Object> update( Pointer<Object> pointer, byte[] payload )
+ {
+ return memoryManager.update( pointer, payload );
+ }
+
+ public static byte[] retrieve( Pointer<Object> pointer )
+ {
+ return memoryManager.retrieve( pointer );
+ }
+
+ public static void free( Pointer<Object> pointer )
+ {
+ memoryManager.free( pointer );
+ }
+
+ public static void clear()
+ {
+ memoryManager.clear();
+ }
+
+ public static long capacity()
+ {
+ return memoryManager.capacity();
+ }
+
+ public static long collectExpired()
+ {
+ return memoryManager.collectExpired();
+ }
+
+ public static void collectLFU()
+ {
+ memoryManager.collectLFU();
+ }
+
+ public static MemoryManagerService<Object> getMemoryManager()
+ {
+ return memoryManager;
+ }
+
+ public static Pointer<Object> allocate( int size )
+ {
+ return memoryManager.allocate( Object.class, size, -1, -1 ); //add a version with expiration
+ }
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/memory/MemoryManagerService.java b/directmemory-cache/src/main/java/org/apache/directmemory/memory/MemoryManagerService.java
new file mode 100644
index 0000000..2b17db5
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/memory/MemoryManagerService.java
@@ -0,0 +1,88 @@
+package org.apache.directmemory.memory;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+public interface MemoryManagerService<V>
+{
+
+ /**
+ * Initialize the internal structure. Need to be called before the service
+ * can be used.
+ *
+ * @param numberOfBuffers
+ * : number of internal bucket
+ * @param size
+ * : size in B of internal buckets
+ */
+ void init( int numberOfBuffers, int size );
+
+ /**
+ * Store function family. Store the given payload at a certain offset in a MemoryBuffer, returning the pointer to the value.
+ *
+ * @param payload : the data to store
+ * @return the pointer to the value, or null if not enough space has been found.
+ */
+ Pointer<V> store( byte[] payload, int expiresIn );
+
+ /**
+ * Same function as {@link #store(byte[])}, but add an relative expiration delta in milliseconds
+ *
+ * @param payload : the data to store
+ * @param expiresIn : relative amount of milliseconds the data will expire
+ * @return the pointer to the value, or null if not enough space has been found.
+ */
+ Pointer<V> store( byte[] payload );
+
+ /**
+ * Same function as {@link #store(byte[])}, but add an absolute expiration date
+ * @param payload : the data to store
+ * @param expires : the absolute date the data will expire
+ * @return the pointer to the value, or null if not enough space has been found.
+ */
+ //public Pointer store(byte[] payload, Date expires);
+
+ /**
+ *
+ *
+ * Update value of a {@link Pointer
+ * @param pointer
+ * @param payload
+ * @return
+ * @throw BufferOverflowException if the size of the payload id bigger than the pointer capacity
+ */
+ Pointer<V> update( Pointer<V> pointer, byte[] payload );
+
+ byte[] retrieve( Pointer<V> pointer );
+
+ void free( Pointer<V> pointer );
+
+ void clear();
+
+ long capacity();
+
+ long used();
+
+ long collectExpired();
+
+ void collectLFU();
+
+ <T extends V> Pointer<V> allocate( Class<T> type, int size, long expiresIn, long expires );
+
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/memory/MemoryManagerServiceImpl.java b/directmemory-cache/src/main/java/org/apache/directmemory/memory/MemoryManagerServiceImpl.java
new file mode 100644
index 0000000..1a90290
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/memory/MemoryManagerServiceImpl.java
@@ -0,0 +1,387 @@
+package org.apache.directmemory.memory;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import com.google.common.base.Predicate;
+import org.apache.directmemory.measures.Ram;
+import org.apache.directmemory.memory.allocator.ByteBufferAllocator;
+import org.apache.directmemory.memory.allocator.MergingByteBufferAllocatorImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static com.google.common.collect.Iterables.filter;
+import static com.google.common.collect.Iterables.limit;
+import static com.google.common.collect.Ordering.from;
+import static java.lang.String.format;
+
+public class MemoryManagerServiceImpl<V>
+ implements MemoryManagerService<V>
+{
+
+ protected static final long NEVER_EXPIRES = 0L;
+
+ protected static Logger logger = LoggerFactory.getLogger( MemoryManager.class );
+
+ private List<ByteBufferAllocator> allocators;
+
+ private final Set<Pointer<V>> pointers = Collections.newSetFromMap( new ConcurrentHashMap<Pointer<V>, Boolean>() );
+
+ private final boolean returnNullWhenFull;
+
+ protected final AtomicLong used = new AtomicLong( 0L );
+
+ protected final AllocationPolicy allocationPolicy;
+
+ public MemoryManagerServiceImpl()
+ {
+ this( true );
+ }
+
+ public MemoryManagerServiceImpl( final boolean returnNullWhenFull )
+ {
+ this( new RoundRobinAllocationPolicy(), returnNullWhenFull );
+ }
+
+ public MemoryManagerServiceImpl( final AllocationPolicy allocationPolicy, final boolean returnNullWhenFull )
+ {
+ this.allocationPolicy = allocationPolicy;
+ this.returnNullWhenFull = returnNullWhenFull;
+ }
+
+ @Override
+ public void init( int numberOfBuffers, int size )
+ {
+
+ allocators = new ArrayList<ByteBufferAllocator>( numberOfBuffers );
+
+ for ( int i = 0; i < numberOfBuffers; i++ )
+ {
+ final ByteBufferAllocator allocator = instanciateByteBufferAllocator( i, size );
+ allocators.add( allocator );
+ }
+
+ allocationPolicy.init( allocators );
+
+ logger.info( format( "MemoryManager initialized - %d buffers, %s each", numberOfBuffers, Ram.inMb( size ) ) );
+ }
+
+
+ protected ByteBufferAllocator instanciateByteBufferAllocator( final int allocatorNumber, final int size )
+ {
+ final MergingByteBufferAllocatorImpl allocator = new MergingByteBufferAllocatorImpl( allocatorNumber, size );
+
+ // Hack to ensure the pointers are always split to keep backward compatibility.
+ allocator.setMinSizeThreshold( 0 );
+ allocator.setSizeRatioThreshold( 1.0 );
+
+ return allocator;
+ }
+
+ protected ByteBufferAllocator getAllocator( int allocatorIndex )
+ {
+ return allocators.get( allocatorIndex );
+ }
+
+ protected ByteBufferAllocator getCurrentAllocator()
+ {
+ return allocationPolicy.getActiveAllocator( null, 0 );
+ }
+
+ @Override
+ public Pointer<V> store( byte[] payload, int expiresIn )
+ {
+ Pointer<V> p = null;
+ ByteBufferAllocator allocator = null;
+ int allocationNumber = 0;
+ do
+ {
+ allocationNumber++;
+ allocator = allocationPolicy.getActiveAllocator( allocator, allocationNumber );
+ if ( allocator == null )
+ {
+ if ( returnsNullWhenFull() )
+ {
+ return null;
+ }
+ else
+ {
+ throw new BufferOverflowException();
+ }
+ }
+ final ByteBuffer buffer = allocator.allocate( payload.length );
+
+ if ( buffer == null )
+ {
+ continue;
+ }
+
+ p = instanciatePointer( buffer, allocator.getNumber(), expiresIn, NEVER_EXPIRES );
+
+ buffer.rewind();
+ buffer.put( payload );
+
+ used.addAndGet( payload.length );
+
+ }
+ while ( p == null );
+ return p;
+ }
+
+ @Override
+ public Pointer<V> store( byte[] payload )
+ {
+ return store( payload, 0 );
+ }
+
+ @Override
+ public Pointer<V> update( Pointer<V> pointer, byte[] payload )
+ {
+ free( pointer );
+ return store( payload );
+ }
+
+ @Override
+ public byte[] retrieve( final Pointer<V> pointer )
+ {
+ // check if pointer has not been freed before
+ if ( !pointers.contains( pointer ) )
+ {
+ return null;
+ }
+
+ pointer.hit();
+
+ final ByteBuffer buf = pointer.getDirectBuffer().asReadOnlyBuffer();
+ buf.rewind();
+
+ final byte[] swp = new byte[buf.limit()];
+ buf.get( swp );
+ return swp;
+ }
+
+ @Override
+ public void free( final Pointer<V> pointer )
+ {
+ if ( !pointers.remove( pointer ) )
+ {
+ // pointers has been already freed.
+ //throw new IllegalArgumentException( "This pointer " + pointer + " has already been freed" );
+ return;
+ }
+
+ getAllocator( pointer.getBufferNumber() ).free( pointer.getDirectBuffer() );
+
+ used.addAndGet( -pointer.getCapacity() );
+
+ pointer.setFree( true );
+ }
+
+ @Override
+ public void clear()
+ {
+ for ( Pointer<V> pointer : pointers )
+ {
+ pointer.setFree( true );
+ }
+ pointers.clear();
+ for ( ByteBufferAllocator allocator : allocators )
+ {
+ allocator.clear();
+ }
+ allocationPolicy.reset();
+ }
+
+ @Override
+ public long capacity()
+ {
+ long totalCapacity = 0;
+ for ( ByteBufferAllocator allocator : allocators )
+ {
+ totalCapacity += allocator.getCapacity();
+ }
+ return totalCapacity;
+ }
+
+ @Override
+ public long used()
+ {
+ return used.get();
+ }
+
+ private final Predicate<Pointer<V>> relative = new Predicate<Pointer<V>>()
+ {
+
+ @Override
+ public boolean apply( Pointer<V> input )
+ {
+ return !input.isFree() && input.isExpired();
+ }
+
+ };
+
+ private final Predicate<Pointer<V>> absolute = new Predicate<Pointer<V>>()
+ {
+
+ @Override
+ public boolean apply( Pointer<V> input )
+ {
+ return !input.isFree() && input.isExpired();
+ }
+
+ };
+
+ @Override
+ public long collectExpired()
+ {
+ int limit = 50;
+ return free( limit( filter( pointers, relative ), limit ) ) + free(
+ limit( filter( pointers, absolute ), limit ) );
+
+ }
+
+ @Override
+ public void collectLFU()
+ {
+
+ int limit = pointers.size() / 10;
+
+ Iterable<Pointer<V>> result = from( new Comparator<Pointer<V>>()
+ {
+
+ public int compare( Pointer<V> o1, Pointer<V> o2 )
+ {
+ float f1 = o1.getFrequency();
+ float f2 = o2.getFrequency();
+
+ return Float.compare( f1, f2 );
+ }
+
+ } ).sortedCopy( limit( filter( pointers, new Predicate<Pointer<V>>()
+ {
+
+ @Override
+ public boolean apply( Pointer<V> input )
+ {
+ return !input.isFree();
+ }
+
+ } ), limit ) );
+
+ free( result );
+
+ }
+
+ protected long free( Iterable<Pointer<V>> pointers )
+ {
+ long howMuch = 0;
+ for ( Pointer<V> expired : pointers )
+ {
+ howMuch += expired.getCapacity();
+ free( expired );
+ }
+ return howMuch;
+ }
+
+
+ protected List<ByteBufferAllocator> getAllocators()
+ {
+ return allocators;
+ }
+
+ @Deprecated
+ @Override
+ public <T extends V> Pointer<V> allocate( final Class<T> type, final int size, final long expiresIn,
+ final long expires )
+ {
+
+ Pointer<V> p = null;
+ ByteBufferAllocator allocator = null;
+ int allocationNumber = 0;
+ do
+ {
+ allocationNumber++;
+ allocator = allocationPolicy.getActiveAllocator( allocator, allocationNumber );
+ if ( allocator == null )
+ {
+ if ( returnsNullWhenFull() )
+ {
+ return null;
+ }
+ else
+ {
+ throw new BufferOverflowException();
+ }
+ }
+
+ final ByteBuffer buffer = allocator.allocate( size );
+
+ if ( buffer == null )
+ {
+ continue;
+ }
+
+ p = instanciatePointer( buffer, allocator.getNumber(), expiresIn, NEVER_EXPIRES );
+
+ used.addAndGet( size );
+ }
+ while ( p == null );
+
+ p.setClazz( type );
+
+ return p;
+ }
+
+ protected Pointer<V> instanciatePointer( final ByteBuffer buffer, final int allocatorIndex, final long expiresIn,
+ final long expires )
+ {
+
+ Pointer<V> p = new PointerImpl<V>();
+
+ p.setDirectBuffer( buffer );
+ p.setExpiration( expires, expiresIn );
+ p.setBufferNumber( allocatorIndex );
+ p.setFree( false );
+ p.createdNow();
+
+ pointers.add( p );
+
+ return p;
+ }
+
+ protected boolean returnsNullWhenFull()
+ {
+ return returnNullWhenFull;
+ }
+
+ public Set<Pointer<V>> getPointers()
+ {
+ return Collections.unmodifiableSet( pointers );
+ }
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/memory/Pointer.java b/directmemory-cache/src/main/java/org/apache/directmemory/memory/Pointer.java
new file mode 100644
index 0000000..8b18f2b
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/memory/Pointer.java
@@ -0,0 +1,67 @@
+package org.apache.directmemory.memory;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import java.nio.ByteBuffer;
+
+public interface Pointer<T>
+{
+
+ byte[] content();
+
+ boolean isFree();
+
+ void setFree( boolean free );
+
+ boolean isExpired();
+
+ float getFrequency();
+
+ int getCapacity();
+
+ void reset();
+
+ int getBufferNumber();
+
+ void setBufferNumber( int bufferNumber );
+
+ int getStart();
+
+ void setStart( int start );
+
+ int getEnd();
+
+ void setEnd( int end );
+
+ void hit();
+
+ Class<? extends T> getClazz();
+
+ void setClazz( Class<? extends T> clazz );
+
+ ByteBuffer getDirectBuffer();
+
+ void setDirectBuffer( ByteBuffer directBuffer );
+
+ void createdNow();
+
+ void setExpiration( long expires, long expiresIn );
+
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/memory/PointerImpl.java b/directmemory-cache/src/main/java/org/apache/directmemory/memory/PointerImpl.java
new file mode 100644
index 0000000..d94e02b
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/memory/PointerImpl.java
@@ -0,0 +1,199 @@
+package org.apache.directmemory.memory;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import static java.lang.System.currentTimeMillis;
+import static java.lang.String.format;
+
+import java.nio.ByteBuffer;
+
+public class PointerImpl<T>
+ implements Pointer<T>
+{
+ public int start;
+
+ public int end;
+
+ public long created;
+
+ public long expires;
+
+ public long expiresIn;
+
+ public long hits;
+
+ public boolean free;
+
+ public long lastHit;
+
+ public int bufferNumber;
+
+ public Class<? extends T> clazz;
+
+ public ByteBuffer directBuffer = null;
+
+ public PointerImpl()
+ {
+ }
+
+ public PointerImpl( int start, int end )
+ {
+ this.start = start;
+ this.end = end;
+ }
+
+ @Override
+ public byte[] content()
+ {
+ return null;
+ }
+
+ @Override
+ public float getFrequency()
+ {
+ return (float) ( currentTimeMillis() - created ) / hits;
+ }
+
+ @Override
+ public int getCapacity()
+ {
+ return directBuffer == null ? end - start + 1 : directBuffer.limit();
+ }
+
+ @Override
+ public String toString()
+ {
+ return format( "%s[%s, %s] %s free", getClass().getSimpleName(), start, end, ( free ? "" : "not" ) );
+ }
+
+ @Override
+ public void reset()
+ {
+ free = true;
+ created = 0;
+ lastHit = 0;
+ hits = 0;
+ expiresIn = 0;
+ clazz = null;
+ directBuffer = null;
+ }
+
+ @Override
+ public boolean isFree()
+ {
+ return free;
+ }
+
+ @Override
+ public boolean isExpired()
+ {
+ if ( expires > 0 || expiresIn > 0 )
+ {
+ return ( expiresIn + created < currentTimeMillis() );
+ }
+ return false;
+ }
+
+ @Override
+ public int getBufferNumber()
+ {
+ return bufferNumber;
+ }
+
+ @Override
+ public int getStart()
+ {
+ return start;
+ }
+
+ @Override
+ public int getEnd()
+ {
+ return end;
+ }
+
+ @Override
+ public void setStart( int start )
+ {
+ this.start = start;
+ }
+
+ @Override
+ public void hit()
+ {
+ lastHit = System.currentTimeMillis();
+ hits++;
+ }
+
+ @Override
+ public Class<? extends T> getClazz()
+ {
+ return clazz;
+ }
+
+ @Override
+ public ByteBuffer getDirectBuffer()
+ {
+ return directBuffer;
+ }
+
+ @Override
+ public void setFree( boolean free )
+ {
+ this.free = free;
+ }
+
+ @Override
+ public void setEnd( int end )
+ {
+ this.end = end;
+ }
+
+ @Override
+ public void setClazz( Class<? extends T> clazz )
+ {
+ this.clazz = clazz;
+ }
+
+ @Override
+ public void setDirectBuffer( ByteBuffer directBuffer )
+ {
+ this.directBuffer = directBuffer;
+ }
+
+ @Override
+ public void createdNow()
+ {
+ created = System.currentTimeMillis();
+ }
+
+ @Override
+ public void setBufferNumber( int bufferNumber )
+ {
+ this.bufferNumber = bufferNumber;
+ }
+
+ @Override
+ public void setExpiration( long expires, long expiresIn )
+ {
+ this.expires = expires;
+ this.expiresIn = expiresIn;
+ }
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/memory/RoundRobinAllocationPolicy.java b/directmemory-cache/src/main/java/org/apache/directmemory/memory/RoundRobinAllocationPolicy.java
new file mode 100644
index 0000000..80cf397
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/memory/RoundRobinAllocationPolicy.java
@@ -0,0 +1,107 @@
+package org.apache.directmemory.memory;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.directmemory.memory.allocator.ByteBufferAllocator;
+
+/**
+ * Round Robin allocation policy. An internal counter is incremented (modulo the size of the buffer), so each calls to
+ * {@link #getActiveBuffer(OffHeapMemoryBuffer, int)} will increment the counter and return the buffer at the index of
+ * the counter.
+ *
+ * @author bperroud
+ */
+public class RoundRobinAllocationPolicy
+ implements AllocationPolicy
+{
+
+ // Increment the counter and get the value. Need to start at -1 to have 0'index at first call.
+ private static final int BUFFERS_INDEX_INITIAL_VALUE = -1;
+
+ // All the buffers to allocate
+ private List<ByteBufferAllocator> allocators;
+
+ // Cyclic counter
+ private AtomicInteger buffersIndexCounter = new AtomicInteger( BUFFERS_INDEX_INITIAL_VALUE );
+
+ // Default max number of allocations before returning null buffer.
+ private static final int DEFAULT_MAX_ALLOCATIONS = 2;
+
+ // Current max number of allocations
+ private int maxAllocations = DEFAULT_MAX_ALLOCATIONS;
+
+ public void setMaxAllocations( final int maxAllocations )
+ {
+ this.maxAllocations = maxAllocations;
+ }
+
+ @Override
+ public void init( final List<ByteBufferAllocator> allocators )
+ {
+ this.allocators = allocators;
+ }
+
+ @Override
+ public ByteBufferAllocator getActiveAllocator( final ByteBufferAllocator previousAllocator, final int allocationNumber )
+ {
+ // If current allocation is more than the limit, return a null buffer.
+ if ( allocationNumber > maxAllocations )
+ {
+ return null;
+ }
+
+ // Thread safely increment and get the next buffer's index
+ int i = incrementAndGetBufferIndex();
+
+ final ByteBufferAllocator allocator = allocators.get( i );
+
+ return allocator;
+ }
+
+ @Override
+ public void reset()
+ {
+ // Reinitialize the counter to it's initial value
+ buffersIndexCounter.set( BUFFERS_INDEX_INITIAL_VALUE );
+ }
+
+ /**
+ * Optimistic (lock free) cyclic (modulo size of the buffer) increment of the counter
+ *
+ * @return next index
+ */
+ private int incrementAndGetBufferIndex()
+ {
+ int newIndex = 0;
+ boolean updateOk = false;
+ do
+ {
+ int currentIndex = buffersIndexCounter.get();
+ newIndex = ( currentIndex + 1 ) % allocators.size();
+ updateOk = buffersIndexCounter.compareAndSet( currentIndex, newIndex );
+ }
+ while ( !updateOk );
+ return newIndex;
+ }
+
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/memory/allocator/AbstractByteBufferAllocator.java b/directmemory-cache/src/main/java/org/apache/directmemory/memory/allocator/AbstractByteBufferAllocator.java
new file mode 100644
index 0000000..be6c328
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/memory/allocator/AbstractByteBufferAllocator.java
@@ -0,0 +1,70 @@
+package org.apache.directmemory.memory.allocator;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.ByteBuffer;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+
+public abstract class AbstractByteBufferAllocator
+ implements ByteBufferAllocator
+{
+
+ protected final Logger logger = LoggerFactory.getLogger( this.getClass() );
+
+ private final int number;
+
+ private final AtomicBoolean closed = new AtomicBoolean( false );
+
+ AbstractByteBufferAllocator( final int number )
+ {
+ this.number = number;
+ }
+
+ @Override
+ public int getNumber()
+ {
+ return number;
+ }
+
+ protected final Logger getLogger()
+ {
+ return logger;
+ }
+
+ protected final boolean isClosed()
+ {
+ return closed.get();
+ }
+
+ protected final void setClosed( final boolean closed )
+ {
+ this.closed.set( closed );
+ }
+
+ protected static Integer getHash( final ByteBuffer buffer )
+ {
+ return Integer.valueOf( System.identityHashCode( buffer ) );
+ }
+
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/memory/allocator/ByteBufferAllocator.java b/directmemory-cache/src/main/java/org/apache/directmemory/memory/allocator/ByteBufferAllocator.java
new file mode 100644
index 0000000..b959c9a
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/memory/allocator/ByteBufferAllocator.java
@@ -0,0 +1,63 @@
+package org.apache.directmemory.memory.allocator;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import java.io.Closeable;
+import java.nio.ByteBuffer;
+
+/**
+ * Interface defining interaction with {@link ByteBuffer}
+ *
+ * @since 0.6
+ */
+public interface ByteBufferAllocator
+ extends Closeable
+{
+
+ /**
+ * Returns the given {@link ByteBuffer} making it available for a future usage. Returning twice a {@link ByteBuffer} won't throw an exception.
+ * @param buffer : the {@link ByteBuffer} to return
+ */
+ void free( final ByteBuffer buffer );
+
+ /**
+ * Allocates and returns a {@link ByteBuffer} with {@link ByteBuffer#limit()} set to the given size.
+ * When the allocation fails, it returns either null or throws an {@link BufferOverflowException}, depending on the implementation.
+ * @param size : the size in byte to allocate
+ * @return a {@link ByteBuffer} of the given size, or either return null or throw an {@link BufferOverflowException} when the allocation fails.
+ */
+ ByteBuffer allocate( final int size );
+
+ /**
+ * Clear all allocated {@link ByteBuffer}, resulting in a empty and ready to deserve {@link ByteBufferAllocator}
+ */
+ void clear();
+
+ /**
+ * @return the internal total size that can be allocated
+ */
+ int getCapacity();
+
+ /**
+ * @return the internal identifier of the {@link ByteBufferAllocator}
+ */
+ int getNumber();
+
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/memory/allocator/DirectByteBufferUtils.java b/directmemory-cache/src/main/java/org/apache/directmemory/memory/allocator/DirectByteBufferUtils.java
new file mode 100644
index 0000000..6932c0c
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/memory/allocator/DirectByteBufferUtils.java
@@ -0,0 +1,61 @@
+package org.apache.directmemory.memory.allocator;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Utility class around direct {@link ByteBuffer}
+ *
+ */
+public class DirectByteBufferUtils
+{
+
+ /**
+ * Clear and release the internal content of a direct {@link ByteBuffer}.
+ * Clearing manually the content avoid waiting till the GC do his job.
+ * @param buffer : the buffer to clear
+ * @throws IllegalArgumentException
+ * @throws IllegalAccessException
+ * @throws InvocationTargetException
+ * @throws SecurityException
+ * @throws NoSuchMethodException
+ */
+ public static void destroyDirectByteBuffer( final ByteBuffer buffer )
+ throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException,
+ NoSuchMethodException
+ {
+
+ checkArgument( buffer.isDirect(), "toBeDestroyed isn't direct!" );
+
+ Method cleanerMethod = buffer.getClass().getMethod( "cleaner" );
+ cleanerMethod.setAccessible( true );
+ Object cleaner = cleanerMethod.invoke( buffer );
+ Method cleanMethod = cleaner.getClass().getMethod( "clean" );
+ cleanMethod.setAccessible( true );
+ cleanMethod.invoke( cleaner );
+
+ }
+
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/memory/allocator/FixedSizeByteBufferAllocatorImpl.java b/directmemory-cache/src/main/java/org/apache/directmemory/memory/allocator/FixedSizeByteBufferAllocatorImpl.java
new file mode 100644
index 0000000..bfe9e35
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/memory/allocator/FixedSizeByteBufferAllocatorImpl.java
@@ -0,0 +1,221 @@
+package org.apache.directmemory.memory.allocator;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+
+/**
+ * {@link ByteBufferAllocator} implementation that instantiate {@link ByteBuffer}s of fixed size, called slices.
+ *
+ * @since 0.6
+ */
+public class FixedSizeByteBufferAllocatorImpl
+ extends AbstractByteBufferAllocator
+{
+
+ // Collection that keeps track of the parent buffers (segments) where slices are allocated
+ private final List<ByteBuffer> segmentsBuffers;
+
+ // Collection that owns all slices that can be used.
+ private final Queue<ByteBuffer> freeBuffers = new ConcurrentLinkedQueue<ByteBuffer>();
+
+ // Size of each slices dividing each segments of the slab
+ private final int sliceSize;
+
+ // Total size of the current slab
+ private int totalSize;
+
+ // Tells if it returns null or throw an BufferOverflowException when the requested size is bigger than the size of the slices
+ private boolean returnNullWhenOversizingSliceSize = true;
+
+ // Tells if it returns null when no buffers are available
+ private boolean returnNullWhenNoBufferAvailable = true;
+
+ // Collection that keeps track of borrowed buffers
+ private final Map<Integer, ByteBuffer> usedSliceBuffers = new ConcurrentHashMap<Integer, ByteBuffer>();
+
+
+ /**
+ * Constructor.
+ *
+ * @param number : internal identifier of the allocator
+ * @param totalSize : the internal buffer
+ * @param sliceSize : arbitrary number of the buffer.
+ * @param numberOfSegments : number of parent {@link ByteBuffer} to allocate.
+ */
+ public FixedSizeByteBufferAllocatorImpl( final int number, final int totalSize, final int sliceSize,
+ final int numberOfSegments )
+ {
+ super( number );
+
+ this.totalSize = totalSize;
+ this.sliceSize = sliceSize;
+
+ this.segmentsBuffers = new ArrayList<ByteBuffer>( numberOfSegments );
+
+ init( numberOfSegments );
+
+ }
+
+ protected void init( final int numberOfSegments )
+ {
+ checkArgument( numberOfSegments > 0 );
+
+ // Compute the size of each segments
+ int segmentSize = totalSize / numberOfSegments;
+ // size is rounded down to a multiple of the slice size
+ segmentSize -= segmentSize % sliceSize;
+
+ for ( int i = 0; i < numberOfSegments; i++ )
+ {
+ final ByteBuffer segment = ByteBuffer.allocateDirect( segmentSize );
+ segmentsBuffers.add( segment );
+
+ for ( int j = 0; j < segment.capacity(); j += sliceSize )
+ {
+ segment.clear();
+ segment.position( j );
+ segment.limit( j + sliceSize );
+ final ByteBuffer slice = segment.slice();
+ freeBuffers.add( slice );
+ }
+ }
+ }
+
+
+ protected ByteBuffer findFreeBuffer( int capacity )
+ {
+ // ensure the requested size is not bigger than the slices' size
+ if ( capacity > sliceSize )
+ {
+ if ( returnNullWhenOversizingSliceSize )
+ {
+ return null;
+ }
+ else
+ {
+ throw new BufferOverflowException();
+ }
+ }
+ // TODO : Add capacity to wait till a given timeout for a freed buffer
+ return freeBuffers.poll();
+ }
+
+ @Override
+ public void free( final ByteBuffer byteBuffer )
+ {
+
+ checkState( !isClosed() );
+
+ if ( usedSliceBuffers.remove( getHash( byteBuffer ) ) == null )
+ {
+ return;
+ }
+
+ // Ensure the buffer belongs to this slab
+ checkArgument( byteBuffer.capacity() == sliceSize );
+
+ freeBuffers.offer( byteBuffer );
+
+ }
+
+ @Override
+ public ByteBuffer allocate( int size )
+ {
+
+ checkState( !isClosed() );
+
+ ByteBuffer allocatedByteBuffer = findFreeBuffer( size );
+
+ if ( allocatedByteBuffer == null )
+ {
+ if ( returnNullWhenNoBufferAvailable )
+ {
+ return null;
+ }
+ else
+ {
+ throw new BufferOverflowException();
+ }
+ }
+
+ // Reset buffer's state
+ allocatedByteBuffer.clear();
+ allocatedByteBuffer.limit( size );
+
+ usedSliceBuffers.put( getHash( allocatedByteBuffer ), allocatedByteBuffer );
+
+ return allocatedByteBuffer;
+
+ }
+
+ public int getSliceSize()
+ {
+ return sliceSize;
+ }
+
+ @Override
+ public void clear()
+ {
+ for ( final Map.Entry<Integer, ByteBuffer> entry : usedSliceBuffers.entrySet() )
+ {
+ freeBuffers.offer( entry.getValue() );
+ }
+ usedSliceBuffers.clear();
+ }
+
+ @Override
+ public int getCapacity()
+ {
+ return totalSize;
+ }
+
+ @Override
+ public void close()
+ {
+ checkState( !isClosed() );
+
+ setClosed( true );
+
+ clear();
+
+ for ( final ByteBuffer buffer : segmentsBuffers )
+ {
+ try
+ {
+ DirectByteBufferUtils.destroyDirectByteBuffer( buffer );
+ }
+ catch ( Exception e )
+ {
+ getLogger().warn( "Exception thrown while closing the allocator", e );
+ }
+ }
+ }
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/memory/allocator/MergingByteBufferAllocatorImpl.java b/directmemory-cache/src/main/java/org/apache/directmemory/memory/allocator/MergingByteBufferAllocatorImpl.java
new file mode 100644
index 0000000..0dc905e
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/memory/allocator/MergingByteBufferAllocatorImpl.java
@@ -0,0 +1,431 @@
+package org.apache.directmemory.memory.allocator;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import java.io.IOException;
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.SortedMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentSkipListMap;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * {@link ByteBufferAllocator} implementation with {@link ByteBuffer} merging capabilities.
+ * <p/>
+ * {@link ByteBuffer}s are wrapped into an {@link LinkedByteBuffer}, and when a {@link ByteBuffer} is freed,
+ * lookup is done to the neighbor to check if they are also free, in which case they are merged.
+ * <p/>
+ * {@link #setMinSizeThreshold(int)} gives the minimum buffer's size under which no splitting is done.
+ * {@link #setSizeRatioThreshold(double)} gives the size ratio (requested allocation / free buffer's size} under which no splitting is done
+ * <p/>
+ * The free {@link ByteBuffer} are held into a {@link NavigableMap} with keys defining the size's range : 0 -> first key (included), first key -> second key (included), ...
+ * Instead of keeping a list of {@link ByteBuffer}s sorted by capacity, {@link ByteBuffer}s in the same size's range are held in the same collection.
+ * The size's range are computed by {@link #generateFreeSizesRange(Integer)} and can be overridden.
+ *
+ * @since 0.6
+ */
+public class MergingByteBufferAllocatorImpl
+ extends AbstractByteBufferAllocator
+{
+
+ private static final double DEFAULT_SIZE_RATIO_THRESHOLD = 0.9;
+
+ private static final int DEFAULT_MIN_SIZE_THRESHOLD = 128;
+
+ // List of free pointers, with several lists of different size
+ private final NavigableMap<Integer, Collection<LinkedByteBuffer>> freePointers =
+ new ConcurrentSkipListMap<Integer, Collection<LinkedByteBuffer>>();
+
+ // Set of used pointers. The key is #getHash(ByteBuffer).
+ private final Map<Integer, LinkedByteBuffer> usedPointers = new ConcurrentHashMap<Integer, LinkedByteBuffer>();
+
+ // Lock used instead of synchronized block to guarantee consistency when manipulating list of pointers.
+ private final Lock linkedStructureManipulationLock = new ReentrantLock();
+
+ // The initial buffer, from which all the others are sliced
+ private final ByteBuffer parentBuffer;
+
+ // Allowed size ratio (requested size / buffer's size) of the returned buffer before splitting
+ private double sizeRatioThreshold = DEFAULT_SIZE_RATIO_THRESHOLD;
+
+ // Min size of the returned buffer before splitting
+ private int minSizeThreshold = DEFAULT_MIN_SIZE_THRESHOLD;
+
+ // Tells if null is returned or an BufferOverflowException is thrown when the buffer is full
+ private boolean returnNullWhenBufferIsFull = true;
+
+ /**
+ * Constructor.
+ *
+ * @param number : the internal buffer identifier
+ * @param totalSize : total size of the parent buffer.
+ */
+ public MergingByteBufferAllocatorImpl( final int number, final int totalSize )
+ {
+ super( number );
+
+ parentBuffer = ByteBuffer.allocateDirect( totalSize );
+ init();
+ }
+
+ /**
+ * Initialization function. Create an initial free {@link Pointer} mapping the whole buffer.
+ */
+ protected void init()
+ {
+ Integer totalSize = Integer.valueOf( parentBuffer.capacity() );
+
+ for ( Integer i : generateFreeSizesRange( totalSize ) )
+ {
+ freePointers.put( Integer.valueOf( i ), new LinkedHashSet<LinkedByteBuffer>() );
+ }
+
+ initFirstBuffer();
+
+ }
+
+ /**
+ * Create the first {@link LinkedByteBuffer}
+ */
+ private void initFirstBuffer()
+ {
+ parentBuffer.clear();
+ final ByteBuffer initialBuffer = parentBuffer.slice();
+ final LinkedByteBuffer initialLinkedBuffer = new LinkedByteBuffer( 0, initialBuffer, null, null );
+
+ insertLinkedBuffer( initialLinkedBuffer );
+ }
+
+
+ /**
+ * Generate free sizes' range. Sizes' range are used to try to allocate the best matching {@ByteBuffer}
+ * regarding the requested size. Instead of using a sorted structure, arbitrary size's range are computed.
+ *
+ * @param totalSize
+ * @return a list of all size's level used by the allocator.
+ */
+ protected List<Integer> generateFreeSizesRange( final Integer totalSize )
+ {
+ List<Integer> sizes = new ArrayList<Integer>();
+
+ for ( int i = minSizeThreshold; i <= totalSize; i *= 8 )
+ {
+ sizes.add( Integer.valueOf( i ) );
+ }
+
+ // If totalSize < minSizeThreshold or totalSize is not a multiple of minSizeThreshold
+ // we force adding an element to the map
+ if ( sizes.isEmpty() || !sizes.contains( totalSize ) )
+ {
+ sizes.add( totalSize );
+ }
+
+ return sizes;
+ }
+
+ @Override
+ public void free( final ByteBuffer buffer )
+ {
+
+ LinkedByteBuffer returningLinkedBuffer = usedPointers.remove( getHash( buffer ) );
+
+ if ( returningLinkedBuffer == null )
+ {
+ // Hu ? returned twice ? Not returned at the right place ?
+ throw new IllegalArgumentException( "The buffer " + buffer + " seems not to belong to this allocator" );
+ }
+
+ try
+ {
+ linkedStructureManipulationLock.lock();
+
+ if ( returningLinkedBuffer.getBefore() != null )
+ {
+ // if returningLinkedBuffer.getBefore is in the free list, it is free, then it's free and can be merged
+ if ( getFreeLinkedByteBufferCollection( returningLinkedBuffer.getBefore() ).contains(
+ returningLinkedBuffer.getBefore() ) )
+ {
+ returningLinkedBuffer = mergePointer( returningLinkedBuffer.getBefore(), returningLinkedBuffer );
+ }
+ }
+
+ if ( returningLinkedBuffer.getAfter() != null )
+ {
+ // if returningLinkedBuffer.getAfter is in the free list, it is free, it is free, then it's free and can be merged
+ if ( getFreeLinkedByteBufferCollection( returningLinkedBuffer.getAfter() ).contains(
+ returningLinkedBuffer.getAfter() ) )
+ {
+ returningLinkedBuffer = mergePointer( returningLinkedBuffer, returningLinkedBuffer.getAfter() );
+ }
+ }
+
+ insertLinkedBuffer( returningLinkedBuffer );
+ }
+ finally
+ {
+ linkedStructureManipulationLock.unlock();
+ }
+ }
+
+ @Override
+ public ByteBuffer allocate( final int size )
+ {
+
+ try
+ {
+ linkedStructureManipulationLock.lock();
+
+ final SortedMap<Integer, Collection<LinkedByteBuffer>> freeMap = freePointers.tailMap( size - 1 );
+ for ( final Map.Entry<Integer, Collection<LinkedByteBuffer>> bufferQueueEntry : freeMap.entrySet() )
+ {
+
+ Iterator<LinkedByteBuffer> linkedByteBufferIterator = bufferQueueEntry.getValue().iterator();
+
+ while ( linkedByteBufferIterator.hasNext() )
+ {
+ LinkedByteBuffer linkedBuffer = linkedByteBufferIterator.next();
+
+ if ( linkedBuffer.getBuffer().capacity() >= size )
+ {
+ // Remove this element from the collection
+ linkedByteBufferIterator.remove();
+
+ LinkedByteBuffer returnedLinkedBuffer = linkedBuffer;
+
+ // Check if splitting need to be performed
+ if ( linkedBuffer.getBuffer().capacity() > minSizeThreshold
+ && ( 1.0 * size / linkedBuffer.getBuffer().capacity() ) < sizeRatioThreshold )
+ {
+ // Split the buffer in a buffer that will be returned and another buffer reinserted in the corresponding queue.
+ parentBuffer.clear();
+ parentBuffer.position( linkedBuffer.getOffset() );
+ parentBuffer.limit( linkedBuffer.getOffset() + size );
+ final ByteBuffer newBuffer = parentBuffer.slice();
+
+ returnedLinkedBuffer =
+ new LinkedByteBuffer( linkedBuffer.getOffset(), newBuffer, linkedBuffer.getBefore(),
+ null );
+
+ if ( linkedBuffer.getBefore() != null )
+ {
+ linkedBuffer.getBefore().setAfter( returnedLinkedBuffer );
+ }
+
+ // Insert the remaining buffer into the structure
+ parentBuffer.clear();
+ parentBuffer.position( linkedBuffer.getOffset() + size );
+ parentBuffer.limit( linkedBuffer.getOffset() + linkedBuffer.getBuffer().capacity() );
+ final ByteBuffer remainingBuffer = parentBuffer.slice();
+ final LinkedByteBuffer remainingLinkedBuffer =
+ new LinkedByteBuffer( linkedBuffer.getOffset() + size, remainingBuffer,
+ returnedLinkedBuffer, linkedBuffer.getAfter() );
+
+ if ( linkedBuffer.getAfter() != null )
+ {
+ linkedBuffer.getAfter().setBefore( remainingLinkedBuffer );
+ }
+
+ returnedLinkedBuffer.setAfter( remainingLinkedBuffer );
+
+ insertLinkedBuffer( remainingLinkedBuffer );
+
+ }
+ else
+ {
+ // If the buffer is not split, set the limit accordingly
+ returnedLinkedBuffer.getBuffer().clear();
+ returnedLinkedBuffer.getBuffer().limit( size );
+ }
+
+ usedPointers.put( getHash( returnedLinkedBuffer.getBuffer() ), returnedLinkedBuffer );
+
+ return returnedLinkedBuffer.getBuffer();
+ }
+
+ }
+ }
+
+ if ( returnNullWhenBufferIsFull )
+ {
+ return null;
+ }
+ else
+ {
+ throw new BufferOverflowException();
+ }
+
+ }
+ finally
+ {
+ linkedStructureManipulationLock.unlock();
+ }
+ }
+
+ @Override
+ public void clear()
+ {
+ usedPointers.clear();
+
+ for ( final Map.Entry<Integer, Collection<LinkedByteBuffer>> bufferQueueEntry : freePointers.entrySet() )
+ {
+ bufferQueueEntry.getValue().clear();
+ }
+
+ initFirstBuffer();
+ }
+
+ private void insertLinkedBuffer( final LinkedByteBuffer linkedBuffer )
+ {
+ getFreeLinkedByteBufferCollection( linkedBuffer ).add( linkedBuffer );
+ }
+
+ private Collection<LinkedByteBuffer> getFreeLinkedByteBufferCollection( final LinkedByteBuffer linkedBuffer )
+ {
+ final Integer size = Integer.valueOf( linkedBuffer.getBuffer().capacity() - 1 );
+ final Map.Entry<Integer, Collection<LinkedByteBuffer>> bufferCollectionEntry =
+ freePointers.ceilingEntry( size );
+ return bufferCollectionEntry.getValue();
+ }
+
+ private LinkedByteBuffer mergePointer( final LinkedByteBuffer first, final LinkedByteBuffer next )
+ {
+ parentBuffer.clear();
+ parentBuffer.position( first.getOffset() );
+ parentBuffer.limit( first.getOffset() + first.getBuffer().capacity() + next.getBuffer().capacity() );
+ final ByteBuffer newByteBuffer = parentBuffer.slice();
+
+ final LinkedByteBuffer newLinkedByteBuffer =
+ new LinkedByteBuffer( first.getOffset(), newByteBuffer, first.getBefore(), next.getAfter() );
+
+ if ( first.getBefore() != null )
+ {
+ first.getBefore().setAfter( newLinkedByteBuffer );
+ }
+
+ if ( next.getAfter() != null )
+ {
+ next.getAfter().setBefore( newLinkedByteBuffer );
+ }
+
+ // Remove the two pointers from their corresponding free lists.
+ getFreeLinkedByteBufferCollection( first ).remove( first );
+ getFreeLinkedByteBufferCollection( next ).remove( next );
+
+ return newLinkedByteBuffer;
+ }
+
+ public void setSizeRatioThreshold( final double sizeRatioThreshold )
+ {
+ this.sizeRatioThreshold = sizeRatioThreshold;
+ }
+
+ public void setMinSizeThreshold( final int minSizeThreshold )
+ {
+ this.minSizeThreshold = minSizeThreshold;
+ }
+
+ public void setReturnNullWhenBufferIsFull( boolean returnNullWhenBufferIsFull )
+ {
+ this.returnNullWhenBufferIsFull = returnNullWhenBufferIsFull;
+ }
+
+ @Override
+ public int getCapacity()
+ {
+ return parentBuffer.capacity();
+ }
+
+ private static class LinkedByteBuffer
+ {
+ private final int offset;
+
+ private final ByteBuffer buffer;
+
+ private volatile LinkedByteBuffer before;
+
+ private volatile LinkedByteBuffer after;
+
+ public LinkedByteBuffer( final int offset, final ByteBuffer buffer, final LinkedByteBuffer before,
+ final LinkedByteBuffer after )
+ {
+ this.offset = offset;
+ this.buffer = buffer;
+ setBefore( before );
+ setAfter( after );
+ }
+
+ public ByteBuffer getBuffer()
+ {
+ return buffer;
+ }
+
+ public int getOffset()
+ {
+ return offset;
+ }
+
+ public LinkedByteBuffer getBefore()
+ {
+ return before;
+ }
+
+ public void setBefore( final LinkedByteBuffer before )
+ {
+ this.before = before;
+ }
+
+ public LinkedByteBuffer getAfter()
+ {
+ return after;
+ }
+
+ public void setAfter( final LinkedByteBuffer after )
+ {
+ this.after = after;
+ }
+ }
+
+ @Override
+ public void close()
+ throws IOException
+ {
+ clear();
+
+ try
+ {
+ DirectByteBufferUtils.destroyDirectByteBuffer( parentBuffer );
+ }
+ catch ( Exception e )
+ {
+ // ignore error as we are on quiet mode here
+ }
+ }
+
+
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/memory/allocator/SlabByteBufferAllocatorImpl.java b/directmemory-cache/src/main/java/org/apache/directmemory/memory/allocator/SlabByteBufferAllocatorImpl.java
new file mode 100644
index 0000000..5c4e9d8
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/memory/allocator/SlabByteBufferAllocatorImpl.java
@@ -0,0 +1,215 @@
+package org.apache.directmemory.memory.allocator;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import java.io.IOException;
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
+import java.util.Collection;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.concurrent.ConcurrentSkipListMap;
+
+
+/**
+ * {@link ByteBufferAllocator} implementation that uses {@link FixedSizeByteBufferAllocatorImpl}
+ * of different size to allocate best matching's size {@link ByteBuffer}
+ *
+ * @since 0.6
+ */
+public class SlabByteBufferAllocatorImpl
+ extends AbstractByteBufferAllocator
+{
+
+ // Tells if it returns null when no buffers are available
+ private boolean returnNullWhenNoBufferAvailable = true;
+
+ // Internal slabs sorted by sliceSize
+ private final NavigableMap<Integer, FixedSizeByteBufferAllocatorImpl> slabs =
+ new ConcurrentSkipListMap<Integer, FixedSizeByteBufferAllocatorImpl>();
+
+ // Tells if it is allowed to look in a bigger slab to perform the request.
+ private final boolean allowAllocationToBiggerSlab;
+
+ /**
+ * Constructor.
+ *
+ * @param number : internal allocator identifier
+ * @param slabs : {@link FixedSizeByteBufferAllocatorImpl} to use for allocation
+ * @param allowAllocationToBiggerSlab : tells if it is allowed to look in a bigger slab to perform the request.
+ */
+ public SlabByteBufferAllocatorImpl( final int number, final Collection<FixedSizeByteBufferAllocatorImpl> slabs,
+ final boolean allowAllocationToBiggerSlab )
+ {
+ super( number );
+
+ this.allowAllocationToBiggerSlab = allowAllocationToBiggerSlab;
+
+ for ( FixedSizeByteBufferAllocatorImpl slab : slabs )
+ {
+ this.slabs.put( slab.getSliceSize(), slab );
+ }
+
+ }
+
+
+ /**
+ * @param size
+ * @return the slab that best match the given size
+ */
+ private FixedSizeByteBufferAllocatorImpl getSlabThatMatchTheSize( final int size )
+ {
+ // Find the slab that can carry the wanted size. -1 is used because higherEntry returns a strictly higher entry.
+ final Map.Entry<Integer, FixedSizeByteBufferAllocatorImpl> entry = slabs.higherEntry( size - 1 );
+
+ if ( entry != null )
+ {
+ return entry.getValue();
+ }
+
+ // If an entry has not been found, this means that no slabs has bigger enough slices to allocate the given size
+ return null;
+ }
+
+ @Override
+ public void free( final ByteBuffer byteBuffer )
+ {
+
+ final FixedSizeByteBufferAllocatorImpl slab = getSlabThatMatchTheSize( byteBuffer.capacity() );
+
+ if ( slab == null )
+ {
+ // Hu ? where this bytebuffer come from ??
+ return;
+ }
+
+ slab.free( byteBuffer );
+
+ }
+
+ @Override
+ public ByteBuffer allocate( final int size )
+ {
+
+ final FixedSizeByteBufferAllocatorImpl slab = getSlabThatMatchTheSize( size );
+
+ if ( slab == null )
+ {
+ // unable to store such big objects
+ if ( returnNullWhenNoBufferAvailable )
+ {
+ return null;
+ }
+ else
+ {
+ throw new BufferOverflowException();
+ }
+ }
+
+ // Try to allocate the given size
+ final ByteBuffer byteBuffer = slab.allocate( size );
+
+ // If allocation succeed, return the buffer
+ if ( byteBuffer != null )
+ {
+ return byteBuffer;
+ }
+
+ // Otherwise we have the option to allow in a bigger slab.
+ if ( !allowAllocationToBiggerSlab )
+ {
+ if ( returnNullWhenNoBufferAvailable )
+ {
+ return null;
+ }
+ else
+ {
+ throw new BufferOverflowException();
+ }
+ }
+ else
+ {
+ // We can try to allocate to a bigger slab.
+ // size + 1 here because getSlabThatMatchTheSize do a size -1 and thus will return the same slab
+ final int biggerSize = slab.getSliceSize() + 1;
+ final FixedSizeByteBufferAllocatorImpl biggerSlab = getSlabThatMatchTheSize( biggerSize );
+ if ( biggerSlab == null )
+ {
+ // We were already trying to allocate in the biggest slab
+ if ( returnNullWhenNoBufferAvailable )
+ {
+ return null;
+ }
+ else
+ {
+ throw new BufferOverflowException();
+ }
+ }
+
+ final ByteBuffer secondByteBuffer = biggerSlab.allocate( size );
+
+ if ( secondByteBuffer == null )
+ {
+ if ( returnNullWhenNoBufferAvailable )
+ {
+ return null;
+ }
+ else
+ {
+ throw new BufferOverflowException();
+ }
+ }
+ else
+ {
+ return secondByteBuffer;
+ }
+ }
+ }
+
+ @Override
+ public void clear()
+ {
+ for ( final Map.Entry<Integer, FixedSizeByteBufferAllocatorImpl> entry : slabs.entrySet() )
+ {
+ entry.getValue().clear();
+ }
+ }
+
+ @Override
+ public int getCapacity()
+ {
+ int totalSize = 0;
+ for ( final Map.Entry<Integer, FixedSizeByteBufferAllocatorImpl> entry : slabs.entrySet() )
+ {
+ totalSize += entry.getValue().getCapacity();
+ }
+ return totalSize;
+ }
+
+ @Override
+ public void close()
+ throws IOException
+ {
+ for ( final Map.Entry<Integer, FixedSizeByteBufferAllocatorImpl> entry : slabs.entrySet() )
+ {
+ entry.getValue().close();
+ }
+ }
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/memory/package-info.java b/directmemory-cache/src/main/java/org/apache/directmemory/memory/package-info.java
new file mode 100644
index 0000000..750e967
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/memory/package-info.java
@@ -0,0 +1,23 @@
+/**
+ * Memory management implementation.
+ */
+package org.apache.directmemory.memory;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/package-info.java b/directmemory-cache/src/main/java/org/apache/directmemory/package-info.java
new file mode 100644
index 0000000..41f42c5
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/package-info.java
@@ -0,0 +1,23 @@
+/**
+ * Apache DirectMemory cache APIs.
+ */
+package org.apache.directmemory;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/serialization/Serializer.java b/directmemory-cache/src/main/java/org/apache/directmemory/serialization/Serializer.java
new file mode 100644
index 0000000..5a4d9e9
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/serialization/Serializer.java
@@ -0,0 +1,33 @@
+package org.apache.directmemory.serialization;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import java.io.IOException;
+
+public interface Serializer
+{
+
+ <T> byte[] serialize( T obj )
+ throws IOException;
+
+ <T> T deserialize( byte[] source, Class<T> clazz )
+ throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException;
+
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/serialization/SerializerFactory.java b/directmemory-cache/src/main/java/org/apache/directmemory/serialization/SerializerFactory.java
new file mode 100644
index 0000000..9758497
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/serialization/SerializerFactory.java
@@ -0,0 +1,122 @@
+package org.apache.directmemory.serialization;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import static java.lang.String.format;
+
+import java.util.Iterator;
+
+import static java.util.ServiceLoader.load;
+
+public final class SerializerFactory
+{
+
+ public static Serializer createNewSerializer()
+ {
+ return createNewSerializer( SerializerFactory.class.getClassLoader() );
+ }
+
+ public static Serializer createNewSerializer( ClassLoader classLoader )
+ {
+ Iterator<Serializer> serializers = load( Serializer.class, classLoader ).iterator();
+
+ // iterate over all found services
+ while ( serializers.hasNext() )
+ {
+ // try getting the current service and return
+ try
+ {
+ return serializers.next();
+ }
+ catch ( Throwable t )
+ {
+ // just ignore, skip and try getting the next
+ }
+ }
+
+ return new StandardSerializer();
+ }
+
+ public static <S extends Serializer> S createNewSerializer( Class<S> serializer )
+ throws SerializerNotFoundException
+ {
+ Iterator<Serializer> serializers = load( Serializer.class, serializer.getClassLoader() ).iterator();
+
+ // iterate over all found services
+ while ( serializers.hasNext() )
+ {
+ // try getting the current service and return
+ try
+ {
+ Serializer next = serializers.next();
+ if ( serializer.isInstance( next ) )
+ {
+ return serializer.cast( next );
+ }
+ }
+ catch ( Throwable t )
+ {
+ // just ignore, skip and try getting the next
+ }
+ }
+
+ throw new SerializerNotFoundException( serializer );
+ }
+
+ public static Serializer createNewSerializer( String serializerClassName )
+ throws SerializerNotFoundException
+ {
+ return createNewSerializer( serializerClassName, SerializerFactory.class.getClassLoader() );
+ }
+
+ public static Serializer createNewSerializer( String serializerClassName, ClassLoader classLoader )
+ throws SerializerNotFoundException
+ {
+ Class<?> anonSerializerClass;
+ try
+ {
+ anonSerializerClass = classLoader.loadClass( serializerClassName );
+ }
+ catch ( ClassNotFoundException e )
+ {
+ throw new SerializerNotFoundException( serializerClassName );
+ }
+
+ if ( Serializer.class.isAssignableFrom( anonSerializerClass ) )
+ {
+ @SuppressWarnings( "unchecked" ) // the assignment is guarded by the previous check
+ Class<? extends Serializer> serializerClass = (Class<? extends Serializer>) anonSerializerClass;
+
+ return createNewSerializer( serializerClass );
+ }
+
+ throw new IllegalArgumentException( format( "Class %s is not a valid Serializer type",
+ anonSerializerClass.getName() ) );
+ }
+
+ /**
+ * Hidden constructor, this class cannot be instantiated
+ */
+ private SerializerFactory()
+ {
+ // do nothing
+ }
+
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/serialization/SerializerNotFoundException.java b/directmemory-cache/src/main/java/org/apache/directmemory/serialization/SerializerNotFoundException.java
new file mode 100644
index 0000000..8cefbbd
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/serialization/SerializerNotFoundException.java
@@ -0,0 +1,43 @@
+package org.apache.directmemory.serialization;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import static java.lang.String.format;
+
+public final class SerializerNotFoundException
+ extends Exception
+{
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 5095679349348496962L;
+
+ public SerializerNotFoundException( String serializerClassName )
+ {
+ super( format( "Serializer of type '%s' has not been found in the current ClassLoader", serializerClassName ) );
+ }
+
+ public SerializerNotFoundException( Class<?> serializerType )
+ {
+ this( serializerType.getName() );
+ }
+
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/serialization/StandardSerializer.java b/directmemory-cache/src/main/java/org/apache/directmemory/serialization/StandardSerializer.java
new file mode 100644
index 0000000..7117a47
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/serialization/StandardSerializer.java
@@ -0,0 +1,75 @@
+package org.apache.directmemory.serialization;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+
+public final class StandardSerializer
+ implements Serializer
+{
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public <T> byte[] serialize( T obj )
+ throws IOException
+ {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream( baos );
+ oos.writeObject( obj );
+ oos.flush();
+ oos.close();
+ return baos.toByteArray();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public <T> T deserialize( byte[] source, final Class<T> clazz )
+ throws IOException, ClassNotFoundException
+ {
+ ByteArrayInputStream bis = new ByteArrayInputStream( source );
+ ObjectInputStream ois = new ObjectInputStream( bis )
+ {
+
+ @Override
+ protected Class<?> resolveClass( ObjectStreamClass objectStreamClass )
+ throws IOException, ClassNotFoundException
+ {
+ ClassLoader classLoader = clazz.getClassLoader();
+ return classLoader != null
+ ? classLoader.loadClass( objectStreamClass.getName() )
+ : Class.forName( objectStreamClass.getName() );
+ }
+
+ };
+ T obj = clazz.cast( ois.readObject() );
+ ois.close();
+ return obj;
+ }
+
+}
diff --git a/directmemory-cache/src/main/java/org/apache/directmemory/serialization/package-info.java b/directmemory-cache/src/main/java/org/apache/directmemory/serialization/package-info.java
new file mode 100644
index 0000000..82871b7
--- /dev/null
+++ b/directmemory-cache/src/main/java/org/apache/directmemory/serialization/package-info.java
@@ -0,0 +1,23 @@
+/**
+ * Objects Serializer APIs.
+ */
+package org.apache.directmemory.serialization;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
diff --git a/directmemory-cache/src/main/resources/META-INF/services/org.apache.directmemory.serialization.Serializer b/directmemory-cache/src/main/resources/META-INF/services/org.apache.directmemory.serialization.Serializer
new file mode 100644
index 0000000..8744225
--- /dev/null
+++ b/directmemory-cache/src/main/resources/META-INF/services/org.apache.directmemory.serialization.Serializer
@@ -0,0 +1,18 @@
+ # Licensed to the Apache Software Foundation (ASF) under one
+ # or more contributor license agreements. See the NOTICE file
+ # distributed with this work for additional information
+ # regarding copyright ownership. The ASF licenses this file
+ # to you 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.
+
+org.apache.directmemory.serialization.StandardSerializer
diff --git a/directmemory-cache/src/test/java/org/apache/directmemory/cache/CacheConcurrentTest.java b/directmemory-cache/src/test/java/org/apache/directmemory/cache/CacheConcurrentTest.java
new file mode 100644
index 0000000..4bd0a2c
--- /dev/null
+++ b/directmemory-cache/src/test/java/org/apache/directmemory/cache/CacheConcurrentTest.java
@@ -0,0 +1,257 @@
+package org.apache.directmemory.cache;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import com.carrotsearch.junitbenchmarks.AbstractBenchmark;
+import com.carrotsearch.junitbenchmarks.BenchmarkOptions;
+import com.carrotsearch.junitbenchmarks.annotation.AxisRange;
+import com.carrotsearch.junitbenchmarks.annotation.BenchmarkHistoryChart;
+import com.carrotsearch.junitbenchmarks.annotation.BenchmarkMethodChart;
+import com.carrotsearch.junitbenchmarks.annotation.LabelType;
+import org.apache.directmemory.cache.Cache;
+import org.apache.directmemory.measures.Monitor;
+import org.apache.directmemory.measures.Ram;
+import org.apache.directmemory.memory.MemoryManager;
+import org.apache.directmemory.memory.Pointer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@AxisRange( min = 0, max = 1 )
+@BenchmarkMethodChart()
+@BenchmarkHistoryChart( labelWith = LabelType.CUSTOM_KEY, maxRuns = 5 )
+@Ignore
+public class CacheConcurrentTest
+ extends AbstractBenchmark
+{
+
+ private final static int entries = 100000;
+
+ public static AtomicInteger count = new AtomicInteger();
+
+ private static AtomicInteger got = new AtomicInteger();
+
+ private static AtomicInteger missed = new AtomicInteger();
+
+ private static AtomicInteger good = new AtomicInteger();
+
+ private static AtomicInteger bad = new AtomicInteger();
+
+ private static AtomicInteger read = new AtomicInteger();
+
+ private static AtomicInteger disposals = new AtomicInteger();
+
+ @BenchmarkOptions( benchmarkRounds = 10000, warmupRounds = 0, concurrency = 1000 )
+ @Test
+ public void store()
+ {
+ final String key = "test-" + count.incrementAndGet();
+ put( key );
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 500, warmupRounds = 0, concurrency = 10 )
+ @Test
+ public void storeSomeWithExpiry()
+ {
+ final String key = "test-" + count.incrementAndGet();
+ putWithExpiry( key );
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 1000000, warmupRounds = 0, concurrency = 100 )
+ @Test
+ public void retrieveCatchThemAll()
+ {
+ String key = "test-" + ( rndGen.nextInt( entries ) + 1 );
+ get( key );
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 1000000, warmupRounds = 0, concurrency = 100 )
+ @Test
+ public void retrieveCatchHalfOfThem()
+ {
+ String key = "test-" + ( rndGen.nextInt( entries * 2 ) + 1 );
+ get( key );
+ }
+
+ private void get( String key )
+ {
+ Pointer<Object> p = Cache.getPointer( key );
+ @SuppressWarnings( "unused" ) byte[] check = Cache.retrieveByteArray( key );
+ read.incrementAndGet();
+ if ( p != null )
+ {
+ got.incrementAndGet();
+ byte[] payload = MemoryManager.retrieve( p );
+ if ( ( new String( payload ) ).startsWith( key ) )
+ {
+ good.incrementAndGet();
+ }
+ else
+ {
+ bad.incrementAndGet();
+ }
+ }
+ else
+ {
+ missed.incrementAndGet();
+ }
+ }
+
+ private void put( String key )
+ {
+ final StringBuilder bldr = new StringBuilder();
+ for ( int i = 0; i < 100; i++ )
+ {
+ bldr.append( key );
+ }
+ Cache.putByteArray( key, bldr.toString().getBytes() );
+ }
+
+ private void putWithExpiry( String key )
+ {
+ final StringBuilder bldr = new StringBuilder();
+ for ( int i = 0; i < 100; i++ )
+ {
+ bldr.append( key );
+ }
+ Cache.putByteArray( key, bldr.toString().getBytes(), rndGen.nextInt( 2000 ) );
+ }
+
+
+ @BenchmarkOptions( benchmarkRounds = 50000, warmupRounds = 0, concurrency = 10 )
+ @Test
+ public void write1Read8AndSomeDisposal()
+ {
+ String key = "test-" + ( rndGen.nextInt( entries * 2 ) + 1 );
+
+ int what = rndGen.nextInt( 10 );
+
+ switch ( what )
+ {
+ case 0:
+ put( key );
+ break;
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ get( key );
+ break;
+ default:
+ final int rndVal = rndGen.nextInt( 1000 );
+ if ( rndVal > 995 )
+ {
+ disposals.incrementAndGet();
+ final long start = System.currentTimeMillis();
+ long howMany = MemoryManager.collectExpired();
+ final long end = System.currentTimeMillis();
+ logger.info( "" + howMany + " disposed in " + ( end - start ) + " milliseconds" );
+ }
+ }
+
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 1000000, warmupRounds = 0, concurrency = 10 )
+ @Test
+ public void write3Read7()
+ {
+ String key = "test-" + ( rndGen.nextInt( entries * 2 ) + 1 );
+
+ int what = rndGen.nextInt( 10 );
+
+ switch ( what )
+ {
+ case 0:
+ case 1:
+ case 2:
+ put( key );
+ break;
+ default:
+ get( key );
+ break;
+ }
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 1000000, warmupRounds = 0, concurrency = 10 )
+ @Test
+ public void write1Read9()
+ {
+ String key = "test-" + ( rndGen.nextInt( entries * 2 ) + 1 );
+
+ int what = rndGen.nextInt( 10 );
+
+ switch ( what )
+ {
+ case 0:
+ put( key );
+ break;
+ default:
+ get( key );
+ break;
+
+ }
+
+ }
+
+ Random rndGen = new Random();
+
+ private static Logger logger = LoggerFactory.getLogger( CacheConcurrentTest.class );
+
+ @BeforeClass
+ public static void init()
+ {
+ Cache.init( 1, Ram.Mb( 512 ) );
+ Cache.dump();
+ }
+
+ @AfterClass
+ public static void dump()
+ {
+
+ Cache.dump();
+ Monitor.dump( "cache" );
+
+ logger.info( "************************************************" );
+ logger.info( "entries: " + entries );
+ logger.info( "inserted: " + Cache.entries() );
+ logger.info( "reads: " + read );
+ logger.info( "count: " + count );
+ logger.info( "got: " + got );
+ logger.info( "missed: " + missed );
+ logger.info( "good: " + good );
+ logger.info( "bad: " + bad );
+ logger.info( "disposals: " + disposals );
+ logger.info( "************************************************" );
+ }
+
+}
+
+
+
diff --git a/directmemory-cache/src/test/java/org/apache/directmemory/cache/CacheLightConcurrentTest.java b/directmemory-cache/src/test/java/org/apache/directmemory/cache/CacheLightConcurrentTest.java
new file mode 100644
index 0000000..5ea5899
--- /dev/null
+++ b/directmemory-cache/src/test/java/org/apache/directmemory/cache/CacheLightConcurrentTest.java
@@ -0,0 +1,268 @@
+package org.apache.directmemory.cache;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.directmemory.measures.Every;
+import org.apache.directmemory.measures.Monitor;
+import org.apache.directmemory.measures.Ram;
+import org.apache.directmemory.memory.MemoryManager;
+import org.apache.directmemory.memory.Pointer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.carrotsearch.junitbenchmarks.AbstractBenchmark;
+import com.carrotsearch.junitbenchmarks.BenchmarkOptions;
+import com.carrotsearch.junitbenchmarks.annotation.AxisRange;
+import com.carrotsearch.junitbenchmarks.annotation.BenchmarkHistoryChart;
+import com.carrotsearch.junitbenchmarks.annotation.BenchmarkMethodChart;
+import com.carrotsearch.junitbenchmarks.annotation.LabelType;
+
+@AxisRange( min = 0, max = 1 )
+@BenchmarkMethodChart()
+@BenchmarkHistoryChart( labelWith = LabelType.CUSTOM_KEY, maxRuns = 5 )
+
+@Ignore
+public class CacheLightConcurrentTest
+ extends AbstractBenchmark
+{
+
+ private final static int entries = 10000;
+
+ public static AtomicInteger count = new AtomicInteger();
+
+ private static AtomicInteger got = new AtomicInteger();
+
+ private static AtomicInteger missed = new AtomicInteger();
+
+ private static AtomicInteger good = new AtomicInteger();
+
+ private static AtomicInteger bad = new AtomicInteger();
+
+ private static AtomicInteger read = new AtomicInteger();
+
+ private static AtomicInteger disposals = new AtomicInteger();
+
+ @BenchmarkOptions( benchmarkRounds = 10000, warmupRounds = 0, concurrency = 100 )
+ @Test
+ public void store()
+ {
+ final String key = "test-" + count.incrementAndGet();
+ put( key );
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 50, warmupRounds = 0, concurrency = 10 )
+ @Test
+ public void storeSomeWithExpiry()
+ {
+ final String key = "test-" + count.incrementAndGet();
+ putWithExpiry( key );
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 100000, warmupRounds = 0, concurrency = 100 )
+ @Test
+ public void retrieveCatchThemAll()
+ {
+ String key = "test-" + ( rndGen.nextInt( entries ) + 1 );
+ getAndRetrieve( key );
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 100000, warmupRounds = 0, concurrency = 100 )
+ @Test
+ public void retrieveCatchHalfOfThem()
+ {
+ String key = "test-" + ( rndGen.nextInt( entries * 2 ) + 1 );
+ getAndRetrieve( key );
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 1, warmupRounds = 0, concurrency = 1 )
+ @Test
+ public void LFUEviction()
+ throws Exception
+ {
+ Cache.collectAll();
+ }
+
+ private void getAndRetrieve( String key )
+ {
+ Pointer<Object> p = Cache.getPointer( key );
+ @SuppressWarnings( "unused" ) byte[] check = Cache.retrieveByteArray( key );
+ read.incrementAndGet();
+ if ( p != null )
+ {
+ got.incrementAndGet();
+ byte[] payload = MemoryManager.retrieve( p );
+ if ( ( new String( payload ) ).startsWith( key ) )
+ {
+ good.incrementAndGet();
+ }
+ else
+ {
+ bad.incrementAndGet();
+ }
+ }
+ else
+ {
+ missed.incrementAndGet();
+ }
+ }
+
+ private void put( String key )
+ {
+ final StringBuilder bldr = new StringBuilder();
+ for ( int i = 0; i < 100; i++ )
+ {
+ bldr.append( key );
+ }
+ Cache.putByteArray( key, bldr.toString().getBytes() );
+ }
+
+ private void putWithExpiry( String key )
+ {
+ final StringBuilder bldr = new StringBuilder();
+ for ( int i = 0; i < 100; i++ )
+ {
+ bldr.append( key );
+ }
+ Cache.putByteArray( key, bldr.toString().getBytes(), rndGen.nextInt( 2000 ) );
+ }
+
+
+ @BenchmarkOptions( benchmarkRounds = 5000, warmupRounds = 0, concurrency = 10 )
+ @Test
+ public void write1Read8AndSomeDisposal()
+ {
+ String key = "test-" + ( rndGen.nextInt( entries * 2 ) + 1 );
+
+ int what = rndGen.nextInt( 10 );
+
+ switch ( what )
+ {
+ case 0:
+ put( key );
+ break;
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ getAndRetrieve( key );
+ break;
+ default:
+ final int rndVal = rndGen.nextInt( 100 );
+ if ( rndVal > 98 )
+ {
+ disposals.incrementAndGet();
+ final long start = System.currentTimeMillis();
+ long howMany = MemoryManager.collectExpired();
+ final long end = System.currentTimeMillis();
+ logger.info( "" + howMany + " disposed in " + ( end - start ) + " milliseconds" );
+ }
+ }
+
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 100000, warmupRounds = 0, concurrency = 10 )
+ @Test
+ public void write3Read7()
+ {
+ String key = "test-" + ( rndGen.nextInt( entries * 2 ) + 1 );
+
+ int what = rndGen.nextInt( 10 );
+
+ switch ( what )
+ {
+ case 0:
+ case 1:
+ case 2:
+ put( key );
+ break;
+ default:
+ getAndRetrieve( key );
+ break;
+ }
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 100000, warmupRounds = 0, concurrency = 10 )
+ @Test
+ public void write1Read9()
+ {
+ String key = "test-" + ( rndGen.nextInt( entries * 2 ) + 1 );
+
+ int what = rndGen.nextInt( 10 );
+
+ switch ( what )
+ {
+ case 0:
+ put( key );
+ break;
+ default:
+ getAndRetrieve( key );
+ break;
+
+ }
+
+ }
+
+ Random rndGen = new Random();
+
+ private static Logger logger = LoggerFactory.getLogger( CacheLightConcurrentTest.class );
+
+ @BeforeClass
+ public static void init()
+ {
+ Cache.init( 1, Ram.Mb( 128 ) );
+ Cache.scheduleDisposalEvery( Every.seconds( 1 ) );
+ Cache.dump();
+ }
+
+ @AfterClass
+ public static void dump()
+ {
+
+ Cache.dump();
+ Monitor.dump( "cache" );
+
+ logger.info( "************************************************" );
+ logger.info( "entries: " + entries );
+ logger.info( "inserted: " + Cache.entries() );
+ logger.info( "reads: " + read );
+ logger.info( "count: " + count );
+ logger.info( "got: " + got );
+ logger.info( "missed: " + missed );
+ logger.info( "good: " + good );
+ logger.info( "bad: " + bad );
+ logger.info( "disposals: " + disposals );
+ logger.info( "************************************************" );
+ }
+
+}
+
+
+
diff --git a/directmemory-cache/src/test/java/org/apache/directmemory/cache/CacheServiceImplTest.java b/directmemory-cache/src/test/java/org/apache/directmemory/cache/CacheServiceImplTest.java
new file mode 100644
index 0000000..827b18d
--- /dev/null
+++ b/directmemory-cache/src/test/java/org/apache/directmemory/cache/CacheServiceImplTest.java
@@ -0,0 +1,125 @@
+package org.apache.directmemory.cache;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import org.apache.directmemory.DirectMemory;
+import org.apache.directmemory.measures.Ram;
+import org.apache.directmemory.memory.AllocationPolicy;
+import org.apache.directmemory.memory.MemoryManagerService;
+import org.apache.directmemory.memory.MemoryManagerServiceImpl;
+import org.apache.directmemory.memory.Pointer;
+import org.apache.directmemory.memory.RoundRobinAllocationPolicy;
+import org.junit.Test;
+
+import java.io.Serializable;
+
+import static org.junit.Assert.*;
+
+public class CacheServiceImplTest
+{
+
+ @Test
+ public void testOffHeapExceedMemoryReturnNullWhenTrue()
+ {
+ AllocationPolicy allocationPolicy = new RoundRobinAllocationPolicy();
+ MemoryManagerService<byte[]> memoryManager = new MemoryManagerServiceImpl<byte[]>( allocationPolicy, true );
+ CacheService<Integer, byte[]> cache =
+ new DirectMemory<Integer, byte[]>().setMemoryManager( memoryManager ).setNumberOfBuffers( 1 ).setSize(
+ Ram.Mb( 1 ) ).newCacheService();
+
+ for ( int i = 0; i < 1000; i++ )
+ {
+ Pointer<byte[]> pointer = cache.put( i, new byte[1024] );
+ if ( ( i % 100 ) == 0 )
+ {
+ System.out.println( pointer );
+ }
+ }
+ assertTrue( "This test ensures that no unexpected errors/behaviours occurs when heap space is full", true );
+
+ }
+
+ private static class MyBean
+ implements Serializable
+ {
+ private static final long serialVersionUID = -8865690921195047235L;
+
+ private String name;
+
+ /**
+ * @return the name
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * @param name the name to set
+ */
+ public void setName( String name )
+ {
+ this.name = name;
+ }
+ }
+
+ @Test
+ public void testEntryIsNoMoreAvailableAfterExpiry()
+ throws InterruptedException
+ {
+ AllocationPolicy allocationPolicy = new RoundRobinAllocationPolicy();
+ MemoryManagerService<MyBean> memoryManager = new MemoryManagerServiceImpl<MyBean>( allocationPolicy, true );
+ CacheService<Integer, MyBean> cache =
+ new DirectMemory<Integer, MyBean>().setMemoryManager( memoryManager ).setNumberOfBuffers( 1 ).setSize(
+ Ram.Mb( 1 ) ).newCacheService();
+ /*
+ * let the scan run every 10s
+ */
+ cache.scheduleDisposalEvery( 3 * 1000 );
+ /*
+ * entry should be expired but not freed after 1s in the cache
+ */
+ MyBean originalEntry = new MyBean();
+ originalEntry.setName( "the name" );
+ cache.put( 1, originalEntry, 1 * 1000 );
+ Pointer<MyBean> pointer = cache.getPointer( 1 );
+ assertNotNull( pointer );
+ assertFalse( pointer.isExpired() );
+ assertFalse( pointer.isFree() );
+ /*
+ * wait for 2s to be sure the entry has been expired
+ */
+ Thread.sleep( 2000 );
+ pointer = cache.getPointer( 1 );
+ assertNotNull( pointer );
+ assertTrue( pointer.isExpired() );
+ assertFalse( pointer.isFree() );
+ /*
+ * wait for 11s to be sure the entry has been evicted
+ */
+ Thread.sleep( 4000 );
+ pointer = cache.getPointer( 1 );
+ assertNotNull( pointer );
+ assertTrue( pointer.isExpired() );
+ assertTrue( pointer.isFree() );
+ }
+
+
+}
diff --git a/directmemory-cache/src/test/java/org/apache/directmemory/cache/TestCachePlusSerialization.java b/directmemory-cache/src/test/java/org/apache/directmemory/cache/TestCachePlusSerialization.java
new file mode 100644
index 0000000..5c4005a
--- /dev/null
+++ b/directmemory-cache/src/test/java/org/apache/directmemory/cache/TestCachePlusSerialization.java
@@ -0,0 +1,77 @@
+package org.apache.directmemory.cache;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import com.carrotsearch.junitbenchmarks.AbstractBenchmark;
+import com.carrotsearch.junitbenchmarks.BenchmarkOptions;
+import org.apache.directmemory.cache.Cache;
+import org.apache.directmemory.measures.Monitor;
+import org.apache.directmemory.measures.Ram;
+import org.apache.directmemory.misc.DummyPojo;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+
+
+@Ignore
+public class TestCachePlusSerialization
+ extends AbstractBenchmark
+{
+
+ private static Logger logger = LoggerFactory.getLogger( TestCachePlusSerialization.class );
+
+ Random rnd = new Random();
+
+ @BeforeClass
+ public static void init()
+ {
+ logger.info( "test started" );
+ Cache.init( 1, Ram.Mb( 100 ) );
+ }
+
+ @AfterClass
+ public static void end()
+ {
+ Cache.dump();
+ Monitor.dump();
+ logger.info( "test ended" );
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 50000, warmupRounds = 0, concurrency = 1 )
+ @Test
+ public void basicBench()
+ {
+
+ DummyPojo d = new DummyPojo( "test-" + rnd.nextInt( 100000 ), 1024 + rnd.nextInt( 1024 ) );
+ Cache.put( d.name, d );
+ DummyPojo d2 = (DummyPojo) Cache.retrieve( d.name );
+
+ assertEquals( d.name, d2.name );
+
+ }
+
+}
diff --git a/directmemory-cache/src/test/java/org/apache/directmemory/memory/AbstractMemoryManagerServiceTest.java b/directmemory-cache/src/test/java/org/apache/directmemory/memory/AbstractMemoryManagerServiceTest.java
new file mode 100644
index 0000000..7d9230c
--- /dev/null
+++ b/directmemory-cache/src/test/java/org/apache/directmemory/memory/AbstractMemoryManagerServiceTest.java
@@ -0,0 +1,407 @@
+package org.apache.directmemory.memory;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import junit.framework.Assert;
+
+import org.apache.directmemory.memory.MemoryManagerService;
+import org.apache.directmemory.memory.MemoryTestUtils;
+import org.apache.directmemory.memory.Pointer;
+import org.junit.Test;
+
+public abstract class AbstractMemoryManagerServiceTest
+{
+
+ protected static final Random R = new Random();
+
+ protected static final int SMALL_PAYLOAD_LENGTH = 4;
+ protected static final byte[] SMALL_PAYLOAD = MemoryTestUtils.generateRandomPayload( SMALL_PAYLOAD_LENGTH );
+
+
+ protected MemoryManagerService<Object> mms;
+
+ protected abstract MemoryManagerService<Object> instanciateMemoryManagerService( int bufferSize );
+
+
+ /**
+ * Test pointers allocation, when buffer size is not aligned with the size of stored objects.
+ * Null {@link Pointer} should be returned to allow {@link MemoryManagerService} to go to next step with allocation policy.
+ */
+ @Test
+ public void testNotEnoughFreeSpace()
+ {
+
+ // Storing a first payload of 4 bytes, 1 byte remaining in the buffer. When storing a second 4 bytes payload, an null pointer should be returned.
+
+ final int BUFFER_SIZE = SMALL_PAYLOAD_LENGTH + 1;
+
+ mms = instanciateMemoryManagerService( BUFFER_SIZE );
+
+ Pointer<Object> pointer1 = mms.store( SMALL_PAYLOAD );
+ Assert.assertNotNull( pointer1 );
+ Assert.assertFalse( pointer1.isFree() );
+
+ Pointer<Object> pointer2 = mms.store( SMALL_PAYLOAD );
+ Assert.assertNull( pointer2 );
+
+ }
+
+
+ /**
+ * Ensure no byte is leaking when allocating several objects.
+ */
+ @Test
+ public void testByteLeaking()
+ {
+
+ // Initializing 1 buffer of 10*4 bytes, should be able to allocate 10 objects of 4 bytes.
+
+ final int NUMBER_OF_OBJECTS = 10;
+ final int BUFFER_SIZE = NUMBER_OF_OBJECTS * SMALL_PAYLOAD_LENGTH;
+
+ mms = instanciateMemoryManagerService( BUFFER_SIZE );
+
+ for ( int i = 0; i < NUMBER_OF_OBJECTS; i++ )
+ {
+ Pointer<Object> pointer = mms.store( SMALL_PAYLOAD );
+ Assert.assertNotNull( pointer );
+ }
+
+ Pointer<Object> pointerNull = mms.store( SMALL_PAYLOAD );
+ Assert.assertNull( pointerNull );
+ }
+
+ /**
+ * Ensure memory usage is reported correctly
+ */
+ @Test
+ public void testReportCorrectUsedMemory()
+ {
+
+ // Initializing 1 buffer of 4*4 bytes, storing and freeing and storing again should report correct numbers.
+
+ final int NUMBER_OF_OBJECTS = 4;
+ final int BUFFER_SIZE = NUMBER_OF_OBJECTS * SMALL_PAYLOAD_LENGTH;
+
+ mms = instanciateMemoryManagerService( BUFFER_SIZE );
+
+ Pointer<Object> lastPointer = null;
+ for ( int i = 0; i < NUMBER_OF_OBJECTS; i++ )
+ {
+ Pointer<Object> pointer = mms.store( SMALL_PAYLOAD );
+ Assert.assertNotNull( pointer );
+ lastPointer = pointer;
+ }
+
+ // Buffer is fully used.
+ Assert.assertEquals( BUFFER_SIZE, mms.used() );
+
+ Assert.assertNotNull( lastPointer );
+ mms.free( lastPointer );
+
+ Pointer<Object> pointerNotNull = mms.store( SMALL_PAYLOAD );
+ Assert.assertNotNull( pointerNotNull );
+
+ // Buffer again fully used.
+ Assert.assertEquals( BUFFER_SIZE, mms.used() );
+
+ }
+
+ /**
+ * Completely fill the buffer, free some pointer, reallocated the freed space, clear the buffer. The entire space should be
+ */
+ @Test
+ public void testFullFillAndFreeAndClearBuffer()
+ {
+
+ final int NUMBER_OF_OBJECTS = 10;
+ final int BUFFER_SIZE = NUMBER_OF_OBJECTS * SMALL_PAYLOAD_LENGTH;
+
+ mms = instanciateMemoryManagerService( BUFFER_SIZE );
+
+ Pointer<Object> pointerFull = mms.store( MemoryTestUtils.generateRandomPayload( BUFFER_SIZE ) );
+ Assert.assertNotNull( pointerFull );
+ mms.free( pointerFull );
+
+ final int size1 = R.nextInt( BUFFER_SIZE / 2 ) + 1;
+ Pointer<Object> pointer1 = mms.store( MemoryTestUtils.generateRandomPayload( size1 ) );
+ Assert.assertNotNull( "Cannot store " + size1 + " bytes", pointer1 );
+
+ final int size2 = R.nextInt( ( BUFFER_SIZE - size1 ) / 2 ) + 1;
+ Pointer<Object> pointer2 = mms.store( MemoryTestUtils.generateRandomPayload( size2 ) );
+ Assert.assertNotNull( "Cannot store " + size2 + " bytes", pointer2 );
+
+ final int size3 = R.nextInt( ( BUFFER_SIZE - size1 - size2 ) / 2 ) + 1;
+ Pointer<Object> pointer3 = mms.store( MemoryTestUtils.generateRandomPayload( size3 ) );
+ Assert.assertNotNull( "Cannot store " + size3 + " bytes", pointer3 );
+
+ final int size4 = BUFFER_SIZE - size1 - size2 - size3;
+ Pointer<Object> pointer4 = mms.store( MemoryTestUtils.generateRandomPayload( size4 ) );
+ Assert.assertNotNull( "Cannot store " + size4 + " bytes", pointer4 );
+
+ mms.free( pointer1 );
+ Assert.assertTrue( pointer1.isFree() );
+
+ mms.free( pointer3 );
+
+ mms.free( pointer4 );
+
+ mms.free( pointer2 );
+
+ Assert.assertEquals( 0, mms.used() );
+
+ // As all pointers have been freeed, we should be able to reallocate the whole buffer
+ Pointer<Object> pointer6 = mms.store( MemoryTestUtils.generateRandomPayload( BUFFER_SIZE ) );
+ Assert.assertNotNull( "Cannot store " + BUFFER_SIZE + " bytes", pointer6 );
+
+ mms.clear();
+
+ // As all pointers have been cleared, we should be able to reallocate the whole buffer
+ Pointer<Object> pointer7 = mms.store( MemoryTestUtils.generateRandomPayload( BUFFER_SIZE ) );
+ Assert.assertNotNull( "Cannot store " + BUFFER_SIZE + " bytes", pointer7 );
+
+ mms.clear();
+
+ // As all pointers have been cleared, we should be able to reallocate the whole buffer
+ for ( int i = 0; i < NUMBER_OF_OBJECTS; i++ )
+ {
+ Pointer<Object> pointer = mms.store( SMALL_PAYLOAD );
+ Assert.assertNotNull( pointer );
+ }
+
+ mms.clear();
+
+ // As all pointers have been cleared, we should be able to reallocate the whole buffer
+ Pointer<Object> pointer8 = mms.store( MemoryTestUtils.generateRandomPayload( BUFFER_SIZE ) );
+ Assert.assertNotNull( "Cannot store " + BUFFER_SIZE + " bytes", pointer8 );
+
+ mms.free( pointer8 );
+
+ // As all pointers have been cleared, we should be able to reallocate the whole buffer
+ for ( int i = 0; i < NUMBER_OF_OBJECTS * 10; i++ )
+ {
+ Pointer<Object> pointer = mms.store( SMALL_PAYLOAD );
+ Assert.assertNotNull( pointer );
+ mms.free( pointer );
+ }
+
+ // After a clear occurs, pointers allocated before the clear should be set as "free"
+ Assert.assertTrue( pointer6.isFree() );
+ Assert.assertTrue( pointer7.isFree() );
+
+ }
+
+ @Test
+ public void testRandomPayload()
+ {
+
+ final int NUMBER_OF_OBJECTS = 10;
+ final int BUFFER_SIZE = NUMBER_OF_OBJECTS * SMALL_PAYLOAD_LENGTH;
+
+ mms = instanciateMemoryManagerService( BUFFER_SIZE );
+
+ for ( int i = 0; i < NUMBER_OF_OBJECTS; i++ )
+ {
+ byte[] payload = MemoryTestUtils.generateRandomPayload( SMALL_PAYLOAD_LENGTH );
+ Pointer<Object> pointer = mms.store( payload );
+ Assert.assertNotNull( pointer );
+ byte[] fetchedPayload = mms.retrieve( pointer );
+ Assert.assertEquals( new String( payload ), new String( fetchedPayload ) );
+ if ( R.nextBoolean() )
+ {
+ mms.free( pointer );
+ }
+ }
+
+ mms.clear();
+
+ for ( int i = 0; i < NUMBER_OF_OBJECTS; i++ )
+ {
+ byte[] payload = MemoryTestUtils.generateRandomPayload( SMALL_PAYLOAD_LENGTH );
+ Pointer<Object> pointer = mms.store( payload );
+ Assert.assertNotNull( pointer );
+ byte[] fetchedPayload = mms.retrieve( pointer );
+ Assert.assertEquals( new String( payload ), new String( fetchedPayload ) );
+ if ( R.nextBoolean() )
+ {
+ mms.free( pointer );
+ i--;
+ }
+ }
+
+ }
+
+ @Test
+ public void testStoreAllocAndFree()
+ {
+
+ final int NUMBER_OF_OBJECTS = 100;
+ final int BUFFER_SIZE = NUMBER_OF_OBJECTS * SMALL_PAYLOAD_LENGTH;
+
+ mms = instanciateMemoryManagerService( BUFFER_SIZE );
+
+ List<Pointer<Object>> pointers = new ArrayList<Pointer<Object>>( NUMBER_OF_OBJECTS );
+ for ( int i = 0; i < NUMBER_OF_OBJECTS; i++ )
+ {
+ byte[] payload = MemoryTestUtils.generateRandomPayload( SMALL_PAYLOAD_LENGTH );
+ Pointer<Object> pointer = mms.store( payload );
+ Assert.assertNotNull( pointer );
+ pointers.add( pointer );
+ byte[] fetchedPayload = mms.retrieve( pointer );
+ Assert.assertEquals( new String( payload ), new String( fetchedPayload ) );
+ }
+
+ // Free 1/4 of the pointers, from 1/4 of the address space to 1/2
+ for ( int i = NUMBER_OF_OBJECTS / 4; i < NUMBER_OF_OBJECTS / 2; i++ )
+ {
+ Pointer<Object> pointer = pointers.get( i );
+ mms.free( pointer );
+ }
+
+ // Should be able to allocate NUMBER_OF_OBJECTS / 4 * SMALL_PAYLOAD_LENGTH bytes
+ Pointer<Object> pointer1 = mms.allocate( Object.class, NUMBER_OF_OBJECTS / 4 * SMALL_PAYLOAD_LENGTH, 0, 0 );
+ Assert.assertNotNull( pointer1 );
+
+ int pointerToSkip = NUMBER_OF_OBJECTS / 2 + NUMBER_OF_OBJECTS / 10;
+ for ( int i = NUMBER_OF_OBJECTS / 2; i < NUMBER_OF_OBJECTS * 3 / 4; i++ )
+ {
+ // skip one pointer
+ if ( i == pointerToSkip )
+ {
+ continue;
+ }
+ Pointer<Object> pointer = pointers.get( i );
+ mms.free( pointer );
+ }
+
+ // Should NOT be able to allocate NUMBER_OF_OBJECTS / 4 * SMALL_PAYLOAD_LENGTH bytes
+ Pointer<Object> pointer2 = mms.allocate( Object.class, NUMBER_OF_OBJECTS / 4 * SMALL_PAYLOAD_LENGTH, 0, 0 );
+ Assert.assertNull( pointer2 );
+
+ // Freeing the previously skipped pointer should then merge the whole memory space
+ mms.free( pointers.get( pointerToSkip ) );
+
+ // Should be able to allocate NUMBER_OF_OBJECTS / 4 * SMALL_PAYLOAD_LENGTH bytes
+ Pointer<Object> pointer3 = mms.allocate( Object.class, NUMBER_OF_OBJECTS / 4 * SMALL_PAYLOAD_LENGTH, 0, 0 );
+ Assert.assertNotNull( pointer3 );
+
+ byte[] payload3 = MemoryTestUtils.generateRandomPayload( NUMBER_OF_OBJECTS / 4 * SMALL_PAYLOAD_LENGTH );
+ pointer3.getDirectBuffer().put( payload3 );
+ byte[] retrievePayload3 = mms.retrieve( pointer3 );
+ Assert.assertEquals( new String( payload3 ), new String( retrievePayload3 ) );
+
+ }
+
+ @Test
+ public void testUpdate()
+ {
+
+ final int NUMBER_OF_OBJECTS = 1;
+ final int BUFFER_SIZE = NUMBER_OF_OBJECTS * SMALL_PAYLOAD_LENGTH;
+
+ mms = instanciateMemoryManagerService( BUFFER_SIZE );
+
+ final byte[] payload = MemoryTestUtils.generateRandomPayload( SMALL_PAYLOAD_LENGTH );
+
+ final Pointer<Object> pointer = mms.store( payload );
+ Assert.assertNotNull( pointer );
+ Assert.assertEquals( new String( payload ), new String( mms.retrieve( pointer ) ) );
+
+ final byte[] otherPayload = MemoryTestUtils.generateRandomPayload( SMALL_PAYLOAD_LENGTH );
+ final Pointer<Object> otherPointer = mms.update( pointer, otherPayload );
+ Assert.assertNotNull( otherPointer );
+ Assert.assertEquals( pointer.getStart(), otherPointer.getStart() );
+ Assert.assertEquals( pointer.getEnd(), otherPointer.getEnd() );
+ Assert.assertEquals( new String( otherPayload ), new String( mms.retrieve( otherPointer ) ) );
+
+ final byte[] evenAnotherPayload = MemoryTestUtils.generateRandomPayload( SMALL_PAYLOAD_LENGTH / 2 );
+ final Pointer<Object> evenAnotherPointer = mms.update( otherPointer, evenAnotherPayload );
+ Assert.assertNotNull( evenAnotherPointer );
+ Assert.assertEquals( pointer.getStart(), evenAnotherPointer.getStart() );
+ Assert.assertEquals( pointer.getEnd(), evenAnotherPointer.getEnd() );
+ //Assert.assertEquals( 2, new String( mms.retrieve( evenAnotherPointer ) ).length() );
+ Assert.assertTrue( new String( mms.retrieve( evenAnotherPointer ) )
+ .startsWith( new String( evenAnotherPayload ) ) );
+
+ }
+
+
+ @Test
+ public void testAllocate()
+ {
+
+ final int NUMBER_OF_OBJECTS = 10;
+ final int BUFFER_SIZE = NUMBER_OF_OBJECTS * SMALL_PAYLOAD_LENGTH;
+
+ mms = instanciateMemoryManagerService( BUFFER_SIZE );
+
+ final byte[] payload1 = MemoryTestUtils.generateRandomPayload( 8 * SMALL_PAYLOAD_LENGTH );
+ final Pointer<Object> pointer1 = mms.store( payload1 );
+ Assert.assertNotNull( pointer1 );
+ Assert.assertEquals( new String( payload1 ), new String( mms.retrieve( pointer1 ) ) );
+
+ final byte[] payload2 = MemoryTestUtils.generateRandomPayload( 2 * SMALL_PAYLOAD_LENGTH );
+ final Pointer<Object> pointer2 = mms.store( payload2 );
+ Assert.assertNotNull( pointer2 );
+ Assert.assertEquals( new String( payload2 ), new String( mms.retrieve( pointer2 ) ) );
+
+ mms.free( pointer1 );
+
+ final byte[] payload3 = MemoryTestUtils.generateRandomPayload( 2 * SMALL_PAYLOAD_LENGTH );
+ final Pointer<Object> pointer3 = mms.store( payload3 );
+ Assert.assertNotNull( pointer3 );
+ Assert.assertEquals( new String( payload3 ), new String( mms.retrieve( pointer3 ) ) );
+
+ final int size1 = 4 * SMALL_PAYLOAD_LENGTH;
+ final byte[] allocatedPayload1 = MemoryTestUtils.generateRandomPayload( size1 );
+ final Pointer<Object> allocatedPointer1 = mms.allocate( Object.class, allocatedPayload1.length, -1, -1 );
+ Assert.assertNotNull( allocatedPointer1 );
+ final ByteBuffer buffer1 = allocatedPointer1.getDirectBuffer();
+ Assert.assertNotNull( buffer1 );
+ Assert.assertEquals( 0, buffer1.position() );
+ Assert.assertEquals( size1, buffer1.limit() );
+ Assert.assertEquals( size1, buffer1.capacity() );
+ buffer1.put( allocatedPayload1 );
+ Assert.assertEquals( new String( allocatedPayload1 ), new String( mms.retrieve( allocatedPointer1 ) ) );
+
+ final int size2 = 2 * SMALL_PAYLOAD_LENGTH;
+ final byte[] allocatedPayload2 = MemoryTestUtils.generateRandomPayload( size2 );
+ final Pointer<Object> allocatedPointer2 = mms.allocate( Object.class, allocatedPayload2.length, -1, -1 );
+ Assert.assertNotNull( allocatedPointer2 );
+ final ByteBuffer buffer2 = allocatedPointer2.getDirectBuffer();
+ Assert.assertNotNull( buffer2 );
+ Assert.assertEquals( size2, buffer2.limit() );
+ Assert.assertEquals( size2, buffer2.capacity() );
+ buffer2.put( allocatedPayload2 );
+ Assert.assertEquals( new String( allocatedPayload2 ), new String( mms.retrieve( allocatedPointer2 ) ) );
+
+
+ // Ensure the new allocation has not overwritten other data
+ Assert.assertEquals( new String( payload2 ), new String( mms.retrieve( pointer2 ) ) );
+ Assert.assertEquals( new String( payload3 ), new String( mms.retrieve( pointer3 ) ) );
+
+ }
+
+}
diff --git a/directmemory-cache/src/test/java/org/apache/directmemory/memory/BaseTest.java b/directmemory-cache/src/test/java/org/apache/directmemory/memory/BaseTest.java
new file mode 100644
index 0000000..8bc84f8
--- /dev/null
+++ b/directmemory-cache/src/test/java/org/apache/directmemory/memory/BaseTest.java
@@ -0,0 +1,237 @@
+package org.apache.directmemory.memory;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.Map;
+import java.util.Random;
+import java.util.zip.CRC32;
+import java.util.zip.Checksum;
+
+import org.apache.directmemory.measures.Ram;
+import org.apache.directmemory.memory.Pointer;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.carrotsearch.junitbenchmarks.AbstractBenchmark;
+import com.carrotsearch.junitbenchmarks.BenchmarkOptions;
+import com.google.common.collect.Maps;
+
+@Ignore
+public class BaseTest
+ extends AbstractBenchmark
+{
+
+ MemoryManagerService<Object> mem;
+
+ @Before
+ public void initMMS()
+ {
+ mem = new MemoryManagerServiceImpl<Object>();
+ mem.init( 1, 1 * 1024 * 1024 );
+ }
+ @Test
+ public void smokeTest()
+ {
+ logger.info( "buffer size=" + mem.capacity() );
+ assertNotNull( mem );
+
+ Random rnd = new Random();
+
+ int size = rnd.nextInt( 10 ) * (int)mem.capacity() / 100;
+
+ logger.info( "size=" + size );
+
+ Pointer<Object> p = mem.store( new byte[size] );
+ assertNotNull( p );
+ assertEquals( size, p.getEnd() );
+ assertEquals( size, mem.used() );
+ mem.free( p );
+ assertEquals( 0, mem.used() );
+ }
+
+
+ private static Logger logger = LoggerFactory.getLogger( MallocTest.class );
+
+ private static Random rnd = new Random();
+
+ final static Map<String, Byte> test = Maps.newHashMap();
+
+ private static int errors;
+
+ public long crc( String str )
+ {
+ final Checksum checksum = new CRC32();
+
+ final byte bytes[] = str.getBytes();
+ checksum.update( bytes, 0, bytes.length );
+ return checksum.getValue();
+ }
+
+ @BeforeClass
+ @AfterClass
+ public static void setup()
+ {
+ rnd = new Random();
+// logger.info("off-heap allocated: " + Ram.inMb(mem.capacity()));
+// logger.info("off-heap used: " + Ram.inMb(mem.used()));
+ logger.info( "test - size: " + test.size() );
+ logger.info( "test - errors: " + errors );
+ logger.info( "heap - max: " + Ram.inMb( Runtime.getRuntime().maxMemory() ) );
+ logger.info( "heap - allocated: " + Ram.inMb( Runtime.getRuntime().totalMemory() ) );
+ logger.info( "heap - free : " + Ram.inMb( Runtime.getRuntime().freeMemory() ) );
+ logger.info( "************************************************" );
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 10000, warmupRounds = 0, concurrency = 10 )
+ @Test
+ public void anyDuplicates()
+ {
+ String key = "test" + rnd.nextInt( 100000 );
+ if ( test.containsKey( key ) )
+ {
+ logger.error( "key " + key + " has already been used" );
+ errors++;
+ }
+ test.put( key, (byte) 0 );
+ }
+
+ @Test
+ public void aFewEntriesWithRead()
+ {
+ logger.info( "total capacity=" + Ram.inMb( mem.capacity() ) );
+ assertNotNull( mem );
+ int howMany = 10000;
+ logger.info( "payload size is variable" );
+ logger.info( "entries=" + howMany );
+ String test = "this is a nicely crafted test";
+ for ( int i = 0; i < howMany; i++ )
+ {
+ final byte[] payload = ( test + " - " + i ).getBytes();
+ Pointer<Object> p = mem.store( payload );
+ final byte[] check = mem.retrieve( p );
+ assertNotNull( check );
+ assertEquals( test + " - " + i, new String( check ) );
+ long crc1 = crc32( payload );
+ long crc2 = crc32( check );
+ assertEquals( crc1, crc2 );
+ }
+
+ logger.info( "total used=" + Ram.inMb( mem.used() ) );
+ }
+
+ private static long crc32( byte[] payload )
+ {
+ final Checksum checksum = new CRC32();
+ checksum.update( payload, 0, payload.length );
+ return checksum.getValue();
+ }
+
+ @Test
+ public void aFewEntriesWithCheck()
+ {
+ logger.info( "total capacity=" + Ram.inMb( mem.capacity() ) );
+ assertNotNull( mem );
+ int howMany = 10;
+ logger.info( "payload size is variable" );
+ logger.info( "entries=" + howMany );
+ String test = "this is a nicely crafted test";
+ Pointer<Object> lastP = null;
+ for ( int i = 0; i < howMany; i++ )
+ {
+ byte[] payload = ( test + " - " + i ).getBytes();
+ Pointer<Object> p = mem.store( payload );
+ logger.info( "p.start=" + p.getStart() );
+ logger.info( "p.end=" + p.getEnd() );
+ if ( lastP != null )
+ {
+ assertEquals( lastP.getEnd() + 1, p.getStart() );
+ }
+ assertEquals( p.getCapacity(), payload.length );
+ lastP = p;
+ logger.info( "---" );
+ }
+
+ logger.info( "total used=" + Ram.inMb( mem.used() ) );
+ }
+
+ @Test
+ public void checkExpiration()
+ throws InterruptedException
+ {
+ assertNotNull( mem );
+ int size = 400;
+ int howMany = 5000;
+
+ logger.info( "off-heap capacity=" + Ram.inMb( mem.capacity() ) );
+ logger.info( "payload size=" + Ram.inKb( size ) );
+ logger.info( "entries=" + howMany );
+
+ byte[] payload = new byte[size];
+ for ( int i = 0; i < howMany; i++ )
+ {
+ mem.store( payload, 2000 );
+ }
+
+ assertEquals( size * howMany, mem.used() );
+
+ logger.info( "entries with relative expiration=" + ( howMany / 2 ) );
+ for ( int i = 0; i < howMany / 2; i++ )
+ {
+ mem.store( payload, 100 );
+ }
+ assertEquals( size * howMany + size * howMany / 2, mem.used() );
+
+ logger.info( "entries with absolute expiration=" + ( howMany / 2 ) );
+ for ( int i = 0; i < howMany / 2; i++ )
+ {
+ mem.store( payload, 1 );
+ }
+ assertEquals( size * howMany * 2, mem.used() );
+ logger.info( "total used=" + Ram.inMb( mem.used() ) );
+
+ Thread.sleep( 1000 );
+
+ logger.info( "calling disposeExpiredAbsolute" );
+ mem.collectExpired();
+ logger.info( "total used=" + Ram.inMb( mem.used() ) );
+ assertEquals( size * howMany + size * howMany / 2, mem.used() );
+
+ logger.info( "calling disposeExpiredRelative" );
+ mem.collectExpired();
+ logger.info( "total used=" + Ram.inMb( mem.used() ) );
+ assertEquals( size * howMany, mem.used() );
+
+ Thread.sleep( 2000 );
+
+ logger.info( "calling disposeExpiredRelative again" );
+ mem.collectExpired();
+ logger.info( "total used=" + Ram.inMb( mem.used() ) );
+ assertEquals( 0, mem.used() );
+
+ }
+}
diff --git a/directmemory-cache/src/test/java/org/apache/directmemory/memory/Concurrent2Test.java b/directmemory-cache/src/test/java/org/apache/directmemory/memory/Concurrent2Test.java
new file mode 100644
index 0000000..f16f639
--- /dev/null
+++ b/directmemory-cache/src/test/java/org/apache/directmemory/memory/Concurrent2Test.java
@@ -0,0 +1,240 @@
+package org.apache.directmemory.memory;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import com.carrotsearch.junitbenchmarks.AbstractBenchmark;
+import com.carrotsearch.junitbenchmarks.BenchmarkOptions;
+import com.carrotsearch.junitbenchmarks.annotation.AxisRange;
+import com.carrotsearch.junitbenchmarks.annotation.BenchmarkHistoryChart;
+import com.carrotsearch.junitbenchmarks.annotation.BenchmarkMethodChart;
+import com.carrotsearch.junitbenchmarks.annotation.LabelType;
+import com.google.common.collect.MapMaker;
+import org.apache.directmemory.measures.Ram;
+import org.apache.directmemory.memory.MemoryManager;
+import org.apache.directmemory.memory.Pointer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Random;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@AxisRange( min = 0, max = 1 )
+@BenchmarkMethodChart()
+@BenchmarkHistoryChart( labelWith = LabelType.CUSTOM_KEY, maxRuns = 5 )
+@Ignore
+public class Concurrent2Test
+ extends AbstractBenchmark
+{
+
+ private final static int entries = 100000;
+
+ public static AtomicInteger count = new AtomicInteger();
+
+ private static AtomicInteger got = new AtomicInteger();
+
+ private static AtomicInteger missed = new AtomicInteger();
+
+ private static AtomicInteger good = new AtomicInteger();
+
+ private static AtomicInteger bad = new AtomicInteger();
+
+ private static AtomicInteger read = new AtomicInteger();
+
+ public static ConcurrentMap<String, Pointer<Object>> map =
+ new MapMaker().concurrencyLevel( 4 ).initialCapacity( 100000 ).makeMap();
+
+
+ @BenchmarkOptions( benchmarkRounds = 100000, warmupRounds = 0, concurrency = 100 )
+ @Test
+ public void store()
+ {
+ final String key = "test-" + count.incrementAndGet();
+ put( key );
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 1000000, warmupRounds = 0, concurrency = 100 )
+ @Test
+ public void retrieveCatchThemAll()
+ {
+ String key = "test-" + ( rndGen.nextInt( entries ) + 1 );
+ Pointer<Object> p = map.get( key );
+ read.incrementAndGet();
+ if ( p != null )
+ {
+ got.incrementAndGet();
+ byte[] payload = MemoryManager.retrieve( p );
+ if ( key.equals( new String( payload ) ) )
+ {
+ good.incrementAndGet();
+ }
+ else
+ {
+ bad.incrementAndGet();
+ }
+ }
+ else
+ {
+ logger.info( "did not find key " + key );
+ missed.incrementAndGet();
+ }
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 1000000, warmupRounds = 0, concurrency = 100 )
+ @Test
+ public void retrieveCatchHalfOfThem()
+ {
+ String key = "test-" + ( rndGen.nextInt( entries * 2 ) + 1 );
+ Pointer<Object> p = map.get( key );
+ read.incrementAndGet();
+ if ( p != null )
+ {
+ got.incrementAndGet();
+ byte[] payload = MemoryManager.retrieve( p );
+ if ( key.equals( new String( payload ) ) )
+ {
+ good.incrementAndGet();
+ }
+ else
+ {
+ bad.incrementAndGet();
+ }
+ }
+ else
+ {
+ missed.incrementAndGet();
+ }
+ }
+
+ private void put( String key )
+ {
+ map.put( key, MemoryManager.store( key.getBytes() ) );
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 1000000, warmupRounds = 0, concurrency = 10 )
+ @Test
+ public void write3Read7()
+ {
+ String key = "test-" + ( rndGen.nextInt( entries * 2 ) + 1 );
+
+ int what = rndGen.nextInt( 10 );
+
+ switch ( what )
+ {
+ case 0:
+ case 1:
+ case 2:
+ put( key );
+ break;
+ default:
+ get( key );
+ break;
+
+ }
+
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 1000000, warmupRounds = 0, concurrency = 10 )
+ @Test
+ public void write1Read9()
+ {
+ String key = "test-" + ( rndGen.nextInt( entries * 2 ) + 1 );
+
+ int what = rndGen.nextInt( 10 );
+
+ switch ( what )
+ {
+ case 0:
+ put( key );
+ break;
+ default:
+ get( key );
+ break;
+
+ }
+
+ }
+
+ private void get( String key )
+ {
+ Pointer<Object> p = map.get( key );
+ read.incrementAndGet();
+ if ( p != null )
+ {
+ got.incrementAndGet();
+ byte[] payload = MemoryManager.retrieve( p );
+ if ( key.equals( new String( payload ) ) )
+ {
+ good.incrementAndGet();
+ }
+ else
+ {
+ bad.incrementAndGet();
+ }
+ }
+ else
+ {
+ missed.incrementAndGet();
+ }
+ }
+
+ Random rndGen = new Random();
+
+ private static Logger logger = LoggerFactory.getLogger( Concurrent2Test.class );
+
+ private static void dump( MemoryManagerService<Object> mms )
+ {
+ logger.info( "off-heap - allocated: " + Ram.inMb( mms.capacity() ) );
+ logger.info( "off-heap - used: " + Ram.inMb( mms.used() ) );
+ logger.info( "heap - max: " + Ram.inMb( Runtime.getRuntime().maxMemory() ) );
+ logger.info( "heap - allocated: " + Ram.inMb( Runtime.getRuntime().totalMemory() ) );
+ logger.info( "heap - free : " + Ram.inMb( Runtime.getRuntime().freeMemory() ) );
+ logger.info( "************************************************" );
+ }
+
+ @BeforeClass
+ public static void init()
+ {
+ MemoryManager.init( 1, Ram.Mb( 512 ) );
+ }
+
+ @AfterClass
+ public static void dump()
+ {
+
+ dump( MemoryManager.getMemoryManager() );
+
+ logger.info( "************************************************" );
+ logger.info( "entries: " + entries );
+ logger.info( "inserted: " + map.size() );
+ logger.info( "reads: " + read );
+ logger.info( "count: " + count );
+ logger.info( "got: " + got );
+ logger.info( "missed: " + missed );
+ logger.info( "good: " + good );
+ logger.info( "bad: " + bad );
+ logger.info( "************************************************" );
+ }
+
+}
diff --git a/directmemory-cache/src/test/java/org/apache/directmemory/memory/Concurrent3Test.java b/directmemory-cache/src/test/java/org/apache/directmemory/memory/Concurrent3Test.java
new file mode 100644
index 0000000..a63c646
--- /dev/null
+++ b/directmemory-cache/src/test/java/org/apache/directmemory/memory/Concurrent3Test.java
@@ -0,0 +1,266 @@
+package org.apache.directmemory.memory;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import com.carrotsearch.junitbenchmarks.BenchmarkOptions;
+import com.carrotsearch.junitbenchmarks.annotation.AxisRange;
+import com.carrotsearch.junitbenchmarks.annotation.BenchmarkHistoryChart;
+import com.carrotsearch.junitbenchmarks.annotation.BenchmarkMethodChart;
+import com.carrotsearch.junitbenchmarks.annotation.LabelType;
+import com.google.common.collect.MapMaker;
+import org.apache.directmemory.measures.Ram;
+import org.apache.directmemory.memory.MemoryManager;
+import org.apache.directmemory.memory.Pointer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Random;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@AxisRange( min = 0, max = 1 )
+@BenchmarkMethodChart()
+@BenchmarkHistoryChart( labelWith = LabelType.CUSTOM_KEY, maxRuns = 5 )
+@Ignore
+public class Concurrent3Test
+{
+
+ private final static int entries = 100000;
+
+ public static AtomicInteger count = new AtomicInteger();
+
+ private static AtomicInteger got = new AtomicInteger();
+
+ private static AtomicInteger missed = new AtomicInteger();
+
+ private static AtomicInteger good = new AtomicInteger();
+
+ private static AtomicInteger bad = new AtomicInteger();
+
+ private static AtomicInteger read = new AtomicInteger();
+
+ private static AtomicInteger disposals = new AtomicInteger();
+
+ public static ConcurrentMap<String, Pointer<Object>> map =
+ new MapMaker().concurrencyLevel( 4 ).initialCapacity( 100000 ).makeMap();
+
+
+ @BenchmarkOptions( benchmarkRounds = 100000, warmupRounds = 0, concurrency = 100 )
+ @Test
+ public void store()
+ {
+ final String key = "test-" + count.incrementAndGet();
+ put( key );
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 500, warmupRounds = 0, concurrency = 10 )
+ @Test
+ public void storeSomeWithExpiry()
+ {
+ final String key = "test-" + count.incrementAndGet();
+ putWithExpiry( key );
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 1000000, warmupRounds = 0, concurrency = 100 )
+ @Test
+ public void retrieveCatchThemAll()
+ {
+ String key = "test-" + ( rndGen.nextInt( entries ) + 1 );
+ get( key );
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 1000000, warmupRounds = 0, concurrency = 100 )
+ @Test
+ public void retrieveCatchHalfOfThem()
+ {
+ String key = "test-" + ( rndGen.nextInt( entries * 2 ) + 1 );
+ get( key );
+ }
+
+ private void get( String key )
+ {
+ Pointer<Object> p = map.get( key );
+ read.incrementAndGet();
+ if ( p != null )
+ {
+ got.incrementAndGet();
+ byte[] payload = MemoryManager.retrieve( p );
+ if ( ( new String( payload ) ).startsWith( key ) )
+ {
+ good.incrementAndGet();
+ }
+ else
+ {
+ bad.incrementAndGet();
+ }
+ }
+ else
+ {
+ missed.incrementAndGet();
+ }
+ }
+
+ private void put( String key )
+ {
+ final StringBuilder bldr = new StringBuilder();
+ for ( int i = 0; i < 100; i++ )
+ {
+ bldr.append( key );
+ }
+ map.put( key, MemoryManager.store( bldr.toString().getBytes() ) );
+ }
+
+ private void putWithExpiry( String key )
+ {
+ final StringBuilder bldr = new StringBuilder();
+ for ( int i = 0; i < 100; i++ )
+ {
+ bldr.append( key );
+ }
+ map.put( key, MemoryManager.store( bldr.toString().getBytes(), rndGen.nextInt( 2000 ) ) );
+ }
+
+
+ @BenchmarkOptions( benchmarkRounds = 50000, warmupRounds = 0, concurrency = 10 )
+ @Test
+ public void write1Read8AndSomeDisposal()
+ {
+ String key = "test-" + ( rndGen.nextInt( entries * 2 ) + 1 );
+
+ int what = rndGen.nextInt( 10 );
+
+ switch ( what )
+ {
+ case 0:
+ put( key );
+ break;
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ get( key );
+ break;
+ default:
+ final int rndVal = rndGen.nextInt( 1000 );
+ if ( rndVal > 995 )
+ {
+ disposals.incrementAndGet();
+ final long start = System.currentTimeMillis();
+ long howMany = MemoryManager.collectExpired();
+ final long end = System.currentTimeMillis();
+ logger.info( "" + howMany + " disposed in " + ( end - start ) + " milliseconds" );
+ }
+ }
+
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 1000000, warmupRounds = 0, concurrency = 10 )
+ @Test
+ public void write3Read7()
+ {
+ String key = "test-" + ( rndGen.nextInt( entries * 2 ) + 1 );
+
+ int what = rndGen.nextInt( 10 );
+
+ switch ( what )
+ {
+ case 0:
+ case 1:
+ case 2:
+ put( key );
+ break;
+ default:
+ get( key );
+ break;
+ }
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 1000000, warmupRounds = 0, concurrency = 10 )
+ @Test
+ public void write1Read9()
+ {
+ String key = "test-" + ( rndGen.nextInt( entries * 2 ) + 1 );
+
+ int what = rndGen.nextInt( 10 );
+
+ switch ( what )
+ {
+ case 0:
+ put( key );
+ break;
+ default:
+ get( key );
+ break;
+
+ }
+
+ }
+
+ Random rndGen = new Random();
+
+ private static Logger logger = LoggerFactory.getLogger( Concurrent3Test.class );
+
+ private static void dump( MemoryManagerService<Object> mms )
+ {
+ logger.info( "off-heap - allocated: " + Ram.inMb( mms.capacity() ) );
+ logger.info( "off-heap - used: " + Ram.inMb( mms.used() ) );
+ logger.info( "heap - max: " + Ram.inMb( Runtime.getRuntime().maxMemory() ) );
+ logger.info( "heap - allocated: " + Ram.inMb( Runtime.getRuntime().totalMemory() ) );
+ logger.info( "heap - free : " + Ram.inMb( Runtime.getRuntime().freeMemory() ) );
+ logger.info( "************************************************" );
+ }
+
+ @BeforeClass
+ public static void init()
+ {
+ MemoryManager.init( 1, Ram.Mb( 512 ) );
+ }
+
+ @AfterClass
+ public static void dump()
+ {
+
+ dump( MemoryManager.getMemoryManager() );
+
+ logger.info( "************************************************" );
+ logger.info( "entries: " + entries );
+ logger.info( "inserted: " + map.size() );
+ logger.info( "reads: " + read );
+ logger.info( "count: " + count );
+ logger.info( "got: " + got );
+ logger.info( "missed: " + missed );
+ logger.info( "good: " + good );
+ logger.info( "bad: " + bad );
+ logger.info( "disposals: " + disposals );
+ logger.info( "************************************************" );
+ }
+
+}
+
+
+
diff --git a/directmemory-cache/src/test/java/org/apache/directmemory/memory/ConcurrentTest.java b/directmemory-cache/src/test/java/org/apache/directmemory/memory/ConcurrentTest.java
new file mode 100644
index 0000000..5144044
--- /dev/null
+++ b/directmemory-cache/src/test/java/org/apache/directmemory/memory/ConcurrentTest.java
@@ -0,0 +1,239 @@
+package org.apache.directmemory.memory;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import com.carrotsearch.junitbenchmarks.AbstractBenchmark;
+import com.carrotsearch.junitbenchmarks.BenchmarkOptions;
+import com.carrotsearch.junitbenchmarks.annotation.AxisRange;
+import com.carrotsearch.junitbenchmarks.annotation.BenchmarkHistoryChart;
+import com.carrotsearch.junitbenchmarks.annotation.BenchmarkMethodChart;
+import com.carrotsearch.junitbenchmarks.annotation.LabelType;
+import com.google.common.collect.MapMaker;
+import org.apache.directmemory.measures.Ram;
+import org.apache.directmemory.memory.Pointer;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Random;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@AxisRange( min = 0, max = 1 )
+@BenchmarkMethodChart()
+@BenchmarkHistoryChart( labelWith = LabelType.CUSTOM_KEY, maxRuns = 5 )
+
+@Ignore
+public class ConcurrentTest
+ extends AbstractBenchmark
+{
+
+ private final static int entries = 100000;
+
+ public static AtomicInteger count = new AtomicInteger();
+
+ private static AtomicInteger got = new AtomicInteger();
+
+ private static AtomicInteger missed = new AtomicInteger();
+
+ private static AtomicInteger good = new AtomicInteger();
+
+ private static AtomicInteger bad = new AtomicInteger();
+
+ private static AtomicInteger read = new AtomicInteger();
+
+ private static MemoryManagerService<Object> mem;
+
+ public static ConcurrentMap<String, Pointer<Object>> map =
+ new MapMaker().concurrencyLevel( 4 ).initialCapacity( 100000 ).makeMap();
+
+ @Before
+ public void initMMS()
+ {
+ mem = new MemoryManagerServiceImpl<Object>();
+ mem.init( 1, 512 * 1024 * 1024 );
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 100000, warmupRounds = 0, concurrency = 100 )
+ @Test
+ public void store()
+ {
+ final String key = "test-" + count.incrementAndGet();
+ map.put( key, mem.store( key.getBytes() ) );
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 1000000, warmupRounds = 0, concurrency = 100 )
+ @Test
+ public void retrieveCatchThemAll()
+ {
+ String key = "test-" + ( rndGen.nextInt( entries ) + 1 );
+ Pointer<Object> p = map.get( key );
+ read.incrementAndGet();
+ if ( p != null )
+ {
+ got.incrementAndGet();
+ byte[] payload = mem.retrieve( p );
+ if ( key.equals( new String( payload ) ) )
+ {
+ good.incrementAndGet();
+ }
+ else
+ {
+ bad.incrementAndGet();
+ }
+ }
+ else
+ {
+ logger.info( "did not find key " + key );
+ missed.incrementAndGet();
+ }
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 1000000, warmupRounds = 0, concurrency = 100 )
+ @Test
+ public void retrieveCatchHalfOfThem()
+ {
+ String key = "test-" + ( rndGen.nextInt( entries * 2 ) + 1 );
+ Pointer<Object> p = map.get( key );
+ read.incrementAndGet();
+ if ( p != null )
+ {
+ got.incrementAndGet();
+ byte[] payload = mem.retrieve( p );
+ if ( key.equals( new String( payload ) ) )
+ {
+ good.incrementAndGet();
+ }
+ else
+ {
+ bad.incrementAndGet();
+ }
+ }
+ else
+ {
+ missed.incrementAndGet();
+ }
+ }
+
+ private void put( String key )
+ {
+ map.put( key, mem.store( key.getBytes() ) );
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 1000000, warmupRounds = 0, concurrency = 10 )
+ @Test
+ public void write3Read7()
+ {
+ String key = "test-" + ( rndGen.nextInt( entries * 2 ) + 1 );
+
+ int what = rndGen.nextInt( 10 );
+
+ switch ( what )
+ {
+ case 0:
+ case 1:
+ case 2:
+ put( key );
+ break;
+ default:
+ get( key );
+ break;
+
+ }
+
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 1000000, warmupRounds = 0, concurrency = 10 )
+ @Test
+ public void write1Read9()
+ {
+ String key = "test-" + ( rndGen.nextInt( entries * 2 ) + 1 );
+
+ int what = rndGen.nextInt( 10 );
+
+ switch ( what )
+ {
+ case 0:
+ put( key );
+ break;
+ default:
+ get( key );
+ break;
+
+ }
+
+ }
+
+ private void get( String key )
+ {
+ Pointer<Object> p = map.get( key );
+ read.incrementAndGet();
+ if ( p != null )
+ {
+ got.incrementAndGet();
+ byte[] payload = mem.retrieve( p );
+ if ( key.equals( new String( payload ) ) )
+ {
+ good.incrementAndGet();
+ }
+ else
+ {
+ bad.incrementAndGet();
+ }
+ }
+ else
+ {
+ missed.incrementAndGet();
+ }
+ }
+
+ Random rndGen = new Random();
+
+ private static Logger logger = LoggerFactory.getLogger( ConcurrentTest.class );
+
+ @BeforeClass
+ @AfterClass
+ public static void dump()
+ {
+ logger.info( "off-heap allocated: " + Ram.inMb( mem.capacity() ) );
+ logger.info( "off-heap used: " + Ram.inMb( mem.used() ) );
+ logger.info( "heap - max: " + Ram.inMb( Runtime.getRuntime().maxMemory() ) );
+ logger.info( "heap - allocated: " + Ram.inMb( Runtime.getRuntime().totalMemory() ) );
+ logger.info( "heap - free : " + Ram.inMb( Runtime.getRuntime().freeMemory() ) );
+ logger.info( "************************************************" );
+ logger.info( "entries: " + entries );
+ logger.info( "inserted: " + map.size() );
+ logger.info( "reads: " + read );
+ logger.info( "count: " + count );
+ logger.info( "got: " + got );
+ logger.info( "missed: " + missed );
+ logger.info( "good: " + good );
+ logger.info( "bad: " + bad );
+ logger.info( "************************************************" );
+ }
+
+}
+
+
+
diff --git a/directmemory-cache/src/test/java/org/apache/directmemory/memory/DefaultMemoryManagerServiceTest.java b/directmemory-cache/src/test/java/org/apache/directmemory/memory/DefaultMemoryManagerServiceTest.java
new file mode 100644
index 0000000..dbe7900
--- /dev/null
+++ b/directmemory-cache/src/test/java/org/apache/directmemory/memory/DefaultMemoryManagerServiceTest.java
@@ -0,0 +1,34 @@
+package org.apache.directmemory.memory;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+public class DefaultMemoryManagerServiceTest
+ extends AbstractMemoryManagerServiceTest
+{
+
+ @Override
+ protected MemoryManagerService<Object> instanciateMemoryManagerService( int bufferSize )
+ {
+ final MemoryManagerService<Object> mms = new MemoryManagerServiceImpl<Object>();
+ mms.init( 1, bufferSize );
+ return mms;
+ }
+
+}
diff --git a/directmemory-cache/src/test/java/org/apache/directmemory/memory/MallocTest.java b/directmemory-cache/src/test/java/org/apache/directmemory/memory/MallocTest.java
new file mode 100644
index 0000000..d400013
--- /dev/null
+++ b/directmemory-cache/src/test/java/org/apache/directmemory/memory/MallocTest.java
@@ -0,0 +1,199 @@
+package org.apache.directmemory.memory;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import com.carrotsearch.junitbenchmarks.AbstractBenchmark;
+import com.carrotsearch.junitbenchmarks.BenchmarkOptions;
+import com.carrotsearch.junitbenchmarks.annotation.AxisRange;
+import com.carrotsearch.junitbenchmarks.annotation.BenchmarkHistoryChart;
+import com.carrotsearch.junitbenchmarks.annotation.BenchmarkMethodChart;
+import com.carrotsearch.junitbenchmarks.annotation.LabelType;
+import com.google.common.collect.MapMaker;
+import org.apache.directmemory.measures.Ram;
+import org.apache.directmemory.memory.Pointer;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Random;
+import java.util.concurrent.ConcurrentMap;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+@AxisRange( min = 0, max = 1 )
+@BenchmarkMethodChart()
+@BenchmarkOptions( benchmarkRounds = 1, warmupRounds = 0 )
+@BenchmarkHistoryChart( labelWith = LabelType.CUSTOM_KEY, maxRuns = 5 )
+@Ignore
+
+public class MallocTest
+ extends AbstractBenchmark
+{
+
+ Random rnd = new Random();
+
+ private static Logger logger = LoggerFactory.getLogger( MallocTest.class );
+
+ @After
+ public void dump()
+ {
+ logger.info( "off-heap allocated: " + Ram.inMb( mem.capacity() ) );
+ logger.info( "off-heap used: " + Ram.inMb( mem.used() ) );
+ logger.info( "heap - max: " + Ram.inMb( Runtime.getRuntime().maxMemory() ) );
+ logger.info( "heap - allocated: " + Ram.inMb( Runtime.getRuntime().totalMemory() ) );
+ logger.info( "heap - free : " + Ram.inMb( Runtime.getRuntime().freeMemory() ) );
+ logger.info( "************************************************" );
+ }
+
+ MemoryManagerService<Object> mem;
+
+ @Before
+ public void initMMS()
+ {
+ mem = new MemoryManagerServiceImpl<Object>();
+ mem.init( 1, 512 * 1024 * 1024 );
+ }
+
+ @Test
+ public void oneMillionEntries()
+ {
+ assertNotNull( mem );
+ int howMany = 1000000;
+ int size = (int)mem.capacity() / ( howMany );
+ size -= size / 100 * 1;
+ logger.info( "payload size=" + size );
+ logger.info( "entries=" + howMany );
+
+ logger.info( "starting..." );
+
+ long start = System.currentTimeMillis();
+
+ byte[] payload = new byte[size];
+ for ( int i = 0; i < howMany; i++ )
+ {
+ mem.store( payload );
+ }
+
+ logger.info( "...done in " + ( System.currentTimeMillis() - start ) + " msecs." );
+ }
+
+ @Test
+ public void twoMillionEntries()
+ {
+
+ assertNotNull( mem );
+ int howMany = 2000000;
+ int size = (int)mem.capacity() / ( howMany );
+ size -= size / 100 * 1;
+ logger.info( "payload size=" + size );
+ logger.info( "entries=" + howMany );
+
+ logger.info( "starting..." );
+ long start = System.currentTimeMillis();
+
+ byte[] payload = new byte[size];
+ for ( int i = 0; i < howMany; i++ )
+ {
+ mem.store( payload );
+ }
+
+ logger.info( "...done in " + ( System.currentTimeMillis() - start ) + " msecs." );
+ }
+
+ @Test
+ public void fiveMillionEntries()
+ {
+
+ assertNotNull( mem );
+ int howMany = 5000000;
+ int size = (int)mem.capacity() / ( howMany );
+ size -= size / 100 * 1;
+ logger.info( "payload size=" + size );
+ logger.info( "entries=" + howMany );
+
+ logger.info( "starting..." );
+ long start = System.currentTimeMillis();
+
+ byte[] payload = new byte[size];
+ for ( int i = 0; i < howMany; i++ )
+ {
+ mem.store( payload );
+ }
+
+ logger.info( "...done in " + ( System.currentTimeMillis() - start ) + " msecs." );
+ }
+
+
+ @Test
+ public void withMap()
+ {
+
+ ConcurrentMap<Long, Pointer<Object>> map = new MapMaker().concurrencyLevel( 4 ).initialCapacity( 500000 ).makeMap();
+
+ String str = "This is the string to store into the off-heap memory";
+
+ int size = str.length();
+ int howMany = 1000000;
+ byte[] payload = str.getBytes();
+
+ logger.info( "adding " + howMany + " strings of " + size + " bytes..." );
+ for ( long i = 0; i < howMany; i++ )
+ {
+ Pointer<Object> p = mem.store( payload );
+ map.put( i, p );
+ }
+ logger.info( "...done" );
+
+ }
+
+ @Before
+ public void before()
+ {
+ mem.clear();
+ }
+
+
+ @Test
+ public void oneMillionEntriesWithRead()
+ {
+
+ logger.info( "total capacity=" + Ram.inMb( mem.capacity() ) );
+ assertNotNull( mem );
+ int size = 400;
+ int howMany = 1000000;
+ logger.info( "payload size=" + Ram.inKb( size ) );
+ logger.info( "entries=" + howMany );
+ String test = "this is a nicely crafted test";
+ byte[] payload = test.getBytes();
+ for ( int i = 0; i < howMany; i++ )
+ {
+ Pointer<Object> p = mem.store( payload );
+ byte[] check = mem.retrieve( p );
+ assertNotNull( check );
+ assertEquals( test, new String( check ) );
+ }
+
+ logger.info( "total used=" + Ram.inMb( mem.used() ) );
+ }
+}
diff --git a/directmemory-cache/src/test/java/org/apache/directmemory/memory/MemoryManagerServiceImplTest.java b/directmemory-cache/src/test/java/org/apache/directmemory/memory/MemoryManagerServiceImplTest.java
new file mode 100644
index 0000000..a78885a
--- /dev/null
+++ b/directmemory-cache/src/test/java/org/apache/directmemory/memory/MemoryManagerServiceImplTest.java
@@ -0,0 +1,202 @@
+package org.apache.directmemory.memory;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import java.util.Random;
+
+import junit.framework.Assert;
+
+import org.apache.directmemory.memory.MemoryManagerService;
+import org.apache.directmemory.memory.MemoryManagerServiceImpl;
+import org.apache.directmemory.memory.Pointer;
+import org.junit.Test;
+
+public class MemoryManagerServiceImplTest
+{
+
+ protected static final Random R = new Random();
+
+ protected static final byte[] SMALL_PAYLOAD = "ABCD".getBytes();
+
+ protected MemoryManagerService<Object> getMemoryManagerService()
+ {
+ return new MemoryManagerServiceImpl<Object>();
+ }
+
+ @Test
+ public void testFirstMatchBorderCase()
+ {
+
+ // Storing a first payload of 4 bytes, 1 byte remaining in the buffer.
+ // When storing a second 4 bytes payload, an BufferOverflowException is
+ // thrown instead of an easy check.
+
+ final int BUFFER_SIZE = 5;
+
+ final MemoryManagerService<Object> memoryManagerService = getMemoryManagerService();
+
+ memoryManagerService.init( 1, BUFFER_SIZE );
+
+ Pointer<Object> pointer1 = memoryManagerService.store( SMALL_PAYLOAD );
+ Assert.assertNotNull( pointer1 );
+
+ Pointer<Object> pointer2 = memoryManagerService.store( SMALL_PAYLOAD );
+ Assert.assertNull( pointer2 );
+
+ }
+
+ @Test
+ public void testAllocateMultipleBuffers()
+ {
+
+ // Initializing 4 buffers of 4 bytes, MemoryManagerService should search
+ // for available space in another buffer.
+
+ final int NUMBER_OF_OBJECTS = 4;
+
+ final MemoryManagerService<Object> memoryManagerService = getMemoryManagerService();
+
+ memoryManagerService.init( NUMBER_OF_OBJECTS, SMALL_PAYLOAD.length );
+
+ for ( int i = 0; i < NUMBER_OF_OBJECTS; i++ )
+ {
+ Pointer<Object> pointer = memoryManagerService.store( SMALL_PAYLOAD );
+ Assert.assertNotNull( pointer );
+ }
+
+ Pointer<Object> pointerNull = memoryManagerService.store( SMALL_PAYLOAD );
+ Assert.assertNull( pointerNull );
+ }
+
+ @Test
+ public void testByteLeaking()
+ {
+
+ // Initializing 1 buffer of 10*4 bytes, should be able to allocate 10
+ // objects of 4 bytes.
+
+ final int NUMBER_OF_OBJECTS = 10;
+
+ final MemoryManagerService<Object> memoryManagerService = getMemoryManagerService();
+ memoryManagerService.init( 1, NUMBER_OF_OBJECTS * SMALL_PAYLOAD.length );
+
+ for ( int i = 0; i < NUMBER_OF_OBJECTS; i++ )
+ {
+ Pointer<Object> pointer = memoryManagerService.store( SMALL_PAYLOAD );
+ Assert.assertNotNull( pointer );
+ }
+
+ Pointer<Object> pointerNull = memoryManagerService.store( SMALL_PAYLOAD );
+ Assert.assertNull( pointerNull );
+ }
+
+ @Test
+ public void testReportCorrectUsedMemory()
+ {
+
+ // Initializing 1 buffer of 4*4 bytes, storing and freeing and storing
+ // again should report correct numbers.
+
+ final int NUMBER_OF_OBJECTS = 4;
+ final int BUFFER_SIZE = NUMBER_OF_OBJECTS * SMALL_PAYLOAD.length;
+
+ final MemoryManagerService<Object> memoryManagerService = getMemoryManagerService();
+
+ memoryManagerService.init( 1, BUFFER_SIZE );
+
+ Pointer<Object> lastPointer = null;
+ for ( int i = 0; i < NUMBER_OF_OBJECTS; i++ )
+ {
+ Pointer<Object> pointer = memoryManagerService.store( SMALL_PAYLOAD );
+ Assert.assertNotNull( pointer );
+ lastPointer = pointer;
+ }
+
+ // Buffer is fully used.
+ Assert.assertEquals( BUFFER_SIZE, memoryManagerService.used() );
+
+ Assert.assertNotNull( lastPointer );
+ memoryManagerService.free( lastPointer );
+
+ Pointer<Object> pointerNotNull = memoryManagerService.store( SMALL_PAYLOAD );
+ Assert.assertNotNull( pointerNotNull );
+
+ // Buffer again fully used.
+ Assert.assertEquals( BUFFER_SIZE, memoryManagerService.used() );
+
+ }
+
+ @Test
+ public void testRandomPayload()
+ {
+
+ final int NUMBER_OF_OBJECTS = 10;
+ final int BUFFER_SIZE = NUMBER_OF_OBJECTS * SMALL_PAYLOAD.length;
+
+ final MemoryManagerService<Object> memoryManagerService = getMemoryManagerService();
+
+ memoryManagerService.init( 1, BUFFER_SIZE );
+
+ for ( int i = 0; i < NUMBER_OF_OBJECTS; i++ )
+ {
+ byte[] payload = MemoryTestUtils.generateRandomPayload( SMALL_PAYLOAD.length );
+ Pointer<Object> pointer = memoryManagerService.store( payload );
+ Assert.assertNotNull( pointer );
+ byte[] fetchedPayload = memoryManagerService.retrieve( pointer );
+ Assert.assertEquals( new String( payload ), new String( fetchedPayload ) );
+ if ( R.nextBoolean() )
+ {
+ memoryManagerService.free( pointer );
+ }
+ }
+
+ memoryManagerService.clear();
+
+ for ( int i = 0; i < NUMBER_OF_OBJECTS; i++ )
+ {
+ byte[] payload = MemoryTestUtils.generateRandomPayload( SMALL_PAYLOAD.length );
+ Pointer<Object> pointer = memoryManagerService.store( payload );
+ Assert.assertNotNull( pointer );
+ byte[] fetchedPayload = memoryManagerService.retrieve( pointer );
+ Assert.assertEquals( new String( payload ), new String( fetchedPayload ) );
+ if ( R.nextBoolean() )
+ {
+ memoryManagerService.free( pointer );
+ i--;
+ }
+ }
+
+ memoryManagerService.clear();
+
+ Pointer<Object> pointer = null;
+ do
+ {
+ byte[] payload = MemoryTestUtils.generateRandomPayload( R.nextInt( BUFFER_SIZE / 4 + 1 ) );
+ pointer = memoryManagerService.store( payload );
+ if ( pointer != null && R.nextBoolean() )
+ {
+ memoryManagerService.free( pointer );
+ }
+ }
+ while ( pointer != null );
+
+ }
+
+}
diff --git a/directmemory-cache/src/test/java/org/apache/directmemory/memory/MemoryManagerTest.java b/directmemory-cache/src/test/java/org/apache/directmemory/memory/MemoryManagerTest.java
new file mode 100644
index 0000000..32a6640
--- /dev/null
+++ b/directmemory-cache/src/test/java/org/apache/directmemory/memory/MemoryManagerTest.java
@@ -0,0 +1,106 @@
+package org.apache.directmemory.memory;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import com.carrotsearch.junitbenchmarks.AbstractBenchmark;
+import com.google.common.collect.Maps;
+import org.apache.directmemory.measures.Ram;
+import org.apache.directmemory.memory.MemoryManager;
+import org.apache.directmemory.memory.Pointer;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+public class MemoryManagerTest
+ extends AbstractBenchmark
+{
+
+
+ @BeforeClass
+ public static void init()
+ {
+ logger.info( "init" );
+ MemoryManager.init( 1, Ram.Mb( 100 ) );
+ }
+
+ @Test
+ public void smokeTest()
+ {
+ Random rnd = new Random();
+ int size = rnd.nextInt( 10 ) * (int) MemoryManager.capacity() / 100;
+ logger.info( "payload size=" + Ram.inKb( size ) );
+ Pointer<Object> p = MemoryManager.store( new byte[size] );
+ logger.info( "stored" );
+ assertNotNull( p );
+ //assertEquals(size,p.end);
+ assertEquals( size, p.getCapacity() );
+ assertEquals( size, MemoryManager.getMemoryManager().used() );
+ MemoryManager.free( p );
+ assertEquals( 0, MemoryManager.getMemoryManager().used() );
+ logger.info( "end" );
+ }
+
+ byte[] payload = "012345678901234567890123456789012345678901234567890123456789".getBytes();
+
+ @Test
+ public void fillupTest()
+ {
+ MemoryManager.clear();
+ logger.info( "payload size=" + Ram.inKb( payload.length ) );
+ long howMany = ( MemoryManager.capacity() / payload.length );
+ howMany = ( howMany * 90 ) / 100;
+
+ for ( int i = 0; i < howMany; i++ )
+ {
+ Pointer<Object> p = MemoryManager.store( payload );
+ assertNotNull( p );
+ }
+
+ logger.info( "" + howMany + " items stored" );
+ }
+
+
+ @Test
+ public void readTest()
+ {
+ for ( Pointer<Object> ptr : ((MemoryManagerServiceImpl<Object>)MemoryManager.getMemoryManager()).getPointers() )
+ {
+ if ( !ptr.isFree() )
+ {
+ byte[] res = MemoryManager.retrieve( ptr );
+ assertNotNull( res );
+ assertEquals( new String( payload ), new String( res ) );
+ }
+ }
+ }
+
+
+ private static Logger logger = LoggerFactory.getLogger( MallocTest.class );
+
+ final static Map<String, Byte> test = Maps.newHashMap();
+
+}
diff --git a/directmemory-cache/src/test/java/org/apache/directmemory/memory/MemoryTestUtils.java b/directmemory-cache/src/test/java/org/apache/directmemory/memory/MemoryTestUtils.java
new file mode 100644
index 0000000..dae116e
--- /dev/null
+++ b/directmemory-cache/src/test/java/org/apache/directmemory/memory/MemoryTestUtils.java
@@ -0,0 +1,41 @@
+package org.apache.directmemory.memory;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import java.util.Random;
+
+public class MemoryTestUtils
+{
+
+ private static final String PAYLOAD_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+ private static final Random R = new Random();
+
+ public static byte[] generateRandomPayload( int sizeInByte )
+ {
+ final StringBuilder sb = new StringBuilder( sizeInByte );
+ for ( int i = 0; i < sizeInByte; i++ )
+ {
+ sb.append( PAYLOAD_CHARS.charAt( R.nextInt( PAYLOAD_CHARS.length() ) ) );
+ }
+ return sb.toString().getBytes();
+ }
+
+}
diff --git a/directmemory-cache/src/test/java/org/apache/directmemory/memory/NIOTest.java b/directmemory-cache/src/test/java/org/apache/directmemory/memory/NIOTest.java
new file mode 100644
index 0000000..470c5a4
--- /dev/null
+++ b/directmemory-cache/src/test/java/org/apache/directmemory/memory/NIOTest.java
@@ -0,0 +1,89 @@
+package org.apache.directmemory.memory;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import org.apache.directmemory.measures.Ram;
+import org.apache.directmemory.memory.MemoryManager;
+import org.apache.directmemory.memory.Pointer;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.ByteBuffer;
+import java.util.Random;
+
+import static org.junit.Assert.*;
+
+@Ignore
+public class NIOTest
+{
+
+ private static Logger logger = LoggerFactory.getLogger( NIOTest.class );
+
+ @BeforeClass
+ public static void init()
+ {
+ byte[] payload = "012345678901234567890123456789012345678901234567890123456789".getBytes();
+
+ logger.info( "init" );
+ MemoryManager.init( 1, Ram.Mb( 100 ) );
+
+ logger.info( "payload size=" + Ram.inKb( payload.length ) );
+ long howMany = ( MemoryManager.capacity() / payload.length );
+ howMany = ( howMany * 50 ) / 100;
+
+ for ( int i = 0; i < howMany; i++ )
+ {
+ Pointer<Object> p = MemoryManager.store( payload );
+ assertNotNull( p );
+ }
+
+ logger.info( "" + howMany + " items stored" );
+ }
+
+ @Test
+ public void nioTest()
+ {
+ Random rnd = new Random();
+ int size = rnd.nextInt( 10 ) * (int) MemoryManager.capacity() / 100;
+ logger.info( "payload size=" + Ram.inKb( size ) );
+ Pointer<Object> p = MemoryManager.allocate( size );
+ ByteBuffer b = p.getDirectBuffer();
+ logger.info( "allocated" );
+ assertNotNull( p );
+ assertNotNull( b );
+
+ assertTrue( b.isDirect() );
+ assertEquals( 0, b.position() );
+ assertEquals( size, b.limit() );
+ assertEquals( size, b.capacity() );
+
+ byte[] check = MemoryManager.retrieve( p );
+
+ assertNotNull( check );
+
+ assertEquals( size, p.getCapacity() );
+ logger.info( "end" );
+ }
+
+
+}
diff --git a/directmemory-cache/src/test/java/org/apache/directmemory/memory/RoundRobinAllocationPolicyTest.java b/directmemory-cache/src/test/java/org/apache/directmemory/memory/RoundRobinAllocationPolicyTest.java
new file mode 100644
index 0000000..81ccf55
--- /dev/null
+++ b/directmemory-cache/src/test/java/org/apache/directmemory/memory/RoundRobinAllocationPolicyTest.java
@@ -0,0 +1,152 @@
+package org.apache.directmemory.memory;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.directmemory.memory.allocator.ByteBufferAllocator;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Unit test of {@link RoundRobinAllocationPolicy} class.
+ *
+ * @author benoit@noisette.ch
+ *
+ */
+public class RoundRobinAllocationPolicyTest
+{
+
+ private static final int NUMBER_OF_BUFFERS = 4;
+
+ List<ByteBufferAllocator> allocators;
+
+ RoundRobinAllocationPolicy allocationPolicy;
+
+ @Before
+ public void initAllocationPolicy()
+ {
+
+ allocators = new ArrayList<ByteBufferAllocator>();
+
+ for ( int i = 0; i < NUMBER_OF_BUFFERS; i++ )
+ {
+ allocators.add( new DummyByteBufferAllocator() );
+ }
+
+ allocationPolicy = new RoundRobinAllocationPolicy();
+ allocationPolicy.init( allocators );
+ }
+
+ @Test
+ public void testSequence()
+ {
+
+ assertEquals( allocators.get( 0 ), allocationPolicy.getActiveAllocator( null, 1 ) );
+ assertEquals( allocators.get( 1 ), allocationPolicy.getActiveAllocator( null, 1 ) );
+ assertEquals( allocators.get( 2 ), allocationPolicy.getActiveAllocator( null, 1 ) );
+ assertEquals( allocators.get( 3 ), allocationPolicy.getActiveAllocator( null, 1 ) );
+ assertEquals( allocators.get( 0 ), allocationPolicy.getActiveAllocator( null, 1 ) );
+ assertEquals( allocators.get( 1 ), allocationPolicy.getActiveAllocator( null, 1 ) );
+ assertEquals( allocators.get( 2 ), allocationPolicy.getActiveAllocator( null, 1 ) );
+ assertEquals( allocators.get( 3 ), allocationPolicy.getActiveAllocator( null, 1 ) );
+
+ assertNotNull( allocationPolicy.getActiveAllocator( null, 1 ) );
+ assertNotNull( allocationPolicy.getActiveAllocator( null, 2 ) );
+ assertNull( allocationPolicy.getActiveAllocator( null, 3 ) );
+
+ allocationPolicy.reset();
+
+ assertEquals( allocators.get( 0 ), allocationPolicy.getActiveAllocator( null, 1 ) );
+ assertEquals( allocators.get( 1 ), allocationPolicy.getActiveAllocator( null, 1 ) );
+ assertEquals( allocators.get( 2 ), allocationPolicy.getActiveAllocator( null, 1 ) );
+ assertEquals( allocators.get( 3 ), allocationPolicy.getActiveAllocator( null, 1 ) );
+ assertEquals( allocators.get( 0 ), allocationPolicy.getActiveAllocator( null, 1 ) );
+ assertEquals( allocators.get( 1 ), allocationPolicy.getActiveAllocator( null, 1 ) );
+ assertEquals( allocators.get( 2 ), allocationPolicy.getActiveAllocator( null, 1 ) );
+ assertEquals( allocators.get( 3 ), allocationPolicy.getActiveAllocator( null, 1 ) );
+
+ }
+
+
+ @Test
+ public void testMaxAllocation()
+ {
+
+ allocationPolicy.setMaxAllocations( 1 );
+
+ assertNotNull( allocationPolicy.getActiveAllocator( null, 1 ) );
+ assertNull( allocationPolicy.getActiveAllocator( null, 2 ) );
+ assertNull( allocationPolicy.getActiveAllocator( null, 3 ) );
+
+ }
+
+
+ /**
+ * Dummy {@link OffHeapMemoryBuffer} that do nothing.
+ */
+ private static class DummyByteBufferAllocator
+ implements ByteBufferAllocator
+ {
+
+ @Override
+ public void free( ByteBuffer buffer )
+ {
+ }
+
+ @Override
+ public ByteBuffer allocate( int size )
+ {
+ return null;
+ }
+
+ @Override
+ public void clear()
+ {
+ }
+
+ @Override
+ public int getCapacity()
+ {
+ return 0;
+ }
+
+ @Override
+ public int getNumber()
+ {
+ return 0;
+ }
+
+ @Override
+ public void close()
+ throws IOException
+ {
+
+ }
+
+ }
+}
diff --git a/directmemory-cache/src/test/java/org/apache/directmemory/memory/SlabMemoryManagerServiceTest.java b/directmemory-cache/src/test/java/org/apache/directmemory/memory/SlabMemoryManagerServiceTest.java
new file mode 100644
index 0000000..f32e8c2
--- /dev/null
+++ b/directmemory-cache/src/test/java/org/apache/directmemory/memory/SlabMemoryManagerServiceTest.java
@@ -0,0 +1,79 @@
+package org.apache.directmemory.memory;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import java.util.Collection;
+import java.util.HashSet;
+
+import org.apache.directmemory.memory.allocator.ByteBufferAllocator;
+import org.apache.directmemory.memory.allocator.FixedSizeByteBufferAllocatorImpl;
+import org.apache.directmemory.memory.allocator.SlabByteBufferAllocatorImpl;
+import org.junit.Test;
+
+public class SlabMemoryManagerServiceTest
+ extends AbstractMemoryManagerServiceTest
+{
+
+ @Override
+ protected MemoryManagerService<Object> instanciateMemoryManagerService( int bufferSize )
+ {
+ final MemoryManagerService<Object> mms = new MemoryManagerServiceImpl<Object>() {
+
+ @Override
+ protected ByteBufferAllocator instanciateByteBufferAllocator( int allocatorNumber, int size )
+ {
+ Collection<FixedSizeByteBufferAllocatorImpl> slabs = new HashSet<FixedSizeByteBufferAllocatorImpl>();
+
+ slabs.add( new FixedSizeByteBufferAllocatorImpl(0, size, SMALL_PAYLOAD_LENGTH / 2, 1) );
+ slabs.add( new FixedSizeByteBufferAllocatorImpl(1, size, SMALL_PAYLOAD_LENGTH, 1) );
+
+ final SlabByteBufferAllocatorImpl allocator = new SlabByteBufferAllocatorImpl( allocatorNumber, slabs, false );
+
+ return allocator;
+ }
+
+ };
+ mms.init( 1, bufferSize );
+ return mms;
+ }
+
+ @Override
+ @Test
+ public void testFullFillAndFreeAndClearBuffer()
+ {
+
+ }
+
+ @Override
+ @Test
+ public void testStoreAllocAndFree()
+ {
+
+ }
+
+
+ @Override
+ @Test
+ public void testAllocate()
+ {
+
+ }
+
+}
diff --git a/directmemory-cache/src/test/java/org/apache/directmemory/memory/Starter.java b/directmemory-cache/src/test/java/org/apache/directmemory/memory/Starter.java
new file mode 100644
index 0000000..903d21d
--- /dev/null
+++ b/directmemory-cache/src/test/java/org/apache/directmemory/memory/Starter.java
@@ -0,0 +1,132 @@
+package org.apache.directmemory.memory;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import org.apache.directmemory.measures.Ram;
+import org.apache.directmemory.memory.MemoryManager;
+import org.apache.directmemory.memory.allocator.ByteBufferAllocator;
+import org.apache.directmemory.memory.allocator.MergingByteBufferAllocatorImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.junit.Assert.assertNotNull;
+
+public class Starter
+{
+
+ private static Logger logger = LoggerFactory.getLogger( MallocTest.class );
+
+ /**
+ * @param args
+ */
+ public static void main( String[] args )
+ {
+
+ if ( args.length < 3 )
+ {
+ System.out.println( "DirectMemory (for real testers only!) - usage:" );
+ System.out.println(
+ " java -XX:MaxDirectMemorySize=XXXXm -XmxXXXXm -XmsXXXXm -jar dm-test.jar <buffers> <Mb for each buffer> <entries>" );
+ return;
+ }
+
+ int buffers = new Integer( args[0] );
+ int mb = new Integer( args[1] );
+ int entries = new Integer( args[2] );
+
+ logger.info( "buffers: " + buffers );
+ logger.info( "mb: " + mb );
+ logger.info( "entries: " + entries );
+
+ Starter starter = new Starter();
+ starter.rawInsertMultipleBuffers( buffers, mb, entries );
+ }
+
+
+ private static void dump( MemoryManagerService<Object> mms )
+ {
+ logger.info( "off-heap - allocated: " + Ram.inMb( mms.capacity() ) );
+ logger.info( "off-heap - used: " + Ram.inMb( mms.used() ) );
+ logger.info( "heap - max: " + Ram.inMb( Runtime.getRuntime().maxMemory() ) );
+ logger.info( "heap - allocated: " + Ram.inMb( Runtime.getRuntime().totalMemory() ) );
+ logger.info( "heap - free : " + Ram.inMb( Runtime.getRuntime().freeMemory() ) );
+ logger.info( "************************************************" );
+ }
+
+ public void dump( ByteBufferAllocator mem )
+ {
+ logger.info( "off-heap - buffer: " + mem.getNumber() );
+ logger.info( "off-heap - allocated: " + Ram.inMb( mem.getCapacity() ) );
+ logger.info( "heap - max: " + Ram.inMb( Runtime.getRuntime().maxMemory() ) );
+ logger.info( "heap - allocated: " + Ram.inMb( Runtime.getRuntime().totalMemory() ) );
+ logger.info( "heap - free : " + Ram.inMb( Runtime.getRuntime().freeMemory() ) );
+ logger.info( "************************************************" );
+ }
+
+ public void rawInsert( int megabytes, int howMany )
+ {
+ ByteBufferAllocator allocator = new MergingByteBufferAllocatorImpl( 1, megabytes * 1024 * 1024 );
+ assertNotNull( allocator );
+ int size = allocator.getCapacity() / ( howMany );
+ size -= size / 100 * 1;
+ logger.info( "payload size=" + size );
+ logger.info( "entries=" + howMany );
+
+ logger.info( "starting..." );
+
+ long start = System.currentTimeMillis();
+
+ for ( int i = 0; i < howMany; i++ )
+ {
+ allocator.allocate( size );
+ }
+
+ logger.info( "...done in " + ( System.currentTimeMillis() - start ) + " msecs." );
+ logger.info( "---------------------------------" );
+ dump( allocator );
+ }
+
+
+ public void rawInsertMultipleBuffers( int buffers, int megabytes, int howMany )
+ {
+ MemoryManager.init( buffers, Ram.Mb( megabytes ) );
+ int size = (int) ( MemoryManager.capacity() / ( howMany ) );
+ size -= size / 100 * 1;
+ logger.info( "payload size=" + size );
+ logger.info( "entries=" + howMany );
+
+ logger.info( "starting..." );
+
+ long start = System.currentTimeMillis();
+
+ byte[] payload = new byte[size];
+ for ( int i = 0; i < howMany; i++ )
+ {
+ MemoryManager.store( payload );
+ }
+
+ logger.info( "...done in " + ( System.currentTimeMillis() - start ) + " msecs." );
+ logger.info( "---------------------------------" );
+
+ dump( MemoryManager.getMemoryManager() );
+ }
+
+
+}
diff --git a/directmemory-cache/src/test/java/org/apache/directmemory/memory/allocator/FixedSizeByteBufferAllocatorImplTest.java b/directmemory-cache/src/test/java/org/apache/directmemory/memory/allocator/FixedSizeByteBufferAllocatorImplTest.java
new file mode 100644
index 0000000..7d6ed2e
--- /dev/null
+++ b/directmemory-cache/src/test/java/org/apache/directmemory/memory/allocator/FixedSizeByteBufferAllocatorImplTest.java
@@ -0,0 +1,130 @@
+package org.apache.directmemory.memory.allocator;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import java.nio.ByteBuffer;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+public class FixedSizeByteBufferAllocatorImplTest
+{
+ @Test
+ public void allocationTest()
+ {
+
+ ByteBufferAllocator allocator = new FixedSizeByteBufferAllocatorImpl( 0, 5000, 256, 1 );
+
+ ByteBuffer bf1 = allocator.allocate( 250 );
+ Assert.assertEquals( 256, bf1.capacity() );
+ Assert.assertEquals( 250, bf1.limit() );
+
+ ByteBuffer bf2 = allocator.allocate( 251 );
+ Assert.assertEquals( 256, bf2.capacity() );
+ Assert.assertEquals( 251, bf2.limit() );
+
+ ByteBuffer bf3 = allocator.allocate( 200 );
+ Assert.assertEquals( 256, bf3.capacity() );
+ Assert.assertEquals( 200, bf3.limit() );
+
+ ByteBuffer bf4 = allocator.allocate( 2000 );
+ Assert.assertNull( bf4 );
+
+ ByteBuffer bf5 = allocator.allocate( 298 );
+ Assert.assertNull( bf5 );
+
+ ByteBuffer bf6 = allocator.allocate( 128 );
+ Assert.assertEquals( 256, bf6.capacity() );
+ Assert.assertEquals( 128, bf6.limit() );
+
+ }
+
+
+ @Test
+ public void releaseTest()
+ {
+
+ ByteBufferAllocator allocator = new FixedSizeByteBufferAllocatorImpl( 0, 1000, 256, 1 );
+
+ ByteBuffer bf1 = allocator.allocate( 250 );
+ Assert.assertEquals( 256, bf1.capacity() );
+ Assert.assertEquals( 250, bf1.limit() );
+
+ ByteBuffer bf2 = allocator.allocate( 251 );
+ Assert.assertEquals( 256, bf2.capacity() );
+ Assert.assertEquals( 251, bf2.limit() );
+
+ ByteBuffer bf3 = allocator.allocate( 252 );
+ Assert.assertEquals( 256, bf3.capacity() );
+ Assert.assertEquals( 252, bf3.limit() );
+
+ ByteBuffer bf4 = allocator.allocate( 500 );
+ Assert.assertNull( bf4 );
+
+ allocator.free( bf1 );
+ allocator.free( bf2 );
+
+ ByteBuffer bf5 = allocator.allocate( 500 );
+ Assert.assertNull( bf5 );
+
+ ByteBuffer bf6 = allocator.allocate( 249 );
+ Assert.assertEquals( 256, bf6.capacity() );
+ Assert.assertEquals( 249, bf6.limit() );
+
+ ByteBuffer bf7 = allocator.allocate( 248 );
+ Assert.assertEquals( 256, bf7.capacity() );
+ Assert.assertEquals( 248, bf7.limit() );
+
+ }
+
+ @Test
+ public void allocateAndFreeTest()
+ {
+
+ ByteBufferAllocator allocator = new FixedSizeByteBufferAllocatorImpl( 0, 1000, 256, 1 );
+
+ for (int i = 0; i < 1000; i++)
+ {
+ ByteBuffer bf1 = allocator.allocate( 250 );
+ Assert.assertEquals( 256, bf1.capacity() );
+ Assert.assertEquals( 250, bf1.limit() );
+
+ allocator.free( bf1 );
+ }
+
+
+ ByteBuffer bf2 = allocator.allocate( 1000 );
+ Assert.assertNull( bf2 );
+
+ for (int i = 0; i < 3; i++)
+ {
+ ByteBuffer bf3 = allocator.allocate( 250 );
+ Assert.assertEquals( 256, bf3.capacity() );
+ Assert.assertEquals( 250, bf3.limit() );
+
+ }
+
+ ByteBuffer bf4 = allocator.allocate( 238 );
+ Assert.assertNull( bf4 );
+
+ }
+
+}
diff --git a/directmemory-cache/src/test/java/org/apache/directmemory/memory/allocator/MergingByteBufferAllocatorImplTest.java b/directmemory-cache/src/test/java/org/apache/directmemory/memory/allocator/MergingByteBufferAllocatorImplTest.java
new file mode 100644
index 0000000..6eefb31
--- /dev/null
+++ b/directmemory-cache/src/test/java/org/apache/directmemory/memory/allocator/MergingByteBufferAllocatorImplTest.java
@@ -0,0 +1,188 @@
+package org.apache.directmemory.memory.allocator;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+public class MergingByteBufferAllocatorImplTest
+{
+ @Test
+ public void allocationTest()
+ {
+
+ ByteBufferAllocator allocator = new MergingByteBufferAllocatorImpl( 0, 5000 );
+
+ ByteBuffer bf1 = allocator.allocate( 250 );
+ Assert.assertEquals( 250, bf1.capacity() );
+ Assert.assertEquals( 250, bf1.limit() );
+
+ ByteBuffer bf2 = allocator.allocate( 251 );
+ Assert.assertEquals( 251, bf2.capacity() );
+ Assert.assertEquals( 251, bf2.limit() );
+
+ ByteBuffer bf3 = allocator.allocate( 200 );
+ Assert.assertEquals( 200, bf3.capacity() );
+ Assert.assertEquals( 200, bf3.limit() );
+
+ ByteBuffer bf4 = allocator.allocate( 2000 );
+ Assert.assertEquals( 2000, bf4.capacity() );
+ Assert.assertEquals( 2000, bf4.limit() );
+
+ ByteBuffer bf5 = allocator.allocate( 2001 );
+ Assert.assertEquals( 2001, bf5.capacity() );
+ Assert.assertEquals( 2001, bf5.limit() );
+
+ ByteBuffer bf6 = allocator.allocate( 298 );
+ Assert.assertEquals( 298, bf6.capacity() );
+ Assert.assertEquals( 298, bf6.limit() );
+
+ ByteBuffer bf7 = allocator.allocate( 128 );
+ Assert.assertNull( bf7 );
+
+ }
+
+
+ @Test
+ public void releaseTest()
+ {
+
+ ByteBufferAllocator allocator = new MergingByteBufferAllocatorImpl( 0, 1000 );
+
+ ByteBuffer bf1 = allocator.allocate( 250 );
+ Assert.assertEquals( 250, bf1.capacity() );
+ Assert.assertEquals( 250, bf1.limit() );
+
+ ByteBuffer bf2 = allocator.allocate( 251 );
+ Assert.assertEquals( 251, bf2.capacity() );
+ Assert.assertEquals( 251, bf2.limit() );
+
+ ByteBuffer bf3 = allocator.allocate( 252 );
+ Assert.assertEquals( 252, bf3.capacity() );
+ Assert.assertEquals( 252, bf3.limit() );
+
+ ByteBuffer bf4 = allocator.allocate( 500 );
+ Assert.assertNull( bf4 );
+
+ allocator.free( bf1 );
+ allocator.free( bf2 );
+
+ ByteBuffer bf5 = allocator.allocate( 500 );
+ Assert.assertEquals( 501, bf5.capacity() );
+ Assert.assertEquals( 500, bf5.limit() );
+
+ }
+
+ @Test
+ public void allocateAndFreeTest()
+ {
+
+ ByteBufferAllocator allocator = new MergingByteBufferAllocatorImpl( 0, 1000 );
+
+ for (int i = 0; i < 1000; i++)
+ {
+ ByteBuffer bf1 = allocator.allocate( 250 );
+ Assert.assertEquals( 250, bf1.capacity() );
+ Assert.assertEquals( 250, bf1.limit() );
+
+ allocator.free( bf1 );
+ }
+
+
+ ByteBuffer bf2 = allocator.allocate( 1000 );
+ Assert.assertEquals( 1000, bf2.capacity() );
+ Assert.assertEquals( 1000, bf2.limit() );
+
+ }
+
+ @Test
+ public void allocationWithoutSplittingPointerTest()
+ {
+
+ ByteBufferAllocator allocator = new MergingByteBufferAllocatorImpl( 0, 200 );
+
+ ByteBuffer bf1 = allocator.allocate( 180 );
+ Assert.assertEquals( 200, bf1.capacity() );
+ Assert.assertEquals( 180, bf1.limit() );
+
+ ByteBuffer bf2 = allocator.allocate( 5 );
+ Assert.assertNull( bf2 );
+
+ allocator.free( bf1 );
+
+
+ ByteBuffer bf3 = allocator.allocate( 10 );
+ Assert.assertEquals( 10, bf3.capacity() );
+ Assert.assertEquals( 10, bf3.limit() );
+
+ ByteBuffer bf4 = allocator.allocate( 20 );
+ Assert.assertEquals( 20, bf4.capacity() );
+ Assert.assertEquals( 20, bf4.limit() );
+
+ ByteBuffer bf5 = allocator.allocate( 30 );
+ Assert.assertEquals( 30, bf5.capacity() );
+ Assert.assertEquals( 30, bf5.limit() );
+
+ allocator.free( bf4 );
+ allocator.free( bf3 );
+
+ ByteBuffer bf6 = allocator.allocate( 25 );
+ Assert.assertEquals( 30, bf6.capacity() );
+ Assert.assertEquals( 25, bf6.limit() );
+
+ }
+
+ @Test
+ public void allocationWithDifferentRatioTest()
+ {
+
+ MergingByteBufferAllocatorImpl allocator = new MergingByteBufferAllocatorImpl( 0, 200 );
+ allocator.setSizeRatioThreshold( 0.95 );
+
+ allocator.setSizeRatioThreshold( 10 );
+
+ ByteBuffer bf1 = allocator.allocate( 180 );
+ Assert.assertEquals( 180, bf1.capacity() );
+ Assert.assertEquals( 180, bf1.limit() );
+
+ ByteBuffer bf2 = allocator.allocate( 10 );
+ Assert.assertEquals( 20, bf2.capacity() );
+ Assert.assertEquals( 10, bf2.limit() );
+
+ }
+
+
+ @Test(expected = BufferOverflowException.class)
+ public void allocationThrowingBOExceptionTest()
+ {
+
+ MergingByteBufferAllocatorImpl allocator = new MergingByteBufferAllocatorImpl( 0, 200 );
+ allocator.setReturnNullWhenBufferIsFull( false );
+
+ allocator.allocate( 210 );
+ Assert.fail();
+ }
+
+
+}
diff --git a/directmemory-cache/src/test/java/org/apache/directmemory/memory/allocator/SlabByteBufferAllocatorImplTest.java b/directmemory-cache/src/test/java/org/apache/directmemory/memory/allocator/SlabByteBufferAllocatorImplTest.java
new file mode 100644
index 0000000..2d16337
--- /dev/null
+++ b/directmemory-cache/src/test/java/org/apache/directmemory/memory/allocator/SlabByteBufferAllocatorImplTest.java
@@ -0,0 +1,76 @@
+package org.apache.directmemory.memory.allocator;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+public class SlabByteBufferAllocatorImplTest
+{
+ @Test
+ public void allocationTest()
+ {
+
+ List<FixedSizeByteBufferAllocatorImpl> slabs = new ArrayList<FixedSizeByteBufferAllocatorImpl>();
+ slabs.add( new FixedSizeByteBufferAllocatorImpl( 0, 1024, 128, 1 ) );
+ slabs.add( new FixedSizeByteBufferAllocatorImpl( 1, 1024, 256, 1 ) );
+ slabs.add( new FixedSizeByteBufferAllocatorImpl( 2, 1024, 512, 1 ) );
+ slabs.add( new FixedSizeByteBufferAllocatorImpl( 3, 1024, 1024, 1 ) );
+
+
+ ByteBufferAllocator allocator = new SlabByteBufferAllocatorImpl( 0, slabs, false );
+
+ ByteBuffer bf1 = allocator.allocate( 250 );
+ Assert.assertEquals( 256, bf1.capacity() );
+ Assert.assertEquals( 250, bf1.limit() );
+
+ ByteBuffer bf2 = allocator.allocate( 251 );
+ Assert.assertEquals( 256, bf2.capacity() );
+ Assert.assertEquals( 251, bf2.limit() );
+
+ ByteBuffer bf3 = allocator.allocate( 200 );
+ Assert.assertEquals( 256, bf3.capacity() );
+ Assert.assertEquals( 200, bf3.limit() );
+
+ ByteBuffer bf4 = allocator.allocate( 100 );
+ Assert.assertEquals( 128, bf4.capacity() );
+ Assert.assertEquals( 100, bf4.limit() );
+
+ ByteBuffer bf5 = allocator.allocate( 550 );
+ Assert.assertEquals( 1024, bf5.capacity() );
+ Assert.assertEquals( 550, bf5.limit() );
+
+ ByteBuffer bf6 = allocator.allocate( 800 );
+ Assert.assertNull( bf6 );
+
+ allocator.free( bf5 );
+
+ ByteBuffer bf7 = allocator.allocate( 800 );
+ Assert.assertEquals( 1024, bf7.capacity() );
+ Assert.assertEquals( 800, bf7.limit() );
+
+ }
+
+}
diff --git a/directmemory-cache/src/test/java/org/apache/directmemory/misc/DummyPojo.java b/directmemory-cache/src/test/java/org/apache/directmemory/misc/DummyPojo.java
new file mode 100644
index 0000000..a41ad33
--- /dev/null
+++ b/directmemory-cache/src/test/java/org/apache/directmemory/misc/DummyPojo.java
@@ -0,0 +1,50 @@
+package org.apache.directmemory.misc;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import java.io.Serializable;
+
+
+public class DummyPojo
+ implements Serializable
+{
+ /**
+ * A dummy pojo implementation for test purposes
+ */
+ private static final long serialVersionUID = 1L;
+
+ public DummyPojo()
+ {
+
+ }
+
+ public DummyPojo( String name, int size )
+ {
+ this.name = name;
+ this.size = size;
+ payLoad = new String( new byte[size] );
+ }
+
+ public String name;
+
+ public int size;
+
+ public String payLoad;
+}
diff --git a/directmemory-cache/src/test/java/org/apache/directmemory/preliminary/MicroBenchmark.java b/directmemory-cache/src/test/java/org/apache/directmemory/preliminary/MicroBenchmark.java
new file mode 100644
index 0000000..d6369fb
--- /dev/null
+++ b/directmemory-cache/src/test/java/org/apache/directmemory/preliminary/MicroBenchmark.java
@@ -0,0 +1,211 @@
+package org.apache.directmemory.preliminary;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import com.carrotsearch.junitbenchmarks.AbstractBenchmark;
+import com.carrotsearch.junitbenchmarks.BenchmarkOptions;
+import com.carrotsearch.junitbenchmarks.annotation.AxisRange;
+import com.carrotsearch.junitbenchmarks.annotation.BenchmarkHistoryChart;
+import com.carrotsearch.junitbenchmarks.annotation.BenchmarkMethodChart;
+import com.carrotsearch.junitbenchmarks.annotation.LabelType;
+import com.google.common.collect.MapMaker;
+import com.google.common.collect.Maps;
+import org.apache.directmemory.measures.Ram;
+import org.junit.Before;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.ByteBuffer;
+import java.util.Map;
+import java.util.concurrent.ConcurrentMap;
+
+@AxisRange( min = 0, max = 1 )
+@BenchmarkMethodChart( filePrefix = "latest-microbench" )
+@BenchmarkOptions( benchmarkRounds = 1, warmupRounds = 0 )
+@BenchmarkHistoryChart( labelWith = LabelType.CUSTOM_KEY, maxRuns = 5 )
+
+public class MicroBenchmark
+ extends AbstractBenchmark
+{
+
+ private static Logger logger = LoggerFactory.getLogger( MicroBenchmark.class );
+
+
+ private int many = 3000000;
+
+ private int less = 300000;
+
+ @Before
+ public void cleanup()
+ {
+ dump( "Before cleanup" );
+ //Runtime.getRuntime().gc();
+ //dump("After cleanup");
+ logger.info( "************************************************" );
+ }
+
+ private void dump( String message )
+ {
+ logger.info( message );
+ logger.info( "Memory - max: " + Ram.inMb( Runtime.getRuntime().maxMemory() ) );
+ logger.info( "Memory - allocated: " + Ram.inMb( Runtime.getRuntime().totalMemory() ) );
+ logger.info( "Memory - free : " + Ram.inMb( Runtime.getRuntime().freeMemory() ) );
+ }
+
+ @Test
+ public void manySmallInHeapWithHashmap()
+ {
+ final Map<String, byte[]> test = Maps.newHashMap();
+ final byte payload[] = new byte[450];
+ long ops = many;
+ for ( int i = 0; i < ops; i++ )
+ {
+ final String key = "test-" + i;
+ test.put( key, payload.clone() );
+ }
+ logger.info( "many=" + ops );
+ logger.info( "payload.length=" + payload.length );
+ logger.info( "stored " + Ram.inMb( payload.length * ops ) );
+ }
+
+
+ @Test
+ public void manySmallInHeapWithMapMaker()
+ {
+ final byte payload[] = new byte[450];
+ int ops = many;
+
+ logger.info( "many=" + ops );
+ logger.info( "payload.length=" + payload.length );
+ pumpTheHeap( ops, payload );
+
+ }
+
+ @Test
+ public void manySmallOffHeap()
+ {
+
+ final byte payload[] = new byte[450];
+ int ops = many;
+
+ logger.info( "many=" + ops );
+ logger.info( "payload.length=" + payload.length );
+ pumpOffHeap( ops, payload );
+
+ }
+
+
+ @Test
+ public void lessButLargerOffHeap()
+ {
+
+ final byte payload[] = new byte[5120];
+ int ops = less;
+
+ logger.info( "less=" + ops );
+ logger.info( "payload.length=" + payload.length );
+ pumpOffHeap( ops, payload );
+
+ }
+
+ @Test
+ public void lessButLargerInHeap()
+ {
+
+ final byte payload[] = new byte[5120];
+ int ops = less;
+
+ logger.info( "less=" + ops );
+ logger.info( "payload.length=" + payload.length );
+ pumpTheHeap( ops, payload );
+
+ }
+
+ /*
+ *
+ *
+ * ExecutorService executor = Executors.newCachedThreadPool();
+ Callable<Object> task = new Callable<Object>() {
+ public Object call() {
+ return something.blockingMethod();
+ }
+ }
+ Future<Object> future = executor.submit(task);
+ try {
+ Object result = future.get(5, TimeUnit.SECONDS);
+ } catch (TimeoutException ex) {
+ // handle the timeout
+ } finally {
+ future.cancel(); // may or may not desire this
+ }
+ */
+
+
+ private void pumpOffHeap( int ops, byte[] payload )
+ {
+
+ ConcurrentMap<String, ByteBuffer> test = new MapMaker().concurrencyLevel( 4 ).makeMap();
+
+ logger.info( Ram.inMb( ops * payload.length ) + " in " + ops + " slices to store" );
+
+ ByteBuffer bulk = ByteBuffer.allocateDirect( ops * payload.length );
+
+ double started = System.currentTimeMillis();
+
+ for ( int i = 0; i < ops; i++ )
+ {
+ bulk.position( i * payload.length );
+ final ByteBuffer buf = bulk.duplicate();
+ buf.put( payload );
+ test.put( "test-" + i, buf );
+ }
+
+ double finished = System.currentTimeMillis();
+
+ logger.info( "done in " + ( finished - started ) / 1000 + " seconds" );
+
+ for ( ByteBuffer buf : test.values() )
+ {
+ buf.clear();
+ }
+ }
+
+ private void pumpTheHeap( int ops, byte[] payload )
+ {
+
+ ConcurrentMap<String, byte[]> test = new MapMaker().concurrencyLevel( 4 ).makeMap();
+
+ logger.info( Ram.inMb( ops * payload.length ) + " in " + ops + " slices to store" );
+
+ double started = System.currentTimeMillis();
+
+ for ( int i = 0; i < ops; i++ )
+ {
+ test.put( "test-" + i, payload.clone() );
+ }
+
+ double finished = System.currentTimeMillis();
+
+ logger.info( "done in " + ( finished - started ) / 1000 + " seconds" );
+ }
+
+
+}
diff --git a/directmemory-cache/src/test/java/org/apache/directmemory/preliminary/PreliminarBenchmark.java b/directmemory-cache/src/test/java/org/apache/directmemory/preliminary/PreliminarBenchmark.java
new file mode 100644
index 0000000..01e2ef7
--- /dev/null
+++ b/directmemory-cache/src/test/java/org/apache/directmemory/preliminary/PreliminarBenchmark.java
@@ -0,0 +1,236 @@
+package org.apache.directmemory.preliminary;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import com.carrotsearch.junitbenchmarks.AbstractBenchmark;
+import com.carrotsearch.junitbenchmarks.BenchmarkOptions;
+import com.google.common.collect.MapMaker;
+import com.google.common.collect.Maps;
+import org.apache.directmemory.measures.Ram;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.ByteBuffer;
+import java.util.Map;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.assertNotNull;
+
+public class PreliminarBenchmark
+ extends AbstractBenchmark
+{
+
+ private static Logger logger = LoggerFactory.getLogger( PreliminarBenchmark.class );
+
+ final static byte payload[] = new byte[1024];
+
+ // @Before
+// @After
+ public void cleanup()
+ {
+ dump( "Before cleanup" );
+ Runtime.getRuntime().gc();
+ dump( "After cleanup" );
+ logger.info( "************************************************" );
+ }
+
+ private void dump( String message )
+ {
+ logger.info( message );
+ logger.info( "Memory - max: " + Ram.inMb( Runtime.getRuntime().maxMemory() ) );
+ logger.info( "Memory - allocated: " + Ram.inMb( Runtime.getRuntime().totalMemory() ) );
+ logger.info( "Memory - free : " + Ram.inMb( Runtime.getRuntime().freeMemory() ) );
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 5, warmupRounds = 0 )
+ @Test
+ public void justMap()
+ {
+ final Map<String, byte[]> test = Maps.newHashMap();
+ long ops = 100000;
+ for ( int i = 0; i < ops; i++ )
+ {
+ final String key = "test-" + i;
+ test.put( key, payload.clone() );
+ }
+ logger.info( "stored " + Ram.inMb( payload.length * ops ) );
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 1, warmupRounds = 0 )
+ @Test
+ public void oneMillionSmallWithDirectBuffersOneAllocation()
+ {
+
+ logger.info( "payload is " + payload.length + " bytes" );
+ final byte payload[] = new byte[500];
+ int ops = 1000000;
+
+ pumpWithOneAllocation( ops, payload );
+
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 1, warmupRounds = 0 )
+ @Test
+ public void lessButLargerWithDirectBuffersOneAllocation()
+ {
+
+ logger.info( "payload is " + payload.length + " bytes" );
+ final byte payload[] = new byte[2048];
+ int ops = 210000;
+
+ pumpWithOneAllocation( ops, payload );
+
+ }
+
+ /*
+ *
+ *
+ * ExecutorService executor = Executors.newCachedThreadPool();
+ Callable<Object> task = new Callable<Object>() {
+ public Object call() {
+ return something.blockingMethod();
+ }
+ }
+ Future<Object> future = executor.submit(task);
+ try {
+ Object result = future.get(5, TimeUnit.SECONDS);
+ } catch (TimeoutException ex) {
+ // handle the timeout
+ } finally {
+ future.cancel(); // may or may not desire this
+ }
+ */
+
+ @BenchmarkOptions( benchmarkRounds = 5, warmupRounds = 0 )
+ @Test
+ public void withDirectBuffers150k()
+ {
+
+ logger.info( "payload is " + payload.length + " bytes" );
+ int ops = 150000;
+
+ pump( ops );
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 5, warmupRounds = 0 )
+ @Test
+ public void withDirectBuffers180k()
+ {
+
+ logger.info( "payload is " + payload.length + " bytes" );
+ int ops = 180000;
+
+ pump( ops );
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 5, warmupRounds = 0 )
+ @Test
+ public void withDirectBuffers150kAgain()
+ {
+
+ logger.info( "payload is " + payload.length + " bytes" );
+ int ops = 150000;
+
+ pump( ops );
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 1, warmupRounds = 0 )
+ @Test
+ public void testAllocation()
+ {
+
+ logger.info( "payload is " + payload.length + " bytes" );
+ logger.info( "allocating " + Ram.inMb( payload.length * 200000 ) );
+ ByteBuffer buf = ByteBuffer.allocateDirect( payload.length * 200000 );
+ assertNotNull( buf );
+ logger.info( "done" );
+ }
+
+
+ private void pumpWithOneAllocation( int ops, byte[] payload )
+ {
+
+ ConcurrentMap<String, ByteBuffer> test =
+ new MapMaker().concurrencyLevel( 4 ).maximumSize( ops ).expireAfterWrite( 10, TimeUnit.MINUTES ).makeMap();
+
+ logger.info( Ram.inMb( ops * payload.length ) + " in " + ops + " slices to store" );
+
+ ByteBuffer bulk = ByteBuffer.allocateDirect( ops * payload.length );
+
+ double started = System.currentTimeMillis();
+
+ for ( int i = 0; i < ops; i++ )
+ {
+ bulk.position( i * payload.length );
+ final ByteBuffer buf = bulk.duplicate();
+ buf.put( payload );
+ test.put( "test-" + i, buf );
+ }
+
+ double finished = System.currentTimeMillis();
+
+ logger.info( "done in " + ( finished - started ) / 1000 + " seconds" );
+
+ for ( ByteBuffer buf : test.values() )
+ {
+ buf.clear();
+ }
+ }
+
+ @BenchmarkOptions( benchmarkRounds = 5, warmupRounds = 0 )
+ @Test
+ public void withDirectBuffers100k()
+ {
+
+ logger.info( "payload is " + payload.length + " bytes" );
+ int ops = 100000;
+
+ pump( ops );
+ }
+
+ private void pump( int ops )
+ {
+ ConcurrentMap<String, ByteBuffer> test =
+ new MapMaker().concurrencyLevel( 4 ).maximumSize( ops ).expireAfterWrite( 10, TimeUnit.MINUTES ).makeMap();
+
+ logger.info( Ram.inMb( ops * payload.length ) + " to store" );
+
+ double started = System.currentTimeMillis();
+
+ for ( int i = 0; i < ops; i++ )
+ {
+ ByteBuffer buf = ByteBuffer.allocateDirect( payload.length );
+ buf.put( payload );
+ test.put( "test-" + i, buf );
+ }
+
+ double finished = System.currentTimeMillis();
+
+ logger.info( "done in " + ( finished - started ) / 1000 + " seconds" );
+
+ for ( ByteBuffer buf : test.values() )
+ {
+ buf.clear();
+ }
+ }
+
+}
diff --git a/directmemory-cache/src/test/java/org/apache/directmemory/serialization/DummyPojoSerializer.java b/directmemory-cache/src/test/java/org/apache/directmemory/serialization/DummyPojoSerializer.java
new file mode 100644
index 0000000..ce5ad6c
--- /dev/null
+++ b/directmemory-cache/src/test/java/org/apache/directmemory/serialization/DummyPojoSerializer.java
@@ -0,0 +1,69 @@
+package org.apache.directmemory.serialization;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+
+import org.apache.directmemory.measures.Ram;
+import org.apache.directmemory.misc.DummyPojo;
+
+public final class DummyPojoSerializer
+ implements Serializer
+{
+
+ final DummyPojo pojo = new DummyPojo( "test", Ram.Kb( 2 ) );
+
+ final byte[] data;
+
+ public DummyPojoSerializer()
+ {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try
+ {
+ ObjectOutputStream oos = new ObjectOutputStream( baos );
+ oos.writeObject( pojo );
+ oos.flush();
+ oos.close();
+ }
+ catch ( Exception e )
+ {
+ // should not happen
+ }
+ data = baos.toByteArray();
+ }
+
+ @Override
+ public <T> T deserialize( byte[] source, Class<T> clazz )
+ throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException
+ {
+ // testing puts only
+ return (T) pojo;
+ }
+
+ @Override
+ public <T> byte[] serialize( T obj )
+ throws IOException
+ {
+ return data;
+ }
+
+}
diff --git a/directmemory-cache/src/test/java/org/apache/directmemory/serialization/SerializerFactoryTestCase.java b/directmemory-cache/src/test/java/org/apache/directmemory/serialization/SerializerFactoryTestCase.java
new file mode 100644
index 0000000..2685c6c
--- /dev/null
+++ b/directmemory-cache/src/test/java/org/apache/directmemory/serialization/SerializerFactoryTestCase.java
@@ -0,0 +1,41 @@
+package org.apache.directmemory.serialization;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import static org.apache.directmemory.serialization.SerializerFactory.createNewSerializer;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public final class SerializerFactoryTestCase
+{
+
+ /*
+ * TODO please update the test once DM will be modularized!
+ */
+ @Test
+ public void verifySerializerInstantiation()
+ {
+ Serializer serializer = createNewSerializer();
+
+ assertTrue( serializer instanceof StandardSerializer );
+ }
+
+}
diff --git a/directmemory-cache/src/test/java/org/apache/directmemory/serialization/SerializerTest.java b/directmemory-cache/src/test/java/org/apache/directmemory/serialization/SerializerTest.java
new file mode 100644
index 0000000..7682816
--- /dev/null
+++ b/directmemory-cache/src/test/java/org/apache/directmemory/serialization/SerializerTest.java
@@ -0,0 +1,85 @@
+package org.apache.directmemory.serialization;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.IOException;
+
+import org.apache.directmemory.measures.Monitor;
+import org.apache.directmemory.measures.MonitorService;
+import org.apache.directmemory.measures.Ram;
+import org.apache.directmemory.misc.DummyPojo;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.carrotsearch.junitbenchmarks.AbstractBenchmark;
+import com.carrotsearch.junitbenchmarks.BenchmarkOptions;
+import com.carrotsearch.junitbenchmarks.annotation.AxisRange;
+import com.carrotsearch.junitbenchmarks.annotation.BenchmarkHistoryChart;
+import com.carrotsearch.junitbenchmarks.annotation.BenchmarkMethodChart;
+import com.carrotsearch.junitbenchmarks.annotation.LabelType;
+
+@AxisRange( min = 0, max = 1 )
+@BenchmarkMethodChart()
+@BenchmarkHistoryChart( labelWith = LabelType.CUSTOM_KEY, maxRuns = 5 )
+@BenchmarkOptions( benchmarkRounds = 2, warmupRounds = 1, concurrency = 1 )
+
+public class SerializerTest
+ extends AbstractBenchmark
+{
+
+ private static Logger logger = LoggerFactory.getLogger( SerializerTest.class );
+
+ private void testSerializer( String name, Serializer serializer, int size, int howMany )
+ throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException
+ {
+ logger.info( "begin " + serializer.getClass().toString() );
+ MonitorService stopWatch = Monitor.get( "serializer." + name + "." + size + "bytes" );
+ MonitorService stopWatch2 = Monitor.get( "deserializer." + name + "." + size + "bytes" );
+ DummyPojo pojo = new DummyPojo( "test", size );
+ for ( int i = 0; i < howMany; i++ )
+ {
+ long split = stopWatch.start();
+ final byte[] array = serializer.serialize( pojo );
+ stopWatch.stop( split );
+ long split2 = stopWatch2.start();
+ DummyPojo check = (DummyPojo) serializer.deserialize( array, pojo.getClass() );
+ stopWatch2.stop( split2 );
+ assertNotNull( "object has not been serialized", check );
+ assertEquals( pojo.name, check.name );
+ }
+ logger.info( "end serialize " + serializer.getClass().toString() + "\r\n" + stopWatch.toString() );
+ logger.info( "end deserialize " + serializer.getClass().toString() + "\r\n" + stopWatch2.toString() );
+ }
+
+ @Test
+ public void StandardTest()
+ throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException
+ {
+ testSerializer( "java-serialization", new StandardSerializer(), Ram.Kb( 1 ), 20000 );
+ testSerializer( "java-serialization", new StandardSerializer(), Ram.Kb( 2 ), 20000 );
+ testSerializer( "java-serialization", new StandardSerializer(), Ram.Kb( 3 ), 20000 );
+ testSerializer( "java-serialization", new StandardSerializer(), Ram.Kb( 4 ), 20000 );
+ }
+
+}
diff --git a/directmemory-cache/src/test/java/org/apache/directmemory/serialization/StandardSerializerTest.java b/directmemory-cache/src/test/java/org/apache/directmemory/serialization/StandardSerializerTest.java
new file mode 100644
index 0000000..a35caff
--- /dev/null
+++ b/directmemory-cache/src/test/java/org/apache/directmemory/serialization/StandardSerializerTest.java
@@ -0,0 +1,215 @@
+package org.apache.directmemory.serialization;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.Random;
+
+import static org.junit.Assert.*;
+
+public class StandardSerializerTest
+{
+
+ private static Serializer serializer;
+
+ private static final Random r = new Random();
+
+ @BeforeClass
+ public static void init()
+ {
+ serializer = new StandardSerializer();
+ }
+
+ @Test
+ public void validateBooleanSerialization()
+ throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException
+ {
+ byte[] payload = serializer.serialize( true );
+ boolean res = serializer.deserialize( payload, Boolean.class );
+ assertEquals( true, res );
+ }
+
+ @Test
+ public void validateBooleanArraySerialization()
+ throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException
+ {
+ byte[] payload = serializer.serialize( new boolean[]{ true } );
+ boolean[] res = serializer.deserialize( payload, boolean[].class );
+ assertEquals( 1, res.length );
+ assertTrue( res[0] );
+ }
+
+ @Test
+ public void validateByteSerialization()
+ throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException
+ {
+ byte[] payload = serializer.serialize( (byte) 127 );
+ byte res = serializer.deserialize( payload, Byte.class );
+ assertEquals( (byte) 127, res );
+ }
+
+ @Test
+ public void validateByteArraySerialization()
+ throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException
+ {
+ byte[] value = new byte[1024];
+ r.nextBytes( value );
+ byte[] payload = serializer.serialize( value );
+ byte[] res = serializer.deserialize( payload, byte[].class );
+ assertArrayEquals( value, res );
+ }
+
+ @Test
+ public void validateCharacterSerialization()
+ throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException
+ {
+ byte[] payload = serializer.serialize( 'z' );
+ char res = serializer.deserialize( payload, Character.class );
+ assertEquals( 'z', res );
+ }
+
+ @Test
+ public void validateCharacterArraySerialization()
+ throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException
+ {
+ char[] value = new char[]{ 'a', 'z', 'x', ' ', '-', '-' };
+ byte[] payload = serializer.serialize( value );
+ char[] res = serializer.deserialize( payload, char[].class );
+ assertArrayEquals( value, res );
+ }
+
+ @Test
+ public void validateDoubleSerialization()
+ throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException
+ {
+ byte[] payload = serializer.serialize( 1.2d );
+ double res = serializer.deserialize( payload, Double.class );
+ assertEquals( 1.2d, res, 0.0d );
+ }
+
+ @Test
+ public void validateDoubleArraySerialization()
+ throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException
+ {
+ double[] value = new double[]{ 1.1d, 3.1d, 0.1d };
+ byte[] payload = serializer.serialize( value );
+ double[] res = serializer.deserialize( payload, double[].class );
+ assertArrayEquals( value, res, 0.0d );
+ }
+
+ @Test
+ public void validateFloatSerialization()
+ throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException
+ {
+ byte[] payload = serializer.serialize( 1.2f );
+ float res = serializer.deserialize( payload, Float.class );
+ assertEquals( 1.2f, res, 0.0d );
+ }
+
+ @Test
+ public void validateFloatArraySerialization()
+ throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException
+ {
+ float[] value = new float[]{ 1.1f, 4.05f, 55.5f };
+ byte[] payload = serializer.serialize( value );
+ float[] res = serializer.deserialize( payload, float[].class );
+ assertArrayEquals( value, res, 0.0f );
+ }
+
+ @Test
+ public void validateIntegerSerialization()
+ throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException
+ {
+ byte[] payload = serializer.serialize( 1 );
+ int res = serializer.deserialize( payload, Integer.class );
+ assertEquals( 1, res );
+ }
+
+ @Test
+ public void validateIntegerArraySerialization()
+ throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException
+ {
+ int[] value = new int[]{ 3, 1, -1 };
+ byte[] payload = serializer.serialize( value );
+ int[] res = serializer.deserialize( payload, int[].class );
+ assertArrayEquals( value, res );
+ }
+
+ @Test
+ public void validateLongSerialization()
+ throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException
+ {
+ byte[] payload = serializer.serialize( 1L );
+ long res = serializer.deserialize( payload, Long.class );
+ assertEquals( 1, res );
+ }
+
+ @Test
+ public void validateLongArraySerialization()
+ throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException
+ {
+ long[] value = new long[]{ 1l, 3l, 1212121212121l };
+ byte[] payload = serializer.serialize( value );
+ long[] res = serializer.deserialize( payload, long[].class );
+ assertArrayEquals( value, res );
+ }
+
+ @Test
+ public void validateShortSerialization()
+ throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException
+ {
+ byte[] payload = serializer.serialize( (short) 1234 );
+ short res = serializer.deserialize( payload, Short.class );
+ assertEquals( 1234, res );
+ }
+
+ @Test
+ public void validateShortArraySerialization()
+ throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException
+ {
+ short[] value = new short[]{ 1, -1, 4, 32767 };
+ byte[] payload = serializer.serialize( value );
+ short[] res = serializer.deserialize( payload, short[].class );
+ assertArrayEquals( value, res );
+ }
+
+ @Test
+ public void validateStringSerialization()
+ throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException
+ {
+ String value = "a sample string to serialize";
+ byte[] payload = serializer.serialize( value );
+ String res = serializer.deserialize( payload, String.class );
+ assertEquals( value, res );
+ }
+
+ @Test
+ public void validateStringArraySerialization()
+ throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException
+ {
+ String[] value = new String[]{ "String1", "", "String2" };
+ byte[] payload = serializer.serialize( value );
+ String[] res = serializer.deserialize( payload, String[].class );
+ assertArrayEquals( value, res );
+ }
+}
diff --git a/directmemory-cache/src/test/resources/logback-test.xml b/directmemory-cache/src/test/resources/logback-test.xml
new file mode 100644
index 0000000..de5484e
--- /dev/null
+++ b/directmemory-cache/src/test/resources/logback-test.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you 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.
+ -->
+<configuration>
+
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %method - %msg%n</Pattern>
+ </encoder>
+ </appender>
+
+
+ <logger name="org.apache.directmemory.serialization" level="warn">
+ <appender-ref ref="STDOUT"/>
+
+ </logger>
+
+ <logger name="org.apache.directmemory.measures" level="info">
+ <appender-ref ref="STDOUT"/>
+
+ </logger>
+
+ <logger name="org.apache.directmemory.memory" level="info">
+ <appender-ref ref="STDOUT"/>
+
+ </logger>
+
+ <logger name="org.apache.directmemory.cache" level="info">
+ <appender-ref ref="STDOUT"/>
+
+ </logger>
+
+</configuration>
\ No newline at end of file
diff --git a/directmemory-tests/pom.xml b/directmemory-tests/pom.xml
new file mode 100644
index 0000000..1dc9a37
--- /dev/null
+++ b/directmemory-tests/pom.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.directmemory</groupId>
+ <artifactId>directmemory</artifactId>
+ <version>0.1-incubating</version>
+ </parent>
+
+
+ <artifactId>directmemory-tests</artifactId>
+ <packaging>jar</packaging>
+
+ <name>Apache DirectMemory :: Tests</name>
+ <description>${project.name}</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.directmemory</groupId>
+ <artifactId>directmemory-cache</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/directmemory-tests/src/main/java/org/apache/directmemory/test/AbstractSerializerTest.java b/directmemory-tests/src/main/java/org/apache/directmemory/test/AbstractSerializerTest.java
new file mode 100644
index 0000000..e0605ae
--- /dev/null
+++ b/directmemory-tests/src/main/java/org/apache/directmemory/test/AbstractSerializerTest.java
@@ -0,0 +1,73 @@
+package org.apache.directmemory.test;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import org.apache.directmemory.serialization.Serializer;
+import org.apache.directmemory.serialization.SerializerFactory;
+import org.apache.directmemory.serialization.SerializerNotFoundException;
+import org.junit.Test;
+
+import static junit.framework.Assert.assertEquals;
+
+/**
+ * A kind of tck test for serializer.
+ */
+public abstract class AbstractSerializerTest
+{
+ public abstract String getSerializerClassName();
+
+ @Test
+ public void factoryWithFQDN()
+ throws Exception
+ {
+ assertEquals( getSerializerClassName(),
+ SerializerFactory.createNewSerializer( getSerializerClassName() ).getClass().getName() );
+ }
+
+ @Test
+ public void simpleSerialization()
+ throws Exception
+ {
+ Wine wine = getWineInstance();
+
+ Serializer serializer = SerializerFactory.createNewSerializer( getSerializerClassName() );
+
+ byte[] bytes = serializer.serialize( wine );
+
+ Wine newWine = serializer.deserialize( bytes, Wine.class );
+
+ assertEquals( wine.getName(), newWine.getName() );
+ assertEquals( wine.getDescription(), newWine.getDescription() );
+
+ }
+
+ protected Wine getWineInstance()
+ {
+ return new Wine( "Gevrey-Chambertin", "nice French wine from Bourgogne" );
+ }
+
+ @Test( expected = SerializerNotFoundException.class )
+ public void serialiazerNotFoundException()
+ throws Exception
+ {
+ // toto.titi means foo.bar in French :-)
+ SerializerFactory.createNewSerializer( "toto.titi" );
+ }
+}
diff --git a/directmemory-tests/src/main/java/org/apache/directmemory/test/Wine.java b/directmemory-tests/src/main/java/org/apache/directmemory/test/Wine.java
new file mode 100644
index 0000000..5abd2e8
--- /dev/null
+++ b/directmemory-tests/src/main/java/org/apache/directmemory/test/Wine.java
@@ -0,0 +1,64 @@
+package org.apache.directmemory.test;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import java.io.Serializable;
+
+/**
+ * @author Olivier Lamy
+ */
+public class Wine
+ implements Serializable
+{
+ private String name;
+
+ private String description;
+
+ public Wine()
+ {
+ // no op
+ }
+
+ public Wine( String name, String description )
+ {
+ this.name = name;
+ this.description = description;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public void setName( String name )
+ {
+ this.name = name;
+ }
+
+ public String getDescription()
+ {
+ return description;
+ }
+
+ public void setDescription( String description )
+ {
+ this.description = description;
+ }
+}
diff --git a/directmemory-tests/src/test/java/org/apache/directmemory/test/StandardSerializerTest.java b/directmemory-tests/src/test/java/org/apache/directmemory/test/StandardSerializerTest.java
new file mode 100644
index 0000000..de85d16
--- /dev/null
+++ b/directmemory-tests/src/test/java/org/apache/directmemory/test/StandardSerializerTest.java
@@ -0,0 +1,34 @@
+package org.apache.directmemory.test;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import org.apache.directmemory.serialization.StandardSerializer;
+
+/**
+ * @author Olivier Lamy
+ */
+public class StandardSerializerTest
+ extends AbstractSerializerTest
+{
+ @Override
+ public String getSerializerClassName()
+ {
+ return StandardSerializer.class.getName();
+ }
+}
diff --git a/examples/pom.xml b/examples/pom.xml
new file mode 100644
index 0000000..c9c88e2
--- /dev/null
+++ b/examples/pom.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you 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.
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>org.apache.directmemory</groupId>
+ <artifactId>directmemory</artifactId>
+ <version>0.1-incubating</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>examples</artifactId>
+ <packaging>pom</packaging>
+ <name>Apache DirectMemory :: Examples</name>
+ <description>${project.name}</description>
+
+ <modules>
+ <module>server-example</module>
+ </modules>
+
+
+</project>
\ No newline at end of file
diff --git a/examples/server-example/pom.xml b/examples/server-example/pom.xml
new file mode 100644
index 0000000..72b86de
--- /dev/null
+++ b/examples/server-example/pom.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you 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.
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>org.apache.directmemory</groupId>
+ <artifactId>examples</artifactId>
+ <version>0.1-incubating</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>server-example</artifactId>
+ <packaging>war</packaging>
+ <name>Apache DirectMemory :: Examples :: Server Javascript</name>
+ <description>${project.name}</description>
+
+ <properties>
+ <tomcatRunPort>9091</tomcatRunPort>
+ <tomcatRunPath>/dm</tomcatRunPath>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.directmemory.server</groupId>
+ <artifactId>directmemory-server</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.directmemory</groupId>
+ <artifactId>directmemory-protostuff</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ <version>2.5</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <version>${slf4j.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <version>1.2.16</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.tomcat.maven</groupId>
+ <artifactId>tomcat7-maven-plugin</artifactId>
+ <configuration>
+ <port>${tomcatRunPort}</port>
+ <path>${tomcatRunPath}</path>
+ <warSourceDirectory>${basedir}/src/main/webapp</warSourceDirectory>
+ <systemProperties>
+ <directMemory.size>1000</directMemory.size>
+ </systemProperties>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
\ No newline at end of file
diff --git a/examples/server-example/src/main/resources/log4j.xml b/examples/server-example/src/main/resources/log4j.xml
new file mode 100644
index 0000000..04b1f88
--- /dev/null
+++ b/examples/server-example/src/main/resources/log4j.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you 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.
+ -->
+
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+
+ <appender name="console" class="org.apache.log4j.ConsoleAppender">
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="%d %-5p %c %x - %m%n"/>
+ </layout>
+ </appender>
+
+
+ <logger name="org.apache.directmemory">
+ <level value="debug"/>
+ </logger>
+
+ <root>
+ <priority value ="info" />
+ <appender-ref ref="console" />
+ </root>
+
+</log4j:configuration>
diff --git a/examples/server-example/src/main/webapp/WEB-INF/web.xml b/examples/server-example/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..f10e085
--- /dev/null
+++ b/examples/server-example/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ ~ Copyright 2005-2006 The Apache Software Foundation.
+ ~
+ ~ 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.
+ -->
+
+<web-app xmlns="http://java.sun.com/xml/ns/j2ee" version="2.4"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
+
+ <display-name>Apache DirectMemory</display-name>
+
+ <servlet>
+ <servlet-name>DirectMemoryServlet</servlet-name>
+ <servlet-class>org.apache.directmemory.server.services.DirectMemoryServlet</servlet-class>
+ <load-on-startup>1</load-on-startup>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>DirectMemoryServlet</servlet-name>
+ <url-pattern>/cache/*</url-pattern>
+ </servlet-mapping>
+
+
+</web-app>
\ No newline at end of file
diff --git a/examples/server-example/src/main/webapp/css/bootstrap.2.0.4.css b/examples/server-example/src/main/webapp/css/bootstrap.2.0.4.css
new file mode 100644
index 0000000..bb40c85
--- /dev/null
+++ b/examples/server-example/src/main/webapp/css/bootstrap.2.0.4.css
@@ -0,0 +1,4983 @@
+/*!
+ * Bootstrap v2.0.4
+ *
+ * Copyright 2012 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ */
+
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+nav,
+section {
+ display: block;
+}
+
+audio,
+canvas,
+video {
+ display: inline-block;
+ *display: inline;
+ *zoom: 1;
+}
+
+audio:not([controls]) {
+ display: none;
+}
+
+html {
+ font-size: 100%;
+ -webkit-text-size-adjust: 100%;
+ -ms-text-size-adjust: 100%;
+}
+
+a:focus {
+ outline: thin dotted #333;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
+
+a:hover,
+a:active {
+ outline: 0;
+}
+
+sub,
+sup {
+ position: relative;
+ font-size: 75%;
+ line-height: 0;
+ vertical-align: baseline;
+}
+
+sup {
+ top: -0.5em;
+}
+
+sub {
+ bottom: -0.25em;
+}
+
+img {
+ max-width: 100%;
+ vertical-align: middle;
+ border: 0;
+ -ms-interpolation-mode: bicubic;
+}
+
+#map_canvas img {
+ max-width: none;
+}
+
+button,
+input,
+select,
+textarea {
+ margin: 0;
+ font-size: 100%;
+ vertical-align: middle;
+}
+
+button,
+input {
+ *overflow: visible;
+ line-height: normal;
+}
+
+button::-moz-focus-inner,
+input::-moz-focus-inner {
+ padding: 0;
+ border: 0;
+}
+
+button,
+input[type="button"],
+input[type="reset"],
+input[type="submit"] {
+ cursor: pointer;
+ -webkit-appearance: button;
+}
+
+input[type="search"] {
+ -webkit-box-sizing: content-box;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+ -webkit-appearance: textfield;
+}
+
+input[type="search"]::-webkit-search-decoration,
+input[type="search"]::-webkit-search-cancel-button {
+ -webkit-appearance: none;
+}
+
+textarea {
+ overflow: auto;
+ vertical-align: top;
+}
+
+.clearfix {
+ *zoom: 1;
+}
+
+.clearfix:before,
+.clearfix:after {
+ display: table;
+ content: "";
+}
+
+.clearfix:after {
+ clear: both;
+}
+
+.hide-text {
+ font: 0/0 a;
+ color: transparent;
+ text-shadow: none;
+ background-color: transparent;
+ border: 0;
+}
+
+.input-block-level {
+ display: block;
+ width: 100%;
+ min-height: 28px;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+body {
+ margin: 0;
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-size: 13px;
+ line-height: 18px;
+ color: #333333;
+ background-color: #ffffff;
+}
+
+a {
+ color: #0088cc;
+ text-decoration: none;
+}
+
+a:hover {
+ color: #005580;
+ text-decoration: underline;
+}
+
+.row {
+ margin-left: -20px;
+ *zoom: 1;
+}
+
+.row:before,
+.row:after {
+ display: table;
+ content: "";
+}
+
+.row:after {
+ clear: both;
+}
+
+[class*="span"] {
+ float: left;
+ margin-left: 20px;
+}
+
+.container,
+.navbar-fixed-top .container,
+.navbar-fixed-bottom .container {
+ width: 940px;
+}
+
+.span12 {
+ width: 940px;
+}
+
+.span11 {
+ width: 860px;
+}
+
+.span10 {
+ width: 780px;
+}
+
+.span9 {
+ width: 700px;
+}
+
+.span8 {
+ width: 620px;
+}
+
+.span7 {
+ width: 540px;
+}
+
+.span6 {
+ width: 460px;
+}
+
+.span5 {
+ width: 380px;
+}
+
+.span4 {
+ width: 300px;
+}
+
+.span3 {
+ width: 220px;
+}
+
+.span2 {
+ width: 140px;
+}
+
+.span1 {
+ width: 60px;
+}
+
+.offset12 {
+ margin-left: 980px;
+}
+
+.offset11 {
+ margin-left: 900px;
+}
+
+.offset10 {
+ margin-left: 820px;
+}
+
+.offset9 {
+ margin-left: 740px;
+}
+
+.offset8 {
+ margin-left: 660px;
+}
+
+.offset7 {
+ margin-left: 580px;
+}
+
+.offset6 {
+ margin-left: 500px;
+}
+
+.offset5 {
+ margin-left: 420px;
+}
+
+.offset4 {
+ margin-left: 340px;
+}
+
+.offset3 {
+ margin-left: 260px;
+}
+
+.offset2 {
+ margin-left: 180px;
+}
+
+.offset1 {
+ margin-left: 100px;
+}
+
+.row-fluid {
+ width: 100%;
+ *zoom: 1;
+}
+
+.row-fluid:before,
+.row-fluid:after {
+ display: table;
+ content: "";
+}
+
+.row-fluid:after {
+ clear: both;
+}
+
+.row-fluid [class*="span"] {
+ display: block;
+ float: left;
+ width: 100%;
+ min-height: 28px;
+ margin-left: 2.127659574%;
+ *margin-left: 2.0744680846382977%;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+.row-fluid [class*="span"]:first-child {
+ margin-left: 0;
+}
+
+.row-fluid .span12 {
+ width: 99.99999998999999%;
+ *width: 99.94680850063828%;
+}
+
+.row-fluid .span11 {
+ width: 91.489361693%;
+ *width: 91.4361702036383%;
+}
+
+.row-fluid .span10 {
+ width: 82.97872339599999%;
+ *width: 82.92553190663828%;
+}
+
+.row-fluid .span9 {
+ width: 74.468085099%;
+ *width: 74.4148936096383%;
+}
+
+.row-fluid .span8 {
+ width: 65.95744680199999%;
+ *width: 65.90425531263828%;
+}
+
+.row-fluid .span7 {
+ width: 57.446808505%;
+ *width: 57.3936170156383%;
+}
+
+.row-fluid .span6 {
+ width: 48.93617020799999%;
+ *width: 48.88297871863829%;
+}
+
+.row-fluid .span5 {
+ width: 40.425531911%;
+ *width: 40.3723404216383%;
+}
+
+.row-fluid .span4 {
+ width: 31.914893614%;
+ *width: 31.8617021246383%;
+}
+
+.row-fluid .span3 {
+ width: 23.404255317%;
+ *width: 23.3510638276383%;
+}
+
+.row-fluid .span2 {
+ width: 14.89361702%;
+ *width: 14.8404255306383%;
+}
+
+.row-fluid .span1 {
+ width: 6.382978723%;
+ *width: 6.329787233638298%;
+}
+
+.container {
+ margin-right: auto;
+ margin-left: auto;
+ *zoom: 1;
+}
+
+.container:before,
+.container:after {
+ display: table;
+ content: "";
+}
+
+.container:after {
+ clear: both;
+}
+
+.container-fluid {
+ padding-right: 20px;
+ padding-left: 20px;
+ *zoom: 1;
+}
+
+.container-fluid:before,
+.container-fluid:after {
+ display: table;
+ content: "";
+}
+
+.container-fluid:after {
+ clear: both;
+}
+
+p {
+ margin: 0 0 9px;
+}
+
+p small {
+ font-size: 11px;
+ color: #999999;
+}
+
+.lead {
+ margin-bottom: 18px;
+ font-size: 20px;
+ font-weight: 200;
+ line-height: 27px;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ margin: 0;
+ font-family: inherit;
+ font-weight: bold;
+ color: inherit;
+ text-rendering: optimizelegibility;
+}
+
+h1 small,
+h2 small,
+h3 small,
+h4 small,
+h5 small,
+h6 small {
+ font-weight: normal;
+ color: #999999;
+}
+
+h1 {
+ font-size: 30px;
+ line-height: 36px;
+}
+
+h1 small {
+ font-size: 18px;
+}
+
+h2 {
+ font-size: 24px;
+ line-height: 36px;
+}
+
+h2 small {
+ font-size: 18px;
+}
+
+h3 {
+ font-size: 18px;
+ line-height: 27px;
+}
+
+h3 small {
+ font-size: 14px;
+}
+
+h4,
+h5,
+h6 {
+ line-height: 18px;
+}
+
+h4 {
+ font-size: 14px;
+}
+
+h4 small {
+ font-size: 12px;
+}
+
+h5 {
+ font-size: 12px;
+}
+
+h6 {
+ font-size: 11px;
+ color: #999999;
+ text-transform: uppercase;
+}
+
+.page-header {
+ padding-bottom: 17px;
+ margin: 18px 0;
+ border-bottom: 1px solid #eeeeee;
+}
+
+.page-header h1 {
+ line-height: 1;
+}
+
+ul,
+ol {
+ padding: 0;
+ margin: 0 0 9px 25px;
+}
+
+ul ul,
+ul ol,
+ol ol,
+ol ul {
+ margin-bottom: 0;
+}
+
+ul {
+ list-style: disc;
+}
+
+ol {
+ list-style: decimal;
+}
+
+li {
+ line-height: 18px;
+}
+
+ul.unstyled,
+ol.unstyled {
+ margin-left: 0;
+ list-style: none;
+}
+
+dl {
+ margin-bottom: 18px;
+}
+
+dt,
+dd {
+ line-height: 18px;
+}
+
+dt {
+ font-weight: bold;
+ line-height: 17px;
+}
+
+dd {
+ margin-left: 9px;
+}
+
+.dl-horizontal dt {
+ float: left;
+ width: 120px;
+ overflow: hidden;
+ clear: left;
+ text-align: right;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.dl-horizontal dd {
+ margin-left: 130px;
+}
+
+hr {
+ margin: 18px 0;
+ border: 0;
+ border-top: 1px solid #eeeeee;
+ border-bottom: 1px solid #ffffff;
+}
+
+strong {
+ font-weight: bold;
+}
+
+em {
+ font-style: italic;
+}
+
+.muted {
+ color: #999999;
+}
+
+abbr[title] {
+ cursor: help;
+ border-bottom: 1px dotted #999999;
+}
+
+abbr.initialism {
+ font-size: 90%;
+ text-transform: uppercase;
+}
+
+blockquote {
+ padding: 0 0 0 15px;
+ margin: 0 0 18px;
+ border-left: 5px solid #eeeeee;
+}
+
+blockquote p {
+ margin-bottom: 0;
+ font-size: 16px;
+ font-weight: 300;
+ line-height: 22.5px;
+}
+
+blockquote small {
+ display: block;
+ line-height: 18px;
+ color: #999999;
+}
+
+blockquote small:before {
+ content: '\2014 \00A0';
+}
+
+blockquote.pull-right {
+ float: right;
+ padding-right: 15px;
+ padding-left: 0;
+ border-right: 5px solid #eeeeee;
+ border-left: 0;
+}
+
+blockquote.pull-right p,
+blockquote.pull-right small {
+ text-align: right;
+}
+
+q:before,
+q:after,
+blockquote:before,
+blockquote:after {
+ content: "";
+}
+
+address {
+ display: block;
+ margin-bottom: 18px;
+ font-style: normal;
+ line-height: 18px;
+}
+
+small {
+ font-size: 100%;
+}
+
+cite {
+ font-style: normal;
+}
+
+code,
+pre {
+ padding: 0 3px 2px;
+ font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
+ font-size: 12px;
+ color: #333333;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+}
+
+code {
+ padding: 2px 4px;
+ color: #d14;
+ background-color: #f7f7f9;
+ border: 1px solid #e1e1e8;
+}
+
+pre {
+ display: block;
+ padding: 8.5px;
+ margin: 0 0 9px;
+ font-size: 12.025px;
+ line-height: 18px;
+ word-break: break-all;
+ word-wrap: break-word;
+ white-space: pre;
+ white-space: pre-wrap;
+ background-color: #f5f5f5;
+ border: 1px solid #ccc;
+ border: 1px solid rgba(0, 0, 0, 0.15);
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+
+pre.prettyprint {
+ margin-bottom: 18px;
+}
+
+pre code {
+ padding: 0;
+ color: inherit;
+ background-color: transparent;
+ border: 0;
+}
+
+.pre-scrollable {
+ max-height: 340px;
+ overflow-y: scroll;
+}
+
+form {
+ margin: 0 0 18px;
+}
+
+fieldset {
+ padding: 0;
+ margin: 0;
+ border: 0;
+}
+
+legend {
+ display: block;
+ width: 100%;
+ padding: 0;
+ margin-bottom: 27px;
+ font-size: 19.5px;
+ line-height: 36px;
+ color: #333333;
+ border: 0;
+ border-bottom: 1px solid #e5e5e5;
+}
+
+legend small {
+ font-size: 13.5px;
+ color: #999999;
+}
+
+label,
+input,
+button,
+select,
+textarea {
+ font-size: 13px;
+ font-weight: normal;
+ line-height: 18px;
+}
+
+input,
+button,
+select,
+textarea {
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+}
+
+label {
+ display: block;
+ margin-bottom: 5px;
+}
+
+select,
+textarea,
+input[type="text"],
+input[type="password"],
+input[type="datetime"],
+input[type="datetime-local"],
+input[type="date"],
+input[type="month"],
+input[type="time"],
+input[type="week"],
+input[type="number"],
+input[type="email"],
+input[type="url"],
+input[type="search"],
+input[type="tel"],
+input[type="color"],
+.uneditable-input {
+ display: inline-block;
+ height: 18px;
+ padding: 4px;
+ margin-bottom: 9px;
+ font-size: 13px;
+ line-height: 18px;
+ color: #555555;
+}
+
+input,
+textarea {
+ width: 210px;
+}
+
+textarea {
+ height: auto;
+}
+
+textarea,
+input[type="text"],
+input[type="password"],
+input[type="datetime"],
+input[type="datetime-local"],
+input[type="date"],
+input[type="month"],
+input[type="time"],
+input[type="week"],
+input[type="number"],
+input[type="email"],
+input[type="url"],
+input[type="search"],
+input[type="tel"],
+input[type="color"],
+.uneditable-input {
+ background-color: #ffffff;
+ border: 1px solid #cccccc;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -webkit-transition: border linear 0.2s, box-shadow linear 0.2s;
+ -moz-transition: border linear 0.2s, box-shadow linear 0.2s;
+ -ms-transition: border linear 0.2s, box-shadow linear 0.2s;
+ -o-transition: border linear 0.2s, box-shadow linear 0.2s;
+ transition: border linear 0.2s, box-shadow linear 0.2s;
+}
+
+textarea:focus,
+input[type="text"]:focus,
+input[type="password"]:focus,
+input[type="datetime"]:focus,
+input[type="datetime-local"]:focus,
+input[type="date"]:focus,
+input[type="month"]:focus,
+input[type="time"]:focus,
+input[type="week"]:focus,
+input[type="number"]:focus,
+input[type="email"]:focus,
+input[type="url"]:focus,
+input[type="search"]:focus,
+input[type="tel"]:focus,
+input[type="color"]:focus,
+.uneditable-input:focus {
+ border-color: rgba(82, 168, 236, 0.8);
+ outline: 0;
+ outline: thin dotted \9;
+ /* IE6-9 */
+
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
+}
+
+input[type="radio"],
+input[type="checkbox"] {
+ margin: 3px 0;
+ *margin-top: 0;
+ /* IE7 */
+
+ line-height: normal;
+ cursor: pointer;
+}
+
+input[type="submit"],
+input[type="reset"],
+input[type="button"],
+input[type="radio"],
+input[type="checkbox"] {
+ width: auto;
+}
+
+.uneditable-textarea {
+ width: auto;
+ height: auto;
+}
+
+select,
+input[type="file"] {
+ height: 28px;
+ /* In IE7, the height of the select element cannot be changed by height, only font-size */
+
+ *margin-top: 4px;
+ /* For IE7, add top margin to align select with labels */
+
+ line-height: 28px;
+}
+
+select {
+ width: 220px;
+ border: 1px solid #bbb;
+}
+
+select[multiple],
+select[size] {
+ height: auto;
+}
+
+select:focus,
+input[type="file"]:focus,
+input[type="radio"]:focus,
+input[type="checkbox"]:focus {
+ outline: thin dotted #333;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
+
+.radio,
+.checkbox {
+ min-height: 18px;
+ padding-left: 18px;
+}
+
+.radio input[type="radio"],
+.checkbox input[type="checkbox"] {
+ float: left;
+ margin-left: -18px;
+}
+
+.controls > .radio:first-child,
+.controls > .checkbox:first-child {
+ padding-top: 5px;
+}
+
+.radio.inline,
+.checkbox.inline {
+ display: inline-block;
+ padding-top: 5px;
+ margin-bottom: 0;
+ vertical-align: middle;
+}
+
+.radio.inline + .radio.inline,
+.checkbox.inline + .checkbox.inline {
+ margin-left: 10px;
+}
+
+.input-mini {
+ width: 60px;
+}
+
+.input-small {
+ width: 90px;
+}
+
+.input-medium {
+ width: 150px;
+}
+
+.input-large {
+ width: 210px;
+}
+
+.input-xlarge {
+ width: 270px;
+}
+
+.input-xxlarge {
+ width: 530px;
+}
+
+input[class*="span"],
+select[class*="span"],
+textarea[class*="span"],
+.uneditable-input[class*="span"],
+.row-fluid input[class*="span"],
+.row-fluid select[class*="span"],
+.row-fluid textarea[class*="span"],
+.row-fluid .uneditable-input[class*="span"] {
+ float: none;
+ margin-left: 0;
+}
+
+.input-append input[class*="span"],
+.input-append .uneditable-input[class*="span"],
+.input-prepend input[class*="span"],
+.input-prepend .uneditable-input[class*="span"],
+.row-fluid .input-prepend [class*="span"],
+.row-fluid .input-append [class*="span"] {
+ display: inline-block;
+}
+
+input,
+textarea,
+.uneditable-input {
+ margin-left: 0;
+}
+
+input.span12,
+textarea.span12,
+.uneditable-input.span12 {
+ width: 930px;
+}
+
+input.span11,
+textarea.span11,
+.uneditable-input.span11 {
+ width: 850px;
+}
+
+input.span10,
+textarea.span10,
+.uneditable-input.span10 {
+ width: 770px;
+}
+
+input.span9,
+textarea.span9,
+.uneditable-input.span9 {
+ width: 690px;
+}
+
+input.span8,
+textarea.span8,
+.uneditable-input.span8 {
+ width: 610px;
+}
+
+input.span7,
+textarea.span7,
+.uneditable-input.span7 {
+ width: 530px;
+}
+
+input.span6,
+textarea.span6,
+.uneditable-input.span6 {
+ width: 450px;
+}
+
+input.span5,
+textarea.span5,
+.uneditable-input.span5 {
+ width: 370px;
+}
+
+input.span4,
+textarea.span4,
+.uneditable-input.span4 {
+ width: 290px;
+}
+
+input.span3,
+textarea.span3,
+.uneditable-input.span3 {
+ width: 210px;
+}
+
+input.span2,
+textarea.span2,
+.uneditable-input.span2 {
+ width: 130px;
+}
+
+input.span1,
+textarea.span1,
+.uneditable-input.span1 {
+ width: 50px;
+}
+
+input[disabled],
+select[disabled],
+textarea[disabled],
+input[readonly],
+select[readonly],
+textarea[readonly] {
+ cursor: not-allowed;
+ background-color: #eeeeee;
+ border-color: #ddd;
+}
+
+input[type="radio"][disabled],
+input[type="checkbox"][disabled],
+input[type="radio"][readonly],
+input[type="checkbox"][readonly] {
+ background-color: transparent;
+}
+
+.control-group.warning > label,
+.control-group.warning .help-block,
+.control-group.warning .help-inline {
+ color: #c09853;
+}
+
+.control-group.warning .checkbox,
+.control-group.warning .radio,
+.control-group.warning input,
+.control-group.warning select,
+.control-group.warning textarea {
+ color: #c09853;
+ border-color: #c09853;
+}
+
+.control-group.warning .checkbox:focus,
+.control-group.warning .radio:focus,
+.control-group.warning input:focus,
+.control-group.warning select:focus,
+.control-group.warning textarea:focus {
+ border-color: #a47e3c;
+ -webkit-box-shadow: 0 0 6px #dbc59e;
+ -moz-box-shadow: 0 0 6px #dbc59e;
+ box-shadow: 0 0 6px #dbc59e;
+}
+
+.control-group.warning .input-prepend .add-on,
+.control-group.warning .input-append .add-on {
+ color: #c09853;
+ background-color: #fcf8e3;
+ border-color: #c09853;
+}
+
+.control-group.error > label,
+.control-group.error .help-block,
+.control-group.error .help-inline {
+ color: #b94a48;
+}
+
+.control-group.error .checkbox,
+.control-group.error .radio,
+.control-group.error input,
+.control-group.error select,
+.control-group.error textarea {
+ color: #b94a48;
+ border-color: #b94a48;
+}
+
+.control-group.error .checkbox:focus,
+.control-group.error .radio:focus,
+.control-group.error input:focus,
+.control-group.error select:focus,
+.control-group.error textarea:focus {
+ border-color: #953b39;
+ -webkit-box-shadow: 0 0 6px #d59392;
+ -moz-box-shadow: 0 0 6px #d59392;
+ box-shadow: 0 0 6px #d59392;
+}
+
+.control-group.error .input-prepend .add-on,
+.control-group.error .input-append .add-on {
+ color: #b94a48;
+ background-color: #f2dede;
+ border-color: #b94a48;
+}
+
+.control-group.success > label,
+.control-group.success .help-block,
+.control-group.success .help-inline {
+ color: #468847;
+}
+
+.control-group.success .checkbox,
+.control-group.success .radio,
+.control-group.success input,
+.control-group.success select,
+.control-group.success textarea {
+ color: #468847;
+ border-color: #468847;
+}
+
+.control-group.success .checkbox:focus,
+.control-group.success .radio:focus,
+.control-group.success input:focus,
+.control-group.success select:focus,
+.control-group.success textarea:focus {
+ border-color: #356635;
+ -webkit-box-shadow: 0 0 6px #7aba7b;
+ -moz-box-shadow: 0 0 6px #7aba7b;
+ box-shadow: 0 0 6px #7aba7b;
+}
+
+.control-group.success .input-prepend .add-on,
+.control-group.success .input-append .add-on {
+ color: #468847;
+ background-color: #dff0d8;
+ border-color: #468847;
+}
+
+input:focus:required:invalid,
+textarea:focus:required:invalid,
+select:focus:required:invalid {
+ color: #b94a48;
+ border-color: #ee5f5b;
+}
+
+input:focus:required:invalid:focus,
+textarea:focus:required:invalid:focus,
+select:focus:required:invalid:focus {
+ border-color: #e9322d;
+ -webkit-box-shadow: 0 0 6px #f8b9b7;
+ -moz-box-shadow: 0 0 6px #f8b9b7;
+ box-shadow: 0 0 6px #f8b9b7;
+}
+
+.form-actions {
+ padding: 17px 20px 18px;
+ margin-top: 18px;
+ margin-bottom: 18px;
+ background-color: #f5f5f5;
+ border-top: 1px solid #e5e5e5;
+ *zoom: 1;
+}
+
+.form-actions:before,
+.form-actions:after {
+ display: table;
+ content: "";
+}
+
+.form-actions:after {
+ clear: both;
+}
+
+.uneditable-input {
+ overflow: hidden;
+ white-space: nowrap;
+ cursor: not-allowed;
+ background-color: #ffffff;
+ border-color: #eee;
+ -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
+ -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
+}
+
+:-moz-placeholder {
+ color: #999999;
+}
+
+:-ms-input-placeholder {
+ color: #999999;
+}
+
+::-webkit-input-placeholder {
+ color: #999999;
+}
+
+.help-block,
+.help-inline {
+ color: #555555;
+}
+
+.help-block {
+ display: block;
+ margin-bottom: 9px;
+}
+
+.help-inline {
+ display: inline-block;
+ *display: inline;
+ padding-left: 5px;
+ vertical-align: middle;
+ *zoom: 1;
+}
+
+.input-prepend,
+.input-append {
+ margin-bottom: 5px;
+}
+
+.input-prepend input,
+.input-append input,
+.input-prepend select,
+.input-append select,
+.input-prepend .uneditable-input,
+.input-append .uneditable-input {
+ position: relative;
+ margin-bottom: 0;
+ *margin-left: 0;
+ vertical-align: middle;
+ -webkit-border-radius: 0 3px 3px 0;
+ -moz-border-radius: 0 3px 3px 0;
+ border-radius: 0 3px 3px 0;
+}
+
+.input-prepend input:focus,
+.input-append input:focus,
+.input-prepend select:focus,
+.input-append select:focus,
+.input-prepend .uneditable-input:focus,
+.input-append .uneditable-input:focus {
+ z-index: 2;
+}
+
+.input-prepend .uneditable-input,
+.input-append .uneditable-input {
+ border-left-color: #ccc;
+}
+
+.input-prepend .add-on,
+.input-append .add-on {
+ display: inline-block;
+ width: auto;
+ height: 18px;
+ min-width: 16px;
+ padding: 4px 5px;
+ font-weight: normal;
+ line-height: 18px;
+ text-align: center;
+ text-shadow: 0 1px 0 #ffffff;
+ vertical-align: middle;
+ background-color: #eeeeee;
+ border: 1px solid #ccc;
+}
+
+.input-prepend .add-on,
+.input-append .add-on,
+.input-prepend .btn,
+.input-append .btn {
+ margin-left: -1px;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.input-prepend .active,
+.input-append .active {
+ background-color: #a9dba9;
+ border-color: #46a546;
+}
+
+.input-prepend .add-on,
+.input-prepend .btn {
+ margin-right: -1px;
+}
+
+.input-prepend .add-on:first-child,
+.input-prepend .btn:first-child {
+ -webkit-border-radius: 3px 0 0 3px;
+ -moz-border-radius: 3px 0 0 3px;
+ border-radius: 3px 0 0 3px;
+}
+
+.input-append input,
+.input-append select,
+.input-append .uneditable-input {
+ -webkit-border-radius: 3px 0 0 3px;
+ -moz-border-radius: 3px 0 0 3px;
+ border-radius: 3px 0 0 3px;
+}
+
+.input-append .uneditable-input {
+ border-right-color: #ccc;
+ border-left-color: #eee;
+}
+
+.input-append .add-on:last-child,
+.input-append .btn:last-child {
+ -webkit-border-radius: 0 3px 3px 0;
+ -moz-border-radius: 0 3px 3px 0;
+ border-radius: 0 3px 3px 0;
+}
+
+.input-prepend.input-append input,
+.input-prepend.input-append select,
+.input-prepend.input-append .uneditable-input {
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.input-prepend.input-append .add-on:first-child,
+.input-prepend.input-append .btn:first-child {
+ margin-right: -1px;
+ -webkit-border-radius: 3px 0 0 3px;
+ -moz-border-radius: 3px 0 0 3px;
+ border-radius: 3px 0 0 3px;
+}
+
+.input-prepend.input-append .add-on:last-child,
+.input-prepend.input-append .btn:last-child {
+ margin-left: -1px;
+ -webkit-border-radius: 0 3px 3px 0;
+ -moz-border-radius: 0 3px 3px 0;
+ border-radius: 0 3px 3px 0;
+}
+
+.search-query {
+ padding-right: 14px;
+ padding-right: 4px \9;
+ padding-left: 14px;
+ padding-left: 4px \9;
+ /* IE7-8 doesn't have border-radius, so don't indent the padding */
+
+ margin-bottom: 0;
+ -webkit-border-radius: 14px;
+ -moz-border-radius: 14px;
+ border-radius: 14px;
+}
+
+.form-search input,
+.form-inline input,
+.form-horizontal input,
+.form-search textarea,
+.form-inline textarea,
+.form-horizontal textarea,
+.form-search select,
+.form-inline select,
+.form-horizontal select,
+.form-search .help-inline,
+.form-inline .help-inline,
+.form-horizontal .help-inline,
+.form-search .uneditable-input,
+.form-inline .uneditable-input,
+.form-horizontal .uneditable-input,
+.form-search .input-prepend,
+.form-inline .input-prepend,
+.form-horizontal .input-prepend,
+.form-search .input-append,
+.form-inline .input-append,
+.form-horizontal .input-append {
+ display: inline-block;
+ *display: inline;
+ margin-bottom: 0;
+ *zoom: 1;
+}
+
+.form-search .hide,
+.form-inline .hide,
+.form-horizontal .hide {
+ display: none;
+}
+
+.form-search label,
+.form-inline label {
+ display: inline-block;
+}
+
+.form-search .input-append,
+.form-inline .input-append,
+.form-search .input-prepend,
+.form-inline .input-prepend {
+ margin-bottom: 0;
+}
+
+.form-search .radio,
+.form-search .checkbox,
+.form-inline .radio,
+.form-inline .checkbox {
+ padding-left: 0;
+ margin-bottom: 0;
+ vertical-align: middle;
+}
+
+.form-search .radio input[type="radio"],
+.form-search .checkbox input[type="checkbox"],
+.form-inline .radio input[type="radio"],
+.form-inline .checkbox input[type="checkbox"] {
+ float: left;
+ margin-right: 3px;
+ margin-left: 0;
+}
+
+.control-group {
+ margin-bottom: 9px;
+}
+
+legend + .control-group {
+ margin-top: 18px;
+ -webkit-margin-top-collapse: separate;
+}
+
+.form-horizontal .control-group {
+ margin-bottom: 18px;
+ *zoom: 1;
+}
+
+.form-horizontal .control-group:before,
+.form-horizontal .control-group:after {
+ display: table;
+ content: "";
+}
+
+.form-horizontal .control-group:after {
+ clear: both;
+}
+
+.form-horizontal .control-label {
+ float: left;
+ width: 140px;
+ padding-top: 5px;
+ text-align: right;
+}
+
+.form-horizontal .controls {
+ *display: inline-block;
+ *padding-left: 20px;
+ margin-left: 160px;
+ *margin-left: 0;
+}
+
+.form-horizontal .controls:first-child {
+ *padding-left: 160px;
+}
+
+.form-horizontal .help-block {
+ margin-top: 9px;
+ margin-bottom: 0;
+}
+
+.form-horizontal .form-actions {
+ padding-left: 160px;
+}
+
+table {
+ max-width: 100%;
+ background-color: transparent;
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+.table {
+ width: 100%;
+ margin-bottom: 18px;
+}
+
+.table th,
+.table td {
+ padding: 8px;
+ line-height: 18px;
+ text-align: left;
+ vertical-align: top;
+ border-top: 1px solid #dddddd;
+}
+
+.table th {
+ font-weight: bold;
+}
+
+.table thead th {
+ vertical-align: bottom;
+}
+
+.table caption + thead tr:first-child th,
+.table caption + thead tr:first-child td,
+.table colgroup + thead tr:first-child th,
+.table colgroup + thead tr:first-child td,
+.table thead:first-child tr:first-child th,
+.table thead:first-child tr:first-child td {
+ border-top: 0;
+}
+
+.table tbody + tbody {
+ border-top: 2px solid #dddddd;
+}
+
+.table-condensed th,
+.table-condensed td {
+ padding: 4px 5px;
+}
+
+.table-bordered {
+ border: 1px solid #dddddd;
+ border-collapse: separate;
+ *border-collapse: collapsed;
+ border-left: 0;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+
+.table-bordered th,
+.table-bordered td {
+ border-left: 1px solid #dddddd;
+}
+
+.table-bordered caption + thead tr:first-child th,
+.table-bordered caption + tbody tr:first-child th,
+.table-bordered caption + tbody tr:first-child td,
+.table-bordered colgroup + thead tr:first-child th,
+.table-bordered colgroup + tbody tr:first-child th,
+.table-bordered colgroup + tbody tr:first-child td,
+.table-bordered thead:first-child tr:first-child th,
+.table-bordered tbody:first-child tr:first-child th,
+.table-bordered tbody:first-child tr:first-child td {
+ border-top: 0;
+}
+
+.table-bordered thead:first-child tr:first-child th:first-child,
+.table-bordered tbody:first-child tr:first-child td:first-child {
+ -webkit-border-top-left-radius: 4px;
+ border-top-left-radius: 4px;
+ -moz-border-radius-topleft: 4px;
+}
+
+.table-bordered thead:first-child tr:first-child th:last-child,
+.table-bordered tbody:first-child tr:first-child td:last-child {
+ -webkit-border-top-right-radius: 4px;
+ border-top-right-radius: 4px;
+ -moz-border-radius-topright: 4px;
+}
+
+.table-bordered thead:last-child tr:last-child th:first-child,
+.table-bordered tbody:last-child tr:last-child td:first-child {
+ -webkit-border-radius: 0 0 0 4px;
+ -moz-border-radius: 0 0 0 4px;
+ border-radius: 0 0 0 4px;
+ -webkit-border-bottom-left-radius: 4px;
+ border-bottom-left-radius: 4px;
+ -moz-border-radius-bottomleft: 4px;
+}
+
+.table-bordered thead:last-child tr:last-child th:last-child,
+.table-bordered tbody:last-child tr:last-child td:last-child {
+ -webkit-border-bottom-right-radius: 4px;
+ border-bottom-right-radius: 4px;
+ -moz-border-radius-bottomright: 4px;
+}
+
+.table-striped tbody tr:nth-child(odd) td,
+.table-striped tbody tr:nth-child(odd) th {
+ background-color: #f9f9f9;
+}
+
+.table tbody tr:hover td,
+.table tbody tr:hover th {
+ background-color: #f5f5f5;
+}
+
+table .span1 {
+ float: none;
+ width: 44px;
+ margin-left: 0;
+}
+
+table .span2 {
+ float: none;
+ width: 124px;
+ margin-left: 0;
+}
+
+table .span3 {
+ float: none;
+ width: 204px;
+ margin-left: 0;
+}
+
+table .span4 {
+ float: none;
+ width: 284px;
+ margin-left: 0;
+}
+
+table .span5 {
+ float: none;
+ width: 364px;
+ margin-left: 0;
+}
+
+table .span6 {
+ float: none;
+ width: 444px;
+ margin-left: 0;
+}
+
+table .span7 {
+ float: none;
+ width: 524px;
+ margin-left: 0;
+}
+
+table .span8 {
+ float: none;
+ width: 604px;
+ margin-left: 0;
+}
+
+table .span9 {
+ float: none;
+ width: 684px;
+ margin-left: 0;
+}
+
+table .span10 {
+ float: none;
+ width: 764px;
+ margin-left: 0;
+}
+
+table .span11 {
+ float: none;
+ width: 844px;
+ margin-left: 0;
+}
+
+table .span12 {
+ float: none;
+ width: 924px;
+ margin-left: 0;
+}
+
+table .span13 {
+ float: none;
+ width: 1004px;
+ margin-left: 0;
+}
+
+table .span14 {
+ float: none;
+ width: 1084px;
+ margin-left: 0;
+}
+
+table .span15 {
+ float: none;
+ width: 1164px;
+ margin-left: 0;
+}
+
+table .span16 {
+ float: none;
+ width: 1244px;
+ margin-left: 0;
+}
+
+table .span17 {
+ float: none;
+ width: 1324px;
+ margin-left: 0;
+}
+
+table .span18 {
+ float: none;
+ width: 1404px;
+ margin-left: 0;
+}
+
+table .span19 {
+ float: none;
+ width: 1484px;
+ margin-left: 0;
+}
+
+table .span20 {
+ float: none;
+ width: 1564px;
+ margin-left: 0;
+}
+
+table .span21 {
+ float: none;
+ width: 1644px;
+ margin-left: 0;
+}
+
+table .span22 {
+ float: none;
+ width: 1724px;
+ margin-left: 0;
+}
+
+table .span23 {
+ float: none;
+ width: 1804px;
+ margin-left: 0;
+}
+
+table .span24 {
+ float: none;
+ width: 1884px;
+ margin-left: 0;
+}
+
+[class^="icon-"],
+[class*=" icon-"] {
+ display: inline-block;
+ width: 14px;
+ height: 14px;
+ *margin-right: .3em;
+ line-height: 14px;
+ vertical-align: text-top;
+ background-image: url("../img/glyphicons-halflings.png");
+ background-position: 14px 14px;
+ background-repeat: no-repeat;
+}
+
+[class^="icon-"]:last-child,
+[class*=" icon-"]:last-child {
+ *margin-left: 0;
+}
+
+.icon-white {
+ background-image: url("../img/glyphicons-halflings-white.png");
+}
+
+.icon-glass {
+ background-position: 0 0;
+}
+
+.icon-music {
+ background-position: -24px 0;
+}
+
+.icon-search {
+ background-position: -48px 0;
+}
+
+.icon-envelope {
+ background-position: -72px 0;
+}
+
+.icon-heart {
+ background-position: -96px 0;
+}
+
+.icon-star {
+ background-position: -120px 0;
+}
+
+.icon-star-empty {
+ background-position: -144px 0;
+}
+
+.icon-user {
+ background-position: -168px 0;
+}
+
+.icon-film {
+ background-position: -192px 0;
+}
+
+.icon-th-large {
+ background-position: -216px 0;
+}
+
+.icon-th {
+ background-position: -240px 0;
+}
+
+.icon-th-list {
+ background-position: -264px 0;
+}
+
+.icon-ok {
+ background-position: -288px 0;
+}
+
+.icon-remove {
+ background-position: -312px 0;
+}
+
+.icon-zoom-in {
+ background-position: -336px 0;
+}
+
+.icon-zoom-out {
+ background-position: -360px 0;
+}
+
+.icon-off {
+ background-position: -384px 0;
+}
+
+.icon-signal {
+ background-position: -408px 0;
+}
+
+.icon-cog {
+ background-position: -432px 0;
+}
+
+.icon-trash {
+ background-position: -456px 0;
+}
+
+.icon-home {
+ background-position: 0 -24px;
+}
+
+.icon-file {
+ background-position: -24px -24px;
+}
+
+.icon-time {
+ background-position: -48px -24px;
+}
+
+.icon-road {
+ background-position: -72px -24px;
+}
+
+.icon-download-alt {
+ background-position: -96px -24px;
+}
+
+.icon-download {
+ background-position: -120px -24px;
+}
+
+.icon-upload {
+ background-position: -144px -24px;
+}
+
+.icon-inbox {
+ background-position: -168px -24px;
+}
+
+.icon-play-circle {
+ background-position: -192px -24px;
+}
+
+.icon-repeat {
+ background-position: -216px -24px;
+}
+
+.icon-refresh {
+ background-position: -240px -24px;
+}
+
+.icon-list-alt {
+ background-position: -264px -24px;
+}
+
+.icon-lock {
+ background-position: -287px -24px;
+}
+
+.icon-flag {
+ background-position: -312px -24px;
+}
+
+.icon-headphones {
+ background-position: -336px -24px;
+}
+
+.icon-volume-off {
+ background-position: -360px -24px;
+}
+
+.icon-volume-down {
+ background-position: -384px -24px;
+}
+
+.icon-volume-up {
+ background-position: -408px -24px;
+}
+
+.icon-qrcode {
+ background-position: -432px -24px;
+}
+
+.icon-barcode {
+ background-position: -456px -24px;
+}
+
+.icon-tag {
+ background-position: 0 -48px;
+}
+
+.icon-tags {
+ background-position: -25px -48px;
+}
+
+.icon-book {
+ background-position: -48px -48px;
+}
+
+.icon-bookmark {
+ background-position: -72px -48px;
+}
+
+.icon-print {
+ background-position: -96px -48px;
+}
+
+.icon-camera {
+ background-position: -120px -48px;
+}
+
+.icon-font {
+ background-position: -144px -48px;
+}
+
+.icon-bold {
+ background-position: -167px -48px;
+}
+
+.icon-italic {
+ background-position: -192px -48px;
+}
+
+.icon-text-height {
+ background-position: -216px -48px;
+}
+
+.icon-text-width {
+ background-position: -240px -48px;
+}
+
+.icon-align-left {
+ background-position: -264px -48px;
+}
+
+.icon-align-center {
+ background-position: -288px -48px;
+}
+
+.icon-align-right {
+ background-position: -312px -48px;
+}
+
+.icon-align-justify {
+ background-position: -336px -48px;
+}
+
+.icon-list {
+ background-position: -360px -48px;
+}
+
+.icon-indent-left {
+ background-position: -384px -48px;
+}
+
+.icon-indent-right {
+ background-position: -408px -48px;
+}
+
+.icon-facetime-video {
+ background-position: -432px -48px;
+}
+
+.icon-picture {
+ background-position: -456px -48px;
+}
+
+.icon-pencil {
+ background-position: 0 -72px;
+}
+
+.icon-map-marker {
+ background-position: -24px -72px;
+}
+
+.icon-adjust {
+ background-position: -48px -72px;
+}
+
+.icon-tint {
+ background-position: -72px -72px;
+}
+
+.icon-edit {
+ background-position: -96px -72px;
+}
+
+.icon-share {
+ background-position: -120px -72px;
+}
+
+.icon-check {
+ background-position: -144px -72px;
+}
+
+.icon-move {
+ background-position: -168px -72px;
+}
+
+.icon-step-backward {
+ background-position: -192px -72px;
+}
+
+.icon-fast-backward {
+ background-position: -216px -72px;
+}
+
+.icon-backward {
+ background-position: -240px -72px;
+}
+
+.icon-play {
+ background-position: -264px -72px;
+}
+
+.icon-pause {
+ background-position: -288px -72px;
+}
+
+.icon-stop {
+ background-position: -312px -72px;
+}
+
+.icon-forward {
+ background-position: -336px -72px;
+}
+
+.icon-fast-forward {
+ background-position: -360px -72px;
+}
+
+.icon-step-forward {
+ background-position: -384px -72px;
+}
+
+.icon-eject {
+ background-position: -408px -72px;
+}
+
+.icon-chevron-left {
+ background-position: -432px -72px;
+}
+
+.icon-chevron-right {
+ background-position: -456px -72px;
+}
+
+.icon-plus-sign {
+ background-position: 0 -96px;
+}
+
+.icon-minus-sign {
+ background-position: -24px -96px;
+}
+
+.icon-remove-sign {
+ background-position: -48px -96px;
+}
+
+.icon-ok-sign {
+ background-position: -72px -96px;
+}
+
+.icon-question-sign {
+ background-position: -96px -96px;
+}
+
+.icon-info-sign {
+ background-position: -120px -96px;
+}
+
+.icon-screenshot {
+ background-position: -144px -96px;
+}
+
+.icon-remove-circle {
+ background-position: -168px -96px;
+}
+
+.icon-ok-circle {
+ background-position: -192px -96px;
+}
+
+.icon-ban-circle {
+ background-position: -216px -96px;
+}
+
+.icon-arrow-left {
+ background-position: -240px -96px;
+}
+
+.icon-arrow-right {
+ background-position: -264px -96px;
+}
+
+.icon-arrow-up {
+ background-position: -289px -96px;
+}
+
+.icon-arrow-down {
+ background-position: -312px -96px;
+}
+
+.icon-share-alt {
+ background-position: -336px -96px;
+}
+
+.icon-resize-full {
+ background-position: -360px -96px;
+}
+
+.icon-resize-small {
+ background-position: -384px -96px;
+}
+
+.icon-plus {
+ background-position: -408px -96px;
+}
+
+.icon-minus {
+ background-position: -433px -96px;
+}
+
+.icon-asterisk {
+ background-position: -456px -96px;
+}
+
+.icon-exclamation-sign {
+ background-position: 0 -120px;
+}
+
+.icon-gift {
+ background-position: -24px -120px;
+}
+
+.icon-leaf {
+ background-position: -48px -120px;
+}
+
+.icon-fire {
+ background-position: -72px -120px;
+}
+
+.icon-eye-open {
+ background-position: -96px -120px;
+}
+
+.icon-eye-close {
+ background-position: -120px -120px;
+}
+
+.icon-warning-sign {
+ background-position: -144px -120px;
+}
+
+.icon-plane {
+ background-position: -168px -120px;
+}
+
+.icon-calendar {
+ background-position: -192px -120px;
+}
+
+.icon-random {
+ background-position: -216px -120px;
+}
+
+.icon-comment {
+ background-position: -240px -120px;
+}
+
+.icon-magnet {
+ background-position: -264px -120px;
+}
+
+.icon-chevron-up {
+ background-position: -288px -120px;
+}
+
+.icon-chevron-down {
+ background-position: -313px -119px;
+}
+
+.icon-retweet {
+ background-position: -336px -120px;
+}
+
+.icon-shopping-cart {
+ background-position: -360px -120px;
+}
+
+.icon-folder-close {
+ background-position: -384px -120px;
+}
+
+.icon-folder-open {
+ background-position: -408px -120px;
+}
+
+.icon-resize-vertical {
+ background-position: -432px -119px;
+}
+
+.icon-resize-horizontal {
+ background-position: -456px -118px;
+}
+
+.icon-hdd {
+ background-position: 0 -144px;
+}
+
+.icon-bullhorn {
+ background-position: -24px -144px;
+}
+
+.icon-bell {
+ background-position: -48px -144px;
+}
+
+.icon-certificate {
+ background-position: -72px -144px;
+}
+
+.icon-thumbs-up {
+ background-position: -96px -144px;
+}
+
+.icon-thumbs-down {
+ background-position: -120px -144px;
+}
+
+.icon-hand-right {
+ background-position: -144px -144px;
+}
+
+.icon-hand-left {
+ background-position: -168px -144px;
+}
+
+.icon-hand-up {
+ background-position: -192px -144px;
+}
+
+.icon-hand-down {
+ background-position: -216px -144px;
+}
+
+.icon-circle-arrow-right {
+ background-position: -240px -144px;
+}
+
+.icon-circle-arrow-left {
+ background-position: -264px -144px;
+}
+
+.icon-circle-arrow-up {
+ background-position: -288px -144px;
+}
+
+.icon-circle-arrow-down {
+ background-position: -312px -144px;
+}
+
+.icon-globe {
+ background-position: -336px -144px;
+}
+
+.icon-wrench {
+ background-position: -360px -144px;
+}
+
+.icon-tasks {
+ background-position: -384px -144px;
+}
+
+.icon-filter {
+ background-position: -408px -144px;
+}
+
+.icon-briefcase {
+ background-position: -432px -144px;
+}
+
+.icon-fullscreen {
+ background-position: -456px -144px;
+}
+
+.dropup,
+.dropdown {
+ position: relative;
+}
+
+.dropdown-toggle {
+ *margin-bottom: -3px;
+}
+
+.dropdown-toggle:active,
+.open .dropdown-toggle {
+ outline: 0;
+}
+
+.caret {
+ display: inline-block;
+ width: 0;
+ height: 0;
+ vertical-align: top;
+ border-top: 4px solid #000000;
+ border-right: 4px solid transparent;
+ border-left: 4px solid transparent;
+ content: "";
+ opacity: 0.3;
+ filter: alpha(opacity=30);
+}
+
+.dropdown .caret {
+ margin-top: 8px;
+ margin-left: 2px;
+}
+
+.dropdown:hover .caret,
+.open .caret {
+ opacity: 1;
+ filter: alpha(opacity=100);
+}
+
+.dropdown-menu {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ z-index: 1000;
+ display: none;
+ float: left;
+ min-width: 160px;
+ padding: 4px 0;
+ margin: 1px 0 0;
+ list-style: none;
+ background-color: #ffffff;
+ border: 1px solid #ccc;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ *border-right-width: 2px;
+ *border-bottom-width: 2px;
+ -webkit-border-radius: 5px;
+ -moz-border-radius: 5px;
+ border-radius: 5px;
+ -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ -webkit-background-clip: padding-box;
+ -moz-background-clip: padding;
+ background-clip: padding-box;
+}
+
+.dropdown-menu.pull-right {
+ right: 0;
+ left: auto;
+}
+
+.dropdown-menu .divider {
+ *width: 100%;
+ height: 1px;
+ margin: 8px 1px;
+ *margin: -5px 0 5px;
+ overflow: hidden;
+ background-color: #e5e5e5;
+ border-bottom: 1px solid #ffffff;
+}
+
+.dropdown-menu a {
+ display: block;
+ padding: 3px 15px;
+ clear: both;
+ font-weight: normal;
+ line-height: 18px;
+ color: #333333;
+ white-space: nowrap;
+}
+
+.dropdown-menu li > a:hover,
+.dropdown-menu .active > a,
+.dropdown-menu .active > a:hover {
+ color: #ffffff;
+ text-decoration: none;
+ background-color: #0088cc;
+}
+
+.open {
+ *z-index: 1000;
+}
+
+.open > .dropdown-menu {
+ display: block;
+}
+
+.pull-right > .dropdown-menu {
+ right: 0;
+ left: auto;
+}
+
+.dropup .caret,
+.navbar-fixed-bottom .dropdown .caret {
+ border-top: 0;
+ border-bottom: 4px solid #000000;
+ content: "\2191";
+}
+
+.dropup .dropdown-menu,
+.navbar-fixed-bottom .dropdown .dropdown-menu {
+ top: auto;
+ bottom: 100%;
+ margin-bottom: 1px;
+}
+
+.typeahead {
+ margin-top: 2px;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+
+.well {
+ min-height: 20px;
+ padding: 19px;
+ margin-bottom: 20px;
+ background-color: #f5f5f5;
+ border: 1px solid #eee;
+ border: 1px solid rgba(0, 0, 0, 0.05);
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
+}
+
+.well blockquote {
+ border-color: #ddd;
+ border-color: rgba(0, 0, 0, 0.15);
+}
+
+.well-large {
+ padding: 24px;
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+}
+
+.well-small {
+ padding: 9px;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+}
+
+.fade {
+ opacity: 0;
+ -webkit-transition: opacity 0.15s linear;
+ -moz-transition: opacity 0.15s linear;
+ -ms-transition: opacity 0.15s linear;
+ -o-transition: opacity 0.15s linear;
+ transition: opacity 0.15s linear;
+}
+
+.fade.in {
+ opacity: 1;
+}
+
+.collapse {
+ position: relative;
+ height: 0;
+ overflow: hidden;
+ -webkit-transition: height 0.35s ease;
+ -moz-transition: height 0.35s ease;
+ -ms-transition: height 0.35s ease;
+ -o-transition: height 0.35s ease;
+ transition: height 0.35s ease;
+}
+
+.collapse.in {
+ height: auto;
+}
+
+.close {
+ float: right;
+ font-size: 20px;
+ font-weight: bold;
+ line-height: 18px;
+ color: #000000;
+ text-shadow: 0 1px 0 #ffffff;
+ opacity: 0.2;
+ filter: alpha(opacity=20);
+}
+
+.close:hover {
+ color: #000000;
+ text-decoration: none;
+ cursor: pointer;
+ opacity: 0.4;
+ filter: alpha(opacity=40);
+}
+
+button.close {
+ padding: 0;
+ cursor: pointer;
+ background: transparent;
+ border: 0;
+ -webkit-appearance: none;
+}
+
+.btn {
+ display: inline-block;
+ *display: inline;
+ padding: 4px 10px 4px;
+ margin-bottom: 0;
+ *margin-left: .3em;
+ font-size: 13px;
+ line-height: 18px;
+ *line-height: 20px;
+ color: #333333;
+ text-align: center;
+ text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
+ vertical-align: middle;
+ cursor: pointer;
+ background-color: #f5f5f5;
+ *background-color: #e6e6e6;
+ background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));
+ background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6);
+ background-image: -o-linear-gradient(top, #ffffff, #e6e6e6);
+ background-image: linear-gradient(top, #ffffff, #e6e6e6);
+ background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6);
+ background-repeat: repeat-x;
+ border: 1px solid #cccccc;
+ *border: 0;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ border-color: #e6e6e6 #e6e6e6 #bfbfbf;
+ border-bottom-color: #b3b3b3;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);
+ filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+ *zoom: 1;
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+ -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.btn:hover,
+.btn:active,
+.btn.active,
+.btn.disabled,
+.btn[disabled] {
+ background-color: #e6e6e6;
+ *background-color: #d9d9d9;
+}
+
+.btn:active,
+.btn.active {
+ background-color: #cccccc \9;
+}
+
+.btn:first-child {
+ *margin-left: 0;
+}
+
+.btn:hover {
+ color: #333333;
+ text-decoration: none;
+ background-color: #e6e6e6;
+ *background-color: #d9d9d9;
+ /* Buttons in IE7 don't get borders, so darken on hover */
+
+ background-position: 0 -15px;
+ -webkit-transition: background-position 0.1s linear;
+ -moz-transition: background-position 0.1s linear;
+ -ms-transition: background-position 0.1s linear;
+ -o-transition: background-position 0.1s linear;
+ transition: background-position 0.1s linear;
+}
+
+.btn:focus {
+ outline: thin dotted #333;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
+
+.btn.active,
+.btn:active {
+ background-color: #e6e6e6;
+ background-color: #d9d9d9 \9;
+ background-image: none;
+ outline: 0;
+ -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+ -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.btn.disabled,
+.btn[disabled] {
+ cursor: default;
+ background-color: #e6e6e6;
+ background-image: none;
+ opacity: 0.65;
+ filter: alpha(opacity=65);
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+}
+
+.btn-large {
+ padding: 9px 14px;
+ font-size: 15px;
+ line-height: normal;
+ -webkit-border-radius: 5px;
+ -moz-border-radius: 5px;
+ border-radius: 5px;
+}
+
+.btn-large [class^="icon-"] {
+ margin-top: 1px;
+}
+
+.btn-small {
+ padding: 5px 9px;
+ font-size: 11px;
+ line-height: 16px;
+}
+
+.btn-small [class^="icon-"] {
+ margin-top: -1px;
+}
+
+.btn-mini {
+ padding: 2px 6px;
+ font-size: 11px;
+ line-height: 14px;
+}
+
+.btn-primary,
+.btn-primary:hover,
+.btn-warning,
+.btn-warning:hover,
+.btn-danger,
+.btn-danger:hover,
+.btn-success,
+.btn-success:hover,
+.btn-info,
+.btn-info:hover,
+.btn-inverse,
+.btn-inverse:hover {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+}
+
+.btn-primary.active,
+.btn-warning.active,
+.btn-danger.active,
+.btn-success.active,
+.btn-info.active,
+.btn-inverse.active {
+ color: rgba(255, 255, 255, 0.75);
+}
+
+.btn {
+ border-color: #ccc;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+}
+
+.btn-primary {
+ background-color: #0074cc;
+ *background-color: #0055cc;
+ background-image: -ms-linear-gradient(top, #0088cc, #0055cc);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0055cc));
+ background-image: -webkit-linear-gradient(top, #0088cc, #0055cc);
+ background-image: -o-linear-gradient(top, #0088cc, #0055cc);
+ background-image: -moz-linear-gradient(top, #0088cc, #0055cc);
+ background-image: linear-gradient(top, #0088cc, #0055cc);
+ background-repeat: repeat-x;
+ border-color: #0055cc #0055cc #003580;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#0088cc', endColorstr='#0055cc', GradientType=0);
+ filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+}
+
+.btn-primary:hover,
+.btn-primary:active,
+.btn-primary.active,
+.btn-primary.disabled,
+.btn-primary[disabled] {
+ background-color: #0055cc;
+ *background-color: #004ab3;
+}
+
+.btn-primary:active,
+.btn-primary.active {
+ background-color: #004099 \9;
+}
+
+.btn-warning {
+ background-color: #faa732;
+ *background-color: #f89406;
+ background-image: -ms-linear-gradient(top, #fbb450, #f89406);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));
+ background-image: -webkit-linear-gradient(top, #fbb450, #f89406);
+ background-image: -o-linear-gradient(top, #fbb450, #f89406);
+ background-image: -moz-linear-gradient(top, #fbb450, #f89406);
+ background-image: linear-gradient(top, #fbb450, #f89406);
+ background-repeat: repeat-x;
+ border-color: #f89406 #f89406 #ad6704;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0);
+ filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+}
+
+.btn-warning:hover,
+.btn-warning:active,
+.btn-warning.active,
+.btn-warning.disabled,
+.btn-warning[disabled] {
+ background-color: #f89406;
+ *background-color: #df8505;
+}
+
+.btn-warning:active,
+.btn-warning.active {
+ background-color: #c67605 \9;
+}
+
+.btn-danger {
+ background-color: #da4f49;
+ *background-color: #bd362f;
+ background-image: -ms-linear-gradient(top, #ee5f5b, #bd362f);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f));
+ background-image: -webkit-linear-gradient(top, #ee5f5b, #bd362f);
+ background-image: -o-linear-gradient(top, #ee5f5b, #bd362f);
+ background-image: -moz-linear-gradient(top, #ee5f5b, #bd362f);
+ background-image: linear-gradient(top, #ee5f5b, #bd362f);
+ background-repeat: repeat-x;
+ border-color: #bd362f #bd362f #802420;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#bd362f', GradientType=0);
+ filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+}
+
+.btn-danger:hover,
+.btn-danger:active,
+.btn-danger.active,
+.btn-danger.disabled,
+.btn-danger[disabled] {
+ background-color: #bd362f;
+ *background-color: #a9302a;
+}
+
+.btn-danger:active,
+.btn-danger.active {
+ background-color: #942a25 \9;
+}
+
+.btn-success {
+ background-color: #5bb75b;
+ *background-color: #51a351;
+ background-image: -ms-linear-gradient(top, #62c462, #51a351);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));
+ background-image: -webkit-linear-gradient(top, #62c462, #51a351);
+ background-image: -o-linear-gradient(top, #62c462, #51a351);
+ background-image: -moz-linear-gradient(top, #62c462, #51a351);
+ background-image: linear-gradient(top, #62c462, #51a351);
+ background-repeat: repeat-x;
+ border-color: #51a351 #51a351 #387038;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);
+ filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+}
+
+.btn-success:hover,
+.btn-success:active,
+.btn-success.active,
+.btn-success.disabled,
+.btn-success[disabled] {
+ background-color: #51a351;
+ *background-color: #499249;
+}
+
+.btn-success:active,
+.btn-success.active {
+ background-color: #408140 \9;
+}
+
+.btn-info {
+ background-color: #49afcd;
+ *background-color: #2f96b4;
+ background-image: -ms-linear-gradient(top, #5bc0de, #2f96b4);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4));
+ background-image: -webkit-linear-gradient(top, #5bc0de, #2f96b4);
+ background-image: -o-linear-gradient(top, #5bc0de, #2f96b4);
+ background-image: -moz-linear-gradient(top, #5bc0de, #2f96b4);
+ background-image: linear-gradient(top, #5bc0de, #2f96b4);
+ background-repeat: repeat-x;
+ border-color: #2f96b4 #2f96b4 #1f6377;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#5bc0de', endColorstr='#2f96b4', GradientType=0);
+ filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+}
+
+.btn-info:hover,
+.btn-info:active,
+.btn-info.active,
+.btn-info.disabled,
+.btn-info[disabled] {
+ background-color: #2f96b4;
+ *background-color: #2a85a0;
+}
+
+.btn-info:active,
+.btn-info.active {
+ background-color: #24748c \9;
+}
+
+.btn-inverse {
+ background-color: #414141;
+ *background-color: #222222;
+ background-image: -ms-linear-gradient(top, #555555, #222222);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#555555), to(#222222));
+ background-image: -webkit-linear-gradient(top, #555555, #222222);
+ background-image: -o-linear-gradient(top, #555555, #222222);
+ background-image: -moz-linear-gradient(top, #555555, #222222);
+ background-image: linear-gradient(top, #555555, #222222);
+ background-repeat: repeat-x;
+ border-color: #222222 #222222 #000000;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#555555', endColorstr='#222222', GradientType=0);
+ filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+}
+
+.btn-inverse:hover,
+.btn-inverse:active,
+.btn-inverse.active,
+.btn-inverse.disabled,
+.btn-inverse[disabled] {
+ background-color: #222222;
+ *background-color: #151515;
+}
+
+.btn-inverse:active,
+.btn-inverse.active {
+ background-color: #080808 \9;
+}
+
+button.btn,
+input[type="submit"].btn {
+ *padding-top: 2px;
+ *padding-bottom: 2px;
+}
+
+button.btn::-moz-focus-inner,
+input[type="submit"].btn::-moz-focus-inner {
+ padding: 0;
+ border: 0;
+}
+
+button.btn.btn-large,
+input[type="submit"].btn.btn-large {
+ *padding-top: 7px;
+ *padding-bottom: 7px;
+}
+
+button.btn.btn-small,
+input[type="submit"].btn.btn-small {
+ *padding-top: 3px;
+ *padding-bottom: 3px;
+}
+
+button.btn.btn-mini,
+input[type="submit"].btn.btn-mini {
+ *padding-top: 1px;
+ *padding-bottom: 1px;
+}
+
+.btn-group {
+ position: relative;
+ *margin-left: .3em;
+ *zoom: 1;
+}
+
+.btn-group:before,
+.btn-group:after {
+ display: table;
+ content: "";
+}
+
+.btn-group:after {
+ clear: both;
+}
+
+.btn-group:first-child {
+ *margin-left: 0;
+}
+
+.btn-group + .btn-group {
+ margin-left: 5px;
+}
+
+.btn-toolbar {
+ margin-top: 9px;
+ margin-bottom: 9px;
+}
+
+.btn-toolbar .btn-group {
+ display: inline-block;
+ *display: inline;
+ /* IE7 inline-block hack */
+
+ *zoom: 1;
+}
+
+.btn-group > .btn {
+ position: relative;
+ float: left;
+ margin-left: -1px;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.btn-group > .btn:first-child {
+ margin-left: 0;
+ -webkit-border-bottom-left-radius: 4px;
+ border-bottom-left-radius: 4px;
+ -webkit-border-top-left-radius: 4px;
+ border-top-left-radius: 4px;
+ -moz-border-radius-bottomleft: 4px;
+ -moz-border-radius-topleft: 4px;
+}
+
+.btn-group > .btn:last-child,
+.btn-group > .dropdown-toggle {
+ -webkit-border-top-right-radius: 4px;
+ border-top-right-radius: 4px;
+ -webkit-border-bottom-right-radius: 4px;
+ border-bottom-right-radius: 4px;
+ -moz-border-radius-topright: 4px;
+ -moz-border-radius-bottomright: 4px;
+}
+
+.btn-group > .btn.large:first-child {
+ margin-left: 0;
+ -webkit-border-bottom-left-radius: 6px;
+ border-bottom-left-radius: 6px;
+ -webkit-border-top-left-radius: 6px;
+ border-top-left-radius: 6px;
+ -moz-border-radius-bottomleft: 6px;
+ -moz-border-radius-topleft: 6px;
+}
+
+.btn-group > .btn.large:last-child,
+.btn-group > .large.dropdown-toggle {
+ -webkit-border-top-right-radius: 6px;
+ border-top-right-radius: 6px;
+ -webkit-border-bottom-right-radius: 6px;
+ border-bottom-right-radius: 6px;
+ -moz-border-radius-topright: 6px;
+ -moz-border-radius-bottomright: 6px;
+}
+
+.btn-group > .btn:hover,
+.btn-group > .btn:focus,
+.btn-group > .btn:active,
+.btn-group > .btn.active {
+ z-index: 2;
+}
+
+.btn-group .dropdown-toggle:active,
+.btn-group.open .dropdown-toggle {
+ outline: 0;
+}
+
+.btn-group > .dropdown-toggle {
+ *padding-top: 4px;
+ padding-right: 8px;
+ *padding-bottom: 4px;
+ padding-left: 8px;
+ -webkit-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+ -moz-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+ box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.btn-group > .btn-mini.dropdown-toggle {
+ padding-right: 5px;
+ padding-left: 5px;
+}
+
+.btn-group > .btn-small.dropdown-toggle {
+ *padding-top: 4px;
+ *padding-bottom: 4px;
+}
+
+.btn-group > .btn-large.dropdown-toggle {
+ padding-right: 12px;
+ padding-left: 12px;
+}
+
+.btn-group.open .dropdown-toggle {
+ background-image: none;
+ -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+ -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.btn-group.open .btn.dropdown-toggle {
+ background-color: #e6e6e6;
+}
+
+.btn-group.open .btn-primary.dropdown-toggle {
+ background-color: #0055cc;
+}
+
+.btn-group.open .btn-warning.dropdown-toggle {
+ background-color: #f89406;
+}
+
+.btn-group.open .btn-danger.dropdown-toggle {
+ background-color: #bd362f;
+}
+
+.btn-group.open .btn-success.dropdown-toggle {
+ background-color: #51a351;
+}
+
+.btn-group.open .btn-info.dropdown-toggle {
+ background-color: #2f96b4;
+}
+
+.btn-group.open .btn-inverse.dropdown-toggle {
+ background-color: #222222;
+}
+
+.btn .caret {
+ margin-top: 7px;
+ margin-left: 0;
+}
+
+.btn:hover .caret,
+.open.btn-group .caret {
+ opacity: 1;
+ filter: alpha(opacity=100);
+}
+
+.btn-mini .caret {
+ margin-top: 5px;
+}
+
+.btn-small .caret {
+ margin-top: 6px;
+}
+
+.btn-large .caret {
+ margin-top: 6px;
+ border-top-width: 5px;
+ border-right-width: 5px;
+ border-left-width: 5px;
+}
+
+.dropup .btn-large .caret {
+ border-top: 0;
+ border-bottom: 5px solid #000000;
+}
+
+.btn-primary .caret,
+.btn-warning .caret,
+.btn-danger .caret,
+.btn-info .caret,
+.btn-success .caret,
+.btn-inverse .caret {
+ border-top-color: #ffffff;
+ border-bottom-color: #ffffff;
+ opacity: 0.75;
+ filter: alpha(opacity=75);
+}
+
+.alert {
+ padding: 8px 35px 8px 14px;
+ margin-bottom: 18px;
+ color: #c09853;
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+ background-color: #fcf8e3;
+ border: 1px solid #fbeed5;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+
+.alert-heading {
+ color: inherit;
+}
+
+.alert .close {
+ position: relative;
+ top: -2px;
+ right: -21px;
+ line-height: 18px;
+}
+
+.alert-success {
+ color: #468847;
+ background-color: #dff0d8;
+ border-color: #d6e9c6;
+}
+
+.alert-danger,
+.alert-error {
+ color: #b94a48;
+ background-color: #f2dede;
+ border-color: #eed3d7;
+}
+
+.alert-info {
+ color: #3a87ad;
+ background-color: #d9edf7;
+ border-color: #bce8f1;
+}
+
+.alert-block {
+ padding-top: 14px;
+ padding-bottom: 14px;
+}
+
+.alert-block > p,
+.alert-block > ul {
+ margin-bottom: 0;
+}
+
+.alert-block p + p {
+ margin-top: 5px;
+}
+
+.nav {
+ margin-bottom: 18px;
+ margin-left: 0;
+ list-style: none;
+}
+
+.nav > li > a {
+ display: block;
+}
+
+.nav > li > a:hover {
+ text-decoration: none;
+ background-color: #eeeeee;
+}
+
+.nav > .pull-right {
+ float: right;
+}
+
+.nav .nav-header {
+ display: block;
+ padding: 3px 15px;
+ font-size: 11px;
+ font-weight: bold;
+ line-height: 18px;
+ color: #999999;
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+ text-transform: uppercase;
+}
+
+.nav li + .nav-header {
+ margin-top: 9px;
+}
+
+.nav-list {
+ padding-right: 15px;
+ padding-left: 15px;
+ margin-bottom: 0;
+}
+
+.nav-list > li > a,
+.nav-list .nav-header {
+ margin-right: -15px;
+ margin-left: -15px;
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+}
+
+.nav-list > li > a {
+ padding: 3px 15px;
+}
+
+.nav-list > .active > a,
+.nav-list > .active > a:hover {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);
+ background-color: #0088cc;
+}
+
+.nav-list [class^="icon-"] {
+ margin-right: 2px;
+}
+
+.nav-list .divider {
+ *width: 100%;
+ height: 1px;
+ margin: 8px 1px;
+ *margin: -5px 0 5px;
+ overflow: hidden;
+ background-color: #e5e5e5;
+ border-bottom: 1px solid #ffffff;
+}
+
+.nav-tabs,
+.nav-pills {
+ *zoom: 1;
+}
+
+.nav-tabs:before,
+.nav-pills:before,
+.nav-tabs:after,
+.nav-pills:after {
+ display: table;
+ content: "";
+}
+
+.nav-tabs:after,
+.nav-pills:after {
+ clear: both;
+}
+
+.nav-tabs > li,
+.nav-pills > li {
+ float: left;
+}
+
+.nav-tabs > li > a,
+.nav-pills > li > a {
+ padding-right: 12px;
+ padding-left: 12px;
+ margin-right: 2px;
+ line-height: 14px;
+}
+
+.nav-tabs {
+ border-bottom: 1px solid #ddd;
+}
+
+.nav-tabs > li {
+ margin-bottom: -1px;
+}
+
+.nav-tabs > li > a {
+ padding-top: 8px;
+ padding-bottom: 8px;
+ line-height: 18px;
+ border: 1px solid transparent;
+ -webkit-border-radius: 4px 4px 0 0;
+ -moz-border-radius: 4px 4px 0 0;
+ border-radius: 4px 4px 0 0;
+}
+
+.nav-tabs > li > a:hover {
+ border-color: #eeeeee #eeeeee #dddddd;
+}
+
+.nav-tabs > .active > a,
+.nav-tabs > .active > a:hover {
+ color: #555555;
+ cursor: default;
+ background-color: #ffffff;
+ border: 1px solid #ddd;
+ border-bottom-color: transparent;
+}
+
+.nav-pills > li > a {
+ padding-top: 8px;
+ padding-bottom: 8px;
+ margin-top: 2px;
+ margin-bottom: 2px;
+ -webkit-border-radius: 5px;
+ -moz-border-radius: 5px;
+ border-radius: 5px;
+}
+
+.nav-pills > .active > a,
+.nav-pills > .active > a:hover {
+ color: #ffffff;
+ background-color: #0088cc;
+}
+
+.nav-stacked > li {
+ float: none;
+}
+
+.nav-stacked > li > a {
+ margin-right: 0;
+}
+
+.nav-tabs.nav-stacked {
+ border-bottom: 0;
+}
+
+.nav-tabs.nav-stacked > li > a {
+ border: 1px solid #ddd;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.nav-tabs.nav-stacked > li:first-child > a {
+ -webkit-border-radius: 4px 4px 0 0;
+ -moz-border-radius: 4px 4px 0 0;
+ border-radius: 4px 4px 0 0;
+}
+
+.nav-tabs.nav-stacked > li:last-child > a {
+ -webkit-border-radius: 0 0 4px 4px;
+ -moz-border-radius: 0 0 4px 4px;
+ border-radius: 0 0 4px 4px;
+}
+
+.nav-tabs.nav-stacked > li > a:hover {
+ z-index: 2;
+ border-color: #ddd;
+}
+
+.nav-pills.nav-stacked > li > a {
+ margin-bottom: 3px;
+}
+
+.nav-pills.nav-stacked > li:last-child > a {
+ margin-bottom: 1px;
+}
+
+.nav-tabs .dropdown-menu {
+ -webkit-border-radius: 0 0 5px 5px;
+ -moz-border-radius: 0 0 5px 5px;
+ border-radius: 0 0 5px 5px;
+}
+
+.nav-pills .dropdown-menu {
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+
+.nav-tabs .dropdown-toggle .caret,
+.nav-pills .dropdown-toggle .caret {
+ margin-top: 6px;
+ border-top-color: #0088cc;
+ border-bottom-color: #0088cc;
+}
+
+.nav-tabs .dropdown-toggle:hover .caret,
+.nav-pills .dropdown-toggle:hover .caret {
+ border-top-color: #005580;
+ border-bottom-color: #005580;
+}
+
+.nav-tabs .active .dropdown-toggle .caret,
+.nav-pills .active .dropdown-toggle .caret {
+ border-top-color: #333333;
+ border-bottom-color: #333333;
+}
+
+.nav > .dropdown.active > a:hover {
+ color: #000000;
+ cursor: pointer;
+}
+
+.nav-tabs .open .dropdown-toggle,
+.nav-pills .open .dropdown-toggle,
+.nav > li.dropdown.open.active > a:hover {
+ color: #ffffff;
+ background-color: #999999;
+ border-color: #999999;
+}
+
+.nav li.dropdown.open .caret,
+.nav li.dropdown.open.active .caret,
+.nav li.dropdown.open a:hover .caret {
+ border-top-color: #ffffff;
+ border-bottom-color: #ffffff;
+ opacity: 1;
+ filter: alpha(opacity=100);
+}
+
+.tabs-stacked .open > a:hover {
+ border-color: #999999;
+}
+
+.tabbable {
+ *zoom: 1;
+}
+
+.tabbable:before,
+.tabbable:after {
+ display: table;
+ content: "";
+}
+
+.tabbable:after {
+ clear: both;
+}
+
+.tab-content {
+ overflow: auto;
+}
+
+.tabs-below > .nav-tabs,
+.tabs-right > .nav-tabs,
+.tabs-left > .nav-tabs {
+ border-bottom: 0;
+}
+
+.tab-content > .tab-pane,
+.pill-content > .pill-pane {
+ display: none;
+}
+
+.tab-content > .active,
+.pill-content > .active {
+ display: block;
+}
+
+.tabs-below > .nav-tabs {
+ border-top: 1px solid #ddd;
+}
+
+.tabs-below > .nav-tabs > li {
+ margin-top: -1px;
+ margin-bottom: 0;
+}
+
+.tabs-below > .nav-tabs > li > a {
+ -webkit-border-radius: 0 0 4px 4px;
+ -moz-border-radius: 0 0 4px 4px;
+ border-radius: 0 0 4px 4px;
+}
+
+.tabs-below > .nav-tabs > li > a:hover {
+ border-top-color: #ddd;
+ border-bottom-color: transparent;
+}
+
+.tabs-below > .nav-tabs > .active > a,
+.tabs-below > .nav-tabs > .active > a:hover {
+ border-color: transparent #ddd #ddd #ddd;
+}
+
+.tabs-left > .nav-tabs > li,
+.tabs-right > .nav-tabs > li {
+ float: none;
+}
+
+.tabs-left > .nav-tabs > li > a,
+.tabs-right > .nav-tabs > li > a {
+ min-width: 74px;
+ margin-right: 0;
+ margin-bottom: 3px;
+}
+
+.tabs-left > .nav-tabs {
+ float: left;
+ margin-right: 19px;
+ border-right: 1px solid #ddd;
+}
+
+.tabs-left > .nav-tabs > li > a {
+ margin-right: -1px;
+ -webkit-border-radius: 4px 0 0 4px;
+ -moz-border-radius: 4px 0 0 4px;
+ border-radius: 4px 0 0 4px;
+}
+
+.tabs-left > .nav-tabs > li > a:hover {
+ border-color: #eeeeee #dddddd #eeeeee #eeeeee;
+}
+
+.tabs-left > .nav-tabs .active > a,
+.tabs-left > .nav-tabs .active > a:hover {
+ border-color: #ddd transparent #ddd #ddd;
+ *border-right-color: #ffffff;
+}
+
+.tabs-right > .nav-tabs {
+ float: right;
+ margin-left: 19px;
+ border-left: 1px solid #ddd;
+}
+
+.tabs-right > .nav-tabs > li > a {
+ margin-left: -1px;
+ -webkit-border-radius: 0 4px 4px 0;
+ -moz-border-radius: 0 4px 4px 0;
+ border-radius: 0 4px 4px 0;
+}
+
+.tabs-right > .nav-tabs > li > a:hover {
+ border-color: #eeeeee #eeeeee #eeeeee #dddddd;
+}
+
+.tabs-right > .nav-tabs .active > a,
+.tabs-right > .nav-tabs .active > a:hover {
+ border-color: #ddd #ddd #ddd transparent;
+ *border-left-color: #ffffff;
+}
+
+.navbar {
+ *position: relative;
+ *z-index: 2;
+ margin-bottom: 18px;
+ overflow: visible;
+}
+
+.navbar-inner {
+ min-height: 40px;
+ padding-right: 20px;
+ padding-left: 20px;
+ background-color: #2c2c2c;
+ background-image: -moz-linear-gradient(top, #333333, #222222);
+ background-image: -ms-linear-gradient(top, #333333, #222222);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));
+ background-image: -webkit-linear-gradient(top, #333333, #222222);
+ background-image: -o-linear-gradient(top, #333333, #222222);
+ background-image: linear-gradient(top, #333333, #222222);
+ background-repeat: repeat-x;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);
+ -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1);
+ -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1);
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1);
+}
+
+.navbar .container {
+ width: auto;
+}
+
+.nav-collapse.collapse {
+ height: auto;
+}
+
+.navbar {
+ color: #999999;
+}
+
+.navbar .brand:hover {
+ text-decoration: none;
+}
+
+.navbar .brand {
+ display: block;
+ float: left;
+ padding: 8px 20px 12px;
+ margin-left: -20px;
+ font-size: 20px;
+ font-weight: 200;
+ line-height: 1;
+ color: #999999;
+}
+
+.navbar .navbar-text {
+ margin-bottom: 0;
+ line-height: 40px;
+}
+
+.navbar .navbar-link {
+ color: #999999;
+}
+
+.navbar .navbar-link:hover {
+ color: #ffffff;
+}
+
+.navbar .btn,
+.navbar .btn-group {
+ margin-top: 5px;
+}
+
+.navbar .btn-group .btn {
+ margin: 0;
+}
+
+.navbar-form {
+ margin-bottom: 0;
+ *zoom: 1;
+}
+
+.navbar-form:before,
+.navbar-form:after {
+ display: table;
+ content: "";
+}
+
+.navbar-form:after {
+ clear: both;
+}
+
+.navbar-form input,
+.navbar-form select,
+.navbar-form .radio,
+.navbar-form .checkbox {
+ margin-top: 5px;
+}
+
+.navbar-form input,
+.navbar-form select {
+ display: inline-block;
+ margin-bottom: 0;
+}
+
+.navbar-form input[type="image"],
+.navbar-form input[type="checkbox"],
+.navbar-form input[type="radio"] {
+ margin-top: 3px;
+}
+
+.navbar-form .input-append,
+.navbar-form .input-prepend {
+ margin-top: 6px;
+ white-space: nowrap;
+}
+
+.navbar-form .input-append input,
+.navbar-form .input-prepend input {
+ margin-top: 0;
+}
+
+.navbar-search {
+ position: relative;
+ float: left;
+ margin-top: 6px;
+ margin-bottom: 0;
+}
+
+.navbar-search .search-query {
+ padding: 4px 9px;
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-size: 13px;
+ font-weight: normal;
+ line-height: 1;
+ color: #ffffff;
+ background-color: #626262;
+ border: 1px solid #151515;
+ -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);
+ -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);
+ -webkit-transition: none;
+ -moz-transition: none;
+ -ms-transition: none;
+ -o-transition: none;
+ transition: none;
+}
+
+.navbar-search .search-query:-moz-placeholder {
+ color: #cccccc;
+}
+
+.navbar-search .search-query:-ms-input-placeholder {
+ color: #cccccc;
+}
+
+.navbar-search .search-query::-webkit-input-placeholder {
+ color: #cccccc;
+}
+
+.navbar-search .search-query:focus,
+.navbar-search .search-query.focused {
+ padding: 5px 10px;
+ color: #333333;
+ text-shadow: 0 1px 0 #ffffff;
+ background-color: #ffffff;
+ border: 0;
+ outline: 0;
+ -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);
+ -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);
+ box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);
+}
+
+.navbar-fixed-top,
+.navbar-fixed-bottom {
+ position: fixed;
+ right: 0;
+ left: 0;
+ z-index: 1030;
+ margin-bottom: 0;
+}
+
+.navbar-fixed-top .navbar-inner,
+.navbar-fixed-bottom .navbar-inner {
+ padding-right: 0;
+ padding-left: 0;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.navbar-fixed-top .container,
+.navbar-fixed-bottom .container {
+ width: 940px;
+}
+
+.navbar-fixed-top {
+ top: 0;
+}
+
+.navbar-fixed-bottom {
+ bottom: 0;
+}
+
+.navbar .nav {
+ position: relative;
+ left: 0;
+ display: block;
+ float: left;
+ margin: 0 10px 0 0;
+}
+
+.navbar .nav.pull-right {
+ float: right;
+}
+
+.navbar .nav > li {
+ display: block;
+ float: left;
+}
+
+.navbar .nav > li > a {
+ float: none;
+ padding: 9px 10px 11px;
+ line-height: 19px;
+ color: #999999;
+ text-decoration: none;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+}
+
+.navbar .btn {
+ display: inline-block;
+ padding: 4px 10px 4px;
+ margin: 5px 5px 6px;
+ line-height: 18px;
+}
+
+.navbar .btn-group {
+ padding: 5px 5px 6px;
+ margin: 0;
+}
+
+.navbar .nav > li > a:hover {
+ color: #ffffff;
+ text-decoration: none;
+ background-color: transparent;
+}
+
+.navbar .nav .active > a,
+.navbar .nav .active > a:hover {
+ color: #ffffff;
+ text-decoration: none;
+ background-color: #222222;
+}
+
+.navbar .divider-vertical {
+ width: 1px;
+ height: 40px;
+ margin: 0 9px;
+ overflow: hidden;
+ background-color: #222222;
+ border-right: 1px solid #333333;
+}
+
+.navbar .nav.pull-right {
+ margin-right: 0;
+ margin-left: 10px;
+}
+
+.navbar .btn-navbar {
+ display: none;
+ float: right;
+ padding: 7px 10px;
+ margin-right: 5px;
+ margin-left: 5px;
+ background-color: #2c2c2c;
+ *background-color: #222222;
+ background-image: -ms-linear-gradient(top, #333333, #222222);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));
+ background-image: -webkit-linear-gradient(top, #333333, #222222);
+ background-image: -o-linear-gradient(top, #333333, #222222);
+ background-image: linear-gradient(top, #333333, #222222);
+ background-image: -moz-linear-gradient(top, #333333, #222222);
+ background-repeat: repeat-x;
+ border-color: #222222 #222222 #000000;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);
+ filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);
+ -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);
+}
+
+.navbar .btn-navbar:hover,
+.navbar .btn-navbar:active,
+.navbar .btn-navbar.active,
+.navbar .btn-navbar.disabled,
+.navbar .btn-navbar[disabled] {
+ background-color: #222222;
+ *background-color: #151515;
+}
+
+.navbar .btn-navbar:active,
+.navbar .btn-navbar.active {
+ background-color: #080808 \9;
+}
+
+.navbar .btn-navbar .icon-bar {
+ display: block;
+ width: 18px;
+ height: 2px;
+ background-color: #f5f5f5;
+ -webkit-border-radius: 1px;
+ -moz-border-radius: 1px;
+ border-radius: 1px;
+ -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
+ -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
+ box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
+}
+
+.btn-navbar .icon-bar + .icon-bar {
+ margin-top: 3px;
+}
+
+.navbar .dropdown-menu:before {
+ position: absolute;
+ top: -7px;
+ left: 9px;
+ display: inline-block;
+ border-right: 7px solid transparent;
+ border-bottom: 7px solid #ccc;
+ border-left: 7px solid transparent;
+ border-bottom-color: rgba(0, 0, 0, 0.2);
+ content: '';
+}
+
+.navbar .dropdown-menu:after {
+ position: absolute;
+ top: -6px;
+ left: 10px;
+ display: inline-block;
+ border-right: 6px solid transparent;
+ border-bottom: 6px solid #ffffff;
+ border-left: 6px solid transparent;
+ content: '';
+}
+
+.navbar-fixed-bottom .dropdown-menu:before {
+ top: auto;
+ bottom: -7px;
+ border-top: 7px solid #ccc;
+ border-bottom: 0;
+ border-top-color: rgba(0, 0, 0, 0.2);
+}
+
+.navbar-fixed-bottom .dropdown-menu:after {
+ top: auto;
+ bottom: -6px;
+ border-top: 6px solid #ffffff;
+ border-bottom: 0;
+}
+
+.navbar .nav li.dropdown .dropdown-toggle .caret,
+.navbar .nav li.dropdown.open .caret {
+ border-top-color: #ffffff;
+ border-bottom-color: #ffffff;
+}
+
+.navbar .nav li.dropdown.active .caret {
+ opacity: 1;
+ filter: alpha(opacity=100);
+}
+
+.navbar .nav li.dropdown.open > .dropdown-toggle,
+.navbar .nav li.dropdown.active > .dropdown-toggle,
+.navbar .nav li.dropdown.open.active > .dropdown-toggle {
+ background-color: transparent;
+}
+
+.navbar .nav li.dropdown.active > .dropdown-toggle:hover {
+ color: #ffffff;
+}
+
+.navbar .pull-right .dropdown-menu,
+.navbar .dropdown-menu.pull-right {
+ right: 0;
+ left: auto;
+}
+
+.navbar .pull-right .dropdown-menu:before,
+.navbar .dropdown-menu.pull-right:before {
+ right: 12px;
+ left: auto;
+}
+
+.navbar .pull-right .dropdown-menu:after,
+.navbar .dropdown-menu.pull-right:after {
+ right: 13px;
+ left: auto;
+}
+
+.breadcrumb {
+ padding: 7px 14px;
+ margin: 0 0 18px;
+ list-style: none;
+ background-color: #fbfbfb;
+ background-image: -moz-linear-gradient(top, #ffffff, #f5f5f5);
+ background-image: -ms-linear-gradient(top, #ffffff, #f5f5f5);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f5f5f5));
+ background-image: -webkit-linear-gradient(top, #ffffff, #f5f5f5);
+ background-image: -o-linear-gradient(top, #ffffff, #f5f5f5);
+ background-image: linear-gradient(top, #ffffff, #f5f5f5);
+ background-repeat: repeat-x;
+ border: 1px solid #ddd;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffffff', endColorstr='#f5f5f5', GradientType=0);
+ -webkit-box-shadow: inset 0 1px 0 #ffffff;
+ -moz-box-shadow: inset 0 1px 0 #ffffff;
+ box-shadow: inset 0 1px 0 #ffffff;
+}
+
+.breadcrumb li {
+ display: inline-block;
+ *display: inline;
+ text-shadow: 0 1px 0 #ffffff;
+ *zoom: 1;
+}
+
+.breadcrumb .divider {
+ padding: 0 5px;
+ color: #999999;
+}
+
+.breadcrumb .active a {
+ color: #333333;
+}
+
+.pagination {
+ height: 36px;
+ margin: 18px 0;
+}
+
+.pagination ul {
+ display: inline-block;
+ *display: inline;
+ margin-bottom: 0;
+ margin-left: 0;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+ *zoom: 1;
+ -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
+ -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.pagination li {
+ display: inline;
+}
+
+.pagination a {
+ float: left;
+ padding: 0 14px;
+ line-height: 34px;
+ text-decoration: none;
+ border: 1px solid #ddd;
+ border-left-width: 0;
+}
+
+.pagination a:hover,
+.pagination .active a {
+ background-color: #f5f5f5;
+}
+
+.pagination .active a {
+ color: #999999;
+ cursor: default;
+}
+
+.pagination .disabled span,
+.pagination .disabled a,
+.pagination .disabled a:hover {
+ color: #999999;
+ cursor: default;
+ background-color: transparent;
+}
+
+.pagination li:first-child a {
+ border-left-width: 1px;
+ -webkit-border-radius: 3px 0 0 3px;
+ -moz-border-radius: 3px 0 0 3px;
+ border-radius: 3px 0 0 3px;
+}
+
+.pagination li:last-child a {
+ -webkit-border-radius: 0 3px 3px 0;
+ -moz-border-radius: 0 3px 3px 0;
+ border-radius: 0 3px 3px 0;
+}
+
+.pagination-centered {
+ text-align: center;
+}
+
+.pagination-right {
+ text-align: right;
+}
+
+.pager {
+ margin-bottom: 18px;
+ margin-left: 0;
+ text-align: center;
+ list-style: none;
+ *zoom: 1;
+}
+
+.pager:before,
+.pager:after {
+ display: table;
+ content: "";
+}
+
+.pager:after {
+ clear: both;
+}
+
+.pager li {
+ display: inline;
+}
+
+.pager a {
+ display: inline-block;
+ padding: 5px 14px;
+ background-color: #fff;
+ border: 1px solid #ddd;
+ -webkit-border-radius: 15px;
+ -moz-border-radius: 15px;
+ border-radius: 15px;
+}
+
+.pager a:hover {
+ text-decoration: none;
+ background-color: #f5f5f5;
+}
+
+.pager .next a {
+ float: right;
+}
+
+.pager .previous a {
+ float: left;
+}
+
+.pager .disabled a,
+.pager .disabled a:hover {
+ color: #999999;
+ cursor: default;
+ background-color: #fff;
+}
+
+.modal-open .dropdown-menu {
+ z-index: 2050;
+}
+
+.modal-open .dropdown.open {
+ *z-index: 2050;
+}
+
+.modal-open .popover {
+ z-index: 2060;
+}
+
+.modal-open .tooltip {
+ z-index: 2070;
+}
+
+.modal-backdrop {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1040;
+ background-color: #000000;
+}
+
+.modal-backdrop.fade {
+ opacity: 0;
+}
+
+.modal-backdrop,
+.modal-backdrop.fade.in {
+ opacity: 0.8;
+ filter: alpha(opacity=80);
+}
+
+.modal {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ z-index: 1050;
+ width: 560px;
+ margin: -250px 0 0 -280px;
+ overflow: auto;
+ background-color: #ffffff;
+ border: 1px solid #999;
+ border: 1px solid rgba(0, 0, 0, 0.3);
+ *border: 1px solid #999;
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+ -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
+ -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
+ box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
+ -webkit-background-clip: padding-box;
+ -moz-background-clip: padding-box;
+ background-clip: padding-box;
+}
+
+.modal.fade {
+ top: -25%;
+ -webkit-transition: opacity 0.3s linear, top 0.3s ease-out;
+ -moz-transition: opacity 0.3s linear, top 0.3s ease-out;
+ -ms-transition: opacity 0.3s linear, top 0.3s ease-out;
+ -o-transition: opacity 0.3s linear, top 0.3s ease-out;
+ transition: opacity 0.3s linear, top 0.3s ease-out;
+}
+
+.modal.fade.in {
+ top: 50%;
+}
+
+.modal-header {
+ padding: 9px 15px;
+ border-bottom: 1px solid #eee;
+}
+
+.modal-header .close {
+ margin-top: 2px;
+}
+
+.modal-body {
+ max-height: 400px;
+ padding: 15px;
+ overflow-y: auto;
+}
+
+.modal-form {
+ margin-bottom: 0;
+}
+
+.modal-footer {
+ padding: 14px 15px 15px;
+ margin-bottom: 0;
+ text-align: right;
+ background-color: #f5f5f5;
+ border-top: 1px solid #ddd;
+ -webkit-border-radius: 0 0 6px 6px;
+ -moz-border-radius: 0 0 6px 6px;
+ border-radius: 0 0 6px 6px;
+ *zoom: 1;
+ -webkit-box-shadow: inset 0 1px 0 #ffffff;
+ -moz-box-shadow: inset 0 1px 0 #ffffff;
+ box-shadow: inset 0 1px 0 #ffffff;
+}
+
+.modal-footer:before,
+.modal-footer:after {
+ display: table;
+ content: "";
+}
+
+.modal-footer:after {
+ clear: both;
+}
+
+.modal-footer .btn + .btn {
+ margin-bottom: 0;
+ margin-left: 5px;
+}
+
+.modal-footer .btn-group .btn + .btn {
+ margin-left: -1px;
+}
+
+.tooltip {
+ position: absolute;
+ z-index: 1020;
+ display: block;
+ padding: 5px;
+ font-size: 11px;
+ opacity: 0;
+ filter: alpha(opacity=0);
+ visibility: visible;
+}
+
+.tooltip.in {
+ opacity: 0.8;
+ filter: alpha(opacity=80);
+}
+
+.tooltip.top {
+ margin-top: -2px;
+}
+
+.tooltip.right {
+ margin-left: 2px;
+}
+
+.tooltip.bottom {
+ margin-top: 2px;
+}
+
+.tooltip.left {
+ margin-left: -2px;
+}
+
+.tooltip.top .tooltip-arrow {
+ bottom: 0;
+ left: 50%;
+ margin-left: -5px;
+ border-top: 5px solid #000000;
+ border-right: 5px solid transparent;
+ border-left: 5px solid transparent;
+}
+
+.tooltip.left .tooltip-arrow {
+ top: 50%;
+ right: 0;
+ margin-top: -5px;
+ border-top: 5px solid transparent;
+ border-bottom: 5px solid transparent;
+ border-left: 5px solid #000000;
+}
+
+.tooltip.bottom .tooltip-arrow {
+ top: 0;
+ left: 50%;
+ margin-left: -5px;
+ border-right: 5px solid transparent;
+ border-bottom: 5px solid #000000;
+ border-left: 5px solid transparent;
+}
+
+.tooltip.right .tooltip-arrow {
+ top: 50%;
+ left: 0;
+ margin-top: -5px;
+ border-top: 5px solid transparent;
+ border-right: 5px solid #000000;
+ border-bottom: 5px solid transparent;
+}
+
+.tooltip-inner {
+ max-width: 200px;
+ padding: 3px 8px;
+ color: #ffffff;
+ text-align: center;
+ text-decoration: none;
+ background-color: #000000;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+
+.tooltip-arrow {
+ position: absolute;
+ width: 0;
+ height: 0;
+}
+
+.popover {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 1010;
+ display: none;
+ padding: 5px;
+}
+
+.popover.top {
+ margin-top: -5px;
+}
+
+.popover.right {
+ margin-left: 5px;
+}
+
+.popover.bottom {
+ margin-top: 5px;
+}
+
+.popover.left {
+ margin-left: -5px;
+}
+
+.popover.top .arrow {
+ bottom: 0;
+ left: 50%;
+ margin-left: -5px;
+ border-top: 5px solid #000000;
+ border-right: 5px solid transparent;
+ border-left: 5px solid transparent;
+}
+
+.popover.right .arrow {
+ top: 50%;
+ left: 0;
+ margin-top: -5px;
+ border-top: 5px solid transparent;
+ border-right: 5px solid #000000;
+ border-bottom: 5px solid transparent;
+}
+
+.popover.bottom .arrow {
+ top: 0;
+ left: 50%;
+ margin-left: -5px;
+ border-right: 5px solid transparent;
+ border-bottom: 5px solid #000000;
+ border-left: 5px solid transparent;
+}
+
+.popover.left .arrow {
+ top: 50%;
+ right: 0;
+ margin-top: -5px;
+ border-top: 5px solid transparent;
+ border-bottom: 5px solid transparent;
+ border-left: 5px solid #000000;
+}
+
+.popover .arrow {
+ position: absolute;
+ width: 0;
+ height: 0;
+}
+
+.popover-inner {
+ width: 280px;
+ padding: 3px;
+ overflow: hidden;
+ background: #000000;
+ background: rgba(0, 0, 0, 0.8);
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+ -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
+ -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
+ box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
+}
+
+.popover-title {
+ padding: 9px 15px;
+ line-height: 1;
+ background-color: #f5f5f5;
+ border-bottom: 1px solid #eee;
+ -webkit-border-radius: 3px 3px 0 0;
+ -moz-border-radius: 3px 3px 0 0;
+ border-radius: 3px 3px 0 0;
+}
+
+.popover-content {
+ padding: 14px;
+ background-color: #ffffff;
+ -webkit-border-radius: 0 0 3px 3px;
+ -moz-border-radius: 0 0 3px 3px;
+ border-radius: 0 0 3px 3px;
+ -webkit-background-clip: padding-box;
+ -moz-background-clip: padding-box;
+ background-clip: padding-box;
+}
+
+.popover-content p,
+.popover-content ul,
+.popover-content ol {
+ margin-bottom: 0;
+}
+
+.thumbnails {
+ margin-left: -20px;
+ list-style: none;
+ *zoom: 1;
+}
+
+.thumbnails:before,
+.thumbnails:after {
+ display: table;
+ content: "";
+}
+
+.thumbnails:after {
+ clear: both;
+}
+
+.row-fluid .thumbnails {
+ margin-left: 0;
+}
+
+.thumbnails > li {
+ float: left;
+ margin-bottom: 18px;
+ margin-left: 20px;
+}
+
+.thumbnail {
+ display: block;
+ padding: 4px;
+ line-height: 1;
+ border: 1px solid #ddd;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075);
+ -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+
+a.thumbnail:hover {
+ border-color: #0088cc;
+ -webkit-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);
+ -moz-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);
+ box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);
+}
+
+.thumbnail > img {
+ display: block;
+ max-width: 100%;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.thumbnail .caption {
+ padding: 9px;
+}
+
+.label,
+.badge {
+ font-size: 10.998px;
+ font-weight: bold;
+ line-height: 14px;
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ white-space: nowrap;
+ vertical-align: baseline;
+ background-color: #999999;
+}
+
+.label {
+ padding: 1px 4px 2px;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+}
+
+.badge {
+ padding: 1px 9px 2px;
+ -webkit-border-radius: 9px;
+ -moz-border-radius: 9px;
+ border-radius: 9px;
+}
+
+a.label:hover,
+a.badge:hover {
+ color: #ffffff;
+ text-decoration: none;
+ cursor: pointer;
+}
+
+.label-important,
+.badge-important {
+ background-color: #b94a48;
+}
+
+.label-important[href],
+.badge-important[href] {
+ background-color: #953b39;
+}
+
+.label-warning,
+.badge-warning {
+ background-color: #f89406;
+}
+
+.label-warning[href],
+.badge-warning[href] {
+ background-color: #c67605;
+}
+
+.label-success,
+.badge-success {
+ background-color: #468847;
+}
+
+.label-success[href],
+.badge-success[href] {
+ background-color: #356635;
+}
+
+.label-info,
+.badge-info {
+ background-color: #3a87ad;
+}
+
+.label-info[href],
+.badge-info[href] {
+ background-color: #2d6987;
+}
+
+.label-inverse,
+.badge-inverse {
+ background-color: #333333;
+}
+
+.label-inverse[href],
+.badge-inverse[href] {
+ background-color: #1a1a1a;
+}
+
+@-webkit-keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0;
+ }
+ to {
+ background-position: 0 0;
+ }
+}
+
+@-moz-keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0;
+ }
+ to {
+ background-position: 0 0;
+ }
+}
+
+@-ms-keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0;
+ }
+ to {
+ background-position: 0 0;
+ }
+}
+
+@-o-keyframes progress-bar-stripes {
+ from {
+ background-position: 0 0;
+ }
+ to {
+ background-position: 40px 0;
+ }
+}
+
+@keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0;
+ }
+ to {
+ background-position: 0 0;
+ }
+}
+
+.progress {
+ height: 18px;
+ margin-bottom: 18px;
+ overflow: hidden;
+ background-color: #f7f7f7;
+ background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9);
+ background-image: -ms-linear-gradient(top, #f5f5f5, #f9f9f9);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));
+ background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9);
+ background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9);
+ background-image: linear-gradient(top, #f5f5f5, #f9f9f9);
+ background-repeat: repeat-x;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#f5f5f5', endColorstr='#f9f9f9', GradientType=0);
+ -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+ -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+}
+
+.progress .bar {
+ width: 0;
+ height: 18px;
+ font-size: 12px;
+ color: #ffffff;
+ text-align: center;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #0e90d2;
+ background-image: -moz-linear-gradient(top, #149bdf, #0480be);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be));
+ background-image: -webkit-linear-gradient(top, #149bdf, #0480be);
+ background-image: -o-linear-gradient(top, #149bdf, #0480be);
+ background-image: linear-gradient(top, #149bdf, #0480be);
+ background-image: -ms-linear-gradient(top, #149bdf, #0480be);
+ background-repeat: repeat-x;
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#149bdf', endColorstr='#0480be', GradientType=0);
+ -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+ -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+ box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ box-sizing: border-box;
+ -webkit-transition: width 0.6s ease;
+ -moz-transition: width 0.6s ease;
+ -ms-transition: width 0.6s ease;
+ -o-transition: width 0.6s ease;
+ transition: width 0.6s ease;
+}
+
+.progress-striped .bar {
+ background-color: #149bdf;
+ background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ -webkit-background-size: 40px 40px;
+ -moz-background-size: 40px 40px;
+ -o-background-size: 40px 40px;
+ background-size: 40px 40px;
+}
+
+.progress.active .bar {
+ -webkit-animation: progress-bar-stripes 2s linear infinite;
+ -moz-animation: progress-bar-stripes 2s linear infinite;
+ -ms-animation: progress-bar-stripes 2s linear infinite;
+ -o-animation: progress-bar-stripes 2s linear infinite;
+ animation: progress-bar-stripes 2s linear infinite;
+}
+
+.progress-danger .bar {
+ background-color: #dd514c;
+ background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35);
+ background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35));
+ background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35);
+ background-image: -o-linear-gradient(top, #ee5f5b, #c43c35);
+ background-image: linear-gradient(top, #ee5f5b, #c43c35);
+ background-repeat: repeat-x;
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0);
+}
+
+.progress-danger.progress-striped .bar {
+ background-color: #ee5f5b;
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+
+.progress-success .bar {
+ background-color: #5eb95e;
+ background-image: -moz-linear-gradient(top, #62c462, #57a957);
+ background-image: -ms-linear-gradient(top, #62c462, #57a957);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957));
+ background-image: -webkit-linear-gradient(top, #62c462, #57a957);
+ background-image: -o-linear-gradient(top, #62c462, #57a957);
+ background-image: linear-gradient(top, #62c462, #57a957);
+ background-repeat: repeat-x;
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0);
+}
+
+.progress-success.progress-striped .bar {
+ background-color: #62c462;
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+
+.progress-info .bar {
+ background-color: #4bb1cf;
+ background-image: -moz-linear-gradient(top, #5bc0de, #339bb9);
+ background-image: -ms-linear-gradient(top, #5bc0de, #339bb9);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9));
+ background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9);
+ background-image: -o-linear-gradient(top, #5bc0de, #339bb9);
+ background-image: linear-gradient(top, #5bc0de, #339bb9);
+ background-repeat: repeat-x;
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0);
+}
+
+.progress-info.progress-striped .bar {
+ background-color: #5bc0de;
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+
+.progress-warning .bar {
+ background-color: #faa732;
+ background-image: -moz-linear-gradient(top, #fbb450, #f89406);
+ background-image: -ms-linear-gradient(top, #fbb450, #f89406);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));
+ background-image: -webkit-linear-gradient(top, #fbb450, #f89406);
+ background-image: -o-linear-gradient(top, #fbb450, #f89406);
+ background-image: linear-gradient(top, #fbb450, #f89406);
+ background-repeat: repeat-x;
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0);
+}
+
+.progress-warning.progress-striped .bar {
+ background-color: #fbb450;
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+
+.accordion {
+ margin-bottom: 18px;
+}
+
+.accordion-group {
+ margin-bottom: 2px;
+ border: 1px solid #e5e5e5;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+
+.accordion-heading {
+ border-bottom: 0;
+}
+
+.accordion-heading .accordion-toggle {
+ display: block;
+ padding: 8px 15px;
+}
+
+.accordion-toggle {
+ cursor: pointer;
+}
+
+.accordion-inner {
+ padding: 9px 15px;
+ border-top: 1px solid #e5e5e5;
+}
+
+.carousel {
+ position: relative;
+ margin-bottom: 18px;
+ line-height: 1;
+}
+
+.carousel-inner {
+ position: relative;
+ width: 100%;
+ overflow: hidden;
+}
+
+.carousel .item {
+ position: relative;
+ display: none;
+ -webkit-transition: 0.6s ease-in-out left;
+ -moz-transition: 0.6s ease-in-out left;
+ -ms-transition: 0.6s ease-in-out left;
+ -o-transition: 0.6s ease-in-out left;
+ transition: 0.6s ease-in-out left;
+}
+
+.carousel .item > img {
+ display: block;
+ line-height: 1;
+}
+
+.carousel .active,
+.carousel .next,
+.carousel .prev {
+ display: block;
+}
+
+.carousel .active {
+ left: 0;
+}
+
+.carousel .next,
+.carousel .prev {
+ position: absolute;
+ top: 0;
+ width: 100%;
+}
+
+.carousel .next {
+ left: 100%;
+}
+
+.carousel .prev {
+ left: -100%;
+}
+
+.carousel .next.left,
+.carousel .prev.right {
+ left: 0;
+}
+
+.carousel .active.left {
+ left: -100%;
+}
+
+.carousel .active.right {
+ left: 100%;
+}
+
+.carousel-control {
+ position: absolute;
+ top: 40%;
+ left: 15px;
+ width: 40px;
+ height: 40px;
+ margin-top: -20px;
+ font-size: 60px;
+ font-weight: 100;
+ line-height: 30px;
+ color: #ffffff;
+ text-align: center;
+ background: #222222;
+ border: 3px solid #ffffff;
+ -webkit-border-radius: 23px;
+ -moz-border-radius: 23px;
+ border-radius: 23px;
+ opacity: 0.5;
+ filter: alpha(opacity=50);
+}
+
+.carousel-control.right {
+ right: 15px;
+ left: auto;
+}
+
+.carousel-control:hover {
+ color: #ffffff;
+ text-decoration: none;
+ opacity: 0.9;
+ filter: alpha(opacity=90);
+}
+
+.carousel-caption {
+ position: absolute;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ padding: 10px 15px 5px;
+ background: #333333;
+ background: rgba(0, 0, 0, 0.75);
+}
+
+.carousel-caption h4,
+.carousel-caption p {
+ color: #ffffff;
+}
+
+.hero-unit {
+ padding: 60px;
+ margin-bottom: 30px;
+ background-color: #eeeeee;
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+}
+
+.hero-unit h1 {
+ margin-bottom: 0;
+ font-size: 60px;
+ line-height: 1;
+ letter-spacing: -1px;
+ color: inherit;
+}
+
+.hero-unit p {
+ font-size: 18px;
+ font-weight: 200;
+ line-height: 27px;
+ color: inherit;
+}
+
+.pull-right {
+ float: right;
+}
+
+.pull-left {
+ float: left;
+}
+
+.hide {
+ display: none;
+}
+
+.show {
+ display: block;
+}
+
+.invisible {
+ visibility: hidden;
+}
diff --git a/examples/server-example/src/main/webapp/index.html b/examples/server-example/src/main/webapp/index.html
new file mode 100644
index 0000000..bcb941b
--- /dev/null
+++ b/examples/server-example/src/main/webapp/index.html
@@ -0,0 +1,137 @@
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you 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.
+ -->
+<!DOCTYPE html>
+<html>
+<head>
+ <link rel="stylesheet" href="css/bootstrap.2.0.4.css">
+
+ <script type="text/javascript" src="js/jquery-1.7.2.js"></script>
+ <script type="text/javascript" src="js/bootstrap.2.0.4.js"></script>
+ <script type="text/javascript" src="js/sample.js"></script>
+ <script type="text/javascript" src="js/jquery.json-2.3.js"></script>
+ <script type="text/javascript" src="js/jquery-tmpl.js"></script>
+ <title>Apache DirectMemory Sample</title>
+
+</head>
+
+<body style="padding-top: 1px;">
+
+<div id="topbar-menu-container"></div>
+
+<div class="container-fluid" style="min-height: 450px">
+ <div class="row-fluid">
+
+ <div class="span10 columns">
+ <div class="content">
+
+
+ <div id="wine-result">
+ <div class="page-header"><h4>Results</h4></div>
+ <div id="result-content"></div>
+ </div>
+
+ <div class="accordion" id="accordion-div">
+
+ <div class="accordion-group">
+ <div class="accordion-heading">
+ <a class="accordion-toggle" href="#collapse-put" data-parent="#accordion-div" data-toggle="collapse"><h4>Put in cache</h4></a>
+ </div>
+ <div id="collapse-put" class="accordion-body collapse" style="height: auto;">
+ <div class="well">
+ Wine Name: <input type="text" id="wine_name" name="wine_name">
+ Wine Description: <input type="text" id="wine_description" name="wine_description">
+ ExpiresIn: <input type="text" id="expires-in" name="expires-in">
+ Serializer:
+ <select name="serialize" id="serializer" class="span8">
+ <option value="org.apache.directmemory.serialization.StandardSerializer">org.apache.directmemory.serialization.StandardSerializer</option>
+ <option value="org.apache.directmemory.serialization.protostuff.ProtoStuffWithLinkedBufferSerializer">org.apache.directmemory.serialization.protostuff.ProtoStuffWithLinkedBufferSerializer</option>
+ </select>
+ <br/>
+ <button class="btn" id="put-cache-btn">Put</button>
+ </div>
+ </div>
+ </div>
+
+ <div class="accordion-group">
+ <div class="accordion-heading">
+ <a class="accordion-toggle" href="#collapse-get" data-parent="#accordion-div" data-toggle="collapse"><h4>Get from cache</h4></a>
+ </div>
+ <div id="collapse-get" class="accordion-body collapse" style="height: 0px;">
+ <div class="well">
+ Wine Key: <input type="text" id="wine_name_cache" name="wine_name_cache">
+ <br/>
+ <button class="btn" id="get_cache_btn">Get</button>
+ </div>
+ </div>
+
+ </div>
+
+ <div class="accordion-group">
+ <div class="accordion-heading">
+ <a class="accordion-toggle" href="#collapse-delete" data-parent="#accordion-div" data-toggle="collapse"><h4>Delete from cache</h4></a>
+ </div>
+ <div id="collapse-delete" class="accordion-body collapse" style="height: 0px;">
+ <div class="well">
+ Wine Key: <input type="text" id="wine_key_cache" name="wine_key_cache">
+ <br/>
+ <button class="btn" id="delete_cache_btn">Delete</button>
+ </div>
+ </div>
+
+ </div>
+
+
+ </div>
+ </div>
+ </div>
+
+</div>
+
+<footer id="footer-content" style="vertical-align: bottom">
+ <div class="pull-left">
+ <a href="http://incubator.apache.org/directmemory" target="_blank">Apache DirectMemory Sample</a>
+ </div>
+ <div class="pull-right">
+ Copyright © 2005 - 2012 <a href="http://www.apache.org/" target="_blank">The Apache Software Foundation</a>
+ </div>
+</footer>
+</body>
+
+<script id='alert-message-error' type='text/html'>
+ <div class="alert fade in alert-error" data-alert="alert">
+ <a class="close" href="#" data-dismiss="alert" id='alert-message-error-close-a'>×</a>
+ ${message}
+ </div>
+</script>
+<script id='alert-message-warning' type='text/html'>
+ <div class="alert fade in alert-heading" data-alert="alert">
+ <a class="close" href="#" data-dismiss="alert" id='alert-message-warning-close-a'>×</a>
+ ${message}
+ </div>
+</script>
+<script id='alert-message-info' type='text/html'>
+ <div class="alert fade in alert-info" data-alert="alert">
+ <a class="close" href="#" data-dismiss="alert" id='alert-message-info-close-a'>×</a>
+ ${message}
+ </div>
+</script>
+
+</html>
+
+
diff --git a/examples/server-example/src/main/webapp/js/bootstrap.2.0.4.js b/examples/server-example/src/main/webapp/js/bootstrap.2.0.4.js
new file mode 100644
index 0000000..5d6e65b
--- /dev/null
+++ b/examples/server-example/src/main/webapp/js/bootstrap.2.0.4.js
@@ -0,0 +1,1825 @@
+/* ===================================================
+ * bootstrap-transition.js v2.0.4
+ * http://twitter.github.com/bootstrap/javascript.html#transitions
+ * ===================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * 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.
+ * ========================================================== */
+
+
+!function ($) {
+
+ $(function () {
+
+ "use strict"; // jshint ;_;
+
+
+ /* CSS TRANSITION SUPPORT (http://www.modernizr.com/)
+ * ======================================================= */
+
+ $.support.transition = (function () {
+
+ var transitionEnd = (function () {
+
+ var el = document.createElement('bootstrap')
+ , transEndEventNames = {
+ 'WebkitTransition' : 'webkitTransitionEnd'
+ , 'MozTransition' : 'transitionend'
+ , 'OTransition' : 'oTransitionEnd'
+ , 'msTransition' : 'MSTransitionEnd'
+ , 'transition' : 'transitionend'
+ }
+ , name
+
+ for (name in transEndEventNames){
+ if (el.style[name] !== undefined) {
+ return transEndEventNames[name]
+ }
+ }
+
+ }())
+
+ return transitionEnd && {
+ end: transitionEnd
+ }
+
+ })()
+
+ })
+
+}(window.jQuery);/* ==========================================================
+ * bootstrap-alert.js v2.0.4
+ * http://twitter.github.com/bootstrap/javascript.html#alerts
+ * ==========================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * 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.
+ * ========================================================== */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* ALERT CLASS DEFINITION
+ * ====================== */
+
+ var dismiss = '[data-dismiss="alert"]'
+ , Alert = function (el) {
+ $(el).on('click', dismiss, this.close)
+ }
+
+ Alert.prototype.close = function (e) {
+ var $this = $(this)
+ , selector = $this.attr('data-target')
+ , $parent
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
+ }
+
+ $parent = $(selector)
+
+ e && e.preventDefault()
+
+ $parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent())
+
+ $parent.trigger(e = $.Event('close'))
+
+ if (e.isDefaultPrevented()) return
+
+ $parent.removeClass('in')
+
+ function removeElement() {
+ $parent
+ .trigger('closed')
+ .remove()
+ }
+
+ $.support.transition && $parent.hasClass('fade') ?
+ $parent.on($.support.transition.end, removeElement) :
+ removeElement()
+ }
+
+
+ /* ALERT PLUGIN DEFINITION
+ * ======================= */
+
+ $.fn.alert = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('alert')
+ if (!data) $this.data('alert', (data = new Alert(this)))
+ if (typeof option == 'string') data[option].call($this)
+ })
+ }
+
+ $.fn.alert.Constructor = Alert
+
+
+ /* ALERT DATA-API
+ * ============== */
+
+ $(function () {
+ $('body').on('click.alert.data-api', dismiss, Alert.prototype.close)
+ })
+
+}(window.jQuery);/* ============================================================
+ * bootstrap-button.js v2.0.4
+ * http://twitter.github.com/bootstrap/javascript.html#buttons
+ * ============================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * 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.
+ * ============================================================ */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* BUTTON PUBLIC CLASS DEFINITION
+ * ============================== */
+
+ var Button = function (element, options) {
+ this.$element = $(element)
+ this.options = $.extend({}, $.fn.button.defaults, options)
+ }
+
+ Button.prototype.setState = function (state) {
+ var d = 'disabled'
+ , $el = this.$element
+ , data = $el.data()
+ , val = $el.is('input') ? 'val' : 'html'
+
+ state = state + 'Text'
+ data.resetText || $el.data('resetText', $el[val]())
+
+ $el[val](data[state] || this.options[state])
+
+ // push to event loop to allow forms to submit
+ setTimeout(function () {
+ state == 'loadingText' ?
+ $el.addClass(d).attr(d, d) :
+ $el.removeClass(d).removeAttr(d)
+ }, 0)
+ }
+
+ Button.prototype.toggle = function () {
+ var $parent = this.$element.parent('[data-toggle="buttons-radio"]')
+
+ $parent && $parent
+ .find('.active')
+ .removeClass('active')
+
+ this.$element.toggleClass('active')
+ }
+
+
+ /* BUTTON PLUGIN DEFINITION
+ * ======================== */
+
+ $.fn.button = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('button')
+ , options = typeof option == 'object' && option
+ if (!data) $this.data('button', (data = new Button(this, options)))
+ if (option == 'toggle') data.toggle()
+ else if (option) data.setState(option)
+ })
+ }
+
+ $.fn.button.defaults = {
+ loadingText: 'loading...'
+ }
+
+ $.fn.button.Constructor = Button
+
+
+ /* BUTTON DATA-API
+ * =============== */
+
+ $(function () {
+ $('body').on('click.button.data-api', '[data-toggle^=button]', function ( e ) {
+ var $btn = $(e.target)
+ if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
+ $btn.button('toggle')
+ })
+ })
+
+}(window.jQuery);/* ==========================================================
+ * bootstrap-carousel.js v2.0.4
+ * http://twitter.github.com/bootstrap/javascript.html#carousel
+ * ==========================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * 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.
+ * ========================================================== */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* CAROUSEL CLASS DEFINITION
+ * ========================= */
+
+ var Carousel = function (element, options) {
+ this.$element = $(element)
+ this.options = options
+ this.options.slide && this.slide(this.options.slide)
+ this.options.pause == 'hover' && this.$element
+ .on('mouseenter', $.proxy(this.pause, this))
+ .on('mouseleave', $.proxy(this.cycle, this))
+ }
+
+ Carousel.prototype = {
+
+ cycle: function (e) {
+ if (!e) this.paused = false
+ this.options.interval
+ && !this.paused
+ && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
+ return this
+ }
+
+ , to: function (pos) {
+ var $active = this.$element.find('.active')
+ , children = $active.parent().children()
+ , activePos = children.index($active)
+ , that = this
+
+ if (pos > (children.length - 1) || pos < 0) return
+
+ if (this.sliding) {
+ return this.$element.one('slid', function () {
+ that.to(pos)
+ })
+ }
+
+ if (activePos == pos) {
+ return this.pause().cycle()
+ }
+
+ return this.slide(pos > activePos ? 'next' : 'prev', $(children[pos]))
+ }
+
+ , pause: function (e) {
+ if (!e) this.paused = true
+ clearInterval(this.interval)
+ this.interval = null
+ return this
+ }
+
+ , next: function () {
+ if (this.sliding) return
+ return this.slide('next')
+ }
+
+ , prev: function () {
+ if (this.sliding) return
+ return this.slide('prev')
+ }
+
+ , slide: function (type, next) {
+ var $active = this.$element.find('.active')
+ , $next = next || $active[type]()
+ , isCycling = this.interval
+ , direction = type == 'next' ? 'left' : 'right'
+ , fallback = type == 'next' ? 'first' : 'last'
+ , that = this
+ , e = $.Event('slide')
+
+ this.sliding = true
+
+ isCycling && this.pause()
+
+ $next = $next.length ? $next : this.$element.find('.item')[fallback]()
+
+ if ($next.hasClass('active')) return
+
+ if ($.support.transition && this.$element.hasClass('slide')) {
+ this.$element.trigger(e)
+ if (e.isDefaultPrevented()) return
+ $next.addClass(type)
+ $next[0].offsetWidth // force reflow
+ $active.addClass(direction)
+ $next.addClass(direction)
+ this.$element.one($.support.transition.end, function () {
+ $next.removeClass([type, direction].join(' ')).addClass('active')
+ $active.removeClass(['active', direction].join(' '))
+ that.sliding = false
+ setTimeout(function () { that.$element.trigger('slid') }, 0)
+ })
+ } else {
+ this.$element.trigger(e)
+ if (e.isDefaultPrevented()) return
+ $active.removeClass('active')
+ $next.addClass('active')
+ this.sliding = false
+ this.$element.trigger('slid')
+ }
+
+ isCycling && this.cycle()
+
+ return this
+ }
+
+ }
+
+
+ /* CAROUSEL PLUGIN DEFINITION
+ * ========================== */
+
+ $.fn.carousel = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('carousel')
+ , options = $.extend({}, $.fn.carousel.defaults, typeof option == 'object' && option)
+ if (!data) $this.data('carousel', (data = new Carousel(this, options)))
+ if (typeof option == 'number') data.to(option)
+ else if (typeof option == 'string' || (option = options.slide)) data[option]()
+ else if (options.interval) data.cycle()
+ })
+ }
+
+ $.fn.carousel.defaults = {
+ interval: 5000
+ , pause: 'hover'
+ }
+
+ $.fn.carousel.Constructor = Carousel
+
+
+ /* CAROUSEL DATA-API
+ * ================= */
+
+ $(function () {
+ $('body').on('click.carousel.data-api', '[data-slide]', function ( e ) {
+ var $this = $(this), href
+ , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
+ , options = !$target.data('modal') && $.extend({}, $target.data(), $this.data())
+ $target.carousel(options)
+ e.preventDefault()
+ })
+ })
+
+}(window.jQuery);/* =============================================================
+ * bootstrap-collapse.js v2.0.4
+ * http://twitter.github.com/bootstrap/javascript.html#collapse
+ * =============================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * 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.
+ * ============================================================ */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* COLLAPSE PUBLIC CLASS DEFINITION
+ * ================================ */
+
+ var Collapse = function (element, options) {
+ this.$element = $(element)
+ this.options = $.extend({}, $.fn.collapse.defaults, options)
+
+ if (this.options.parent) {
+ this.$parent = $(this.options.parent)
+ }
+
+ this.options.toggle && this.toggle()
+ }
+
+ Collapse.prototype = {
+
+ constructor: Collapse
+
+ , dimension: function () {
+ var hasWidth = this.$element.hasClass('width')
+ return hasWidth ? 'width' : 'height'
+ }
+
+ , show: function () {
+ var dimension
+ , scroll
+ , actives
+ , hasData
+
+ if (this.transitioning) return
+
+ dimension = this.dimension()
+ scroll = $.camelCase(['scroll', dimension].join('-'))
+ actives = this.$parent && this.$parent.find('> .accordion-group > .in')
+
+ if (actives && actives.length) {
+ hasData = actives.data('collapse')
+ if (hasData && hasData.transitioning) return
+ actives.collapse('hide')
+ hasData || actives.data('collapse', null)
+ }
+
+ this.$element[dimension](0)
+ this.transition('addClass', $.Event('show'), 'shown')
+ this.$element[dimension](this.$element[0][scroll])
+ }
+
+ , hide: function () {
+ var dimension
+ if (this.transitioning) return
+ dimension = this.dimension()
+ this.reset(this.$element[dimension]())
+ this.transition('removeClass', $.Event('hide'), 'hidden')
+ this.$element[dimension](0)
+ }
+
+ , reset: function (size) {
+ var dimension = this.dimension()
+
+ this.$element
+ .removeClass('collapse')
+ [dimension](size || 'auto')
+ [0].offsetWidth
+
+ this.$element[size !== null ? 'addClass' : 'removeClass']('collapse')
+
+ return this
+ }
+
+ , transition: function (method, startEvent, completeEvent) {
+ var that = this
+ , complete = function () {
+ if (startEvent.type == 'show') that.reset()
+ that.transitioning = 0
+ that.$element.trigger(completeEvent)
+ }
+
+ this.$element.trigger(startEvent)
+
+ if (startEvent.isDefaultPrevented()) return
+
+ this.transitioning = 1
+
+ this.$element[method]('in')
+
+ $.support.transition && this.$element.hasClass('collapse') ?
+ this.$element.one($.support.transition.end, complete) :
+ complete()
+ }
+
+ , toggle: function () {
+ this[this.$element.hasClass('in') ? 'hide' : 'show']()
+ }
+
+ }
+
+
+ /* COLLAPSIBLE PLUGIN DEFINITION
+ * ============================== */
+
+ $.fn.collapse = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('collapse')
+ , options = typeof option == 'object' && option
+ if (!data) $this.data('collapse', (data = new Collapse(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.collapse.defaults = {
+ toggle: true
+ }
+
+ $.fn.collapse.Constructor = Collapse
+
+
+ /* COLLAPSIBLE DATA-API
+ * ==================== */
+
+ $(function () {
+ $('body').on('click.collapse.data-api', '[data-toggle=collapse]', function ( e ) {
+ var $this = $(this), href
+ , target = $this.attr('data-target')
+ || e.preventDefault()
+ || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
+ , option = $(target).data('collapse') ? 'toggle' : $this.data()
+ $(target).collapse(option)
+ })
+ })
+
+}(window.jQuery);/* ============================================================
+ * bootstrap-dropdown.js v2.0.4
+ * http://twitter.github.com/bootstrap/javascript.html#dropdowns
+ * ============================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * 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.
+ * ============================================================ */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* DROPDOWN CLASS DEFINITION
+ * ========================= */
+
+ var toggle = '[data-toggle="dropdown"]'
+ , Dropdown = function (element) {
+ var $el = $(element).on('click.dropdown.data-api', this.toggle)
+ $('html').on('click.dropdown.data-api', function () {
+ $el.parent().removeClass('open')
+ })
+ }
+
+ Dropdown.prototype = {
+
+ constructor: Dropdown
+
+ , toggle: function (e) {
+ var $this = $(this)
+ , $parent
+ , selector
+ , isActive
+
+ if ($this.is('.disabled, :disabled')) return
+
+ selector = $this.attr('data-target')
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
+ }
+
+ $parent = $(selector)
+ $parent.length || ($parent = $this.parent())
+
+ isActive = $parent.hasClass('open')
+
+ clearMenus()
+
+ if (!isActive) $parent.toggleClass('open')
+
+ return false
+ }
+
+ }
+
+ function clearMenus() {
+ $(toggle).parent().removeClass('open')
+ }
+
+
+ /* DROPDOWN PLUGIN DEFINITION
+ * ========================== */
+
+ $.fn.dropdown = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('dropdown')
+ if (!data) $this.data('dropdown', (data = new Dropdown(this)))
+ if (typeof option == 'string') data[option].call($this)
+ })
+ }
+
+ $.fn.dropdown.Constructor = Dropdown
+
+
+ /* APPLY TO STANDARD DROPDOWN ELEMENTS
+ * =================================== */
+
+ $(function () {
+ $('html').on('click.dropdown.data-api', clearMenus)
+ $('body')
+ .on('click.dropdown', '.dropdown form', function (e) { e.stopPropagation() })
+ .on('click.dropdown.data-api', toggle, Dropdown.prototype.toggle)
+ })
+
+}(window.jQuery);/* =========================================================
+ * bootstrap-modal.js v2.0.4
+ * http://twitter.github.com/bootstrap/javascript.html#modals
+ * =========================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * 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.
+ * ========================================================= */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* MODAL CLASS DEFINITION
+ * ====================== */
+
+ var Modal = function (content, options) {
+ this.options = options
+ this.$element = $(content)
+ .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this))
+ }
+
+ Modal.prototype = {
+
+ constructor: Modal
+
+ , toggle: function () {
+ return this[!this.isShown ? 'show' : 'hide']()
+ }
+
+ , show: function () {
+ var that = this
+ , e = $.Event('show')
+
+ this.$element.trigger(e)
+
+ if (this.isShown || e.isDefaultPrevented()) return
+
+ $('body').addClass('modal-open')
+
+ this.isShown = true
+
+ escape.call(this)
+ backdrop.call(this, function () {
+ var transition = $.support.transition && that.$element.hasClass('fade')
+
+ if (!that.$element.parent().length) {
+ that.$element.appendTo(document.body) //don't move modals dom position
+ }
+
+ that.$element
+ .show()
+
+ if (transition) {
+ that.$element[0].offsetWidth // force reflow
+ }
+
+ that.$element.addClass('in')
+
+ transition ?
+ that.$element.one($.support.transition.end, function () { that.$element.trigger('shown') }) :
+ that.$element.trigger('shown')
+
+ })
+ }
+
+ , hide: function (e) {
+ e && e.preventDefault()
+
+ var that = this
+
+ e = $.Event('hide')
+
+ this.$element.trigger(e)
+
+ if (!this.isShown || e.isDefaultPrevented()) return
+
+ this.isShown = false
+
+ $('body').removeClass('modal-open')
+
+ escape.call(this)
+
+ this.$element.removeClass('in')
+
+ $.support.transition && this.$element.hasClass('fade') ?
+ hideWithTransition.call(this) :
+ hideModal.call(this)
+ }
+
+ }
+
+
+ /* MODAL PRIVATE METHODS
+ * ===================== */
+
+ function hideWithTransition() {
+ var that = this
+ , timeout = setTimeout(function () {
+ that.$element.off($.support.transition.end)
+ hideModal.call(that)
+ }, 500)
+
+ this.$element.one($.support.transition.end, function () {
+ clearTimeout(timeout)
+ hideModal.call(that)
+ })
+ }
+
+ function hideModal(that) {
+ this.$element
+ .hide()
+ .trigger('hidden')
+
+ backdrop.call(this)
+ }
+
+ function backdrop(callback) {
+ var that = this
+ , animate = this.$element.hasClass('fade') ? 'fade' : ''
+
+ if (this.isShown && this.options.backdrop) {
+ var doAnimate = $.support.transition && animate
+
+ this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
+ .appendTo(document.body)
+
+ if (this.options.backdrop != 'static') {
+ this.$backdrop.click($.proxy(this.hide, this))
+ }
+
+ if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
+
+ this.$backdrop.addClass('in')
+
+ doAnimate ?
+ this.$backdrop.one($.support.transition.end, callback) :
+ callback()
+
+ } else if (!this.isShown && this.$backdrop) {
+ this.$backdrop.removeClass('in')
+
+ $.support.transition && this.$element.hasClass('fade')?
+ this.$backdrop.one($.support.transition.end, $.proxy(removeBackdrop, this)) :
+ removeBackdrop.call(this)
+
+ } else if (callback) {
+ callback()
+ }
+ }
+
+ function removeBackdrop() {
+ this.$backdrop.remove()
+ this.$backdrop = null
+ }
+
+ function escape() {
+ var that = this
+ if (this.isShown && this.options.keyboard) {
+ $(document).on('keyup.dismiss.modal', function ( e ) {
+ e.which == 27 && that.hide()
+ })
+ } else if (!this.isShown) {
+ $(document).off('keyup.dismiss.modal')
+ }
+ }
+
+
+ /* MODAL PLUGIN DEFINITION
+ * ======================= */
+
+ $.fn.modal = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('modal')
+ , options = $.extend({}, $.fn.modal.defaults, $this.data(), typeof option == 'object' && option)
+ if (!data) $this.data('modal', (data = new Modal(this, options)))
+ if (typeof option == 'string') data[option]()
+ else if (options.show) data.show()
+ })
+ }
+
+ $.fn.modal.defaults = {
+ backdrop: true
+ , keyboard: true
+ , show: true
+ }
+
+ $.fn.modal.Constructor = Modal
+
+
+ /* MODAL DATA-API
+ * ============== */
+
+ $(function () {
+ $('body').on('click.modal.data-api', '[data-toggle="modal"]', function ( e ) {
+ var $this = $(this), href
+ , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
+ , option = $target.data('modal') ? 'toggle' : $.extend({}, $target.data(), $this.data())
+
+ e.preventDefault()
+ $target.modal(option)
+ })
+ })
+
+}(window.jQuery);/* ===========================================================
+ * bootstrap-tooltip.js v2.0.4
+ * http://twitter.github.com/bootstrap/javascript.html#tooltips
+ * Inspired by the original jQuery.tipsy by Jason Frame
+ * ===========================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * 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.
+ * ========================================================== */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* TOOLTIP PUBLIC CLASS DEFINITION
+ * =============================== */
+
+ var Tooltip = function (element, options) {
+ this.init('tooltip', element, options)
+ }
+
+ Tooltip.prototype = {
+
+ constructor: Tooltip
+
+ , init: function (type, element, options) {
+ var eventIn
+ , eventOut
+
+ this.type = type
+ this.$element = $(element)
+ this.options = this.getOptions(options)
+ this.enabled = true
+
+ if (this.options.trigger != 'manual') {
+ eventIn = this.options.trigger == 'hover' ? 'mouseenter' : 'focus'
+ eventOut = this.options.trigger == 'hover' ? 'mouseleave' : 'blur'
+ this.$element.on(eventIn, this.options.selector, $.proxy(this.enter, this))
+ this.$element.on(eventOut, this.options.selector, $.proxy(this.leave, this))
+ }
+
+ this.options.selector ?
+ (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
+ this.fixTitle()
+ }
+
+ , getOptions: function (options) {
+ options = $.extend({}, $.fn[this.type].defaults, options, this.$element.data())
+
+ if (options.delay && typeof options.delay == 'number') {
+ options.delay = {
+ show: options.delay
+ , hide: options.delay
+ }
+ }
+
+ return options
+ }
+
+ , enter: function (e) {
+ var self = $(e.currentTarget)[this.type](this._options).data(this.type)
+
+ if (!self.options.delay || !self.options.delay.show) return self.show()
+
+ clearTimeout(this.timeout)
+ self.hoverState = 'in'
+ this.timeout = setTimeout(function() {
+ if (self.hoverState == 'in') self.show()
+ }, self.options.delay.show)
+ }
+
+ , leave: function (e) {
+ var self = $(e.currentTarget)[this.type](this._options).data(this.type)
+
+ if (this.timeout) clearTimeout(this.timeout)
+ if (!self.options.delay || !self.options.delay.hide) return self.hide()
+
+ self.hoverState = 'out'
+ this.timeout = setTimeout(function() {
+ if (self.hoverState == 'out') self.hide()
+ }, self.options.delay.hide)
+ }
+
+ , show: function () {
+ var $tip
+ , inside
+ , pos
+ , actualWidth
+ , actualHeight
+ , placement
+ , tp
+
+ if (this.hasContent() && this.enabled) {
+ $tip = this.tip()
+ this.setContent()
+
+ if (this.options.animation) {
+ $tip.addClass('fade')
+ }
+
+ placement = typeof this.options.placement == 'function' ?
+ this.options.placement.call(this, $tip[0], this.$element[0]) :
+ this.options.placement
+
+ inside = /in/.test(placement)
+
+ $tip
+ .remove()
+ .css({ top: 0, left: 0, display: 'block' })
+ .appendTo(inside ? this.$element : document.body)
+
+ pos = this.getPosition(inside)
+
+ actualWidth = $tip[0].offsetWidth
+ actualHeight = $tip[0].offsetHeight
+
+ switch (inside ? placement.split(' ')[1] : placement) {
+ case 'bottom':
+ tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}
+ break
+ case 'top':
+ tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}
+ break
+ case 'left':
+ tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}
+ break
+ case 'right':
+ tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}
+ break
+ }
+
+ $tip
+ .css(tp)
+ .addClass(placement)
+ .addClass('in')
+ }
+ }
+
+ , isHTML: function(text) {
+ // html string detection logic adapted from jQuery
+ return typeof text != 'string'
+ || ( text.charAt(0) === "<"
+ && text.charAt( text.length - 1 ) === ">"
+ && text.length >= 3
+ ) || /^(?:[^<]*<[\w\W]+>[^>]*$)/.exec(text)
+ }
+
+ , setContent: function () {
+ var $tip = this.tip()
+ , title = this.getTitle()
+
+ $tip.find('.tooltip-inner')[this.isHTML(title) ? 'html' : 'text'](title)
+ $tip.removeClass('fade in top bottom left right')
+ }
+
+ , hide: function () {
+ var that = this
+ , $tip = this.tip()
+
+ $tip.removeClass('in')
+