package org.esupportail.webdavserver.store.uPortal;

import java.net.MalformedURLException;
import java.net.URL;
import java.rmi.RemoteException;
import java.security.Principal;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import java.util.Vector;

import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import javax.xml.rpc.ServiceException;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.XMLConfiguration;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.slide.authenticate.CredentialsToken;
import org.apache.slide.common.AbstractXAService;
import org.apache.slide.common.NamespaceAccessToken;
import org.apache.slide.common.ServiceAccessException;
import org.apache.slide.common.ServiceConnectionFailedException;
import org.apache.slide.common.ServiceDisconnectionFailedException;
import org.apache.slide.common.ServiceInitializationFailedException;
import org.apache.slide.common.ServiceParameterErrorException;
import org.apache.slide.common.ServiceParameterMissingException;
import org.apache.slide.common.ServiceResetFailedException;
import org.apache.slide.common.SlideToken;
import org.apache.slide.common.Uri;
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.security.NodePermission;
import org.apache.slide.store.ContentStore;
import org.apache.slide.store.LockStore;
import org.apache.slide.store.NodeStore;
import org.apache.slide.store.RevisionDescriptorStore;
import org.apache.slide.store.RevisionDescriptorsStore;
import org.apache.slide.store.SecurityStore;
import org.apache.slide.structure.ObjectAlreadyExistsException;
import org.apache.slide.structure.ObjectNode;
import org.apache.slide.structure.ObjectNotFoundException;
import org.apache.slide.structure.SubjectNode;
import org.apache.slide.util.logger.Logger;
import org.esupportail.wsclient.PortalGroups.PortalGroups;
import org.esupportail.wsclient.PortalGroups.PortalGroupsServiceLocator;

import fr.univrennes1.cri.util.cacheManagement.CacheManager;
import fr.univrennes1.cri.util.cacheManagement.Cacheable;
import fr.univrennes1.cri.util.commonUtils.CommonUtils;

