diff options
Diffstat (limited to 'org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStore.java')
-rw-r--r-- | org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStore.java | 1310 |
1 files changed, 1310 insertions, 0 deletions
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStore.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStore.java new file mode 100644 index 0000000000..96f26ac283 --- /dev/null +++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStore.java @@ -0,0 +1,1310 @@ +/** + * Copyright (c) 2004 - 2011 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: + * Simon McDuff - initial API and implementation + * Simon McDuff - bug 233273 + * Eike Stepper - maintenance + * Andre Dietisheim - bug 256649 + */ +package org.eclipse.emf.cdo.internal.server.mem; + +import org.eclipse.emf.cdo.common.CDOCommonRepository.IDGenerationLocation; +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchHandler; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.branch.CDOBranchVersion; +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.id.CDOIDUtil; +import org.eclipse.emf.cdo.common.lob.CDOLobHandler; +import org.eclipse.emf.cdo.common.lock.CDOLockUtil; +import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockArea.Handler; +import org.eclipse.emf.cdo.common.model.CDOModelConstants; +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +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.IStoreAccessor.DurableLocking2; +import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext; +import org.eclipse.emf.cdo.server.ITransaction; +import org.eclipse.emf.cdo.server.IView; +import org.eclipse.emf.cdo.server.StoreThreadLocal; +import org.eclipse.emf.cdo.server.mem.IMEMStore; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager.BranchLoader; +import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment; +import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager; +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.InternalLockManager; +import org.eclipse.emf.cdo.spi.server.LongIDStore; +import org.eclipse.emf.cdo.spi.server.StoreAccessorPool; + +import org.eclipse.net4j.util.HexUtil; +import org.eclipse.net4j.util.ObjectUtil; +import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump; +import org.eclipse.net4j.util.collection.Pair; +import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; +import org.eclipse.net4j.util.io.IOUtil; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.CharArrayReader; +import java.io.CharArrayWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + * @author Simon McDuff + */ +public class MEMStore extends LongIDStore implements IMEMStore, BranchLoader, DurableLocking2 +{ + public static final String TYPE = "mem"; //$NON-NLS-1$ + + private long creationTime; + + private Map<String, String> properties = new HashMap<String, String>(); + + private Map<Integer, BranchInfo> branchInfos = new HashMap<Integer, BranchInfo>(); + + private int lastBranchID; + + private int lastLocalBranchID; + + private Map<Object, List<InternalCDORevision>> revisions = new HashMap<Object, List<InternalCDORevision>>(); + + private List<CommitInfo> commitInfos = new ArrayList<CommitInfo>(); + + private Map<CDOID, EClass> objectTypes = new HashMap<CDOID, EClass>(); + + private Map<String, LockArea> lockAreas = new HashMap<String, LockArea>(); + + private Map<String, Object> lobs = new HashMap<String, Object>(); + + private int listLimit; + + @ExcludeFromDump + private transient EStructuralFeature resourceNameFeature; + + /** + * @param listLimit + * See {@link #setListLimit(int)}. + * @since 2.0 + */ + public MEMStore(int listLimit) + { + super(TYPE, set(ChangeFormat.REVISION, ChangeFormat.DELTA), set(RevisionTemporality.NONE, + RevisionTemporality.AUDITING), set(RevisionParallelism.NONE, RevisionParallelism.BRANCHING)); + setRevisionTemporality(RevisionTemporality.AUDITING); + setRevisionParallelism(RevisionParallelism.BRANCHING); + this.listLimit = listLimit; + } + + public MEMStore() + { + this(UNLIMITED); + } + + @Override + public CDOID createObjectID(String val) + { + if (getRepository().getIDGenerationLocation() == IDGenerationLocation.CLIENT) + { + byte[] decoded = CDOIDUtil.decodeUUID(val); + return CDOIDUtil.createUUID(decoded); + } + + return super.createObjectID(val); + } + + @Override + public boolean isLocal(CDOID id) + { + if (getRepository().getIDGenerationLocation() == IDGenerationLocation.CLIENT) + { + return false; + } + + return super.isLocal(id); + } + + @Override + public void ensureLastObjectID(CDOID id) + { + if (getRepository().getIDGenerationLocation() == IDGenerationLocation.CLIENT) + { + return; + } + + super.ensureLastObjectID(id); + } + + public synchronized Map<String, String> getPersistentProperties(Set<String> names) + { + if (names == null || names.isEmpty()) + { + return new HashMap<String, String>(properties); + } + + Map<String, String> result = new HashMap<String, String>(); + for (String name : names) + { + String value = properties.get(name); + if (value != null) + { + result.put(name, value); + } + } + + return result; + } + + public synchronized void setPersistentProperties(Map<String, String> properties) + { + this.properties.putAll(properties); + } + + public synchronized void removePersistentProperties(Set<String> names) + { + for (String name : names) + { + properties.remove(name); + } + } + + public synchronized Pair<Integer, Long> createBranch(int branchID, BranchInfo branchInfo) + { + if (branchID == NEW_BRANCH) + { + branchID = ++lastBranchID; + } + else if (branchID == NEW_LOCAL_BRANCH) + { + branchID = --lastLocalBranchID; + } + + branchInfos.put(branchID, branchInfo); + return new Pair<Integer, Long>(branchID, branchInfo.getBaseTimeStamp()); + } + + public synchronized BranchInfo loadBranch(int branchID) + { + return branchInfos.get(branchID); + } + + public synchronized SubBranchInfo[] loadSubBranches(int branchID) + { + List<SubBranchInfo> result = new ArrayList<SubBranchInfo>(); + for (Entry<Integer, BranchInfo> entry : branchInfos.entrySet()) + { + BranchInfo branchInfo = entry.getValue(); + if (branchInfo.getBaseBranchID() == branchID) + { + int id = entry.getKey(); + result.add(new SubBranchInfo(id, branchInfo.getName(), branchInfo.getBaseTimeStamp())); + } + } + + return result.toArray(new SubBranchInfo[result.size()]); + } + + public synchronized int loadBranches(int startID, int endID, CDOBranchHandler handler) + { + int count = 0; + InternalCDOBranchManager branchManager = getRepository().getBranchManager(); + for (Entry<Integer, BranchInfo> entry : branchInfos.entrySet()) + { + int id = entry.getKey(); + if (startID <= id && (id <= endID || endID == 0)) + { + BranchInfo branchInfo = entry.getValue(); + InternalCDOBranch branch = branchManager.getBranch(id, branchInfo); + handler.handleBranch(branch); + ++count; + } + } + + return count; + } + + public synchronized void loadCommitInfos(CDOBranch branch, long startTime, long endTime, CDOCommitInfoHandler handler) + { + InternalCDOCommitInfoManager manager = getRepository().getCommitInfoManager(); + for (int i = 0; i < commitInfos.size(); i++) + { + CommitInfo info = commitInfos.get(i); + if (startTime != CDOBranchPoint.UNSPECIFIED_DATE && info.getTimeStamp() < startTime) + { + continue; + } + + if (endTime != CDOBranchPoint.UNSPECIFIED_DATE && info.getTimeStamp() > endTime) + { + continue; + } + + if (branch != null && !ObjectUtil.equals(info.getBranch(), branch)) + { + continue; + } + + info.handle(manager, handler); + } + } + + public synchronized Set<CDOID> readChangeSet(CDOChangeSetSegment[] segments) + { + Set<CDOID> ids = new HashSet<CDOID>(); + for (CDOChangeSetSegment segment : segments) + { + for (List<InternalCDORevision> list : revisions.values()) + { + readChangeSet(segment, list, ids); + } + } + + return ids; + } + + private void readChangeSet(CDOChangeSetSegment segment, List<InternalCDORevision> list, Set<CDOID> ids) + { + long startTime = segment.getTimeStamp(); + long endTime = segment.getEndTime(); + boolean listCheckDone = false; + for (InternalCDORevision revision : list) + { + CDOID id = revision.getID(); + if (!listCheckDone) + { + if (ids.contains(id)) + { + return; + } + + if (!ObjectUtil.equals(revision.getBranch(), segment.getBranch())) + { + return; + } + + listCheckDone = true; + } + + if (CDOCommonUtil.isValidTimeStamp(revision.getTimeStamp(), startTime, endTime)) + { + ids.add(id); + } + } + } + + public synchronized void handleRevisions(EClass eClass, CDOBranch branch, long timeStamp, boolean exactTime, + CDORevisionHandler handler) + { + for (List<InternalCDORevision> list : revisions.values()) + { + for (InternalCDORevision revision : list) + { + if (!handleRevision(revision, eClass, branch, timeStamp, exactTime, handler)) + { + return; + } + } + } + } + + private boolean handleRevision(InternalCDORevision revision, EClass eClass, CDOBranch branch, long timeStamp, + boolean exactTime, CDORevisionHandler handler) + { + if (eClass != null && revision.getEClass() != eClass) + { + return true; + } + + if (branch != null && !ObjectUtil.equals(revision.getBranch(), branch)) + { + return true; + } + + if (timeStamp != CDOBranchPoint.INVALID_DATE) + { + if (exactTime) + { + if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE && revision.getTimeStamp() != timeStamp) + { + return true; + } + } + else + { + if (!revision.isValid(timeStamp)) + { + return true; + } + } + } + + return handler.handleRevision(revision); + } + + /** + * @since 2.0 + */ + public int getListLimit() + { + return listLimit; + } + + /** + * @since 2.0 + */ + public synchronized void setListLimit(int listLimit) + { + if (listLimit != UNLIMITED && this.listLimit != listLimit) + { + for (List<InternalCDORevision> list : revisions.values()) + { + enforceListLimit(list); + } + } + + this.listLimit = listLimit; + } + + /** + * @since 2.0 + */ + public synchronized List<InternalCDORevision> getCurrentRevisions() + { + ArrayList<InternalCDORevision> simpleRevisions = new ArrayList<InternalCDORevision>(); + Iterator<List<InternalCDORevision>> itr = revisions.values().iterator(); + while (itr.hasNext()) + { + List<InternalCDORevision> list = itr.next(); + InternalCDORevision revision = list.get(list.size() - 1); + simpleRevisions.add(revision); + } + + return simpleRevisions; + } + + public synchronized InternalCDORevision getRevisionByVersion(CDOID id, CDOBranchVersion branchVersion) + { + Object listKey = getListKey(id, branchVersion.getBranch()); + List<InternalCDORevision> list = revisions.get(listKey); + if (list == null) + { + return null; + } + + return getRevisionByVersion(list, branchVersion.getVersion()); + } + + /** + * @since 2.0 + */ + public synchronized InternalCDORevision getRevision(CDOID id, CDOBranchPoint branchPoint) + { + Object listKey = getListKey(id, branchPoint.getBranch()); + if (branchPoint.getTimeStamp() == CDORevision.UNSPECIFIED_DATE) + { + List<InternalCDORevision> list = revisions.get(listKey); + if (list == null) + { + return null; + } + + return list.get(list.size() - 1); + } + + if (!getRepository().isSupportingAudits()) + { + throw new UnsupportedOperationException("Auditing not supported"); + } + + List<InternalCDORevision> list = revisions.get(listKey); + if (list == null) + { + return null; + } + + return getRevision(list, branchPoint); + } + + public synchronized void addRevision(InternalCDORevision revision, boolean raw) + { + Object listKey = getListKey(revision.getID(), revision.getBranch()); + List<InternalCDORevision> list = revisions.get(listKey); + if (list == null) + { + list = new ArrayList<InternalCDORevision>(); + revisions.put(listKey, list); + } + + addRevision(list, revision, raw); + + if (raw) + { + ensureLastObjectID(revision.getID()); + } + } + + public synchronized void addCommitInfo(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, + String comment) + { + int index = commitInfos.size() - 1; + while (index >= 0) + { + CommitInfo info = commitInfos.get(index); + if (timeStamp > info.getTimeStamp()) + { + break; + } + + --index; + } + + CommitInfo commitInfo = new CommitInfo(branch, timeStamp, previousTimeStamp, userID, comment); + commitInfos.add(index + 1, commitInfo); + } + + /** + * @since 2.0 + */ + public synchronized boolean rollbackRevision(InternalCDORevision revision) + { + CDOID id = revision.getID(); + CDOBranch branch = revision.getBranch(); + int version = revision.getVersion(); + + Object listKey = getListKey(id, branch); + List<InternalCDORevision> list = revisions.get(listKey); + if (list == null) + { + return false; + } + + for (Iterator<InternalCDORevision> it = list.iterator(); it.hasNext();) + { + InternalCDORevision rev = it.next(); + if (rev.getVersion() == version) + { + it.remove(); + return true; + } + else if (rev.getVersion() == version - 1) + { + rev.setRevised(CDORevision.UNSPECIFIED_DATE); + } + } + + return false; + } + + /** + * @since 3.0 + */ + public synchronized DetachedCDORevision detachObject(CDOID id, CDOBranch branch, long timeStamp) + { + Object listKey = getListKey(id, branch); + List<InternalCDORevision> list = revisions.get(listKey); + if (list != null) + { + InternalCDORevision revision = getRevision(list, branch.getHead()); + if (revision != null) + { + revision.setRevised(timeStamp - 1); + } + } + + int version; + if (list == null) + { + list = new ArrayList<InternalCDORevision>(); + revisions.put(listKey, list); + version = CDOBranchVersion.FIRST_VERSION; + } + else + { + version = getHighestVersion(list) + 1; + } + + EClass eClass = getObjectType(id); + DetachedCDORevision detached = new DetachedCDORevision(eClass, id, branch, version, timeStamp); + addRevision(list, detached, false); + return detached; + } + + /** + * @since 2.0 + */ + public synchronized void queryResources(IStoreAccessor.QueryResourcesContext context) + { + CDOID folderID = context.getFolderID(); + String name = context.getName(); + boolean exactMatch = context.exactMatch(); + for (Entry<Object, List<InternalCDORevision>> entry : revisions.entrySet()) + { + CDOBranch branch = getBranch(entry.getKey()); + if (!ObjectUtil.equals(branch, context.getBranch())) + { + continue; + } + + List<InternalCDORevision> list = entry.getValue(); + if (list.isEmpty()) + { + continue; + } + + InternalCDORevision revision = list.get(0); + if (revision instanceof SyntheticCDORevision) + { + continue; + } + + if (!revision.isResourceNode()) + { + continue; + } + + revision = getRevision(list, context); + if (revision == null || revision instanceof DetachedCDORevision) + { + continue; + } + + CDOID revisionFolder = (CDOID)revision.data().getContainerID(); + if (!CDOIDUtil.equals(revisionFolder, folderID)) + { + continue; + } + + String revisionName = (String)revision.data().get(resourceNameFeature, 0); + boolean useEquals = exactMatch || revisionName == null || name == null; + boolean match = useEquals ? ObjectUtil.equals(revisionName, name) : revisionName.startsWith(name); + + if (match) + { + if (!context.addResource(revision.getID())) + { + // No more results allowed + break; + } + } + } + } + + public synchronized void queryXRefs(QueryXRefsContext context) + { + Set<CDOID> targetIDs = context.getTargetObjects().keySet(); + Map<EClass, List<EReference>> sourceCandidates = context.getSourceCandidates(); + + for (Entry<Object, List<InternalCDORevision>> entry : revisions.entrySet()) + { + CDOBranch branch = getBranch(entry.getKey()); + if (!ObjectUtil.equals(branch, context.getBranch())) + { + continue; + } + + List<InternalCDORevision> list = entry.getValue(); + if (list.isEmpty()) + { + continue; + } + + InternalCDORevision revision = getRevision(list, context); + if (revision == null || revision instanceof SyntheticCDORevision) + { + continue; + } + + EClass eClass = revision.getEClass(); + CDOID sourceID = revision.getID(); + + List<EReference> eReferences = sourceCandidates.get(eClass); + if (eReferences != null) + { + for (EReference eReference : eReferences) + { + Object value = revision.getValue(eReference); + if (value != null) + { + if (eReference.isMany()) + { + @SuppressWarnings("unchecked") + List<CDOID> ids = (List<CDOID>)value; + int index = 0; + for (CDOID id : ids) + { + if (!queryXRefs(context, targetIDs, id, sourceID, eReference, index++)) + { + return; + } + } + } + else + { + CDOID id = (CDOID)value; + if (!queryXRefs(context, targetIDs, id, sourceID, eReference, 0)) + { + return; + } + } + } + } + } + } + } + + private boolean queryXRefs(QueryXRefsContext context, Set<CDOID> targetIDs, CDOID targetID, CDOID sourceID, + EReference sourceReference, int index) + { + for (CDOID id : targetIDs) + { + if (id.equals(targetID)) + { + if (!context.addXRef(targetID, sourceID, sourceReference, index)) + { + // No more results allowed + return false; + } + } + } + + return true; + } + + public synchronized void rawExport(CDODataOutput out, int fromBranchID, int toBranchID, long fromCommitTime, + long toCommitTime) + { + // TODO: implement MEMStore.rawExport(out, fromBranchID, toBranchID, fromCommitTime, toCommitTime) + throw new UnsupportedOperationException(); + } + + public synchronized void rawImport(CDODataInput in, int fromBranchID, int toBranchID, long fromCommitTime, + long toCommitTime, OMMonitor monitor) + { + // TODO: implement MEMStore.rawImport(in, fromBranchID, toBranchID, fromCommitTime, toCommitTime, monitor) + throw new UnsupportedOperationException(); + } + + public synchronized void rawDelete(CDOID id, int version, CDOBranch branch) + { + Object listKey = getListKey(id, branch); + List<InternalCDORevision> list = revisions.get(listKey); + if (list != null) + { + for (Iterator<InternalCDORevision> it = list.iterator(); it.hasNext();) + { + InternalCDORevision rev = it.next(); + if (rev.getVersion() == version) + { + it.remove(); + break; + } + } + } + } + + public synchronized LockArea createLockArea(String userID, CDOBranchPoint branchPoint, boolean readOnly, + Map<CDOID, LockGrade> locks) + { + return createLockArea(null, userID, branchPoint, readOnly, locks); + } + + public synchronized LockArea createLockArea(String durableLockingID, String userID, CDOBranchPoint branchPoint, + boolean readOnly, Map<CDOID, LockGrade> locks) + { + if (durableLockingID != null) + { + // If the caller is specifying the ID, make sure there is no area with this ID yet + if (lockAreas.containsKey(durableLockingID)) + { + throw new LockAreaAlreadyExistsException(durableLockingID); + } + } + else + { + do + { + durableLockingID = CDOLockUtil.createDurableLockingID(); + } while (lockAreas.containsKey(durableLockingID)); + } + + LockArea area = CDOLockUtil.createLockArea(durableLockingID, userID, branchPoint, readOnly, locks); + lockAreas.put(durableLockingID, area); + return area; + } + + public synchronized void updateLockArea(LockArea lockArea) + { + String durableLockingID = lockArea.getDurableLockingID(); + lockAreas.put(durableLockingID, lockArea); + } + + public synchronized LockArea getLockArea(String durableLockingID) throws LockAreaNotFoundException + { + LockArea area = lockAreas.get(durableLockingID); + if (area == null) + { + throw new LockAreaNotFoundException(durableLockingID); + } + + return area; + } + + public synchronized void getLockAreas(String userIDPrefix, Handler handler) + { + for (LockArea area : lockAreas.values()) + { + String userID = area.getUserID(); + if (userID == null || userID.startsWith(userIDPrefix)) + { + if (!handler.handleLockArea(area)) + { + return; + } + } + } + } + + public synchronized void deleteLockArea(String durableLockingID) + { + lockAreas.remove(durableLockingID); + } + + public synchronized void lock(String durableLockingID, LockType type, Collection<? extends Object> objectsToLock) + { + LockArea area = getLockArea(durableLockingID); + Map<CDOID, LockGrade> locks = area.getLocks(); + + InternalLockManager lockManager = getRepository().getLockManager(); + for (Object objectToLock : objectsToLock) + { + CDOID id = lockManager.getLockKeyID(objectToLock); + LockGrade grade = locks.get(id); + if (grade != null) + { + grade = grade.getUpdated(type, true); + } + else + { + grade = LockGrade.get(type); + } + + locks.put(id, grade); + } + } + + public synchronized void unlock(String durableLockingID, LockType type, Collection<? extends Object> objectsToUnlock) + { + LockArea area = getLockArea(durableLockingID); + Map<CDOID, LockGrade> locks = area.getLocks(); + + InternalLockManager lockManager = getRepository().getLockManager(); + for (Object objectToUnlock : objectsToUnlock) + { + CDOID id = lockManager.getLockKeyID(objectToUnlock); + LockGrade grade = locks.get(id); + if (grade != null) + { + grade = grade.getUpdated(type, false); + if (grade == LockGrade.NONE) + { + locks.remove(id); + } + } + } + } + + public synchronized void unlock(String durableLockingID) + { + LockArea area = getLockArea(durableLockingID); + Map<CDOID, LockGrade> locks = area.getLocks(); + locks.clear(); + } + + public synchronized void queryLobs(List<byte[]> ids) + { + for (Iterator<byte[]> it = ids.iterator(); it.hasNext();) + { + byte[] id = it.next(); + String key = HexUtil.bytesToHex(id); + if (!lobs.containsKey(key)) + { + it.remove(); + } + } + } + + public void handleLobs(long fromTime, long toTime, CDOLobHandler handler) throws IOException + { + for (Entry<String, Object> entry : lobs.entrySet()) + { + byte[] id = HexUtil.hexToBytes(entry.getKey()); + Object lob = entry.getValue(); + if (lob instanceof byte[]) + { + byte[] blob = (byte[])lob; + ByteArrayInputStream in = new ByteArrayInputStream(blob); + OutputStream out = handler.handleBlob(id, blob.length); + if (out != null) + { + try + { + IOUtil.copyBinary(in, out, blob.length); + } + finally + { + IOUtil.close(out); + } + } + } + else + { + char[] clob = (char[])lob; + CharArrayReader in = new CharArrayReader(clob); + Writer out = handler.handleClob(id, clob.length); + if (out != null) + { + try + { + IOUtil.copyCharacter(in, out, clob.length); + } + finally + { + IOUtil.close(out); + } + } + } + } + } + + public synchronized void loadLob(byte[] id, OutputStream out) throws IOException + { + String key = HexUtil.bytesToHex(id); + Object lob = lobs.get(key); + if (lob == null) + { + throw new IOException("Lob not found: " + key); + } + + if (lob instanceof byte[]) + { + byte[] blob = (byte[])lob; + ByteArrayInputStream in = new ByteArrayInputStream(blob); + IOUtil.copyBinary(in, out, blob.length); + } + else + { + char[] clob = (char[])lob; + CharArrayReader in = new CharArrayReader(clob); + IOUtil.copyCharacter(in, new OutputStreamWriter(out), clob.length); + } + } + + public synchronized void writeBlob(byte[] id, long size, InputStream inputStream) throws IOException + { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + IOUtil.copyBinary(inputStream, out, size); + lobs.put(HexUtil.bytesToHex(id), out.toByteArray()); + } + + public synchronized void writeClob(byte[] id, long size, Reader reader) throws IOException + { + CharArrayWriter out = new CharArrayWriter(); + IOUtil.copyCharacter(reader, out, size); + lobs.put(HexUtil.bytesToHex(id), out.toCharArray()); + } + + @Override + public MEMStoreAccessor createReader(ISession session) + { + return new MEMStoreAccessor(this, session); + } + + /** + * @since 2.0 + */ + @Override + public MEMStoreAccessor createWriter(ITransaction transaction) + { + return new MEMStoreAccessor(this, transaction); + } + + /** + * @since 2.0 + */ + public long getCreationTime() + { + return creationTime; + } + + public void setCreationTime(long creationTime) + { + this.creationTime = creationTime; + } + + public boolean isFirstStart() + { + return true; + } + + public synchronized Map<CDOBranch, List<CDORevision>> getAllRevisions() + { + Map<CDOBranch, List<CDORevision>> result = new HashMap<CDOBranch, List<CDORevision>>(); + InternalCDOBranchManager branchManager = getRepository().getBranchManager(); + result.put(branchManager.getMainBranch(), new ArrayList<CDORevision>()); + + for (Integer branchID : branchInfos.keySet()) + { + InternalCDOBranch branch = branchManager.getBranch(branchID); + result.put(branch, new ArrayList<CDORevision>()); + } + + for (List<InternalCDORevision> list : revisions.values()) + { + for (InternalCDORevision revision : list) + { + CDOBranch branch = revision.getBranch(); + List<CDORevision> resultList = result.get(branch); + resultList.add(revision); + } + } + + return result; + } + + public synchronized EClass getObjectType(CDOID id) + { + return objectTypes.get(id); + } + + /** + * @since 2.0 + */ + @Override + protected void doActivate() throws Exception + { + super.doActivate(); + creationTime = getRepository().getTimeStamp(); + + if (getRepository().getIDGenerationLocation() == IDGenerationLocation.CLIENT) + { + setObjectIDTypes(Collections.singleton(CDOID.ObjectType.UUID)); + } + } + + @Override + protected void doDeactivate() throws Exception + { + revisions.clear(); + branchInfos.clear(); + commitInfos.clear(); + objectTypes.clear(); + properties.clear(); + resourceNameFeature = null; + lastBranchID = 0; + lastLocalBranchID = 0; + super.doDeactivate(); + } + + @Override + protected StoreAccessorPool getReaderPool(ISession session, boolean forReleasing) + { + // Pooling of store accessors not supported + return null; + } + + @Override + protected StoreAccessorPool getWriterPool(IView view, boolean forReleasing) + { + // Pooling of store accessors not supported + return null; + } + + private Object getListKey(CDOID id, CDOBranch branch) + { + if (getRevisionParallelism() == RevisionParallelism.NONE) + { + return id; + } + + return new ListKey(id, branch); + } + + private CDOBranch getBranch(Object key) + { + if (key instanceof ListKey) + { + return ((ListKey)key).getBranch(); + } + + return getRepository().getBranchManager().getMainBranch(); + } + + private int getHighestVersion(List<InternalCDORevision> list) + { + int version = CDOBranchVersion.UNSPECIFIED_VERSION; + for (InternalCDORevision revision : list) + { + if (revision.getVersion() > version) + { + version = revision.getVersion(); + } + } + + return version; + } + + private InternalCDORevision getRevisionByVersion(List<InternalCDORevision> list, int version) + { + for (InternalCDORevision revision : list) + { + if (revision.getVersion() == version) + { + return revision; + } + } + + return null; + } + + private InternalCDORevision getRevision(List<InternalCDORevision> list, CDOBranchPoint branchPoint) + { + long timeStamp = branchPoint.getTimeStamp(); + for (InternalCDORevision revision : list) + { + if (timeStamp == CDORevision.UNSPECIFIED_DATE) + { + if (!revision.isHistorical()) + { + return revision; + } + } + else + { + if (revision.isValid(timeStamp)) + { + return revision; + } + } + } + + return null; + } + + private void addRevision(List<InternalCDORevision> list, InternalCDORevision revision, boolean raw) + { + boolean resource = !(revision instanceof SyntheticCDORevision) && revision.isResource(); + if (resource && resourceNameFeature == null) + { + resourceNameFeature = revision.getEClass().getEStructuralFeature(CDOModelConstants.RESOURCE_NODE_NAME_ATTRIBUTE); + } + + if (!raw) + { + // Check version conflict + int version = revision.getVersion(); + InternalCDORevision rev = getRevisionByVersion(list, version); + if (rev != null) + { + rev = getRevisionByVersion(list, version); + throw new IllegalStateException("Concurrent modification of " + rev.getEClass().getName() + "@" + rev.getID()); + } + + // Revise old revision + int oldVersion = version - 1; + if (oldVersion >= CDORevision.UNSPECIFIED_VERSION) + { + InternalCDORevision oldRevision = getRevisionByVersion(list, oldVersion); + if (oldRevision != null) + { + if (getRepository().isSupportingAudits()) + { + oldRevision.setRevised(revision.getTimeStamp() - 1); + } + else + { + list.remove(oldRevision); + } + } + } + + // Check duplicate resource + if (resource) + { + checkDuplicateResource(revision); + } + } + + // Adjust the list + list.add(revision); + if (listLimit != UNLIMITED) + { + enforceListLimit(list); + } + + CDOID id = revision.getID(); + if (!objectTypes.containsKey(id)) + { + objectTypes.put(id, revision.getEClass()); + } + } + + private void checkDuplicateResource(InternalCDORevision revision) + { + CDOID revisionFolder = (CDOID)revision.data().getContainerID(); + String revisionName = (String)revision.data().get(resourceNameFeature, 0); + + IStoreAccessor accessor = StoreThreadLocal.getAccessor(); + + CDOID resourceID = accessor.readResourceID(revisionFolder, revisionName, revision); + if (!CDOIDUtil.isNull(resourceID)) + { + throw new IllegalStateException("Duplicate resource: name=" + revisionName + ", folderID=" + revisionFolder); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + private void enforceListLimit(List<InternalCDORevision> list) + { + while (list.size() > listLimit) + { + list.remove(0); + } + } + + /** + * @author Eike Stepper + */ + private static final class ListKey + { + private CDOID id; + + private CDOBranch branch; + + public ListKey(CDOID id, CDOBranch branch) + { + this.id = id; + this.branch = branch; + } + + public CDOID getID() + { + return id; + } + + public CDOBranch getBranch() + { + return branch; + } + + @Override + public int hashCode() + { + return id.hashCode() ^ branch.hashCode(); + } + + @Override + public boolean equals(Object obj) + { + if (obj == this) + { + return true; + } + + if (obj instanceof ListKey) + { + ListKey that = (ListKey)obj; + return ObjectUtil.equals(id, that.getID()) && ObjectUtil.equals(branch, that.getBranch()); + } + + return false; + } + + @Override + public String toString() + { + return MessageFormat.format("{0}:{1}", id, branch.getID()); + } + } + + /** + * @author Eike Stepper + */ + private static final class CommitInfo + { + private CDOBranch branch; + + private long timeStamp; + + private long previousTimeStamp; + + private String userID; + + private String comment; + + public CommitInfo(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment) + { + this.branch = branch; + this.timeStamp = timeStamp; + this.previousTimeStamp = previousTimeStamp; + this.userID = userID; + this.comment = comment; + } + + public CDOBranch getBranch() + { + return branch; + } + + public long getTimeStamp() + { + return timeStamp; + } + + public void handle(InternalCDOCommitInfoManager manager, CDOCommitInfoHandler handler) + { + CDOCommitInfo commitInfo = manager.createCommitInfo(branch, timeStamp, previousTimeStamp, userID, comment, null); + handler.handleCommitInfo(commitInfo); + } + + @Override + public String toString() + { + return MessageFormat.format("CommitInfo[{0}, {1}, {2}, {3}, {4}]", branch, timeStamp, previousTimeStamp, userID, + comment); + } + } +} |