blob: 9033d3703e15e60f5b587c523eb46fa4bfc47abc [file] [log] [blame]
/*
* Copyright (c) 2021 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.internal.cdo.session;
import org.eclipse.emf.cdo.common.branch.CDOBranch;
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.lock.CDOLockDelta;
import org.eclipse.emf.cdo.common.lock.CDOLockOwner;
import org.eclipse.emf.cdo.common.lock.CDOLockState;
import org.eclipse.emf.cdo.common.lock.CDOLockUtil;
import org.eclipse.emf.cdo.common.revision.CDOIDAndBranch;
import org.eclipse.emf.cdo.session.CDOSession;
import org.eclipse.emf.cdo.spi.common.lock.AbstractCDOLockState;
import org.eclipse.emf.cdo.view.CDOView;
import org.eclipse.emf.cdo.view.CDOViewTargetChangedEvent;
import org.eclipse.net4j.util.CheckUtil;
import org.eclipse.net4j.util.ImplementationError;
import org.eclipse.net4j.util.ObjectUtil;
import org.eclipse.net4j.util.ReflectUtil;
import org.eclipse.net4j.util.collection.CollectionUtil;
import org.eclipse.net4j.util.collection.CollectionUtil.KeepMappedValue;
import org.eclipse.net4j.util.collection.HashBag;
import org.eclipse.net4j.util.collection.Pair;
import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType;
import org.eclipse.net4j.util.container.ContainerEventAdapter;
import org.eclipse.net4j.util.container.IContainer;
import org.eclipse.net4j.util.event.IEvent;
import org.eclipse.net4j.util.event.IListener;
import org.eclipse.net4j.util.io.IOUtil;
import org.eclipse.net4j.util.lifecycle.ILifecycle;
import org.eclipse.net4j.util.lifecycle.Lifecycle;
import org.eclipse.net4j.util.om.OMPlatform;
import org.eclipse.emf.spi.cdo.CDOLockStateCache;
import org.eclipse.emf.spi.cdo.CDOSessionProtocol;
import org.eclipse.emf.spi.cdo.InternalCDOSession;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringJoiner;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.BiFunction;
import java.util.function.Consumer;
/**
* @author Eike Stepper
*/
public final class CDOLockStateCacheImpl extends Lifecycle implements CDOLockStateCache
{
private static final Class<CDOLockOwner[]> ARRAY_CLASS = CDOLockOwner[].class;
private static final boolean DEBUG = OMPlatform.INSTANCE.isProperty("org.eclipse.emf.internal.cdo.session.CDOLockStateCacheImpl.DEBUG");
private static final boolean DEBUG_STACK_TRACE = //
OMPlatform.INSTANCE.isProperty("org.eclipse.emf.internal.cdo.session.CDOLockStateCacheImpl.DEBUG_STACK_TRACE");
private final Map<CDOBranch, ConcurrentMap<CDOID, OwnerInfo>> ownerInfosPerBranch = new HashMap<>();
private final ConcurrentMap<CDOID, OwnerInfo> ownerInfosOfMainBranch = new ConcurrentHashMap<>();
@SuppressWarnings("unchecked")
private final ConcurrentMap<CDOLockOwner, SingleOwnerInfo>[] singleOwnerInfos = new ConcurrentMap[] { //
new ConcurrentHashMap<>(), // SingleOwnerInfo.ReadLock.TYPE
new ConcurrentHashMap<>(), // SingleOwnerInfo.ReadLockAndWriteLock.TYPE
new ConcurrentHashMap<>(), // SingleOwnerInfo.ReadLockAndWriteOption.TYPE
new ConcurrentHashMap<>(), // SingleOwnerInfo.WriteLock.TYPE
new ConcurrentHashMap<>() // SingleOwnerInfo.WriteOption.TYPE
};
private final InternalCDOSession session;
private final IListener sessionListener = new ContainerEventAdapter<CDOView>()
{
@Override
protected void onAdded(IContainer<CDOView> container, CDOView view)
{
viewAdded(view);
}
@Override
protected void onRemoved(IContainer<CDOView> container, CDOView view)
{
viewRemoved(view);
}
@Override
protected void onDeactivated(ILifecycle lifecycle)
{
deactivate();
}
};
private final IListener viewListener = new IListener()
{
@Override
public void notifyEvent(IEvent event)
{
if (event instanceof CDOViewTargetChangedEvent)
{
CDOViewTargetChangedEvent e = (CDOViewTargetChangedEvent)event;
viewTargetChanged(e.getSource(), e.getOldBranchPoint(), e.getBranchPoint());
}
}
};
private final boolean branching;
private final CDOBranch mainBranch;
private final HashBag<CDOBranch> branches = new HashBag<>();
public CDOLockStateCacheImpl(CDOSession session)
{
this.session = (InternalCDOSession)session;
branching = session.getRepositoryInfo().isSupportingBranches();
mainBranch = session.getBranchManager().getMainBranch();
ownerInfosPerBranch.put(mainBranch, ownerInfosOfMainBranch);
activate();
}
@Override
public InternalCDOSession getSession()
{
return session;
}
@Override
public Object createKey(CDOBranch branch, CDOID id)
{
if (branching)
{
return CDOIDUtil.createIDAndBranch(id, branch);
}
return id;
}
@Override
public CDOLockState getLockState(CDOBranch branch, CDOID id)
{
Object key = createKey(branch, id);
return new LockState(key, this);
}
@Override
public void getLockStates(CDOBranch branch, Collection<CDOID> ids, boolean loadOnDemand, Consumer<CDOLockState> consumer)
{
if (ids == null)
{
if (loadOnDemand)
{
loadLockStates(branch, null, consumer);
}
else
{
collectLockStates(branch, ids, null, consumer);
}
}
else
{
Set<CDOID> missingIDs = loadOnDemand ? new HashSet<>() : null;
collectLockStates(branch, ids, missingIDs, consumer);
if (!ObjectUtil.isEmpty(missingIDs))
{
loadLockStates(branch, missingIDs, lockState -> {
missingIDs.remove(lockState.getID());
consumer.accept(lockState);
});
int size = missingIDs.size();
if (size != 0)
{
List<CDOLockState> defaultLockStatesToCache = new ArrayList<>(size);
for (CDOID id : missingIDs)
{
CDOLockState defaultLockStateToCache = getLockState(branch, id);
defaultLockStatesToCache.add(defaultLockStateToCache);
}
addLockStates(branch, defaultLockStatesToCache, consumer);
}
}
}
}
@Override
public void forEachLockState(CDOBranch branch, CDOLockOwner owner, Consumer<CDOLockState> consumer)
{
ConcurrentMap<CDOID, OwnerInfo> infos = getOwnerInfoMap(branch);
infos.forEach((id, info) -> {
if (info.isLocked(null, owner, false))
{
notifyConsumer(branch, id, consumer);
}
});
}
@Override
public synchronized void updateLockStates(CDOBranch branch, Collection<CDOLockDelta> lockDeltas, Collection<CDOLockState> lockStates,
Consumer<CDOLockState> consumer)
{
try
{
ConcurrentMap<CDOID, OwnerInfo> infos = getOwnerInfoMap(branch);
delta_loop: for (CDOLockDelta delta : lockDeltas)
{
CDOID id = delta.getID();
OwnerInfo info = null;
OwnerInfo newInfo = null;
for (int i = 50; i >= 0; --i)
{
info = infos.get(id);
if (info == null || delta.getType() == null)
{
for (CDOLockState lockState : lockStates)
{
if (lockState.getID() == id)
{
addLockState(infos, lockState, consumer);
continue delta_loop;
}
}
continue delta_loop;
}
try
{
newInfo = info.applyDelta(this, delta);
break;
}
catch (ObjectAlreadyLockedException ex)
{
if (i == 0)
{
throw new ObjectAlreadyLockedException(id, branch, ex);
}
try
{
wait(100);
}
catch (InterruptedException ex1)
{
throw new Error(ex1);
}
}
}
if (newInfo != info)
{
trace(id, info, newInfo);
infos.put(id, newInfo);
}
if (consumer != null)
{
Object target = delta.getTarget();
CDOLockState lockState = new LockState(target, this);
consumer.accept(lockState);
}
}
}
finally
{
notifyAll();
}
}
@Override
public void addLockStates(CDOBranch branch, Collection<? extends CDOLockState> lockStates, Consumer<CDOLockState> consumer)
{
ConcurrentMap<CDOID, OwnerInfo> infos = getOwnerInfoMap(branch);
for (CDOLockState lockState : lockStates)
{
try
{
addLockState(infos, lockState, consumer);
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
}
@Override
public List<CDOLockDelta> removeOwner(CDOBranch branch, CDOLockOwner owner, Consumer<CDOLockState> consumer)
{
List<CDOLockDelta> deltas = new ArrayList<>();
List<Pair<CDOID, OwnerInfo>> newInfos = new ArrayList<>();
ConcurrentMap<CDOID, OwnerInfo> infos = getOwnerInfoMap(branch);
infos.forEach((id, info) -> {
OwnerInfo newInfo = info.removeOwner(this, owner, lockType -> appendRemoveDelta(deltas, branch, id, lockType, owner));
if (newInfo != info)
{
newInfos.add(Pair.create(id, newInfo));
notifyConsumer(branch, id, consumer);
}
});
for (Pair<CDOID, OwnerInfo> pair : newInfos)
{
CDOID id = pair.getElement1();
OwnerInfo newInfo = pair.getElement2();
OwnerInfo oldInfo = infos.put(id, newInfo);
trace(id, oldInfo, newInfo);
}
return deltas;
}
@Override
public void remapOwner(CDOBranch branch, CDOLockOwner oldOwner, CDOLockOwner newOwner)
{
for (int type = 0; type < singleOwnerInfos.length; type++)
{
for (SingleOwnerInfo info : singleOwnerInfos[type].values())
{
remapOwnerInfo(info, oldOwner, newOwner);
}
}
ConcurrentMap<CDOID, OwnerInfo> infos = getOwnerInfoMap(branch);
for (OwnerInfo info : infos.values())
{
remapOwnerInfo(info, oldOwner, newOwner);
}
}
@Override
public void removeLockStates(CDOBranch branch, Collection<CDOID> ids, Consumer<CDOLockState> consumer)
{
if (ObjectUtil.isEmpty(ids))
{
return;
}
ConcurrentMap<CDOID, OwnerInfo> infos = getOwnerInfoMap(branch);
for (CDOID id : ids)
{
OwnerInfo info = infos.remove(id);
if (info != null)
{
notifyConsumer(branch, id, consumer);
}
}
}
@Override
public void removeLockStates(CDOBranch branch)
{
ConcurrentMap<CDOID, OwnerInfo> infos = getOwnerInfoMap(branch);
infos.clear();
}
@Override
protected void doActivate() throws Exception
{
super.doActivate();
session.addListener(sessionListener);
}
@Override
protected void doDeactivate() throws Exception
{
session.removeListener(sessionListener);
super.doDeactivate();
}
private void viewAdded(CDOView view)
{
CDOLockOwner owner = view.getLockOwner();
addSingleOwnerInfos(owner, -1);
CDOBranch branch = view.getBranch();
addBranch(branch);
view.addListener(viewListener);
}
private void viewRemoved(CDOView view)
{
view.removeListener(viewListener);
CDOBranch branch = view.getBranch();
removeBranch(branch);
CDOLockOwner owner = view.getLockOwner();
removeSingleOwnerInfos(owner);
}
private void viewTargetChanged(CDOView view, CDOBranchPoint oldBranchPoint, CDOBranchPoint newBranchPoint)
{
CDOBranch oldBranch = oldBranchPoint.getBranch();
CDOBranch newBranch = newBranchPoint.getBranch();
if (newBranch != oldBranch)
{
removeBranch(oldBranch);
addBranch(newBranch);
}
}
private void addBranch(CDOBranch branch)
{
synchronized (branches)
{
if (branches.addAndGet(branch, 1) == 1)
{
if (!branch.isMainBranch())
{
synchronized (ownerInfosPerBranch)
{
ownerInfosPerBranch.put(branch, new ConcurrentHashMap<>());
}
}
}
}
}
private void removeBranch(CDOBranch branch)
{
synchronized (branches)
{
if (branches.removeAndGet(branch, 1) == 0)
{
if (!branch.isMainBranch())
{
synchronized (ownerInfosPerBranch)
{
ownerInfosPerBranch.remove(branch);
}
}
}
}
}
private SingleOwnerInfo addSingleOwnerInfos(CDOLockOwner owner, int typeToReturn)
{
SingleOwnerInfo infoToReturn = null;
for (int type = 0; type < singleOwnerInfos.length; type++)
{
int finalType = type;
// Don't overwrite existing infos. Infos can exist if the owning view is durable.
SingleOwnerInfo info = singleOwnerInfos[type].computeIfAbsent(owner, o -> SingleOwnerInfo.create(o, finalType));
if (type == typeToReturn)
{
infoToReturn = info;
}
}
return infoToReturn;
}
private void removeSingleOwnerInfos(CDOLockOwner owner)
{
for (int type = 0; type < singleOwnerInfos.length; type++)
{
singleOwnerInfos[type].remove(owner);
}
}
private <T> T withKey(Object key, BiFunction<CDOBranch, CDOID, T> function)
{
CDOBranch branch;
CDOID id;
if (branching)
{
CDOIDAndBranch idAndBranch = (CDOIDAndBranch)key;
branch = idAndBranch.getBranch();
id = idAndBranch.getID();
}
else
{
branch = mainBranch;
id = (CDOID)key;
}
return function.apply(branch, id);
}
private void notifyConsumer(CDOBranch branch, CDOID id, Consumer<CDOLockState> consumer)
{
if (consumer != null)
{
CDOLockState lockState = getLockState(branch, id);
consumer.accept(lockState);
}
}
private SingleOwnerInfo getSingleOwnerInfo(CDOLockOwner owner, int type)
{
SingleOwnerInfo info = singleOwnerInfos[type].get(owner);
if (info == null)
{
info = addSingleOwnerInfos(owner, type);
}
return info;
}
private ConcurrentMap<CDOID, OwnerInfo> getOwnerInfoMap(CDOBranch branch)
{
if (branch.isMainBranch())
{
return ownerInfosOfMainBranch;
}
synchronized (ownerInfosPerBranch)
{
return ownerInfosPerBranch.get(branch);
}
}
@SuppressWarnings("unchecked")
private Map.Entry<CDOBranch, ConcurrentMap<CDOID, OwnerInfo>>[] getOwnerInfoMapEntries()
{
synchronized (ownerInfosPerBranch)
{
return ownerInfosPerBranch.entrySet().toArray(new Map.Entry[ownerInfosPerBranch.size()]);
}
}
private OwnerInfo getOwnerInfo(Object key)
{
return withKey(key, (branch, id) -> {
ConcurrentMap<CDOID, OwnerInfo> infos = getOwnerInfoMap(branch);
return getOwnerInfo(infos, branch, id);
});
}
private OwnerInfo getOwnerInfo(ConcurrentMap<CDOID, OwnerInfo> infos, CDOBranch branch, CDOID id)
{
OwnerInfo info = infos.get(id);
if (info == null)
{
return NoOwnerInfo.INSTANCE;
}
return info;
}
private boolean changeOwnerInfo(Object key, CDOLockOwner owner, BiFunction<OwnerInfo, CDOLockOwner, OwnerInfo> changer)
{
return withKey(key, (branch, id) -> {
boolean[] changed = { false };
ConcurrentMap<CDOID, OwnerInfo> infos = getOwnerInfoMap(branch);
CollectionUtil.compute(infos, id, (unused, info) -> {
if (info == null)
{
info = NoOwnerInfo.INSTANCE;
}
OwnerInfo newInfo = changer.apply(info, owner);
if (newInfo != info)
{
trace(id, info, newInfo);
changed[0] = true;
return newInfo;
}
throw new KeepMappedValue(info);
});
return changed[0];
});
}
private void remapOwnerInfo(OwnerInfo info, CDOLockOwner oldOwner, CDOLockOwner newOwner)
{
if (info.remapOwner(oldOwner, newOwner) && DEBUG)
{
IOUtil.OUT().println("Remap owner: " + info);
}
}
private void collectLockStates(CDOBranch branch, Collection<CDOID> ids, Set<CDOID> missingIDs, Consumer<CDOLockState> consumer)
{
if (branch != null)
{
ConcurrentMap<CDOID, OwnerInfo> infos = getOwnerInfoMap(branch);
collectLockStates(branch, ids, infos, missingIDs, consumer);
}
else
{
for (Map.Entry<CDOBranch, ConcurrentMap<CDOID, OwnerInfo>> entry : getOwnerInfoMapEntries())
{
CDOBranch infosBranch = entry.getKey();
ConcurrentMap<CDOID, OwnerInfo> infos = entry.getValue();
collectLockStates(infosBranch, ids, infos, missingIDs, consumer);
}
}
}
private void collectLockStates(CDOBranch branch, Collection<CDOID> ids, ConcurrentMap<CDOID, OwnerInfo> infos, //
Set<CDOID> missingIDs, Consumer<CDOLockState> consumer)
{
for (CDOID id : ids)
{
OwnerInfo info = infos.get(id);
if (info != null)
{
notifyConsumer(branch, id, consumer);
}
else if (missingIDs != null)
{
missingIDs.add(id);
}
}
}
private void loadLockStates(CDOBranch branch, Set<CDOID> ids, Consumer<CDOLockState> consumer)
{
CDOSessionProtocol sessionProtocol = session.getSessionProtocol();
List<CDOLockState> lockStates = sessionProtocol.getLockStates2(branch.getID(), ids, CDOLockState.DEPTH_NONE);
addLockStates(branch, lockStates, consumer);
}
private void addLockState(ConcurrentMap<CDOID, OwnerInfo> infos, CDOLockState lockState, Consumer<CDOLockState> consumer)
{
CDOID id = lockState.getID();
OwnerInfo info = createOwnerInfo(lockState);
trace(id, null, info);
infos.put(id, info);
if (consumer != null)
{
consumer.accept(lockState);
}
}
private OwnerInfo createOwnerInfo(CDOLockState lockState)
{
return updateOwnerInfo(NoOwnerInfo.INSTANCE, lockState);
}
private OwnerInfo updateOwnerInfo(OwnerInfo info, CDOLockState lockState)
{
try
{
for (CDOLockOwner readLockOwner : lockState.getReadLockOwners())
{
info = info.addReadLockOwner(this, readLockOwner);
}
info = info.addWriteLockOwner(this, lockState.getWriteLockOwner());
info = info.addWriteOptionOwner(this, lockState.getWriteOptionOwner());
}
catch (ObjectAlreadyLockedException ex)
{
CDOBranch branch = lockState.getBranch();
if (branch == null)
{
branch = mainBranch;
}
throw new ObjectAlreadyLockedException(lockState.getID(), branch, ex);
}
catch (RuntimeException | Error ex)
{
throw new ImplementationError("Could not modify " + info + " of " + lockState.getLockedObject(), ex);
}
return info;
}
private boolean appendRemoveDelta(List<CDOLockDelta> deltas, CDOBranch branch, CDOID id, LockType lockType, CDOLockOwner owner)
{
return deltas.add(CDOLockUtil.createLockDelta(createKey(branch, id), lockType, owner, null));
}
private static boolean isOwnerInfoLocked(OwnerInfo info, LockType lockType, CDOLockOwner lockOwner, boolean others)
{
if (lockType == null)
{
return isOwnerInfoReadLocked(info, lockOwner, others) //
|| isOwnerInfoWriteLocked(info, lockOwner, others) //
|| isOwnerInfoOptionLocked(info, lockOwner, others);
}
switch (lockType)
{
case READ:
return isOwnerInfoReadLocked(info, lockOwner, others);
case WRITE:
return isOwnerInfoWriteLocked(info, lockOwner, others);
case OPTION:
return isOwnerInfoOptionLocked(info, lockOwner, others);
}
return false;
}
private static boolean isOwnerInfoReadLocked(OwnerInfo info, CDOLockOwner by, boolean others)
{
Object readLockOwners = info.getReadLockOwners();
if (readLockOwners == null)
{
return false;
}
int n;
boolean contained;
if (readLockOwners.getClass() == ARRAY_CLASS)
{
CDOLockOwner[] owners = (CDOLockOwner[])readLockOwners;
n = owners.length;
if (n == 0)
{
return false;
}
contained = CDOLockUtil.indexOf(owners, by) != -1;
}
else
{
n = 1;
contained = readLockOwners == by;
}
if (others)
{
int ownCount = contained ? 1 : 0;
return n > ownCount;
}
return contained;
}
private static boolean isOwnerInfoWriteLocked(OwnerInfo info, CDOLockOwner by, boolean others)
{
CDOLockOwner writeLockOwner = info.getWriteLockOwner();
if (writeLockOwner == null)
{
return false;
}
return writeLockOwner == by ^ others;
}
private static boolean isOwnerInfoOptionLocked(OwnerInfo info, CDOLockOwner by, boolean others)
{
CDOLockOwner writeOptionOwner = info.getWriteOptionOwner();
if (writeOptionOwner == null)
{
return false;
}
return writeOptionOwner == by ^ others;
}
private void trace(CDOID id, OwnerInfo oldInfo, OwnerInfo newInfo)
{
if (DEBUG)
{
String message = "[" + session.getSessionID() + "] " + id + ": " + oldInfo + " --> " + newInfo;
if (DEBUG_STACK_TRACE)
{
message += "\n" + ReflectUtil.dumpThread();
}
IOUtil.OUT().println(message);
}
}
/**
* @author Eike Stepper
*/
protected static abstract class OwnerInfo
{
public OwnerInfo()
{
}
public final boolean isLocked(LockType lockType, CDOLockOwner lockOwner, boolean others)
{
return isOwnerInfoLocked(this, lockType, lockOwner, others);
}
public abstract Object getReadLockOwners();
public abstract CDOLockOwner getWriteLockOwner();
public abstract CDOLockOwner getWriteOptionOwner();
public abstract OwnerInfo addReadLockOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner);
public abstract OwnerInfo removeReadLockOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner);
public abstract OwnerInfo addWriteLockOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner);
public abstract OwnerInfo removeWriteLockOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner);
public abstract OwnerInfo addWriteOptionOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner);
public abstract OwnerInfo removeWriteOptionOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner);
public abstract OwnerInfo removeOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner, Consumer<LockType> consumer);
public abstract boolean remapOwner(CDOLockOwner oldOwner, CDOLockOwner newOwner);
public final OwnerInfo applyDelta(CDOLockStateCacheImpl cache, CDOLockDelta delta)
{
OwnerInfo info = this;
CDOLockOwner oldOwner = delta.getOldOwner();
CDOLockOwner newOwner = delta.getNewOwner();
switch (delta.getType())
{
case READ:
if (oldOwner != null)
{
info = info.removeReadLockOwner(cache, oldOwner);
}
if (newOwner != null)
{
info = info.addReadLockOwner(cache, newOwner);
}
break;
case OPTION:
if (oldOwner != null)
{
info = info.removeWriteOptionOwner(cache, oldOwner);
}
if (newOwner != null)
{
info = info.addWriteOptionOwner(cache, newOwner);
}
break;
case WRITE:
if (oldOwner != null)
{
info = info.removeWriteLockOwner(cache, oldOwner);
}
if (newOwner != null)
{
info = info.addWriteLockOwner(cache, newOwner);
}
break;
default:
throw new AssertionError();
}
return info;
}
@Override
public String toString()
{
String name = getClass().getName();
name = name.replace(CDOLockStateCacheImpl.class.getName() + "$", "");
name = name.replace('$', '.');
return name;
}
}
/**
* @author Eike Stepper
*/
protected static final class NoOwnerInfo extends OwnerInfo
{
public static final OwnerInfo INSTANCE = new NoOwnerInfo();
private NoOwnerInfo()
{
}
@Override
public Object getReadLockOwners()
{
return null;
}
@Override
public CDOLockOwner getWriteLockOwner()
{
return null;
}
@Override
public CDOLockOwner getWriteOptionOwner()
{
return null;
}
@Override
public OwnerInfo addReadLockOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
if (owner == null)
{
return this;
}
return cache.getSingleOwnerInfo(owner, SingleOwnerInfo.ReadLock.TYPE);
}
@Override
public OwnerInfo removeReadLockOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
return this;
}
@Override
public OwnerInfo addWriteLockOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
if (owner == null)
{
return this;
}
return cache.getSingleOwnerInfo(owner, SingleOwnerInfo.WriteLock.TYPE);
}
@Override
public OwnerInfo removeWriteLockOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
return this;
}
@Override
public OwnerInfo addWriteOptionOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
if (owner == null)
{
return this;
}
return cache.getSingleOwnerInfo(owner, SingleOwnerInfo.WriteOption.TYPE);
}
@Override
public OwnerInfo removeWriteOptionOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
return this;
}
@Override
public OwnerInfo removeOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner, Consumer<LockType> consumer)
{
return this;
}
@Override
public boolean remapOwner(CDOLockOwner oldOwner, CDOLockOwner newOwner)
{
// Do nothing.
return false;
}
}
/**
* @author Eike Stepper
*/
protected static abstract class SingleOwnerInfo extends OwnerInfo
{
protected CDOLockOwner owner;
public SingleOwnerInfo(CDOLockOwner owner)
{
this.owner = owner;
}
@Override
public Object getReadLockOwners()
{
return null;
}
@Override
public CDOLockOwner getWriteLockOwner()
{
return null;
}
@Override
public CDOLockOwner getWriteOptionOwner()
{
return null;
}
@Override
public final OwnerInfo removeOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner, Consumer<LockType> consumer)
{
if (owner == this.owner)
{
notifyLockTypes(consumer);
return NoOwnerInfo.INSTANCE;
}
return this;
}
@Override
public final boolean remapOwner(CDOLockOwner oldOwner, CDOLockOwner newOwner)
{
if (owner == oldOwner)
{
owner = newOwner;
return true;
}
return false;
}
@Override
public String toString()
{
return super.toString() + "[" + owner + "]";
}
protected final ObjectAlreadyLockedException failure(CDOLockOwner owner, LockType lockType)
{
StringJoiner joiner = new StringJoiner(" and the ", //
owner + " could not acquire the " + lockType + " lock because the ", //
" is already acquired by " + this.owner);
notifyLockTypes(type -> {
joiner.add(type + " lock");
});
return new ObjectAlreadyLockedException(joiner.toString());
}
protected abstract void notifyLockTypes(Consumer<LockType> consumer);
public static SingleOwnerInfo create(CDOLockOwner owner, int type)
{
switch (type)
{
case SingleOwnerInfo.ReadLock.TYPE:
return new SingleOwnerInfo.ReadLock(owner);
case SingleOwnerInfo.ReadLockAndWriteLock.TYPE:
return new SingleOwnerInfo.ReadLockAndWriteLock(owner);
case SingleOwnerInfo.ReadLockAndWriteOption.TYPE:
return new SingleOwnerInfo.ReadLockAndWriteOption(owner);
case SingleOwnerInfo.WriteLock.TYPE:
return new SingleOwnerInfo.WriteLock(owner);
case SingleOwnerInfo.WriteOption.TYPE:
return new SingleOwnerInfo.WriteOption(owner);
default:
throw new IllegalArgumentException("Illegal type: " + type);
}
}
/**
* @author Eike Stepper
*/
protected static final class ReadLock extends SingleOwnerInfo
{
public static final int TYPE = 0;
public ReadLock(CDOLockOwner owner)
{
super(owner);
}
@Override
public Object getReadLockOwners()
{
return owner;
}
@Override
public OwnerInfo addReadLockOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
if (owner == null)
{
return this;
}
if (owner == this.owner)
{
return this;
}
return new MultiOwnerInfo.ReadLock(this.owner, owner);
}
@Override
public OwnerInfo removeReadLockOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
return removeOwner(cache, owner, null);
}
@Override
public OwnerInfo addWriteLockOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
if (owner == null)
{
return this;
}
if (owner == this.owner)
{
return cache.getSingleOwnerInfo(owner, SingleOwnerInfo.ReadLockAndWriteLock.TYPE);
}
throw failure(owner, LockType.WRITE);
}
@Override
public OwnerInfo removeWriteLockOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
return this;
}
@Override
public OwnerInfo addWriteOptionOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
if (owner == null)
{
return this;
}
if (owner == this.owner)
{
return cache.getSingleOwnerInfo(owner, SingleOwnerInfo.ReadLockAndWriteOption.TYPE);
}
return new MultiOwnerInfo.ReadLockAndWriteOption(owner, this.owner);
}
@Override
public OwnerInfo removeWriteOptionOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
return this;
}
@Override
protected void notifyLockTypes(Consumer<LockType> consumer)
{
if (consumer != null)
{
consumer.accept(LockType.READ);
}
}
}
/**
* @author Eike Stepper
*/
protected static final class ReadLockAndWriteLock extends SingleOwnerInfo
{
public static final int TYPE = 1;
public ReadLockAndWriteLock(CDOLockOwner owner)
{
super(owner);
}
@Override
public Object getReadLockOwners()
{
return owner;
}
@Override
public CDOLockOwner getWriteLockOwner()
{
return owner;
}
@Override
public OwnerInfo addReadLockOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
if (owner == null)
{
return this;
}
if (owner == this.owner)
{
return this;
}
throw failure(owner, LockType.READ);
}
@Override
public OwnerInfo removeReadLockOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
if (owner == this.owner)
{
return cache.getSingleOwnerInfo(owner, SingleOwnerInfo.WriteLock.TYPE);
}
return this;
}
@Override
public OwnerInfo addWriteLockOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
if (owner == null)
{
return this;
}
if (owner == this.owner)
{
return this;
}
throw failure(owner, LockType.WRITE);
}
@Override
public OwnerInfo removeWriteLockOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
if (owner == this.owner)
{
return cache.getSingleOwnerInfo(owner, SingleOwnerInfo.ReadLock.TYPE);
}
return this;
}
@Override
public OwnerInfo addWriteOptionOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
if (owner == null)
{
return this;
}
throw failure(owner, LockType.OPTION);
}
@Override
public OwnerInfo removeWriteOptionOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
return this;
}
@Override
protected void notifyLockTypes(Consumer<LockType> consumer)
{
consumer.accept(LockType.READ);
consumer.accept(LockType.WRITE);
}
}
/**
* @author Eike Stepper
*/
protected static final class ReadLockAndWriteOption extends SingleOwnerInfo
{
public static final int TYPE = 2;
public ReadLockAndWriteOption(CDOLockOwner owner)
{
super(owner);
}
@Override
public Object getReadLockOwners()
{
return owner;
}
@Override
public CDOLockOwner getWriteOptionOwner()
{
return owner;
}
@Override
public OwnerInfo addReadLockOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
if (owner == null)
{
return this;
}
if (owner == this.owner)
{
return this;
}
return new MultiOwnerInfo.ReadLockAndWriteOption(this.owner, this.owner, owner);
}
@Override
public OwnerInfo removeReadLockOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
if (owner == this.owner)
{
return cache.getSingleOwnerInfo(owner, SingleOwnerInfo.WriteOption.TYPE);
}
return this;
}
@Override
public OwnerInfo addWriteLockOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
if (owner == null)
{
return this;
}
if (owner == this.owner)
{
return cache.getSingleOwnerInfo(owner, SingleOwnerInfo.WriteLock.TYPE);
}
throw failure(owner, LockType.WRITE);
}
@Override
public OwnerInfo removeWriteLockOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
return this;
}
@Override
public OwnerInfo addWriteOptionOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
if (owner == null)
{
return this;
}
if (owner == this.owner)
{
return this;
}
throw failure(owner, LockType.OPTION);
}
@Override
public OwnerInfo removeWriteOptionOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
if (owner == this.owner)
{
return cache.getSingleOwnerInfo(owner, SingleOwnerInfo.ReadLock.TYPE);
}
return this;
}
@Override
protected void notifyLockTypes(Consumer<LockType> consumer)
{
consumer.accept(LockType.READ);
consumer.accept(LockType.OPTION);
}
}
/**
* @author Eike Stepper
*/
protected static final class WriteLock extends SingleOwnerInfo
{
public static final int TYPE = 3;
public WriteLock(CDOLockOwner owner)
{
super(owner);
}
@Override
public CDOLockOwner getWriteLockOwner()
{
return owner;
}
@Override
public OwnerInfo addReadLockOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
if (owner == null)
{
return this;
}
if (owner == this.owner)
{
return cache.getSingleOwnerInfo(owner, SingleOwnerInfo.ReadLockAndWriteLock.TYPE);
}
throw failure(owner, LockType.READ);
}
@Override
public OwnerInfo removeReadLockOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
return this;
}
@Override
public OwnerInfo addWriteLockOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
if (owner == null)
{
return this;
}
if (owner == this.owner)
{
return this;
}
throw failure(owner, LockType.WRITE);
}
@Override
public OwnerInfo removeWriteLockOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
if (owner == this.owner)
{
return NoOwnerInfo.INSTANCE;
}
return this;
}
@Override
public OwnerInfo addWriteOptionOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
if (owner == null)
{
return this;
}
throw failure(owner, LockType.OPTION);
}
@Override
public OwnerInfo removeWriteOptionOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
return this;
}
@Override
protected void notifyLockTypes(Consumer<LockType> consumer)
{
consumer.accept(LockType.WRITE);
}
}
/**
* @author Eike Stepper
*/
protected static final class WriteOption extends SingleOwnerInfo
{
public static final int TYPE = 4;
public WriteOption(CDOLockOwner owner)
{
super(owner);
}
@Override
public CDOLockOwner getWriteOptionOwner()
{
return owner;
}
@Override
public OwnerInfo addReadLockOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
if (owner == null)
{
return this;
}
if (owner == this.owner)
{
return cache.getSingleOwnerInfo(owner, SingleOwnerInfo.ReadLockAndWriteOption.TYPE);
}
return new MultiOwnerInfo.ReadLockAndWriteOption(this.owner, owner);
}
@Override
public OwnerInfo removeReadLockOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
return this;
}
@Override
public OwnerInfo addWriteLockOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
if (owner == null)
{
return this;
}
if (owner == this.owner)
{
return cache.getSingleOwnerInfo(owner, SingleOwnerInfo.WriteLock.TYPE);
}
throw failure(owner, LockType.WRITE);
}
@Override
public OwnerInfo removeWriteLockOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
return this;
}
@Override
public OwnerInfo addWriteOptionOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
if (owner == null)
{
return this;
}
if (owner == this.owner)
{
return this;
}
throw failure(owner, LockType.OPTION);
}
@Override
public OwnerInfo removeWriteOptionOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
if (owner == this.owner)
{
return NoOwnerInfo.INSTANCE;
}
return this;
}
@Override
protected void notifyLockTypes(Consumer<LockType> consumer)
{
consumer.accept(LockType.OPTION);
}
}
}
/**
* @author Eike Stepper
*/
protected static abstract class MultiOwnerInfo extends OwnerInfo
{
public MultiOwnerInfo()
{
}
@Override
public final CDOLockOwner getWriteLockOwner()
{
return null;
}
@Override
public final OwnerInfo addWriteLockOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
if (owner == null)
{
return this;
}
throw failure(owner, LockType.WRITE);
}
@Override
public final OwnerInfo removeWriteLockOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
return this;
}
protected final ObjectAlreadyLockedException failure(CDOLockOwner owner, LockType lockType)
{
String message = owner + " could not acquire the " + lockType + " lock because the READ lock is already acquired by " + getReadLockOwners();
CDOLockOwner writeOptionOwner = getWriteOptionOwner();
if (writeOptionOwner != null)
{
message += " and the OPTION lock is already acquired by " + writeOptionOwner;
}
return new ObjectAlreadyLockedException(message);
}
/**
* @author Eike Stepper
*/
protected static class ReadLock extends MultiOwnerInfo
{
protected final CDOLockOwner[] readLockOwners;
public ReadLock(CDOLockOwner... readLockOwners)
{
CheckUtil.checkArg(readLockOwners.length != 0, "No read lock owners");
this.readLockOwners = readLockOwners;
}
@Override
public Object getReadLockOwners()
{
return readLockOwners;
}
@Override
public OwnerInfo addReadLockOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
if (owner == null)
{
return this;
}
CDOLockOwner[] owners = readLockOwners;
if (CDOLockUtil.indexOf(owners, owner) == -1)
{
int oldLength = owners.length;
CDOLockOwner[] newOwners = new CDOLockOwner[oldLength + 1];
System.arraycopy(owners, 0, newOwners, 0, oldLength);
newOwners[oldLength] = owner;
return createMultiOwnerInfo(getWriteOptionOwner(), newOwners);
}
return this;
}
@Override
public OwnerInfo removeReadLockOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
CDOLockOwner[] owners = readLockOwners;
int index = CDOLockUtil.indexOf(owners, owner);
if (index != -1)
{
CDOLockOwner writeOptionOwner = getWriteOptionOwner();
int oldLength = owners.length;
if (oldLength == 1)
{
// Removal will leave no readLockOwners.
return cache.getSingleOwnerInfo(writeOptionOwner, SingleOwnerInfo.WriteOption.TYPE);
}
if (oldLength == 2)
{
// Removal will leave only one readLockOwner.
CDOLockOwner remainingReadLockOwner = readLockOwners[index == 0 ? 1 : 0];
// If that's also the writeOptionOwner convert to a SingleOwnerInfo.WriteOptionAndReadLock.
if (writeOptionOwner == remainingReadLockOwner)
{
return cache.getSingleOwnerInfo(writeOptionOwner, SingleOwnerInfo.ReadLockAndWriteOption.TYPE);
}
return createMultiOwnerInfo(writeOptionOwner, remainingReadLockOwner);
}
CDOLockOwner[] newOwners = new CDOLockOwner[oldLength - 1];
if (index > 0)
{
System.arraycopy(owners, 0, newOwners, 0, index);
}
int rest = oldLength - index - 1;
if (rest > 0)
{
System.arraycopy(owners, index + 1, newOwners, index, rest);
}
return createMultiOwnerInfo(writeOptionOwner, newOwners);
}
return this;
}
@Override
public CDOLockOwner getWriteOptionOwner()
{
return null;
}
@Override
public OwnerInfo addWriteOptionOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
if (owner == null)
{
return this;
}
return new MultiOwnerInfo.ReadLockAndWriteOption(owner, readLockOwners);
}
@Override
public OwnerInfo removeWriteOptionOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
return this;
}
@Override
public OwnerInfo removeOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner, Consumer<LockType> consumer)
{
OwnerInfo newInfo = removeReadLockOwner(cache, owner);
if (newInfo != this)
{
consumer.accept(LockType.READ);
}
return newInfo;
}
@Override
public boolean remapOwner(CDOLockOwner oldOwner, CDOLockOwner newOwner)
{
CDOLockOwner[] owners = readLockOwners;
int index = CDOLockUtil.indexOf(owners, oldOwner);
if (index != -1)
{
readLockOwners[index] = newOwner;
return true;
}
return false;
}
@Override
public String toString()
{
return super.toString() + "[readLockOwners=" + Arrays.asList(readLockOwners) + "]";
}
private static MultiOwnerInfo createMultiOwnerInfo(CDOLockOwner writeOptionOwner, CDOLockOwner... readLockOwners)
{
if (writeOptionOwner != null)
{
return new MultiOwnerInfo.ReadLockAndWriteOption(writeOptionOwner, readLockOwners);
}
return new MultiOwnerInfo.ReadLock(readLockOwners);
}
}
/**
* @author Eike Stepper
*/
protected static final class ReadLockAndWriteOption extends MultiOwnerInfo.ReadLock
{
private CDOLockOwner writeOptionOwner;
public ReadLockAndWriteOption(CDOLockOwner writeOptionOwner, CDOLockOwner... readLockOwners)
{
super(readLockOwners);
this.writeOptionOwner = writeOptionOwner;
}
@Override
public CDOLockOwner getWriteOptionOwner()
{
return writeOptionOwner;
}
@Override
public OwnerInfo addWriteOptionOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
if (owner == null)
{
return this;
}
if (owner == writeOptionOwner)
{
return this;
}
throw failure(owner, LockType.OPTION);
}
@Override
public OwnerInfo removeWriteOptionOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner)
{
if (owner == writeOptionOwner)
{
return new MultiOwnerInfo.ReadLock(readLockOwners);
}
return this;
}
@Override
public OwnerInfo removeOwner(CDOLockStateCacheImpl cache, CDOLockOwner owner, Consumer<LockType> consumer)
{
OwnerInfo newInfo = super.removeOwner(cache, owner, consumer);
if (owner == newInfo.getWriteOptionOwner())
{
consumer.accept(LockType.OPTION);
return newInfo.removeWriteOptionOwner(cache, owner);
}
return newInfo;
}
@Override
public boolean remapOwner(CDOLockOwner oldOwner, CDOLockOwner newOwner)
{
boolean remapped = super.remapOwner(oldOwner, newOwner);
if (writeOptionOwner == oldOwner)
{
writeOptionOwner = newOwner;
remapped = true;
}
return remapped;
}
@Override
public String toString()
{
return super.toString() + "[readLockOwners=" + Arrays.asList(readLockOwners) + ", writeOptionOwner=" + writeOptionOwner + "]";
}
}
}
/**
* @author Eike Stepper
*/
private static final class LockState extends AbstractCDOLockState
{
private static final Set<CDOLockOwner> NO_LOCK_OWNERS = Collections.emptySet();
private final CDOLockStateCacheImpl cache;
public LockState(Object lockedObject, CDOLockStateCacheImpl cache)
{
super(lockedObject);
this.cache = cache;
}
@Override
public final boolean isLocked(LockType lockType, CDOLockOwner lockOwner, boolean others)
{
OwnerInfo info = getInfo();
return info.isLocked(lockType, lockOwner, others);
}
@Override
public final Set<CDOLockOwner> getReadLockOwners()
{
OwnerInfo info = getInfo();
Object readLockOwners = info.getReadLockOwners();
if (readLockOwners == null)
{
return NO_LOCK_OWNERS;
}
if (readLockOwners.getClass() == ARRAY_CLASS)
{
CDOLockOwner[] owners = (CDOLockOwner[])readLockOwners;
Set<CDOLockOwner> result = new HashSet<>();
for (CDOLockOwner owner : owners)
{
result.add(owner);
}
return Collections.unmodifiableSet(result);
}
return Collections.singleton((CDOLockOwner)readLockOwners);
}
@Override
public final CDOLockOwner getWriteLockOwner()
{
OwnerInfo info = getInfo();
return info.getWriteLockOwner();
}
@Override
public final CDOLockOwner getWriteOptionOwner()
{
OwnerInfo info = getInfo();
return info.getWriteOptionOwner();
}
@Override
protected CDOLockDelta addReadOwner(CDOLockOwner owner)
{
if (cache.changeOwnerInfo(lockedObject, owner, (i, o) -> i.addReadLockOwner(cache, o)))
{
return CDOLockUtil.createLockDelta(lockedObject, LockType.READ, null, owner);
}
return null;
}
@Override
protected CDOLockDelta addWriteOwner(CDOLockOwner owner)
{
if (cache.changeOwnerInfo(lockedObject, owner, (i, o) -> i.addWriteLockOwner(cache, o)))
{
return CDOLockUtil.createLockDelta(lockedObject, LockType.WRITE, null, owner);
}
return null;
}
@Override
protected CDOLockDelta addOptionOwner(CDOLockOwner owner)
{
if (cache.changeOwnerInfo(lockedObject, owner, (i, o) -> i.addWriteOptionOwner(cache, o)))
{
return CDOLockUtil.createLockDelta(lockedObject, LockType.OPTION, null, owner);
}
return null;
}
@Override
protected CDOLockDelta removeReadOwner(CDOLockOwner owner)
{
if (cache.changeOwnerInfo(lockedObject, owner, (i, o) -> i.removeReadLockOwner(cache, o)))
{
return CDOLockUtil.createLockDelta(lockedObject, LockType.READ, owner, null);
}
return null;
}
@Override
protected CDOLockDelta removeWriteOwner(CDOLockOwner owner)
{
if (cache.changeOwnerInfo(lockedObject, owner, (i, o) -> i.removeWriteLockOwner(cache, o)))
{
return CDOLockUtil.createLockDelta(lockedObject, LockType.WRITE, owner, null);
}
return null;
}
@Override
protected CDOLockDelta removeOptionOwner(CDOLockOwner owner)
{
if (cache.changeOwnerInfo(lockedObject, owner, (i, o) -> i.removeWriteOptionOwner(cache, o)))
{
return CDOLockUtil.createLockDelta(lockedObject, LockType.OPTION, owner, null);
}
return null;
}
private OwnerInfo getInfo()
{
return cache.getOwnerInfo(lockedObject);
}
/**
* @deprecated Call {@link CDOLockStateCacheImpl#remapOwner(CDOBranch, CDOLockOwner, CDOLockOwner)} instead.
*/
@Deprecated
@Override
public CDOLockDelta[] remapOwner(CDOLockOwner oldOwner, CDOLockOwner newOwner)
{
throw new UnsupportedOperationException();
}
}
}