org.mapdb.DB Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mapdb Show documentation
Show all versions of mapdb Show documentation
MapDB provides concurrent Maps, Sets and Queues backed by disk storage or off-heap memory. It is a fast, scalable and easy to use embedded Java database.
/*
* Copyright (c) 2012 Jan Kotek
*
* 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 org.mapdb;
import java.lang.ref.WeakReference;
import java.util.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* A database with easy access to named maps and other collections.
*
* @author Jan Kotek
*/
//TODO DB uses global lock, replace it with ReadWrite lock or fine grained locking.
@SuppressWarnings("unchecked")
public class DB {
protected final boolean strictDBGet;
/** Engine which provides persistence for this DB*/
protected Engine engine;
/** already loaded named collections. It is important to keep collections as singletons, because of 'in-memory' locking*/
protected Map> namesInstanciated = new HashMap>();
protected Map namesLookup =
Collections.synchronizedMap( //TODO remove synchronized map, after DB locking is resolved
new HashMap());
/** view over named records */
protected SortedMap catalog;
protected static class IdentityWrapper{
final Object o;
public IdentityWrapper(Object o) {
this.o = o;
}
@Override
public int hashCode() {
return System.identityHashCode(o);
}
@Override
public boolean equals(Object v) {
return ((IdentityWrapper)v).o==o;
}
}
/**
* Construct new DB. It is just thin layer over {@link Engine} which does the real work.
* @param engine
*/
public DB(final Engine engine){
this(engine,false,false);
}
public DB(Engine engine, boolean strictDBGet, boolean disableLocks) {
if(!(engine instanceof EngineWrapper)){
//access to Store should be prevented after `close()` was called.
//So for this we have to wrap raw Store into EngineWrapper
engine = new EngineWrapper(engine);
}
this.engine = engine;
this.strictDBGet = strictDBGet;
engine.getSerializerPojo().setDb(this);
reinit();
}
protected void reinit() {
//open name dir
catalog = BTreeMap.preinitCatalog(this);
}
public A catGet(String name, A init){
assert(Thread.holdsLock(DB.this));
A ret = (A) catalog.get(name);
return ret!=null? ret : init;
}
public A catGet(String name){
assert(Thread.holdsLock(DB.this));
return (A) catalog.get(name);
}
public A catPut(String name, A value){
assert(Thread.holdsLock(DB.this));
catalog.put(name, value);
return value;
}
public A catPut(String name, A value, A retValueIfNull){
assert(Thread.holdsLock(DB.this));
if(value==null) return retValueIfNull;
catalog.put(name, value);
return value;
}
/** returns name for this object, if it has name and was instanciated by this DB*/
public String getNameForObject(Object obj) {
//TODO this method should be synchronized, but it causes deadlock.
return namesLookup.get(new IdentityWrapper(obj));
}
public class HTreeMapMaker{
protected final String name;
public HTreeMapMaker(String name) {
this.name = name;
}
protected boolean counter = false;
protected Serializer keySerializer = null;
protected Serializer valueSerializer = null;
protected long expireMaxSize = 0L;
protected long expire = 0L;
protected long expireAccess = 0L;
protected long expireStoreSize;
protected Hasher hasher = null;
protected Fun.Function1 valueCreator = null;
/** by default collection does not have counter, without counter updates are faster, but entire collection needs to be traversed to count items.*/
public HTreeMapMaker counterEnable(){
this.counter = true;
return this;
}
/** keySerializer used to convert keys into/from binary form. */
public HTreeMapMaker keySerializer(Serializer keySerializer){
this.keySerializer = keySerializer;
return this;
}
/** valueSerializer used to convert values into/from binary form. */
public HTreeMapMaker valueSerializer(Serializer valueSerializer){
this.valueSerializer = valueSerializer;
return this;
}
/** maximal number of entries in this map. Less used entries will be expired and removed to make collection smaller */
public HTreeMapMaker expireMaxSize(long maxSize){
this.expireMaxSize = maxSize;
this.counter = true;
return this;
}
/** Specifies that each entry should be automatically removed from the map once a fixed duration has elapsed after the entry's creation, or the most recent replacement of its value. */
public HTreeMapMaker expireAfterWrite(long interval, TimeUnit timeUnit){
this.expire = timeUnit.toMillis(interval);
return this;
}
/** Specifies that each entry should be automatically removed from the map once a fixed duration has elapsed after the entry's creation, or the most recent replacement of its value. */
public HTreeMapMaker expireAfterWrite(long interval){
this.expire = interval;
return this;
}
/** Specifies that each entry should be automatically removed from the map once a fixed duration has elapsed after the entry's creation, the most recent replacement of its value, or its last access. Access time is reset by all map read and write operations */
public HTreeMapMaker expireAfterAccess(long interval, TimeUnit timeUnit){
this.expireAccess = timeUnit.toMillis(interval);
return this;
}
/** Specifies that each entry should be automatically removed from the map once a fixed duration has elapsed after the entry's creation, the most recent replacement of its value, or its last access. Access time is reset by all map read and write operations */
public HTreeMapMaker expireAfterAccess(long interval){
this.expireAccess = interval;
return this;
}
public HTreeMapMaker expireStoreSize(double maxStoreSize) {
this.expireStoreSize = (long) (maxStoreSize*1024L*1024L*1024L);
return this;
}
/** If value is not found, HTreeMap can fetch and insert default value. `valueCreator` is used to return new value.
* This way `HTreeMap.get()` never returns null */
public HTreeMapMaker valueCreator(Fun.Function1 valueCreator){
this.valueCreator = valueCreator;
return this;
}
public HTreeMapMaker hasher(Hasher hasher){
this.hasher = hasher;
return this;
}
public HTreeMap make(){
if(expireMaxSize!=0) counter =true;
return DB.this.createHashMap(HTreeMapMaker.this);
}
public HTreeMap makeOrGet(){
synchronized (DB.this){
//TODO add parameter check
return (HTreeMap) (catGet(name+".type")==null?
make():getHashMap(name));
}
}
}
public class HTreeSetMaker{
protected final String name;
public HTreeSetMaker(String name) {
this.name = name;
}
protected boolean counter = false;
protected Serializer serializer = null;
protected long expireMaxSize = 0L;
protected long expireStoreSize = 0L;
protected long expire = 0L;
protected long expireAccess = 0L;
protected Hasher hasher = null;
/** by default collection does not have counter, without counter updates are faster, but entire collection needs to be traversed to count items.*/
public HTreeSetMaker counterEnable(){
this.counter = true;
return this;
}
/** keySerializer used to convert keys into/from binary form. */
public HTreeSetMaker serializer(Serializer serializer){
this.serializer = serializer;
return this;
}
/** maximal number of entries in this map. Less used entries will be expired and removed to make collection smaller */
public HTreeSetMaker expireMaxSize(long maxSize){
this.expireMaxSize = maxSize;
this.counter = true;
return this;
}
/** maximal size of store in GB, if store is larger entries will start expiring */
public HTreeSetMaker expireStoreSize(double maxStoreSize){
this.expireStoreSize = (long) (maxStoreSize * 1024L * 1024L * 1024L);
return this;
}
/** Specifies that each entry should be automatically removed from the map once a fixed duration has elapsed after the entry's creation, or the most recent replacement of its value. */
public HTreeSetMaker expireAfterWrite(long interval, TimeUnit timeUnit){
this.expire = timeUnit.toMillis(interval);
return this;
}
/** Specifies that each entry should be automatically removed from the map once a fixed duration has elapsed after the entry's creation, or the most recent replacement of its value. */
public HTreeSetMaker expireAfterWrite(long interval){
this.expire = interval;
return this;
}
/** Specifies that each entry should be automatically removed from the map once a fixed duration has elapsed after the entry's creation, the most recent replacement of its value, or its last access. Access time is reset by all map read and write operations */
public HTreeSetMaker expireAfterAccess(long interval, TimeUnit timeUnit){
this.expireAccess = timeUnit.toMillis(interval);
return this;
}
/** Specifies that each entry should be automatically removed from the map once a fixed duration has elapsed after the entry's creation, the most recent replacement of its value, or its last access. Access time is reset by all map read and write operations */
public HTreeSetMaker expireAfterAccess(long interval){
this.expireAccess = interval;
return this;
}
public HTreeSetMaker hasher(Hasher hasher){
this.hasher = hasher;
return this;
}
public Set make(){
if(expireMaxSize!=0) counter =true;
return DB.this.createHashSet(HTreeSetMaker.this);
}
public Set makeOrGet(){
synchronized (DB.this){
//TODO add parameter check
return (Set) (catGet(name+".type")==null?
make():getHashSet(name));
}
}
}
/**
* Opens existing or creates new Hash Tree Map.
* This collection perform well under concurrent access.
* Is best for large keys and large values.
*
* @param name of the map
* @return map
*/
synchronized public HTreeMap getHashMap(String name){
return getHashMap(name, null);
}
/**
* Opens existing or creates new Hash Tree Map.
* This collection perform well under concurrent access.
* Is best for large keys and large values.
*
* @param name of map
* @param valueCreator if value is not found, new is created and placed into map.
* @return map
*/
synchronized public HTreeMap getHashMap(String name, Fun.Function1 valueCreator){
checkNotClosed();
HTreeMap ret = (HTreeMap) getFromWeakCollection(name);
if(ret!=null) return ret;
String type = catGet(name + ".type", null);
if(type==null){
checkShouldCreate(name);
if(engine.isReadOnly()){
Engine e = new StoreHeap();
new DB(e).getHashMap("a");
return namedPut(name,
new DB(new EngineWrapper.ReadOnlyEngine(e)).getHashMap("a"));
}
return createHashMap(name).make();
}
//check type
checkType(type, "HashMap");
//open existing map
ret = new HTreeMap(engine,
(Long)catGet(name+".counterRecid"),
(Integer)catGet(name+".hashSalt"),
(long[])catGet(name+".segmentRecids"),
catGet(name+".keySerializer",getDefaultSerializer()),
catGet(name+".valueSerializer",getDefaultSerializer()),
catGet(name+".expireTimeStart",0L),
catGet(name+".expire",0L),
catGet(name+".expireAccess",0L),
catGet(name+".expireMaxSize",0L),
catGet(name+".expireStoreSize",0L),
(long[])catGet(name+".expireHeads",null),
(long[])catGet(name+".expireTails",null),
valueCreator,
catGet(name+".hasher", Hasher.BASIC),
false);
namedPut(name, ret);
return ret;
}
public V namedPut(String name, Object ret) {
namesInstanciated.put(name, new WeakReference