blob: 66b0d78951dc4b03c4627adb8d82ff7719aeeb8a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jackrabbit.oak.jcr.lock;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.jcr.InvalidItemStateException;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.lock.Lock;
import javax.jcr.lock.LockException;
import javax.jcr.lock.LockManager;
import org.apache.jackrabbit.oak.api.Tree.Status;
import org.apache.jackrabbit.oak.jcr.session.SessionContext;
import org.apache.jackrabbit.oak.jcr.delegate.NodeDelegate;
import org.apache.jackrabbit.oak.jcr.delegate.SessionDelegate;
import org.apache.jackrabbit.oak.jcr.session.operation.SessionOperation;
/**
* Simple lock manager implementation that just keeps track of a set of lock
* tokens and delegates all locking operations back to the {@link Session}
* and {@link Node} implementations.
*/
public class LockManagerImpl implements LockManager {
private final SessionContext sessionContext;
private final SessionDelegate delegate;
public LockManagerImpl(SessionContext sessionContext) {
this.sessionContext = sessionContext;
this.delegate = sessionContext.getSessionDelegate();
}
@Override @Nonnull
public String[] getLockTokens() throws RepositoryException {
return perform(new SessionOperation<String[]>() {
@Override @Nonnull
public String[] perform() {
Set<String> tokens = sessionContext.getOpenScopedLocks();
return tokens.toArray(new String[tokens.size()]);
}
});
}
@Override
public void addLockToken(final String lockToken)
throws RepositoryException {
try {
perform(new LockOperation<String>(sessionContext, lockToken) {
@Override
protected String perform(NodeDelegate node)
throws LockException {
if (node.holdsLock(false)) { // TODO: check ownership?
String token = node.getPath();
sessionContext.getOpenScopedLocks().add(token);
return null;
} else {
throw new LockException(
"Invalid lock token: " + lockToken);
}
}
});
} catch (IllegalArgumentException e) { // TODO: better exception
throw new LockException("Invalid lock token: " + lockToken);
}
}
@Override
public void removeLockToken(final String lockToken)
throws RepositoryException {
if (!perform(new SessionOperation<Boolean>() {
@Override @Nonnull
public Boolean perform() {
// TODO: name mapping?
return sessionContext.getOpenScopedLocks().remove(lockToken);
}
})) {
throw new LockException(
"Lock token " + lockToken + " is not held by this session");
}
}
@Override
public boolean isLocked(String absPath) throws RepositoryException {
return perform(new LockOperation<Boolean>(sessionContext, absPath) {
@Override
protected Boolean perform(NodeDelegate node) {
return node.isLocked();
}
});
}
@Override
public boolean holdsLock(String absPath) throws RepositoryException {
return perform(new LockOperation<Boolean>(sessionContext, absPath) {
@Override
protected Boolean perform(NodeDelegate node) {
return node.holdsLock(false);
}
});
}
@Override @Nonnull
public Lock getLock(String absPath) throws RepositoryException {
NodeDelegate lock = perform(
new LockOperation<NodeDelegate>(sessionContext, absPath) {
@Override
protected NodeDelegate perform(NodeDelegate node) {
return node.getLock();
}
});
if (lock != null) {
return new LockImpl(sessionContext, lock);
} else {
throw new LockException("Node " + absPath + " is not locked");
}
}
@Override @Nonnull
public Lock lock(
String absPath, final boolean isDeep, final boolean isSessionScoped,
long timeoutHint, String ownerInfo) throws RepositoryException {
return new LockImpl(sessionContext, perform(
new LockOperation<NodeDelegate>(sessionContext, absPath) {
@Override
protected NodeDelegate perform(NodeDelegate node)
throws RepositoryException {
if (node.getStatus() != Status.EXISTING) {
throw new InvalidItemStateException(
"Unable to lock a node with pending changes");
}
node.lock(isDeep);
String path = node.getPath();
if (isSessionScoped) {
sessionContext.getSessionScopedLocks().add(path);
} else {
sessionContext.getOpenScopedLocks().add(path);
}
session.refresh(true);
return node;
}
}));
}
@Override
public void unlock(String absPath) throws RepositoryException {
perform(new LockOperation<Void>(sessionContext, absPath) {
@Override
protected Void perform(NodeDelegate node)
throws RepositoryException {
String path = node.getPath();
if (sessionContext.getSessionScopedLocks().contains(path)
|| sessionContext.getOpenScopedLocks().contains(path)) {
node.unlock();
sessionContext.getSessionScopedLocks().remove(path);
sessionContext.getOpenScopedLocks().remove(path);
session.refresh(true);
return null;
} else {
throw new LockException("Not an owner of the lock " + path);
}
}
});
}
private <T> T perform(SessionOperation<T> operation)
throws RepositoryException {
return delegate.perform(operation);
}
}