org.mapdb.DB Maven / Gradle / Ivy
/*
* 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);
}
public DB(Engine engine, boolean strictDBGet) {
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);
}
protected A catGet(String name, A init){
assert(Thread.holdsLock(DB.this));
A ret = (A) catalog.get(name);
return ret!=null? ret : init;
}
protected A catGet(String name){
assert(Thread.holdsLock(DB.this));
return (A) catalog.get(name);
}
protected A catPut(String name, A value){
assert(Thread.holdsLock(DB.this));
catalog.put(name, value);
return value;
}
protected 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 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;
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;
}
/** 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 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;
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 map
* @param key
* @param value
* @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.
* @param key
* @param value
* @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),
(long[])catGet(name+".expireHeads",null),
(long[])catGet(name+".expireTails",null),
valueCreator,
null);
namedPut(name, ret);
return ret;
}
protected V namedPut(String name, Object ret) {
namesInstanciated.put(name, new WeakReference
© 2015 - 2025 Weber Informatics LLC | Privacy Policy