org.geogit.storage.AbstractObjectDatabase.java Source code

Java tutorial

Introduction

Here is the source code for org.geogit.storage.AbstractObjectDatabase.java

Source

/* Copyright (c) 2011 TOPP - www.openplans.org. All rights reserved.
 * This code is licensed under the LGPL 2.1 license, available at the root
 * application directory.
 */
package org.geogit.storage;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.map.LRUMap;
import org.geogit.api.MutableTree;
import org.geogit.api.ObjectId;
import org.geogit.api.Ref;
import org.geogit.api.RevBlob;
import org.geogit.api.RevCommit;
import org.geogit.api.RevObject.TYPE;
import org.geogit.api.RevTree;
import org.geogit.repository.DepthSearch;

import com.google.common.base.Preconditions;
import com.ning.compress.lzf.LZFInputStream;
import com.ning.compress.lzf.LZFOutputStream;

public abstract class AbstractObjectDatabase implements ObjectDatabase {

    protected Map<ObjectId, Object> cache;

    @SuppressWarnings("unchecked")
    public AbstractObjectDatabase() {
        // TODO: use an external cache
        final long maxMemMegs = Runtime.getRuntime().maxMemory() / 1024 / 1024;
        final int maxSize;
        if (maxMemMegs <= 64) {
            maxSize = 16;
        } else if (maxMemMegs <= 100) {
            maxSize = 64;
        } else if (maxMemMegs <= 200) {
            maxSize = 128;
        } else {
            maxSize = 256;
        }
        cache = new LRUMap(maxSize);
    }

    /**
     * @see org.geogit.storage.ObjectDatabase#lookUp(java.lang.String)
     */
    @Override
    public final List<ObjectId> lookUp(final String partialId) {
        Preconditions.checkNotNull(partialId);

        byte[] raw = ObjectId.toRaw(partialId);

        return lookUpInternal(raw);
    }

    protected abstract List<ObjectId> lookUpInternal(byte[] raw);

    /**
     * @see org.geogit.storage.ObjectDatabase#get(org.geogit.api.ObjectId,
     *      org.geogit.storage.ObjectReader)
     */
    @Override
    public <T> T get(final ObjectId id, final ObjectReader<T> reader) throws IOException {
        Preconditions.checkNotNull(id, "id");
        Preconditions.checkNotNull(reader, "reader");

        T object;
        InputStream raw = getRaw(id);
        try {
            object = reader.read(id, raw);
        } finally {
            raw.close();
        }
        return object;
    }

    /**
     * @see org.geogit.storage.ObjectDatabase#getCached(org.geogit.api.ObjectId,
     *      org.geogit.storage.ObjectReader)
     */
    @Override
    @SuppressWarnings("unchecked")
    public <T> T getCached(final ObjectId id, final ObjectReader<T> reader) throws IOException {
        Preconditions.checkNotNull(id, "id");
        Preconditions.checkNotNull(reader, "reader");

        T object = (T) cache.get(id);
        if (object == null) {
            object = get(id, reader);
            if (object != null) {
                assert !(object instanceof MutableTree);
                cache.put(id, object);
            }
        }
        return object;
    }

    /**
     * @see org.geogit.storage.ObjectDatabase#getRaw(org.geogit.api.ObjectId)
     */
    @Override
    public final InputStream getRaw(final ObjectId id) throws IOException {
        InputStream in = getRawInternal(id);
        return new LZFInputStream(in);
    }

    protected abstract InputStream getRawInternal(ObjectId id) throws IOException;

    /**
     * @see org.geogit.storage.ObjectDatabase#put(org.geogit.storage.ObjectWriter)
     */
    @Override
    public final <T> ObjectId put(final ObjectWriter<T> writer) throws Exception {
        MessageDigest sha1;
        sha1 = MessageDigest.getInstance("SHA1");

        ByteArrayOutputStream rawOut = new ByteArrayOutputStream();

        DigestOutputStream keyGenOut = new DigestOutputStream(rawOut, sha1);
        // GZIPOutputStream cOut = new GZIPOutputStream(keyGenOut);
        LZFOutputStream cOut = new LZFOutputStream(keyGenOut);

        try {
            writer.write(cOut);
        } finally {
            // cOut.finish();
            cOut.flush();
            cOut.close();
            keyGenOut.flush();
            keyGenOut.close();
            rawOut.flush();
            rawOut.close();
        }

        final byte[] rawData = rawOut.toByteArray();
        final byte[] rawKey = keyGenOut.getMessageDigest().digest();
        final ObjectId id = new ObjectId(rawKey);
        putInternal(id, rawData, false);
        return id;
    }

