org.eclipse.emf.cdo.server.internal.mongodb.MongoDBStore.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.emf.cdo.server.internal.mongodb.MongoDBStore.java

Source

/*
 * Copyright (c) 2011, 2012, 2015 Eike Stepper (Berlin, Germany) and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Eike Stepper - initial API and implementation
 */
package org.eclipse.emf.cdo.server.internal.mongodb;

import org.eclipse.emf.cdo.common.id.CDOID;
import org.eclipse.emf.cdo.common.model.CDOType;
import org.eclipse.emf.cdo.common.util.CDOCommonUtil;
import org.eclipse.emf.cdo.server.ISession;
import org.eclipse.emf.cdo.server.IStoreAccessor;
import org.eclipse.emf.cdo.server.ITransaction;
import org.eclipse.emf.cdo.server.IView;
import org.eclipse.emf.cdo.server.internal.mongodb.bundle.OM;
import org.eclipse.emf.cdo.server.mongodb.IMongoDBStore;
import org.eclipse.emf.cdo.server.mongodb.IMongoDBStoreAccessor;
import org.eclipse.emf.cdo.spi.server.InternalRepository;
import org.eclipse.emf.cdo.spi.server.InternalStore.NoExternalReferences;
import org.eclipse.emf.cdo.spi.server.InternalStore.NoFeatureMaps;
import org.eclipse.emf.cdo.spi.server.InternalStore.NoHandleRevisions;
import org.eclipse.emf.cdo.spi.server.InternalStore.NoLargeObjects;
import org.eclipse.emf.cdo.spi.server.InternalStore.NoQueryXRefs;
import org.eclipse.emf.cdo.spi.server.InternalStore.NoRawAccess;
import org.eclipse.emf.cdo.spi.server.Store;
import org.eclipse.emf.cdo.spi.server.StoreAccessorPool;

import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump;
import org.eclipse.net4j.util.lifecycle.LifecycleUtil;

import org.eclipse.emf.common.util.Enumerator;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.EFactory;
import org.eclipse.emf.ecore.EStructuralFeature;

import com.mongodb.DB;
import com.mongodb.Mongo;
import com.mongodb.MongoURI;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * TODO:
 * <ul>
 * <li>Are indexes always unique? Do unique indexes exist?
 * <li>Are <code>_id</code> fields in embedded objects automatically indexed?
 * <li>Ensure indexes: cdo_class, (cdo_branch/cdo_version)?
 * <li>Remove skipMongo() - server-side CommitInfoManager
 * </ul>
 *
 * @author Eike Stepper
 */
