blob: 9022ac7c03627c91d6a73272fb4c5fde3ef451fa [file] [log] [blame]
/*
* Copyright (c) 2020 Eike Stepper (Loehne, 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.view;
import org.eclipse.emf.cdo.CDOObject;
import org.eclipse.emf.cdo.common.id.CDOID;
import org.eclipse.emf.cdo.common.lock.CDOLockState;
import org.eclipse.emf.cdo.common.lock.CDOLockUtil;
import org.eclipse.emf.cdo.common.revision.CDORevision;
import org.eclipse.emf.cdo.common.revision.CDORevisionsLoadedEvent;
import org.eclipse.emf.internal.cdo.bundle.OM;
import org.eclipse.emf.internal.cdo.view.CDOViewImpl;
import org.eclipse.net4j.util.event.IEvent;
import org.eclipse.net4j.util.event.IListener;
import org.eclipse.net4j.util.lifecycle.ILifecycle;
import org.eclipse.net4j.util.lifecycle.LifecycleEventAdapter;
import org.eclipse.emf.spi.cdo.CDOSessionProtocol;
import org.eclipse.emf.spi.cdo.InternalCDOView;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.function.Predicate;
/**
* Prefetches {@link CDOLockState lock states} for a given {@link #getView() view} when {@link CDORevision revisions} are loaded.
*
* @author Eike Stepper
* @since 4.12
*/
public class CDOLockStatePrefetcher
{
public static final Predicate<CDOID> DEFAULT_OBJECT_FILTER = id -> true;
private final IListener revisionManagerListener = new IListener()
{
@Override
public void notifyEvent(IEvent event)
{
if (event instanceof CDORevisionsLoadedEvent)
{
CDORevisionsLoadedEvent e = (CDORevisionsLoadedEvent)event;
if (asyncUpdate)
{
ExecutorService executorService = view.getExecutorService();
executorService.submit(() -> updateLockStates(e));
}
else
{
updateLockStates(e);
}
}
}
};
private final IListener viewListener = new LifecycleEventAdapter()
{
@Override
protected void onDeactivated(ILifecycle view)
{
dispose();
}
};
private final InternalCDOView view;
private final Map<CDOObject, CDOLockState> viewLockStates;
private final boolean asyncUpdate;
private Predicate<CDOID> objectFilter = DEFAULT_OBJECT_FILTER;
public CDOLockStatePrefetcher(CDOView view, boolean asyncUpdate)
{
this.view = (InternalCDOView)view;
this.asyncUpdate = asyncUpdate;
viewLockStates = ((CDOViewImpl)view).getLockStates();
view.addListener(viewListener);
view.getSession().getRevisionManager().addListener(revisionManagerListener);
}
public CDOLockStatePrefetcher(CDOView view)
{
this(view, true);
}
public final Predicate<CDOID> getObjectFilter()
{
return objectFilter;
}
public final void setObjectFilter(Predicate<CDOID> objectFilter)
{
this.objectFilter = objectFilter;
}
public final InternalCDOView getView()
{
return view;
}
public final boolean isAsyncUpdate()
{
return asyncUpdate;
}
public void dispose()
{
view.getSession().getRevisionManager().removeListener(revisionManagerListener);
view.removeListener(viewListener);
}
protected final Object getLockTarget(CDOObject object)
{
return CDOViewImpl.getLockTarget(object);
}
protected final CDOLockState getLockState(CDOObject object)
{
return viewLockStates.get(object);
}
private void addMissingLockState(CDOObject object, List<CDOLockState> missingLockStates)
{
if (object != null && getLockState(object) == null)
{
Object lockedObject = getLockTarget(object); // CDOID or CDOIDAndBranch
if (lockedObject != null)
{
missingLockStates.add(CDOLockUtil.createLockState(lockedObject));
}
}
}
private void updateLockStates(CDORevisionsLoadedEvent event)
{
view.syncExec(() -> {
try
{
Set<CDOID> ids = new HashSet<>();
Predicate<CDOID> filter = objectFilter;
for (CDORevision revision : event.getPrimaryLoadedRevisions())
{
// Bug 466721 : Check null if it is a about a DetachedRevision
if (revision != null)
{
CDOID id = revision.getID();
if (id != null && filter.test(id))
{
// Don't ask to create an object for CDOResource as the caller of ResourceSet.getResource()
// can have created it but not yet registered in the view. Don't ask others CDOResourceNode
// either as it will create some load revisions request.
boolean normalObject = !revision.isResourceNode();
CDOObject object = view.getObject(id, normalObject);
if (object != null)
{
ids.add(id);
}
}
}
}
if (!ids.isEmpty())
{
// Direct call the session protocol.
CDOSessionProtocol sessionProtocol = view.getSession().getSessionProtocol();
CDOLockState[] loadedLockStates = sessionProtocol.getLockStates(view.getViewID(), ids, event.getPrefetchDepth());
updateLockStatesForAllViews(loadedLockStates, true);
// Add missing lock states.
List<CDOLockState> missingLockStates = new ArrayList<>();
for (CDOID id : ids)
{
CDOObject object = view.getObject(id, false);
addMissingLockState(object, missingLockStates);
}
for (CDORevision revision : event.getAdditionalLoadedRevisions())
{
CDOID id = revision.getID();
if (id != null && filter.test(id))
{
boolean normalObject = !revision.isResourceNode();
CDOObject object = view.getObject(id, normalObject);
addMissingLockState(object, missingLockStates);
}
}
updateLockStatesForAllViews(missingLockStates.toArray(new CDOLockState[missingLockStates.size()]), false);
}
}
catch (Exception ex)
{
if (view.isActive())
{
OM.LOG.error(ex);
}
}
});
}
private void updateLockStatesForAllViews(CDOLockState[] lockStates, boolean loadOnDemand)
{
try
{
view.updateLockStates(lockStates, loadOnDemand, null);
}
catch (Exception ex)
{
if (view.isActive())
{
OM.LOG.error(ex);
}
}
for (InternalCDOView otherview : view.getSession().getViews())
{
if (asyncUpdate)
{
ExecutorService executorService = view.getExecutorService();
executorService.submit(() -> updateLockStatesForOtherView(lockStates, loadOnDemand, otherview));
}
else
{
updateLockStatesForOtherView(lockStates, loadOnDemand, otherview);
}
}
}
private void updateLockStatesForOtherView(CDOLockState[] lockStates, boolean loadOnDemand, InternalCDOView otherview)
{
try
{
if (otherview != view && otherview.getBranch() == view.getBranch())
{
otherview.updateLockStates(lockStates, loadOnDemand, null);
}
}
catch (Exception ex)
{
if (otherview.isActive())
{
OM.LOG.error(ex);
}
}
}
}