From commits-return-2531-apmail-jackrabbit-commits-archive=jackrabbit.apache.org@jackrabbit.apache.org Wed Jul 12 13:35:12 2006 Return-Path: Delivered-To: apmail-jackrabbit-commits-archive@www.apache.org Received: (qmail 16220 invoked from network); 12 Jul 2006 13:35:11 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 12 Jul 2006 13:35:11 -0000 Received: (qmail 26853 invoked by uid 500); 12 Jul 2006 13:35:01 -0000 Delivered-To: apmail-jackrabbit-commits-archive@jackrabbit.apache.org Received: (qmail 26791 invoked by uid 500); 12 Jul 2006 13:35:01 -0000 Mailing-List: contact commits-help@jackrabbit.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@jackrabbit.apache.org Delivered-To: mailing list commits@jackrabbit.apache.org Received: (qmail 26680 invoked by uid 99); 12 Jul 2006 13:35:00 -0000 Received: from asf.osuosl.org (HELO asf.osuosl.org) (140.211.166.49) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 12 Jul 2006 06:35:00 -0700 X-ASF-Spam-Status: No, hits=-8.6 required=10.0 tests=ALL_TRUSTED,INFO_TLD,NO_REAL_NAME X-Spam-Check-By: apache.org Received-SPF: pass (asf.osuosl.org: local policy) Received: from [140.211.166.113] (HELO eris.apache.org) (140.211.166.113) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 12 Jul 2006 06:34:46 -0700 Received: by eris.apache.org (Postfix, from userid 65534) id 91AF21A9829; Wed, 12 Jul 2006 06:34:00 -0700 (PDT) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r421270 [7/23] - in /jackrabbit/trunk/contrib/spi: ./ commons/ commons/src/ commons/src/main/ commons/src/main/java/ commons/src/main/java/org/ commons/src/main/java/org/apache/ commons/src/main/java/org/apache/jackrabbit/ commons/src/main/... Date: Wed, 12 Jul 2006 13:33:27 -0000 To: commits@jackrabbit.apache.org From: angela@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20060712133400.91AF21A9829@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java?rev=421270&view=auto ============================================================================== --- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java (added) +++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java Wed Jul 12 06:33:19 2006 @@ -0,0 +1,959 @@ +/* + * 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.jcr2spi; + +import org.apache.jackrabbit.jcr2spi.nodetype.NodeTypeRegistryImpl; +import org.apache.jackrabbit.jcr2spi.nodetype.NodeTypeStorage; +import org.apache.jackrabbit.jcr2spi.nodetype.NodeTypeRegistry; +import org.apache.jackrabbit.jcr2spi.name.NamespaceStorage; +import org.apache.jackrabbit.jcr2spi.name.NamespaceRegistryImpl; +import org.apache.jackrabbit.jcr2spi.state.ItemState; +import org.apache.jackrabbit.jcr2spi.state.NoSuchItemStateException; +import org.apache.jackrabbit.jcr2spi.state.ItemStateException; +import org.apache.jackrabbit.jcr2spi.state.NodeState; +import org.apache.jackrabbit.jcr2spi.state.PropertyState; +import org.apache.jackrabbit.jcr2spi.state.ChangeLog; +import org.apache.jackrabbit.jcr2spi.state.UpdatableItemStateManager; +import org.apache.jackrabbit.jcr2spi.state.NodeReferences; +import org.apache.jackrabbit.jcr2spi.state.ItemStateManager; +import org.apache.jackrabbit.jcr2spi.state.CachingItemStateManager; +import org.apache.jackrabbit.jcr2spi.operation.OperationVisitor; +import org.apache.jackrabbit.jcr2spi.operation.AddNode; +import org.apache.jackrabbit.jcr2spi.operation.AddProperty; +import org.apache.jackrabbit.jcr2spi.operation.Clone; +import org.apache.jackrabbit.jcr2spi.operation.Copy; +import org.apache.jackrabbit.jcr2spi.operation.Move; +import org.apache.jackrabbit.jcr2spi.operation.Remove; +import org.apache.jackrabbit.jcr2spi.operation.SetMixin; +import org.apache.jackrabbit.jcr2spi.operation.SetPropertyValue; +import org.apache.jackrabbit.jcr2spi.operation.ReorderNodes; +import org.apache.jackrabbit.jcr2spi.operation.Operation; +import org.apache.jackrabbit.jcr2spi.operation.Checkout; +import org.apache.jackrabbit.jcr2spi.operation.Checkin; +import org.apache.jackrabbit.jcr2spi.operation.Update; +import org.apache.jackrabbit.jcr2spi.operation.Restore; +import org.apache.jackrabbit.jcr2spi.operation.ResolveMergeConflict; +import org.apache.jackrabbit.jcr2spi.operation.Merge; +import org.apache.jackrabbit.jcr2spi.operation.LockOperation; +import org.apache.jackrabbit.jcr2spi.operation.LockRefresh; +import org.apache.jackrabbit.jcr2spi.operation.LockRelease; +import org.apache.jackrabbit.jcr2spi.operation.AddLabel; +import org.apache.jackrabbit.jcr2spi.operation.RemoveLabel; +import org.apache.jackrabbit.jcr2spi.security.AccessManager; +import org.apache.jackrabbit.jcr2spi.observation.InternalEventListener; +import org.apache.jackrabbit.util.IteratorHelper; +import org.apache.jackrabbit.name.QName; +import org.apache.jackrabbit.spi.RepositoryService; +import org.apache.jackrabbit.spi.SessionInfo; +import org.apache.jackrabbit.spi.NodeId; +import org.apache.jackrabbit.spi.EventListener; +import org.apache.jackrabbit.spi.IdFactory; +import org.apache.jackrabbit.spi.LockInfo; +import org.apache.jackrabbit.spi.QueryInfo; +import org.apache.jackrabbit.spi.QNodeDefinition; +import org.apache.jackrabbit.spi.QNodeTypeDefinitionIterator; +import org.apache.jackrabbit.spi.EventIterator; +import org.apache.jackrabbit.spi.Event; +import org.apache.jackrabbit.spi.ItemId; +import org.apache.jackrabbit.spi.PropertyId; +import org.apache.jackrabbit.spi.NodeInfo; +import org.apache.jackrabbit.spi.PropertyInfo; +import org.apache.jackrabbit.name.Path; +import org.apache.jackrabbit.spi.QNodeTypeDefinition; +import org.apache.jackrabbit.spi.IdIterator; +import org.apache.jackrabbit.value.QValue; +import org.slf4j.LoggerFactory; +import org.slf4j.Logger; + +import javax.jcr.RepositoryException; +import javax.jcr.NamespaceRegistry; +import javax.jcr.NamespaceException; +import javax.jcr.UnsupportedRepositoryOperationException; +import javax.jcr.AccessDeniedException; +import javax.jcr.PathNotFoundException; +import javax.jcr.PropertyType; +import javax.jcr.ItemNotFoundException; +import javax.jcr.NoSuchWorkspaceException; +import javax.jcr.ItemExistsException; +import javax.jcr.Repository; +import javax.jcr.InvalidItemStateException; +import javax.jcr.MergeException; +import javax.jcr.Session; +import javax.jcr.version.VersionException; +import javax.jcr.lock.LockException; +import javax.jcr.nodetype.NoSuchNodeTypeException; +import javax.jcr.nodetype.ConstraintViolationException; + +import java.util.List; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Properties; +import java.util.Set; +import java.util.HashSet; +import java.util.Collection; +import java.util.Collections; +import java.io.InputStream; +import java.io.IOException; + +/** + * WorkspaceManager... + */ +public class WorkspaceManager implements UpdatableItemStateManager, NamespaceStorage, NodeTypeStorage, AccessManager { + + private static Logger log = LoggerFactory.getLogger(WorkspaceManager.class); + + private final RepositoryService service; + private final SessionInfo sessionInfo; + + // TODO: TO-BE-FIXED. Major refactoring of caching mechanism with change to SPI ids + private final CachingItemStateManager cache; + + // TODO: TO-BE-FIXED. With SPI_ItemId rootId must not be stored separately + private final NodeId rootNodeId; + + private final NamespaceRegistryImpl nsRegistry; + private final NodeTypeRegistry ntRegistry; + + /** + * This is the event listener that listens on the repository service + * for external changes. If null then the underlying repository + * service does not support observation. + */ + private final EventListener externalChangeListener; + + /** + * List of event listener that are set on this WorkspaceManager to get + * notifications about local and external changes. + */ + private Set listeners = new HashSet(); + + public WorkspaceManager(RepositoryService service, SessionInfo sessionInfo) throws RepositoryException { + this.service = service; + this.sessionInfo = sessionInfo; + + cache = new CachingItemStateManager(new WorkspaceItemStateManager()); + addEventListener(cache); + + nsRegistry = createNamespaceRegistry(); + ntRegistry = createNodeTypeRegistry(nsRegistry); + rootNodeId = createRootNodeId(); + externalChangeListener = createChangeListener(); + } + + public NamespaceRegistryImpl getNamespaceRegistryImpl() { + return nsRegistry; + } + + public NodeTypeRegistry getNodeTypeRegistry() { + return ntRegistry; + } + + public NodeId getRootNodeId() { + return rootNodeId; + } + + public String[] getWorkspaceNames() throws RepositoryException { + // TODO: review + return service.getWorkspaceNames(sessionInfo); + } + + public IdFactory getIdFactory() { + return service.getIdFactory(); + } + + public LockInfo getLockInfo(NodeId nodeId) throws LockException, RepositoryException { + return service.getLockInfo(sessionInfo, nodeId); + } + + public String[] getLockTokens() { + return sessionInfo.getLockTokens(); + } + + /** + * This method always succeeds. + * This is not compliant to the requirements for {@link Session#addLockToken(String)} + * as defined by JSR170, which defines that at most one single Session + * may contain the same lock token. However, with SPI it is not possible + * to determine, whether another session holds the lock, nor can the client + * determine, which lock this token belongs to. The latter would be + * necessary in order to build the 'Lock' object properly. + * + * TODO: check if throwing an exception would be more appropriate + * + * @param lt + * @throws LockException + * @throws RepositoryException + */ + public void addLockToken(String lt) throws LockException, RepositoryException { + sessionInfo.addLockToken(lt); + /* + // TODO: JSR170 defines that a token can be present with one session only. + // however, we cannot find out about another session holding the lock. + // and neither knows the server, which session is holding a lock token. + // TODO: check if throwing would be more appropriate + throw new UnsupportedRepositoryOperationException("Session.addLockToken is not possible on the client."); + */ + } + + /** + * Tries to remove the given token from the SessionInfo. If the + * SessionInfo does not contains the specified token, this method returns + * silently.
+ * Note, that any restriction regarding removal of lock tokens must be asserted + * before this method is called. + * + * @param lt + * @throws LockException + * @throws RepositoryException + */ + public void removeLockToken(String lt) throws LockException, RepositoryException { + sessionInfo.removeLockToken(lt); + } + + public String[] getSupportedQueryLanguages() throws RepositoryException { + // TODO: review + return service.getSupportedQueryLanguages(sessionInfo); + } + + public QueryInfo executeQuery(String statement, String language) + throws RepositoryException { + return service.executeQuery(sessionInfo, statement, language); + } + + /** + * Sets the InternalEventListener that gets notifications about + * local and external changes. + * @param listener the new listener. + */ + public void addEventListener(InternalEventListener listener) { + listeners.add(listener); + } + + /** + * + * @param listener + */ + public void removeEventListener(InternalEventListener listener) { + listeners.remove(listener); + } + //-------------------------------------------------------------------------- + + + private NamespaceRegistryImpl createNamespaceRegistry() throws RepositoryException { + return new NamespaceRegistryImpl(this, service.getRegisteredNamespaces(sessionInfo)); + } + + private NodeTypeRegistry createNodeTypeRegistry(NamespaceRegistry nsRegistry) throws RepositoryException { + QNodeDefinition rootNodeDef = service.getNodeDefinition(sessionInfo, service.getRootId(sessionInfo)); + QNodeTypeDefinitionIterator it = service.getNodeTypeDefinitions(sessionInfo); + List ntDefs = new ArrayList(); + while (it.hasNext()) { + ntDefs.add(it.nextDefinition()); + } + return NodeTypeRegistryImpl.create(ntDefs, this, rootNodeDef, nsRegistry); + } + + /** + * Creates and registers an EventListener on the RepositoryService that + * listens for external changes. + * + * @return the listener or null if the underlying + * RepositoryService does not support observation. + * @throws RepositoryException if an error occurs while registering the + * event listener. + */ + private EventListener createChangeListener() throws RepositoryException { + Properties descriptors = service.getRepositoryDescriptors(); + String desc = descriptors.getProperty(Repository.OPTION_OBSERVATION_SUPPORTED); + EventListener l = null; + if (Boolean.getBoolean(desc)) { + l = new EventListener() { + public void onEvent(EventIterator events) { + onEventReceived(null, events, false); // external + } + }; + int allTypes = Event.NODE_ADDED | Event.NODE_REMOVED | + Event.PROPERTY_ADDED | Event.PROPERTY_CHANGED | Event.PROPERTY_REMOVED; + // register for all events + service.addEventListener(sessionInfo, service.getRootId(sessionInfo), + l, allTypes, true, null, null); + } + return l; + } + + private NodeId createRootNodeId() throws RepositoryException { + return service.getRootId(sessionInfo); + } + + //---------------------------------------------------< ItemStateManager >--- + /** + * @inheritDoc + */ + public ItemState getItemState(ItemId id) throws NoSuchItemStateException, ItemStateException { + // retrieve through cache + synchronized (cache) { + return cache.getItemState(id); + } + } + + /** + * @inheritDoc + */ + public boolean hasItemState(ItemId id) { + synchronized (cache) { + return cache.hasItemState(id); + } + } + + /** + * @inheritDoc + */ + public NodeReferences getNodeReferences(NodeId id) throws NoSuchItemStateException, ItemStateException { + synchronized (cache) { + return cache.getNodeReferences(id); + } + } + + /** + * @inheritDoc + */ + public boolean hasNodeReferences(NodeId id) { + synchronized (cache) { + return cache.hasNodeReferences(id); + } + } + + //------ updatable -:>> review --------------------------------------------- + /** + * Creates a new Batch from the single workspace operation and + * executes it. + * + * @see UpdatableItemStateManager#execute(Operation) + */ + public void execute(Operation operation) throws RepositoryException { + new Batch(sessionInfo).execute(operation); + } + + /** + * Creates a new Batch from the given Batch and + * executes it. + * + * @param changes + * @throws RepositoryException + */ + public void execute(ChangeLog changes) throws RepositoryException { + new Batch(sessionInfo).execute(changes); + } + + public void store(ItemState state) throws IllegalStateException { + } + + public void destroy(ItemState state) throws IllegalStateException { + } + + public void dispose() { + if (externalChangeListener != null) { + try { + service.removeEventListener(sessionInfo, rootNodeId, externalChangeListener); + } catch (RepositoryException e) { + log.warn("exception while disposing workspace manager: " + e); + } + } + } + //------------------------------------------------------< AccessManager >--- + + // TODO: method can be removed, if jcr2spi uses spi-ids as well + public boolean isGranted(NodeId parentId, Path relPath, String[] actions) throws ItemNotFoundException, RepositoryException { + // TODO: 'createNodeId' is basically wrong since isGranted is unspecific for any item. + ItemId id = getIdFactory().createNodeId(parentId, relPath); + return isGranted(id, actions); + } + + public boolean isGranted(ItemId id, String[] actions) throws ItemNotFoundException, RepositoryException { + return service.isGranted(sessionInfo, id, actions); + } + + public boolean canRead(ItemId id) throws ItemNotFoundException, RepositoryException { + return service.isGranted(sessionInfo, id, AccessManager.READ); + } + + public boolean canRemove(ItemId id) throws ItemNotFoundException, RepositoryException { + return service.isGranted(sessionInfo, id, AccessManager.REMOVE); + } + + public boolean canAccess(String workspaceName) throws NoSuchWorkspaceException, RepositoryException { + String[] wspNames = getWorkspaceNames(); + for (int i = 0; i < wspNames.length; i++) { + if (wspNames[i].equals(wspNames)) { + return true; + } + } + return false; + } + + //---------------------------------------------------------< XML import >--- + public void importXml(NodeId parentId, InputStream xmlStream, int uuidBehaviour) throws RepositoryException, LockException, ConstraintViolationException, AccessDeniedException, UnsupportedRepositoryOperationException, ItemExistsException, VersionException { + service.importXml(sessionInfo, parentId, xmlStream, uuidBehaviour); + } + + //---------------------------------------------------< NamespaceStorage >--- + /** + * @inheritDoc + */ + public void registerNamespace(String prefix, String uri) throws NamespaceException, UnsupportedRepositoryOperationException, AccessDeniedException, RepositoryException { + service.registerNamespace(sessionInfo, prefix, uri); + } + + /** + * @inheritDoc + */ + public void unregisterNamespace(String uri) throws NamespaceException, UnsupportedRepositoryOperationException, AccessDeniedException, RepositoryException { + service.unregisterNamespace(sessionInfo, uri); + } + + //----------------------------------------------------< NodetypeStorage >--- + /** + * @inheritDoc + */ + public void registerNodeTypes(QNodeTypeDefinition[] nodeTypeDefs) throws NoSuchNodeTypeException, UnsupportedRepositoryOperationException, RepositoryException { + service.registerNodeTypes(sessionInfo, nodeTypeDefs); + } + + /** + * @inheritDoc + */ + public void reregisterNodeTypes(QNodeTypeDefinition[] nodeTypeDefs) throws NoSuchNodeTypeException, UnsupportedRepositoryOperationException, RepositoryException { + service.reregisterNodeTypes(sessionInfo, nodeTypeDefs); + } + + /** + * @inheritDoc + */ + public void unregisterNodeTypes(QName[] nodeTypeNames) throws NoSuchNodeTypeException, UnsupportedRepositoryOperationException, RepositoryException { + service.unregisterNodeTypes(sessionInfo, nodeTypeNames); + } + + //-------------------------------------------------------------------------- + + /** + * Called when local or external events occured. This method is called after + * changes have been applied to the repository. + * + * @param changeLog + * @param events the events. + * @param isLocal true if changes were local. + */ + private void onEventReceived(ChangeLog changeLog, EventIterator events, boolean isLocal) { + if (changeLog != null) { + // use current change log for notification + changeLog.persisted(); + } + + // notify listener + // need to copy events into a list because we notify multiple listeners + List eventList = new ArrayList(); + while (events.hasNext()) { + Event e = events.nextEvent(); + eventList.add(e); + } + + InternalEventListener[] lstnrs = (InternalEventListener[]) listeners.toArray(new InternalEventListener[listeners.size()]); + for (int i = 0; i < lstnrs.length; i++) { + lstnrs[i].onEvent(new EventIteratorImpl(eventList), isLocal); + } + } + + /** + * Build a new NodeState from the information retrieved + * from the RepositoryService. + * + * @param id node id + * @return node state + * @throws NoSuchItemStateException + * @throws ItemStateException + */ + private NodeState getNodeState(NodeId id) + throws NoSuchItemStateException, ItemStateException { + try { + NodeInfo info = service.getNodeInfo(sessionInfo, id); + QName ntName = info.getNodetype(); + NodeId parentId = (info.getParentId() != null) ? info.getParentId() : null; + + // build the node state + // NOTE: unable to retrieve definitionId -> needs to be retrieved + // by the itemManager upon Node creation. + NodeState state = new NodeState(info.getId(), ntName, parentId, ItemState.STATUS_EXISTING, false, getIdFactory()); + // set mixin nodetypes + state.setMixinTypeNames(info.getMixins()); + // references to child items + + for (IdIterator it = info.getNodeIds(); it.hasNext(); ) { + NodeInfo childInfo = service.getNodeInfo(sessionInfo, (org.apache.jackrabbit.spi.NodeId)it.nextId()); + NodeId childId = childInfo.getId(); + state.addChildNodeEntry(childInfo.getQName(), childId); + } + + for (IdIterator it = info.getPropertyIds(); it.hasNext(); ) { + PropertyId pId = (PropertyId) it.nextId(); + state.addPropertyName(pId.getQName()); + } + + // copied from local-state-mgr TODO... check + // register as listener + // TODO check if needed + //state.addListener(this); + return state; + } catch (PathNotFoundException e) { + throw new NoSuchItemStateException(e.getMessage(), e); + } catch (RepositoryException e) { + throw new ItemStateException(e.getMessage(), e); + } + } + + /** + * Build a new PropertyState from the information retrieved + * from the RepositoryService. + * + * @param id property id + * @return property state + * @throws NoSuchItemStateException + * @throws ItemStateException + */ + private PropertyState getPropertyState(PropertyId id) + throws NoSuchItemStateException, ItemStateException { + try { + PropertyInfo info = service.getPropertyInfo(sessionInfo, id); + + // build the PropertyState + // NOTE: unable to retrieve definitionId -> needs to be retrieved + // by the itemManager upon Property creation. + PropertyState state = new PropertyState(info.getId(), ItemState.STATUS_EXISTING, false); + state.setMultiValued(info.isMultiValued()); + state.setType(info.getType()); + QValue[] qValues; + if (info.getType() == PropertyType.BINARY) { + InputStream[] ins = info.getValuesAsStream(); + qValues = new QValue[ins.length]; + for (int i = 0; i < ins.length; i++) { + qValues[i] = QValue.create(ins[i]); + } + } else { + String[] str = info.getValues(); + qValues = new QValue[str.length]; + for (int i = 0; i < str.length; i++) { + qValues[i] = QValue.create(str[i], info.getType()); + } + } + + state.setValues(qValues); + + // register as listener + // TODO check if needed + // state.addListener(this); + return state; + } catch (PathNotFoundException e) { + throw new NoSuchItemStateException(e.getMessage(), e); + } catch (RepositoryException e) { + throw new ItemStateException(e.getMessage(), e); + } catch (IOException e) { + throw new ItemStateException(e.getMessage(), e); + } + } + + /** + * Executes a sequence of operations on the repository service within + * a given SessionInfo. + */ + private final class Batch implements OperationVisitor { + + /** + * The session info for all operations in this batch. + */ + private final SessionInfo sessionInfo; + + private org.apache.jackrabbit.spi.Batch batch; + private EventIterator events; + + private Batch(SessionInfo sessionInfo) { + this.sessionInfo = sessionInfo; + } + + /** + * Executes the operations on the repository service. + */ + private void execute(ChangeLog changeLog) throws RepositoryException, ConstraintViolationException, AccessDeniedException, ItemExistsException, NoSuchNodeTypeException, UnsupportedRepositoryOperationException, VersionException { + try { + batch = service.createBatch(sessionInfo); + Iterator it = changeLog.getOperations(); + while (it.hasNext()) { + Operation op = (Operation) it.next(); + log.info("executing: " + op); + op.accept(this); + } + } finally { + if (batch != null) { + EventIterator events = service.submit(batch); + onEventReceived(changeLog, events, true); + // reset batch field + batch = null; + } + } + } + + /** + * Executes the operations on the repository service. + */ + private void execute(Operation workspaceOperation) throws RepositoryException, ConstraintViolationException, AccessDeniedException, ItemExistsException, NoSuchNodeTypeException, UnsupportedRepositoryOperationException, VersionException { + boolean success = false; + try { + log.info("executing: " + workspaceOperation); + workspaceOperation.accept(this); + success = true; + } finally { + if (success && events != null) { + onEventReceived(null, events, true); + } + } + } + //-----------------------< OperationVisitor >--------------------------- + + public void visit(AddNode operation) throws RepositoryException { + batch.addNode(operation.getParentId(), operation.getNodeName(), operation.getNodeTypeName(), operation.getUuid()); + } + + public void visit(AddProperty operation) throws RepositoryException { + org.apache.jackrabbit.spi.NodeId parentId = operation.getParentId(); + QName propertyName = operation.getPropertyName(); + int type = operation.getPropertyType(); + if (operation.isMultiValued()) { + QValue[] values = operation.getValues(); + if (type == PropertyType.BINARY) { + InputStream[] ins = new InputStream[values.length]; + for (int i = 0; i < values.length; i++) { + ins[i] = values[i].getStream(); + } + batch.addProperty(parentId, propertyName, ins, type); + } else { + String[] strs = new String[values.length]; + for (int i = 0; i < values.length; i++) { + strs[i] = values[i].getString(); + } + batch.addProperty(parentId, propertyName, strs, type); + } + } else { + QValue value = operation.getValues()[0]; + if (type == PropertyType.BINARY) { + batch.addProperty(parentId, propertyName, value.getStream(), type); + } else { + batch.addProperty(parentId, propertyName, value.getString(), type); + } + } + } + + public void visit(Clone operation) throws NoSuchWorkspaceException, LockException, ConstraintViolationException, AccessDeniedException, ItemExistsException, UnsupportedRepositoryOperationException, VersionException, RepositoryException { + events = service.clone(sessionInfo, operation.getWorkspaceName(), operation.getNodeId(), operation.getDestinationParentId(), operation.getDestinationName(), operation.isRemoveExisting()); + } + + public void visit(Copy operation) throws NoSuchWorkspaceException, LockException, ConstraintViolationException, AccessDeniedException, ItemExistsException, UnsupportedRepositoryOperationException, VersionException, RepositoryException { + events = service.copy(sessionInfo, operation.getWorkspaceName(), operation.getNodeId(), operation.getDestinationParentId(), operation.getDestinationName()); + } + + public void visit(Move operation) throws LockException, ConstraintViolationException, AccessDeniedException, ItemExistsException, UnsupportedRepositoryOperationException, VersionException, RepositoryException { + if (batch == null) { + events = service.move(sessionInfo, operation.getNodeId(), operation.getDestinationParentId(), operation.getDestinationName()); + } else { + batch.move(operation.getNodeId(), operation.getDestinationParentId(), operation.getDestinationName()); + } + } + + public void visit(Update operation) throws NoSuchWorkspaceException, AccessDeniedException, LockException, InvalidItemStateException, RepositoryException { + events = service.update(sessionInfo, operation.getNodeId(), operation.getSourceWorkspaceName()); + } + + public void visit(Remove operation) throws RepositoryException { + batch.remove(operation.getRemoveId()); + } + + public void visit(SetMixin operation) throws RepositoryException { + batch.setMixins(operation.getNodeId(), operation.getMixinNames()); + } + + public void visit(SetPropertyValue operation) throws RepositoryException { + org.apache.jackrabbit.spi.PropertyId id = operation.getPropertyId(); + int type = operation.getPropertyType(); + if (operation.isMultiValued()) { + QValue[] values = operation.getValues(); + if (type == PropertyType.BINARY) { + InputStream[] ins = new InputStream[values.length]; + for (int i = 0; i < values.length; i++) { + ins[i] = values[i].getStream(); + } + batch.setValue(id, ins, type); + } else { + String[] strs = new String[values.length]; + for (int i = 0; i < values.length; i++) { + strs[i] = values[i].getString(); + } + batch.setValue(id, strs, type); + } + } else { + QValue value = operation.getValues()[0]; + if (operation.getPropertyType() == PropertyType.BINARY) { + batch.setValue(id, value.getStream(), type); + } else { + batch.setValue(id, value.getString(), type); + } + } + } + + public void visit(ReorderNodes operation) throws RepositoryException { + batch.reorderNodes(operation.getParentId(), operation.getInsertNodeId(), operation.getBeforeNodeId()); + } + + public void visit(Checkout operation) throws UnsupportedRepositoryOperationException, LockException, RepositoryException { + events = service.checkout(sessionInfo, operation.getNodeId()); + } + + public void visit(Checkin operation) throws UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException { + events = service.checkin(sessionInfo, operation.getNodeId()); + } + + public void visit(Restore operation) throws VersionException, PathNotFoundException, ItemExistsException, UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException { + NodeId nId = operation.getNodeId(); + NodeId[] versionIds = operation.getVersionIds(); + NodeId[] vIds = new NodeId[versionIds.length]; + for (int i = 0; i < vIds.length; i++) { + vIds[i] = versionIds[i]; + } + + if (nId == null) { + events = service.restore(sessionInfo, vIds, operation.removeExisting()); + } else { + if (vIds.length > 1) { + throw new IllegalArgumentException("Restore from a single node must specify but one single Version."); + } + events = service.restore(sessionInfo, nId, vIds[0], operation.removeExisting()); + } + } + + public void visit(Merge operation) throws NoSuchWorkspaceException, AccessDeniedException, MergeException, LockException, InvalidItemStateException, RepositoryException { + events = service.merge(sessionInfo, operation.getNodeId(), operation.getSourceWorkspaceName(), operation.bestEffort()); + // todo: improve.... inform operation about modified items (build mergefailed iterator) + operation.getEventListener().onEvent(events, true); + } + + public void visit(ResolveMergeConflict operation) throws VersionException, InvalidItemStateException, UnsupportedRepositoryOperationException, RepositoryException { + try { + NodeId nId = operation.getNodeId(); + NodeId vId = operation.getVersionId(); + + PropertyState mergeFailedState = getPropertyState(getIdFactory().createPropertyId(nId, QName.JCR_MERGEFAILED)); + QValue[] vs = mergeFailedState.getValues(); + + NodeId[] mergeFailedIds = new NodeId[vs.length - 1]; + for (int i = 0, j = 0; i < vs.length; i++) { + NodeId id = getIdFactory().createNodeId(vs[i].getString()); + if (!id.equals(vId)) { + mergeFailedIds[j] = id; + j++; + } + // else: the version id is being solved by this call and not + // part of 'jcr:mergefailed' any more + } + + PropertyState predecessorState = getPropertyState(getIdFactory().createPropertyId(nId, QName.JCR_PREDECESSORS)); + vs = predecessorState.getValues(); + + boolean resolveDone = operation.resolveDone(); + int noOfPredecessors = (resolveDone) ? vs.length + 1 : vs.length; + NodeId[] predecessorIds = new NodeId[noOfPredecessors]; + + int i = 0; + while (i < vs.length) { + predecessorIds[i] = getIdFactory().createNodeId(vs[i].getString()); + i++; + } + if (resolveDone) { + predecessorIds[i] = vId; + } + events = service.resolveMergeConflict(sessionInfo, nId, mergeFailedIds, predecessorIds); + } catch (ItemStateException e) { + // should not occur. + throw new RepositoryException(e); + } + } + + public void visit(LockOperation operation) throws AccessDeniedException, InvalidItemStateException, UnsupportedRepositoryOperationException, LockException, RepositoryException { + events = service.lock(sessionInfo, operation.getNodeId(), operation.isDeep()); + } + + public void visit(LockRefresh operation) throws AccessDeniedException, InvalidItemStateException, UnsupportedRepositoryOperationException, LockException, RepositoryException { + events = service.refreshLock(sessionInfo, operation.getNodeId()); + } + + public void visit(LockRelease operation) throws AccessDeniedException, InvalidItemStateException, UnsupportedRepositoryOperationException, LockException, RepositoryException { + events = service.unlock(sessionInfo, operation.getNodeId()); + } + + public void visit(AddLabel operation) throws VersionException, RepositoryException { + events = service.addVersionLabel(sessionInfo, operation.getVersionHistoryId(), operation.getVersionId(), operation.getLabel(), operation.moveLabel()); + } + + public void visit(RemoveLabel operation) throws VersionException, RepositoryException { + events = service.removeVersionLabel(sessionInfo, operation.getVersionHistoryId(), operation.getVersionId(), operation.getLabel()); + } + } + + private static final class EventIteratorImpl extends IteratorHelper implements EventIterator { + + public EventIteratorImpl(Collection c) { + super(c); + } + + public Event nextEvent() { + return (Event) next(); + } + } + + /** + * NodeReferences represents the references (i.e. properties of + * type REFERENCE) to a particular node (denoted by its uuid). + */ + public class NodeReferencesImpl implements NodeReferences { + + /** + * identifier of this NodeReferences instance. + */ + private NodeId id; + + /** + * list of PropertyId's (i.e. the id's of the properties that refer to + * the target node denoted by id.getTargetId()). + *

+ * note that the list can contain duplicate entries because a specific + * REFERENCE property can contain multiple references (if it's multi-valued) + * to potentially the same target node. + */ + private ArrayList references = new ArrayList(); + + /** + * Package private constructor + * + * @param id + */ + private NodeReferencesImpl(NodeId id) { + this.id = id; + } + + //-------------------------------------------------< NodeReferences >--- + /** + * Returns the identifier of this node references object. + * + * @return the id of this node references object. + */ + public NodeId getId() { + return id; + } + + /** + * Returns a flag indicating whether this object holds any references + * + * @return true if this object holds references, + * false otherwise + */ + public boolean hasReferences() { + return !references.isEmpty(); + } + + /** + * @return the list of references + */ + public List getReferences() { + return Collections.unmodifiableList(references); + } + + //--------------------------------------------------------< private >--- + /** + * @param refId + */ + private void addReference(PropertyId refId) { + references.add(refId); + } + + /** + * @param refId + * @return true if the reference was removed; + * false otherwise. + */ + private boolean removeReference(PropertyId refId) { + return references.remove(refId); + } + } + + public class WorkspaceItemStateManager implements ItemStateManager { + + public ItemState getItemState(ItemId id) throws NoSuchItemStateException, ItemStateException { + ItemState state; + if (id.denotesNode()) { + state = getNodeState((NodeId) id); + } else { + state = getPropertyState((PropertyId) id); + } + return state; + } + + public boolean hasItemState(ItemId id) { + try { + return service.exists(sessionInfo, id); + } catch (RepositoryException e) { + log.error(e.getMessage()); + return false; + } + } + + public NodeReferences getNodeReferences(NodeId id) + throws NoSuchItemStateException, ItemStateException { + try { + NodeReferencesImpl nrefs = new NodeReferencesImpl(id); + NodeInfo info = service.getNodeInfo(sessionInfo, id); + PropertyId[] refs = info.getReferences(); + for (int i = 0; i < refs.length; i++) { + PropertyInfo pInfo = service.getPropertyInfo(sessionInfo, refs[i]); + nrefs.addReference(pInfo.getId()); + } + return nrefs; + } catch (PathNotFoundException e) { + log.error(e.getMessage()); + throw new NoSuchItemStateException(e.getMessage(), e); + } catch (RepositoryException e) { + throw new ItemStateException(e.getMessage(), e); + } + } + + public boolean hasNodeReferences(NodeId id) { + try { + NodeInfo info = service.getNodeInfo(sessionInfo, id); + return info.getReferences().length > 0; + } catch (PathNotFoundException e) { + log.error(e.getMessage()); + } catch (RepositoryException e) { + log.error(e.getMessage()); + } + return false; + } + } +} Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/XASession.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/XASession.java?rev=421270&view=auto ============================================================================== --- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/XASession.java (added) +++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/XASession.java Wed Jul 12 06:33:19 2006 @@ -0,0 +1,44 @@ +/* + * 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.jcr2spi; + +import javax.jcr.Session; +import javax.transaction.xa.XAResource; + +/** + * The XASession interface extends the capability of + * Session by adding access to a JCR repository's support for + * the Java Transaction API (JTA). + *

+ * This support takes the form of a javax.transaction.xa.XAResource + * object. The functionality of this object closely resembles that defined by + * the standard X/Open XA Resource interface. + *

+ * This interface is used by the transaction manager; an application does not + * use it directly. + */ +public interface XASession extends Session { + + /** + * Retrieves an XAResource object that the transaction manager + * will use to manage this XASession object's participation in + * a distributed transaction. + * + * @return the XAResource object. + */ + XAResource getXAResource(); +} Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/XASession.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/XASession.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/XASessionImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/XASessionImpl.java?rev=421270&view=auto ============================================================================== --- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/XASessionImpl.java (added) +++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/XASessionImpl.java Wed Jul 12 06:33:19 2006 @@ -0,0 +1,60 @@ +/* + * 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.jcr2spi; + +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import org.apache.jackrabbit.spi.XASessionInfo; +import org.apache.jackrabbit.jcr2spi.config.RepositoryConfig; + +import javax.transaction.xa.XAResource; + +/** + * XASessionImpl extends the regular session implementation with + * access to the XAResource. + */ +public class XASessionImpl extends SessionImpl implements XASession { + + /** + * The XASessionInfo of this SessionImpl. + */ + private final XASessionInfo sessionInfo; + + /** + * Creates a new XASessionImpl. + * + * @param repository the repository instance associated with this session. + * @param sessionInfo the session info. + * @param service the underlying repository service. + * @throws RepositoryException if an error occurs while creating a session. + */ + XASessionImpl(XASessionInfo sessionInfo, Repository repository, + RepositoryConfig config) throws RepositoryException { + super(sessionInfo, repository, config); + this.sessionInfo = sessionInfo; + } + + //--------------------------------< XASession >----------------------------- + + /** + * @inheritDoc + */ + public XAResource getXAResource() { + return sessionInfo.getXAResource(); + } + +} Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/XASessionImpl.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/XASessionImpl.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/ZombieHierarchyManager.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/ZombieHierarchyManager.java?rev=421270&view=auto ============================================================================== --- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/ZombieHierarchyManager.java (added) +++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/ZombieHierarchyManager.java Wed Jul 12 06:33:19 2006 @@ -0,0 +1,136 @@ +/* + * 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.jcr2spi; + +import org.apache.jackrabbit.jcr2spi.state.ItemState; +import org.apache.jackrabbit.jcr2spi.state.ItemStateException; +import org.apache.jackrabbit.jcr2spi.state.ItemStateManager; +import org.apache.jackrabbit.jcr2spi.state.NoSuchItemStateException; +import org.apache.jackrabbit.jcr2spi.state.NodeState; +import org.apache.jackrabbit.name.NamespaceResolver; +import org.apache.jackrabbit.spi.ItemId; +import org.apache.jackrabbit.name.QName; +import org.apache.jackrabbit.spi.NodeId; + +import java.util.Iterator; + +/** + * HierarchyManager implementation that is also able to + * build/resolve paths of those items that have been moved or removed + * (i.e. moved to the attic). + */ +public class ZombieHierarchyManager extends HierarchyManagerImpl { + + /** + * the attic + */ + protected ItemStateManager attic; + + public ZombieHierarchyManager(NodeId rootNodeId, + ItemStateManager provider, + ItemStateManager attic, + NamespaceResolver nsResolver) { + super(rootNodeId, provider, nsResolver); + this.attic = attic; + } + + /** + * {@inheritDoc} + *

+ * Delivers state from attic if such exists, otherwise calls base class. + */ + protected ItemState getItemState(ItemId id) + throws NoSuchItemStateException, ItemStateException { + // always check attic first + if (attic.hasItemState(id)) { + return attic.getItemState(id); + } + // delegate to base class + return super.getItemState(id); + } + + /** + * {@inheritDoc} + *

+ * Returns true if there's state on the attic for the + * requested item; otherwise delegates to base class. + */ + protected boolean hasItemState(ItemId id) { + // always check attic first + if (attic.hasItemState(id)) { + return true; + } + // delegate to base class + return super.hasItemState(id); + } + + /** + * {@inheritDoc} + *

+ * Also allows for removed items. + */ + protected NodeId getParentId(ItemState state) { + if (state.hasOverlayedState()) { + // use 'old' parent in case item has been removed + return state.getOverlayedState().getParentId(); + } + // delegate to base class + return super.getParentId(state); + } + + /** + * {@inheritDoc} + *

+ * Also allows for removed/renamed child node entries. + */ + protected NodeState.ChildNodeEntry getChildNodeEntry(NodeState parent, + QName name, + int index) { + // check removed child node entries first + Iterator iter = parent.getRemovedChildNodeEntries().iterator(); + while (iter.hasNext()) { + NodeState.ChildNodeEntry entry = (NodeState.ChildNodeEntry) iter.next(); + if (entry.getName().equals(name) && entry.getIndex() == index) { + return entry; + } + } + // no matching removed child node entry found in parent, + // delegate to base class + return super.getChildNodeEntry(parent, name, index); + } + + /** + * {@inheritDoc} + *

+ * Also allows for removed child node entries. + */ + protected NodeState.ChildNodeEntry getChildNodeEntry(NodeState parent, + NodeId id) { + // check removed child node entries first + Iterator iter = parent.getRemovedChildNodeEntries().iterator(); + while (iter.hasNext()) { + NodeState.ChildNodeEntry entry = + (NodeState.ChildNodeEntry) iter.next(); + if (entry.getId().equals(id)) { + return entry; + } + } + // no matching removed child node entry found in parent, + // delegate to base class + return super.getChildNodeEntry(parent, id); + } +} Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/ZombieHierarchyManager.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/ZombieHierarchyManager.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/config/RepositoryConfig.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/config/RepositoryConfig.java?rev=421270&view=auto ============================================================================== --- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/config/RepositoryConfig.java (added) +++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/config/RepositoryConfig.java Wed Jul 12 06:33:19 2006 @@ -0,0 +1,33 @@ +/* + * 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.jcr2spi.config; + +import org.apache.jackrabbit.spi.RepositoryService; + +import javax.jcr.RepositoryException; +import javax.jcr.ValueFactory; + +/** + * RepositoryConfig... + */ +// TODO: needs to be done properly +public interface RepositoryConfig { + + public RepositoryService getRepositoryService() throws RepositoryException; + + public ValueFactory getValueFactory() throws RepositoryException; +} \ No newline at end of file Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/config/RepositoryConfig.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/config/RepositoryConfig.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/DefaultLockManager.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/DefaultLockManager.java?rev=421270&view=auto ============================================================================== --- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/DefaultLockManager.java (added) +++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/DefaultLockManager.java Wed Jul 12 06:33:19 2006 @@ -0,0 +1,67 @@ +/* + * 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.jcr2spi.lock; + +import org.apache.jackrabbit.spi.NodeId; +import org.slf4j.LoggerFactory; +import org.slf4j.Logger; + +import javax.jcr.lock.Lock; +import javax.jcr.lock.LockException; +import javax.jcr.RepositoryException; +import javax.jcr.UnsupportedRepositoryOperationException; + +/** + * DefaultLockManager... + */ +public class DefaultLockManager implements LockManager { + + private static Logger log = LoggerFactory.getLogger(DefaultLockManager.class); + + public Lock lock(NodeId nodeId, boolean isDeep, boolean isSessionScoped) throws LockException, RepositoryException { + throw new UnsupportedRepositoryOperationException("Locking ist not supported by this repository."); + } + + public Lock getLock(NodeId nodeId) throws LockException, RepositoryException { + throw new UnsupportedRepositoryOperationException("Locking ist not supported by this repository."); + } + + public void unlock(NodeId nodeId) throws LockException, RepositoryException { + throw new UnsupportedRepositoryOperationException("Locking ist not supported by this repository."); + } + + public boolean isLocked(NodeId nodeId) throws RepositoryException { + return false; + } + + public void checkLock(NodeId nodeId) throws LockException, RepositoryException { + // nothing to do since locking is not supported + } + + public String[] getLockTokens() { + // return an empty string array + return new String[0]; + } + + public void addLockToken(String lt) { + // nothing to do since locking is not supported + } + + public void removeLockToken(String lt) { + // nothing to do since locking is not supported + } +} \ No newline at end of file Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/DefaultLockManager.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/DefaultLockManager.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/LockManager.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/LockManager.java?rev=421270&view=auto ============================================================================== --- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/LockManager.java (added) +++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/LockManager.java Wed Jul 12 06:33:19 2006 @@ -0,0 +1,110 @@ +/* + * 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.jcr2spi.lock; + +import javax.jcr.RepositoryException; +import org.apache.jackrabbit.spi.NodeId; +import javax.jcr.lock.Lock; +import javax.jcr.lock.LockException; + +/** + * Defines the functionality needed for locking and unlocking nodes. + */ +public interface LockManager { + + /** + * Lock a node. Checks whether the node is not locked and then + * returns a lock object for this node. + * + * @param nodeId node id. + * @param isDeep whether the lock applies to this node only + * @param isSessionScoped whether the lock is session scoped + * @return lock object + * @throws LockException if this node already is locked, or some descendant + * node is locked and isDeep is true + * @see javax.jcr.Node#lock + */ + Lock lock(NodeId nodeId, boolean isDeep, boolean isSessionScoped) + throws LockException, RepositoryException; + + /** + * Returns the Lock object that applies to a node. This may be either a lock + * on this node itself or a deep lock on a node above this node. + * + * @param nodeId + * @return lock object + * @throws LockException if this node is not locked + * @see javax.jcr.Node#getLock + */ + Lock getLock(NodeId nodeId) throws LockException, RepositoryException; + + /** + * Removes the lock on a node. + * + * @param nodeId + * @throws LockException if this node is not locked or the session does not + * have the correct lock token + * @see javax.jcr.Node#unlock + */ + void unlock(NodeId nodeId) throws LockException, RepositoryException; + + /** + * Returns true if this node is locked either as a result + * of a lock held by this node or by a deep lock on a node above this + * node; otherwise returns false. + * + * @param nodeId + * @return true if this node is locked either as a result + * of a lock held by this node or by a deep lock on a node above this + * node; otherwise returns false + * @see javax.jcr.Node#isLocked + */ + boolean isLocked(NodeId nodeId) throws RepositoryException; + + /** + * Check whether the node given is locked by somebody else than the + * current session. Access is allowed if the node is not locked or + * if the session itself holds the lock to this node, i.e. the session + * contains the lock token for the lock. If the node is not locked at + * all this method returns silently. + * + * @param nodeId to check + * @throws LockException if write access to the specified node is not allowed + * @throws RepositoryException if some other error occurs + */ + void checkLock(NodeId nodeId) throws LockException, RepositoryException; + + /** + * + * @return + */ + public String[] getLockTokens(); + + /** + * Invoked by a session to inform that a lock token has been added. + * + * @param lt added lock token + */ + void addLockToken(String lt) throws LockException, RepositoryException; + + /** + * Invoked by a session to inform that a lock token has been removed. + * + * @param lt removed lock token + */ + void removeLockToken(String lt) throws LockException, RepositoryException; +} Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/LockManager.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/LockManager.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/LockManagerImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/LockManagerImpl.java?rev=421270&view=auto ============================================================================== --- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/LockManagerImpl.java (added) +++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/LockManagerImpl.java Wed Jul 12 06:33:19 2006 @@ -0,0 +1,562 @@ +/* + * 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.jcr2spi.lock; + +import org.apache.jackrabbit.jcr2spi.ItemManager; +import org.apache.jackrabbit.jcr2spi.SessionListener; +import org.apache.jackrabbit.jcr2spi.WorkspaceManager; +import org.apache.jackrabbit.jcr2spi.IdKeyMap; +import org.apache.jackrabbit.jcr2spi.DefaultIdKeyMap; +import org.apache.jackrabbit.jcr2spi.observation.InternalEventListener; +import org.apache.jackrabbit.jcr2spi.operation.Operation; +import org.apache.jackrabbit.jcr2spi.operation.LockOperation; +import org.apache.jackrabbit.jcr2spi.operation.LockRelease; +import org.apache.jackrabbit.jcr2spi.operation.LockRefresh; +import org.apache.jackrabbit.jcr2spi.state.NodeState; +import org.apache.jackrabbit.jcr2spi.state.ItemStateException; +import org.apache.jackrabbit.name.QName; +import org.apache.jackrabbit.spi.EventIterator; +import org.apache.jackrabbit.spi.Event; +import org.apache.jackrabbit.spi.LockInfo; +import org.apache.jackrabbit.spi.NodeId; +import org.apache.jackrabbit.spi.ItemId; +import org.slf4j.LoggerFactory; +import org.slf4j.Logger; + +import javax.jcr.lock.Lock; +import javax.jcr.lock.LockException; +import javax.jcr.RepositoryException; +import javax.jcr.Node; +import javax.jcr.Item; +import javax.jcr.Session; + +import java.util.Iterator; + +/** + * LockManagerImpl... + */ +public class LockManagerImpl implements LockManager, SessionListener { + + private static Logger log = LoggerFactory.getLogger(LockManagerImpl.class); + + private final WorkspaceManager wspManager; + private final ItemManager itemManager; + + /** + * Internal map holding all locks that where created by {@link #lock(NodeId, boolean, boolean)} + * or accessed by {@link #getLock(NodeId)} until they end their life by + * an unlock (be it by the current Session or external reported by means + * of events). + */ + // TODO: TO-BE-FIXED. With SPI_ItemId simple map cannot be used any more + private final IdKeyMap lockMap = new DefaultIdKeyMap(); + + public LockManagerImpl(WorkspaceManager wspManager, ItemManager itemManager) { + this.wspManager = wspManager; + this.itemManager = itemManager; + } + + /** + * @see LockManager#lock(NodeId, boolean, boolean) + */ + public Lock lock(NodeId nodeId, boolean isDeep, boolean isSessionScoped) throws LockException, RepositoryException { + // make sure the node is accessible before trying to create a lock. + Node node = (Node) itemManager.getItem(nodeId); + // execute the operation + Operation op = LockOperation.create(nodeId, isDeep, isSessionScoped); + wspManager.execute(op); + + Lock lock = new LockImpl(nodeId, node, isSessionScoped); + return lock; + } + + /** + * If the session created a lock on the node with the given id, we already + * know the lock. Otherwise, we look in the node state and the states of + * the ancestor nodes for properties indicating a lock.
+ * Note, that the flag indicating session-scoped lock cannot be retrieved + * and the lock will always report 'false'. + * + * @see LockManager#getLock(NodeId) + */ + public Lock getLock(NodeId nodeId) throws LockException, RepositoryException { + // shortcut: check if node holds a lock and lock has been accessed before + if (lockMap.containsKey(nodeId)) { + return (Lock) lockMap.get(nodeId); + } + + // try to retrieve parent state that holds a lock. + NodeState lockHoldingState = getLockHoldingState(nodeId); + if (lockHoldingState == null) { + throw new LockException("Node with id '" + nodeId + "' is not locked."); + } else { + NodeId lhNodeId = lockHoldingState.getNodeId(); + // check again lockMap with id of lockholding node + if (lockMap.containsKey(lhNodeId)) { + return (Lock) lockMap.get(lhNodeId); + } + + // retrieve lock holding node. not that this may fail if the session + // does not have permission to see this node. + Item lockHoldingNode = itemManager.getItem(lhNodeId); + // TODO: we don;t know if lock is session scoped -> set flag to false + // TODO: ev. add 'isSessionScoped' to RepositoryService lock-call. + Lock l = new LockImpl(lhNodeId, (Node)lockHoldingNode, false); + return l; + } + } + + /** + * @see LockManager#unlock(NodeId) + */ + public void unlock(NodeId nodeId) throws LockException, RepositoryException { + // execute the operation. Note, that its possible that the session is + // lock holder and still the lock was never accessed. thus the lockMap + // does not provide sufficient information. + Operation op = LockRelease.create(nodeId); + wspManager.execute(op); + + // if unlock was successfull: clean up lock map and lock life cycle + // in case the corresponding Lock object exists (and thus has been + // added to the map. + if (lockMap.containsKey(nodeId)) { + LockImpl l = (LockImpl) lockMap.remove(nodeId); + l.unlocked(); + } + } + + /** + * @see LockManager#isLocked(NodeId) + */ + public boolean isLocked(NodeId nodeId) throws RepositoryException { + // shortcut: check if a given node holds a lock and lock has been + // accessed before (thus is known to the manager). + if (lockMap.containsKey(nodeId)) { + return true; + } else { + // check if any lock is present (checking lock-specific properties) + LockInfo lInfo = getLockInfo(nodeId); + return lInfo != null; + } + } + + /** + * @see LockManager#checkLock(NodeId) + */ + public void checkLock(NodeId nodeId) throws LockException, RepositoryException { + LockInfo lInfo; + // shortcut: check if a given node holds a lock and lock has been + // accessed before (thus is known to the manager). + if (lockMap.containsKey(nodeId)) { + lInfo = ((LockImpl)lockMap.get(nodeId)).lockInfo; + } else { + // check if any lock is present (checking lock-specific properties) + lInfo = getLockInfo(nodeId); + } + + if (lInfo != null && lInfo.getLockToken() == null) { + // lock is present and token is null -> session is not lock-holder. + throw new LockException("Node with id '" + nodeId + "' is locked."); + } + } + + /** + * Returns the lock tokens present on the SessionInfo this + * manager has been created with. + * + * @see LockManager#getLockTokens() + */ + public String[] getLockTokens() { + return wspManager.getLockTokens(); + } + + /** + * Delegates this call to {@link WorkspaceManager#addLockToken(String)}. + * If this succeeds this method will inform all locks stored in the local + * map in order to give them the chance to update their lock information. + * + * @see LockManager#addLockToken(String) + */ + public void addLockToken(String lt) throws LockException, RepositoryException { + wspManager.addLockToken(lt); + notifyTokenAdded(lt); + } + + /** + * If the lock addressed by the token is session-scoped, this method will + * throw a LockException, such as defined by JSR170 v.1.0.1 for + * {@link Session#removeLockToken(String)}.
Otherwise the call is + * delegated to {@link WorkspaceManager#removeLockToken(String)} and + * all locks stored in the local lock map are notified by the removed + * token in order to give them the chance to update their lock information. + * + * @see LockManager#removeLockToken(String) + */ + public void removeLockToken(String lt) throws LockException, RepositoryException { + // JSR170 v. 1.0.1 defines that the token of a session-scoped lock may + // not be moved over to another session. thus removal ist not possible + // and the lock is always present in the lock map. + NodeId key = null; + Iterator it = lockMap.values().iterator(); + while (it.hasNext() && key == null) { + LockImpl l = (LockImpl) it.next(); + // break as soon as the lock associated with the given token is found. + if (lt.equals(l.getLockToken())) { + if (l.isSessionScoped()) { + throw new LockException("Cannot remove lock token associated with a session scoped lock."); + } + key = l.nodeId; + } + } + + // remove lock token from sessionInfo + wspManager.removeLockToken(lt); + // inform about this lt being removed from this session + notifyTokenRemoved(lt); + } + + //----------------------------------------------------< SessionListener >--- + /** + * + * @param session + * @see SessionListener#loggingOut(Session) + */ + public void loggingOut(Session session) { + // remove any session scoped locks: + ItemId[] ids = (ItemId[]) lockMap.keySet().toArray(new ItemId[lockMap.size()]); + for (int i = 0; i < ids.length; i++) { + NodeId nId = (NodeId) ids[i]; + LockImpl l = (LockImpl) lockMap.get(nId); + if (l.isSessionScoped()) { + try { + unlock(nId); + } catch (RepositoryException e) { + log.error("Error while unlocking session scoped lock. Cleaning up local lock status."); + // at least clean up local lock map and the locks life cycle + l.unlocked(); + } + } + } + } + + /** + * + * @param session + * @see SessionListener#loggedOut(Session) + */ + public void loggedOut(Session session) { + // release all remaining locks without modifying their lock status + LockImpl[] locks = (LockImpl[]) lockMap.values().toArray(new LockImpl[lockMap.size()]); + for (int i = 0; i < locks.length; i++) { + locks[i].release(); + } + } + + //------------------------------------------------------------< private >--- + /** + * + * @param nodeId + * @return + * @throws LockException + * @throws RepositoryException + */ + private LockInfo getLockInfo(NodeId nodeId) throws RepositoryException { + try { + return wspManager.getLockInfo(nodeId); + } catch (LockException e) { + log.debug("No lock present on node with id '" + nodeId + "'", e); + return null; + } + } + + /** + * + * @param nodeId + * @return + * @throws RepositoryException + */ + private NodeState getLockHoldingState(NodeId nodeId) throws RepositoryException { + try { + NodeState nodeState = (NodeState) wspManager.getItemState(nodeId); + // search nearest ancestor that is locked + /** + * FIXME should not only rely on existence of jcr:lockOwner property + * but also verify that node.isNodeType("mix:lockable")==true; + * this would have a negative impact on performance though... + */ + while (!nodeState.hasPropertyName(QName.JCR_LOCKOWNER)) { + if (nodeState.getParentId() == null) { + // reached root state without finding a locked node + return null; + } + nodeState = (NodeState) wspManager.getItemState(nodeState.getParentId()); + } + return nodeState; + } catch (ItemStateException e) { + // should not occur + throw new RepositoryException(e); + } + } + + //----------------------------< Notification about modified lock-tokens >--- + /** + * Notify all Locks that have been accessed so far about the + * new lock token present on the session and allow them to reload their + * lock info. + * + * @param lt + * @throws LockException + * @throws RepositoryException + */ + private void notifyTokenAdded(String lt) throws LockException, RepositoryException { + LockTokenListener[] listeners = (LockTokenListener[]) lockMap.values().toArray(new LockTokenListener[lockMap.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].lockTokenAdded(lt); + } + } + + /** + * Notify all Locks that have been accessed so far about the + * removed lock token and allow them to reload their lock info, if necessary. + * + * @param lt + * @throws LockException + * @throws RepositoryException + */ + private void notifyTokenRemoved(String lt) throws LockException, RepositoryException { + LockTokenListener[] listeners = (LockTokenListener[]) lockMap.values().toArray(new LockTokenListener[lockMap.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].lockTokenRemoved(lt); + } + } + + //---------------------------------------------------------------< Lock >--- + /** + * Inner class implementing the {@link Lock} interface. + */ + private class LockImpl implements Lock, InternalEventListener, LockTokenListener { + + private final NodeId nodeId; + private final Node node; + private final boolean isSessionScoped; + + private LockInfo lockInfo; + private boolean isLive = true; + + /** + * + * @param lockHoldingId The Id of the lock holding Node. + * @param lockHoldingNode the lock holding Node itself. + * @param lockHoldingNode + */ + public LockImpl(NodeId lockHoldingId, Node lockHoldingNode, boolean isSessionScoped) throws LockException, RepositoryException { + this.nodeId = lockHoldingId; + this.node = lockHoldingNode; + this.isSessionScoped = isSessionScoped; + + // retrieve lock info from wsp-manager, in order to get the complete + // lockInfo including lock-token, which is not available from the + // child properties nor from the original lock request. + this.lockInfo = wspManager.getLockInfo(nodeId); + + // register as internal listener to the wsp manager in order to get + // informed if this lock ends his life. + wspManager.addEventListener(this); + // store lock in the map + lockMap.put(nodeId, this); + } + + /** + * @see Lock#getLockOwner() + */ + public String getLockOwner() { + return lockInfo.getOwner(); + } + + /** + * @see Lock#isDeep() + */ + public boolean isDeep() { + return lockInfo.isDeep(); + } + + /** + * @see Lock#getNode() + */ + public Node getNode() { + return node; + } + + /** + * @see Lock#getLockToken() + */ + public String getLockToken() { + return lockInfo.getLockToken(); + } + + /** + * @see Lock#isLive() + */ + public boolean isLive() throws RepositoryException { + return isLive; + } + + /** + * @see Lock#isSessionScoped() + */ + public boolean isSessionScoped() { + return isSessionScoped; + } + + /** + * @see Lock#refresh() + */ + public void refresh() throws LockException, RepositoryException { + if (!isLive()) { + throw new LockException("Lock is not alive any more."); + } + + if (getLockToken() == null) { + // shortcut, since lock is always updated if the session became + // lock-holder of a foreign lock. + throw new LockException("Session does not hold lock."); + } else { + // lock is still alive -> send refresh-lock operation. + Operation op = LockRefresh.create(nodeId); + wspManager.execute(op); + } + } + + //----------------------------------------------< InternalEventListener >--- + /** + * + * @param events + * @param isLocal + */ + public void onEvent(EventIterator events, boolean isLocal) { + if (!isLive) { + // since only we only monitor the removal of the lock (by means + // of deletion of the jcr:lockOwner property, we are not interested + // if the lock is not active any more. + return; + } + + while (events.hasNext()) { + Event ev = events.nextEvent(); + // if the jcr:lockOwner property related to this Lock got removed, + // we assume that the lock has been released. + if (ev.getType() == Event.PROPERTY_REMOVED && + nodeId.equals(ev.getParentId()) && + QName.JCR_LOCKOWNER.equals(ev.getQPath().getNameElement().getName())) { + // TODO: check if removal jcr:lockIsDeep must be checked as well. + + // this lock has been release by someone else (and not by + // a call to LockManager#unlock -> clean up and set isLive + // flag to false. + unlocked(); + break; + } + } + } + + /** + * + * @param lockToken + * @throws LockException + * @throws RepositoryException + */ + public void lockTokenAdded(String lockToken) throws LockException, RepositoryException { + if (getLockToken() == null) { + // could be that this affects this lock and session became + // lock holder -> releoad info + reloadLockInfo(); + } + } + + /** + * + * @param lockToken + * @throws LockException + * @throws RepositoryException + */ + public void lockTokenRemoved(String lockToken) throws LockException, RepositoryException { + // reload lock info, if session gave away its lock-holder state + // for this lock + if (lockToken.equals(getLockToken())) { + reloadLockInfo(); + } + } + + //--------------------------------------------------------< private >--- + /** + * + * @throws LockException + * @throws RepositoryException + */ + private void reloadLockInfo() throws LockException, RepositoryException { + // re-check with server, since Session.addLockToken will not + // automatically result in an update of the lock-map. + lockInfo = wspManager.getLockInfo(nodeId); + } + + /** + * Release this lock by removing from the lock map and unregistering + * it from event listening + */ + private void release() { + if (lockMap.containsKey(nodeId)) { + lockMap.remove(nodeId); + } + wspManager.removeEventListener(this); + } + + /** + * This lock has been removed by the current Session or by an external + * unlock request. Since a lock will never come back to life after + * unlocking, it is released an its status is reset accordingly. + */ + private void unlocked() { + if (isLive) { + isLive = false; + release(); + } + } + } + + //--------------------------------------------------< LockTokenListener >--- + /** + * + */ + private interface LockTokenListener { + + /** + * + * @param lockToken + * @throws LockException + * @throws RepositoryException + */ + void lockTokenAdded(String lockToken) throws LockException, RepositoryException; + + /** + * + * @param lockToken + * @throws LockException + * @throws RepositoryException + */ + void lockTokenRemoved(String lockToken) throws LockException, RepositoryException; + } +} \ No newline at end of file Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/LockManagerImpl.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/LockManagerImpl.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url