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