| /* |
| * Copyright (c) 2011, 2012 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.branch.CDOBranch; |
| import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; |
| import org.eclipse.emf.cdo.common.branch.CDOBranchVersion; |
| import org.eclipse.emf.cdo.common.commit.CDOChangeKind; |
| import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; |
| import org.eclipse.emf.cdo.common.commit.CDOCommitInfoHandler; |
| import org.eclipse.emf.cdo.common.id.CDOID; |
| import org.eclipse.emf.cdo.common.model.CDOClassInfo; |
| import org.eclipse.emf.cdo.common.model.CDOClassifierRef; |
| import org.eclipse.emf.cdo.common.model.CDOModelConstants; |
| import org.eclipse.emf.cdo.common.model.CDOModelUtil; |
| import org.eclipse.emf.cdo.common.model.CDOPackageUnit; |
| import org.eclipse.emf.cdo.common.model.CDOType; |
| import org.eclipse.emf.cdo.common.model.EMFUtil; |
| import org.eclipse.emf.cdo.common.revision.CDOList; |
| import org.eclipse.emf.cdo.common.revision.CDORevisionCacheAdder; |
| import org.eclipse.emf.cdo.common.revision.CDORevisionData; |
| import org.eclipse.emf.cdo.common.util.CDOCommonUtil; |
| import org.eclipse.emf.cdo.server.IStoreAccessor; |
| import org.eclipse.emf.cdo.server.IStoreAccessor.QueryResourcesContext; |
| import org.eclipse.emf.cdo.server.StoreThreadLocal; |
| import org.eclipse.emf.cdo.server.internal.mongodb.MongoDBStore.ValueHandler; |
| import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager; |
| import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager; |
| import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageInfo; |
| import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry; |
| import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit; |
| import org.eclipse.emf.cdo.spi.common.revision.DetachedCDORevision; |
| import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; |
| import org.eclipse.emf.cdo.spi.common.revision.SyntheticCDORevision; |
| import org.eclipse.emf.cdo.spi.server.InternalCommitContext; |
| import org.eclipse.emf.cdo.spi.server.InternalRepository; |
| |
| import org.eclipse.net4j.util.ObjectUtil; |
| import org.eclipse.net4j.util.StringUtil; |
| import org.eclipse.net4j.util.om.monitor.OMMonitor; |
| |
| import org.eclipse.emf.ecore.EClass; |
| import org.eclipse.emf.ecore.EClassifier; |
| import org.eclipse.emf.ecore.EPackage; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| import org.eclipse.emf.ecore.InternalEObject.EStore; |
| import org.eclipse.emf.ecore.resource.ResourceSet; |
| |
| import com.mongodb.BasicDBList; |
| import com.mongodb.BasicDBObject; |
| import com.mongodb.DBObject; |
| import com.mongodb.QueryOperators; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.regex.Pattern; |
| |
| /** |
| * @author Eike Stepper |
| */ |
| public class Commits extends Coll |
| { |
| public static final String COMMITS = "commits"; |
| |
| public static final String COMMITS_ID = "_id"; |
| |
| public static final String COMMITS_PREVIOUS = "previous"; |
| |
| public static final String COMMITS_BRANCH = "branch"; |
| |
| public static final String COMMITS_USER = "user"; |
| |
| public static final String COMMITS_COMMENT = "comment"; |
| |
| public static final String UNITS = "units"; |
| |
| public static final String UNITS_ID = "id"; |
| |
| public static final String UNITS_TYPE = "type"; |
| |
| public static final String UNITS_DATA = "data"; |
| |
| public static final String PACKAGES = "packages"; |
| |
| public static final String PACKAGES_URI = "uri"; |
| |
| public static final String PACKAGES_PARENT = "parent"; |
| |
| public static final String CLASSIFIER_PREFIX = "c"; |
| |
| public static final String SET_SUFFIX = "___set"; |
| |
| public static final String REVISIONS = "revisions"; |
| |
| public static final String REVISIONS_ID = "cdo_id"; |
| |
| public static final String REVISIONS_VERSION = "cdo_version"; |
| |
| private static final String REVISIONS_REVISED = "cdo_revised"; |
| |
| public static final String REVISIONS_CLASS = "cdo_class"; |
| |
| public static final String REVISIONS_RESOURCE = "cdo_resource"; |
| |
| public static final String REVISIONS_CONTAINER = "cdo_container"; |
| |
| public static final String REVISIONS_FEATURE = "cdo_feature"; |
| |
| private static final boolean ZIP_PACKAGE_BYTES = true; |
| |
| private InternalCDOPackageRegistry packageRegistry; |
| |
| private IDHandler idHandler; |
| |
| private InternalCDOPackageUnit[] systemPackageUnits; |
| |
| private EStructuralFeature resourceNameFeature; |
| |
| public Commits(MongoDBStore store) |
| { |
| super(store, COMMITS); |
| ensureIndex(UNITS, UNITS_ID); |
| |
| if (store.isBranching()) |
| { |
| DBObject index = new BasicDBObject(); |
| index.put(REVISIONS + "." + REVISIONS_ID, 1); |
| index.put(COMMITS_BRANCH, 1); |
| index.put(REVISIONS + "." + REVISIONS_VERSION, 1); |
| |
| collection.ensureIndex(index); |
| } |
| else |
| { |
| ensureIndex(REVISIONS, REVISIONS_ID, REVISIONS_VERSION); |
| } |
| |
| packageRegistry = store.getRepository().getPackageRegistry(); |
| idHandler = store.getIDHandler(); |
| } |
| |
| public void writePackageUnits(MongoDBStoreAccessor mongoDBStoreAccessor, InternalCDOPackageUnit[] packageUnits, |
| OMMonitor monitor) |
| { |
| // This must be the first commit into the repo. |
| systemPackageUnits = packageUnits; |
| |
| for (int i = 0; i < packageUnits.length; i++) |
| { |
| InternalCDOPackageUnit packageUnit = packageUnits[i]; |
| InternalCDOPackageInfo[] packageInfos = packageUnit.getPackageInfos(); |
| for (int j = 0; j < packageInfos.length; j++) |
| { |
| InternalCDOPackageInfo packageInfo = packageInfos[j]; |
| for (EClassifier classifier : packageInfo.getEPackage().getEClassifiers()) |
| { |
| store.getClasses().mapNewClassifier(classifier); |
| } |
| } |
| } |
| } |
| |
| private DBObject[] marshallUnits(InternalCDOPackageUnit[] packageUnits) |
| { |
| DBObject[] result = new DBObject[packageUnits.length]; |
| InternalCDOPackageRegistry packageRegistry = store.getRepository().getPackageRegistry(); |
| |
| for (int i = 0; i < packageUnits.length; i++) |
| { |
| InternalCDOPackageUnit packageUnit = packageUnits[i]; |
| EPackage ePackage = packageUnit.getTopLevelPackageInfo().getEPackage(); |
| byte[] bytes = EMFUtil.getEPackageBytes(ePackage, ZIP_PACKAGE_BYTES, packageRegistry); |
| DBObject[] packages = marshallPackages(packageUnit.getPackageInfos()); |
| |
| DBObject doc = new BasicDBObject(); |
| doc.put(UNITS_ID, packageUnit.getID()); |
| doc.put(UNITS_TYPE, packageUnit.getOriginalType().toString()); |
| doc.put(UNITS_DATA, bytes); |
| doc.put(PACKAGES, packages); |
| |
| result[i] = doc; |
| } |
| |
| return result; |
| } |
| |
| private DBObject[] marshallPackages(InternalCDOPackageInfo[] packageInfos) |
| { |
| DBObject[] result = new DBObject[packageInfos.length]; |
| for (int i = 0; i < packageInfos.length; i++) |
| { |
| InternalCDOPackageInfo packageInfo = packageInfos[i]; |
| |
| DBObject doc = new BasicDBObject(); |
| doc.put(PACKAGES_URI, packageInfo.getPackageURI()); |
| String parent = packageInfo.getParentURI(); |
| if (!StringUtil.isEmpty(parent)) |
| { |
| doc.put(PACKAGES_PARENT, parent); |
| } |
| |
| for (EClassifier classifier : packageInfo.getEPackage().getEClassifiers()) |
| { |
| int classifierID = store.getClasses().mapNewClassifier(classifier); |
| doc.put(CLASSIFIER_PREFIX + classifierID, classifier.getName()); |
| } |
| |
| result[i] = doc; |
| } |
| |
| return result; |
| } |
| |
| public Collection<InternalCDOPackageUnit> readPackageUnits() |
| { |
| final Collection<InternalCDOPackageUnit> packageUnits = new ArrayList<InternalCDOPackageUnit>(); |
| |
| DBObject query = new BasicDBObject(); |
| query.put(UNITS, new BasicDBObject("$exists", true)); |
| |
| new QueryEmbeddedUnits<Object>(query) |
| { |
| @Override |
| protected Object handleEmbedded(DBObject doc, DBObject embedded) |
| { |
| long time = (Long)doc.get(COMMITS_ID); |
| CDOPackageUnit.Type type = CDOPackageUnit.Type.valueOf((String)embedded.get(UNITS_TYPE)); |
| InternalCDOPackageInfo[] infos = readPackageInfos(embedded); |
| |
| InternalCDOPackageUnit packageUnit = createPackageUnit(); |
| packageUnit.setOriginalType(type); |
| packageUnit.setTimeStamp(time); |
| packageUnit.setPackageInfos(infos); |
| |
| packageUnits.add(packageUnit); |
| return null; |
| } |
| |
| private InternalCDOPackageInfo[] readPackageInfos(DBObject embedded) |
| { |
| BasicDBList infos = (BasicDBList)embedded.get(PACKAGES); |
| InternalCDOPackageInfo[] result = new InternalCDOPackageInfo[infos.size()]; |
| int i = 0; |
| |
| for (Object info : infos) |
| { |
| DBObject infoObject = (DBObject)info; |
| String uri = (String)infoObject.get(PACKAGES_URI); |
| String parent = (String)infoObject.get(PACKAGES_PARENT); |
| |
| InternalCDOPackageInfo packageInfo = createPackageInfo(); |
| packageInfo.setPackageURI(uri); |
| packageInfo.setParentURI(parent); |
| |
| result[i++] = packageInfo; |
| } |
| |
| return result; |
| } |
| |
| private InternalCDOPackageUnit createPackageUnit() |
| { |
| return (InternalCDOPackageUnit)CDOModelUtil.createPackageUnit(); |
| } |
| |
| private InternalCDOPackageInfo createPackageInfo() |
| { |
| return (InternalCDOPackageInfo)CDOModelUtil.createPackageInfo(); |
| } |
| }.execute(); |
| |
| return packageUnits; |
| } |
| |
| public EPackage[] loadPackageUnit(final InternalCDOPackageUnit packageUnit) |
| { |
| DBObject query = new BasicDBObject(); |
| query.put(UNITS + "." + UNITS_ID, packageUnit.getID()); |
| |
| return new QueryEmbeddedUnits<EPackage[]>(query) |
| { |
| @Override |
| protected EPackage[] handleEmbedded(DBObject doc, DBObject embedded) |
| { |
| byte[] data = (byte[])embedded.get(UNITS_DATA); |
| EPackage ePackage = createEPackage(packageUnit, data); |
| return EMFUtil.getAllPackages(ePackage); |
| } |
| |
| private EPackage createEPackage(InternalCDOPackageUnit packageUnit, byte[] bytes) |
| { |
| ResourceSet resourceSet = EMFUtil.newEcoreResourceSet(packageRegistry); |
| return EMFUtil.createEPackage(packageUnit.getID(), bytes, ZIP_PACKAGE_BYTES, resourceSet, false); |
| } |
| }.execute(); |
| } |
| |
| public void initializeClassifiers() |
| { |
| final Classes classes = store.getClasses(); |
| |
| DBObject query = new BasicDBObject(); |
| query.put(UNITS, new BasicDBObject("$exists", true)); |
| |
| new QueryEmbeddedUnits<Object>(query) |
| { |
| @Override |
| protected Object handleEmbedded(DBObject doc, DBObject embedded) |
| { |
| BasicDBList infos = (BasicDBList)embedded.get(PACKAGES); |
| for (Object info : infos) |
| { |
| DBObject infoObject = (DBObject)info; |
| String uri = (String)infoObject.get(PACKAGES_URI); |
| handleClassifiers(infoObject, uri); |
| } |
| |
| return null; |
| } |
| |
| private void handleClassifiers(DBObject embedded, String packageURI) |
| { |
| Set<String> keys = embedded.keySet(); |
| for (String key : keys) |
| { |
| if (key.startsWith(CLASSIFIER_PREFIX)) |
| { |
| int id = Integer.parseInt(key.substring(CLASSIFIER_PREFIX.length())); |
| String classifierName = (String)embedded.get(key); |
| |
| CDOClassifierRef classifierRef = new CDOClassifierRef(packageURI, classifierName); |
| EClassifier classifier = classifierRef.resolve(packageRegistry); |
| |
| classes.mapClassifier(classifier, id); |
| } |
| } |
| } |
| }.execute(); |
| } |
| |
| public void write(MongoDBStoreAccessor accessor, InternalCommitContext context, OMMonitor monitor) |
| { |
| try |
| { |
| monitor.begin(104); |
| CDOBranchPoint branchPoint = context.getBranchPoint(); |
| |
| DBObject doc = new BasicDBObject(); |
| doc.put(COMMITS_ID, branchPoint.getTimeStamp()); |
| |
| long previous = context.getPreviousTimeStamp(); |
| boolean firstCommit = previous == CDOBranchPoint.UNSPECIFIED_DATE; |
| if (!firstCommit) |
| { |
| doc.put(COMMITS_PREVIOUS, previous); |
| } |
| |
| if (store.isBranching()) |
| { |
| doc.put(COMMITS_BRANCH, branchPoint.getBranch().getID()); |
| } |
| |
| String user = context.getUserID(); |
| if (!StringUtil.isEmpty(user)) |
| { |
| doc.put(COMMITS_USER, user); |
| } |
| |
| String comment = context.getCommitComment(); |
| if (!StringUtil.isEmpty(comment)) |
| { |
| doc.put(COMMITS_COMMENT, comment); |
| } |
| |
| InternalCDOPackageUnit[] newPackageUnits = firstCommit ? systemPackageUnits : context.getNewPackageUnits(); |
| if (!ObjectUtil.isEmpty(newPackageUnits)) |
| { |
| doc.put(UNITS, marshallUnits(newPackageUnits)); |
| } |
| |
| monitor.worked(); |
| accessor.addIDMappings(context, monitor.fork()); |
| context.applyIDMappings(monitor.fork()); |
| |
| List<DBObject> docs = new ArrayList<DBObject>(); |
| marshalRevisions(docs, context, context.getNewObjects(), CDOChangeKind.NEW); |
| marshalRevisions(docs, context, context.getDirtyObjects(), CDOChangeKind.CHANGED); |
| marshalRevisions(docs, context, context.getDetachedRevisions(), CDOChangeKind.DETACHED); |
| |
| if (!docs.isEmpty()) |
| { |
| doc.put(REVISIONS, docs); |
| } |
| |
| monitor.worked(); |
| |
| collection.insert(doc); |
| monitor.worked(100); |
| } |
| finally |
| { |
| monitor.done(); |
| } |
| } |
| |
| private void marshalRevisions(List<DBObject> docs, InternalCommitContext context, InternalCDORevision[] revisions, |
| CDOChangeKind changeKind) |
| { |
| for (InternalCDORevision revision : revisions) |
| { |
| DBObject doc = marshallRevision(context, revision, changeKind); |
| docs.add(doc); |
| } |
| } |
| |
| private DBObject marshallRevision(InternalCommitContext context, InternalCDORevision revision, |
| CDOChangeKind changeKind) |
| { |
| boolean resource = !(revision instanceof SyntheticCDORevision) && revision.isResource(); |
| if (resource && resourceNameFeature == null) |
| { |
| resourceNameFeature = revision.getEClass().getEStructuralFeature(CDOModelConstants.RESOURCE_NODE_NAME_ATTRIBUTE); |
| } |
| |
| DBObject doc = new BasicDBObject(); |
| idHandler.write(doc, REVISIONS_ID, revision.getID()); |
| |
| EClass eClass = revision.getEClass(); |
| doc.put(REVISIONS_CLASS, store.getClasses().getClassifierID(eClass)); |
| |
| if (changeKind == CDOChangeKind.DETACHED) |
| { |
| doc.put(REVISIONS_VERSION, -revision.getVersion() - 1); |
| return doc; |
| } |
| |
| doc.put(REVISIONS_VERSION, revision.getVersion()); |
| |
| CDOID resourceID = revision.getResourceID(); |
| idHandler.write(doc, REVISIONS_RESOURCE, resourceID); |
| |
| CDOID containerID = (CDOID)revision.getContainerID(); |
| idHandler.write(doc, REVISIONS_CONTAINER, containerID); |
| |
| int featureID = revision.getContainingFeatureID(); |
| doc.put(REVISIONS_FEATURE, featureID); |
| |
| if (resource && changeKind != CDOChangeKind.DETACHED) |
| { |
| String name = (String)revision.data().get(resourceNameFeature, 0); |
| IStoreAccessor accessor = StoreThreadLocal.getAccessor(); |
| |
| CDOID existingID = accessor.readResourceID(containerID, name, revision); |
| if (existingID != null && !existingID.equals(revision.getID()) && !isBeingDetached(context, existingID)) |
| { |
| throw new IllegalStateException("Duplicate resource: name=" + name + ", folderID=" + containerID); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } |
| |
| CDOClassInfo classInfo = revision.getClassInfo(); |
| for (EStructuralFeature feature : classInfo.getAllPersistentFeatures()) |
| { |
| Object value = revision.getValue(feature); |
| |
| CDOType type = CDOModelUtil.getType(feature); |
| ValueHandler valueHandler = store.getValueHandler(type); |
| |
| if (feature.isUnsettable()) |
| { |
| if (value == null) |
| { |
| doc.put(feature.getName() + SET_SUFFIX, false); |
| doc.put(feature.getName(), valueHandler.getMongoDefaultValue(feature)); |
| continue; |
| } |
| |
| doc.put(feature.getName() + SET_SUFFIX, true); |
| } |
| |
| if (value == CDORevisionData.NIL) |
| { |
| doc.put(feature.getName(), null); |
| } |
| else if (value == null) |
| { |
| if (feature.isMany() || feature.getDefaultValue() == null) |
| { |
| doc.put(feature.getName(), null); |
| } |
| else |
| { |
| doc.put(feature.getName(), valueHandler.getMongoDefaultValue(feature)); |
| } |
| } |
| else |
| { |
| if (feature.isMany()) |
| { |
| List<?> cdoList = (List<?>)value; |
| BasicDBList mongoList = new BasicDBList(); |
| for (Object element : cdoList) |
| { |
| element = valueHandler.toMongo(element); |
| mongoList.add(element); |
| } |
| |
| value = mongoList; |
| } |
| else |
| { |
| value = valueHandler.toMongo(value); |
| } |
| |
| doc.put(feature.getName(), value); |
| } |
| } |
| |
| return doc; |
| } |
| |
| private boolean isBeingDetached(InternalCommitContext context, CDOID id) |
| { |
| for (CDOID idBeingDetached : context.getDetachedObjects()) |
| { |
| if (id.equals(idBeingDetached)) |
| { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| public void queryResources(final QueryResourcesContext context) |
| { |
| Classes classes = getStore().getClasses(); |
| final int folderCID = classes.getResourceFolderClassID(); |
| final int resourceCID = classes.getResourceClassID(); |
| |
| final CDOID folderID = context.getFolderID(); |
| final String name = context.getName(); |
| final boolean exactMatch = context.exactMatch(); |
| final long timeStamp = context.getTimeStamp(); |
| |
| DBObject query = new BasicDBObject(); |
| |
| query.put(REVISIONS + "." + REVISIONS_CLASS, new BasicDBObject("$in", new int[] { folderCID, resourceCID })); |
| |
| addToQuery(query, context); |
| query.put(REVISIONS + "." + REVISIONS_CONTAINER, idHandler.toValue(folderID)); |
| |
| if (name == null || exactMatch) |
| { |
| query.put(REVISIONS + "." + "name", name); |
| } |
| else |
| { |
| query.put(REVISIONS + "." + "name", Pattern.compile("^" + name)); |
| } |
| |
| new QueryEmbeddedRevisions<Boolean>(query) |
| { |
| @Override |
| protected Boolean handleEmbedded(DBObject doc, DBObject embedded) |
| { |
| int classID = (Integer)embedded.get(REVISIONS_CLASS); |
| if (classID != folderCID && classID != resourceCID) |
| { |
| return null; |
| } |
| |
| int version = (Integer)embedded.get(REVISIONS_VERSION); |
| if (version <= 0) |
| { |
| return null; |
| } |
| |
| CDOID container = idHandler.read(embedded, REVISIONS_CONTAINER); |
| if (!ObjectUtil.equals(container, folderID)) |
| { |
| return null; |
| } |
| |
| String revisionName = (String)embedded.get("name"); |
| if (name == null || exactMatch) |
| { |
| if (!ObjectUtil.equals(revisionName, name)) |
| { |
| return null; |
| } |
| } |
| else |
| { |
| if (!revisionName.startsWith(name)) |
| { |
| return null; |
| } |
| } |
| |
| CDOID id = idHandler.read(embedded, REVISIONS_ID); |
| |
| long created = (Long)doc.get(COMMITS_ID); |
| long revised = getRevised(id, context.getBranch(), version, doc, embedded); |
| if (!CDOCommonUtil.isValidTimeStamp(timeStamp, created, revised)) |
| { |
| return null; |
| } |
| |
| if (!context.addResource(id)) |
| { |
| // No more results allowed |
| return true; |
| } |
| |
| return null; |
| } |
| }.execute(); |
| } |
| |
| private long getRevised(CDOID id, CDOBranch branch, int version, DBObject doc, DBObject revision) |
| { |
| Object value = revision.get(REVISIONS_REVISED); |
| if (value instanceof Long) |
| { |
| return (Long)value; |
| } |
| |
| DBObject query = new BasicDBObject(); |
| idHandler.write(query, REVISIONS + "." + REVISIONS_ID, id); |
| if (store.isBranching()) |
| { |
| query.put(COMMITS_BRANCH, branch.getID()); |
| } |
| |
| int nextVersion = version + 1; |
| query.put(REVISIONS + "." + REVISIONS_VERSION, new BasicDBObject("$in", new int[] { nextVersion, -nextVersion })); |
| |
| Long result = new Query<Long>(query) |
| { |
| @Override |
| protected Long handleDoc(DBObject doc) |
| { |
| return (Long)doc.get(COMMITS_ID); |
| } |
| }.execute(); |
| |
| if (result != null) |
| { |
| long revised = result - 1; |
| |
| // try |
| // { |
| // revision.put(REVISIONS_REVISED, revised); |
| // collection.save(doc); |
| // } |
| // catch (Exception ex) |
| // { |
| // OM.LOG.warn(ex); |
| // } |
| |
| return revised; |
| } |
| |
| return CDOBranchPoint.UNSPECIFIED_DATE; |
| } |
| |
| public InternalCDORevision readRevision(final CDOID id, final CDOBranchPoint branchPoint, int listChunk, |
| CDORevisionCacheAdder cache) |
| { |
| final CDOBranch branch = branchPoint.getBranch(); |
| final long timeStamp = branchPoint.getTimeStamp(); |
| |
| DBObject query = new BasicDBObject(); |
| idHandler.write(query, REVISIONS + "." + REVISIONS_ID, id); |
| |
| addToQuery(query, branchPoint); |
| |
| return new QueryEmbeddedRevisions<InternalCDORevision>(query) |
| { |
| @Override |
| public InternalCDORevision execute() |
| { |
| return execute(collection.find(getRef()).sort(new BasicDBObject(COMMITS_ID, -1)).limit(1)); |
| } |
| |
| @Override |
| protected InternalCDORevision handleEmbedded(DBObject doc, DBObject embedded) |
| { |
| CDOID embeddedID = idHandler.read(embedded, REVISIONS_ID); |
| if (!ObjectUtil.equals(embeddedID, id)) |
| { |
| return null; |
| } |
| |
| long created = (Long)doc.get(COMMITS_ID); |
| CDOBranchPoint revisionBranchPoint = branch.getPoint(created); |
| |
| InternalCDORevision revision = unmarshallRevision(doc, embedded, id, revisionBranchPoint); |
| |
| long revised = revision.getRevised(); |
| if (!CDOCommonUtil.isValidTimeStamp(timeStamp, created, revised)) |
| { |
| return null; |
| } |
| |
| return revision; |
| } |
| }.execute(); |
| } |
| |
| private void addToQuery(DBObject query, CDOBranchPoint branchPoint) |
| { |
| // Exclude detached objects |
| query.put(REVISIONS + "." + REVISIONS_VERSION, new BasicDBObject("$gte", CDOBranchVersion.FIRST_VERSION)); |
| |
| long timeStamp = branchPoint.getTimeStamp(); |
| if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE) |
| { |
| query.put(COMMITS_ID, new BasicDBObject("$lte", timeStamp)); |
| } |
| |
| if (store.isBranching()) |
| { |
| int branch = branchPoint.getBranch().getID(); |
| query.put(COMMITS_BRANCH, branch); |
| } |
| } |
| |
| public InternalCDORevision readRevisionByVersion(final CDOID id, CDOBranchVersion branchVersion, int listChunk, |
| CDORevisionCacheAdder cache) |
| { |
| DBObject query = new BasicDBObject(); |
| idHandler.write(query, REVISIONS + "." + REVISIONS_ID, id); |
| |
| int version = branchVersion.getVersion(); |
| query.put(REVISIONS + "." + REVISIONS_VERSION, new BasicDBObject("$in", new int[] { version, -version })); |
| |
| final CDOBranch branch = branchVersion.getBranch(); |
| if (store.isBranching()) |
| { |
| query.put(COMMITS_BRANCH, branch.getID()); |
| } |
| |
| return new QueryEmbeddedRevisions<InternalCDORevision>(query) |
| { |
| @Override |
| protected InternalCDORevision handleEmbedded(DBObject doc, DBObject embedded) |
| { |
| CDOID revisionID = idHandler.read(embedded, REVISIONS_ID); |
| if (!ObjectUtil.equals(revisionID, id)) |
| { |
| return null; |
| } |
| |
| long revisionTime = (Long)doc.get(COMMITS_ID); |
| CDOBranchPoint branchPoint = branch.getPoint(revisionTime); |
| |
| return unmarshallRevision(doc, embedded, id, branchPoint); |
| } |
| }.execute(); |
| } |
| |
| private InternalCDORevision unmarshallRevision(DBObject doc, DBObject embedded, CDOID id, CDOBranchPoint branchPoint) |
| { |
| int classID = (Integer)embedded.get(REVISIONS_CLASS); |
| EClass eClass = store.getClasses().getClass(classID); |
| |
| CDOBranch branch = branchPoint.getBranch(); |
| int version = (Integer)embedded.get(REVISIONS_VERSION); |
| long revised = getRevised(id, branch, Math.abs(version), doc, embedded); |
| |
| if (version < CDOBranchVersion.FIRST_VERSION) |
| { |
| long timeStamp = branchPoint.getTimeStamp(); |
| return new DetachedCDORevision(eClass, id, branch, -version, timeStamp, revised); |
| } |
| |
| CDOID resourceID = idHandler.read(embedded, REVISIONS_RESOURCE); |
| CDOID containerID = idHandler.read(embedded, REVISIONS_CONTAINER); |
| int featureID = (Integer)embedded.get(REVISIONS_FEATURE); |
| |
| InternalCDORevision result = store.createRevision(eClass, id); |
| result.setBranchPoint(branchPoint); |
| result.setRevised(revised); |
| result.setVersion(version); |
| result.setResourceID(resourceID); |
| result.setContainerID(containerID); |
| result.setContainingFeatureID(featureID); |
| |
| unmarshallRevision(embedded, result); |
| |
| return result; |
| } |
| |
| private void unmarshallRevision(DBObject doc, InternalCDORevision revision) |
| { |
| CDOClassInfo classInfo = revision.getClassInfo(); |
| for (EStructuralFeature feature : classInfo.getAllPersistentFeatures()) |
| { |
| Object value = doc.get(feature.getName()); |
| |
| if (feature.isUnsettable()) |
| { |
| boolean set = (Boolean)doc.get(feature.getName() + SET_SUFFIX); |
| if (!set) |
| { |
| continue; |
| } |
| } |
| |
| if (value == null) |
| { |
| if (!feature.isMany()) |
| { |
| if (feature.getDefaultValue() != null) |
| { |
| value = CDORevisionData.NIL; |
| } |
| } |
| } |
| |
| CDOType type = CDOModelUtil.getType(feature); |
| ValueHandler valueHandler = store.getValueHandler(type); |
| |
| if (feature.isMany()) |
| { |
| if (value != null) |
| { |
| List<?> list = (List<?>)value; |
| CDOList revisionList = revision.getList(feature, list.size()); |
| for (Object element : list) |
| { |
| element = valueHandler.fromMongo(element); |
| revisionList.add(element); |
| } |
| } |
| } |
| else |
| { |
| value = valueHandler.fromMongo(value); |
| revision.set(feature, EStore.NO_INDEX, value); |
| } |
| } |
| } |
| |
| public void loadCommitInfos(CDOBranch branch, long startTime, long endTime, final CDOCommitInfoHandler handler) |
| { |
| if (endTime < CDOBranchPoint.UNSPECIFIED_DATE) |
| { |
| throw new IllegalArgumentException("Counting not supported"); |
| } |
| |
| DBObject query = new BasicDBObject(); |
| |
| if (branch != null && store.isBranching()) |
| { |
| query.put(COMMITS_BRANCH, branch.getID()); |
| } |
| |
| BasicDBList list = new BasicDBList(); |
| if (startTime != CDOBranchPoint.UNSPECIFIED_DATE) |
| { |
| list.add(new BasicDBObject(QueryOperators.GTE, startTime)); |
| } |
| |
| if (endTime != CDOBranchPoint.UNSPECIFIED_DATE) |
| { |
| list.add(new BasicDBObject(QueryOperators.LTE, endTime)); |
| } |
| |
| int size = list.size(); |
| if (size == 2) |
| { |
| query.put(COMMITS_ID, list); |
| } |
| else if (size == 1) |
| { |
| query.put(COMMITS_ID, list.get(0)); |
| } |
| |
| InternalRepository repository = store.getRepository(); |
| final InternalCDOBranchManager branchManager = repository.getBranchManager(); |
| final InternalCDOCommitInfoManager commitManager = repository.getCommitInfoManager(); |
| |
| new Query<Object>(query) |
| { |
| @Override |
| public Object execute() |
| { |
| return execute(collection.find(getRef()).sort(new BasicDBObject(COMMITS_ID, 1))); |
| } |
| |
| @Override |
| protected Object handleDoc(DBObject doc) |
| { |
| long time = (Long)doc.get(COMMITS_ID); |
| Object value = doc.get(COMMITS_PREVIOUS); |
| long previous = value == null ? 0L : (Long)value; |
| |
| CDOBranch commitBranch; |
| if (store.isBranching()) |
| { |
| int branchID = (Integer)doc.get(COMMITS_BRANCH); |
| commitBranch = branchManager.getBranch(branchID); |
| } |
| else |
| { |
| commitBranch = branchManager.getMainBranch(); |
| } |
| |
| String user = (String)doc.get(COMMITS_USER); |
| String comment = (String)doc.get(COMMITS_COMMENT); |
| |
| CDOCommitInfo commitInfo = commitManager.createCommitInfo(commitBranch, time, previous, user, comment, null); |
| handler.handleCommitInfo(commitInfo); |
| return null; |
| } |
| }.execute(); |
| } |
| |
| /** |
| * @author Eike Stepper |
| */ |
| public abstract class QueryEmbedded<RESULT> extends Query<RESULT> |
| { |
| private String field; |
| |
| public QueryEmbedded(DBObject ref, String field) |
| { |
| super(ref); |
| this.field = field; |
| } |
| |
| @Override |
| protected RESULT handleDoc(DBObject doc) |
| { |
| BasicDBList list = (BasicDBList)doc.get(field); |
| for (Object object : list) |
| { |
| DBObject embedded = (DBObject)object; |
| RESULT result = handleEmbedded(doc, embedded); |
| if (result != null) |
| { |
| return result; |
| } |
| } |
| |
| return null; |
| } |
| |
| protected abstract RESULT handleEmbedded(DBObject doc, DBObject embedded); |
| } |
| |
| /** |
| * @author Eike Stepper |
| */ |
| public abstract class QueryEmbeddedUnits<RESULT> extends QueryEmbedded<RESULT> |
| { |
| public QueryEmbeddedUnits(DBObject ref) |
| { |
| super(ref, UNITS); |
| } |
| } |
| |
| /** |
| * @author Eike Stepper |
| */ |
| public abstract class QueryEmbeddedRevisions<RESULT> extends QueryEmbedded<RESULT> |
| { |
| public QueryEmbeddedRevisions(DBObject ref) |
| { |
| super(ref, REVISIONS); |
| } |
| } |
| } |