| /* |
| * Copyright (c) 2010-2014 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.net4j.protocol; |
| |
| import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; |
| import org.eclipse.emf.cdo.common.id.CDOID; |
| import org.eclipse.emf.cdo.common.id.CDOIDUtil; |
| import org.eclipse.emf.cdo.common.model.CDOClassInfo; |
| import org.eclipse.emf.cdo.common.protocol.CDODataInput; |
| import org.eclipse.emf.cdo.common.protocol.CDODataOutput; |
| import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; |
| import org.eclipse.emf.cdo.common.revision.CDORevision; |
| import org.eclipse.emf.cdo.common.util.CDOFetchRule; |
| import org.eclipse.emf.cdo.server.internal.net4j.bundle.OM; |
| import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; |
| import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager; |
| import org.eclipse.emf.cdo.spi.common.revision.RevisionInfo; |
| import org.eclipse.emf.cdo.spi.common.revision.RevisionInfo.Type; |
| |
| import org.eclipse.net4j.util.collection.MoveableList; |
| import org.eclipse.net4j.util.om.trace.ContextTracer; |
| |
| import org.eclipse.emf.ecore.EClass; |
| import org.eclipse.emf.ecore.EReference; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * @author Eike Stepper |
| */ |
| public class LoadRevisionsIndication extends CDOServerReadIndication |
| { |
| private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_PROTOCOL, LoadRevisionsIndication.class); |
| |
| private RevisionInfo[] infos; |
| |
| private CDOBranchPoint branchPoint; |
| |
| private int referenceChunk; |
| |
| private int prefetchDepth; |
| |
| private Map<EClass, CDOFetchRule> fetchRules = new HashMap<EClass, CDOFetchRule>(); |
| |
| private CDOID contextID = CDOID.NULL; |
| |
| private int loadRevisionCollectionChunkSize; |
| |
| public LoadRevisionsIndication(CDOServerProtocol protocol) |
| { |
| super(protocol, CDOProtocolConstants.SIGNAL_LOAD_REVISIONS); |
| } |
| |
| @Override |
| protected void indicating(CDODataInput in) throws IOException |
| { |
| branchPoint = in.readCDOBranchPoint(); |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format("Read branchPoint: {0}", branchPoint); //$NON-NLS-1$ |
| } |
| |
| referenceChunk = in.readInt(); |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format("Read referenceChunk: {0}", referenceChunk); //$NON-NLS-1$ |
| } |
| |
| int size = in.readInt(); |
| if (size < 0) |
| { |
| size = -size; |
| prefetchDepth = in.readInt(); |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format("Read prefetchDepth: {0}", prefetchDepth); //$NON-NLS-1$ |
| } |
| } |
| |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format("Reading {0} infos", size); //$NON-NLS-1$ |
| } |
| |
| infos = new RevisionInfo[size]; |
| for (int i = 0; i < size; i++) |
| { |
| RevisionInfo info = RevisionInfo.read(in, branchPoint); |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format("Read info: {0}", info); //$NON-NLS-1$ |
| } |
| |
| infos[i] = info; |
| } |
| |
| int fetchSize = in.readInt(); |
| if (fetchSize > 0) |
| { |
| loadRevisionCollectionChunkSize = in.readInt(); |
| if (loadRevisionCollectionChunkSize < 1) |
| { |
| loadRevisionCollectionChunkSize = 1; |
| } |
| |
| contextID = in.readCDOID(); |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format("Reading fetch rules for context {0}", contextID); //$NON-NLS-1$ |
| } |
| |
| for (int i = 0; i < fetchSize; i++) |
| { |
| CDOFetchRule fetchRule = new CDOFetchRule(in, getRepository().getPackageRegistry()); |
| fetchRules.put(fetchRule.getEClass(), fetchRule); |
| } |
| } |
| } |
| |
| @Override |
| protected void responding(CDODataOutput out) throws IOException |
| { |
| List<CDORevision> additionalRevisions = new ArrayList<CDORevision>(); |
| List<RevisionInfo> additionalRevisionInfos = new ArrayList<RevisionInfo>(); |
| Set<CDOID> revisionIDs = new HashSet<CDOID>(); |
| int size = infos.length; |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format("Writing {0} results", size); //$NON-NLS-1$ |
| } |
| |
| for (RevisionInfo info : infos) |
| { |
| revisionIDs.add(info.getID()); |
| } |
| |
| // Need to fetch the rule first. |
| Set<CDOFetchRule> visitedFetchRules = new HashSet<CDOFetchRule>(); |
| if (!CDOIDUtil.isNull(contextID) && fetchRules.size() > 0) |
| { |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format("Collecting more revisions based on rules"); //$NON-NLS-1$ |
| } |
| |
| RevisionInfo info = getRevisionInfo(contextID); |
| InternalCDORevision revisionContext = info.getResult(); |
| collectRevisions(revisionContext, revisionIDs, additionalRevisionInfos, additionalRevisions, visitedFetchRules); |
| } |
| |
| InternalCDORevisionManager revisionManager = getRepository().getRevisionManager(); |
| InternalCDORevision[] revisions = new InternalCDORevision[size]; |
| for (int i = 0; i < size; i++) |
| { |
| RevisionInfo info = infos[i]; |
| info.execute(revisionManager, referenceChunk); |
| revisions[i] = info.getResult(); |
| if (loadRevisionCollectionChunkSize > 0) |
| { |
| collectRevisions(revisions[i], revisionIDs, additionalRevisionInfos, additionalRevisions, visitedFetchRules); |
| } |
| } |
| |
| if (prefetchDepth != 0) |
| { |
| prefetchRevisions(prefetchDepth > 0 ? prefetchDepth : Integer.MAX_VALUE, revisions, additionalRevisionInfos, |
| additionalRevisions); |
| } |
| |
| getRepository().notifyReadAccessHandlers(getSession(), revisions, additionalRevisions); |
| |
| for (int i = 0; i < size; i++) |
| { |
| RevisionInfo info = infos[i]; |
| info.setResult(revisions[i]); |
| info.writeResult(out, referenceChunk, branchPoint); // Exposes revision to client side |
| } |
| |
| int additionalSize = additionalRevisionInfos.size(); |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format("Writing {0} additional revision infos", additionalSize); //$NON-NLS-1$ |
| } |
| |
| out.writeInt(additionalSize); |
| for (int i = 0; i < additionalSize; i++) |
| { |
| InternalCDORevision revision = (InternalCDORevision)additionalRevisions.get(i); |
| RevisionInfo info = additionalRevisionInfos.get(i); |
| info.setResult(revision); |
| out.write(Type.MISSING.ordinal()); |
| out.writeCDOID(info.getID()); |
| info.writeResult(out, referenceChunk, branchPoint); |
| } |
| } |
| |
| private RevisionInfo getRevisionInfo(CDOID id) |
| { |
| RevisionInfo info = new RevisionInfo.Missing(id, branchPoint); |
| info.execute(getRepository().getRevisionManager(), referenceChunk); |
| return info; |
| } |
| |
| private void collectRevisions(InternalCDORevision revision, Set<CDOID> revisions, |
| List<RevisionInfo> additionalRevisionInfos, List<CDORevision> additionalRevisions, |
| Set<CDOFetchRule> visitedFetchRules) |
| { |
| if (revision == null) |
| { |
| return; |
| } |
| |
| getSession().collectContainedRevisions(revision, branchPoint, referenceChunk, revisions, additionalRevisions); |
| |
| CDOFetchRule fetchRule = fetchRules.get(revision.getEClass()); |
| if (fetchRule == null || visitedFetchRules.contains(fetchRule)) |
| { |
| return; |
| } |
| |
| visitedFetchRules.add(fetchRule); |
| |
| for (EStructuralFeature feature : fetchRule.getFeatures()) |
| { |
| if (feature.isMany()) |
| { |
| MoveableList<Object> list = revision.getList(feature); |
| int toIndex = Math.min(loadRevisionCollectionChunkSize, list.size()) - 1; |
| for (int i = 0; i <= toIndex; i++) |
| { |
| Object value = list.get(i); |
| if (value instanceof CDOID) |
| { |
| CDOID id = (CDOID)value; |
| if (!CDOIDUtil.isNull(id) && !revisions.contains(id)) |
| { |
| RevisionInfo info = getRevisionInfo(id); |
| InternalCDORevision containedRevision = info.getResult(); |
| if (containedRevision != null) |
| { |
| additionalRevisionInfos.add(info); |
| revisions.add(containedRevision.getID()); |
| additionalRevisions.add(containedRevision); |
| collectRevisions(containedRevision, revisions, additionalRevisionInfos, additionalRevisions, |
| visitedFetchRules); |
| } |
| } |
| } |
| } |
| } |
| else |
| { |
| Object value = revision.getValue(feature); |
| if (value instanceof CDOID) |
| { |
| CDOID id = (CDOID)value; |
| if (!id.isNull() && !revisions.contains(id)) |
| { |
| RevisionInfo info = getRevisionInfo(id); |
| InternalCDORevision containedRevision = info.getResult(); |
| if (containedRevision != null) |
| { |
| revisions.add(containedRevision.getID()); |
| additionalRevisions.add(containedRevision); |
| collectRevisions(containedRevision, revisions, additionalRevisionInfos, additionalRevisions, |
| visitedFetchRules); |
| } |
| } |
| } |
| } |
| } |
| |
| visitedFetchRules.remove(fetchRule); |
| } |
| |
| private void prefetchRevisions(int depth, CDORevision[] revisions, List<RevisionInfo> additionalRevisionInfos, |
| List<CDORevision> additionalRevisions) |
| { |
| Map<CDOID, CDORevision> map = CDOIDUtil.createMap(); |
| for (CDORevision revision : revisions) |
| { |
| map.put(revision.getID(), revision); |
| } |
| |
| for (CDORevision revision : additionalRevisions) |
| { |
| map.put(revision.getID(), revision); |
| } |
| |
| for (CDORevision revision : revisions) |
| { |
| prefetchRevision(depth, (InternalCDORevision)revision, additionalRevisionInfos, additionalRevisions, map); |
| } |
| } |
| |
| private void prefetchRevision(int depth, InternalCDORevision revision, List<RevisionInfo> additionalRevisionInfos, |
| List<CDORevision> additionalRevisions, Map<CDOID, CDORevision> map) |
| { |
| CDOClassInfo classInfo = revision.getClassInfo(); |
| for (EStructuralFeature feature : classInfo.getAllPersistentFeatures()) |
| { |
| if (feature instanceof EReference) |
| { |
| EReference reference = (EReference)feature; |
| if (reference.isContainment()) |
| { |
| Object value = revision.getValue(reference); |
| if (value instanceof CDOID) |
| { |
| CDOID id = (CDOID)value; |
| prefetchRevisionChild(depth, id, additionalRevisionInfos, additionalRevisions, map); |
| } |
| else if (value instanceof Collection<?>) |
| { |
| Collection<?> c = (Collection<?>)value; |
| for (Object e : c) |
| { |
| // If this revision was loaded with referenceChunk != UNCHUNKED, then |
| // some elements might be uninitialized, i.e. not instanceof CDOID. |
| // (See bug 339313.) |
| if (e instanceof CDOID) |
| { |
| CDOID id = (CDOID)e; |
| prefetchRevisionChild(depth, id, additionalRevisionInfos, additionalRevisions, map); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| private void prefetchRevisionChild(int depth, CDOID id, List<RevisionInfo> additionalRevisionInfos, |
| List<CDORevision> additionalRevisions, Map<CDOID, CDORevision> map) |
| { |
| if (CDOIDUtil.isNull(id)) |
| { |
| return; |
| } |
| |
| CDORevision child = map.get(id); |
| if (child == null) |
| { |
| RevisionInfo info = getRevisionInfo(id); |
| child = info.getResult(); |
| if (child != null) |
| { |
| map.put(id, child); |
| additionalRevisions.add(child); |
| additionalRevisionInfos.add(info); |
| } |
| } |
| |
| if (child != null && depth > 0) |
| { |
| prefetchRevision(depth - 1, (InternalCDORevision)child, additionalRevisionInfos, additionalRevisions, map); |
| } |
| } |
| |
| } |