public class MongoDBStore extends Store implements IMongoDBStore, //
        NoExternalReferences, NoQueryXRefs, NoLargeObjects, NoFeatureMaps, NoHandleRevisions, NoRawAccess {
    public static final String TYPE = "mongodb"; //$NON-NLS-1$

    @ExcludeFromDump
    private ValueHandler[] valueHandlers = new ValueHandler[Byte.MAX_VALUE - Byte.MIN_VALUE + 1];

    private IDHandler idHandler = new IDHandler.LongValue(this);

    private MongoURI mongoURI;

    private String dbName;

    private DB db;

    private Props props;

    private Commits commits;

    private Classes classes;

    private boolean firstStart;

    private long creationTime;

    private boolean branching;

    public static Map<String, InternalRepository> REPOS = new HashMap<String, InternalRepository>();

    public MongoDBStore() {
        super(TYPE, null, set(ChangeFormat.DELTA), //
                set(RevisionTemporality.AUDITING, RevisionTemporality.NONE), //
                set(RevisionParallelism.NONE, RevisionParallelism.BRANCHING));
    }

    public ValueHandler getValueHandler(CDOType type) {
        return valueHandlers[type.getTypeID() - Byte.MIN_VALUE];
    }

    protected void initValueHandlers() {
        initValueHandler(CDOType.OBJECT, new ValueHandler() {
            @Override
            public Object toMongo(Object value) {
                if (value == null) {
                    return null;
                }

                return idHandler.toValue((CDOID) value);
            }

            @Override
            public Object fromMongo(Object value) {
                if (value == null) {
                    return null;
                }

                return idHandler.fromValue(value);
            }
        });

        initValueHandler(CDOType.CHAR, new ValueHandler() {
            @Override
            public Object toMongo(Object value) {
                if (value == null) {
                    return null;
                }

                return Character.toString((Character) value);
            }

            @Override
            public Object fromMongo(Object value) {
                if (value instanceof String) {
                    return ((String) value).charAt(0);
                }

                return value;
            }
        });

        initValueHandler(CDOType.BYTE, new ValueHandler() {
            @Override
            public Object fromMongo(Object value) {
                if (value instanceof Integer) {
                    return (byte) (int) (Integer) value;
                }

                return value;
            }
        });

        initValueHandler(CDOType.SHORT, new ValueHandler() {
            @Override
            public Object fromMongo(Object value) {
                if (value instanceof Integer) {
                    return (short) (int) (Integer) value;
                }

                return value;
            }
        });

        initValueHandler(CDOType.LONG, new ValueHandler() {
            @Override
            public Object fromMongo(Object value) {
                if (value instanceof Integer) {
                    return (long) (Integer) value;
                }

                return value;
            }
        });

        initValueHandler(CDOType.FLOAT, new ValueHandler() {
            @Override
            public Object fromMongo(Object value) {
                if (value instanceof Double) {
                    return (float) (double) (Double) value;
                }

                return value;
            }
        });

        initValueHandler(CDOType.BIG_DECIMAL, new ValueHandler() {
            @Override
            public Object toMongo(Object value) {
                if (value == null) {
                    return null;
                }

                return ((BigDecimal) value).toPlainString();
            }

            @Override
            public Object fromMongo(Object value) {
                if (value == null) {
                    return null;
                }

                return new BigDecimal((String) value);
            }
        });

        initValueHandler(CDOType.BIG_INTEGER, new ValueHandler() {
            @Override
            public Object toMongo(Object value) {
                if (value == null) {
                    return null;
                }

                return ((BigInteger) value).toString();
            }

            @Override
            public Object fromMongo(Object value) {
                if (value == null) {
                    return null;
                }

                return new BigInteger((String) value);
            }
        });

        initValueHandler(CDOType.ENUM_ORDINAL, new ValueHandler() {
            @Override
            public Object getMongoDefaultValue(EStructuralFeature feature) {
                EEnum eenum = (EEnum) feature.getEType();

                String defaultValueLiteral = feature.getDefaultValueLiteral();
                if (defaultValueLiteral != null) {
                    EEnumLiteral literal = eenum.getEEnumLiteralByLiteral(defaultValueLiteral);
                    return literal.getValue();
                }

                Enumerator enumerator = (Enumerator) eenum.getDefaultValue();
                return enumerator.getValue();
            }
        });

        initValueHandler(CDOType.CUSTOM, new ValueHandler() {
            @Override
            public Object getMongoDefaultValue(EStructuralFeature feature) {
                Object defaultValue = feature.getDefaultValue();
                EClassifier eType = feature.getEType();
                EFactory factory = eType.getEPackage().getEFactoryInstance();
                return factory.convertToString((EDataType) eType, defaultValue);
            }
        });
    }

    protected void initValueHandler(CDOType type, ValueHandler valueHandler) {
        valueHandlers[type.getTypeID() - Byte.MIN_VALUE] = valueHandler;
    }

    public IDHandler getIDHandler() {
        return idHandler;
    }

    public void setIDHandler(IDHandler idHandler) {
        checkInactive();
        this.idHandler = idHandler;
    }

    public MongoURI getMongoURI() {
        return mongoURI;
    }

    public void setMongoURI(MongoURI mongoURI) {
        checkInactive();
        this.mongoURI = mongoURI;
    }

    public String getDBName() {
        return dbName;
    }

    public void setDBName(String dbName) {
        checkInactive();
        this.dbName = dbName;
    }

    public DB getDB() {
        return db;
    }

    public Props getProps() {
        return props;
    }

    public Commits getCommits() {
        return commits;
    }

    public Classes getClasses() {
        return classes;
    }

    public Map<String, String> getPersistentProperties(Set<String> names) {
        return props.get(names);
    }

    public void setPersistentProperties(Map<String, String> properties) {
        props.set(properties);
    }

    public void removePersistentProperties(Set<String> names) {
        props.remove(names);
    }

    public boolean isFirstStart() {
        return firstStart;
    }

    public long getCreationTime() {
        return creationTime;
    }

    public void setCreationTime(long creationTime) {
        this.creationTime = creationTime;

        Map<String, String> map = new HashMap<String, String>();
        map.put(Props.REPOSITORY_CREATED, Long.toString(creationTime));
        props.set(map);
    }

    public boolean isBranching() {
        return branching;
    }

    public CDOID createObjectID(String val) {
        throw new UnsupportedOperationException("Not yet implemented"); // TODO Implement me
    }

    @Deprecated
    public boolean isLocal(CDOID id) {
        throw new UnsupportedOperationException();
    }

    @Override
    public IMongoDBStoreAccessor getReader(ISession session) {
        return (IMongoDBStoreAccessor) super.getReader(session);
    }

    @Override
    public IMongoDBStoreAccessor getWriter(ITransaction transaction) {
        return (IMongoDBStoreAccessor) super.getWriter(transaction);
    }

    @Override
    protected IStoreAccessor createReader(ISession session) {
        return new MongoDBStoreAccessor(this, session);
    }

    @Override
    protected IStoreAccessor createWriter(ITransaction transaction) {
        return new MongoDBStoreAccessor(this, transaction);
    }

    @Override
    protected StoreAccessorPool getReaderPool(ISession session, boolean forReleasing) {
        // No pooling needed
        return null;
    }

    @Override
    protected StoreAccessorPool getWriterPool(IView view, boolean forReleasing) {
        // No pooling needed
        return null;
    }

    @Override
    protected void doBeforeActivate() throws Exception {
        super.doBeforeActivate();
        checkState(mongoURI, "mongoURI");
        checkState(dbName, "dbName");
    }

    @Override
    protected void doActivate() throws Exception {
        InternalRepository repository = getRepository();
        branching = repository.isSupportingBranches();
        if (branching) {
            throw new IllegalStateException("Branching is not supported");
        }

        REPOS.put(repository.getName(), repository);

        super.doActivate();

        Mongo mongo = new Mongo(mongoURI);
        db = mongo.getDB(dbName);

        Set<String> collectionNames = db.getCollectionNames();
        firstStart = !collectionNames.contains(Props.NAME);

        props = new Props(this);
        commits = new Commits(this);
        classes = new Classes(this);

        LifecycleUtil.activate(idHandler);
        setObjectIDTypes(idHandler.getObjectIDTypes());

        Arrays.fill(valueHandlers, new ValueHandler());
        initValueHandlers();

        if (firstStart) {
            firstStart();
        } else {
            reStart();
        }
    }

    @Override
    protected void doDeactivate() throws Exception {
        Map<String, String> map = new HashMap<String, String>();
        map.put(Props.GRACEFULLY_SHUT_DOWN, Boolean.TRUE.toString());
        map.put(Props.REPOSITORY_STOPPED, Long.toString(getRepository().getTimeStamp()));
        map.put(Props.NEXT_LOCAL_CDOID, Store.idToString(idHandler.getNextLocalObjectID()));
        map.put(Props.LAST_CDOID, Store.idToString(idHandler.getLastObjectID()));
        map.put(Props.LAST_CLASSIFIERID, Integer.toString(classes.getLastClassifierID()));
        map.put(Props.LAST_BRANCHID, Integer.toString(getLastBranchID()));
        map.put(Props.LAST_LOCAL_BRANCHID, Integer.toString(getLastLocalBranchID()));
        map.put(Props.LAST_COMMITTIME, Long.toString(getLastCommitTime()));
        map.put(Props.LAST_NONLOCAL_COMMITTIME, Long.toString(getLastNonLocalCommitTime()));
        setPersistentProperties(map);

        LifecycleUtil.deactivate(idHandler);

        REPOS.remove(getRepository().getName());

        if (db != null) {
            db.getMongo().close();
            db = null;
        }

        super.doDeactivate();
    }

    protected void firstStart() {
        setCreationTime(getRepository().getTimeStamp());
        OM.LOG.info("First start: " + CDOCommonUtil.formatTimeStamp(creationTime));
    }

    protected void reStart() {
        Set<String> names = new HashSet<String>();
        names.add(Props.REPOSITORY_CREATED);
        names.add(Props.GRACEFULLY_SHUT_DOWN);

        Map<String, String> map = getPersistentProperties(names);
        creationTime = Long.valueOf(map.get(Props.REPOSITORY_CREATED));

        if (map.containsKey(Props.GRACEFULLY_SHUT_DOWN)) {
            names.clear();
            names.add(Props.NEXT_LOCAL_CDOID);
            names.add(Props.LAST_CDOID);
            names.add(Props.LAST_CLASSIFIERID);
            names.add(Props.LAST_BRANCHID);
            names.add(Props.LAST_LOCAL_BRANCHID);
            names.add(Props.LAST_COMMITTIME);
            names.add(Props.LAST_NONLOCAL_COMMITTIME);
            map = getPersistentProperties(names);

            idHandler.setNextLocalObjectID(stringToID(map.get(Props.NEXT_LOCAL_CDOID)));
            idHandler.setLastObjectID(stringToID(map.get(Props.LAST_CDOID)));
            classes.setLastClassifierID(Integer.valueOf(map.get(Props.LAST_CLASSIFIERID)));
            setLastBranchID(Integer.valueOf(map.get(Props.LAST_BRANCHID)));
            setLastLocalBranchID(Integer.valueOf(map.get(Props.LAST_LOCAL_BRANCHID)));
            setLastCommitTime(Long.valueOf(map.get(Props.LAST_COMMITTIME)));
            setLastNonLocalCommitTime(Long.valueOf(map.get(Props.LAST_NONLOCAL_COMMITTIME)));
        } else {
            repairAfterCrash();
        }

        removePersistentProperties(Collections.singleton(Props.GRACEFULLY_SHUT_DOWN));
    }

    protected void repairAfterCrash() {
        throw new UnsupportedOperationException("Not yet implemented"); // TODO Implement me
    }

    /**
     * @author Eike Stepper
     */
    public static class ValueHandler {
        public Object getMongoDefaultValue(EStructuralFeature feature) {
            Object defaultValue = feature.getDefaultValue();
            return toMongo(defaultValue);
        }

        public Object toMongo(Object value) {
            return value;
        }

        public Object fromMongo(Object value) {
            return value;
        }
    }
}