Package org.apache.jackrabbit.mk.persistence

Source Code of org.apache.jackrabbit.mk.persistence.MongoPersistence$DBObjectBinding

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.jackrabbit.mk.persistence;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Iterator;

import org.apache.jackrabbit.mk.blobs.BlobStore;
import org.apache.jackrabbit.mk.model.ChildNodeEntriesMap;
import org.apache.jackrabbit.mk.model.Commit;
import org.apache.jackrabbit.mk.model.Id;
import org.apache.jackrabbit.mk.model.Node;
import org.apache.jackrabbit.mk.model.StoredCommit;
import org.apache.jackrabbit.mk.model.StoredNode;
import org.apache.jackrabbit.mk.store.BinaryBinding;
import org.apache.jackrabbit.mk.store.Binding;
import org.apache.jackrabbit.mk.store.IdFactory;
import org.apache.jackrabbit.mk.store.NotFoundException;
import org.apache.jackrabbit.mk.util.ExceptionFactory;
import org.apache.jackrabbit.mk.util.IOUtils;
import org.apache.jackrabbit.mk.util.StringUtils;
import org.bson.types.ObjectId;

import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.Mongo;
import com.mongodb.MongoException;
import com.mongodb.WriteConcern;
import com.mongodb.gridfs.GridFS;
import com.mongodb.gridfs.GridFSDBFile;
import com.mongodb.gridfs.GridFSInputFile;

/**
*
*/
public class MongoPersistence implements Persistence, BlobStore {

    private static final boolean BINARY_FORMAT = false;

    private static final String HEAD_COLLECTION = "head";
    private static final String NODES_COLLECTION = "nodes";
    private static final String COMMITS_COLLECTION = "commits";
    private static final String CNEMAPS_COLLECTION = "cneMaps";
    private static final String ID_FIELD = ":id";
    private static final String DATA_FIELD = ":data";

    private Mongo con;
    private DB db;
    private DBCollection nodes;
    private DBCollection commits;
    private DBCollection cneMaps;
    private GridFS fs;

    // TODO: make this configurable
    private IdFactory idFactory = IdFactory.getDigestFactory();
   
    public void initialize(File homeDir) throws Exception {
        con = new Mongo();
        //con = new Mongo("localhost", 27017);

        db = con.getDB("mk");
        db.setWriteConcern(WriteConcern.SAFE);

        if (!db.collectionExists(HEAD_COLLECTION)) {
            // capped collection of size 1
            db.createCollection(HEAD_COLLECTION, new BasicDBObject("capped", true).append("size", 256).append("max", 1));
        }

        nodes = db.getCollection(NODES_COLLECTION);
        nodes.ensureIndex(
                new BasicDBObject(ID_FIELD, 1),
                new BasicDBObject("unique", true));

        commits = db.getCollection(COMMITS_COLLECTION);
        commits.ensureIndex(
                new BasicDBObject(ID_FIELD, 1),
                new BasicDBObject("unique", true));

        cneMaps = db.getCollection(CNEMAPS_COLLECTION);
        cneMaps.ensureIndex(
                new BasicDBObject(ID_FIELD, 1),
                new BasicDBObject("unique", true));

        fs = new GridFS(db);
    }

    public void close() {
        con.close();
        con = null;
        db = null;
    }

    public Id readHead() throws Exception {
        DBObject entry = db.getCollection(HEAD_COLLECTION).findOne();
        if (entry == null) {
            return null;
        }
        return new Id((byte[]) entry.get(ID_FIELD));
    }

    public void writeHead(Id id) throws Exception {
        // capped collection of size 1
        db.getCollection(HEAD_COLLECTION).insert(new BasicDBObject(ID_FIELD, id.getBytes()));
    }

    public void readNode(StoredNode node) throws NotFoundException, Exception {
        Id id = node.getId();
        BasicDBObject key = new BasicDBObject();
        if (BINARY_FORMAT) {
            key.put(ID_FIELD, id.getBytes());
        } else {
            key.put(ID_FIELD, id.toString());
        }
        final BasicDBObject nodeObject = (BasicDBObject) nodes.findOne(key);
        if (nodeObject != null) {
            Binding binding;
            if (BINARY_FORMAT) {
                byte[] bytes = (byte[]) nodeObject.get(DATA_FIELD);
                binding = new BinaryBinding(new ByteArrayInputStream(bytes));
            } else {
                binding = new DBObjectBinding(nodeObject);
            }
            node.deserialize(binding);
        } else {
            throw new NotFoundException(id.toString());
        }
    }
   
    public Id writeNode(Node node) throws Exception {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        node.serialize(new BinaryBinding(out));
        byte[] bytes = out.toByteArray();
        Id id = new Id(idFactory.createContentId(bytes));

        BasicDBObject nodeObject;
        if (BINARY_FORMAT) {
            nodeObject = new BasicDBObject(ID_FIELD, id.getBytes()).append(DATA_FIELD, bytes);
        } else {
            nodeObject = new BasicDBObject(ID_FIELD, id.toString());
            node.serialize(new DBObjectBinding(nodeObject));
        }
        try {
            nodes.insert(nodeObject);
        } catch (MongoException.DuplicateKey ignore) {
            // fall through
        }

        return id;
    }