public class UPortalRolesStore
		extends AbstractXAService
		implements ContentStore, LockStore, NodeStore, RevisionDescriptorStore,
			RevisionDescriptorsStore, SecurityStore, CachedObjectsConstants {

	public static final String CACHE_NAME              = UPortalRolesStore.class.getName();
	public static final String CACHE_OBJECT_PREFIX     = "object: ";
	public static final String CACHE_DESCRIPTOR_PREFIX = "descriptor: ";
	
	public static final String JNDI_PROPERTY_PREFIX = "java.naming";

	// Parameter keys
	public static final String PARAM_UPORTAL_WEBSERVICE_URL = "uportal.webservice.url";
	public static final String PARAM_UPORTAL_STORE_CACHE_DISABLECACHE = "uportal.store.cache.disableCache";
	public static final String PARAM_UPORTAL_STORE_CACHE_SLEEPINGTIME = "uportal.store.cache.cleaningThreadSleepingTimeInMinutes";
	public static final String PARAM_UPORTAL_STORE_CACHE_USERINFO_MTL = "uportal.store.cache.cachedUserInformationMinutesTolive";
	public static final String PARAM_UPORTAL_STORE_CACHE_OBJECTNODE_MTL = "uportal.store.cache.cachedObjectNodeMinutesTolive";
	public static final String PARAM_UPORTAL_STORE_DUMP_CACHE = "uportal.store.cache.dumpCache";
	public static final String PARAM_UPORTAL_STORE_CLEAN_CACHE = "uportal.store.cache.cleanCache";
	public static final String PARAM_UPORTAL_STORE_GROUPMAPPING_FILEPATH = "uportal.store.groupMappingFilePath";
	
	// Const.
	public static final String PARAM_LOG_VALIDATION_ERRORS = "log.validationerrors";
	public static final String DUMP_CACHE_ONLY_USERS = "-u";
	public static final String DUMP_CACHE_ONLY_GROUPS = "-g";
	public static final int INFINITY = (int)10E100;
	public static final String uPortalRootGroupKeyHolder = "UPORTAL_ROOT_GROUP_KEY_HOLDER";
	
	// Default values
	//public static final String LDAP_NAMESPACE = "LDAP:";
	public static final String LOG_CHANNEL = UPortalRolesStore.class.getName();
	protected boolean logValidationErrors = false;
	
	/////////////
	// Parameters
	//
	// Disable the cache !
	public boolean disableCache;
	// Sleeping time between each cache cleaning cycle
	public int sleepingTime;
	// User information TTL in minutes
	public int userInfoMinutesToLive;
	// Object Node TTL in minutes
	public int objectNodeMinutesToLive;
	// key word that will lead to a cache dump
	public String dumpCacheKeyword;
	// key word that will lead to a cache clean
	public String cleanCacheKeyword;
	// Cache manager caching information returned by the web service
	public CacheManager cacheManager;
	// Group mapping file path
	public String groupMappingFilePath;
	
	////////////
	// Variables
	//
	// Usually /users, /roles /roles/uPortal ...
	private String usersPath; // not used for the moment
	private String rolesPath; // usually /roles
	private String roleScope; // usually /roles/uPortal
	
	// WebService variables
	private PortalGroups uPortalWSStore; // web service object
	private String uPortalWebserviceUrl; // web service URL
	
	// Other variables
	private Map mappedGroups;         // ToutLeMonde/local.101/Personnels/pags.COMPPERS/CRI > local.0/local.101/local.104/pags.COMPPERS/pags.PERS_957
	private Map reversedMappedGroups; // local.0/local.101/local.104/pags.COMPPERS/pags.PERS_957 > ToutLeMonde/local.101/Personnels/pags.COMPPERS/CRI
	private long lastCacheDump = System.currentTimeMillis();
	private boolean mappingFilePresent = false;
	private static final Log log = LogFactory.getLog(UPortalRolesStore.class);
	
	/////////////////////////
	// get the connected user
	//
	private String getPrincipal(Uri uri){
		
		String resultPrincipal = "";
		
		SlideToken slideToken = uri.getToken();
		if (slideToken != null){
			CredentialsToken credentialsToken = slideToken.getCredentialsToken();
			if (credentialsToken != null){
				Principal principal = credentialsToken.getPrincipal();
				if (principal != null){
					resultPrincipal = principal.getName();
				}// if (principal != null)
			}// if (credentialsToken != null)
		}// if (slideToken != null)
		
		return resultPrincipal;
		
	}// getPrincipal(Uri uri)
	
	public UPortalRolesStore() {
		if(log.isDebugEnabled()) log.debug("UPortalRolesStore - constructor");
	}
	
	// ----------------------------------------------------------- Service Methods --------

	public void initialize( NamespaceAccessToken token )
			throws ServiceInitializationFailedException {
		
		log.info("initialize");
		
		super.initialize( token );
		
		// Setting usersPath and rolesPath
		usersPath = token.getNamespaceConfig().getUsersPath();
		rolesPath = token.getNamespaceConfig().getRolesPath();
		log.info("initialize:usersPath="+usersPath);
		log.info("initialize:rolesPath="+rolesPath);

		// Reading group mapping file
		try {
			XMLConfiguration groupMappingFile = new XMLConfiguration(groupMappingFilePath);
			log.info("initialize:reading "+groupMappingFilePath+" ...");
			// Validating the XML - DOES NOT WORK
			//groupMappingFile.setFileName(groupMappingFilePath);
			//groupMappingFile.setValidating(true);
			//groupMappingFile.load();
			
			// Loading the XML
			mappedGroups = new HashMap();
			reversedMappedGroups = new HashMap();
			
			int mappingNumber = groupMappingFile.getDocument().getElementsByTagName("mapping").getLength();
			log.info("initialize:number of mapping:"+mappingNumber);
			
			for(int i=0; i<mappingNumber; i++) {
				String alias = (String)groupMappingFile.getProperty("mapping("+i+")[@alias]");
				String targetGroup = (String)groupMappingFile.getProperty("mapping("+i+")[@targetGroup]");
				log.info("initialize:mapping "+i+":"+alias+" > "+targetGroup);
				mappedGroups.put(targetGroup, alias);
				reversedMappedGroups.put(alias, targetGroup);
			}// for(int i=0; i<mappingNumber; i++)
			
			mappingFilePresent = (mappingNumber!=0);
						
			if(log.isDebugEnabled()) log.debug("initialize:mappedGroups="+mappedGroups);
			if(log.isDebugEnabled()) log.debug("initialize:reversedMappedGroups="+reversedMappedGroups);
			
		} catch (ConfigurationException e) {
			log.info("initialize:No mapping file...");
			//throw new ServiceInitializationFailedException(this, e);
		}

//		getLogger().log("initialize:Building the new displayed tree...", LOG_CHANNEL, Logger.INFO );
//		
//		getLogger().log("initialize:>Sorting the groups", LOG_CHANNEL, Logger.DEBUG);
//		sortedDefinedGroups.putAll(definedGroups);
//		getLogger().log("initialize:sorted groups:"+sortedDefinedGroups.toString(), LOG_CHANNEL, Logger.DEBUG);
//		
//		getLogger().log("initialize:>Replacing groups by aliases", LOG_CHANNEL, Logger.DEBUG);
//		Iterator targetGroups = sortedDefinedGroups.keySet().iterator();
//		Vector mappedDefinedGroups = new Vector();
//		Vector mappedDefinedGroups_save = new Vector();
//		while (targetGroups.hasNext()) {
//			String targetGroup = (String) targetGroups.next();
//			mappedDefinedGroups.add(targetGroup);
//			mappedDefinedGroups_save.add(targetGroup);
//		}// while (targetGroups.hasNext())
//		
//		childrenMappedGroups = new HashMap();
//		int mappedDefinedGroups_size = mappedDefinedGroups.size();
//		for(int i=0; i<mappedDefinedGroups_size; i++) {
//			String groupA = (String)mappedDefinedGroups.elementAt(i);
//			String groupA_save = (String)mappedDefinedGroups_save.elementAt(i);
//			String aliasA = (String)definedGroups.get(groupA_save);
//			childrenMappedGroups.put(groupA, aliasA);
//			String mappedGroupA;
//			if (!StringUtils.contains(groupA, "/")) mappedGroupA = aliasA;
//			else mappedGroupA = (StringUtils.substringBeforeLast(groupA,"/"))+"/"+aliasA;
//			getLogger().log("initialize:groupA:"+groupA, LOG_CHANNEL, Logger.DEBUG);
//			getLogger().log("initialize:groupA_save:"+groupA_save, LOG_CHANNEL, Logger.DEBUG);
//			getLogger().log("initialize:aliasA:"+aliasA, LOG_CHANNEL, Logger.DEBUG);
//			getLogger().log("initialize:mappedGroupA:"+mappedGroupA, LOG_CHANNEL, Logger.DEBUG);
//			
//			for(int j=i; j<mappedDefinedGroups_size; j++) {
//				String groupB = (String)mappedDefinedGroups.elementAt(j);
//				getLogger().log("initialize:groupB:"+groupB, LOG_CHANNEL, Logger.DEBUG);
//				if(StringUtils.indexOf(groupB, groupA) == 0) {
//					String newGroupB = StringUtils.replace(groupB, groupA, mappedGroupA);
//					getLogger().log("initialize:newGroupB:"+newGroupB, LOG_CHANNEL, Logger.DEBUG);
//					mappedDefinedGroups.remove(j);
//					mappedDefinedGroups.add(j, newGroupB);
//				}// if(StringUtils.indexOf(groupB, groupA) == 0)
//			}// for(int j=i; i<mappedDefinedGroups_size-1; i++)
//		}// for(int i=0; i<mappedDefinedGroups_size-1; i++) 
//		
//		getLogger().log("initialize:New displayed tree:"+mappedDefinedGroups.toString(), LOG_CHANNEL, Logger.INFO );
//		
//		Map _mappedGroups = new HashMap();
//		Map _reversedMappedGroups = new HashMap();
//		for(int i=0; i<mappedDefinedGroups_size; i++) {
//			_mappedGroups.put(mappedDefinedGroups.elementAt(i), mappedDefinedGroups_save.elementAt(i));
//			_reversedMappedGroups.put(mappedDefinedGroups_save.elementAt(i) ,mappedDefinedGroups.elementAt(i));
//		}// for(int i=0; i<mappedDefinedGroups_size; i++)
//		
//		reversedMappedGroups = new TreeMap(new GroupsComparator_bySize());
//		mappedGroups = new TreeMap(new GroupsComparator_bySize());
//		reversedMappedGroups.putAll(_reversedMappedGroups);
//		mappedGroups.putAll(_mappedGroups);
//		
//		getLogger().log("initialize:mappedGroups:"+mappedGroups.toString());
//		getLogger().log("initialize:_reversedMappedGroups:"+_reversedMappedGroups.toString());
//		getLogger().log("initialize:childrenMappedGroups:"+childrenMappedGroups.toString());
		
	}// initialize

	
	public void setParameters( Hashtable parameters )
			throws ServiceParameterErrorException,
			ServiceParameterMissingException {
		
		log.info("setParameters");
		
		// Setting cache parameters
		String tmp = (String)parameters.get( PARAM_UPORTAL_STORE_CACHE_DISABLECACHE );
		if (!tmp.equalsIgnoreCase("true") && !tmp.equalsIgnoreCase("false")) tmp = "false";
		Boolean bool = Boolean.valueOf(tmp);
		disableCache = bool.booleanValue();
		
		tmp = (String)parameters.get( PARAM_UPORTAL_STORE_CACHE_SLEEPINGTIME );
		sleepingTime = Integer.parseInt(tmp);
		if (sleepingTime < 1) sleepingTime = 1;
		sleepingTime = sleepingTime*60*1000; // min value for sleeping time = 1mn
		
		tmp = (String)parameters.get( PARAM_UPORTAL_STORE_CACHE_USERINFO_MTL );
		userInfoMinutesToLive = Integer.parseInt(tmp);
		if (userInfoMinutesToLive < 1) userInfoMinutesToLive = 1; // min value for TTL = 1mn
		
		tmp = (String)parameters.get( PARAM_UPORTAL_STORE_CACHE_OBJECTNODE_MTL );
		objectNodeMinutesToLive = Integer.parseInt(tmp);
		if (objectNodeMinutesToLive < 1) objectNodeMinutesToLive = 1; // min value for TTL = 1mn
		
		dumpCacheKeyword = (String)parameters.get( PARAM_UPORTAL_STORE_DUMP_CACHE );
		cleanCacheKeyword = (String)parameters.get( PARAM_UPORTAL_STORE_CLEAN_CACHE );
		groupMappingFilePath = (String)parameters.get( PARAM_UPORTAL_STORE_GROUPMAPPING_FILEPATH );
		
		log.info("setParameters:"+
				PARAM_UPORTAL_STORE_GROUPMAPPING_FILEPATH+"="+groupMappingFilePath);
		
		if (!disableCache ){
			log.info("setParameters:starting cache manager");
			log.info("setParameters:"+PARAM_UPORTAL_STORE_CACHE_USERINFO_MTL+"="+userInfoMinutesToLive);
			log.info("setParameters:"+PARAM_UPORTAL_STORE_CACHE_OBJECTNODE_MTL+"="+objectNodeMinutesToLive);
			log.info("setParameters:"+PARAM_UPORTAL_STORE_CACHE_SLEEPINGTIME+"="+sleepingTime);
			log.info("setParameters:"+PARAM_UPORTAL_STORE_CLEAN_CACHE+"="+cleanCacheKeyword);
			log.info("setParameters:"+PARAM_UPORTAL_STORE_DUMP_CACHE+"="+dumpCacheKeyword);
			cacheManager = new CacheManager(sleepingTime); // milliseconds required here
		}else {
			log.info("setParameters:cache manager not started");
		}// if (!disableCache )
		
		// Setting uPortalWebserviceUrl
		uPortalWebserviceUrl = (String)parameters.get( PARAM_UPORTAL_WEBSERVICE_URL );
		if ( uPortalWebserviceUrl == null || uPortalWebserviceUrl.length() == 0 ) {
			log.fatal("no value set for "+PARAM_UPORTAL_WEBSERVICE_URL);
		}else{
			log.info("setParameters:"+PARAM_UPORTAL_WEBSERVICE_URL+"="+uPortalWebserviceUrl);
		}// if ( uPortalWebserviceUrl == null || uPortalWebserviceUrl.length() == 0 )
		
		// Initializing uPortalWSStore
		log.info("setParameters:connecting uPortalWSStore...");
		try {
			uPortalWSStore =
				new PortalGroupsServiceLocator().getPortalGroups(new URL(uPortalWebserviceUrl));
		} catch (ServiceException e) {
			log.fatal("setParameters:"+e.getMessage());
		} catch (MalformedURLException e) {
			log.fatal("setParameters:"+e.getMessage());
		}
		
		// Setting logValidationErrors
		String temp = null;
		temp = (String)parameters.get( PARAM_LOG_VALIDATION_ERRORS );
		if ( "true".equalsIgnoreCase( temp ) ) {
			logValidationErrors = true;
		}
	}// setParameters
	
	public boolean cacheResults() {
		return false;
	}

	
	//------------------------------------------------------ NodeStore Methods ----------
	
	public void storeObject( Uri uri, ObjectNode object )
		throws ServiceAccessException, ObjectNotFoundException {}

	public void createObject( Uri uri, ObjectNode object )
		throws ServiceAccessException, ObjectAlreadyExistsException {}

	public void removeObject( Uri uri, ObjectNode object )
		throws ServiceAccessException, ObjectNotFoundException {}

	public ObjectNode retrieveObject( Uri uri ) throws ServiceAccessException,
	ObjectNotFoundException {
		
		// Getting the connected userName
		String connectedUser = getPrincipal(uri);
		if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):connected user=" + connectedUser);
		if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):uri=" + uri.toString());
				
		////////////////
		// Mapping stuff
		//
		String mappedUri = uri.toString();
		String noScopeRealUri = null;
		String noScopeMappedUri = uri.toString();
		noScopeMappedUri= StringUtils.removeStart(noScopeMappedUri, roleScope);
		noScopeMappedUri= StringUtils.removeStart(StringUtils.removeEnd(noScopeMappedUri, "/"), "/");
		if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):noScopeMappedUri="+noScopeMappedUri);
		
		///////////////////////
		// Cache dump and clean
		//
		// given that the retrieveObject function is called more than one time per WebDAV request and to display only one time
		// the cache when requested, the dump is allowed only every 5 seconds
		boolean dumpFullCache = StringUtils.contains(uri.toString(), dumpCacheKeyword);
		boolean dumpCacheOnlyUsers = StringUtils.contains(uri.toString(), dumpCacheKeyword+DUMP_CACHE_ONLY_USERS);
		boolean dumpCacheOnlyGroups = StringUtils.contains(uri.toString(), dumpCacheKeyword+DUMP_CACHE_ONLY_GROUPS);
		
		long currentDate= System.currentTimeMillis();
		boolean tooEarlyToDump = ((new Long(lastCacheDump+5000)).compareTo(new Long(currentDate))) > 0;
		if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):lastCacheDump:"+lastCacheDump);
		if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):currentDate:  "+currentDate);
		if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):tooEarlyToDump:"+tooEarlyToDump);
		lastCacheDump = currentDate;
		
		if((dumpFullCache || dumpCacheOnlyUsers || dumpCacheOnlyGroups) && !tooEarlyToDump && !disableCache) {
			int counter = 0;
			
			Vector dumpCache = CacheManager.dumpCache();
			Enumeration _dumpCache = dumpCache.elements();
			while (_dumpCache.hasMoreElements()) {
				Cacheable element = (Cacheable) _dumpCache.nextElement();
				
				if ((element instanceof CachedGroupName) && !dumpCacheOnlyUsers){
					log.info("cache:"+counter+" "+element.toString());
				}// if ((element instanceof CachedGroupName) && !dumpCacheOnlyUsers)
				else if ((element instanceof CachedUserInformation) && !dumpCacheOnlyGroups){
					Enumeration groupsOfUser = ((CachedUserInformation)element).getGroupsOfUser().elements();
					String userName = ((CachedUserInformation)element).getUserName();
					int counter2 = 0;
					while (groupsOfUser.hasMoreElements()) {
						Vector groupOfUser = (Vector) groupsOfUser.nextElement();
						log.info("cache:"+counter+"_"+counter2+" "+userName+">"+groupOfUser);
						counter2++;
					}// while (groupsOfUser.hasMoreElements())
				}// else ((element instanceof CachedGroupName) && !dumpCacheOnlyUsers)
				counter++;
				
			}// while (_dumpCache.hasMoreElements())
			//return new SubjectNode(uri.toString(), dumpCache, null);
			Vector tmp = new Vector();
			tmp.add("Cache dump processed.");
			return new SubjectNode(uri.toString(), tmp, null);
		}// if(StringUtils.contains(uri.toString(), dumpCacheKeyword))
	
		// cache cleaning
		if(StringUtils.contains(uri.toString(), cleanCacheKeyword) && !disableCache) {
			CacheManager.deleteCache();
			log.info(">>> cache cleaned <<<");
			return new SubjectNode(uri.toString());
		}// if(StringUtils.contains(uri.toString(), dumpCacheKeyword))
		
		////////////////////////////////////////
		// Trying to find this node in the cache
		//
		if (!disableCache) {
			if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):..getting node "+uri.toString()+" from the cache");
			//TODO RB ICI le uri.toString() renvoie toujours un nouvel objet --> on ne peut pas le retrouver dans le cache. Il faudrait une cle de cache de type sting
			CachedObjectNode cachedObjectNode = (CachedObjectNode) CacheManager.getCache(CACHED_OBJECT_NODE+uri.toString());		
			if (cachedObjectNode != null){ // node found in the cache - returning it
				if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):....node "+uri.toString()+" found in the cache");
				return cachedObjectNode.getObjectNode();
			}// if (cachedObjectNode != null)
			// here, node not found in the cache
			if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):....node "+uri.toString()+" NOT found in the cache");
		} // if (!disableCache)
		
		////////////////
		// Mapping stuff
		//
		Map sortedReversedMappedGroups_bySize = new TreeMap(new GroupsComparator_bySize());
		sortedReversedMappedGroups_bySize.putAll(reversedMappedGroups);
		if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):sortedReversedMappedGroups_bySize="+sortedReversedMappedGroups_bySize);
		if(mappingFilePresent && !uri.toString().equals("/") && !uri.toString().equals(rolesPath) && !uri.isStoreRoot()) {
			
			if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):mapping file present");
			roleScope = uri.getScope().toString();
			if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):roleScope="+roleScope);
			
			boolean noMatchingMappingFound = true;
			Iterator mappedGroupsAliases = sortedReversedMappedGroups_bySize.keySet().iterator();
			int noScopeMappedUriSize = StringUtils.countMatches(noScopeMappedUri, "/");
			if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):noScopeMappedUriSize="+noScopeMappedUriSize);
			String mappedGroupsAlias = null;
			int counter = 0; // just for DEBUG
			while (mappedGroupsAliases.hasNext() && noMatchingMappingFound) {
				
				mappedGroupsAlias = (String) mappedGroupsAliases.next();
				if (StringUtils.indexOf(noScopeMappedUri, mappedGroupsAlias) == 0) {
					noMatchingMappingFound = false;
					if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):(loop "+counter+")matching mapping found !");
				}// if (StringUtils.indexOf(tmpUri, mappedGroupKey) == 0)
				if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):(loop "+counter+")noScopeMappedUri="+noScopeMappedUri);
				if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):(loop "+counter+")mappedGroupsAlias="+mappedGroupsAlias);
				if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):(loop "+counter+")noMatchingMappingFound="+noMatchingMappingFound);
				if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):(loop "+counter+")mappedGroupsAliases.hasNext()="+mappedGroupsAliases.hasNext());
				counter++;
				
			}// while (mappedGroupsAliases.hasNext() && noScopeMappedUriSize<currentAliasSize+1 && noMatchingMappingFound)
			if (!noMatchingMappingFound) {
				String mappedGroupsTargetGroup = (String)reversedMappedGroups.get(mappedGroupsAlias);
				String realUri = StringUtils.replace(uri.toString(), mappedGroupsAlias, mappedGroupsTargetGroup);
				noScopeRealUri = StringUtils.removeStart(realUri, roleScope);
				noScopeRealUri= StringUtils.removeStart(StringUtils.removeEnd(noScopeRealUri, "/"), "/");
				uri.setUri(realUri);
				if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):mappedGroupsTargetGroup="+mappedGroupsTargetGroup);
				if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):realUri="+realUri);
				if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):noScopeRealUri="+noScopeRealUri);
			}else {
				throw new ObjectNotFoundException(uri);
				// TODO
			}// if (!noMatchingMappingFound)
			
		}// if(mappingFilePresent && !uri.toString().equals("/") && !uri.toString().equals(rolesPath) && !uri.isStoreRoot())
		
