[Home] DMelt member access . Java source code: org.mapdb.DB
/*
* 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.io.Closeable;
import java.io.File;
import java.io.IOError;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.security.SecureRandom;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* 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 implements Closeable {
protected static final Logger LOG = Logger.getLogger(DB.class.getName());
public static final String METRICS_DATA_WRITE = "data.write";
public static final String METRICS_RECORD_WRITE = "record.write";
public static final String METRICS_DATA_READ = "data.read";
public static final String METRICS_RECORD_READ = "record.read";
public static final String METRICS_CACHE_HIT = "cache.hit";
public static final String METRICS_CACHE_MISS = "cache.miss";
protected interface Keys{
String type = ".type";
String keySerializer = ".keySerializer";
String valueSerializer = ".valueSerializer";
String serializer = ".serializer";
String counterRecids = ".counterRecids";
String hashSalt = ".hashSalt";
String segmentRecids = ".segmentRecids";
String expire = ".expire";
String expireMaxSize = ".expireMaxSize";
String expireAccess = ".expireAccess";
String expireStoreSize = ".expireStoreSize";
String expireHeads = ".expireHeads";
String expireTails = ".expireTails";
String expireTimeStart = ".expireTimeStart";
String rootRecidRef = ".rootRecidRef";
String maxNodeSize = ".maxNodeSize";
String valuesOutsideNodes = ".valuesOutsideNodes";
String numberOfNodeMetas = ".numberOfNodeMetas";
String headRecid = ".headRecid";
String tailRecid = ".tailRecid";
String useLocks = ".useLocks";
String size = ".size";
String recid = ".recid";
String headInsertRecid = ".headInsertRecid";
}
protected final boolean strictDBGet;
protected final boolean deleteFilesAfterClose;
/** 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 =
new ConcurrentHashMap();
/** view over named records */
protected SortedMap catalog;
protected ScheduledExecutorService executor = null;
protected SerializerPojo serializerPojo;
protected ScheduledExecutorService metricsExecutor;
protected ScheduledExecutorService storeExecutor;
protected ScheduledExecutorService cacheExecutor;
protected final Set unknownClasses = new ConcurrentSkipListSet();
//TODO collection get/create should be under consistencyLock.readLock()
protected final ReadWriteLock consistencyLock;
/** changes object hash and equals method to use identity */
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, null, false, null, 0, null, null, null);
}
public DB(
final Engine engine,
boolean strictDBGet,
boolean deleteFilesAfterClose,
ScheduledExecutorService executor,
boolean lockDisable,
ScheduledExecutorService metricsExecutor,
long metricsLogInterval,
ScheduledExecutorService storeExecutor,
ScheduledExecutorService cacheExecutor,
Fun.Function1 classLoader
) {
//TODO investigate dereference and how non-final field affect performance. Perhaps abandon dereference completely
// 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;
this.deleteFilesAfterClose = deleteFilesAfterClose;
this.executor = executor;
this.consistencyLock = lockDisable ?
new Store.ReadWriteSingleLock(Store.NOLOCK) :
new ReentrantReadWriteLock();
this.metricsExecutor = metricsExecutor==null ? executor : metricsExecutor;
this.storeExecutor = storeExecutor;
this.cacheExecutor = cacheExecutor;
serializerPojo = new SerializerPojo(
//get name for given object
new Fun.Function1() {
@Override
public String run(Object o) {
if(o==DB.this)
return "$$DB_OBJECT_Q!#!@#!#@9009a09sd";
return getNameForObject(o);
}
},
//get object with given name
new Fun.Function1() {
@Override
public Object run(String name) {
Object ret = get(name);
if(ret == null && "$$DB_OBJECT_Q!#!@#!#@9009a09sd".equals(name))
return DB.this;
return ret;
}
},
//load class catalog
new Fun.Function1Int() {
@Override
public SerializerPojo.ClassInfo run(int index) {
long[] classInfoRecids = DB.this.engine.get(Engine.RECID_CLASS_CATALOG, Serializer.RECID_ARRAY);
if(classInfoRecids==null || index<0 || index>=classInfoRecids.length)
return null;
return getEngine().get(classInfoRecids[index], serializerPojo.classInfoSerializer);
}
},
new Fun.Function0() {
@Override
public SerializerPojo.ClassInfo[] run() {
long[] classInfoRecids = engine.get(Engine.RECID_CLASS_CATALOG, Serializer.RECID_ARRAY);
SerializerPojo.ClassInfo[] ret = new SerializerPojo.ClassInfo[classInfoRecids==null?0:classInfoRecids.length];
for(int i=0;i() {
@Override public Void run(String className) {
unknownClasses.add(className);
return null;
}
},
classLoader,
engine);
reinit();
if(metricsExecutor!=null && metricsLogInterval!=0){
if(!CC.METRICS_CACHE){
LOG.warning("MapDB was compiled without cache metrics. No cache hit/miss will be reported");
}
metricsExecutor.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
metricsLog();
}
}, metricsLogInterval, metricsLogInterval, TimeUnit.MILLISECONDS);
}
}
public void metricsLog() {
Map metrics = DB.this.metricsGet();
String s = metrics.toString();
LOG.info("Metrics: "+s);
}
public Map metricsGet() {
Map ret = new TreeMap();
Store s = Store.forEngine(engine);
s.metricsCollect(ret);
return Collections.unmodifiableMap(ret);
}
protected void reinit() {
//open name dir
//$DELAY$
catalog = BTreeMap.preinitCatalog(this);
}
public A catGet(String name, A init){
if(CC.ASSERT && ! (Thread.holdsLock(DB.this)))
throw new AssertionError();
A ret = (A) catalog.get(name);
return ret!=null? ret : init;
}
public A catGet(String name){
if(CC.ASSERT && ! (Thread.holdsLock(DB.this)))
throw new AssertionError();
//$DELAY$
return (A) catalog.get(name);
}
public A catPut(String name, A value){
if(CC.ASSERT && ! (Thread.holdsLock(DB.this)))
throw new AssertionError();
//$DELAY$
catalog.put(name, value);
return value;
}
public A catPut(String name, A value, A retValueIfNull){
if(CC.ASSERT && ! (Thread.holdsLock(DB.this)))
throw new AssertionError();
if(value==null) return retValueIfNull;
//$DELAY$
catalog.put(name, value);
return value;
}
/**
* Get name for object. DB keeps weak reference to all objects it instanciated
*
* @param obj object to get name for
* @return name for this object, if it has name and was instanciated by this DB
*/
public String getNameForObject(Object obj) {
return namesLookup.get(new IdentityWrapper(obj));
}
static public class HTreeMapMaker{
protected final DB db;
protected final String name;
protected final Engine[] engines;
public HTreeMapMaker(DB db, String name, Engine[] engines) {
this.db = db;
this.name = name;
this.engines = engines;
this.executor = db.executor;
}
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 Bind.MapWithModificationListener ondisk;
protected boolean ondiskOverwrite;
protected Fun.Function1,?> valueCreator = null;
protected Iterator pumpSource;
protected Fun.Function1 pumpKeyExtractor;
protected Fun.Function1 pumpValueExtractor;
protected int pumpPresortBatchSize = (int) 1e7;
protected boolean pumpIgnoreDuplicates = false;
protected boolean closeEngine = false;
protected ScheduledExecutorService executor;
protected long executorPeriod = CC.DEFAULT_HTREEMAP_EXECUTOR_PERIOD;
/** 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;
}
/** maximal size of store in GB, if store is larger entries will start expiring */
public HTreeMapMaker expireStoreSize(double maxStoreSize) {
this.expireStoreSize = (long) (maxStoreSize*1024L*1024L*1024L);
return this;
}
/**
* After expiration (or deletion), put entries into given map
*
* @param ondisk Map populated with data after expiration
* @param overwrite if true any data in onDisk will be overwritten.
* If false only non-existing keys will be inserted
* ({@code put() versus putIfAbsent()};
*
* @return this builder
*/
public HTreeMapMaker expireOverflow(Bind.MapWithModificationListener ondisk, boolean overwrite){
this.ondisk = ondisk;
this.ondiskOverwrite = overwrite;
return this;
}
/** If value is not found, HTreeMap can fetch and insert default value. {@code valueCreator} is used to return new value.
* This way {@code HTreeMap.get()} never returns null */
public HTreeMapMaker valueCreator(Fun.Function1,?> valueCreator){
this.valueCreator = valueCreator;
return this;
}
public HTreeMapMaker pumpSource(Iterator keysSource, Fun.Function1 valueExtractor){
this.pumpSource = keysSource;
this.pumpKeyExtractor = Fun.extractNoTransform();
this.pumpValueExtractor = valueExtractor;
return this;
}
public HTreeMapMaker pumpSource(Iterator> entriesSource){
this.pumpSource = entriesSource;
this.pumpKeyExtractor = Fun.extractKey();
this.pumpValueExtractor = Fun.extractValue();
return this;
}
public HTreeMapMaker pumpPresort(int batchSize){
this.pumpPresortBatchSize = batchSize;
return this;
}
public HTreeMapMaker executorEnable(){
return executorEnable(Executors.newSingleThreadScheduledExecutor());
}
public HTreeMapMaker executorEnable(ScheduledExecutorService executor) {
this.executor = executor;
return this;
}
public HTreeMapMaker executorPeriod(long period){
this.executorPeriod = period;
return this;
}
/**
* If source iteretor contains an duplicate key, exception is thrown.
* This options will only use firts key and ignore any consequentive duplicates.
*/
public HTreeMapMaker pumpIgnoreDuplicates(){
this.pumpIgnoreDuplicates = true;
return this;
}
protected HTreeMapMaker closeEngine() {
closeEngine = true;
return this;
}
public HTreeMap make(){
if(expireMaxSize!=0) counter =true;
return db.hashMapCreate(HTreeMapMaker.this);
}
public HTreeMap makeOrGet(){
//$DELAY$
synchronized (db){
//TODO add parameter check
//$DELAY$
return (HTreeMap) (db.catGet(name+Keys.type)==null?
make():
db.hashMap(name,keySerializer,valueSerializer,(Fun.Function1)valueCreator));
}
}
}
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 Iterator pumpSource;
protected int pumpPresortBatchSize = (int) 1e7;
protected boolean pumpIgnoreDuplicates = false;
protected boolean closeEngine = false;
protected ScheduledExecutorService executor = DB.this.executor;
protected long executorPeriod = CC.DEFAULT_HTREEMAP_EXECUTOR_PERIOD;
/** 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 pumpSource(Iterator> source){
this.pumpSource = source;
return this;
}
/**
* If source iteretor contains an duplicate key, exception is thrown.
* This options will only use firts key and ignore any consequentive duplicates.
*/
public HTreeSetMaker pumpIgnoreDuplicates(){
this.pumpIgnoreDuplicates = true;
return this;
}
public HTreeSetMaker pumpPresort(int batchSize){
this.pumpPresortBatchSize = batchSize;
return this;
}
public HTreeSetMaker executorEnable(){
return executorEnable(Executors.newSingleThreadScheduledExecutor());
}
public HTreeSetMaker executorEnable(ScheduledExecutorService executor) {
this.executor = executor;
return this;
}
public HTreeSetMaker executorPeriod(long period){
this.executorPeriod = period;
return this;
}
protected HTreeSetMaker closeEngine() {
this.closeEngine = true;
return this;
}
public Set make(){
if(expireMaxSize!=0) counter =true;
return DB.this.hashSetCreate(HTreeSetMaker.this);
}
public Set makeOrGet(){
synchronized (DB.this){
//$DELAY$
//TODO add parameter check
return (Set) (catGet(name+Keys.type)==null?
make(): hashSet(name,serializer));
}
}
}
/**
* @deprecated method renamed, use {@link DB#hashMap(String)}
*/
synchronized public HTreeMap getHashMap(String name){
return hashMap(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 hashMap(String name){
return hashMap(name, null, null, null);
}
/**
* @deprecated method renamed, use {@link DB#hashMap(String,Serializer, Serializer, org.mapdb.Fun.Function1)}
*/
synchronized public HTreeMap getHashMap(String name, Fun.Function1 valueCreator){
return hashMap(name, null, null, valueCreator);
}
/**
* 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 keySerializer serializer used on keys
* @param valueSerializer serializer used on values
* @return map
*/
synchronized public HTreeMap hashMap(
String name,
Serializer keySerializer,
Serializer valueSerializer) {
return hashMap(name, keySerializer,valueSerializer,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 keySerializer serializer used on keys
* @param valueSerializer serializer used on values
* @param valueCreator if value is not found, new is created and placed into map.
* @return map
*/
synchronized public HTreeMap hashMap(
String name,
Serializer keySerializer,
Serializer valueSerializer,
Fun.Function1 valueCreator){
checkNotClosed();
HTreeMap ret = (HTreeMap) getFromWeakCollection(name);
if(ret!=null) return ret;
String type = catGet(name + Keys.type, null);
//$DELAY$
if(type==null){
//$DELAY$
checkShouldCreate(name);
if(engine.isReadOnly()){
Engine e = new StoreHeap(true,1,0,false);
//$DELAY$
new DB(e).hashMap("a");
return namedPut(name,
new DB(new Engine.ReadOnlyWrapper(e)).hashMap("a"));
}
HTreeMapMaker m = hashMapCreate(name);
if(valueCreator!=null)
m = m.valueCreator(valueCreator);
if(keySerializer!=null)
m = m.keySerializer(keySerializer);
if(valueSerializer!=null)
m = m.valueSerializer(valueSerializer);
return m.make();
}
//check type
checkType(type, "HashMap");
Object keySer2 = checkPlaceholder(name+Keys.keySerializer, keySerializer);
Object valSer2 = checkPlaceholder(name+Keys.valueSerializer, valueSerializer);
//open existing map
//$DELAY$
ret = new HTreeMap(
HTreeMap.fillEngineArray(engine),
false,
(long[])catGet(name+Keys.counterRecids),
(Integer)catGet(name+Keys.hashSalt),
(long[])catGet(name+Keys.segmentRecids),
(Serializer)keySer2,
(Serializer)valSer2,
catGet(name+Keys.expireTimeStart,0L),
catGet(name+Keys.expire,0L),
catGet(name+Keys.expireAccess,0L),
catGet(name+Keys.expireMaxSize,0L),
catGet(name+Keys.expireStoreSize,0L),
(long[])catGet(name+Keys.expireHeads,null),
(long[])catGet(name+Keys.expireTails,null),
valueCreator,
executor,
CC.DEFAULT_HTREEMAP_EXECUTOR_PERIOD,
false,
consistencyLock.readLock()
);
//$DELAY$
namedPut(name, ret);
//$DELAY$
return ret;
}
protected K checkPlaceholder(String nameCatParam, K fromConstructor) {
K fromCatalog = catGet(nameCatParam);
if(fromConstructor!=null){
if(fromCatalog!= Fun.PLACEHOLDER && fromCatalog!=fromConstructor &&
!((SerializerBase)getDefaultSerializer()).equalsBinary(fromCatalog, fromConstructor)){
LOG.warning(nameCatParam+" is defined in Name Catalog, but other serializer was passed as constructor argument. Using one from constructor argument");
}
fromCatalog = fromConstructor;
}
if(fromCatalog==Fun.PLACEHOLDER || fromCatalog==null){
throw new DBException.UnknownSerializer(nameCatParam+" is not defined in Name Catalog nor constructor argument");
}
return fromCatalog;
}
public V namedPut(String name, Object ret) {
//$DELAY$
namesInstanciated.put(name, new WeakReference(ret));
//$DELAY$
namesLookup.put(new IdentityWrapper(ret), name);
return (V) ret;
}
/**
* @deprecated method renamed, use {@link DB#hashMapCreate(String)}
*/
public HTreeMapMaker createHashMap(String name){
return hashMapCreate(name);
}
/**
* Returns new builder for HashMap with given name
*
* @param name of map to create
* @throws IllegalArgumentException if name is already used
* @return maker, call {@code .make()} to create map
*/
public HTreeMapMaker hashMapCreate(String name){
return new HTreeMapMaker(DB.this, name, HTreeMap.fillEngineArray(engine));
}
/**
* Creates new HashMap with more specific arguments
*
* @throws IllegalArgumentException if name is already used
* @return newly created map
*/
synchronized protected HTreeMap hashMapCreate(HTreeMapMaker m){
String name = m.name;
checkNameNotExists(name);
//$DELAY$
long expireTimeStart=0, expire=0, expireAccess=0, expireMaxSize = 0, expireStoreSize=0;
long[] expireHeads=null, expireTails=null;
if(m.ondisk!=null) {
if (m.valueCreator != null) {
throw new IllegalArgumentException("ValueCreator can not be used together with ExpireOverflow.");
}
final Map ondisk = m.ondisk;
m.valueCreator = new Fun.Function1() {
@Override
public Object run(Object key) {
return ondisk.get(key);
}
};
}
if(m.expire!=0 || m.expireAccess!=0 || m.expireMaxSize !=0 || m.expireStoreSize!=0){
expireTimeStart = catPut(name+Keys.expireTimeStart,System.currentTimeMillis());
expire = catPut(name+Keys.expire,m.expire);
expireAccess = catPut(name+Keys.expireAccess,m.expireAccess);
expireMaxSize = catPut(name+Keys.expireMaxSize,m.expireMaxSize);
expireStoreSize = catPut(name+Keys.expireStoreSize,m.expireStoreSize);
//$DELAY$
expireHeads = new long[HTreeMap.SEG];
expireTails = new long[HTreeMap.SEG];
for(int i=0;i ret = new HTreeMap(
m.engines,
m.closeEngine,
counterRecids==null? null : catPut(name + Keys.counterRecids, counterRecids),
catPut(name+Keys.hashSalt,new SecureRandom().nextInt()),
catPut(name+Keys.segmentRecids,HTreeMap.preallocateSegments(m.engines)),
(Serializer)m.keySerializer,
(Serializer)m.valueSerializer,
expireTimeStart,expire,expireAccess,expireMaxSize, expireStoreSize, expireHeads ,expireTails,
(Fun.Function1) m.valueCreator,
m.executor,
m.executorPeriod,
m.executor!=executor,
consistencyLock.readLock());
//$DELAY$
catalog.put(name + Keys.type, "HashMap");
namedPut(name, ret);
//pump data if specified2
if(m.pumpSource!=null) {
Pump.fillHTreeMap(
ret,
m.pumpSource,
m.pumpKeyExtractor,
m.pumpValueExtractor,
m.pumpPresortBatchSize,
m.pumpIgnoreDuplicates,
getDefaultSerializer(),
m.executor);
}
if(m.ondisk!=null){
Bind.mapPutAfterDelete(ret,m.ondisk, m.ondiskOverwrite);
}
return ret;
}
protected Object serializableOrPlaceHolder(Object o) {
SerializerBase b = (SerializerBase)getDefaultSerializer();
if(o == null || b.isSerializable(o)){
//try to serialize into temporary buffer
try {
DataIO.DataOutputByteArray out = new DataIO.DataOutputByteArray();
b.serialize(out,o);
//object is serializable
return o;
} catch (Exception e) {
//object is not serializable
return Fun.PLACEHOLDER;
}
}
return Fun.PLACEHOLDER;
}
/**
* @deprecated method renamed, use {@link DB#hashSet(String)}
*/
synchronized public Set getHashSet(String name){
return hashSet(name);
}
/**
* Opens existing or creates new Hash Tree Set.
*
* @param name of the Set
* @return set
*/
synchronized public Set hashSet(String name){
return hashSet(name,null);
}
synchronized public Set hashSet(String name, Serializer serializer){
checkNotClosed();
Set ret = (Set) getFromWeakCollection(name);
if(ret!=null) return ret;
String type = catGet(name + Keys.type, null);
//$DELAY$
if(type==null){
checkShouldCreate(name);
if(engine.isReadOnly()){
Engine e = new StoreHeap(true,1,0,false);
//$DELAY$
new DB(e).hashSet("a");
return namedPut(name,
new DB(new Engine.ReadOnlyWrapper(e)).hashSet("a"));
}
HTreeSetMaker m = hashSetCreate(name);
if(serializer!=null)
m = m.serializer(serializer);
return m.makeOrGet();
//$DELAY$
}
//check type
checkType(type, "HashSet");
Object keySer2 = checkPlaceholder(name+Keys.serializer, serializer);
//open existing map
ret = new HTreeMap(
HTreeMap.fillEngineArray(engine),
false,
(long[])catGet(name+Keys.counterRecids),
(Integer)catGet(name+Keys.hashSalt),
(long[])catGet(name+Keys.segmentRecids),
(Serializer)keySer2,
null,
catGet(name+Keys.expireTimeStart,0L),
catGet(name+Keys.expire,0L),
catGet(name+Keys.expireAccess,0L),
catGet(name+Keys.expireMaxSize,0L),
catGet(name+Keys.expireStoreSize,0L),
(long[])catGet(name+Keys.expireHeads,null),
(long[])catGet(name+Keys.expireTails,null),
null,
executor,
CC.DEFAULT_HTREEMAP_EXECUTOR_PERIOD,
false,
consistencyLock.readLock()
).keySet();
//$DELAY$
namedPut(name, ret);
//$DELAY$
return ret;
}
/**
* @deprecated method renamed, use {@link DB#hashSetCreate(String)}
*/
synchronized public HTreeSetMaker createHashSet(String name){
return hashSetCreate(name);
}
/**
* Creates new HashSet
*
* @param name of set to create
*/
synchronized public HTreeSetMaker hashSetCreate(String name){
return new HTreeSetMaker(name);
}
synchronized protected Set hashSetCreate(HTreeSetMaker m){
String name = m.name;
checkNameNotExists(name);
long expireTimeStart=0, expire=0, expireAccess=0, expireMaxSize = 0, expireStoreSize = 0;
long[] expireHeads=null, expireTails=null;
if(m.expire!=0 || m.expireAccess!=0 || m.expireMaxSize !=0){
expireTimeStart = catPut(name+Keys.expireTimeStart,System.currentTimeMillis());
expire = catPut(name+Keys.expire,m.expire);
expireAccess = catPut(name+Keys.expireAccess,m.expireAccess);
expireMaxSize = catPut(name+Keys.expireMaxSize,m.expireMaxSize);
expireStoreSize = catPut(name+Keys.expireStoreSize,m.expireStoreSize);
expireHeads = new long[HTreeMap.SEG];
//$DELAY$
expireTails = new long[HTreeMap.SEG];
for(int i=0;i ret = new HTreeMap(
engines,
m.closeEngine,
counterRecids == null ? null : catPut(name + Keys.counterRecids, counterRecids),
catPut(name+Keys.hashSalt, new SecureRandom().nextInt()), //TODO investigate if hashSalt actually prevents collision attack
catPut(name+Keys.segmentRecids,HTreeMap.preallocateSegments(engines)),
(Serializer)m.serializer,
null,
expireTimeStart,expire,expireAccess,expireMaxSize, expireStoreSize, expireHeads ,expireTails,
null,
m.executor,
m.executorPeriod,
m.executor!=executor,
consistencyLock.readLock()
);
Set ret2 = ret.keySet();
//$DELAY$
catalog.put(name + Keys.type, "HashSet");
namedPut(name, ret2);
//$DELAY$
//pump data if specified2
if(m.pumpSource!=null) {
Pump.fillHTreeMap(
ret,
m.pumpSource,
(Fun.Function1)Fun.extractNoTransform(),
null,
m.pumpPresortBatchSize,
m.pumpIgnoreDuplicates,
getDefaultSerializer(),
m.executor);
}
return ret2;
}
public static class BTreeMapMaker{
protected final String name;
protected final DB db;
public BTreeMapMaker(String name) {
this(name,null);
}
protected BTreeMapMaker(String name, DB db) {
this.name = name;
this.db = db;
executor = db==null ? null : db.executor;
}
protected int nodeSize = 32;
protected boolean valuesOutsideNodes = false;
protected boolean counter = false;
private BTreeKeySerializer _keySerializer;
private Serializer _keySerializer2;
private Comparator _comparator;
protected Serializer> valueSerializer;
protected Iterator pumpSource;
protected Fun.Function1 pumpKeyExtractor;
protected Fun.Function1 pumpValueExtractor;
protected int pumpPresortBatchSize = -1;
protected boolean pumpIgnoreDuplicates = false;
protected boolean closeEngine = false;
protected Executor executor = null;
/** nodeSize maximal size of node, larger node causes overflow and creation of new BTree node. Use large number for small keys, use small number for large keys.*/
public BTreeMapMaker nodeSize(int nodeSize){
if(nodeSize>=BTreeMap.NodeSerializer.SIZE_MASK)
throw new IllegalArgumentException("Too large max node size");
this.nodeSize = nodeSize;
return this;
}
/** by default values are stored inside BTree Nodes. Large values should be stored outside of BTreeNodes*/
public BTreeMapMaker valuesOutsideNodesEnable(){
this.valuesOutsideNodes = true;
return this;
}
/** by default collection does not have counter, without counter updates are faster, but entire collection needs to be traversed to count items.*/
public BTreeMapMaker counterEnable(){
this.counter = true;
return this;
}
/** keySerializer used to convert keys into/from binary form. */
public BTreeMapMaker keySerializer(BTreeKeySerializer,?> keySerializer){
this._keySerializer = keySerializer;
return this;
}
/**
* keySerializer used to convert keys into/from binary form.
*/
public BTreeMapMaker keySerializer(Serializer> serializer){
this._keySerializer2 = serializer;
return this;
}
/**
* keySerializer used to convert keys into/from binary form.
*/
public BTreeMapMaker keySerializer(Serializer> serializer, Comparator> comparator){
this._keySerializer2 = serializer;
this._comparator = comparator;
return this;
}
/**
* @deprecated compatibility with 1.0
*/
public BTreeMapMaker keySerializerWrap(Serializer> serializer){
return keySerializer(serializer);
}
/** valueSerializer used to convert values into/from binary form. */
public BTreeMapMaker valueSerializer(Serializer> valueSerializer){
this.valueSerializer = valueSerializer;
return this;
}
/** comparator used to sort keys. */
public BTreeMapMaker comparator(Comparator> comparator){
this._comparator = comparator;
return this;
}
public BTreeMapMaker pumpSource(Iterator keysSource, Fun.Function1 valueExtractor){
this.pumpSource = keysSource;
this.pumpKeyExtractor = Fun.extractNoTransform();
this.pumpValueExtractor = valueExtractor;
return this;
}
public BTreeMapMaker pumpSource(Iterator> entriesSource){
this.pumpSource = entriesSource;
this.pumpKeyExtractor = Fun.extractKey();
this.pumpValueExtractor = Fun.extractValue();
return this;
}
public BTreeMapMaker pumpSource(NavigableMap m) {
this.pumpSource = m.descendingMap().entrySet().iterator();
this.pumpKeyExtractor = Fun.extractMapEntryKey();
this.pumpValueExtractor = Fun.extractMapEntryValue();
return this;
}
public BTreeMapMaker pumpPresort(int batchSize){
this.pumpPresortBatchSize = batchSize;
return this;
}
/**
* If source iterator contains an duplicate key, exception is thrown.
* This options will only use firts key and ignore any consequentive duplicates.
*/
public BTreeMapMaker pumpIgnoreDuplicates(){
this.pumpIgnoreDuplicates = true;
return this;
}
public BTreeMap make(){
if(db==null)
throw new IllegalAccessError("This maker is not attached to any DB, it only hold configuration");
return db.treeMapCreate(BTreeMapMaker.this);
}
public BTreeMap makeOrGet(){
if(db==null)
throw new IllegalAccessError("This maker is not attached to any DB, it only hold configuration");
synchronized(db){
//TODO add parameter check
return (BTreeMap) (db.catGet(name + Keys.type)==null?
make() :
db.treeMap(name, getKeySerializer(), valueSerializer));
}
}
protected BTreeKeySerializer getKeySerializer() {
if(_keySerializer==null) {
if (_keySerializer2 == null && _comparator!=null)
_keySerializer2 = db.getDefaultSerializer();
if(_keySerializer2!=null)
_keySerializer = _keySerializer2.getBTreeKeySerializer(_comparator);
}
return _keySerializer;
}
/**
* creates map optimized for using {@code String} keys
* @deprecated MapDB 1.0 compat, will be removed in 2.1
*/
public BTreeMap makeStringMap() {
keySerializer(Serializer.STRING);
return make();
}
/**
* creates map optimized for using zero or positive {@code Long} keys
* @deprecated MapDB 1.0 compat, will be removed in 2.1
*/
public BTreeMap makeLongMap() {
keySerializer(Serializer.LONG);
return make();
}
protected BTreeMapMaker closeEngine() {
closeEngine = true;
return this;
}
}
public class BTreeSetMaker{
protected final String name;
public BTreeSetMaker(String name) {
this.name = name;
}
protected int nodeSize = 32;
protected boolean counter = false;
private BTreeKeySerializer _serializer;
private Serializer _serializer2;
private Comparator _comparator;
protected Iterator> pumpSource;
protected int pumpPresortBatchSize = -1;
protected boolean pumpIgnoreDuplicates = false;
protected boolean standalone = false;
protected Executor executor = DB.this.executor;
/** nodeSize maximal size of node, larger node causes overflow and creation of new BTree node. Use large number for small keys, use small number for large keys.*/
public BTreeSetMaker nodeSize(int nodeSize){
this.nodeSize = nodeSize;
return this;
}
/** by default collection does not have counter, without counter updates are faster, but entire collection needs to be traversed to count items.*/
public BTreeSetMaker counterEnable(){
this.counter = true;
return this;
}
/** serializer used to convert keys into/from binary form. */
public BTreeSetMaker serializer(BTreeKeySerializer serializer){
this._serializer = serializer;
return this;
}
/** serializer used to convert keys into/from binary form. */
public BTreeSetMaker serializer(Serializer serializer){
this._serializer2 = serializer;
return this;
}
/** serializer used to convert keys into/from binary form. */
public BTreeSetMaker serializer(Serializer serializer, Comparator comparator){
this._serializer2 = serializer;
this._comparator = comparator;
return this;
}
/** comparator used to sort keys. */
public BTreeSetMaker comparator(Comparator> comparator){
this._comparator = comparator;
return this;
}
protected BTreeKeySerializer getSerializer() {
if(_serializer==null) {
if (_serializer2 == null && _comparator!=null)
_serializer2 = getDefaultSerializer();
if(_serializer2!=null)
_serializer = _serializer2.getBTreeKeySerializer(_comparator);
}
return _serializer;
}
public BTreeSetMaker pumpSource(Iterator> source){
this.pumpSource = source;
return this;
}
public BTreeSetMaker pumpSource(NavigableSet m) {
this.pumpSource = m.descendingIterator();
return this;
}
/**
* If source iteretor contains an duplicate key, exception is thrown.
* This options will only use firts key and ignore any consequentive duplicates.
*/
public BTreeSetMaker pumpIgnoreDuplicates(){
this.pumpIgnoreDuplicates = true;
return this;
}
public BTreeSetMaker pumpPresort(int batchSize){
this.pumpPresortBatchSize = batchSize;
return this;
}
protected BTreeSetMaker standalone() {
this.standalone = true;
return this;
}
public NavigableSet make(){
return DB.this.treeSetCreate(BTreeSetMaker.this);
}
public NavigableSet makeOrGet(){
synchronized (DB.this){
//TODO add parameter check
return (NavigableSet) (catGet(name+Keys.type)==null?
make():
treeSet(name,getSerializer()));
}
}
/** creates set optimized for using {@code String}
* @deprecated MapDB 1.0 compat, will be removed in 2.1
*/
public NavigableSet makeStringSet() {
serializer(BTreeKeySerializer.STRING);
return make();
}
/** creates set optimized for using zero or positive {@code Long}
* @deprecated MapDB 1.0 compat, will be removed in 2.1
*/
public NavigableSet makeLongSet() {
serializer(BTreeKeySerializer.LONG);
return make();
}
}
/**
* @deprecated method renamed, use {@link DB#treeMap(String)}
*/
synchronized public BTreeMap getTreeMap(String name){
return treeMap(name);
}
/**
* Opens existing or creates new B-linked-tree Map.
* This collection performs well under concurrent access.
* Only trade-off are deletes, which causes tree fragmentation.
* It is ordered and best suited for small keys and values.
*
* @param name of map
* @return map
*/
synchronized public BTreeMap treeMap(String name) {
return treeMap(name,(BTreeKeySerializer)null,null);
}
synchronized public BTreeMap treeMap(String name, Serializer keySerializer, Serializer valueSerializer) {
if(keySerializer==null)
keySerializer = getDefaultSerializer();
return treeMap(name,keySerializer.getBTreeKeySerializer(null),valueSerializer);
}
synchronized public BTreeMap treeMap(String name, BTreeKeySerializer keySerializer, Serializer valueSerializer){
checkNotClosed();
BTreeMap ret = (BTreeMap) getFromWeakCollection(name);
if(ret!=null) return ret;
String type = catGet(name + Keys.type, null);
//$DELAY$
if(type==null){
checkShouldCreate(name);
if(engine.isReadOnly()){
Engine e = new StoreHeap(true,1,0,false);
new DB(e).treeMap("a");
//$DELAY$
return namedPut(name,
new DB(new Engine.ReadOnlyWrapper(e)).treeMap("a"));
}
BTreeMapMaker m = treeMapCreate(name);
if(keySerializer!=null)
m = m.keySerializer(keySerializer);
if(valueSerializer!=null)
m = m.valueSerializer(valueSerializer);
return m.make();
}
checkType(type, "TreeMap");
Object keySer2 = checkPlaceholder(name+Keys.keySerializer, keySerializer);
Object valSer2 = checkPlaceholder(name+Keys.valueSerializer, valueSerializer);
ret = new BTreeMap(engine,
false,
(Long) catGet(name + Keys.rootRecidRef),
catGet(name+Keys.maxNodeSize,32),
catGet(name+Keys.valuesOutsideNodes,false),
catGet(name+Keys.counterRecids,0L),
(BTreeKeySerializer)keySer2,
(Serializer)valSer2,
catGet(name+Keys.numberOfNodeMetas,0)
);
//$DELAY$
namedPut(name, ret);
return ret;
}
/**
* @deprecated method renamed, use {@link DB#treeMapCreate(String)}
*/
public BTreeMapMaker createTreeMap(String name){
return treeMapCreate(name);
}
/**
* Returns new builder for TreeMap with given name
*
* @param name of map to create
* @throws IllegalArgumentException if name is already used
* @return maker, call {@code .make()} to create map
*/
public BTreeMapMaker treeMapCreate(String name){
return new BTreeMapMaker(name,DB.this);
}
synchronized protected BTreeMap treeMapCreate(final BTreeMapMaker m){
String name = m.name;
checkNameNotExists(name);
//$DELAY$
BTreeKeySerializer keySerializer = fillNulls(m.getKeySerializer());
catPut(name+Keys.keySerializer,serializableOrPlaceHolder(keySerializer));
if(m.valueSerializer==null)
m.valueSerializer = getDefaultSerializer();
catPut(name+Keys.valueSerializer,serializableOrPlaceHolder(m.valueSerializer));
if(m.pumpPresortBatchSize!=-1 && m.pumpSource!=null){
final Comparator comp = keySerializer.comparator();
final Fun.Function1 extr = m.pumpKeyExtractor;
Comparator presortComp = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return - comp.compare(extr.run(o1), extr.run(o2));
}
};
m.pumpSource = Pump.sort(
m.pumpSource,
m.pumpIgnoreDuplicates,
m.pumpPresortBatchSize,
presortComp,
getDefaultSerializer(),
m.executor);
}
//$DELAY$
long counterRecid = !m.counter ?0L:engine.put(0L, Serializer.LONG);
long rootRecidRef;
if(m.pumpSource==null || !m.pumpSource.hasNext()){
rootRecidRef = BTreeMap.createRootRef(engine,keySerializer,m.valueSerializer,m.valuesOutsideNodes,0);
}else{
rootRecidRef = Pump.buildTreeMap(
(Iterator)m.pumpSource,
engine,
(Fun.Function1)m.pumpKeyExtractor,
(Fun.Function1)m.pumpValueExtractor,
m.pumpIgnoreDuplicates,m.nodeSize,
m.valuesOutsideNodes,
counterRecid,
keySerializer,
(Serializer)m.valueSerializer,
m.executor
);
}
//$DELAY$
BTreeMap ret = new BTreeMap(
engine,
m.closeEngine,
catPut(name+Keys.rootRecidRef, rootRecidRef),
catPut(name+Keys.maxNodeSize,m.nodeSize),
catPut(name+Keys.valuesOutsideNodes,m.valuesOutsideNodes),
catPut(name+Keys.counterRecids,counterRecid),
keySerializer,
(Serializer)m.valueSerializer,
catPut(m.name+Keys.numberOfNodeMetas,0)
);
//$DELAY$
catalog.put(name + Keys.type, "TreeMap");
namedPut(name, ret);
return ret;
}
/**
* Replace nulls in tuple serializers with default (Comparable) values
*
* @param keySerializer with nulls
* @return keySerializers which does not contain any nulls
*/
protected BTreeKeySerializer,?> fillNulls(BTreeKeySerializer,?> keySerializer) {
if(keySerializer==null)
return new BTreeKeySerializer.BasicKeySerializer(getDefaultSerializer(),Fun.COMPARATOR);
if(keySerializer instanceof BTreeKeySerializer.ArrayKeySerializer) {
BTreeKeySerializer.ArrayKeySerializer k = (BTreeKeySerializer.ArrayKeySerializer) keySerializer;
Serializer>[] serializers = new Serializer[k.tsize];
Comparator>[] comparators = new Comparator[k.tsize];
//$DELAY$
for (int i = 0; i < k.tsize; i++) {
serializers[i] = k.serializers[i] != null && k.serializers[i]!=Serializer.BASIC ? k.serializers[i] : getDefaultSerializer();
comparators[i] = k.comparators[i] != null ? k.comparators[i] : Fun.COMPARATOR;
}
//$DELAY$
return new BTreeKeySerializer.ArrayKeySerializer(comparators, serializers);
}
//$DELAY$
return keySerializer;
}
/**
* Get Name Catalog.
* It is metatable which contains information about named collections and records.
* Each collection constructor takes number of parameters, this map contains those parameters.
*
* _Note:_ Do not modify this map, unless you know what you are doing!
*
* @return Name Catalog
*/
public SortedMap getCatalog(){
return catalog;
}
/**
* @deprecated method renamed, use {@link DB#treeSet(String)}
*/
synchronized public NavigableSet getTreeSet(String name){
return treeSet(name);
}
/**
* Opens existing or creates new B-linked-tree Set.
*
* @param name of set
* @return set
*/
synchronized public NavigableSet treeSet(String name) {
return treeSet(name, (BTreeKeySerializer)null);
}
synchronized public NavigableSet treeSet(String name, Serializer serializer) {
if(serializer == null)
serializer = getDefaultSerializer();
return treeSet(name,serializer.getBTreeKeySerializer(null));
}
synchronized public NavigableSet treeSet(String name,BTreeKeySerializer serializer){
checkNotClosed();
NavigableSet ret = (NavigableSet) getFromWeakCollection(name);
if(ret!=null) return ret;
String type = catGet(name + Keys.type, null);
if(type==null){
checkShouldCreate(name);
if(engine.isReadOnly()){
Engine e = new StoreHeap(true,1,0,false);
new DB(e).treeSet("a");
return namedPut(name,
new DB(new Engine.ReadOnlyWrapper(e)).treeSet("a"));
}
//$DELAY$
BTreeSetMaker m = treeSetCreate(name);
if(serializer!=null)
m = m.serializer(serializer);
return m.make();
}
checkType(type, "TreeSet");
Object keySer2 = checkPlaceholder(name+Keys.serializer, serializer);
//$DELAY$
ret = new BTreeMap(
engine,
false,
(Long) catGet(name+Keys.rootRecidRef),
catGet(name+Keys.maxNodeSize,32),
false,
catGet(name+Keys.counterRecids,0L),
(BTreeKeySerializer)keySer2,
null,
catGet(name+Keys.numberOfNodeMetas,0)
).keySet();
//$DELAY$
namedPut(name, ret);
return ret;
}
/**
* @deprecated method renamed, use {@link DB#treeSetCreate(String)}
*/
synchronized public BTreeSetMaker createTreeSet(String name){
return treeSetCreate(name);
}
/**
* Creates new TreeSet.
* @param name of set to create
* @throws IllegalArgumentException if name is already used
* @return maker used to construct set
*/
synchronized public BTreeSetMaker treeSetCreate(String name){
return new BTreeSetMaker(name);
}
synchronized public NavigableSet treeSetCreate(BTreeSetMaker m){
checkNameNotExists(m.name);
//$DELAY$
BTreeKeySerializer serializer = fillNulls(m.getSerializer());
catPut(m.name+Keys.serializer,serializableOrPlaceHolder(serializer));
if(m.pumpPresortBatchSize!=-1){
m.pumpSource = Pump.sort(
m.pumpSource,
m.pumpIgnoreDuplicates,
m.pumpPresortBatchSize,
Collections.reverseOrder(serializer.comparator()),
getDefaultSerializer(),
m.executor);
}
long counterRecid = !m.counter ?0L:engine.put(0L, Serializer.LONG);
long rootRecidRef;
//$DELAY$
if(m.pumpSource==null || !m.pumpSource.hasNext()){
rootRecidRef = BTreeMap.createRootRef(engine,serializer,null,false, 0);
}else{
rootRecidRef = Pump.buildTreeMap(
(Iterator)m.pumpSource,
engine,
Fun.extractNoTransform(),
null,
m.pumpIgnoreDuplicates,
m.nodeSize,
false,
counterRecid,
serializer,
null,
m.executor);
}
//$DELAY$
NavigableSet ret = new BTreeMap(
engine,
m.standalone,
catPut(m.name+Keys.rootRecidRef, rootRecidRef),
catPut(m.name+Keys.maxNodeSize,m.nodeSize),
false,
catPut(m.name+Keys.counterRecids,counterRecid),
serializer,
null,
catPut(m.name+Keys.numberOfNodeMetas,0)
).keySet();
//$DELAY$
catalog.put(m.name + Keys.type, "TreeSet");
namedPut(m.name, ret);
return ret;
}
/**
*
* Why are queues methods deprecated?
*
* I am not really happy with current implementation.
* But I also have no time to rewrite them in 2.0.
* So current version is going to stay in 2.0 with deprecated flag.
*
* I am not sure what will happen in 2.1. Most likely I will redesign
* and extend Queues to include blocking, counted, full-dequeue implementation,
* multiple/single consumers/producers... etc. API will switch
* to some sort of Maker style pattern.
*
* In case of new queue framework, I might keep old implementation, but move it to separate JAR.
* So storage format will not change, but it will use different API to instantiate Queues.
*
* But there is also small chance I will remove deprecation flag and keep current implementation.
* I am not going to leave MapDB without at least some sort of Queue support.
*
* So feel free to use current implementation, it will be supported for 2.0 lifecycle (probably 6+ months).
* But once 2.1 release with long-term support is out, there might be some migration needed.
*
* Hope this makes sense
*
* @deprecated queues API is going to be reworked */
synchronized public BlockingQueue getQueue(String name) {
checkNotClosed();
Queues.Queue ret = (Queues.Queue) getFromWeakCollection(name);
if(ret!=null) return ret;
String type = catGet(name + Keys.type, null);
//$DELAY$
if(type==null){
checkShouldCreate(name);
if(engine.isReadOnly()){
Engine e = new StoreHeap(true,1,0,false);
new DB(e).getQueue("a");
return namedPut(name,
new DB(new Engine.ReadOnlyWrapper(e)).getQueue("a"));
}
//$DELAY$
return createQueue(name,null,true);
}
checkType(type, "Queue");
//$DELAY$
ret = new Queues.Queue(engine,
(Serializer) catGet(name+Keys.serializer,getDefaultSerializer()),
(Long) catGet(name+Keys.headRecid),
(Long)catGet(name+Keys.tailRecid),
(Boolean)catGet(name+Keys.useLocks)
);
//$DELAY$
namedPut(name, ret);
return ret;
}
/**
*
* Why are queues methods deprecated?
*
* I am not really happy with current implementation.
* But I also have no time to rewrite them in 2.0.
* So current version is going to stay in 2.0 with deprecated flag.
*
* I am not sure what will happen in 2.1. Most likely I will redesign
* and extend Queues to include blocking, counted, full-dequeue implementation,
* multiple/single consumers/producers... etc. API will switch
* to some sort of Maker style pattern.
*
* In case of new queue framework, I might keep old implementation, but move it to separate JAR.
* So storage format will not change, but it will use different API to instantiate Queues.
*
* But there is also small chance I will remove deprecation flag and keep current implementation.
* I am not going to leave MapDB without at least some sort of Queue support.
*
* So feel free to use current implementation, it will be supported for 2.0 lifecycle (probably 6+ months).
* But once 2.1 release with long-term support is out, there might be some migration needed.
*
* Hope this makes sense
*
* @deprecated queues API is going to be reworked */
synchronized public BlockingQueue createQueue(String name, Serializer serializer, boolean useLocks) {
checkNameNotExists(name);
if(serializer==null)
serializer= getDefaultSerializer();
long node = engine.put(null,new Queues.Queue.NodeSerializer(serializer));
long headRecid = engine.put(node, Serializer.LONG);
long tailRecid = engine.put(node, Serializer.LONG);
//$DELAY$
Queues.Queue ret = new Queues.Queue(engine,
catPut(name+Keys.serializer,serializer),
catPut(name +Keys.headRecid,headRecid),
catPut(name+Keys.tailRecid,tailRecid),
catPut(name+Keys.useLocks,useLocks)
);
catalog.put(name + Keys.type, "Queue");
//$DELAY$
namedPut(name, ret);
return ret;
}
/**
*
* Why are queues methods deprecated?
*
* I am not really happy with current implementation.
* But I also have no time to rewrite them in 2.0.
* So current version is going to stay in 2.0 with deprecated flag.
*
* I am not sure what will happen in 2.1. Most likely I will redesign
* and extend Queues to include blocking, counted, full-dequeue implementation,
* multiple/single consumers/producers... etc. API will switch
* to some sort of Maker style pattern.
*
* In case of new queue framework, I might keep old implementation, but move it to separate JAR.
* So storage format will not change, but it will use different API to instantiate Queues.
*
* But there is also small chance I will remove deprecation flag and keep current implementation.
* I am not going to leave MapDB without at least some sort of Queue support.
*
* So feel free to use current implementation, it will be supported for 2.0 lifecycle (probably 6+ months).
* But once 2.1 release with long-term support is out, there might be some migration needed.
*
* Hope this makes sense
*
* @deprecated queues API is going to be reworked */
synchronized public BlockingQueue getStack(String name) {
checkNotClosed();
Queues.Stack ret = (Queues.Stack) getFromWeakCollection(name);
if(ret!=null) return ret;
//$DELAY$
String type = catGet(name + Keys.type, null);
if(type==null){
checkShouldCreate(name);
if(engine.isReadOnly()){
Engine e = new StoreHeap(true,1,0,false);
//$DELAY$
new DB(e).getStack("a");
return namedPut(name,
new DB(new Engine.ReadOnlyWrapper(e)).getStack("a"));
}
return createStack(name,null,true);
}
//$DELAY$
checkType(type, "Stack");
ret = new Queues.Stack(engine,
(Serializer) catGet(name+Keys.serializer,getDefaultSerializer()),
(Long)catGet(name+Keys.headRecid)
);
//$DELAY$
namedPut(name, ret);
//$DELAY$
return ret;
}
/**
*
* Why are queues methods deprecated?
*
* I am not really happy with current implementation.
* But I also have no time to rewrite them in 2.0.
* So current version is going to stay in 2.0 with deprecated flag.
*
* I am not sure what will happen in 2.1. Most likely I will redesign
* and extend Queues to include blocking, counted, full-dequeue implementation,
* multiple/single consumers/producers... etc. API will switch
* to some sort of Maker style pattern.
*
* In case of new queue framework, I might keep old implementation, but move it to separate JAR.
* So storage format will not change, but it will use different API to instantiate Queues.
*
* But there is also small chance I will remove deprecation flag and keep current implementation.
* I am not going to leave MapDB without at least some sort of Queue support.
*
* So feel free to use current implementation, it will be supported for 2.0 lifecycle (probably 6+ months).
* But once 2.1 release with long-term support is out, there might be some migration needed.
*
* Hope this makes sense
*
* @deprecated queues API is going to be reworked */
synchronized public BlockingQueue createStack(String name, Serializer serializer, boolean useLocks) {
checkNameNotExists(name);
if(serializer==null)
serializer = getDefaultSerializer();
long node = engine.put(null, new Queues.SimpleQueue.NodeSerializer(serializer));
long headRecid = engine.put(node, Serializer.LONG);
//$DELAY$
Queues.Stack ret = new Queues.Stack(engine,
catPut(name+Keys.serializer,serializer),
catPut(name+Keys.headRecid,headRecid)
);
//$DELAY$
catalog.put(name + Keys.type, "Stack");
namedPut(name, ret);
return ret;
}
/**
*
* Why are queues methods deprecated?
*
* I am not really happy with current implementation.
* But I also have no time to rewrite them in 2.0.
* So current version is going to stay in 2.0 with deprecated flag.
*
* I am not sure what will happen in 2.1. Most likely I will redesign
* and extend Queues to include blocking, counted, full-dequeue implementation,
* multiple/single consumers/producers... etc. API will switch
* to some sort of Maker style pattern.
*
* In case of new queue framework, I might keep old implementation, but move it to separate JAR.
* So storage format will not change, but it will use different API to instantiate Queues.
*
* But there is also small chance I will remove deprecation flag and keep current implementation.
* I am not going to leave MapDB without at least some sort of Queue support.
*
* So feel free to use current implementation, it will be supported for 2.0 lifecycle (probably 6+ months).
* But once 2.1 release with long-term support is out, there might be some migration needed.
*
* Hope this makes sense
*
* @deprecated queues API is going to be reworked */
synchronized public BlockingQueue getCircularQueue(String name) {
checkNotClosed();
BlockingQueue ret = (BlockingQueue) getFromWeakCollection(name);
if(ret!=null) return ret;
String type = catGet(name + Keys.type, null);
//$DELAY$
if(type==null){
checkShouldCreate(name);
if(engine.isReadOnly()) {
Engine e = new StoreHeap(true,1,0,false);
new DB(e).getCircularQueue("a");
//$DELAY$
return namedPut(name,
new DB(new Engine.ReadOnlyWrapper(e)).getCircularQueue("a"));
}
return createCircularQueue(name,null, 1024);
}
checkType(type, "CircularQueue");
ret = new Queues.CircularQueue(engine,
(Serializer) catGet(name+Keys.serializer,getDefaultSerializer()),
(Long)catGet(name+Keys.headRecid),
(Long)catGet(name+Keys.headInsertRecid),
(Long)catGet(name+Keys.size)
);
//$DELAY$
namedPut(name, ret);
return ret;
}
/**
*
* Why are queues methods deprecated?
*
* I am not really happy with current implementation.
* But I also have no time to rewrite them in 2.0.
* So current version is going to stay in 2.0 with deprecated flag.
*
* I am not sure what will happen in 2.1. Most likely I will redesign
* and extend Queues to include blocking, counted, full-dequeue implementation,
* multiple/single consumers/producers... etc. API will switch
* to some sort of Maker style pattern.
*
* In case of new queue framework, I might keep old implementation, but move it to separate JAR.
* So storage format will not change, but it will use different API to instantiate Queues.
*
* But there is also small chance I will remove deprecation flag and keep current implementation.
* I am not going to leave MapDB without at least some sort of Queue support.
*
* So feel free to use current implementation, it will be supported for 2.0 lifecycle (probably 6+ months).
* But once 2.1 release with long-term support is out, there might be some migration needed.
*
* Hope this makes sense
*
* @deprecated queues API is going to be reworked */
synchronized public BlockingQueue createCircularQueue(String name, Serializer serializer, long size) {
checkNameNotExists(name);
if(serializer==null) serializer = getDefaultSerializer();
// long headerRecid = engine.put(0L, Serializer.LONG);
//insert N Nodes empty nodes into a circle
long prevRecid = 0;
long firstRecid = 0;
//$DELAY$
Serializer> nodeSer = new Queues.SimpleQueue.NodeSerializer(serializer);
/*
* 'size+1' because one spot is always kept empty
* otherwise, "empty" and "full" conditions will match:
* http://en.wikipedia.org/wiki/Circular_buffer#Difficulties
*/
for(long i=0;i n = new Queues.SimpleQueue.Node(prevRecid, null);
prevRecid = engine.put(n, nodeSer);
if(firstRecid==0) firstRecid = prevRecid;
}
//update first node to point to last recid
engine.update(firstRecid, new Queues.SimpleQueue.Node(prevRecid, null), nodeSer );
long headRecid = engine.put(prevRecid, Serializer.LONG);
long headInsertRecid = engine.put(prevRecid, Serializer.LONG);
//$DELAY$
Queues.CircularQueue ret = new Queues.CircularQueue(engine,
catPut(name+Keys.serializer,serializer),
catPut(name+Keys.headRecid,headRecid),
catPut(name+Keys.headInsertRecid,headInsertRecid),
catPut(name+Keys.size,size)
);
//$DELAY$
catalog.put(name + Keys.type, "CircularQueue");
namedPut(name, ret);
return ret;
}
/**
* @deprecated method renamed, use {@link DB#atomicLongCreate(String, long)}
*/
synchronized public Atomic.Long createAtomicLong(String name, long initValue){
return atomicLongCreate(name, initValue);
}
synchronized public Atomic.Long atomicLongCreate(String name, long initValue){
checkNameNotExists(name);
long recid = engine.put(initValue,Serializer.LONG);
Atomic.Long ret = new Atomic.Long(engine,
catPut(name+Keys.recid,recid)
);
//$DELAY$
catalog.put(name + Keys.type, "AtomicLong");
namedPut(name, ret);
return ret;
}
/**
* @deprecated method renamed, use {@link DB#atomicLong(String)}
*/
synchronized public Atomic.Long getAtomicLong(String name){
return atomicLong(name);
}
synchronized public Atomic.Long atomicLong(String name){
checkNotClosed();
Atomic.Long ret = (Atomic.Long) getFromWeakCollection(name);
if(ret!=null) return ret;
//$DELAY$
String type = catGet(name + Keys.type, null);
if(type==null){
checkShouldCreate(name);
if (engine.isReadOnly()){
Engine e = new StoreHeap(true,1,0,false);
new DB(e).atomicLong("a");
//$DELAY$
return namedPut(name,
new DB(new Engine.ReadOnlyWrapper(e)).atomicLong("a"));
}
return atomicLongCreate(name, 0L);
}
checkType(type, "AtomicLong");
//$DELAY$
ret = new Atomic.Long(engine, (Long) catGet(name+Keys.recid));
namedPut(name, ret);
return ret;
}
/**
* @deprecated method renamed, use {@link DB#atomicIntegerCreate(String, int)}
*/
synchronized public Atomic.Integer createAtomicInteger(String name, int initValue){
return atomicIntegerCreate(name,initValue);
}
synchronized public Atomic.Integer atomicIntegerCreate(String name, int initValue){
checkNameNotExists(name);
long recid = engine.put(initValue,Serializer.INTEGER);
Atomic.Integer ret = new Atomic.Integer(engine,
catPut(name+Keys.recid,recid)
);
//$DELAY$
catalog.put(name + Keys.type, "AtomicInteger");
namedPut(name, ret);
return ret;
}
/**
* @deprecated method renamed, use {@link DB#atomicInteger(String)}
*/
synchronized public Atomic.Integer getAtomicInteger(String name){
return atomicInteger(name);
}
synchronized public Atomic.Integer atomicInteger(String name){
checkNotClosed();
Atomic.Integer ret = (Atomic.Integer) getFromWeakCollection(name);
if(ret!=null) return ret;
//$DELAY$
String type = catGet(name + Keys.type, null);
if(type==null){
checkShouldCreate(name);
if(engine.isReadOnly()){
Engine e = new StoreHeap(true,1,0,false);
new DB(e).atomicInteger("a");
//$DELAY$
return namedPut(name,
new DB(new Engine.ReadOnlyWrapper(e)).atomicInteger("a"));
}
return atomicIntegerCreate(name, 0);
}
checkType(type, "AtomicInteger");
ret = new Atomic.Integer(engine, (Long) catGet(name+Keys.recid));
namedPut(name, ret);
return ret;
}
/**
* @deprecated method renamed, use {@link DB#atomicBooleanCreate(String, boolean)}
*/
synchronized public Atomic.Boolean createAtomicBoolean(String name, boolean initValue){
return atomicBooleanCreate(name, initValue);
}
synchronized public Atomic.Boolean atomicBooleanCreate(String name, boolean initValue){
checkNameNotExists(name);
long recid = engine.put(initValue,Serializer.BOOLEAN);
//$DELAY$
Atomic.Boolean ret = new Atomic.Boolean(engine,
catPut(name+Keys.recid,recid)
);
catalog.put(name + Keys.type, "AtomicBoolean");
//$DELAY$
namedPut(name, ret);
return ret;
}
/**
* @deprecated method renamed, use {@link DB#atomicBoolean(String)}
*/
synchronized public Atomic.Boolean getAtomicBoolean(String name){
return atomicBoolean(name);
}
synchronized public Atomic.Boolean atomicBoolean(String name){
checkNotClosed();
Atomic.Boolean ret = (Atomic.Boolean) getFromWeakCollection(name);
if(ret!=null) return ret;
//$DELAY$
String type = catGet(name + Keys.type, null);
if(type==null){
checkShouldCreate(name);
if(engine.isReadOnly()){
Engine e = new StoreHeap(true,1,0,false);
new DB(e).atomicBoolean("a");
return namedPut(name,
new DB(new Engine.ReadOnlyWrapper(e)).atomicBoolean("a"));
}
//$DELAY$
return atomicBooleanCreate(name, false);
}
checkType(type, "AtomicBoolean");
//$DELAY$
ret = new Atomic.Boolean(engine, (Long) catGet(name+Keys.recid));
namedPut(name, ret);
return ret;
}
public void checkShouldCreate(String name) {
if(strictDBGet) throw new NoSuchElementException("No record with this name was found: "+name);
}
/**
* @deprecated method renamed, use {@link DB#atomicStringCreate(String, String)}
*/
synchronized public Atomic.String createAtomicString(String name, String initValue){
return atomicStringCreate(name,initValue);
}
synchronized public Atomic.String atomicStringCreate(String name, String initValue){
checkNameNotExists(name);
if(initValue==null) throw new IllegalArgumentException("initValue may not be null");
long recid = engine.put(initValue, Serializer.STRING_NOSIZE);
//$DELAY$
Atomic.String ret = new Atomic.String(engine,
catPut(name+Keys.recid,recid)
);
//$DELAY$
catalog.put(name + Keys.type, "AtomicString");
namedPut(name, ret);
return ret;
}
/**
* @deprecated method renamed, use {@link DB#atomicString(String)}
*/
synchronized public Atomic.String getAtomicString(String name) {
return atomicString(name);
}
synchronized public Atomic.String atomicString(String name){
checkNotClosed();
Atomic.String ret = (Atomic.String) getFromWeakCollection(name);
if(ret!=null) return ret;
String type = catGet(name + Keys.type, null);
//$DELAY$
if(type==null){
checkShouldCreate(name);
if(engine.isReadOnly()){
Engine e = new StoreHeap(true,1,0,false);
new DB(e).atomicString("a");
//$DELAY$
return namedPut(name,
new DB(new Engine.ReadOnlyWrapper(e)).atomicString("a"));
}
return atomicStringCreate(name, "");
}
checkType(type, "AtomicString");
ret = new Atomic.String(engine, (Long) catGet(name+Keys.recid));
namedPut(name, ret);
return ret;
}
/**
* @deprecated method renamed, use {@link DB#atomicVarCreate(String, Object, Serializer)}
*/
synchronized public Atomic.Var createAtomicVar(String name, E initValue, Serializer serializer){
return atomicVarCreate(name,initValue,serializer);
}
synchronized public Atomic.Var atomicVarCreate(String name, E initValue, Serializer serializer){
if(catGet(name+Keys.type)!=null){
return atomicVar(name,serializer);
}
if(serializer==null)
serializer=getDefaultSerializer();
catPut(name+Keys.serializer,serializableOrPlaceHolder(serializer));
long recid = engine.put(initValue, serializer);
//$DELAY$
Atomic.Var ret = new Atomic.Var(engine,
catPut(name+Keys.recid,recid),
serializer
);
//$DELAY$
catalog.put(name + Keys.type, "AtomicVar");
namedPut(name, ret);
return ret;
}
/**
* @deprecated method renamed, use {@link DB#atomicVar(String)}
*/
synchronized public Atomic.Var getAtomicVar(String name){
return atomicVar(name);
}
synchronized public Atomic.Var atomicVar(String name){
return atomicVar(name,null);
}
synchronized public Atomic.Var atomicVar(String name,Serializer serializer){
checkNotClosed();
Atomic.Var ret = (Atomic.Var) getFromWeakCollection(name);
if(ret!=null) return ret;
String type = catGet(name + Keys.type, null);
if(type==null){
checkShouldCreate(name);
if(engine.isReadOnly()){
Engine e = new StoreHeap(true,1,0,false);
new DB(e).atomicVar("a");
return namedPut(name,
new DB(new Engine.ReadOnlyWrapper(e)).atomicVar("a"));
}
//$DELAY$
return atomicVarCreate(name, null, getDefaultSerializer());
}
checkType(type, "AtomicVar");
Object serializer2;
if(serializer==null)
serializer2 = catGet(name+Keys.serializer);
else
serializer2 = serializer;
if(serializer2==null)
serializer2 = getDefaultSerializer();
if(serializer2==Fun.PLACEHOLDER){
throw new DBException.UnknownSerializer("Atomic.Var '"+name+"' has no serializer defined in Name Catalog nor constructor argument.");
}
ret = new Atomic.Var(engine, (Long) catGet(name+Keys.recid), (Serializer) serializer2);
namedPut(name, ret);
return ret;
}
/** return record with given name or null if name does not exist*/
synchronized public E get(String name){
//$DELAY$
String type = catGet(name+Keys.type);
if(type==null) return null;
if("HashMap".equals(type)) return (E) hashMap(name);
if("HashSet".equals(type)) return (E) hashSet(name);
if("TreeMap".equals(type)) return (E) treeMap(name);
if("TreeSet".equals(type)) return (E) treeSet(name);
if("AtomicBoolean".equals(type)) return (E) atomicBoolean(name);
if("AtomicInteger".equals(type)) return (E) atomicInteger(name);
if("AtomicLong".equals(type)) return (E) atomicLong(name);
if("AtomicString".equals(type)) return (E) atomicString(name);
if("AtomicVar".equals(type)) return (E) atomicVar(name);
if("Queue".equals(type)) return (E) getQueue(name);
if("Stack".equals(type)) return (E) getStack(name);
if("CircularQueue".equals(type)) return (E) getCircularQueue(name);
throw new DBException.DataCorruption("Unknown type: "+name);
}
synchronized public boolean exists(String name){
return catGet(name+Keys.type)!=null;
}
/** delete record/collection with given name*/
synchronized public void delete(String name){
//$DELAY$
Object r = get(name);
if(r instanceof Atomic.Boolean){
engine.delete(((Atomic.Boolean)r).recid, Serializer.BOOLEAN);
}else if(r instanceof Atomic.Integer){
engine.delete(((Atomic.Integer)r).recid, Serializer.INTEGER);
}else if(r instanceof Atomic.Long){
engine.delete(((Atomic.Long)r).recid, Serializer.LONG);
}else if(r instanceof Atomic.String){
engine.delete(((Atomic.String)r).recid, Serializer.STRING_NOSIZE);
}else if(r instanceof Atomic.Var){
engine.delete(((Atomic.Var)r).recid, ((Atomic.Var)r).serializer);
}else if(r instanceof Queue){
//drain queue
Queue q = (Queue) r;
while(q.poll()!=null){
//do nothing
}
}else if(r instanceof HTreeMap || r instanceof HTreeMap.KeySet){
HTreeMap m = (r instanceof HTreeMap)? (HTreeMap) r : ((HTreeMap.KeySet)r).parent();
m.clear();
//$DELAY$
//delete segments
for(long segmentRecid:m.segmentRecids){
engine.delete(segmentRecid, HTreeMap.DIR_SERIALIZER);
}
}else if(r instanceof BTreeMap || r instanceof BTreeMap.KeySet){
BTreeMap m = (r instanceof BTreeMap)? (BTreeMap) r : (BTreeMap) ((BTreeMap.KeySet) r).m;
//$DELAY$
//TODO on BTreeMap recursively delete all nodes
m.clear();
if(m.counter!=null)
engine.delete(m.counter.recid,Serializer.LONG);
}
for(String n:catalog.keySet()){
if(!n.startsWith(name))
continue;
String suffix = n.substring(name.length());
if(suffix.charAt(0)=='.' && suffix.length()>1 && !suffix.substring(1).contains("."))
catalog.remove(n);
}
namesInstanciated.remove(name);
namesLookup.remove(new IdentityWrapper(r));
}
/**
* return map of all named collections/records
*/
synchronized public Map getAll(){
TreeMap ret= new TreeMap();
//$DELAY$
for(String name:catalog.keySet()){
if(!name.endsWith(Keys.type)) continue;
//$DELAY$
name = name.substring(0,name.length()-5);
ret.put(name,get(name));
}
return Collections.unmodifiableMap(ret);
}
/** rename named record into newName
*
* @param oldName current name of record/collection
* @param newName new name of record/collection
* @throws NoSuchElementException if oldName does not exist
*/
synchronized public void rename(String oldName, String newName){
if(oldName.equals(newName)) return;
//$DELAY$
Map sub = catalog.tailMap(oldName);
List toRemove = new ArrayList();
//$DELAY$
for(String param:sub.keySet()){
if(!param.startsWith(oldName)) break;
String suffix = param.substring(oldName.length());
catalog.put(newName+suffix, catalog.get(param));
toRemove.add(param);
}
if(toRemove.isEmpty()) throw new NoSuchElementException("Could not rename, name does not exist: "+oldName);
//$DELAY$
WeakReference old = namesInstanciated.remove(oldName);
if(old!=null){
Object old2 = old.get();
if(old2!=null){
namesLookup.remove(new IdentityWrapper(old2));
namedPut(newName,old2);
}
}
for(String param:toRemove) catalog.remove(param);
}
/**
* Checks that object with given name does not exist yet.
* @param name to check
* @throws IllegalArgumentException if name is already used
*/
public void checkNameNotExists(String name) {
if(catalog.get(name+Keys.type)!=null)
throw new IllegalArgumentException("Name already used: "+name);
}
/**
*
* Closes database.
* All other methods will throw 'IllegalAccessError' after this method was called.
*
* !! it is necessary to call this method before JVM exits!!
*
*/
synchronized public void close(){
if(engine == null)
return;
consistencyLock.writeLock().lock();
try {
if(metricsExecutor!=null && metricsExecutor!=executor && !metricsExecutor.isShutdown()){
metricsExecutor.shutdown();
metricsExecutor.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
metricsExecutor = null;
}
if(cacheExecutor!=null && cacheExecutor!=executor && !cacheExecutor.isShutdown()){
cacheExecutor.shutdown();
cacheExecutor.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
cacheExecutor = null;
}
if(storeExecutor!=null && storeExecutor!=executor && !storeExecutor.isShutdown()){
storeExecutor.shutdown();
storeExecutor.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
storeExecutor = null;
}
if (executor != null && !executor.isTerminated()) {
executor.shutdown();
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
executor = null;
}
for (WeakReference r : namesInstanciated.values()) {
Object rr = r.get();
if (rr != null && rr instanceof Closeable)
((Closeable) rr).close();
}
String fileName = deleteFilesAfterClose ? Store.forEngine(engine).fileName : null;
engine.close();
//dereference db to prevent memory leaks
engine = Engine.CLOSED_ENGINE;
namesInstanciated = Collections.unmodifiableMap(new HashMap());
namesLookup = Collections.unmodifiableMap(new HashMap());
if (deleteFilesAfterClose && fileName != null) {
File f = new File(fileName);
if (f.exists() && !f.delete()) {
//TODO file was not deleted, log warning
}
//TODO delete WAL files and append-only files
}
} catch (IOException e) {
throw new IOError(e);
} catch (InterruptedException e) {
throw new DBException.Interrupted(e);
}finally {
consistencyLock.writeLock().unlock();
}
}
/**
* All collections are weakly referenced to prevent two instances of the same collection in memory.
* This is mainly for locking, two instances of the same lock would not simply work.
*/
synchronized public Object getFromWeakCollection(String name){
WeakReference> r = namesInstanciated.get(name);
//$DELAY$
if(r==null) return null;
//$DELAY$
Object o = r.get();
if(o==null) namesInstanciated.remove(name);
return o;
}
public void checkNotClosed() {
if(engine == null) throw new IllegalAccessError("DB was already closed");
}
/**
* @return true if DB is closed and can no longer be used
*/
public synchronized boolean isClosed(){
return engine == null || engine.isClosed();
}
/**
* Commit changes made on collections loaded by this DB
*
* @see org.mapdb.Engine#commit()
*/
synchronized public void commit() {
checkNotClosed();
consistencyLock.writeLock().lock();
try {
//update Class Catalog with missing classes as part of this transaction
String[] toBeAdded = unknownClasses.isEmpty() ? null : unknownClasses.toArray(new String[0]);
//TODO if toBeAdded is modified as part of serialization, and `executor` is not null (background threads are enabled),
// schedule this operation with 1ms delay, so it has higher chances of becoming part of the same transaction
if (toBeAdded != null) {
long[] classInfoRecids = engine.get(Engine.RECID_CLASS_CATALOG, Serializer.RECID_ARRAY);
long[] classInfoRecidsOrig = classInfoRecids;
if(classInfoRecids==null)
classInfoRecids = new long[0];
int pos = classInfoRecids.length;
classInfoRecids = Arrays.copyOf(classInfoRecids,classInfoRecids.length+toBeAdded.length);
for (String className : toBeAdded) {
SerializerPojo.ClassInfo classInfo = serializerPojo.makeClassInfo(className);
//persist and add new recids
classInfoRecids[pos++] = engine.put(classInfo,serializerPojo.classInfoSerializer);
}
if(!engine.compareAndSwap(Engine.RECID_CLASS_CATALOG, classInfoRecidsOrig, classInfoRecids, Serializer.RECID_ARRAY)){
LOG.log(Level.WARNING, "Could not update class catalog with new classes, CAS failed");
}
}
engine.commit();
if (toBeAdded != null) {
for (String className : toBeAdded) {
unknownClasses.remove(className);
}
}
}finally {
consistencyLock.writeLock().unlock();
}
}
/**
* Rollback changes made on collections loaded by this DB
*
* @see org.mapdb.Engine#rollback()
*/
synchronized public void rollback() {
checkNotClosed();
consistencyLock.writeLock().lock();
try {
engine.rollback();
}finally {
consistencyLock.writeLock().unlock();
}
}
/**
* Perform storage maintenance.
* Typically compact underlying storage and reclaim unused space.
*
* NOTE: MapDB does not have smart defragmentation algorithms. So compaction usually recreates entire
* store from scratch. This may require additional disk space.
*/
synchronized public void compact(){
engine.compact();
}
/**
* Make readonly snapshot view of DB and all of its collection
* Collections loaded by this instance are not affected (are still mutable).
* You have to load new collections from DB returned by this method
*
* @return readonly snapshot view
*/
synchronized public DB snapshot(){
consistencyLock.writeLock().lock();
try {
Engine snapshot = TxEngine.createSnapshotFor(engine);
return new DB(snapshot);
}finally {
consistencyLock.writeLock().unlock();
}
}
/**
* @return default serializer used in this DB, it handles POJO and other stuff.
*/
public Serializer getDefaultSerializer() {
return serializerPojo;
}
/**
* @return underlying engine which takes care of persistence for this DB.
*/
public Engine getEngine() {
return engine;
}
public void checkType(String type, String expected) {
//$DELAY$
if(!expected.equals(type)) throw new IllegalArgumentException("Wrong type: "+type);
}
/**
* Returns consistency lock which groups operation together and ensures consistency.
* Operations which depends on each other are performed under read lock.
* Snapshots, close etc are performed under write-lock.
*
* @return
*/
public ReadWriteLock consistencyLock(){
return consistencyLock;
}
}
Ads help maintain this website.