    public StoredCommit readCommit(Id id) throws NotFoundException, Exception {
        BasicDBObject key = new BasicDBObject();
       
        if (BINARY_FORMAT) {
            key.put(ID_FIELD, id.getBytes());
        } else {
            key.put(ID_FIELD, id.toString());
        }
        BasicDBObject commitObject = (BasicDBObject) commits.findOne(key);
        if (commitObject != null) {
            if (BINARY_FORMAT) {
                byte[] bytes = (byte[]) commitObject.get(DATA_FIELD);
                return StoredCommit.deserialize(id, new BinaryBinding(new ByteArrayInputStream(bytes)));
            } else {
                return StoredCommit.deserialize(id, new DBObjectBinding(commitObject));
            }
        } else {
            throw new NotFoundException(id.toString());
        }
    }

    public void writeCommit(Id id, Commit commit) throws Exception {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        commit.serialize(new BinaryBinding(out));
        byte[] bytes = out.toByteArray();

        BasicDBObject commitObject;
        if (BINARY_FORMAT) {
            commitObject = new BasicDBObject(ID_FIELD, id.getBytes()).append(DATA_FIELD, bytes);
        } else {
            commitObject = new BasicDBObject(ID_FIELD, id.toString());
            commit.serialize(new DBObjectBinding(commitObject));
        }
        try {
            commits.insert(commitObject);
        } catch (MongoException.DuplicateKey ignore) {
            // fall through
        }
    }

    public ChildNodeEntriesMap readCNEMap(Id id) throws NotFoundException, Exception {
        BasicDBObject key = new BasicDBObject();
        if (BINARY_FORMAT) {
            key.put(ID_FIELD, id.getBytes());
        } else {
            key.put(ID_FIELD, id.toString());
        }
        BasicDBObject mapObject = (BasicDBObject) cneMaps.findOne(key);
        if (mapObject != null) {
            if (BINARY_FORMAT) {
                byte[] bytes = (byte[]) mapObject.get(DATA_FIELD);
                return ChildNodeEntriesMap.deserialize(new BinaryBinding(new ByteArrayInputStream(bytes)));
            } else {
                return ChildNodeEntriesMap.deserialize(new DBObjectBinding(mapObject));
            }
        } else {
            throw new NotFoundException(id.toString());
        }
    }

    public Id writeCNEMap(ChildNodeEntriesMap map) throws Exception {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        map.serialize(new BinaryBinding(out));
        byte[] bytes = out.toByteArray();
        Id id = new Id(idFactory.createContentId(bytes));

        BasicDBObject mapObject;
        if (BINARY_FORMAT) {
            mapObject = new BasicDBObject(ID_FIELD, id.getBytes()).append(DATA_FIELD, bytes);
        } else {
            mapObject = new BasicDBObject(ID_FIELD, id.toString());
            map.serialize(new DBObjectBinding(mapObject));
        }
        try {
            cneMaps.insert(mapObject);
        } catch (MongoException.DuplicateKey ignore) {
            // fall through
        }

        return id;
    }

    //------------------------------------------------------------< BlobStore >

    public String addBlob(String tempFilePath) throws Exception {
        try {
            File file = new File(tempFilePath);
            InputStream in = null;
            try {
                in = new BufferedInputStream(new FileInputStream(file));
                return writeBlob(in);
            } finally {
                file.delete();
            }
        } catch (Exception e) {
            throw ExceptionFactory.convert(e);
        }
    }

    public String writeBlob(InputStream in) throws Exception {
        GridFSInputFile f = fs.createFile(in, true);
        //f.save(0x20000);   // save in 128k chunks
        f.save();

        IOUtils.closeQuietly(in);

        return f.getId().toString();
    }

    public int readBlob(String blobId, long pos, byte[] buff, int off,
            int length) throws Exception {

        GridFSDBFile f = fs.findOne(new ObjectId(blobId));
        if (f == null) {
            throw new NotFoundException(blobId);
        }
        // todo provide a more efficient implementation (gridfs stores the data in chunks)
        //long nChunk = pos / f.getChunkSize();
        InputStream in = f.getInputStream();
        try {
            in.skip(pos);
            return in.read(buff, off, length);
        } finally {
            IOUtils.closeQuietly(in);
        }
    }

    public long getBlobLength(String blobId) throws Exception {
        GridFSDBFile f = fs.findOne(new ObjectId(blobId));
        if (f == null) {
            throw new NotFoundException(blobId);
        }

        return f.getLength();
    }
   
    //-------------------------------------------------------< implementation >

    protected final static String ENCODED_DOT = "_x46_";
    protected final static String ENCODED_DOLLAR_SIGN = "_x36_";