// OLD STUFF
//		////////////////
//		// Mapping stuff
//		//
//		if(mappingFilePresent && !uri.toString().equals("/") && !uri.toString().equals(rolesPath) && !uri.isStoreRoot()) {
//			getLogger().log("retrieveObject:mapping file present");
//			roleScope = uri.getScope().toString();
//			getLogger().log("retrieveObject:roleScope="+roleScope);
//			
//			String tmpUri = uri.toString();
//			tmpUri= StringUtils.removeStart(tmpUri, roleScope);
//			tmpUri= StringUtils.removeStart(StringUtils.removeEnd(tmpUri, "/"), "/");
//			getLogger().log("retrieveObject:tmpUri="+tmpUri);
//			
//			int tmpUriSize = StringUtils.countMatches(tmpUri, "/");
//			int currentUriSize = 9999;
//			boolean notFound = true;
//			Iterator mappedGroupsKeys = mappedGroups.keySet().iterator();
//			String mappedGroupKey = null;
//			while (mappedGroupsKeys.hasNext() && tmpUriSize<currentUriSize+1 && notFound) {
//				mappedGroupKey = (String) mappedGroupsKeys.next();
//				currentUriSize = StringUtils.countMatches(mappedGroupKey, "/");
//				if (StringUtils.indexOf(tmpUri, mappedGroupKey) == 0) {
//					notFound = false;
//				}// if (StringUtils.indexOf(tmpUri, mappedGroupKey) == 0)
//				getLogger().log("retrieveObject:mappedGroupKey="+mappedGroupKey);
//				getLogger().log("retrieveObject:currentUriSize="+currentUriSize);
//				getLogger().log("retrieveObject:notFound="+notFound);
//			}// while (mappedGroupsKeys.hasNext() && tmpUriSize<currentUriSize && notFound)
//			
//			if (!notFound) {
//				String realUri = (String)mappedGroups.get(mappedGroupKey);
//				newUri = StringUtils.replace(uri.toString(), mappedGroupKey, realUri);
//				uri.setUri(newUri);
//				getLogger().log("retrieveObject:tmpUri="+tmpUri);
//				getLogger().log("retrieveObject:realUri="+realUri);
//				getLogger().log("retrieveObject:newUri="+newUri);
//			}else {
//				//return null;
//				// TODO
//			}
//		}// if(mappingFilePresent && !uri.toString().equals("/") && !uri.toString().equals(rolesPath) && !uri.isStoreRoot())	
		
		/////////////////////////////////////////////////////////////////////////////
		// Checking that the group exist - uPortal does not contain empty group names
		// if the group name is empty then the group does not exist
		// We do not process "/" "/roles" "/roles/uPortal"
		//
		if (!uri.toString().equals("/") && !uri.toString().equals(rolesPath) && !uri.isStoreRoot()){
			// Trying to find the group name in the cache
			String groupKey = getObjectNameFromUri(uri);
			String groupName = null;
			if (!disableCache) {
				if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):..trying to find the group name of the group "+groupKey+" in the cache");
				CachedGroupName cachedGroupName = ((CachedGroupName)CacheManager.getCache(CACHED_GROUP_NAME+groupKey));
				if (cachedGroupName == null) {
					if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):....group name of the group "+groupKey+" NOT found in the cache");
					//groupName = groupKey;
				}
				else{
					if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):....group name of the group "+groupKey+" found in the cache");
					groupName = cachedGroupName.getGroupName();
				}
			}// if (!disableCache)
			// If the cache is disable or the cache is enable and the group name empty, requesting the Web service to retrieve the group name
			if (disableCache || (!disableCache && (groupName==null || groupName.equals("")))) {
				try {
					groupName = uPortalWSStore.getGroupName(groupKey);
				} catch (RemoteException e) {
					log.fatal("retrieveObject("+connectedUser+"):"+e.getMessage());
				}
			}// else if (groupName==null | groupName.equals("")) 
			// Finally if the group name is empty, the group does not exist
			if (groupName==null || groupName.equals("")) {
				if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):node "+uri+" does not exist !");
				throw new ObjectNotFoundException(uri);
			}else if (!disableCache){
				if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):..caching the group name of the group "+groupKey+ " name="+groupName);
				CacheManager.putCache(new CachedGroupName(groupKey, groupName, objectNodeMinutesToLive));
			}// else (groupName==null || groupName.equals(""))
		}// if (!uri.equals("/") && !uri.equals(rolesPath) && !uri.equals(roleScope))
		
		////////////////////////////////////////////////////
		// Getting the portal root group from the webservice
		//
		Vector rootGroupsVector = new Vector();
		String rootGroupKey = null;
		if (!disableCache) {
			if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):..trying to find the root group key in the cache");
			CachedGroupName cachedGroupName = ((CachedGroupName)CacheManager.getCache(CACHED_GROUP_NAME+uPortalRootGroupKeyHolder));
			if (cachedGroupName == null) {
				if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):....root group key NOT found in the cache");
				//groupName = groupKey;
			}
			else{
				if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):....root group key found in the cache");
				rootGroupKey = cachedGroupName.getGroupName();
			}
		}// if (!disableCache)
		if (disableCache || (!disableCache && (rootGroupKey==null || rootGroupKey.equals("")))) {
			try {
				rootGroupKey = uPortalWSStore.getRoot();
				if (!disableCache) CacheManager.putCache(new CachedGroupName(uPortalRootGroupKeyHolder, rootGroupKey, objectNodeMinutesToLive));
				if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):..portal root group="+CommonUtils.convertVectorIntoString(rootGroupsVector));
			} catch (RemoteException e) {
				log.fatal("retrieveObject("+connectedUser+"):"+e.getMessage());
			}
		}// if (disableCache || (!disableCache && (rootGroupKey==null || rootGroupKey.equals(""))))
		rootGroupsVector.add(rootGroupKey);
		
		/////////////////////////////////////////////
		// Getting the nested group keys of this node
		//
		String key = getObjectNameFromUri(uri);
		Vector nestedGroupsOfCurrentGroupVector = new Vector();
		try {
			nestedGroupsOfCurrentGroupVector = uPortalWSStore.getNestedGroupsOfGroup(key); // no need to cache this - the entire node is cached at the end
			if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):..nested group keys="+CommonUtils.convertVectorIntoString(nestedGroupsOfCurrentGroupVector));
		} catch (RemoteException e) {
			log.fatal("retrieveObject("+connectedUser+"):"+e.getMessage());
		}
	
		/////////////////////////////////////////
		// Mapping stuff - the story continues...
		//
		Map sortedReversedMappedGroups_natural = new TreeMap(new GroupsComparator_natural());
		sortedReversedMappedGroups_natural.putAll(reversedMappedGroups);
		if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):sortedReversedMappedGroups_natural="+sortedReversedMappedGroups_natural);
		
		Iterator mappedGroupsAliases = sortedReversedMappedGroups_natural.keySet().iterator();
		Vector _nestedGroupsOfCurrentGroupVector = new Vector();
		boolean lastChildFoundViaMappingFile = false;
		boolean oneChildFoundViaMappingFile = false;
		while (mappedGroupsAliases.hasNext() && !lastChildFoundViaMappingFile) {
			
			String mappedGroupsAlias = (String) mappedGroupsAliases.next();
			if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):mappedGroupsAlias="+mappedGroupsAlias);
			if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):noScopeMappedUri="+noScopeMappedUri);
			if(StringUtils.indexOf(mappedGroupsAlias, noScopeMappedUri) == 0 && !mappedGroupsAlias.equals(noScopeMappedUri)) {
				oneChildFoundViaMappingFile = true;
				String newChild = StringUtils.substringAfterLast(mappedGroupsAlias, noScopeMappedUri);
				newChild = StringUtils.removeStart(StringUtils.removeEnd(newChild, "/"), "/");
				newChild = StringUtils.substringBefore(newChild ,"/");
				if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):newChild="+newChild);
				if (!_nestedGroupsOfCurrentGroupVector.contains(newChild)) _nestedGroupsOfCurrentGroupVector.add(newChild);
			} else {
				if(oneChildFoundViaMappingFile) lastChildFoundViaMappingFile = true;
			}// if(StringUtils.indexOf(mappedGroupsAlias, noScopeMappedUri) == 0 && !mappedGroupsAlias.equals(noScopeMappedUri))
			
		}// while (mappedGroupsAliases.hasNext())
		
		if(oneChildFoundViaMappingFile)	nestedGroupsOfCurrentGroupVector = _nestedGroupsOfCurrentGroupVector;
		
