| /* |
| * 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; |
| } |
| } |
| } |