    /**
     * @see org.geogit.storage.ObjectDatabase#put(org.geogit.api.ObjectId,
     *      org.geogit.storage.ObjectWriter)
     */
    @Override
    public final boolean put(final ObjectId id, final ObjectWriter<?> writer) throws Exception {
        ByteArrayOutputStream rawOut = new ByteArrayOutputStream();
        // GZIPOutputStream cOut = new GZIPOutputStream(rawOut);
        LZFOutputStream cOut = new LZFOutputStream(rawOut);
        try {
            // writer.write(cOut);
            writer.write(cOut);
        } finally {
            // cOut.finish();
            cOut.flush();
            cOut.close();
            rawOut.flush();
            rawOut.close();
        }
        final byte[] rawData = rawOut.toByteArray();
        return putInternal(id, rawData, true);
    }

    /**
     * @param id
     * @param rawData
     * @param override
     *            if {@code true} an a record with the given id already exists, it shall be
     *            overriden. If {@code false} and a record with the given id already exists, it
     *            shall not be overriden.
     * @return
     * @throws IOException
     */
    protected abstract boolean putInternal(ObjectId id, byte[] rawData, final boolean override) throws IOException;

    /**
     * @see org.geogit.storage.ObjectDatabase#newObjectInserter()
     */
    @Override
    public ObjectInserter newObjectInserter() {
        return new ObjectInserter(this);
    }

    @Override
    public RevBlob getBlob(ObjectId objectId) {
        try {
            return get(objectId, new BlobReader());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @see org.geogit.storage.ObjectDatabase#getCommit(org.geogit.api.ObjectId)
     */
    @Override
    public RevCommit getCommit(final ObjectId commitId) {
        RevCommit commit;
        try {
            commit = this.getCached(commitId, WrappedSerialisingFactory.getInstance().createCommitReader());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return commit;
    }

    /**
     * @see org.geogit.storage.ObjectDatabase#newTree()
     */
    @Override
    public MutableTree newTree() {
        return new RevSHA1Tree(this).mutable();
    }

    /**
     * @see org.geogit.storage.ObjectDatabase#getTree(org.geogit.api.ObjectId)
     */
    @Override
    public RevTree getTree(final ObjectId treeId) {
        if (treeId.isNull()) {
            return newTree();
        }
        RevTree tree;
        try {
            tree = this.getCached(treeId, WrappedSerialisingFactory.getInstance().createRevTreeReader(this));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return tree;
    }

    /**
     * If a child tree of {@code parent} addressed by the given {@code childPath} exists, returns
     * it's mutable copy, otherwise just returns a new mutable tree without any modification to
     * root.
     * 
     * @throws IllegalArgumentException
     *             if an reference exists for {@code childPath} but is not of type {@code TREE}
     */
    @Override
    public MutableTree getOrCreateSubTree(final RevTree parent, List<String> childPath) {
        Ref treeChildRef = getTreeChild(parent, childPath);
        if (treeChildRef == null) {
            return newTree();
        }
        if (!TYPE.TREE.equals(treeChildRef.getType())) {
            throw new IllegalArgumentException(
                    "Object exsits as child of tree " + parent.getId() + " but is not a tree: " + treeChildRef);
        }
        return getTree(treeChildRef.getObjectId()).mutable();
    }

    /**
     * @param root
     * @param tree
     * @param pathToTree
     * @return the id of the saved state of the modified root
     * @throws Exception
     */
    @Override
    public ObjectId writeBack(MutableTree root, final RevTree tree, final List<String> pathToTree)
            throws Exception {

        final ObjectId treeId = put(WrappedSerialisingFactory.getInstance().createRevTreeWriter(tree));
        final String treeName = pathToTree.get(pathToTree.size() - 1);

        if (pathToTree.size() == 1) {
            root.put(new Ref(treeName, treeId, TYPE.TREE));
            ObjectId newRootId = put(WrappedSerialisingFactory.getInstance().createRevTreeWriter(root));
            return newRootId;
        }
        final List<String> parentPath = pathToTree.subList(0, pathToTree.size() - 1);
        Ref parentRef = getTreeChild(root, parentPath);
        MutableTree parent;
        if (parentRef == null) {
            parent = newTree();
        } else {
            ObjectId parentId = parentRef.getObjectId();
            parent = getTree(parentId).mutable();
        }
        parent.put(new Ref(treeName, treeId, TYPE.TREE));
        return writeBack(root, parent, parentPath);
    }

    /**
     * @see org.geogit.storage.ObjectDatabase#getTreeChild(org.geogit.api.RevTree,
     *      java.lang.String[])
     */
    @Override
    public Ref getTreeChild(RevTree root, String... path) {
        return getTreeChild(root, Arrays.asList(path));
    }

    /**
     * @see org.geogit.storage.ObjectDatabase#getTreeChild(org.geogit.api.RevTree, java.util.List)
     */
    @Override
    public Ref getTreeChild(RevTree root, List<String> path) {
        Ref treeRef = new DepthSearch(this).find(root, path);
        return treeRef;
    }
}