// NO...
//		if (nestedGroupsOfCurrentGroupVector != null && !nestedGroupsOfCurrentGroupVector.isEmpty()) {
//			
//			Vector _nestedGroupsOfCurrentGroupVector = new Vector();
//			Enumeration nestedGroupsOfCurrentGroupEnum = nestedGroupsOfCurrentGroupVector.elements();
//			boolean atLeastOneChildAliased = false;
//			while (nestedGroupsOfCurrentGroupEnum.hasMoreElements()) {
//				String nestedGroupKey = (String) nestedGroupsOfCurrentGroupEnum.nextElement();
//				String fullNestedGroupPath = noScopeRealUri+"/"+nestedGroupKey;
//				getLogger().log("retrieveObject:nestedGroupKey="+nestedGroupKey);
//				getLogger().log("retrieveObject:fullNestedGroupPath="+fullNestedGroupPath);
//				if(mappedGroups.containsKey(fullNestedGroupPath)) {
//					if (!atLeastOneChildAliased) _nestedGroupsOfCurrentGroupVector.removeAllElements();
//					String newNestedGroup = (String)mappedGroups.get(fullNestedGroupPath);
//					newNestedGroup = StringUtils.substringAfterLast(newNestedGroup, "/");
//					getLogger().log("retrieveObject:newNestedGroup="+newNestedGroup);
//					_nestedGroupsOfCurrentGroupVector.add(newNestedGroup);
//					atLeastOneChildAliased = true;
//				}// if(reversedMappedGroups.containsKey(fullNestedGroupPath))
//				else if (!atLeastOneChildAliased){
//					_nestedGroupsOfCurrentGroupVector.add(nestedGroupKey);
//				}// else(reversedMappedGroups.containsKey(fullNestedGroupPath))
//			}// while (nestedGroupsOfCurrentGroupEnum.hasMoreElements())
//			nestedGroupsOfCurrentGroupVector = _nestedGroupsOfCurrentGroupVector;
//			
//		}// if (nestedGroupsOfCurrentGroupVector != null && !nestedGroupsOfCurrentGroupVector.isEmpty())
		uri.setUri(mappedUri);
		
		
// OLD STUFF
//		/////////////////////////////////////////
//		// Mapping stuff - the story continues...
//		//
//		uri.setUri(savedUri);
//		if (nestedGroupsOfCurrentGroupVector != null && !nestedGroupsOfCurrentGroupVector.isEmpty()) {
//			String tmpUri = uri.toString();
//			tmpUri= StringUtils.removeStart(tmpUri, roleScope);
//			tmpUri= StringUtils.removeStart(StringUtils.removeEnd(tmpUri, "/"), "/");
//			getLogger().log("retrieveObject:tmpUri="+tmpUri);
//			
//			Enumeration nestedGroupsOfCurrentGroupEnum = nestedGroupsOfCurrentGroupVector.elements();
//			Vector _nestedGroupsOfCurrentGroupVector = new Vector();
//			while (nestedGroupsOfCurrentGroupEnum.hasMoreElements()) {
//				String nestedGroupKey = (String) nestedGroupsOfCurrentGroupEnum.nextElement();
//				String fullNestedGroupPath = tmpUri+"/"+nestedGroupKey;
//				getLogger().log("retrieveObject:nestedGroupKey="+nestedGroupKey);
//				getLogger().log("retrieveObject:fullNestedGroupPath="+fullNestedGroupPath);
//				if(childrenMappedGroups.containsKey(fullNestedGroupPath)) {
//					String newNestedGroup = (String)childrenMappedGroups.get(fullNestedGroupPath);
//					getLogger().log("retrieveObject:newNestedGroup="+newNestedGroup);
//					_nestedGroupsOfCurrentGroupVector.add(newNestedGroup);
//				}// if(reversedMappedGroups.containsKey(fullNestedGroupPath))
//				else {
//					_nestedGroupsOfCurrentGroupVector.add(nestedGroupKey);
//				}// else(reversedMappedGroups.containsKey(fullNestedGroupPath))
//			}// while (nestedGroupsOfCurrentGroupEnum.hasMoreElements())
//			nestedGroupsOfCurrentGroupVector = _nestedGroupsOfCurrentGroupVector;
//		}// if (nestedGroupsOfCurrentGroupVector != null && !nestedGroupsOfCurrentGroupVector.isEmpty())
				
		/////////////////////////////////////////////////////////////////////////////////////////////
		// If the node is the store root then its nested group is the root returned by the webservice
		//
		// ex : rootPath=/roles
		//      storeRoot=/roles/uPortal < then the nested group of this group is the portal root group
		if (uri.isStoreRoot()){
			if(log.isDebugEnabled()) log.debug("retrieveObject("+connectedUser+"):..group "+key+" is store root");
			nestedGroupsOfCurrentGroupVector = rootGroupsVector;
		}// if (uri.isStoreRoot())

		///////////////////////////////
		// Mapping stuff - ends here...
		//
		String rootGroup = (String)rootGroupsVector.elementAt(0);
		String rootGroupAlias = (String)mappedGroups.get(rootGroup);
		if (uri.isStoreRoot() && rootGroupAlias != null && !rootGroupAlias.equals("")) {
			rootGroupsVector.removeAllElements();
			rootGroupsVector.add(rootGroupAlias);
			nestedGroupsOfCurrentGroupVector = rootGroupsVector;
		}// if (rootGroupAlias != null && !rootGroupAlias.equals(""))
		
