/*
* $Header: /home/cvs/jakarta-slide/src/share/org/apache/slide/structure/StructureImpl.java,v 1.39.2.1 2004/02/05 16:05:13 mholz Exp $
* $Revision: 1.39.2.1 $
* $Date: 2004/02/05 16:05:13 $
*
* ====================================================================
*
* 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.structure;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.Vector;
import org.apache.slide.common.Namespace;
import org.apache.slide.common.NamespaceConfig;
import org.apache.slide.common.ServiceAccessException;
import org.apache.slide.common.SlideToken;
import org.apache.slide.common.SlideTokenWrapper;
import org.apache.slide.common.Uri;
import org.apache.slide.common.UriPath;
import org.apache.slide.common.UriTokenizer;
import org.apache.slide.content.NodeRevisionDescriptor;
import org.apache.slide.content.NodeRevisionNumber;
import org.apache.slide.content.RevisionDescriptorNotFoundException;
import org.apache.slide.lock.Lock;
import org.apache.slide.lock.ObjectLockedException;
import org.apache.slide.security.AccessDeniedException;
import org.apache.slide.security.Security;
import org.apache.slide.store.Store;
import org.apache.slide.util.Configuration;
/**
* Default implementation of the Structure interface.
*
* @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
* @version $Revision: 1.39.2.1 $
*/
public final class StructureImpl implements Structure {
// ----------------------------------------------------------- Constructors
/**
* Constructor.
*
* @param namespace the namespace associated with the helper object
* @param namespaceConfig configuration of the namespace
* @param securityHelper the associated security helper
* @param lockHelper the associated lock helper
*/
public StructureImpl(Namespace namespace, NamespaceConfig namespaceConfig,
Security securityHelper, Lock lockHelper) {
this.namespace = namespace;
this.namespaceConfig = namespaceConfig;
this.securityHelper = securityHelper;
this.lockHelper = lockHelper;
}
// ----------------------------------------------------- Instance Variables
/**
* Namespace.
*/
private Namespace namespace;
/**
* Namespace configuration.
*/
private NamespaceConfig namespaceConfig;
/**
* Security helper.
*/
private Security securityHelper;
/**
* Lock helper.
*/
private Lock lockHelper;
// ------------------------------------------------------ Structure Methods
public Enumeration getChildren(SlideToken token, ObjectNode object)
throws ServiceAccessException, ObjectNotFoundException,
LinkedObjectNotFoundException {
Enumeration childrenUri = object.enumerateChildren();
Vector result = new Vector();
while (childrenUri.hasMoreElements()) {
String childUri = (String) childrenUri.nextElement();
try {
ObjectNode child = retrieve(token, childUri, false);
result.addElement(child);
} catch (AccessDeniedException e) {
}
}
return result.elements();
}
public ObjectNode getParent(SlideToken token, ObjectNode object)
throws ServiceAccessException, ObjectNotFoundException,
LinkedObjectNotFoundException, AccessDeniedException {
String objectUriStr = object.getUri();
Uri parentUri = namespace.getUri(token, objectUriStr).getParentUri();
if (parentUri == null) {
return null;
}
String parentUriStr = parentUri.toString();
ObjectNode parent = retrieve(token, parentUriStr);
return parent;
}
public ObjectNode retrieve(SlideToken token, String strUri)
throws ServiceAccessException, ObjectNotFoundException,
LinkedObjectNotFoundException, AccessDeniedException {
return retrieve(token, strUri, true);
}
public ObjectNode retrieve(SlideToken token, String strUri,
boolean translateLastUriElement)
throws ServiceAccessException, ObjectNotFoundException,
LinkedObjectNotFoundException, AccessDeniedException {
Uri uri = namespace.getUri(token, strUri);
ObjectNode result = null;
// First of all, we try to load the object directly from the given Uri.
try {
result = uri.getStore().retrieveObject(uri);
securityHelper.checkCredentials
(token, result, namespaceConfig.getReadObjectAction());
if ((translateLastUriElement) && (result instanceof LinkNode)) {
LinkNode link = (LinkNode) result;
Uri linkedUri = namespace.getUri(token, link.getLinkedUri());
result = linkedUri.getStore()
.retrieveObject(linkedUri);
securityHelper.checkCredentials
(token, result, namespaceConfig.getReadObjectAction());
}
} catch (ObjectNotFoundException e) {
}
// If the attempt to load the uri failed, it means there is at least
// one link in the uri (or that the uri doe'sn't have any associated
// object).
if (result == null) {
String resolvedUri = uri.toString();
// 1 - Tokemization of the Uri
UriTokenizer uriTokenizer = new UriTokenizer(token, uri.getNamespace(),
resolvedUri);
// 2 - For each element of the Uri
Uri courUri = null;
ObjectNode courObject = null;
while (uriTokenizer.hasMoreElements()) {
// 3 - Load object's class from the uri. If the object
// does not exist, a DataException is thrown.
courUri = uriTokenizer.nextUri();
courObject = courUri.getStore()
.retrieveObject(courUri);
// We check to see if the credentials gives access to
//the current object
securityHelper.checkCredentials
(token, courObject, namespaceConfig.getReadObjectAction());
// 4 - Test if object is a link, ie if it is an instance
// of LinkNode or one of its subclasses
if (((translateLastUriElement)
&& (courObject instanceof LinkNode)) ||
((!translateLastUriElement)
&& (uriTokenizer.hasMoreElements())
&& (courObject instanceof LinkNode))
) {
// 5 - If the object is a link, we get the uri of
// the linked object
// Note : courUri still IS the Uri of the link, and so,
// in a way courUri is the parent of linkedUri.
Uri linkedUri = namespace
.getUri(((LinkNode) courObject).getLinkedUri());
// 6 - We replace the courUri scope in the original uri
String courStrUri = courUri.toString();
resolvedUri = linkedUri.toString()
+ resolvedUri.substring(courStrUri.length());
// 7 - We tokenize again the uri
uriTokenizer = new UriTokenizer(token, uri.getNamespace(),
resolvedUri);
// 8 - We parse it till we get back to the point
// where we stopped
boolean isUriFound = false;
while ((!isUriFound) && (uriTokenizer.hasMoreElements())) {
if (linkedUri.equals(uriTokenizer.nextUri())) {
isUriFound = true;
}
}
if (!isUriFound) {
throw new LinkedObjectNotFoundException(courUri,
resolvedUri);
}
}
// 9 - We continue to go down in the Uri tree
}
// 10 - We return the last object which has been found
result = courObject;
}
return result;
}
public void create(SlideToken token, ObjectNode object,
String strUri)
throws ServiceAccessException, ObjectAlreadyExistsException,
ObjectNotFoundException, LinkedObjectNotFoundException,
AccessDeniedException, ObjectLockedException {
// Checking roles
Enumeration roles = securityHelper.getRoles(object);
while (roles.hasMoreElements()) {
if (!securityHelper.hasRole(token, (String)roles.nextElement())) {
// Allow only the namespace admin to create roles
// he doesn't have
Uri rootUri = namespace.getUri(token, "/");
ObjectNode rootObject =
rootUri.getStore().retrieveObject(rootUri);
securityHelper.checkCredentials
(token, rootObject,
namespaceConfig.getGrantPermissionAction());
break;
}
}
String resolvedUri = strUri;
// 1 - Tokenization of the Uri
UriTokenizer uriTokenizer = new UriTokenizer(token, namespace, resolvedUri);
// 2 - For each element of the Uri
Uri courUri = null;
ObjectNode courObject = null;
ObjectNode parentObject = null;
boolean alreadyExists = false;
while (uriTokenizer.hasMoreElements()) {
parentObject = courObject;
// 3 - Load object's class from the uri. If the object does
// not exist, a DataException is thrown.
courUri = uriTokenizer.nextUri();
try {
courObject = courUri.getStore()
.retrieveObject(courUri);
securityHelper
.checkCredentials(token, courObject,
namespaceConfig.getReadObjectAction());
if (!uriTokenizer.hasMoreElements()) {
// The object already exists
alreadyExists = true;
}
} catch (ObjectNotFoundException e) {
// Load failed, probably because object was not found
// We try to create a new one.
// We have to test if the uri is the last in the list,
// we must create the requested element.
// By default, we create a SubjectNode.
ObjectNode newObject = null;
if (uriTokenizer.hasMoreElements()) {
throw new ObjectNotFoundException(courUri);
} else {
newObject = object;
}
if (parentObject != null) {
securityHelper
.checkCredentials(token, courObject, namespaceConfig
.getBindMemberAction());
// Now creating the new object
newObject.setUri(courUri.toString());
courUri.getStore().createObject(courUri, newObject);
// re-read to obtain UURI
// newObject = courUri.getStore().retrieveObject(courUri);
// re-read the parent taking the forceEnlistment flag into account
Uri parentUri = namespace.getUri(token, parentObject.getUri());
parentObject = parentUri.getStore().retrieveObject(parentUri);
// Add the newly created object to its parent's
// children list
ObjectNode oldChild = null;
// we can check the parentUri, it's in the same store as newObject
if (Configuration.useBinding(parentUri.getStore())) {
String bindingName = newObject.getPath().lastSegment();
if (parentObject.hasBinding(bindingName)) {
oldChild = retrieve(token, parentObject.getUri()+"/"+bindingName, false);
parentObject.removeChild(oldChild);
store(token, oldChild);
}
}
lockHelper.checkLock
(token, parentObject, namespaceConfig.getCreateObjectAction());
parentObject.addChild(newObject);
//namespace.getUri(token, parentObject.getUri())
//.getDataSource().storeObject(parentObject, false);
store(token, parentObject, true);
store(token, newObject);
} else {
throw new ObjectNotFoundException(courUri);
}
courObject = newObject;
}
// 4 - Test if object is a link, ie if it is an instance of
// LinkNode or one of its subclasses
if ((uriTokenizer.hasMoreElements())
&& (courObject instanceof LinkNode)) {
// 5 - If the object is a link, we get the uri of the
// linked object
// Note : courUri still IS the Uri of the link, and so,
// in a way courUri is the parent of linkedUri.
Uri linkedUri = namespace
.getUri(((LinkNode) courObject).getLinkedUri());
// 6 - We replace the courUri scope in the original uri
String courStrUri = courUri.toString();
resolvedUri = linkedUri.toString()
+ resolvedUri.substring(courStrUri.length());
// 7 - We tokenize again the uri
uriTokenizer = new UriTokenizer(token, namespace, resolvedUri);
// 8 - We parse it till we get back to the point
// where we stopped
boolean isUriFound = false;
while ((!isUriFound) && (uriTokenizer.hasMoreElements())) {
if (linkedUri.equals(uriTokenizer.nextUri())) {
isUriFound = true;
}
}
if (!isUriFound) {
throw new LinkedObjectNotFoundException
(courUri, resolvedUri);
}
}
// 9 - We continue to go down in the Uri tree
}
if (alreadyExists) {
if (courUri.isStoreRoot()) {
// if the object already exists map it anyway into
// the node hierarchy, to prevent loose of nodes
// during start up
if (!parentObject.hasChild(courObject)) {
parentObject.addChild(courObject);
store(token, parentObject, true);
}
}
throw new ObjectAlreadyExistsException(strUri);
}
}
public void createLink(SlideToken token, LinkNode link,
String linkUri, ObjectNode linkedObject)
throws ServiceAccessException, ObjectAlreadyExistsException,
ObjectNotFoundException, LinkedObjectNotFoundException,
AccessDeniedException, ObjectLockedException {
link.setLinkedUri(linkedObject.getUri());
create(token, link, linkUri);
}
public void store(SlideToken token, ObjectNode object)
throws ServiceAccessException, ObjectNotFoundException,
AccessDeniedException, LinkedObjectNotFoundException {
store(token, object, false);
}
public void store(SlideToken token, ObjectNode object, boolean setModificationDate)
throws ServiceAccessException, ObjectNotFoundException,
AccessDeniedException, LinkedObjectNotFoundException {
// Checking roles
Enumeration roles = securityHelper.getRoles(object);
while (roles.hasMoreElements()) {
if (!securityHelper.hasRole(token, (String)roles.nextElement())) {
// Allow only the namespace admin to create roles
// he doesn't have
Uri rootUri = namespace.getUri(token, "/");
ObjectNode rootObject =
rootUri.getStore().retrieveObject(rootUri);
securityHelper.checkCredentials
(token, rootObject,
namespaceConfig.getGrantPermissionAction());
break;
}
}
ObjectNode realObject = retrieve(token, object.getUri(), false);
securityHelper
.checkCredentials(token, realObject,
namespaceConfig.getCreateObjectAction());
Uri realObjectUri = namespace.getUri(token, realObject.getUri());
Store store = realObjectUri.getStore();
store.storeObject(realObjectUri, object);
if (setModificationDate) {
try {
NodeRevisionDescriptor revisionDescriptor = store.retrieveRevisionDescriptor(realObjectUri, new NodeRevisionNumber());
revisionDescriptor.setModificationDate(new Date());
revisionDescriptor.setModificationUser(
securityHelper.getPrincipal(token).getPath().lastSegment());
store.storeRevisionDescriptor(realObjectUri, revisionDescriptor );
}
catch (RevisionDescriptorNotFoundException e) {
// ignore silently
}
}
}
/**
* Method remove
*
* @param token a SlideToken
* @param object an ObjectNode
*
* @throws ServiceAccessException
* @throws ObjectNotFoundException
* @throws ObjectHasChildrenException
* @throws AccessDeniedException
* @throws LinkedObjectNotFoundException
* @throws ObjectLockedException
*
*/
public void remove(SlideToken token, ObjectNode object)
throws ServiceAccessException, ObjectNotFoundException,
ObjectHasChildrenException, AccessDeniedException,
LinkedObjectNotFoundException, ObjectLockedException {
ObjectNode nodeToDelete = retrieve(token, object.getUri(), false);
Uri uri = namespace.getUri(token, nodeToDelete.getUri());
if (!object.getUri().equals("/")) {
Uri curUri = namespace.getUri(token, nodeToDelete.getUri());
Uri parentUri = curUri.getParentUri();
ObjectNode parentNode = parentUri.getStore().retrieveObject(parentUri);
securityHelper.checkCredentials
(token, nodeToDelete, namespaceConfig.getRemoveObjectAction());
securityHelper.checkCredentials
(token, parentNode, namespaceConfig.getUnbindMemberAction());
lockHelper.checkLock
(token, nodeToDelete, namespaceConfig.getRemoveObjectAction());
lockHelper.checkLock
(token, parentNode, namespaceConfig.getUnbindMemberAction());
parentNode.removeChild(nodeToDelete);
store(token, parentNode, true);
if (Configuration.useBinding(curUri.getStore()) && nodeToDelete.numberOfParentBindings() > 0) {
store(token, nodeToDelete);
}
else {
Enumeration enum = nodeToDelete.enumerateChildren();
if (enum.hasMoreElements()) {
throw new ObjectHasChildrenException(uri);
}
uri.getStore().removeObject(uri, nodeToDelete);
}
}
}
/**
* Modifies the collection identified by <b>collectionNode</b>, by adding a new binding
* from the specified segment to the resource identified by <b>sourceNode</b>.
*
* @param token a SlideToken
* @param collectionNode an ObjectNode
* @param segment a String
* @param sourceNode an ObjectNode
*
* @throws ServiceAccessException
* @throws ObjectNotFoundException
* @throws AccessDeniedException
* @throws LinkedObjectNotFoundException
* @throws ObjectLockedException
*
*/
public void addBinding(SlideToken token, ObjectNode collectionNode, String segment, ObjectNode sourceNode) throws ServiceAccessException, ObjectNotFoundException, AccessDeniedException, LinkedObjectNotFoundException, ObjectLockedException, CrossServerBindingException {
if (Configuration.useBinding(namespace.getUri(token, collectionNode.getUri()).getStore())) {
collectionNode = retrieve(token, collectionNode.getUri(), false);
sourceNode = retrieve(token, sourceNode.getUri(), false);
Uri collectionUri = namespace.getUri(token, collectionNode.getUri());
Uri sourceUri = namespace.getUri(token, sourceNode.getUri());
// if (collectionUri.getStore() != sourceUri.getStore()) {
// throw new CrossServerBindingException(collectionNode.getUri(), sourceNode.getUri());
// }
lockHelper.checkLock
(token, collectionNode, namespaceConfig.getCreateObjectAction());
ObjectNode oldChild = null;
if (collectionNode.hasBinding(segment)) {
oldChild = retrieve(token, collectionNode.getUri()+"/"+segment, false);
lockHelper.checkLock
(token, oldChild, namespaceConfig.getCreateObjectAction());
collectionNode.removeChild(oldChild);
store( token, oldChild );
}
collectionNode.addBinding( segment, sourceNode );
store( token, collectionNode, true );
store( token, sourceNode );
}
}
/**
* Modifies the collection identified by <b>collectionNode</b>, by removing the binding
* for the specified segment.
*
* @param token a SlideToken
* @param collectionNode an ObjectNode
* @param segment a String
*
* @throws ServiceAccessException
* @throws ObjectNotFoundException
* @throws AccessDeniedException
* @throws LinkedObjectNotFoundException
* @throws ObjectLockedException
*
*/
public void removeBinding(SlideToken token, ObjectNode collectionNode, String segment) throws ServiceAccessException, ObjectNotFoundException, AccessDeniedException, LinkedObjectNotFoundException, ObjectLockedException {
if (Configuration.useBinding(namespace.getUri(token, collectionNode.getUri()).getStore())) {
collectionNode = retrieve(token, collectionNode.getUri(), false);
ObjectNode childNode = retrieve(token, collectionNode.getUri()+"/"+segment, false);
lockHelper.checkLock
(token, collectionNode, namespaceConfig.getCreateObjectAction());
lockHelper.checkLock
(token, childNode, namespaceConfig.getCreateObjectAction());
collectionNode.removeChild( childNode );
store( token, childNode );
store( token, collectionNode, true );
}
}
/**
* Return all parents of this object node. If pathOnly=true, only parents
* on the path of the specified ObjectNode are returned, all parents (binding!)
* otherwise. If storeOnly=true, only parents within the scope of the store
* in charge of the specified ObjectNode are returned, parents up to the root
* ObjectNode (uri="/") otherwise.
*
* @param token a SlideToken
* @param object an ObjectNode
* @param pathOnly if true, only parents on the path of the specified
* ObjectNode are returned, all parents (binding!)
* otherwise
* @param storeOnly if true, only parents within the scope of the store
* in charge of the specified ObjectNode are returned,
* parents up to the root ObjectNode (uri="/") otherwise
* @param includeSelf if true, the ObjectNode specified by object is included,
* otherwise, it is excluded
*
* @return a List of ObjectNode instances
*
* @throws ServiceAccessException
* @throws ObjectNotFoundException
* @throws LinkedObjectNotFoundException
* @throws AccessDeniedException
*
*/
public List getParents(SlideToken token, ObjectNode object, boolean pathOnly, boolean storeOnly, boolean includeSelf) throws ServiceAccessException, ObjectNotFoundException, LinkedObjectNotFoundException, AccessDeniedException {
List result = new ArrayList();
if (token.isForceStoreEnlistment()) {
// get read-only token
token = new SlideTokenWrapper(token, false);
}
if (pathOnly) {
String[] uriTokens = object.getPath().tokens();
UriPath path = new UriPath("/");
Uri currentUri = namespace.getUri(token, path.toString());
Uri objectUri = namespace.getUri(token, object.getUri());
if (!storeOnly || currentUri.getStore() == objectUri.getStore()) {
result.add( retrieve(token, path.toString()) );
}
for (int i = 0; i < uriTokens.length; i++) {
path = path.child( uriTokens[i] );
currentUri = namespace.getUri(token, path.toString());
if (i == uriTokens.length - 1 && !includeSelf) {
break;
}
if (!storeOnly || currentUri.getStore() == objectUri.getStore()) {
result.add( retrieve(token, path.toString()) );
}
}
}
else {
// TODO
}
return result;
}
}