    /**
     * see <a href="http://www.mongodb.org/display/DOCS/Legal+Key+Names">http://www.mongodb.org/display/DOCS/Legal+Key+Names</a>
     *
     * @param name
     */
    protected static String encodeName(String name) {
        StringBuilder buf = null;
        for (int i = 0; i < name.length(); i++) {
            if (i == 0 && name.charAt(i) == '$') {
                // mongodb field names must not start with '$'
                buf = new StringBuilder();
                buf.append(ENCODED_DOLLAR_SIGN);
            } else if (name.charAt(i) == '.') {
                // . is a reserved char for mongodb field names
                if (buf == null) {
                    buf = new StringBuilder(name.substring(0, i));
                }
                buf.append(ENCODED_DOT);
            } else {
                if (buf != null) {
                    buf.append(name.charAt(i));
                }
            }
        }

        return buf == null ? name : buf.toString();
    }

    protected static String decodeName(String name) {
        StringBuilder buf = null;

        int lastPos = 0;
        if (name.startsWith(ENCODED_DOLLAR_SIGN)) {
            buf = new StringBuilder("$");
            lastPos = ENCODED_DOLLAR_SIGN.length();
        }

        int pos;
        while ((pos = name.indexOf(ENCODED_DOT, lastPos)) != -1) {
            if (buf == null) {
                buf = new StringBuilder();
            }
            buf.append(name.substring(lastPos, pos));
            buf.append('.');
            lastPos = pos + ENCODED_DOT.length();
        }

        if (buf != null) {
            buf.append(name.substring(lastPos));
            return buf.toString();
        } else {
            return name;
        }
    }

    //--------------------------------------------------------< inner classes >

    protected class DBObjectBinding implements Binding {

        BasicDBObject obj;

        protected DBObjectBinding(BasicDBObject obj) {
            this.obj = obj;
        }

        @Override
        public void write(String key, String value) throws Exception {
            obj.append(encodeName(key), value);
        }

        @Override
        public void write(String key, byte[] value) throws Exception {
            obj.append(encodeName(key), StringUtils.convertBytesToHex(value));
        }

        @Override
        public void write(String key, long value) throws Exception {
            obj.append(encodeName(key), value);
        }

        @Override
        public void write(String key, int value) throws Exception {
            obj.append(encodeName(key), value);
        }

        @Override
        public void writeMap(String key, int count, StringEntryIterator iterator) throws Exception {
            BasicDBObject childObj = new BasicDBObject();
            while (iterator.hasNext()) {
                StringEntry entry = iterator.next();
                childObj.append(encodeName(entry.getKey()), entry.getValue());
            }
            obj.append(encodeName(key), childObj);
        }

        @Override
        public void writeMap(String key, int count, BytesEntryIterator iterator) throws Exception {
            BasicDBObject childObj = new BasicDBObject();
            while (iterator.hasNext()) {
                BytesEntry entry = iterator.next();
                childObj.append(encodeName(entry.getKey()), StringUtils.convertBytesToHex(entry.getValue()));
            }
            obj.append(encodeName(key), childObj);
        }

        @Override
        public String readStringValue(String key) throws Exception {
            return obj.getString(encodeName(key));
        }

        @Override
        public byte[] readBytesValue(String key) throws Exception {
            return StringUtils.convertHexToBytes(obj.getString(encodeName(key)));
        }

        @Override
        public long readLongValue(String key) throws Exception {
            return obj.getLong(encodeName(key));
        }

        @Override
        public int readIntValue(String key) throws Exception {
            return obj.getInt(encodeName(key));
        }

        @Override
        public StringEntryIterator readStringMap(String key) throws Exception {
            final BasicDBObject childObj = (BasicDBObject) obj.get(encodeName(key));
            final Iterator<String> it = childObj.keySet().iterator();
            return new StringEntryIterator() {
                @Override
                public boolean hasNext() {
                    return it.hasNext();
                }

                @Override
                public StringEntry next() {
                    String key = it.next();
                    return new StringEntry(decodeName(key), childObj.getString(key));
                }

                @Override
                public void remove() {
                    throw new UnsupportedOperationException();
                }
            };
        }

        @Override
        public BytesEntryIterator readBytesMap(String key) throws Exception {
            final BasicDBObject childObj = (BasicDBObject) obj.get(encodeName(key));
            final Iterator<String> it = childObj.keySet().iterator();
            return new BytesEntryIterator() {
                @Override
                public boolean hasNext() {
                    return it.hasNext();
                }

                @Override
                public BytesEntry next() {
                    String key = it.next();
                    return new BytesEntry(
                            decodeName(key),
                            StringUtils.convertHexToBytes(childObj.getString(key)));
                }

                @Override
                public void remove() {
                    throw new UnsupportedOperationException();
                }
            };
        }
    }
}
TOP

Related Classes of org.apache.jackrabbit.mk.persistence.MongoPersistence$DBObjectBinding

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.