// OLD STUFF
//		///////////////////////////////
//		// Mapping stuff - ends here...
//		//
//		String rootGroup = (String)rootGroupsVector.elementAt(0);
//		String rootGroupAlias = (String)reversedMappedGroups.get(rootGroup);
//		if (uri.isStoreRoot() && rootGroupAlias != null && !rootGroupAlias.equals("")) {
//			rootGroupsVector.removeAllElements();
//			rootGroupsVector.add(rootGroupAlias);
//			nestedGroupsOfCurrentGroupVector = rootGroupsVector;
//		}// if (rootGroupAlias != null && !rootGroupAlias.equals(""))
		
		ObjectNode myObjectNode = getObject( uri, nestedGroupsOfCurrentGroupVector);	
		return myObjectNode;
		
	}// retrieveObject

	//-------------------------------------------- RevisionDescriptorStore Methods --------
	
	public void createRevisionDescriptor( Uri uri, NodeRevisionDescriptor revisionDescriptor )
		throws ServiceAccessException {}

	public void storeRevisionDescriptor( Uri uri, NodeRevisionDescriptor revisionDescriptor )
		throws ServiceAccessException, RevisionDescriptorNotFoundException {}

	public void removeRevisionDescriptor( Uri uri, NodeRevisionNumber revisionNumber )
		throws ServiceAccessException {}

	public NodeRevisionDescriptor retrieveRevisionDescriptor( Uri uri,
			NodeRevisionNumber revisionNumber ) throws ServiceAccessException,
			RevisionDescriptorNotFoundException {

		// Getting the connected userName
		String connectedUser = getPrincipal(uri);
		if(log.isDebugEnabled()) log.debug("retrieveRevisionDescriptor("+connectedUser+"):connected user=" + connectedUser);
		if(log.isDebugEnabled()) log.debug("retrieveRevisionDescriptor("+connectedUser+"):uri=" + uri.toString());
		
		////////////////
		// Mapping stuff
		//
		String mappedUri = uri.toString();
		String noScopeMappedUri = uri.toString();
		String noScopeRealUri = null;
		noScopeMappedUri= StringUtils.removeStart(noScopeMappedUri, roleScope);
		noScopeMappedUri= StringUtils.removeStart(StringUtils.removeEnd(noScopeMappedUri, "/"), "/");
		if(log.isDebugEnabled()) log.debug("retrieveRevisionDescriptor("+connectedUser+"):noScopeMappedUri="+noScopeMappedUri);
		
		////////////////
		// Mapping stuff
		//
		Map sortedReversedMappedGroups_bySize = new TreeMap(new GroupsComparator_bySize());
		if(mappingFilePresent && !uri.toString().equals("/") && !uri.toString().equals(rolesPath) && !uri.isStoreRoot()) {
			sortedReversedMappedGroups_bySize.putAll(reversedMappedGroups);
			if(log.isDebugEnabled()) log.debug("retrieveRevisionDescriptor("+connectedUser+"):sortedReversedMappedGroups_bySize="+sortedReversedMappedGroups_bySize);
			
			if(log.isDebugEnabled()) log.debug("retrieveRevisionDescriptor("+connectedUser+"):mapping file present");
			roleScope = uri.getScope().toString();
			if(log.isDebugEnabled()) log.debug("retrieveRevisionDescriptor("+connectedUser+"):roleScope="+roleScope);
			
			boolean noMatchingMappingFound = true;
			Iterator mappedGroupsAliases = sortedReversedMappedGroups_bySize.keySet().iterator();
			int noScopeMappedUriSize = StringUtils.countMatches(noScopeMappedUri, "/");
			if(log.isDebugEnabled()) log.debug("retrieveRevisionDescriptor("+connectedUser+"):noScopeMappedUriSize="+noScopeMappedUriSize);
			String mappedGroupsAlias = null;
			int counter = 0;
			// FIXME factoriser le while dans une méthode à part car travail effectué plusieurs fois dans la classe
			//while (mappedGroupsAliases.hasNext() && noScopeMappedUriSize<=currentAliasSize && noMatchingMappingFound) {
			while (mappedGroupsAliases.hasNext() && noMatchingMappingFound) {
				
				mappedGroupsAlias = (String) mappedGroupsAliases.next();
				if (StringUtils.indexOf(noScopeMappedUri, mappedGroupsAlias) == 0) {
					noMatchingMappingFound = false;
					if(log.isDebugEnabled()) log.debug("retrieveRevisionDescriptor("+connectedUser+"):(loop "+counter+")matching mapping found !");
				}// if (StringUtils.indexOf(tmpUri, mappedGroupKey) == 0)
				if(log.isDebugEnabled()) log.debug("retrieveRevisionDescriptor("+connectedUser+"):(loop "+counter+")mappedGroupsAlias="+mappedGroupsAlias);
				if(log.isDebugEnabled()) log.debug("retrieveRevisionDescriptor("+connectedUser+"):(loop "+counter+")noMatchingMappingFound="+noMatchingMappingFound);
				counter++;
				
			}// while (mappedGroupsAliases.hasNext() && noScopeMappedUriSize<currentAliasSize+1 && noMatchingMappingFound)
			if (!noMatchingMappingFound) {
				String mappedGroupsTargetGroup = (String)reversedMappedGroups.get(mappedGroupsAlias);
				String realUri = StringUtils.replace(uri.toString(), mappedGroupsAlias, mappedGroupsTargetGroup);
				noScopeRealUri = StringUtils.removeStart(realUri, roleScope);
				noScopeRealUri= StringUtils.removeStart(StringUtils.removeEnd(noScopeRealUri, "/"), "/");
				uri.setUri(realUri);
				if(log.isDebugEnabled()) log.debug("retrieveRevisionDescriptor("+connectedUser+"):mappedGroupsTargetGroup="+mappedGroupsTargetGroup);
				if(log.isDebugEnabled()) log.debug("retrieveRevisionDescriptor("+connectedUser+"):realUri="+realUri);
				if(log.isDebugEnabled()) log.debug("retrieveRevisionDescriptor("+connectedUser+"):noScopeRealUri="+noScopeRealUri);
			}else {
				throw new RevisionDescriptorNotFoundException(uri.toString());
			}// if (!noMatchingMappingFound)
			
		}// if(mappingFilePresent && !uri.toString().equals("/") && !uri.toString().equals(rolesPath) && !uri.isStoreRoot())
		
		// Initializing some variables TODO
		Vector groupsOfConnectedUserVector = null; // probably not necessary
		Enumeration groupsOfConnectedUserEnumeration = null; // probably not necessary

		// Getting the role scope
		roleScope = uri.getScope().toString();
		if(log.isDebugEnabled()) log.debug("retrieveRevisionDescriptor("+connectedUser+"):role scope=" + roleScope.toString());
		
		//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		// Trying to find user information in the cache - if not present, requesting the web service and puting gathered info in the cache
		//
		Cacheable cachedUserInformation = null;
		// about the "cachedUserInformation != null" condition : when Slide starts, it calls the retrieveRevisionDescriptor method with no connected user
		// in this case, no need to call the webservice, neither the cache
		if (!disableCache && connectedUser != null && !connectedUser.equals("")){
			if(log.isDebugEnabled()) log.debug("retrieveRevisionDescriptor("+connectedUser+"):trying to find info. in the cache for user " + connectedUser);
			cachedUserInformation = CacheManager.getCache(CACHED_USER_INFORMATION+connectedUser);
		}// if (!disableCache && connectedUser != null && !connectedUser.equals(""))
		if ((cachedUserInformation == null && connectedUser != null && !connectedUser.equals("")) || disableCache){ 
			// ==> no user information for the connected user found in the cache
			if(log.isDebugEnabled()) log.debug("retrieveRevisionDescriptor("+connectedUser+"):no info. in the cache for user " + connectedUser);
						
			// Getting the group(s) of the connected user from the WebService
			try {
				if(log.isDebugEnabled()) log.debug("retrieveRevisionDescriptor("+connectedUser+"):[START]Requesting the uPortal WS getGroupsOfUser method");
				groupsOfConnectedUserVector = uPortalWSStore.getGroupsOfUser(connectedUser);
				if(log.isDebugEnabled()) log.debug("retrieveRevisionDescriptor("+connectedUser+"):[END]Requesting the uPortal WS getGroupsOfUser method");
				if (groupsOfConnectedUserVector == null || groupsOfConnectedUserVector.equals("")) {
					if(log.isDebugEnabled()) log.debug("retrieveRevisionDescriptor("+connectedUser+"):No group retrieved for user "+connectedUser+" !!!");
				}else {
					if(log.isDebugEnabled()) log.debug("retrieveRevisionDescriptor("+connectedUser+"):groups of connected user=" + CommonUtils.convertVectorOfVectorIntoString(groupsOfConnectedUserVector));
				}
			} catch (RemoteException e) {
				throw new RevisionDescriptorNotFoundException(uri.toString());
			}
			
			// ==> caching gathered information
			if (!disableCache && groupsOfConnectedUserVector!=null && !groupsOfConnectedUserVector.isEmpty()) {
				if(log.isDebugEnabled()) log.debug("retrieveRevisionDescriptor("+connectedUser+"):caching gathered info. for user " + connectedUser);
				if(log.isDebugEnabled()) log.debug("retrieveRevisionDescriptor("+connectedUser+"):connectedUser>" + connectedUser);
				if(log.isDebugEnabled()) log.debug("retrieveRevisionDescriptor("+connectedUser+"):userInfoMinutesToLive>" + userInfoMinutesToLive);
				if(log.isDebugEnabled()) log.debug("retrieveRevisionDescriptor("+connectedUser+"):groupsOfConnectedUserVector>" + CommonUtils.convertVectorOfVectorIntoString(groupsOfConnectedUserVector));
				CacheManager.putCache(new CachedUserInformation(groupsOfConnectedUserVector, connectedUser, userInfoMinutesToLive));
			} // if (!disableCache)
		}
		else if (cachedUserInformation != null){
			// ==> user information for the connected user found in the cache
			if(log.isDebugEnabled()) log.debug("retrieveRevisionDescriptor("+connectedUser+"):info. for user " + connectedUser + " found in the cache");
			if(log.isDebugEnabled()) log.debug("retrieveRevisionDescriptor("+connectedUser+"):cachedUserInformation=" + cachedUserInformation.toString());
			groupsOfConnectedUserVector = ((CachedUserInformation) cachedUserInformation).getGroupsOfUser();
		}// if (userInformation == null)
		
		// DEPRECATED
		// Getting the nested group(s) of the current group (uri) from the WebService
//		if ( !uri.isStoreRoot() ) {
//			try {
//				nestedGroupsOfCurrentGroupVector = uPortalWSStore.getNestedGroupsOfGroup(getObjectNameFromUri(uri));
//				getLogger().log( getClass().getName() + "retrieveRevisionDescriptor:nested groups of "+uri+"=" + convertVectorIntoString(nestedGroupsOfCurrentGroupVector),
//						LOG_CHANNEL, Logger.DEBUG );
//			} catch (RemoteException e) {
//				// TODO Auto-generated catch block
//				e.printStackTrace();
//			}
//		}// if ( !uri.isStoreRoot() )
		
		// Building Enumeration to process the results sent by the WebService or retrieved for the cache
		if (groupsOfConnectedUserVector != null){
			Vector temp = new Vector();
			groupsOfConnectedUserEnumeration = groupsOfConnectedUserVector.elements();
			while (groupsOfConnectedUserEnumeration.hasMoreElements()) {
				Vector currentGroupVector = (Vector) groupsOfConnectedUserEnumeration.nextElement();
				temp.add(CommonUtils.buildGroupHierarchy(currentGroupVector));
			}// while (groupsOfConnectedUserEnumeration.hasMoreElements())
			groupsOfConnectedUserEnumeration = temp.elements();
		}// if (groupsOfConnectedUserVector != null)
		
		// DEPRECATED
//		if (nestedGroupsOfCurrentGroupVector != null){
//		nestedGroupsOfCurrentGroupEnumeration = nestedGroupsOfCurrentGroupVector.elements();
//		}// if (nestedGroupsOfCurrentGroupVector != null)

		// This information (real URI when using the mapping file) could be usefull for DAV clients
		NodeRevisionDescriptor temp = getRevisionDescriptor( uri, groupsOfConnectedUserEnumeration);
		if (noScopeRealUri != null && !noScopeRealUri.equals("")) {
			if(log.isDebugEnabled()) log.debug("retrieveRevisionDescriptor("+connectedUser+"):setting metadata uportalrealuri to " + noScopeRealUri);
			temp.setProperty(new NodeProperty( "uportalrealuri", noScopeRealUri, "ESUP:", "", false ));
		}
		else {
			if(log.isDebugEnabled()) log.debug("retrieveRevisionDescriptor("+connectedUser+"):setting metadata uportalrealuri to " + noScopeMappedUri);
			temp.setProperty(new NodeProperty( "uportalrealuri", noScopeMappedUri, "ESUP:", "", false ));
		}
				
		// the following instruction is required!
		uri.setUri(mappedUri);
		
		return temp;
	}// retrieveRevisionDescriptor
	
	// --------------------------------------------- RevisionDescriptorsStore Methods -----

	public NodeRevisionDescriptors retrieveRevisionDescriptors( Uri uri )
			throws ServiceAccessException, RevisionDescriptorNotFoundException {
		
		// Getting the connected userName
		String connectedUser = getPrincipal(uri);
		if(log.isDebugEnabled()) log.debug("retrieveRevisionDescriptors("+connectedUser+"):connected user=" + connectedUser);
		if(log.isDebugEnabled()) log.debug("retrieveRevisionDescriptors("+connectedUser+"):uri=" + uri.toString());
		
		NodeRevisionNumber rev = new NodeRevisionNumber( 1, 0 );
	
		Hashtable workingRevisions = new Hashtable();
		workingRevisions.put( "1.0", rev );
		
		Hashtable latestRevisionNumbers = new Hashtable();
		latestRevisionNumbers.put( "1.0", rev );
	
		// From looking at NodeRevisionDescriptors.cloneObject I see branchNames is
		// supposed to store a Vector. I'm guessing the Vector is of revision numbers
		Vector branches = new Vector();
		branches.add( rev );
		Hashtable branchNames = new Hashtable();
		branchNames.put( "main", branches );
			
		return new NodeRevisionDescriptors(
			uri.toString(),
			rev,
			workingRevisions,
			latestRevisionNumbers,
			branchNames,
			false );
	}// retrieveRevisionDescriptors

	public void createRevisionDescriptors( Uri uri, NodeRevisionDescriptors revisionDescriptors )
		throws ServiceAccessException {}

	public void storeRevisionDescriptors( Uri uri, NodeRevisionDescriptors revisionDescriptors )
		throws ServiceAccessException, RevisionDescriptorNotFoundException {}

	public void removeRevisionDescriptors( Uri uri ) throws ServiceAccessException {}
	
	
	// -------------------------------------------------- ContentStore Methods ----------

	public NodeRevisionContent retrieveRevisionContent(
			Uri uri, NodeRevisionDescriptor revisionDescriptor )
			throws ServiceAccessException, RevisionNotFoundException {
		NodeRevisionContent nrc = new NodeRevisionContent();
		nrc.setContent( new char[0] );
		return nrc;
	}

	public void createRevisionContent(
			Uri uri,
			NodeRevisionDescriptor revisionDescriptor,
			NodeRevisionContent revisionContent )
		throws ServiceAccessException, RevisionAlreadyExistException {}

	public void storeRevisionContent(
			Uri uri,
			NodeRevisionDescriptor revisionDescriptor,
			NodeRevisionContent revisionContent )
		throws ServiceAccessException, RevisionNotFoundException {}

	public void removeRevisionContent( Uri uri, NodeRevisionDescriptor revisionDescriptor )
		throws ServiceAccessException {}
	
	//	 --------------------------------------------------- Security Store Methods ---------------

	public void grantPermission(Uri uri, NodePermission permission) throws ServiceAccessException {
		// TODO Auto-generated method stub
		
	}

	public void revokePermission(Uri uri, NodePermission permission) throws ServiceAccessException {
		// TODO Auto-generated method stub
		
	}

	public void revokePermissions(Uri uri) throws ServiceAccessException {
		// TODO Auto-generated method stub
		
	}

	/**
	 * Always returns read access for all users.
	 */
	public Enumeration enumeratePermissions(Uri uri) throws ServiceAccessException {
		Vector permissions = new Vector();
		permissions.add( new NodePermission( uri.toString(), "all", "/actions/read" ) );
		return permissions.elements();
	}
	
	//	 --------------------------------------------------- LockStore Methods ---------------

	public void putLock(Uri uri, NodeLock lock) throws ServiceAccessException {
		// TODO Auto-generated method stub
		
	}

	public void renewLock(Uri uri, NodeLock lock) throws ServiceAccessException, LockTokenNotFoundException {
		// TODO Auto-generated method stub
		
	}

	public void removeLock(Uri uri, NodeLock lock) throws ServiceAccessException, LockTokenNotFoundException {
		// TODO Auto-generated method stub
		
	}

	public void killLock(Uri uri, NodeLock lock) throws ServiceAccessException, LockTokenNotFoundException {
		// TODO Auto-generated method stub
		
	}

	public Enumeration enumerateLocks(Uri uri) throws ServiceAccessException {
		return new Vector().elements();
	}
	
	// --------------------------------------------------- Worker Methods ---------------
	protected SubjectNode getObject( Uri uri, Vector nestedGroupsOfCurrentGroupVector)
	throws ObjectNotFoundException, ServiceAccessException {
		
		//Getting the connected userName
		String connectedUser = getPrincipal(uri);
		if(log.isDebugEnabled()) log.debug("getObject("+connectedUser+"):connected user=" + connectedUser);
		if(log.isDebugEnabled()) log.debug("getObject("+connectedUser+"):uri=" + uri.toString());
		
		// getting the parent uri and object name
		Uri parentUri = uri.getParentUri();
		String objectName = getObjectNameFromUri( uri );
		
		if(log.isDebugEnabled()) log.debug("getObject("+connectedUser+"):parentUri=" + parentUri.toString());
		if(log.isDebugEnabled()) log.debug("getObject("+connectedUser+"):objectName=" + objectName);
		
		Vector parentBindings = new Vector();
		Vector childBindings = new Vector();
		
		// As long as this node isn't the root node create a parent binding.
		// This doesn't appear to do anything, but just in case.
		if ( !uri.toString().equals( "/" ) ) {
			parentBindings.add( new ObjectNode.Binding( objectName, parentUri.toString() ) );
		}// if ( !uri.toString().equals( "/" ) )
		
		//////////////////////////////
		// Building the child bindings
		//
		if(log.isDebugEnabled()) log.debug("getObject("+connectedUser+"):..creating ChildBindings for "+uri.toString());
		ObjectNode.Binding currentChildBinding;
		childBindings = new Vector();
		
		if (nestedGroupsOfCurrentGroupVector != null){ // no nested groups for the current group
			Enumeration nestedGroupsOfCurrentGroupEnumeration = nestedGroupsOfCurrentGroupVector.elements();
			while (nestedGroupsOfCurrentGroupEnumeration.hasMoreElements()) {
				String element = (String) nestedGroupsOfCurrentGroupEnumeration.nextElement();
				String bindingName = element;
				String bindingUuri = uri.toString()+"/"+element;
				if(log.isDebugEnabled()) log.debug("getObject("+connectedUser+"):....adding ChildBinding - bindingName="+bindingName+" bindingUuri="+bindingUuri);
				currentChildBinding = new ObjectNode.Binding(bindingName, bindingUuri);
				childBindings.add(currentChildBinding);
			}// while (nestedGroupsOfCurrentGroupEnumeration.hasMoreElements())
		}
		else{
			if(log.isDebugEnabled()) log.debug("getObject("+connectedUser+"):..no ChildBindings for "+uri.toString());
		}// if (nestedGroupsOfCurrentGroupVector != null)
		
		///////////////////////////////
		// Building the parent bindings
		//
		String bindingName = getObjectNameFromUri(uri);
		String bindingUuri = getObjectPathFromUri(uri);
		ObjectNode.ParentBinding currentParentBinding= new ObjectNode.ParentBinding(bindingName, bindingUuri);
		parentBindings.add(currentParentBinding);
		
		if(log.isDebugEnabled()) log.debug("getObject("+connectedUser+"):..creating node "+uri.toString());
		
		SubjectNode node = new SubjectNode(
				uri.toString(), childBindings, parentBindings, new Vector() );
		// Workaround for bug in ObjectNode.validate()
		node.setUri( uri.toString() );
		
		// Caching the node
		if (!disableCache){
			if(log.isDebugEnabled()) log.debug("getObject("+connectedUser+"):..caching the node "+uri.toString());
			CacheManager.putCache(new CachedObjectNode(node, uri.toString(), objectNodeMinutesToLive));
		}// if (!disableCache)
		
		return node;
	}
	
