blob: 60936a96ceaa6994fd2c8f91939c5ad132af0f75 [file] [log] [blame]
/*
* 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);
}
}
}