blob: 326529d2cf406f3b1a6fdb23b0d2bea125a98f78 [file] [log] [blame]
/*
* 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;
}
}
}