// DEPRECATED
//	protected SubjectNode getObject( Uri uri )
//	throws ObjectNotFoundException, ServiceAccessException {
//		
//		getLogger().log( getClass().getName() + "getObject:uri=" + uri.toString(),
//				LOG_CHANNEL, Logger.DEBUG );
//		
//		Uri parentUri = uri.getParentUri();
//		String objectName = getObjectNameFromUri( uri );
//		getLogger().log( getClass().getName() + "getObject:parentUri=" + parentUri.toString(),
//				LOG_CHANNEL, Logger.DEBUG );
//		getLogger().log( getClass().getName() + "getObject:objectName=" + objectName,
//				LOG_CHANNEL, Logger.DEBUG );
//		
//		Vector parentBindings = new Vector();
//		Vector childBindings = new Vector();
//		
//		// As long as this node isn't the root node create a parent binding.
//		// This doesn't appear to do anything, but just in case.
//		// TB I agree... not required for role store
//		if ( !uri.toString().equals( "/" ) ) {
//			parentBindings.add( new ObjectNode.Binding( objectName, parentUri.toString() ) );
//		}// if ( !uri.toString().equals( "/" ) )
//		
//		if ( uri.isStoreRoot() ) {
//						
//			Vector childrenNamesVector = new Vector();
//			Enumeration childrenNames = rootGroupsVector.elements();
//			
//			while (childrenNames.hasMoreElements()) {
//				String value = (String) childrenNames.nextElement();
//				
//				getLogger().log( getClass().getName() + "getObject:creating child binding for "+uri.toString()+" - child="+value,
//						LOG_CHANNEL, Logger.DEBUG );
//				
//				childBindings.add(
//						new ObjectNode.Binding( value, uri.toString() + "/" + value ) );
//				
//			}// while (childrenNames.hasMoreElements())
//			
//		} else {
//			// If the uri matches the scope + something try to do a lookup
//			// of the "+ something" in LDAP.
//			// TB just trying to check that the node exist
//			// TODO check that the ressource exist ?
//			
//			
//			//			Vector parentsNamesVector = new Vector();
//			//			//childrenNamesVector.add("root");
//			//			parentsNamesVector.add("roleTest");
//			//			Enumeration parentsNames = parentsNamesVector.elements();
//			//				
//			//			while (parentsNames.hasMoreElements()) {
//			//				String value = (String) parentsNames.nextElement();
//			//				
//			//				getLogger().log(
//			//						getClass().getName() + " Creating child binding \"" + value + "\" for \"" +
//			//						uri.toString() + "\".",
//			//						LOG_CHANNEL, Logger.DEBUG );
//			//				
//			//				childBindings.add(
//			//						new ObjectNode.Binding( value, uri.toString() + "/" + value ) );
//			//				
//			//			}// while (childrenNames.hasMoreElements())
//			
//		}// if ( uri.isStoreRoot() )
//		
//		getLogger().log( getClass().getName() + "getObject:creating SubjectNode for "+uri.toString(),
//				LOG_CHANNEL, Logger.DEBUG );
//		
//		SubjectNode node = new SubjectNode(
//				uri.toString(), childBindings, parentBindings, new Vector() );
//		// Workaround for bug in ObjectNode.validate()
//		node.setUri( uri.toString() );
//		
//		return node;
//	}// getObject

	
	protected NodeRevisionDescriptor getRevisionDescriptor( Uri uri, Enumeration groupsOfConnectedUserEnumeration)
	throws RevisionDescriptorNotFoundException, ServiceAccessException {
		
		//Getting the connected userName
		String connectedUser = getPrincipal(uri);
		if(log.isDebugEnabled()) log.debug("getRevisionDescriptor("+connectedUser+"):connected user=" + connectedUser);
		if(log.isDebugEnabled()) log.debug("getRevisionDescriptor("+connectedUser+"):uri=" + uri.toString());

		////////////////////////////////////////////
		// Getting the group name of the group "uri"
		//
		// Trying to find the group name in the cache
		String groupKey = getObjectNameFromUri(uri);
		String groupName = null;
		if (!disableCache) {
			if(log.isDebugEnabled()) log.debug("getRevisionDescriptor("+connectedUser+"):..trying to find the group name of the group "+groupKey+" in the cache");
			CachedGroupName cachedGroupName = ((CachedGroupName)CacheManager.getCache(CACHED_GROUP_NAME+groupKey));
			if (cachedGroupName == null) {
				if(log.isDebugEnabled()) log.debug("getRevisionDescriptor("+connectedUser+"):....group name of the group "+groupKey+" NOT found in the cache");
				//groupName = groupKey;
			}
			else{
				if(log.isDebugEnabled()) log.debug("getRevisionDescriptor("+connectedUser+"):....group name of the group "+groupKey+" found in the cache");
				groupName = cachedGroupName.getGroupName();
			}
		}// if (!disableCache)
		
		if (groupName == null) {
			
		try {
				
				// group uri
			    if(log.isDebugEnabled()) log.debug("getRevisionDescriptor("+connectedUser+"):..getting the group name of the group "+groupKey);
				
				groupName = uPortalWSStore.getGroupName(groupKey);
				if (groupName == null || groupName.equals("null")) groupName = groupKey;
				if (groupKey != null){
					getLogger().log("getRevisionDescriptor("+connectedUser+"):....key="+groupKey+" name="+groupName);
				}// if (groupName != null && groupKey != null)
				
				// caching the group name
				// using the same TTL as the one used for the objectNode
				if (!disableCache) {
					if(log.isDebugEnabled()) log.debug("getRevisionDescriptor("+connectedUser+"):..caching the group name of the group "+groupKey+ " name="+groupName);
					CacheManager.putCache(new CachedGroupName(groupKey, groupName, objectNodeMinutesToLive));
				}// if (!disableCache)
				
			} catch (RemoteException e) {
				log.fatal("getRevisionDescriptor("+connectedUser+"):"+e.getMessage());
			}
		}// if (groupName == null)
		
		Hashtable props = new Hashtable();
		StringBuffer instantGroupMemberSet = new StringBuffer();
		
		String resourceType = "<collection/>";
		if ( !uri.isStoreRoot() ) {
			resourceType += "<principal/>";
		}// if ( !uri.isStoreRoot() )
		
		props.put(
				"DAV:resourcetype",
				new NodeProperty( "resourcetype", resourceType, "DAV:", "", false ) );
		props.put(
				"DAV:displayname",
				new NodeProperty( "displayname", groupName, "DAV:", "", false ) );

		
		if ( !uri.isStoreRoot() && connectedUser != null && !connectedUser.equals("")) { // /roles
			
			if(log.isDebugEnabled()) log.debug("getRevisionDescriptor("+connectedUser+"):..finding if "+connectedUser+" is member of "+uri);
			// Trying to find if the groupsOfConnectedUserEnumeration is in the uri
			// uri is like : /roles/scope/roleA/roleB
			// groupsOfConnectedUserEnumeration is like : /roleA/roleB/roleC
			// So if uri is in one of the groups of the connected user, it means that the connected user is member of the group "uri" (or of one of its subgroups) 
			boolean uriIsInGroupsOfConnectedUser = false;
			while (groupsOfConnectedUserEnumeration != null && groupsOfConnectedUserEnumeration.hasMoreElements() && !uriIsInGroupsOfConnectedUser){
				// Roles returned by the WebService does not contain the basedir (/roles)
				String currentRole = roleScope+"/"+groupsOfConnectedUserEnumeration.nextElement();
				
				// NO !!!
				//if ((uri.toString()).indexOf(currentRole) != -1) uriIsInGroupsOfConnectedUser=true;
				if (currentRole.startsWith(uri.toString())) uriIsInGroupsOfConnectedUser=true;
				
				if (uriIsInGroupsOfConnectedUser) {
					if(log.isDebugEnabled()) log.debug("getRevisionDescriptor("+connectedUser+"):....current user role="+currentRole+" >>>>match<<<<");
				}// if (uriIsInGroupsOfConnectedUser)
				else {
					if(log.isDebugEnabled()) log.debug("getRevisionDescriptor("+connectedUser+"):....current user role="+currentRole+" >does not match<");
				}// else (uriIsInGroupsOfConnectedUser)
				
			}// while (groupsOfConnectedUserStringTokenizer.hasMoreElements())
			
			if (!uriIsInGroupsOfConnectedUser && log.isDebugEnabled()) log.debug("getRevisionDescriptor("+connectedUser+"):.."+connectedUser+" is NOT member of "+uri);
			else if(log.isDebugEnabled()) log.debug("getRevisionDescriptor("+connectedUser+"):.."+connectedUser+" is member of "+uri);
			
			// If the uri is in groupsOfConnectedUserEnumeration, it means that the connected user is member of the role "uri"
			// So building a group-member-set for "uri" with the connected user
			if (uriIsInGroupsOfConnectedUser){
				if(log.isDebugEnabled()) log.debug("getRevisionDescriptor("+connectedUser+"):..user "+connectedUser+" is member of "+uri);
				instantGroupMemberSet.append( "<D:href xmlns:D='DAV:'>" );
				instantGroupMemberSet.append(usersPath+"/"+connectedUser);
				instantGroupMemberSet.append( "</D:href>" );
				if(log.isDebugEnabled()) log.debug("getRevisionDescriptor("+connectedUser+"):..building instant group-member-set for "+uri);
				if(log.isDebugEnabled()) log.debug("getRevisionDescriptor("+connectedUser+"):....instantGroupMemberSet="+instantGroupMemberSet);
			}// if (uriIsInGroupsOfConnectedUser)
			
			// DEPRECATED
			// If the current group has nested groups then adding them to the group-member-set property
			//			while (nestedGroupsOfCurrentGroupEnumeration != null && nestedGroupsOfCurrentGroupEnumeration.hasMoreElements()){
			//				instantGroupMemberSet.append( "<D:href xmlns:D='DAV:'>" );
			//				instantGroupMemberSet.append(uri.toString()+"/"+nestedGroupsOfCurrentGroupEnumeration.nextElement());
			//				instantGroupMemberSet.append( "</D:href>" );
			//				getLogger().log( getClass().getName() + "- getRevisionDescriptor() - Building instant group-member-set for "+uri,
			//						LOG_CHANNEL, Logger.DEBUG );
			//				getLogger().log( getClass().getName() + "- getRevisionDescriptor() - instantGroupMemberSet="+instantGroupMemberSet,
			//						LOG_CHANNEL, Logger.DEBUG );
			//			}// if (uriIsInGroupsOfConnectedUser)
			
			
			// cache management	
			
			boolean isGms = uriIsInGroupsOfConnectedUser;		
			
			// DEPRECATED
			//if ( isGms || nestedGroupsOfCurrentGroupEnumeration != null) {
			if ( isGms ) {
				if(log.isDebugEnabled()) log.debug("getRevisionDescriptor("+connectedUser+"):adding property \"group-member-set\" in namespace");
				
				props.put(
						"DAV:group-member-set",
						new NodeProperty(
								"group-member-set",
								instantGroupMemberSet.toString(),
						"DAV:" ) );
			}// if ( isGms )
			//else {
			//					getLogger().log(
			//							getClass().getName() + " Adding property "+ key.toString() +" in namespace " +
			//							"\"DAV:\" with value of \"" + valueString.toString() + "\" to " +
			//							uri.toString() + ".",
			//							LOG_CHANNEL, Logger.DEBUG );
			//					
			//					props.put(
			//							"DAV:"+valueString.toString(),
			//						new NodeProperty(
			//							key.toString(),
			//							valueString.toString(),
			//							"DAV:" ) );
			//}// if ( isGms )
			
		}// if ( !uri.isStoreRoot() )
		
		NodeRevisionDescriptor descriptor = new NodeRevisionDescriptor(
				new NodeRevisionNumber( 1, 0 ),
				"main",
				new Vector(),
				props );
		
		return descriptor;
	}// getRevisionDescriptor
	
	// --------------------------------------------------- Helper Methods ---------------
          
	protected String getObjectNameFromUri( Uri uri ) {
		String objectName = uri.toString().substring(
			uri.toString().lastIndexOf( "/" ) + 1 );
		return objectName;
	}
	
	protected String getObjectPathFromUri( Uri uri ) {
		String objectPath = uri.toString().substring(0,
			uri.toString().lastIndexOf( "/" ));
		return objectPath;
	}
	
	
	/**
	 * Checks a String to make sure it is a valid path element.
	 * 
	 * @param name the name to validate
	 * @return false if any of the validation checks failed.
	 */
	protected boolean validatePathName( String name ) {
		boolean valid = true;
		if ( name.indexOf("/") > -1 ) {
			// Skip names that contain "/". They're evil!! (and they break things)
			valid = false;
			if ( logValidationErrors ) {
				getLogger().log(
					" Skipping child with name \"" + name + "\" because " +
					"it contains a /.",
					LOG_CHANNEL, Logger.ERROR );
			}
		}
		return valid;
	}

	//	 --------------------------------------------------------- XA Methods --------------
	
	/* (non-Javadoc)
	 * @see org.apache.slide.common.AbstractServiceBase#connect()
	 */
	public void connect() throws ServiceConnectionFailedException {
		// TODO Auto-generated method stub
		
	}

	/* (non-Javadoc)
	 * @see org.apache.slide.common.AbstractServiceBase#disconnect()
	 */
	public void disconnect() throws ServiceDisconnectionFailedException {
		// TODO Auto-generated method stub
		
	}

	/* (non-Javadoc)
	 * @see org.apache.slide.common.AbstractServiceBase#reset()
	 */
	public void reset() throws ServiceResetFailedException {
		// TODO Auto-generated method stub
		
	}

	/* (non-Javadoc)
	 * @see org.apache.slide.common.AbstractServiceBase#isConnected()
	 */
	public boolean isConnected() throws ServiceAccessException {
		// TODO Auto-generated method stub
		return false;
	}

	/* (non-Javadoc)
	 * @see javax.transaction.xa.XAResource#getTransactionTimeout()
	 */
	public int getTransactionTimeout() throws XAException {
		// TODO Auto-generated method stub
		return 0;
	}

	/* (non-Javadoc)
	 * @see javax.transaction.xa.XAResource#setTransactionTimeout(int)
	 */
	public boolean setTransactionTimeout(int arg0) throws XAException {
		// TODO Auto-generated method stub
		return false;
	}

	/* (non-Javadoc)
	 * @see javax.transaction.xa.XAResource#isSameRM(javax.transaction.xa.XAResource)
	 */
	public boolean isSameRM(XAResource arg0) throws XAException {
		// TODO Auto-generated method stub
		return false;
	}

	/* (non-Javadoc)
	 * @see javax.transaction.xa.XAResource#recover(int)
	 */
	public Xid[] recover(int arg0) throws XAException {
		// TODO Auto-generated method stub
		return null;
	}

	/* (non-Javadoc)
	 * @see javax.transaction.xa.XAResource#prepare(javax.transaction.xa.Xid)
	 */
	public int prepare(Xid arg0) throws XAException {
		// TODO Auto-generated method stub
		return 0;
	}

	/* (non-Javadoc)
	 * @see javax.transaction.xa.XAResource#forget(javax.transaction.xa.Xid)
	 */
	public void forget(Xid arg0) throws XAException {
		// TODO Auto-generated method stub
		
	}

	/* (non-Javadoc)
	 * @see javax.transaction.xa.XAResource#rollback(javax.transaction.xa.Xid)
	 */
	public void rollback(Xid arg0) throws XAException {
		// TODO Auto-generated method stub
		
	}

	/* (non-Javadoc)
	 * @see javax.transaction.xa.XAResource#end(javax.transaction.xa.Xid, int)
	 */
	public void end(Xid arg0, int arg1) throws XAException {
		// TODO Auto-generated method stub
		
	}

	/* (non-Javadoc)
	 * @see javax.transaction.xa.XAResource#start(javax.transaction.xa.Xid, int)
	 */
	public void start(Xid arg0, int arg1) throws XAException {
		// TODO Auto-generated method stub
		
	}

	/* (non-Javadoc)
	 * @see javax.transaction.xa.XAResource#commit(javax.transaction.xa.Xid, boolean)
	 */
	public void commit(Xid arg0, boolean arg1) throws XAException {
		// TODO Auto-generated method stub
		
	}
	
	//	 ----------------------------------------------- Cache Methods/Classes ---------------

