/** * Copyright 2013 Benjamin Lerer * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.horizondb.db.btree; import io.horizondb.db.metrics.Monitorable; import io.horizondb.io.ByteReader; import io.horizondb.io.ByteWriter; import io.horizondb.io.files.FileDataOutput; import io.horizondb.io.files.SeekableFileDataInput; import io.horizondb.io.serialization.Parser; import io.horizondb.io.serialization.Serializable; import java.io.Closeable; import java.io.IOException; import java.nio.file.Path; import com.codahale.metrics.MetricRegistry; import static org.apache.commons.lang.Validate.notEmpty; import static org.apache.commons.lang.Validate.notNull; /** * */ public final class BTreeStore<K extends Comparable<K> & Serializable, V extends Serializable> implements Monitorable, Closeable { /** * The B+Tree name. */ private final String name; /** * The B+Tree in which are stored the entries. */ private final BTree<K, V> btree; /** * The B+Tree node manager. */ private final NodeManager<K, V> nodeManager; public static <K extends Comparable<K> & Serializable, V extends Serializable> BTreeStore<K, V> newDiskStore(String name, Path file, int branchingFactor, Parser<K> keyParser, Parser<V> valueParser) throws IOException { notEmpty(name, "the name parameter must not be empty."); notNull(file, "the file parameter must not be null."); notNull(keyParser, "the keyParser parameter must not be null."); notNull(valueParser, "the valueParser parameter must not be null."); NodeManager<K, V> nodeManager = new OnDiskNodeManager<>(MetricRegistry.name(BTreeStore.class, "bTree"), file, new GenericNodeWriterFactory<K, V>(), new GenericNodeReaderFactory<K, V>(keyParser, valueParser)); return new BTreeStore<K, V>(name, branchingFactor, nodeManager); } /** * Creates a new <code>BTreeStore</code> that store all is data in memory. * * @param name the store name * @param branchingFactor the B+Tree branching factor * @return a new <code>BTreeStore</code> that store all is data in memory. */ public static <K extends Comparable<K> & Serializable, V extends Serializable> BTreeStore<K, V> newInMemoryStore(String name, int branchingFactor) { notEmpty(name, "the name parameter must not be empty."); NodeManager<K, V> nodeManager = new InMemoryNodeManager<>(MetricRegistry.name(BTreeStore.class, "bTree")); return new BTreeStore<K, V>(name, branchingFactor, nodeManager); } private BTreeStore(String name, int branchingFactor, NodeManager<K, V> nodeManager) { this.name = name; this.nodeManager = nodeManager; this.btree = new BTree<>(this.nodeManager, branchingFactor); } /** * {@inheritDoc} */ @Override public String getName() { return this.name; } /** * {@inheritDoc} */ @Override public void register(MetricRegistry registry) { this.nodeManager.register(registry); } /** * {@inheritDoc} */ @Override public void unregister(MetricRegistry registry) { this.nodeManager.unregister(registry); } /** * {@inheritDoc} */ @Override public void close() { this.nodeManager.close(); } public void insert(K key, V value) throws IOException { this.btree.insert(key, value); } /** * Inserts the specified record into this tree if no record exists with the specified key. * * @param key the record key. * @param value the record value. * @return <code>true</code> if the record has been successfully inserted, <code>false</code> otherwise. * @throws IOException if an IO problem occurs. */ public boolean insertIfAbsent(K key, V value) throws IOException { return this.btree.insertIfAbsent(key, value); } /** * Deletes the specified record from this tree if it exists. * * @param key the record key. * @throws IOException if an IO problem occurs. * @return <code>true</code> if the record has been successfully deleted, <code>false</code> otherwise. * */ public boolean deleteIfPresent(K key) throws IOException { return this.btree.deleteIfPresent(key); } public V get(K key) throws IOException { return this.btree.get(key); } public KeyValueIterator<K, V> iterator(K from, K to) throws IOException { return this.btree.iterator(from, to); } /** * The <code>NodeWriter</code> factory for <code></code>. */ public static final class GenericNodeWriterFactory<K extends Comparable<K> & Serializable, V extends Serializable> implements NodeWriterFactory<K, V> { /** * {@inheritDoc} */ @Override public GenericNodeWriter<K, V> newWriter(FileDataOutput output) throws IOException { return new GenericNodeWriter<K, V>(output); } }; /** * Generic <code>NodeWriter</code> for the <code>Node</code>s with <code>Serializable</code> key and value. */ private static final class GenericNodeWriter<K extends Comparable<K> & Serializable, V extends Serializable> extends AbstractNodeWriter<K, V> { /** * Creates a new <code>GenericNodeWriter</code> that write to the specified output. * * @param output the output used by the writer. * @throws IOException if an I/O problem occurs. */ public GenericNodeWriter(FileDataOutput output) throws IOException { super(output); } /** * {@inheritDoc} */ @Override protected int computeKeySize(K key) throws IOException { return key.computeSerializedSize(); } /** * {@inheritDoc} */ @Override protected int computeValueSize(V value) throws IOException { return value.computeSerializedSize(); } /** * {@inheritDoc} */ @Override protected void writeKey(ByteWriter writer, K key) throws IOException { writer.writeObject(key); } /** * {@inheritDoc} */ @Override protected void writeValue(ByteWriter writer, V value) throws IOException { writer.writeObject(value); } } /** * The <code>NodeReader</code> factory. */ public static final class GenericNodeReaderFactory<K extends Comparable<K> & Serializable, V extends Serializable> implements NodeReaderFactory<K, V> { /** * The key parser */ private final Parser<K> keyParser; /** * The value parser */ private final Parser<V> valueParser; /** * Creates a new <code>GenericNodeReaderFactory</code> that creates <code>GenericNodeReader</code>s instances. * * @param keyParser the key parser * @param valueParser the value parser */ public GenericNodeReaderFactory(Parser<K> keyParser, Parser<V> valueParser) { this.keyParser = keyParser; this.valueParser = valueParser; } /** * {@inheritDoc} */ @Override public NodeReader<K, V> newReader(SeekableFileDataInput input) throws IOException { return new GenericNodeReader<K, V>(input, this.keyParser, this.valueParser); } }; /** * Generic <code>NodeReader</code> for the <code>Node</code>s with <code>Serializable</code> key and value. * */ private static final class GenericNodeReader<K extends Comparable<K> & Serializable, V extends Serializable> extends AbstractNodeReader<K, V> { /** * The key parser */ private final Parser<K> keyParser; /** * The value parser */ private final Parser<V> valueParser; /** * Creates a new <code>GenericNodeReader</code> that read from the specified input. * * @param input the input used by the reader. * @param keyParser the key parser * @param valueParser the value parser * @throws IOException if an I/O problem occurs. */ public GenericNodeReader(SeekableFileDataInput input, Parser<K> keyParser, Parser<V> valueParser) throws IOException { super(input); this.keyParser = keyParser; this.valueParser = valueParser; } /** * {@inheritDoc} */ @Override protected V readValue(ByteReader reader) throws IOException { return this.valueParser.parseFrom(reader); } /** * {@inheritDoc} */ @Override protected K readKey(ByteReader reader) throws IOException { return this.keyParser.parseFrom(reader); } } }