com.addthis.hydra.data.query.DiskBackedMap.java Source code

Java tutorial

Introduction

Here is the source code for com.addthis.hydra.data.query.DiskBackedMap.java

Source

/*
 * 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 com.addthis.hydra.data.query;

import java.io.Closeable;
import java.io.File;
import java.io.IOException;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.addthis.codec.annotations.FieldConfig;
import com.addthis.codec.codables.BytesCodable;
import com.addthis.codec.codables.SuperCodable;
import com.addthis.hydra.store.db.DBKey;
import com.addthis.hydra.store.db.PageDB;

import com.addthis.hydra.store.skiplist.ConcurrentPage;
import org.apache.commons.io.FileUtils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DiskBackedMap<T extends DiskBackedMap.DiskObject> implements Map<String, T>, Closeable {

    private static final Logger log = LoggerFactory.getLogger(DiskBackedMap.class);
    //private final PageDB<CodableDiskObject> db;
    // By all rights this should be the above, but generics and inner
    // classes do not seem to play nice
    private final PageDB db;
    private final String diskStoragePath;
    private final File diskStoragePathFile;
    private final DiskObjectFactory factory;
    private long cacheSize;

    public DiskBackedMap(String diskStoragePath, DiskObjectFactory factory, long cacheSize) {
        this.diskStoragePath = diskStoragePath;
        this.factory = factory;
        this.cacheSize = cacheSize;

        // The smallest cache size possible with je is 96k. If the
        // user requests less cache, then use 1M
        if (this.cacheSize < 1024 * 1024) {
            this.cacheSize = 1024 * 1024L;
        }

        try {
            diskStoragePathFile = new File(diskStoragePath).getCanonicalFile();
        } catch (IOException e) {
            log.warn("Error while retrieving the canonical file for: {}", diskStoragePath);
            throw new RuntimeException(e);
        }
        if (diskStoragePathFile.exists()) {
            try {
                FileUtils.deleteDirectory(diskStoragePathFile);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        if (!diskStoragePathFile.mkdirs()) {
            log.warn("Error while creating the directory: {}", diskStoragePath);
            throw new RuntimeException("Error while creating the directory: " + diskStoragePath);
        }

        try {
            db = new PageDB.Builder<>(diskStoragePathFile, CodableDiskObject.class, 1000, 1000)
                    .dbname("DiskBackedMap.db").pageFactory(ConcurrentPage.ConcurrentPageFactory.singleton).build();
            db.setCacheMem(cacheSize);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public int size() {
        throw new UnsupportedOperationException("Not implemented");
    }

    @Override
    public boolean isEmpty() {
        throw new UnsupportedOperationException("Not implemented");
    }

    @Override
    public boolean containsKey(Object o) {
        return db.get(new DBKey(0, o.toString())) != null;
    }

    @Override
    public boolean containsValue(Object o) {
        throw new UnsupportedOperationException("Not implemented");
    }

    @Override
    public T get(Object o) {
        Object val = db.get(new DBKey(0, o.toString()));
        if (val != null) {
            return (T) ((CodableDiskObject) val).getDiskObject();
        } else {
            return null;
        }
    }

    @Override
    public T put(String s, T t) {
        Object old = db.put(new DBKey(0, s.toString()), new CodableDiskObject(t));
        if (old != null) {
            return (T) ((CodableDiskObject) old).getDiskObject();
        } else {
            return null;
        }
    }

    @Override
    public T remove(Object o) {
        return (T) ((CodableDiskObject) db.remove(new DBKey(0, o.toString()))).getDiskObject();
    }

    @Override
    public void putAll(Map<? extends String, ? extends T> map) {
        for (Entry<? extends String, ? extends T> e : map.entrySet()) {
            put(e.getKey(), e.getValue());
        }
    }

    @Override
    public void clear() {
        throw new UnsupportedOperationException("Not implemented");
    }

    @Override
    public Set<String> keySet() {
        throw new UnsupportedOperationException("Not implemented");
    }

    @Override
    public Set<Entry<String, T>> entrySet() {
        throw new UnsupportedOperationException("Not implemented");
    }

    @Override
    public Collection<T> values() {
        List<T> list = new ArrayList<>();
        for (Object obj : db.toTreeMap().values()) {
            list.add((T) ((CodableDiskObject) obj).getDiskObject());
        }
        return list;
    }

    @Override
    public void close() throws IOException {
        // Close the database
        db.close();
        FileUtils.deleteDirectory(diskStoragePathFile);
    }

    public interface DiskObject {

        public abstract byte[] toBytes();
    }

    public interface DiskObjectFactory {

        public abstract DiskObject fromBytes(byte[] bytes);
    }

    // funky non-static class to maintain compatibility with
    // DiskObject interface
    public class CodableDiskObject implements SuperCodable, BytesCodable {

        private DiskObject d;

        @FieldConfig(codable = true)
        private byte[] bytes;

        public CodableDiskObject() {
        }

        public CodableDiskObject(DiskObject d) {
            this.d = d;
        }

        public DiskObject getDiskObject() {
            return d;
        }

        @Override
        public void postDecode() {
            d = factory.fromBytes(bytes);
        }

        @Override
        public void preEncode() {
            bytes = d.toBytes();
        }

        @Override
        public byte[] bytesEncode(long version) {
            preEncode();
            return bytes;
        }

        @Override
        public void bytesDecode(byte[] b, long version) {
            bytes = b;
            postDecode();
        }
    }
}