//	protected synchronized void addRefreshee( Uri uri, int refreshType ) {
//		getLogger().log(
//			name + " Adding refreshee for \"" + uri.toString() + "\" of type \"" +
//			(refreshType == Refreshee.REFRESH_OBJECT ? "object" : "descriptor") + "\".",
//			LOG_CHANNEL,
//			Logger.DEBUG );
//		
//		refreshList.add(
//			new Refreshee( uri, System.currentTimeMillis() + refreshRate, refreshType ) );
//	}
//	
//	protected Cache getCache() {
//		CacheManager cacheManager = null;
//		Cache cache = null;
//		try {
//			cacheManager = CacheManagerFactory.getDefaultCacheManager();
//		} catch ( CacheException e ) {
//			getLogger().log(
//				name + " Error getting default CacheManager.",
//				e,
//				LOG_CHANNEL,
//				Logger.ERROR );
//			return null;
//		}
//		cache = cacheManager.getCache( CACHE_NAME );
//		if ( cache == null ) {
//			cache = new Cache(
//				CACHE_NAME,
//				DEFAULT_CACHE_SIZE,
//				DEFAULT_CACHE_OVERFLOW_TO_DISK,
//				DEFAULT_CACHE_ETERNAL,
//				DEFAULT_CACHE_TTL,
//				DEFAULT_CACHE_TTI );
//			try {
//				cacheManager.addCache(cache);
//			} catch ( IllegalStateException e ) {
//				getLogger().log(
//					name + " Error adding cache \"" + CACHE_NAME + "\" to CacheManager.",
//					e,
//					LOG_CHANNEL,
//					Logger.ERROR);
//			} catch ( ObjectExistsException e ) {
//				getLogger().log(
//					name + " Error adding cache \"" + CACHE_NAME + "\" to CacheManager.",
//					e,
//					LOG_CHANNEL,
//					Logger.ERROR);
//			} catch ( CacheException e ) {
//				getLogger().log(
//					name + " Error adding cache \"" + CACHE_NAME + "\" to CacheManager.",
//					e,
//					LOG_CHANNEL,
//					Logger.ERROR);
//			}
//		}
//		return cache;
//	}
//	
//	protected synchronized Refreshee getNextRefreshee() {
//		Refreshee refreshee = null;
//		try {
//			refreshee = (Refreshee)refreshList.last();
//		} catch ( NoSuchElementException e ) {
//			// expected when the list is emtpy
//		}
//		return refreshee;
//	}
//	
//	protected void refreshCache() {
//		Refreshee oldest = getNextRefreshee();
//		long now = System.currentTimeMillis();
//		while ( oldest != null && oldest.getRefreshTime() < now ) {
//			getLogger().log(
//				name + " Refreshing cache for \"" + oldest.getUri().toString() + "\" of type \"" +
//				(oldest.getRefreshType() == Refreshee.REFRESH_OBJECT ? "object" : "descriptor") +
//				"\".",
//				LOG_CHANNEL,
//				Logger.DEBUG );
//			
//			removeRefreshee( oldest );
//			try {
//				Cache cache = getCache();
//				if ( cache != null ) {
//					switch( oldest.getRefreshType() ) {
//						case Refreshee.REFRESH_OBJECT:
//							getObject( oldest.getUri() );
//							break;
//						case Refreshee.REFRESH_DESCRIPTOR:
//							getRevisionDescriptor( oldest.getUri() );
//							break;
//					}
//				}
//			} catch ( ObjectNotFoundException e ) {
//				getLogger().log(
//					name + " Error refreshing cache for \"" + oldest.getUri().toString() + "\".",
//					e,
//					LOG_CHANNEL,
//					Logger.ERROR );
//			} catch ( ServiceAccessException e ) {
//				getLogger().log(
//					name + " Error refreshing cache for \"" + oldest.getUri().toString() + "\".",
//					e,
//					LOG_CHANNEL,
//					Logger.ERROR );
//			} catch ( RevisionDescriptorNotFoundException e ) {
//				getLogger().log(
//					name + " Error refreshing cache for \"" + oldest.getUri().toString() + "\".",
//					e,
//					LOG_CHANNEL,
//					Logger.ERROR );
//			}
//			oldest = getNextRefreshee();
//		}
//	}
//	
//	protected synchronized void removeRefreshee( Refreshee refreshee ) {
//		refreshList.remove( refreshee );
//	}
//	
//	/**
//	 * Implements Comparable so that the older the object, the sooner it needs to be refreshed,
//	 * and therefor the "greater" it is. When used with a SortedSet this ensures objects are
//	 * refreshed in the proper order.
//	 *
//	 */
//	protected class Refreshee implements Comparable {
//		
//		public static final int REFRESH_OBJECT = 0;
//		public static final int REFRESH_DESCRIPTOR = 1;
//		
//		private long refreshTime;
//		private Uri uri;
//		private int refreshType;
//		
//		// No default constructor
//		private Refreshee() {}
//		
//		public Refreshee( Uri uri, long refreshTime, int refreshType ) {
//			this.refreshTime = refreshTime;
//			this.uri = uri;
//			this.refreshType = refreshType;
//		}
//		
//		public int compareTo( Object object ) {
//			if ( object instanceof Refreshee ) {
//				Refreshee other = (Refreshee)object;
//				int result = compare( this.getRefreshTime(), other.getRefreshTime() );
//				if ( result != 0 ) {
//					return result;
//				}
//			}
//			return compare( hashCode(), object.hashCode() );
//		}
//		
//		public long getRefreshTime() {
//			return refreshTime;
//		}
//		
//		public int getRefreshType() {
//			return refreshType;
//		}
//		
//		public Uri getUri() {
//			return uri;
//		}
//		
//		private int compare( long first, long second ) {
//			if (first < second) {
//				return 1;
//			}
//			if (second < first) {
//				return -1;
//			}
//			return 0;
//		}
//	}
//	
//	protected class RefreshThread extends Thread {
//		
//		private boolean run;
//		
//		public RefreshThread() {
//			super( "RefreshThread-" + name );
//			setPriority( Thread.MIN_PRIORITY );
//			setDaemon( false );
//		}
//		
//		public synchronized void halt() {
//			run = false;
//		}
//		
//		public void run() {
//			run = true;
//			while ( keepRunning() ) {
//				try {
//					Thread.sleep( refreshThreadSleepTime );
//				} catch ( InterruptedException e ) {}
//				refreshCache();
//			}
//		}
//		
//		private synchronized boolean keepRunning() {
//			return run;
//		}
//	}

}