/*
* $Header: /home/cvs/jakarta-slide/src/share/org/apache/slide/store/ParentStore.java,v 1.3.2.1 2004/02/05 16:05:12 mholz Exp $
* $Revision: 1.3.2.1 $
* $Date: 2004/02/05 16:05:12 $
*
* ====================================================================
*
* Copyright 1999-2002 The Apache Software Foundation
*
* Licensed 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.slide.store;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
import javax.transaction.xa.XAException;
import javax.transaction.xa.Xid;
import org.apache.slide.common.NamespaceAccessToken;
import org.apache.slide.common.ServiceAccessException;
import org.apache.slide.common.ServiceInitializationFailedException;
import org.apache.slide.common.Uri;
import org.apache.slide.common.UriPath;
import org.apache.slide.content.NodeProperty;
import org.apache.slide.content.NodeRevisionContent;
import org.apache.slide.content.NodeRevisionDescriptor;
import org.apache.slide.content.NodeRevisionDescriptors;
import org.apache.slide.content.NodeRevisionNumber;
import org.apache.slide.content.RevisionAlreadyExistException;
import org.apache.slide.content.RevisionDescriptorNotFoundException;
import org.apache.slide.content.RevisionNotFoundException;
import org.apache.slide.lock.LockTokenNotFoundException;
import org.apache.slide.lock.NodeLock;
import org.apache.slide.macro.ConflictException;
import org.apache.slide.store.tlock.TLock;
import org.apache.slide.store.tlock.TLockManager;
import org.apache.slide.store.tlock.TLockedException;
import org.apache.slide.security.NodePermission;
import org.apache.slide.store.ExtendedStore;
import org.apache.slide.store.NodeStore;
import org.apache.slide.structure.ObjectAlreadyExistsException;
import org.apache.slide.structure.ObjectNode;
import org.apache.slide.structure.ObjectNotFoundException;
import org.apache.slide.util.Configuration;
import org.apache.slide.util.XMLValue;
import org.jdom.Element;
/**
* Store implementation supporting binding-resolution and t-locking.
* By extending ExtendedStore, this store implementation inherits also
* caching.
*
* @author michael.hartmeier@softwareag.com
* @author peter.nevermann@softwareag.com
* @version $Revision: 1.3.2.1 $
*/
public class ParentStore extends /*AbstractStore*/ ExtendedStore {
private static final int TLOCK_TIMEOUT = 5000; // TODO
private static TLockManager tlockManager = new TLockManager(TLOCK_TIMEOUT);
// overwrites inherited
public void initialize(NamespaceAccessToken token)
throws ServiceInitializationFailedException {
super.initialize(token);
}
// overwrites inherited
public void commit(Xid xid, boolean onePhase) throws XAException {
try {
super.commit(xid, onePhase);
}
finally {
tlockManager.releaseLocks();
}
}
// overwrites inherited
public void rollback(Xid xid) throws XAException {
try {
super.rollback(xid);
}
finally {
tlockManager.releaseLocks();
}
}
// overwrites inherited
public ObjectNode retrieveObject(Uri uri)
throws ServiceAccessException, ObjectNotFoundException {
return doRetrieveObjectNode(uri);
}
// overwrites inherited
public void storeObject(Uri uri, ObjectNode object)
throws ServiceAccessException, ObjectNotFoundException {
ResourceId resourceId = obtainResourceId(uri);
ObjectNode objectClone = object.cloneObject();
objectClone.setUri(resourceId.getUuri()); // switch to uuri
try {
tlockManager.lock(resourceId, uri.toString(), TLock.WRITE_LOCK);
} catch (TLockedException e) {
throw new ServiceAccessException(this, new ConflictException(uri.toString()));
}
super.storeObject(resourceId, objectClone);
}
// overwrites inherited
public void createObject(Uri uri, ObjectNode object)
throws ServiceAccessException, ObjectAlreadyExistsException {
ResourceId resourceId = ResourceId.createNew(uri);
object.setUuri(resourceId.getUuri());
ObjectNode objectClone = object.cloneObject();
objectClone.setUri(resourceId.getUuri()); // switch to uuri
tlockManager.create(TLock.WRITE_LOCK, uri.toString(), resourceId);
cacheResolve(uri, resourceId);
super.createObject(resourceId, objectClone);
}
// overwrites inherited
public void removeObject(Uri uri, ObjectNode object)
throws ServiceAccessException, ObjectNotFoundException {
ResourceId resourceId = obtainResourceId(uri);
ObjectNode objectClone = object.cloneObject();
objectClone.setUri(resourceId.getUuri()); // switch to uuri
try {
tlockManager.lock(resourceId, uri.toString(), TLock.WRITE_LOCK);
} catch (TLockedException e) {
throw new ServiceAccessException(this, new ConflictException(uri.toString()));
}
super.removeObject(resourceId, objectClone);
}
// overwrites inherited
public void grantPermission(Uri uri, NodePermission permission)
throws ServiceAccessException {
try {
ResourceId resourceId = obtainResourceId(uri);
NodePermission permissionClone = permission.cloneObject();
permissionClone.setObject(resourceId.getUuri()); // switch to uuri
super.grantPermission(resourceId, permissionClone);
}
catch (ObjectNotFoundException e) {
throw new ServiceAccessException(this, e);
}
}
// overwrites inherited
public void revokePermission(Uri uri, NodePermission permission)
throws ServiceAccessException {
try {
ResourceId resourceId = obtainResourceId(uri);
NodePermission permissionClone = permission.cloneObject();
permissionClone.setObject(resourceId.getUuri()); // switch to uuri
super.revokePermission(resourceId, permissionClone);
}
catch (ObjectNotFoundException e) {
throw new ServiceAccessException(this, e);
}
}
// overwrites inherited
public void revokePermissions(Uri uri)
throws ServiceAccessException {
try {
super.revokePermissions(obtainResourceId(uri));
}
catch (ObjectNotFoundException e) {
throw new ServiceAccessException(this, e);
}
}
// overwrites inherited
public Enumeration enumeratePermissions(Uri uri)
throws ServiceAccessException {
try {
Enumeration permissions = super.enumeratePermissions(obtainResourceId(uri));
Vector result = new Vector();
while (permissions.hasMoreElements()) {
NodePermission p = ((NodePermission)permissions.nextElement()).cloneObject();
p.setObject(uri.toString()); // switch to uri
result.add(p);
}
return result.elements();
}
catch (ObjectNotFoundException e) {
throw new ServiceAccessException(this, e);
}
}
// overwrites inherited
public void putLock(Uri uri, NodeLock lock)
throws ServiceAccessException {
try {
ResourceId resourceId = obtainResourceId(uri);
NodeLock lockClone = lock.cloneObject();
lockClone.setObjectUri(resourceId.getUuri()); // switch to uuri
super.putLock(resourceId, lockClone);
}
catch (ObjectNotFoundException e) {
throw new ServiceAccessException(this, e);
}
}
// overwrites inherited
public void renewLock(Uri uri, NodeLock lock)
throws ServiceAccessException, LockTokenNotFoundException {
try {
ResourceId resourceId = obtainResourceId(uri);
NodeLock lockClone = lock.cloneObject();
lockClone.setObjectUri(resourceId.getUuri()); // switch to uuri
super.renewLock(resourceId, lockClone);
}
catch (ObjectNotFoundException e) {
throw new ServiceAccessException(this, e);
}
}
// overwrites inherited
public void removeLock(Uri uri, NodeLock lock)
throws ServiceAccessException, LockTokenNotFoundException {
try {
ResourceId resourceId = obtainResourceId(uri);
NodeLock lockClone = lock.cloneObject();
lockClone.setObjectUri(resourceId.getUuri()); // switch to uuri
super.removeLock(resourceId, lockClone);
}
catch (ObjectNotFoundException e) {
throw new ServiceAccessException(this, e);
}
}
// overwrites inherited
public void killLock(Uri uri, NodeLock lock)
throws ServiceAccessException, LockTokenNotFoundException {
try {
ResourceId resourceId = obtainResourceId(uri);
NodeLock lockClone = lock.cloneObject();
lockClone.setObjectUri(resourceId.getUuri()); // switch to uuri
super.killLock(resourceId, lockClone);
}
catch (ObjectNotFoundException e) {
throw new ServiceAccessException(this, e);
}
}
// overwrites inherited
public Enumeration enumerateLocks(Uri uri)
throws ServiceAccessException {
try {
Enumeration locks = super.enumerateLocks(obtainResourceId(uri));
Vector result = new Vector();
while (locks.hasMoreElements()) {
NodeLock l = ((NodeLock)locks.nextElement()).cloneObject();
l.setObjectUri(uri.toString()); // switch to uri
result.add(l);
}
return result.elements();
}
catch (ObjectNotFoundException e) {
throw new ServiceAccessException(this, e);
}
}
// overwrites inherited
public NodeRevisionDescriptors retrieveRevisionDescriptors(Uri uri)
throws ServiceAccessException, RevisionDescriptorNotFoundException {
try {
NodeRevisionDescriptors nrdsClone =
super.retrieveRevisionDescriptors(obtainResourceId(uri)).cloneObject();
nrdsClone.setUri(uri.toString());
return nrdsClone;
}
catch (ObjectNotFoundException e) {
// TODO: throw RevisionDescriptorsNotFoundException???
throw new ServiceAccessException(this, e);
}
}
// overwrites inherited
public void createRevisionDescriptors
(Uri uri, NodeRevisionDescriptors revisionDescriptors)
throws ServiceAccessException {
try {
ResourceId resourceId = obtainResourceId(uri);
NodeRevisionDescriptors nrdsClone = revisionDescriptors.cloneObject();
nrdsClone.setUri(resourceId.getUuri()); // switch to uuri
super.createRevisionDescriptors(resourceId, nrdsClone);
}
catch (ObjectNotFoundException e) {
throw new ServiceAccessException(this, e);
}
}
// overwrites inherited
public void storeRevisionDescriptors
(Uri uri, NodeRevisionDescriptors revisionDescriptors)
throws ServiceAccessException, RevisionDescriptorNotFoundException {
try {
ResourceId resourceId = obtainResourceId(uri);
NodeRevisionDescriptors nrdsClone = revisionDescriptors.cloneObject();
nrdsClone.setUri(resourceId.getUuri()); // switch to uuri
super.storeRevisionDescriptors(resourceId, nrdsClone);
}
catch (ObjectNotFoundException e) {
// TODO: throw RevisionDescriptorsNotFoundException???
throw new ServiceAccessException(this, e);
}
}
// overwrites inherited
public void removeRevisionDescriptors(Uri uri)
throws ServiceAccessException {
try {
super.removeRevisionDescriptors(obtainResourceId(uri));
}
catch (ObjectNotFoundException e) {
throw new ServiceAccessException(this, e);
}
}
// overwrites inherited
public NodeRevisionDescriptor retrieveRevisionDescriptor
(Uri uri, NodeRevisionNumber revisionNumber)
throws ServiceAccessException, RevisionDescriptorNotFoundException {
try {
ObjectNode objectNode = doRetrieveObjectNode(uri);
ResourceId resourceId = obtainResourceId(uri);
NodeRevisionDescriptor nrd = super.retrieveRevisionDescriptor(resourceId, revisionNumber);
nrd.setProperty("resource-id", resourceId.asXml());
nrd.setProperty("parent-set", getXmlParentSet(uri, objectNode));
return nrd;
}
catch (ObjectNotFoundException e) {
// TODO: throw RevisionDescriptorNotFoundException???
throw new ServiceAccessException(this, e);
}
}
public String getXmlParentSet(Uri uri, ObjectNode objectNode) throws ServiceAccessException, ObjectNotFoundException {
ResourceId resourceId = obtainResourceId(uri);
// if objectNode is the root of a store, parent-uuri.equals(""),
// thus we cannot call getOneParentUri because
// we cannot resolve the uuri
boolean useBinding = Configuration.useBinding(this) && !resourceId.isStoreRoot();
XMLValue result = new XMLValue();
Enumeration parentBindings = objectNode.enumerateParentBindings();
while (parentBindings.hasMoreElements()) {
ObjectNode.Binding parentBinding = (ObjectNode.Binding) parentBindings.nextElement();
Element parentElm = new Element("parent", NodeProperty.NamespaceCache.DEFAULT_NAMESPACE);
Element hrefElm = new Element("href", NodeProperty.NamespaceCache.DEFAULT_NAMESPACE);
String parentUriStr = new UriPath(objectNode.getUri()).parent().toString();
Uri parentUri = new Uri(uri.getToken(), uri.getNamespace(), parentUriStr);
String uriStr;
if (useBinding) {
ResourceId parentResourceId = ResourceId.create(parentUri, parentBinding.getUuri());
uriStr = getOneUri(parentResourceId);
}
else {
uriStr = parentUriStr;
}
hrefElm.setText(uriStr);
parentElm.addContent(hrefElm);
Element segmentElm = new Element("segment", NodeProperty.NamespaceCache.DEFAULT_NAMESPACE);
segmentElm.setText(parentBinding.getName());
parentElm.addContent(segmentElm);
result.add(parentElm);
}
return result.toString();
}
private String getOneUri(ResourceId resourceId) throws ServiceAccessException, ObjectNotFoundException {
String result = "";
while (!resourceId.isStoreRoot()) {
ObjectNode objectNode = super.retrieveObject(resourceId);
Enumeration enum = objectNode.enumerateParentBindings();
if (!enum.hasMoreElements()) {
throw new IllegalStateException();
}
ObjectNode.Binding parentBinding = (ObjectNode.Binding) enum.nextElement();
String parentSegment = parentBinding.getName();
result = "/" + parentSegment + result;
resourceId = ResourceId.create(resourceId, parentBinding.getUuri());
}
return "/".equals(resourceId.getScope().toString())
? result
: resourceId.getScope()+result;
}
// overwrites inherited
public void createRevisionDescriptor
(Uri uri, NodeRevisionDescriptor revisionDescriptor)
throws ServiceAccessException {
try {
revisionDescriptor.removeProperty("resource-id");
revisionDescriptor.removeProperty("parent-set");
super.createRevisionDescriptor(obtainResourceId(uri), revisionDescriptor);
}
catch (ObjectNotFoundException e) {
throw new ServiceAccessException(this, e);
}
}
// overwrites inherited
public void storeRevisionDescriptor
(Uri uri, NodeRevisionDescriptor revisionDescriptor)
throws ServiceAccessException, RevisionDescriptorNotFoundException {
try {
revisionDescriptor.removeProperty("resource-id");
revisionDescriptor.removeProperty("parent-set");
super.storeRevisionDescriptor(obtainResourceId(uri), revisionDescriptor);
}
catch (ObjectNotFoundException e) {
// TODO: throw RevisionDescriptorNotFoundException???
throw new ServiceAccessException(this, e);
}
}
// overwrites inherited
public void removeRevisionDescriptor(Uri uri, NodeRevisionNumber number)
throws ServiceAccessException {
try {
super.removeRevisionDescriptor(obtainResourceId(uri), number);
}
catch (ObjectNotFoundException e) {
throw new ServiceAccessException(this, e);
}
}
// overwrites inherited
public NodeRevisionContent retrieveRevisionContent
(Uri uri, NodeRevisionDescriptor revisionDescriptor)
throws ServiceAccessException, RevisionNotFoundException {
try {
// TODO: throw RevisionNotFoundException???
return super.retrieveRevisionContent(obtainResourceId(uri), revisionDescriptor);
}
catch (ObjectNotFoundException e) {
throw new ServiceAccessException(this, e);
}
}
// overwrites inherited
public void createRevisionContent
(Uri uri, NodeRevisionDescriptor revisionDescriptor,
NodeRevisionContent revisionContent)
throws ServiceAccessException, RevisionAlreadyExistException {
try {
super.createRevisionContent(obtainResourceId(uri), revisionDescriptor, revisionContent);
}
catch (ObjectNotFoundException e) {
throw new ServiceAccessException(this, e);
}
}
// overwrites inherited
public void storeRevisionContent
(Uri uri, NodeRevisionDescriptor revisionDescriptor,
NodeRevisionContent revisionContent)
throws ServiceAccessException, RevisionNotFoundException {
try {
// TODO: throw RevisionNotFoundException???
super.storeRevisionContent(obtainResourceId(uri), revisionDescriptor, revisionContent);
}
catch (ObjectNotFoundException e) {
throw new ServiceAccessException(this, e);
}
}
// overwrites inherited
public void removeRevisionContent
(Uri uri, NodeRevisionDescriptor revisionDescriptor)
throws ServiceAccessException {
try {
super.removeRevisionContent(obtainResourceId(uri), revisionDescriptor);
}
catch (ObjectNotFoundException e) {
throw new ServiceAccessException(this, e);
}
}
// ================================================================
/**
* NodeStore accessor
*/
public NodeStore getNodeStore() {
return nodeStore;
}
/**
* Always returns false. Default implementation for this method
* Stores that support binding should override this method.
*/
public boolean useBinding() {
return "true".equalsIgnoreCase((String)getParameter("useBinding"));
}
/**
* Worker method to retrieve the ObjectNode. Resolve if binding is enabled.
*
* @param uri an Uri
* @return an ObjectNode
* @throws ServiceAccessException
* @throws ObjectNotFoundException
*/
private ObjectNode doRetrieveObjectNode( Uri uri ) throws ServiceAccessException, ObjectNotFoundException {
try {
if (isForceStoreEnlistment(uri)) {
return doRetrieveObjectNode( uri, TLock.WRITE_LOCK, TLock.READ_LOCK);
} else {
// we cannot be sure there's a transaction. Locks could get lost
// if there's no commit
return doRetrieveObjectNode( uri, TLock.NO_LOCK, TLock.NO_LOCK);
}
}
catch (TLockedException e) {
throw new ServiceAccessException(this, new ConflictException(uri.toString()));
}
}
private ObjectNode doRetrieveObjectNode(Uri uri, int lockType, int parentLockType)
throws TLockedException, ServiceAccessException, ObjectNotFoundException {
ObjectNode result;
synchronized (tlockManager) {
if (Configuration.useBinding(this)) {
result = doResolve(uri, lockType, parentLockType);
result.setUuri(result.getUri());
result.setUri(uri.toString()); // switch to uri
} else {
ResourceId resourceId = ResourceId.create(uri, uri.toString());
tlockManager.lock(resourceId, uri.toString(), lockType);
// call super such that e.g. caching can take place
result = super.retrieveObject(uri);
result.setUuri(result.getUri());
}
}
return result;
}
/**
* Worker method that actually resolves the uri.
*/
private ObjectNode doResolve(Uri uri, int lockType, int parentLockType) throws TLockedException, ObjectNotFoundException, ServiceAccessException {
// check resolve cache first
ResourceId resourceId = checkResolveCache(uri);
if (resourceId == null) {
UriPath uriPath = new UriPath(uri.toString());
String[] segments = uriPath.tokens();
String rootUriStr = uri.getScope().toString();
String currentUriStr = rootUriStr;
Uri currentUri = new Uri(uri.getToken(), uri.getNamespace(), currentUriStr);
ResourceId currentResourceId = ResourceId.create(currentUri, currentUriStr);
int start = new UriPath(rootUriStr).tokens().length;
for (int i = start; i < segments.length; i++) {
tlockManager.lock(currentResourceId, currentUriStr, parentLockType);
// cacheResolve(currentUri, currentResourceId);
// TODO: make it work with caching here :-(
// call super such that e.g. caching can take place
ObjectNode objectNode = super.retrieveObject(currentResourceId);
currentUriStr = uriPath.subUriPath(0, i + 1).toString();
currentUri = new Uri(uri.getToken(), uri.getNamespace(), currentUriStr);
String currentUuri = objectNode.getBindingUuri(segments[i]);
if (currentUuri == null) {
throw new ObjectNotFoundException(currentUriStr);
}
currentResourceId = ResourceId.create(currentUri, currentUuri);
}
resourceId = currentResourceId;
// cache resolve result
// cacheResolve(uri, resourceId);
// TODO: make it work with caching here :-(
}
// else {
// doTLock(uri, resourceId, lockType, parentLockType);
// }
// TODO: make it work with caching here :-(
tlockManager.lock(resourceId, uri.toString(), lockType);
// call super such that e.g. caching can take place
return super.retrieveObject(resourceId);
}
private void doTLock(Uri uri, ResourceId resourceId, int lockType, int parentLockType) throws TLockedException, ObjectNotFoundException, ServiceAccessException {
UriPath uriPath = new UriPath(uri.toString());
String[] segments = uriPath.tokens();
int start = new UriPath(uri.getScope().toString()).tokens().length;
for (int i = start; i < segments.length; i++) {
String currentUriStr = uriPath.subUriPath(0, i).toString();
Uri currentUri = new Uri(uri.getToken(), uri.getNamespace(), currentUriStr);
// ResourceId currentResourceId = obtainResourceId(currentUri);
ResourceId currentResourceId = checkResolveCache(currentUri);
if (currentResourceId == null) {
currentResourceId = obtainResourceId(currentUri);
}
tlockManager.lock(currentResourceId, currentUriStr, parentLockType);
}
tlockManager.lock(resourceId, uri.toString(), lockType);
}
private void cacheResolve(Uri uri, ResourceId resourceId) {
if (uri.getToken() != null) {
uri.getToken().cacheResolve(uri, resourceId);
// System.out.println("@@@ >>> "+uri+"->"+resourceId);
}
}
private ResourceId checkResolveCache(Uri uri) {
ResourceId resourceId = null;
if (uri.getToken() != null) {
resourceId = uri.getToken().checkResolveCache(uri);
}
return resourceId;
}
private ResourceId obtainResourceId(Uri uri) throws ServiceAccessException, ObjectNotFoundException {
ObjectNode objectNode = doRetrieveObjectNode(uri);
ResourceId resourceId = ResourceId.create(uri, objectNode.getUuri());
return resourceId;
}
}