Merge DEV/V3.4-BUG-FIX to HEAD (28799-28800,28864,28875,28879,28916,28941,28956,28970,28993)

28993: HomeFolderProviderSynchronizer: Provider used for LDAP to continue to use username as home folder name directly under .../app:company_home/app:user_homes
   Property spaces.user_homes.regex.pattern now set to "" rather than "^(..)" which would have given a single level hash structure.
   28970: Merge DEV/ALAN/HOME_FOLDER to DEV/V3.4-BUG-FIX
      28947: - Introduction of version 2 HomeFolderProvider2 and re-factor of version 1
        so that the code used to create the folders is now in the HomeFolderManager.
      - Re-factor homeFolderProviderSynchronizer to handle HomeFolderProvider2.
      - Addition of AbstractHomeFolderProvider.V2Adaptor to allow external providers
        that extend AbstractHomeFolderPathProvider to be handled by
        homeFolderProviderSynchronizer.
      28860: Minor change to class comment and removed unused imports
      28858: ALF-4727 Hashed home folder provider added and used by default for LDAP sync users  - based on Romain Guinot work.
      ALF-7797 HomeFolderProviderSynchronizer added to move existing users (normally those added by LDAP sync) into location preferred by home folder provider.
      - HomeFolderProviderSynchronizer bug fixes
        - tenant accounts supported for first time
        - addition of a phase to create parent folders before moving home folder to avoid a race condition
        - check for conditions that would result in FileExistExceptions as we don't want a the transaction to be discarded as this results in retries.  
      - HomeFolderProviderSynchronizerTest integration test including tenant services
      - HomeFolderManager now sets the HOME_FOLDER_PROVIDER if it uses a default when HOME_FOLDER_PROVIDER is not set.
      - AbstractHomeFolderProvider clears cache when path reset as it will be invalid.
      - UIDBasedHomeFolderProvider.createNewParent creates its own mutable List as the one passed in may not be mutable.
      28580: Save code changes - added comments to do with LDAP syn overriding the HFP value and related to this added a global property to keep empty parent folders. 
      28347: HomeFolderProviderSynchronizer
      - issue to do with new run of sync having created a user via UI that has a home folder as one of the parent folders.
      - issue to do with catching exception when creating temporary folder - transaction is gone
      - give up if error in any phase
      28298: Addition of HomeFolderPathProvider (based on Romain's work) and addition of HomeFolderProviderSynchronizer.
   28956: Merged DEV to V3.4-BUG-FIX
      ALF-9428: Multitenancy users not preserved after upgrade from 3.2.2.7 to 3.4.2
                - Provide correct RunAs context in FixUserQNamesPatch batching.
   28941: ALF-9361 : CLONE -sync Flat IMAP client with Alfresco is slow and inaccurate
   28916: ALF-9421 The AlfrescoJavaScript action now includes company home in the JavaScript scope.
   28879: Fixed ALF-9296: Alfresco Dashboard: Impossible to approve/reject task from My Tasks dashlet on My Alfresco
   28875: Fixed ALF-6329: SPANISH - Share, translation on Transfer Target configuration
   28864: Message:
   ALF-9430: RuntimeExec waitForCompletion logic is obscure
    - Only a single flag 'isCompleted'
    - Set flag in try-finally
    - Added notify()
   However, the wait() code doesn't, in practice, get called because the waitForCompletion is synchronized with the run()
   and is called a while after the reading thread is triggered.  So the logic is less obscure and safer for the finally.
   28800: File for rev 28799: ALF-9240
   28799: Merged DEV to V3.4-BUG-FIX
      28797: ALF-9240: Issue with adding an aspect with large multivalued list
             Added unit test to stress, but could not reproduce

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@28995 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Alan Davis
2011-07-13 16:47:03 +00:00
parent aa700571db
commit 86f9dfea17
31 changed files with 3836 additions and 293 deletions

View File

@@ -375,13 +375,22 @@
<ref bean="userHomesHomeFolderProvider" />
</property>
<property name="enableHomeFolderCreationAsPeopleAreCreated">
<!--<value>false</value> -->
<value>${home.folder.creation.eager}</value>
</property>
<!-- Requests services via ServiceRegistry for audit -->
<property name="serviceRegistry">
<ref bean="ServiceRegistry" />
</property>
<property name="tenantService">
<ref bean="tenantService" />
</property>
</bean>
<bean name="baseHomeFolderProvider" class="org.alfresco.repo.security.person.AbstractHomeFolderProvider" abstract="true">
<!-- Requests services via ServiceRegistry for auditability -->
<!-- deprecated use baseHomeFolderProvider2 -->
<bean name="baseHomeFolderProvider"
class="org.alfresco.repo.security.person.AbstractHomeFolderProvider"
abstract="true">
<!-- Requests services via ServiceRegistry for audit -->
<property name="serviceRegistry">
<ref bean="ServiceRegistry" />
</property>
@@ -393,8 +402,47 @@
</property>
</bean>
<bean name="companyHomeFolderProvider" class="org.alfresco.repo.security.person.ExistingPathBasedHomeFolderProvider" parent="baseHomeFolderProvider">
<property name="path">
<bean name="baseHomeFolderProvider2"
class="org.alfresco.repo.security.person.AbstractHomeFolderProvider2"
abstract="true">
<property name="homeFolderManager">
<ref bean="homeFolderManager" />
</property>
</bean>
<bean name="existingHomeFolderProvider"
class="org.alfresco.repo.security.person.ExistingPathBasedHomeFolderProvider2"
abstract="true" parent="baseHomeFolderProvider2">
</bean>
<bean name="usernameHomeFolderProvider"
class="org.alfresco.repo.security.person.UsernameHomeFolderProvider"
abstract="true" parent="baseHomeFolderProvider2">
<property name="onCreatePermissionsManager">
<ref bean="defaultOnCreatePermissionsManager" />
</property>
<property name="onReferencePermissionsManager">
<ref bean="defaultOnReferencePermissionsManager" />
</property>
</bean>
<bean name="regexHomeFolderProvider"
class="org.alfresco.repo.security.person.RegexHomeFolderProvider"
abstract="true" parent="usernameHomeFolderProvider">
<property name="propertyName">
<value>${spaces.user_homes.regex.key}</value>
</property>
<property name="pattern">
<value>${spaces.user_homes.regex.pattern}</value>
</property>
<property name="groupOrder">
<value>${spaces.user_homes.regex.group_order}</value>
</property>
</bean>
<bean name="companyHomeFolderProvider" parent="existingHomeFolderProvider">
<property name="rootPath">
<value>/${spaces.company_home.childname}</value>
</property>
<property name="storeUrl">
@@ -402,7 +450,8 @@
</property>
</bean>
<bean name="guestHomeFolderProviderPermissionsManager" class="org.alfresco.repo.security.person.PermissionsManagerImpl">
<bean name="guestHomeFolderProviderPermissionsManager"
class="org.alfresco.repo.security.person.PermissionsManagerImpl">
<property name="permissionService">
<ref bean="permissionServiceImpl" />
</property>
@@ -417,12 +466,8 @@
</bean>
<bean name="guestHomeFolderProvider" class="org.alfresco.repo.security.person.ExistingPathBasedHomeFolderProvider" parent="baseHomeFolderProvider">
<!-- Requests services via ServiceRegistry for auditability -->
<property name="serviceRegistry">
<ref bean="ServiceRegistry" />
</property>
<property name="path">
<bean name="guestHomeFolderProvider" parent="existingHomeFolderProvider">
<property name="rootPath">
<value>/${spaces.company_home.childname}/${spaces.guest_home.childname}</value>
</property>
<property name="storeUrl">
@@ -436,9 +481,12 @@
</property>
</bean>
<bean name="bootstrapHomeFolderProvider" class="org.alfresco.repo.security.person.BootstrapHomeFolderProvider" parent="baseHomeFolderProvider" />
<bean name="bootstrapHomeFolderProvider"
class="org.alfresco.repo.security.person.BootstrapHomeFolderProvider"
parent="baseHomeFolderProvider2" />
<bean name="defaultOnCreatePermissionsManager" class="org.alfresco.repo.security.person.PermissionsManagerImpl" >
<bean name="defaultOnCreatePermissionsManager"
class="org.alfresco.repo.security.person.PermissionsManagerImpl" >
<property name="permissionService">
<ref bean="permissionServiceImpl" />
</property>
@@ -460,7 +508,8 @@
</property>
</bean>
<bean name="defaultOnReferencePermissionsManager" class="org.alfresco.repo.security.person.PermissionsManagerImpl" >
<bean name="defaultOnReferencePermissionsManager"
class="org.alfresco.repo.security.person.PermissionsManagerImpl" >
<property name="permissionService">
<ref bean="permissionServiceImpl" />
</property>
@@ -474,40 +523,32 @@
</property>
</bean>
<bean name="personalHomeFolderProvider" class="org.alfresco.repo.security.person.UIDBasedHomeFolderProvider" parent="baseHomeFolderProvider">
<!-- Requests services via ServiceRegistry for auditability -->
<property name="serviceRegistry">
<ref bean="ServiceRegistry" />
</property>
<property name="path">
<bean name="personalHomeFolderProvider" parent="usernameHomeFolderProvider">
<property name="rootPath">
<value>/${spaces.company_home.childname}</value>
</property>
<property name="storeUrl">
<value>${spaces.store}</value>
</property>
<property name="onCreatePermissionsManager">
<ref bean="defaultOnCreatePermissionsManager" />
</property>
<property name="onReferencePermissionsManager">
<ref bean="defaultOnReferencePermissionsManager" />
</property>
</bean>
<bean name="userHomesHomeFolderProvider" class="org.alfresco.repo.security.person.UIDBasedHomeFolderProvider" parent="baseHomeFolderProvider">
<property name="path">
<bean name="userHomesHomeFolderProvider" parent="usernameHomeFolderProvider">
<property name="rootPath">
<value>/${spaces.company_home.childname}/${spaces.user_homes.childname}</value>
</property>
<property name="storeUrl">
<value>${spaces.store}</value>
</property>
<property name="onCreatePermissionsManager">
<ref bean="defaultOnCreatePermissionsManager" />
</property>
<property name="onReferencePermissionsManager">
<ref bean="defaultOnReferencePermissionsManager" />
</property>
</bean>
<bean name="largeHomeFolderProvider" parent="regexHomeFolderProvider">
<property name="rootPath">
<value>/${spaces.company_home.childname}/${spaces.user_homes.childname}</value>
</property>
<property name="storeUrl">
<value>${spaces.store}</value>
</property>
</bean>
<!-- The ticket component. -->

View File

@@ -687,6 +687,18 @@
</property>
</bean>
<!-- Synchronization of home folders locations to their HomeFolderProvider -->
<bean id="homeFolderProviderSynchronizer" class="org.alfresco.repo.security.person.HomeFolderProviderSynchronizer">
<constructor-arg ref="global-properties" />
<constructor-arg ref="transactionService" />
<constructor-arg ref="authorityService" />
<constructor-arg ref="personService" />
<constructor-arg ref="nodeService" />
<constructor-arg ref="fileFolderService" />
<constructor-arg ref="homeFolderManager" />
<constructor-arg ref="tenantAdminService" />
</bean>
<!-- User registry synchronization jobs (e.g. LDAP) -->
<bean id="Synchronization" class="org.alfresco.repo.management.subsystems.ChildApplicationContextFactory" parent="abstractPropertyBackedBean">
<property name="autoStart">

View File

@@ -175,7 +175,7 @@
<title>Can this resource be enabled/disabled.</title>
<properties>
<property name="trx:enabled">
<title>Is this enabled.</title>
<title>Enabled</title>
<type>d:boolean</type>
<mandatory>true</mandatory>
</property>

View File

@@ -384,6 +384,9 @@ spaces.wcm.childname=app:wcm
spaces.wcm_content_forms.childname=app:wcm_forms
spaces.content_forms.childname=app:forms
spaces.user_homes.childname=app:user_homes
spaces.user_homes.regex.key=userName
spaces.user_homes.regex.pattern=
spaces.user_homes.regex.group_order=
spaces.sites.childname=st:sites
spaces.templates.email.invite.childname=cm:invite
spaces.templates.email.activities.childname=cm:activities
@@ -399,6 +402,7 @@ spaces.extension_webscripts.childname=cm:extensionwebscripts
spaces.models.childname=app:models
spaces.workflow.definitions.childname=app:workflow_defs
# ADM VersionStore Configuration
version.store.deprecated.lightWeightVersionStore=workspace://lightWeightVersionStore
version.store.version2Store=workspace://version2Store

View File

@@ -99,7 +99,7 @@ ldap.synchronization.userEmailAttributeName=mail
ldap.synchronization.userOrganizationalIdAttributeName=company
# The default home folder provider to use for people created via LDAP import
ldap.synchronization.defaultHomeFolderProvider=userHomesHomeFolderProvider
ldap.synchronization.defaultHomeFolderProvider=largeHomeFolderProvider
# The attribute on LDAP group objects to map to the authority name property in Alfresco
ldap.synchronization.groupIdAttributeName=cn

View File

@@ -105,7 +105,7 @@ ldap.synchronization.userEmailAttributeName=mail
ldap.synchronization.userOrganizationalIdAttributeName=o
# The default home folder provider to use for people created via LDAP import
ldap.synchronization.defaultHomeFolderProvider=userHomesHomeFolderProvider
ldap.synchronization.defaultHomeFolderProvider=largeHomeFolderProvider
# The attribute on LDAP group objects to map to the authority name property in Alfresco
ldap.synchronization.groupIdAttributeName=cn

View File

@@ -33,6 +33,7 @@ import org.alfresco.repo.batch.BatchProcessor;
import org.alfresco.repo.batch.BatchProcessor.BatchProcessWorker;
import org.alfresco.repo.domain.qname.QNameDAO;
import org.alfresco.repo.importer.ImporterBootstrap;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.rule.RuleService;
@@ -110,18 +111,22 @@ public class FixUserQNamesPatch extends AbstractPatch implements ApplicationEven
20,
this.applicationEventPublisher, logger, 1000);
final String runAsUser = AuthenticationUtil.getRunAsUser();
int updated = batchProcessor.process(new BatchProcessWorker<ChildAssociationRef>()
{
public void beforeProcess() throws Throwable
{
// Disable rules
ruleService.disableRules();
AuthenticationUtil.setRunAsUser(runAsUser);
}
public void afterProcess() throws Throwable
{
// Enable rules
ruleService.enableRules();
AuthenticationUtil.clearCurrentSecurityContext();
}
public String getIdentifier(ChildAssociationRef entry)

View File

@@ -480,7 +480,14 @@ public class AlfrescoImapFolder extends AbstractImapFolder implements Serializab
{
logger.debug("[getMessageInternal] " + this);
}
AbstractMimeMessage mes = (AbstractMimeMessage) messages.get(uid).getMimeMessage();
SimpleStoredMessage storedMessage = messages.get(uid);
if (storedMessage == null)
{
messagesCache.remove(uid);
msnCache.remove(uid);
return null;
}
AbstractMimeMessage mes = (AbstractMimeMessage) storedMessage.getMimeMessage();
FileInfo mesInfo = mes.getMessageInfo();
Date modified = (Date) serviceRegistry.getNodeService().getProperty(mesInfo.getNodeRef(), ContentModel.PROP_MODIFIED);
@@ -1181,7 +1188,10 @@ public class AlfrescoImapFolder extends AbstractImapFolder implements Serializab
if (nodeService.hasAspect(folderNodeRef, ImapModel.ASPECT_IMAP_FOLDER))
{
modifDate = ((Long) nodeService.getProperty(folderNodeRef, ImapModel.PROP_UIDVALIDITY));
return (modifDate - YEAR_2005) / 1000;
// we need tens part of the second at least, because
// we should avoid issues when several changes were completed within a second.
// so, divide by 100 instead of 1000. see ImapServiceImplCacheTest#testRepoBehaviourWithFoldersCache()
return (modifDate - YEAR_2005) / 100;
}
}
return new Long(0);

View File

@@ -100,6 +100,8 @@ import org.springframework.extensions.surf.util.AbstractLifecycleBean;
import org.springframework.extensions.surf.util.I18NUtil;
import org.springframework.util.FileCopyUtils;
import com.icegreen.greenmail.imap.ImapConstants;
/**
* @author Dmitry Vaserin
* @author Arseny Kovalchuk
@@ -216,6 +218,11 @@ public class ImapServiceImpl implements ImapService, OnCreateChildAssociationPol
this.foldersCache = foldersCache;
}
public SimpleCache<Serializable, Object> getFoldersCache()
{
return foldersCache;
}
public FileFolderService getFileFolderService()
{
return fileFolderService;
@@ -792,11 +799,13 @@ public class ImapServiceImpl implements ImapService, OnCreateChildAssociationPol
/**
* Check whether resultFolder is stale
*/
/*
if(resultFolder.isStale())
{
logger.debug("folder is stale");
resultFolder = null;
}
*/
}
if (resultFolder == null)
@@ -841,7 +850,7 @@ public class ImapServiceImpl implements ImapService, OnCreateChildAssociationPol
NodeRef targetNode = fileFolderService.searchSimple(nodeRef, folderNames[i]);
if (targetNode == null)
if (i == 0 && targetNode == null)
{
resultFolder = new AlfrescoImapFolder(user.getQualifiedMailboxName(), serviceRegistry);
if (logger.isDebugEnabled())
@@ -1294,6 +1303,20 @@ public class ImapServiceImpl implements ImapService, OnCreateChildAssociationPol
logger.debug("listMailboxes returning size:" + result.size());
StringBuilder prefix = new StringBuilder(128);
prefix.append(ImapConstants.USER_NAMESPACE)
.append(AlfrescoImapConst.HIERARCHY_DELIMITER)
.append(user.getQualifiedMailboxName())
.append(AlfrescoImapConst.HIERARCHY_DELIMITER);
int prefixLength = prefix.length();
for(AlfrescoImapFolder folder : result)
{
String cacheKey = folder.getFullName().substring(prefixLength + 1, folder.getFullName().length() - 1);
logger.debug("[listMailboxes] Adding the cache entry : " + cacheKey);
foldersCache.put(cacheKey, folder);
}
return result;
}
@@ -2150,6 +2173,38 @@ public class ImapServiceImpl implements ImapService, OnCreateChildAssociationPol
}
}
public void beforeDeleteNode(NodeRef nodeRef)
{
NodeRef parentNodeRef = nodeService.getPrimaryParent(nodeRef).getParentRef();
if (ContentModel.TYPE_FOLDER.equals(nodeService.getType(nodeRef)))
{
// If a node is a folder, we need to remove its' cache with its' children cache as well
invalidateFolderCacheByNodeRef(nodeRef, true);
}
else if (ContentModel.TYPE_CONTENT.equals(nodeService.getType(nodeRef)))
{
// If a node is a content, it is simpler to remove its' parent cache
// to avoid of deal with folder messages cache
invalidateFolderCacheByNodeRef(parentNodeRef, false);
}
else
{
return;
}
// Add a listener once, when a lots of messsages were created/moved into the folder
if (AlfrescoTransactionSupport.getResource(UIDVALIDITY_LISTENER_ALREADY_BOUND) == null)
{
AlfrescoTransactionSupport.bindListener(new UidValidityTransactionListener(parentNodeRef, nodeService));
AlfrescoTransactionSupport.bindResource(UIDVALIDITY_LISTENER_ALREADY_BOUND, true);
}
if (logger.isDebugEnabled())
{
logger.debug("[beforeDeleteNode] Node " + nodeRef + " going to be removed. UIDVALIDITY will be changed for " + parentNodeRef);
}
}
private class UidValidityTransactionListener extends TransactionListenerAdapter
{
@@ -2202,7 +2257,7 @@ public class ImapServiceImpl implements ImapService, OnCreateChildAssociationPol
}
if (logger.isDebugEnabled())
{
logger.debug("UIDVALIDITY was modified");
logger.debug("UIDVALIDITY was modified for " + folderNodeRef);
}
return modifDate;
}
@@ -2212,6 +2267,69 @@ public class ImapServiceImpl implements ImapService, OnCreateChildAssociationPol
}
private void invalidateFolderCacheByNodeRef(NodeRef folderNodeRef, boolean invalidateChildren)
{
if (logger.isDebugEnabled())
{
if (invalidateChildren)
{
logger.debug("[invalidateFolderCacheByNodeRef] Invalidate cache entries for " + folderNodeRef);
}
else
{
logger.debug("[invalidateFolderCacheByNodeRef] Invalidate cache entries for " + folderNodeRef + " and children");
}
}
if (invalidateChildren)
{
SimpleCache<Serializable, Object> foldersCache = getFoldersCache();
List<Serializable> toRemove = new LinkedList<Serializable>();
for(Serializable name : foldersCache.getKeys())
{
AlfrescoImapFolder folder = (AlfrescoImapFolder) foldersCache.get(name);
if (folderNodeRef.equals(folder.getFolderInfo().getNodeRef()))
{
toRemove.add(name);
break;
}
}
if (toRemove.size() > 0)
{
String rootName = (String) toRemove.get(0);
for(Serializable name : foldersCache.getKeys())
{
if (((String) name).startsWith(rootName))
{
toRemove.add(name);
}
}
if (logger.isDebugEnabled())
{
logger.debug("Caches to invalidate: " + toRemove.toString());
}
for(Serializable name : toRemove)
{
foldersCache.remove(name);
}
}
}
else
{
SimpleCache<Serializable, Object> foldersCache = getFoldersCache();
for(Serializable name : foldersCache.getKeys())
{
AlfrescoImapFolder folder = (AlfrescoImapFolder) foldersCache.get(name);
if (folderNodeRef.equals(folder.getFolderInfo().getNodeRef()))
{
foldersCache.remove(name);
break;
}
}
}
}
/**
* Return true if provided nodeRef is in Sites/.../documentlibrary
*/

View File

@@ -0,0 +1,224 @@
package org.alfresco.repo.imap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import junit.framework.TestCase;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory;
import org.alfresco.repo.model.filefolder.FileFolderServiceImpl;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.MutableAuthenticationService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.config.RepositoryFolderConfigBean;
import org.springframework.context.ApplicationContext;
import com.icegreen.greenmail.store.SimpleStoredMessage;
/**
* Unit test for cache implementation in the ImapServiceImpl. Based on ImapServiceImplTest, but
* we need this separate test because we need to get transactions to commit to trigger behaviours in ImapServiceImpl.
*
* @author ArsenyKo
*/
public class ImapServiceImplCacheTest extends TestCase
{
private static final String USER_NAME = "admin";
private static final String USER_PASSWORD = "admin";
private static final String TEST_IMAP_FOLDER_NAME = "aaa";
private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
private NodeService nodeService;
private MutableAuthenticationService authenticationService;
private SearchService searchService;
private NamespaceService namespaceService;
private FileFolderService fileFolderService;
private ContentService contentService;
private ImapService imapService;
private NodeRef testImapFolderNodeRef;
@Override
public void setUp() throws Exception
{
ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean("ServiceRegistry");
nodeService = serviceRegistry.getNodeService();
authenticationService = serviceRegistry.getAuthenticationService();
imapService = serviceRegistry.getImapService();
searchService = serviceRegistry.getSearchService();
namespaceService = serviceRegistry.getNamespaceService();
fileFolderService = serviceRegistry.getFileFolderService();
contentService = serviceRegistry.getContentService();
authenticationService.authenticate(USER_NAME, USER_PASSWORD.toCharArray());
String storePath = "workspace://SpacesStore";
String companyHomePathInStore = "/app:company_home";
StoreRef storeRef = new StoreRef(storePath);
NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef);
List<NodeRef> nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore, null, namespaceService, false);
NodeRef companyHomeNodeRef = nodeRefs.get(0);
ChildApplicationContextFactory imap = (ChildApplicationContextFactory) ctx.getBean("imap");
ApplicationContext imapCtx = imap.getApplicationContext();
ImapServiceImpl imapServiceImpl = (ImapServiceImpl)imapCtx.getBean("imapService");
// Creating IMAP test folder for IMAP root
LinkedList<String> folders = new LinkedList<String>();
folders.add(TEST_IMAP_FOLDER_NAME);
FileFolderServiceImpl.makeFolders(fileFolderService, companyHomeNodeRef, folders, ContentModel.TYPE_FOLDER);
// Setting IMAP root
RepositoryFolderConfigBean imapHome = new RepositoryFolderConfigBean();
imapHome.setStore(storePath);
imapHome.setRootPath(companyHomePathInStore);
imapHome.setFolderPath(TEST_IMAP_FOLDER_NAME);
imapServiceImpl.setImapHome(imapHome);
// Starting IMAP
imapServiceImpl.startup();
nodeRefs = searchService.selectNodes(storeRootNodeRef,
companyHomePathInStore + "/" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + TEST_IMAP_FOLDER_NAME,
null,
namespaceService,
false);
testImapFolderNodeRef = nodeRefs.get(0);
}
public void tearDown() throws Exception
{
fileFolderService.delete(testImapFolderNodeRef);
}
public void testRepoBehaviourWithFoldersCache() throws Exception
{
AlfrescoImapUser localUser = new AlfrescoImapUser(USER_NAME + "@alfresco.com", USER_NAME, USER_PASSWORD);
String folderName = "ALF9361";
String mailbox = "Alfresco IMAP" + AlfrescoImapConst.HIERARCHY_DELIMITER +
TEST_IMAP_FOLDER_NAME + AlfrescoImapConst.HIERARCHY_DELIMITER +
folderName;
int contentItemsCount = 3;
// Create a tree like ALF9361/ALF9361_0/sub_0
// Mailbox path with default mount point should be like 'Alfresco IMAP/aaa/ALF9361/ALF9361_0/sub_0
FileInfo localRootFolder = fileFolderService.create(testImapFolderNodeRef, folderName, ContentModel.TYPE_FOLDER);
List<FileInfo> subFolders = new ArrayList<FileInfo>(10);
for(int i = 0; i < 3; i++)
{
String childMailbox = folderName + "_" + i;
FileInfo subFolder = fileFolderService.create(localRootFolder.getNodeRef(), childMailbox, ContentModel.TYPE_FOLDER);
for(int j = 0; j < 3; j++)
{
String subChildMailbox = "sub_" + j;
fileFolderService.create(subFolder.getNodeRef(), subChildMailbox, ContentModel.TYPE_FOLDER);
}
subFolders.add(subFolder);
}
// Create content within 'Alfresco IMAP/aaa/ALF9361'
createTestContent(localRootFolder, contentItemsCount);
// Load the cache
imapService.listMailboxes(localUser, "*");
imapService.listSubscribedMailboxes(localUser, "*");
// Get the folder to examine
AlfrescoImapFolder folder = imapService.getFolder(localUser, mailbox);
// Check the folder exist via IMAP
assertNotNull("Folder wasn't successfully gotten from IMAP", folder);
assertEquals(contentItemsCount, folder.getMessageCount());
// Check UIDVALIDITY
long uidValidityBefore = folder.getUidValidity();
// Delete first childMailbox 'ALF9361/ALF9361_0'
//System.out.println(" --------------------- DELETE FOLDER --------------------");
//System.out.println(" Parent " + localRootFolder.getNodeRef());
fileFolderService.delete(subFolders.get(0).getNodeRef());
// Get the folder once more and check it was changed since child was removed
folder = imapService.getFolder(localUser, mailbox);
// Content count should be the same since we havn't deleted a content yet
assertEquals(contentItemsCount, folder.getMessageCount());
long uidValidity = folder.getUidValidity();
assertTrue("UIDVALIDITY wasn't incremented", (uidValidity - uidValidityBefore) > 0);
uidValidityBefore = uidValidity;
// Try to get deleted child
try
{
String subFolderName = mailbox + AlfrescoImapConst.HIERARCHY_DELIMITER + folderName + "_0";
folder = imapService.getFolder(localUser, subFolderName);
fail("The folder still in the cache");
}
catch (RuntimeException e)
{
// expected
}
// Try to get deleted sub child. If the cache wasn't invalidated we will get it
// But it should be connected to AlfrescoImapFolder.isStale() method.
// ArsenyKo: I think we should avoid repo API invocation like isStale...
try
{
String subSubFolderName = mailbox + AlfrescoImapConst.HIERARCHY_DELIMITER + mailbox + "_0" + AlfrescoImapConst.HIERARCHY_DELIMITER + "sub_0";
folder = imapService.getFolder(localUser, subSubFolderName);
fail("The folder still in the cache");
}
catch (RuntimeException e)
{
// expected
}
// Do manipulations with a content in the folder to check the cache behaviour
folder = imapService.getFolder(localUser, mailbox);
SimpleStoredMessage message = folder.getMessages().get(0);
AbstractMimeMessage alfrescoMessage = (AbstractMimeMessage) message.getMimeMessage();
long uid = message.getUid();
//System.out.println(" --------------------- DELETE FILE --------------------");
//System.out.println(" Parent " + folder.getFolderInfo().getNodeRef());
// Delete a content
fileFolderService.delete(alfrescoMessage.getMessageInfo().getNodeRef());
// Get a folder once again. We expect that the folder would be retrieved from the repo,
// since its' cache should be invalidated
folder = imapService.getFolder(localUser, mailbox);
// Get UIDVALIDITY. It should be changed, since we removed a message form the mailbox.
uidValidity = folder.getUidValidity();
assertTrue("UIDVALIDITY wasn't incremented", (uidValidity - uidValidityBefore) > 0);
// Additional check whether messages cache is valid. Messages cache should be recreated
//with the new inctance of AlfrescoImapMessage
assertTrue("Messages cache is stale", contentItemsCount > folder.getMessageCount());
long[] uids = folder.getMessageUids();
Arrays.sort(uids);
assertFalse("Messages msn cache is stale", Arrays.binarySearch(uids, uid) > 0);
assertNull("Message is still in the messages cache", folder.getMessage(uid));
//System.out.println(" --------------------- THE END --------------------");
fileFolderService.delete(localRootFolder.getNodeRef());
}
private List<FileInfo> createTestContent(FileInfo parent, int count)
{
List<FileInfo> result = new ArrayList<FileInfo>(count);
for(int i = 0; i < count; i++)
{
FileInfo contentItem = fileFolderService.create(parent.getNodeRef(), "content_" + i, ContentModel.TYPE_CONTENT, ContentModel.ASSOC_CONTAINS);
ContentWriter contentWriter = contentService.getWriter(contentItem.getNodeRef(), ContentModel.PROP_CONTENT, false);
contentWriter.setEncoding("UTF-8");
contentWriter.putContent("TEST" + i);
}
return result;
}
}

View File

@@ -441,8 +441,14 @@ public class ScriptServiceImpl implements ScriptService
// add the well known node wrapper objects
model.put("companyhome", companyHome);
if (userHome!= null)
{
model.put("userhome", userHome);
}
if (person != null)
{
model.put("person", person);
}
if (script != null)
{
model.put("script", script);

View File

@@ -18,9 +18,11 @@
*/
package org.alfresco.repo.security;
import junit.framework.JUnit4TestAdapter;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.alfresco.repo.audit.access.AccessAuditorTest;
import org.alfresco.repo.ownable.impl.OwnableServiceTest;
import org.alfresco.repo.security.authentication.AuthenticationBootstrapTest;
import org.alfresco.repo.security.authentication.AuthenticationTest;
@@ -37,6 +39,7 @@ import org.alfresco.repo.security.permissions.impl.acegi.ACLEntryAfterInvocation
import org.alfresco.repo.security.permissions.impl.acegi.ACLEntryVoterTest;
import org.alfresco.repo.security.permissions.impl.acegi.FilteringResultSetTest;
import org.alfresco.repo.security.permissions.impl.model.PermissionModelTest;
import org.alfresco.repo.security.person.HomeFolderProviderSynchronizerTest;
import org.alfresco.repo.security.person.PersonTest;
/**
@@ -74,6 +77,8 @@ public class SecurityTestSuite extends TestSuite
suite.addTestSuite(OwnableServiceTest.class);
suite.addTestSuite(ReadPermissionTest.class);
suite.addTest(new JUnit4TestAdapter(HomeFolderProviderSynchronizerTest.class));
return suite;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
@@ -19,17 +19,12 @@
package org.alfresco.repo.security.person;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.util.PropertyCheck;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
@@ -38,6 +33,9 @@ import org.springframework.beans.factory.InitializingBean;
* Common support for creating home folders This is hooked into node creation events from Person type objects via the
* homeFolderManager. Provider must all be wired up to the homeFolderManager.
*
* @deprecated
* Depreciated since 4.0. {@link AbstractHomeFolderProvider2} should now be used.
*
* @author Andy Hind
*/
public abstract class AbstractHomeFolderProvider implements HomeFolderProvider, BeanNameAware, InitializingBean
@@ -58,40 +56,34 @@ public abstract class AbstractHomeFolderProvider implements HomeFolderProvider,
private StoreRef storeRef;
/**
* Service registry to get hold of public services (so taht actions are audited)
* Service registry to get hold of public services (so that actions are audited)
*/
private ServiceRegistry serviceRegistry;
/**
* Tenant service - required for MT-enabled environment, else optional
*/
private TenantService tenantService;
/**
* The path to a folder
*/
private String path;
/**
* Cache the result of the path look up.
*/
private Map<String, NodeRef> pathNodeRefs; // MT-aware
/**
* The owner to set on creation of a home folder (if unset this will be the uid).
*/
private String ownerOnCreate;
/**
* PermissionsManager used on creating the home folder
*/
private PermissionsManager onCreatePermissionsManager;
/**
* PermissionsManager used on referencing the home folder
*/
private PermissionsManager onReferencePermissionsManager;
public AbstractHomeFolderProvider()
{
super();
pathNodeRefs = new ConcurrentHashMap<String, NodeRef>();
}
/**
* Adaptor for this instance to be a HomeFolderProvider2
*/
private V2Adaptor v2Adaptor = new V2Adaptor(this);
/**
* Register with the homeFolderManagewr
@@ -99,13 +91,9 @@ public abstract class AbstractHomeFolderProvider implements HomeFolderProvider,
public void afterPropertiesSet() throws Exception
{
PropertyCheck.mandatory(this, "homeFolderManager", homeFolderManager);
homeFolderManager.addProvider(this);
homeFolderManager.addProvider(v2Adaptor);
}
// === //
// IOC //
// === //
/**
* Get the home folder manager.
*/
@@ -116,7 +104,6 @@ public abstract class AbstractHomeFolderProvider implements HomeFolderProvider,
/**
* Set the home folder manager.
*
* @param homeFolderManager
*/
public void setHomeFolderManager(HomeFolderManager homeFolderManager)
@@ -127,6 +114,7 @@ public abstract class AbstractHomeFolderProvider implements HomeFolderProvider,
/**
* Get the provider name
*/
@Override
public String getName()
{
return name;
@@ -135,6 +123,7 @@ public abstract class AbstractHomeFolderProvider implements HomeFolderProvider,
/**
* The provider name is taken from the bean name
*/
@Override
public void setBeanName(String name)
{
this.name = name;
@@ -153,7 +142,14 @@ public abstract class AbstractHomeFolderProvider implements HomeFolderProvider,
*/
public void setPath(String path)
{
boolean reset = this.path != null;
this.path = path;
// If a reset need to clear caches
if (reset)
{
homeFolderManager.clearCaches(v2Adaptor);
}
}
/**
@@ -201,7 +197,7 @@ public abstract class AbstractHomeFolderProvider implements HomeFolderProvider,
*/
public void setTenantService(TenantService tenantService)
{
this.tenantService = tenantService;
// keep class signature but no longer use value
}
/**
@@ -212,11 +208,27 @@ public abstract class AbstractHomeFolderProvider implements HomeFolderProvider,
this.onCreatePermissionsManager = onCreatePermissionsManager;
}
/**
* Gets the PermissionsManager used on creating the home folder
*/
public PermissionsManager getOnCreatePermissionsManager()
{
return onCreatePermissionsManager;
}
public void setOnReferencePermissionsManager(PermissionsManager onReferencePermissionsManager)
{
this.onReferencePermissionsManager = onReferencePermissionsManager;
}
/**
* Gets the PermissionsManager used on referencing the home folder
*/
public PermissionsManager getOnReferencePermissionsManager()
{
return onReferencePermissionsManager;
}
/**
* Set the authority to use as the owner of all home folder nodes.
*/
@@ -225,34 +237,28 @@ public abstract class AbstractHomeFolderProvider implements HomeFolderProvider,
this.ownerOnCreate = ownerOnCreate;
}
/**
* Get the authority to use as the owner of all home folder nodes.
*/
public String getOwnerOnCreate()
{
return ownerOnCreate;
}
/**
* Cache path to node resolution
*/
protected NodeRef getPathNodeRef()
{
String tenantDomain = (tenantService != null ? tenantService.getCurrentUserDomain() : TenantService.DEFAULT_DOMAIN);
NodeRef pathNodeRef = pathNodeRefs.get(tenantDomain);
if (pathNodeRef == null)
{
pathNodeRef = resolvePath(path);
pathNodeRefs.put(tenantDomain, pathNodeRef);
}
return pathNodeRef;
return homeFolderManager.getRootPathNodeRef(v2Adaptor);
}
/**
* Utility metho to resolve paths to nodes.
* Utility method to resolve paths to nodes.
*/
protected NodeRef resolvePath(String pathToResolve)
{
List<NodeRef> refs = serviceRegistry.getSearchService().selectNodes(serviceRegistry.getNodeService().getRootNode(storeRef), pathToResolve, null,
serviceRegistry.getNamespaceService(), false);
if (refs.size() != 1)
{
throw new IllegalStateException("Non-unique path: found : " + pathToResolve + " " + refs.size());
}
return refs.get(0);
return homeFolderManager.resolvePath(v2Adaptor, pathToResolve);
}
/**
@@ -260,69 +266,98 @@ public abstract class AbstractHomeFolderProvider implements HomeFolderProvider,
*/
public void onCreateNode(ChildAssociationRef childAssocRef)
{
AuthenticationUtil.RunAsWork<NodeRef> action = new OnCreateNode(childAssocRef);
AuthenticationUtil.runAs(action, AuthenticationUtil.getSystemUserName());
homeFolderManager.homeFolderCreateAndSetPermissions(v2Adaptor, childAssocRef.getChildRef());
}
/**
* Abstract implementation to find/create the approriate home space.
* Abstract implementation to find/create the appropriate home space.
*/
protected abstract HomeSpaceNodeRef getHomeFolder(NodeRef person);
/**
* Helper class to encapsulate the createion settinhg permissions etc
*
* @author Andy Hind
* Get adaptor for this instance to be a HomeFolderProvider2
*/
private class OnCreateNode implements AuthenticationUtil.RunAsWork<NodeRef>
protected V2Adaptor getV2Adaptor()
{
ChildAssociationRef childAssocRef;
OnCreateNode(ChildAssociationRef childAssocRef)
{
this.childAssocRef = childAssocRef;
return v2Adaptor;
}
public NodeRef doWork() throws Exception
/**
* Adaptor to the HomeFolderProvider2 interface.
*/
public class V2Adaptor implements HomeFolderProvider2
{
AbstractHomeFolderProvider abstractHomeFolderProvider;
// Find person
NodeRef personNodeRef = childAssocRef.getChildRef();
// Get home folder
HomeSpaceNodeRef homeFolder = getHomeFolder(personNodeRef);
// If it exists
if (homeFolder.getNodeRef() != null)
public V2Adaptor(AbstractHomeFolderProvider abstractHomeFolderProvider)
{
// Get uid and keep
String uid = DefaultTypeConverter.INSTANCE.convert(String.class, serviceRegistry.getNodeService().getProperty(personNodeRef, ContentModel.PROP_USERNAME));
// If created or found then set (other wise it was already set correctly)
if (homeFolder.getStatus() != HomeSpaceNodeRef.Status.VALID)
{
serviceRegistry.getNodeService().setProperty(personNodeRef, ContentModel.PROP_HOMEFOLDER, homeFolder.getNodeRef());
this.abstractHomeFolderProvider = abstractHomeFolderProvider;
abstractHomeFolderProvider.v2Adaptor = this;
}
String ownerToSet = ownerOnCreate == null ? uid : ownerOnCreate;
// If created..
if (homeFolder.getStatus() == HomeSpaceNodeRef.Status.CREATED)
@Override
public String getName()
{
if (onCreatePermissionsManager != null)
{
onCreatePermissionsManager.setPermissions(homeFolder.getNodeRef(), ownerToSet, uid);
}
}
else
{
if (onReferencePermissionsManager != null)
{
onReferencePermissionsManager.setPermissions(homeFolder.getNodeRef(), ownerToSet, uid);
}
return abstractHomeFolderProvider.getName();
}
}
return homeFolder.getNodeRef();
}
@Override
public String getStoreUrl()
{
return abstractHomeFolderProvider.getStoreRef().toString();
}
@Override
public String getRootPath()
{
return abstractHomeFolderProvider.getPath();
}
@Override
public List<String> getHomeFolderPath(NodeRef person)
{
return (abstractHomeFolderProvider instanceof UIDBasedHomeFolderProvider)
? ((UIDBasedHomeFolderProvider)abstractHomeFolderProvider).getHomeFolderPath(person)
: null;
}
@Override
public NodeRef getTemplateNodeRef()
{
return (abstractHomeFolderProvider instanceof UIDBasedHomeFolderProvider)
? ((UIDBasedHomeFolderProvider)abstractHomeFolderProvider).getTemplateNodeRef()
: null;
}
@Override
public String getOwner()
{
return abstractHomeFolderProvider.getOwnerOnCreate();
}
@Override
public PermissionsManager getOnCreatePermissionsManager()
{
return abstractHomeFolderProvider.getOnReferencePermissionsManager();
}
@Override
public PermissionsManager getOnReferencePermissionsManager()
{
return abstractHomeFolderProvider.getOnReferencePermissionsManager();
}
@Override
public HomeSpaceNodeRef getHomeFolder(NodeRef person)
{
return abstractHomeFolderProvider.getHomeFolder(person);
}
// The old way to create the home folder, so must still call it in case
// the method is overridden
public void onCreateNode(ChildAssociationRef childAssocRef)
{
abstractHomeFolderProvider.onCreateNode(childAssocRef);
}
}
}

View File

@@ -0,0 +1,209 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.security.person;
import java.util.List;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.util.PropertyCheck;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
/**
* Abstract class that implements {@link HomeFolderProvider2} which
* works with the {@link HomeFolderManager} (which performs most of
* the work) to create home folders in custom locations.
*
* @author Alan Davis
*/
public abstract class AbstractHomeFolderProvider2 implements
HomeFolderProvider2, BeanNameAware, InitializingBean
{
/**
* The provider name
*/
private String name;
/**
* The home folder manager
*/
private HomeFolderManager homeFolderManager;
/**
* The store URL.
*/
private String storeUrl;
/**
* The path to the root folder
*/
private String rootPath;
/**
* Set the authority to use as the owner of all home folder nodes.
* May be {@code null}.
*/
private String owner;
/**
* PermissionsManager used on creating the home folder
*/
private PermissionsManager onCreatePermissionsManager;
/**
* PermissionsManager used on referencing the home folder
*/
private PermissionsManager onReferencePermissionsManager;
/**
* Register with the homeFolderManagewr
*/
public void afterPropertiesSet() throws Exception
{
PropertyCheck.mandatory(this, "homeFolderManager", homeFolderManager);
homeFolderManager.addProvider(this);
}
/**
* Get the home folder manager.
*/
protected HomeFolderManager getHomeFolderManager()
{
return homeFolderManager;
}
/**
* Set the home folder manager.
* @param homeFolderManager
*/
public void setHomeFolderManager(HomeFolderManager homeFolderManager)
{
this.homeFolderManager = homeFolderManager;
}
/**
* Get the provider name
*/
@Override
public String getName()
{
return name;
}
/**
* The provider name is taken from the bean name
*/
@Override
public void setBeanName(String name)
{
this.name = name;
}
/**
* Get the path of the root folder
*/
@Override
public String getRootPath()
{
return rootPath;
}
/**
* Set the path of the root folder
*/
public void setRootPath(String rootPath)
{
boolean reset = this.rootPath != null;
this.rootPath = rootPath;
// If a reset need to clear caches
if (reset)
{
homeFolderManager.clearCaches(this);
}
}
@Override
public String getStoreUrl()
{
return storeUrl;
}
/**
* Set the store URL.
*/
public void setStoreUrl(String storeUrl)
{
this.storeUrl = storeUrl;
}
/**
* Sets the PermissionsManager used on creating the home folder
*/
public void setOnCreatePermissionsManager(PermissionsManager onCreatePermissionsManager)
{
this.onCreatePermissionsManager = onCreatePermissionsManager;
}
@Override
public PermissionsManager getOnCreatePermissionsManager()
{
return onCreatePermissionsManager;
}
/**
* Sets the PermissionsManager used on referencing the home folder
*/
public void setOnReferencePermissionsManager(PermissionsManager onReferencePermissionsManager)
{
this.onReferencePermissionsManager = onReferencePermissionsManager;
}
@Override
public PermissionsManager getOnReferencePermissionsManager()
{
return onReferencePermissionsManager;
}
/**
* Set the authority to use as the owner of all home folder nodes.
*/
public void setOwner(String owner)
{
this.owner = owner;
}
@Override
public String getOwner()
{
return owner;
}
@Override
public List<String> getHomeFolderPath(NodeRef person)
{
return null;
}
@Override
public NodeRef getTemplateNodeRef()
{
return null;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
@@ -23,17 +23,16 @@ import org.alfresco.service.cmr.repository.NodeRef;
/**
* Provider to use in the boostrap process - does nothing
*
* Probably not required as behaviour/policies are disabled during normal import.
* Thought to be probably not required as behaviour/policies are
* disabled during normal import, but is used for 'admin' and 'guest'
*
* @author Andy Hind
*/
public class BootstrapHomeFolderProvider extends AbstractHomeFolderProvider
public class BootstrapHomeFolderProvider extends AbstractHomeFolderProvider2
{
@Override
protected HomeSpaceNodeRef getHomeFolder(NodeRef person)
public HomeSpaceNodeRef getHomeFolder(NodeRef person)
{
return new HomeSpaceNodeRef(null, HomeSpaceNodeRef.Status.VALID);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
@@ -18,35 +18,20 @@
*/
package org.alfresco.repo.security.person;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
/**
* Set a home space from a simple path.
* HomeFolderProvider that simply uses the root path for the home folder.
*
* @deprecated
* Depreciated since 4.0. {@link ExistingPathBasedHomeFolderProvider2} should now be used.
*
* @author Andy Hind
*/
public class ExistingPathBasedHomeFolderProvider extends AbstractHomeFolderProvider
{
public ExistingPathBasedHomeFolderProvider()
{
super();
}
protected HomeSpaceNodeRef getHomeFolder(NodeRef person)
{
NodeRef existingHomeFolder = DefaultTypeConverter.INSTANCE.convert(NodeRef.class, getServiceRegistry().getNodeService().getProperty(
person, ContentModel.PROP_HOMEFOLDER));
if (existingHomeFolder == null)
{
return new HomeSpaceNodeRef(getPathNodeRef(), HomeSpaceNodeRef.Status.REFERENCED);
return getHomeFolderManager().getHomeFolder(getV2Adaptor(), person, true);
}
else
{
return new HomeSpaceNodeRef(existingHomeFolder, HomeSpaceNodeRef.Status.VALID);
}
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.security.person;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* HomeFolderProvider that simply uses the root path for the home folder.
* Generally it is a better idea to give each user their own home folder.
*
* @author Alan Davis
*/
public class ExistingPathBasedHomeFolderProvider2 extends AbstractHomeFolderProvider2
{
@Override
public HomeSpaceNodeRef getHomeFolder(NodeRef person)
{
return getHomeFolderManager().getHomeFolder(this, person, true);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
@@ -18,15 +18,27 @@
*/
package org.alfresco.repo.security.person;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.node.NodeServicePolicies;
import org.alfresco.repo.policy.JavaBehaviour;
import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileFolderUtil;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.model.FileNotFoundException;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
@@ -34,26 +46,43 @@ import org.alfresco.service.namespace.QName;
/**
* Manage home folder creation by binding to events from the cm:person type.
*
* @author Andy Hind
* @author Andy Hind,
* Alan Davis (support v1 and v2 HomeFolderProviders - code from
* v1 HomeFolderProviders moved into HomeFolderManager).
*/
public class HomeFolderManager implements NodeServicePolicies.OnCreateNodePolicy
{
private PolicyComponent policyComponent;
private NodeService nodeService;
private boolean enableHomeFolderCreationAsPeopleAreCreated = false;
private ServiceRegistry serviceRegistry;
private TenantService tenantService;
/**
* A default provider
*/
private HomeFolderProvider defaultProvider;
private HomeFolderProvider2 defaultProvider;
/**
* Providers that have registered and are looken up by name (== bean name)
* Original Providers (now depreciated) that have registered and are looked up by bean name.
*/
private Map<String, HomeFolderProvider> providers = new HashMap<String, HomeFolderProvider>();
@SuppressWarnings("deprecation")
private Map<String, HomeFolderProvider> v1Providers = new HashMap<String, HomeFolderProvider>();
/**
* Providers that have registered and are looked up by bean name.
*/
private Map<String, HomeFolderProvider2> v2Providers = new HashMap<String, HomeFolderProvider2>();
/**
* Cache the result of the path look up.
*/
private Map<String, Map<String, NodeRef>> rootPathNodeRefMaps =
new ConcurrentHashMap<String, Map<String, NodeRef>>();
/**
* Bind the class behaviour to this implementation
@@ -90,21 +119,65 @@ public class HomeFolderManager implements NodeServicePolicies.OnCreateNodePolicy
this.nodeService = nodeService;
}
/**
* Set the service registry.
*/
public void setServiceRegistry(ServiceRegistry serviceRegistry)
{
this.serviceRegistry = serviceRegistry;
}
/**
* Set the tenant service
*/
public void setTenantService(TenantService tenantService)
{
this.tenantService = tenantService;
}
/**
* Register a home folder provider.
*
* @param provider
*/
@SuppressWarnings("deprecation")
public void addProvider(HomeFolderProvider provider)
{
providers.put(provider.getName(), provider);
v1Providers.put(provider.getName(), provider);
}
/**
* Register a home folder provider.
*
* @param provider
*/
public void addProvider(HomeFolderProvider2 provider)
{
v2Providers.put(provider.getName(), provider);
}
/**
* Returns the version 1 HomeFolderProvider with the given name.
*/
@SuppressWarnings("deprecation")
public HomeFolderProvider getHomeFolderProvider1(String providerName)
{
return v1Providers.get(providerName);
}
/**
* Returns the version 2 HomeFolderProvider2 with the given name.
*/
public HomeFolderProvider2 getHomeFolderProvider2(String providerName)
{
return v2Providers.get(providerName);
}
/**
* Set the default home folder provider (user which none is specified or when one is not found)
* @param defaultProvider
*/
public void setDefaultProvider(HomeFolderProvider defaultProvider)
public void setDefaultProvider(HomeFolderProvider2 defaultProvider)
{
this.defaultProvider = defaultProvider;
}
@@ -123,22 +196,302 @@ public class HomeFolderManager implements NodeServicePolicies.OnCreateNodePolicy
/**
* Find the provider and call.
*/
@SuppressWarnings("deprecation")
public void makeHomeFolder(ChildAssociationRef childAssocRef)
{
HomeFolderProvider provider = defaultProvider;
String providerName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(childAssocRef
HomeFolderProvider2 v2Provider = defaultProvider;
HomeFolderProvider v1Provider = null;
String providerName = DefaultTypeConverter.INSTANCE.convert(
String.class, nodeService.getProperty(childAssocRef
.getChildRef(), ContentModel.PROP_HOME_FOLDER_PROVIDER));
if (providerName != null)
{
provider = providers.get(providerName);
if (provider == null)
v2Provider = getHomeFolderProvider2(providerName);
if (v2Provider == null)
{
provider = defaultProvider;
v1Provider = getHomeFolderProvider1(providerName);
if (v1Provider == null)
{
v2Provider = defaultProvider;
}
}
if (provider != null)
}
else
{
provider.onCreateNode(childAssocRef);
providerName = defaultProvider.getName();
nodeService.setProperty(childAssocRef.getChildRef(),
ContentModel.PROP_HOME_FOLDER_PROVIDER, providerName);
}
if (v2Provider != null)
{
// If a V2Adaptor we still must call onCreateNode just like a
// v1 HomeFolderProvider in case it has been overridden
if (v2Provider instanceof AbstractHomeFolderProvider.V2Adaptor)
{
((AbstractHomeFolderProvider.V2Adaptor)v2Provider).onCreateNode(childAssocRef);
}
else
{
homeFolderCreateAndSetPermissions(v2Provider, childAssocRef.getChildRef());
}
}
else if (v1Provider != null)
{
v1Provider.onCreateNode(childAssocRef);
}
}
void homeFolderCreateAndSetPermissions(HomeFolderProvider2 provider, NodeRef personNodeRef)
{
AuthenticationUtil.RunAsWork<NodeRef> action =
new RunAsCreateAndSetPermissions(provider, personNodeRef);
AuthenticationUtil.runAs(action, AuthenticationUtil.getSystemUserName());
}
/**
* Helper class to encapsulate the creation and setting permissions etc
*/
private class RunAsCreateAndSetPermissions implements AuthenticationUtil.RunAsWork<NodeRef>
{
NodeRef personNodeRef;
HomeFolderProvider2 provider;
RunAsCreateAndSetPermissions(HomeFolderProvider2 provider, NodeRef personNodeRef)
{
this.personNodeRef = personNodeRef;
this.provider = provider;
}
public NodeRef doWork() throws Exception
{
// Get home folder
HomeSpaceNodeRef homeFolder = provider.getHomeFolder(personNodeRef);
// If it exists
if (homeFolder.getNodeRef() != null)
{
// Get uid and keep
String uid = DefaultTypeConverter.INSTANCE.convert(String.class,
serviceRegistry.getNodeService().getProperty(
personNodeRef, ContentModel.PROP_USERNAME));
// If created or found then set (other wise it was already set correctly)
if (homeFolder.getStatus() != HomeSpaceNodeRef.Status.VALID)
{
serviceRegistry.getNodeService().setProperty(
personNodeRef, ContentModel.PROP_HOMEFOLDER, homeFolder.getNodeRef());
}
final String providerSuppliedOwner = provider.getOwner();
String owner = (providerSuppliedOwner == null) ? uid : providerSuppliedOwner;
// If created..
if (homeFolder.getStatus() == HomeSpaceNodeRef.Status.CREATED)
{
PermissionsManager onCreatePermissionsManager =
provider.getOnCreatePermissionsManager();
if (onCreatePermissionsManager != null)
{
onCreatePermissionsManager.setPermissions(
homeFolder.getNodeRef(), owner, uid);
}
}
else
{
PermissionsManager onReferencePermissionsManager =
provider.getOnReferencePermissionsManager();
if (onReferencePermissionsManager != null)
{
onReferencePermissionsManager.setPermissions(
homeFolder.getNodeRef(), owner, uid);
}
}
}
return homeFolder.getNodeRef();
}
}
private StoreRef getStoreRef(HomeFolderProvider2 provider)
{
// Could check to see if provider is a V2Adaptor to avoid
// object creation, but there is little point.
return new StoreRef(provider.getStoreUrl());
}
/**
* Helper method for {@link HomeFolderProvider2.getHomeFolder} (so that it
* does not need its own NodeService) that returns a person property value.
*/
public String getPersonProperty(NodeRef person, QName name)
{
String value = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(person, name));
if(value == null || value.length() == 0)
{
throw new PersonException("Can not create a home folder when the "+name+" property is null or empty");
}
return value;
}
void clearCaches(HomeFolderProvider2 provider)
{
getRootPathNodeRefMap(provider).clear();
}
NodeRef getRootPathNodeRef(HomeFolderProvider2 provider)
{
String rootPath = provider.getRootPath();
String tenantDomain = (tenantService != null ? tenantService.getCurrentUserDomain() : TenantService.DEFAULT_DOMAIN);
Map<String, NodeRef> rootPathNodeRefMap = getRootPathNodeRefMap(provider);
NodeRef rootPathNodeRef = rootPathNodeRefMap.get(tenantDomain);
if (rootPathNodeRef == null)
{
// ok with race condition for initial construction
rootPathNodeRef = resolvePath(provider, rootPath);
rootPathNodeRefMap.put(tenantDomain, rootPathNodeRef);
}
return rootPathNodeRef;
}
private Map<String, NodeRef> getRootPathNodeRefMap(HomeFolderProvider2 provider)
{
String name = provider.getName();
Map<String, NodeRef> rootPathNodeRefMap = rootPathNodeRefMaps.get(name);
if (rootPathNodeRefMap == null)
{
// ok with race condition for initial construction
rootPathNodeRefMap = new ConcurrentHashMap<String, NodeRef>();
rootPathNodeRefMaps.put(name, rootPathNodeRefMap);
}
return rootPathNodeRefMap;
}
/**
* Utility method to resolve paths to nodes.
*/
NodeRef resolvePath(HomeFolderProvider2 provider, String pathToResolve)
{
List<NodeRef> refs = serviceRegistry.getSearchService().selectNodes(
serviceRegistry.getNodeService().getRootNode(getStoreRef(provider)),
pathToResolve, null,
serviceRegistry.getNamespaceService(), false);
if (refs.size() != 1)
{
throw new IllegalStateException("Non-unique path: found : " +
pathToResolve + " " + refs.size());
}
return refs.get(0);
}
/**
* Helper method for {@link HomeFolderProvider2.getHomeFolder(NodeRef)}
* implementations to return a {@link HomeSpaceNodeRef}
* @param referenceRootNode indicates that a reference to the root node
* should be returned if the home folder property on the person
* has not yet been set.
*/
public HomeSpaceNodeRef getHomeFolder(HomeFolderProvider2 provider, NodeRef person, boolean referenceRootNode)
{
HomeSpaceNodeRef homeSpaceNodeRef = null;
NodeRef existingHomeFolder = DefaultTypeConverter.INSTANCE.convert(
NodeRef.class, serviceRegistry.getNodeService().getProperty(
person, ContentModel.PROP_HOMEFOLDER));
if (existingHomeFolder != null)
{
homeSpaceNodeRef = new HomeSpaceNodeRef(existingHomeFolder,
HomeSpaceNodeRef.Status.VALID);
}
else if (referenceRootNode)
{
homeSpaceNodeRef = new HomeSpaceNodeRef(getRootPathNodeRef(provider),
HomeSpaceNodeRef.Status.REFERENCED);
}
else
{
FileFolderService fileFolderService = serviceRegistry.getFileFolderService();
List<String> homeFolderPath = provider.getHomeFolderPath(person);
FileInfo fileInfo;
// Test if it already exists
NodeRef existing = getExisting(provider, fileFolderService, homeFolderPath);
if (existing != null)
{
fileInfo = fileFolderService.getFileInfo(existing);
}
else
{
fileInfo = createTree(provider, getRootPathNodeRef(provider), homeFolderPath,
provider.getTemplateNodeRef(), fileFolderService);
}
NodeRef homeFolderNodeRef = fileInfo.getNodeRef();
return new HomeSpaceNodeRef(homeFolderNodeRef, HomeSpaceNodeRef.Status.CREATED);
}
return homeSpaceNodeRef;
}
private NodeRef getExisting(HomeFolderProvider2 provider, FileFolderService fileFolderService,
List<String> homeFolderPath)
{
NodeRef existing;
try
{
FileInfo existingFileInfo = fileFolderService.resolveNamePath(getRootPathNodeRef(provider), homeFolderPath);
existing = existingFileInfo.getNodeRef();
}
catch (FileNotFoundException fnfe)
{
existing = null;// home folder noderef doesn't exist yet
}
return existing;
}
/**
* creates a tree of folder nodes based on the path elements provided.
*/
private FileInfo createTree(HomeFolderProvider2 provider, NodeRef root,
List<String> homeFolderPath, NodeRef templateNodeRef,
FileFolderService fileFolderService)
{
NodeRef newParent = createNewParentIfRequired(root, homeFolderPath, fileFolderService);
String homeFolderName = homeFolderPath.get(homeFolderPath.size()-1);
FileInfo fileInfo;
if (templateNodeRef == null)
{
fileInfo = fileFolderService.create(
newParent,
homeFolderName,
ContentModel.TYPE_FOLDER);
}
else
{
try
{
fileInfo = fileFolderService.copy(
templateNodeRef,
newParent,
homeFolderName);
}
catch (FileNotFoundException e)
{
throw new PersonException("Invalid template to create home space");
}
}
return fileInfo;
}
private NodeRef createNewParentIfRequired(NodeRef root,
List<String> homeFolderPath, FileFolderService fileFolderService)
{
if (homeFolderPath.size() > 1)
{
List<String> parentPath = new ArrayList<String>(homeFolderPath);
parentPath.remove(parentPath.size()-1);
return FileFolderUtil.makeFolders(fileFolderService, root,
parentPath, ContentModel.TYPE_FOLDER).getNodeRef();
}
else
{
return root;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
@@ -23,6 +23,9 @@ import org.alfresco.repo.node.NodeServicePolicies;
/**
* Interface for home folder providers.
*
* @deprecated
* Depreciated since 4.0. {@link HomeFolderProvider2} should now be used.
*
* @author Andy Hind
*/
public interface HomeFolderProvider extends NodeServicePolicies.OnCreateNodePolicy

View File

@@ -0,0 +1,101 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.security.person;
import java.util.List;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* Interface for home folder providers. Instances work with the
* {@link HomeFolderManager} (which performs most of the work)
* to allow it to create home folders in custom locations.
*
* The home folder may be a simple structure where all users share a root folder (See
* {@link ExistingPathBasedHomeFolderProvider2}), or all home folders are in the same root
* folder (See {@link UsernameHomeFolderProvider}) or in a tree of sub folders to
* avoids any single directory containing too many home directories which might cause
* performance issues (See {@link RegexHomeFolderProvider}).<p>
*
* If the HomeFolderProvider is changed, home folders may be
* moved by using the {@link HomeFolderProviderSynchronizer} which optionally runs on
* restart.
*
* @author Andy Hind, Alan Davis (support v1 and v2 HomeFolderProviders)
*/
public interface HomeFolderProvider2
{
/**
* Get the name of the provider (the bean name).
*/
String getName();
/**
* Get the URL String of the node store that will be used.
*/
String getStoreUrl();
/**
* Get the root path in the store under which all home folders will be located.
*/
String getRootPath();
/**
* Returns a preferred path (a list of folder names) for the home folder relative to
* the root path. If all users share the root, the returned value should be an empty
* List or {@code null}. When all users have their own folder under the root
* there should be just one element in the List. Multiple elements should be returned
* when a nested folder structure is preferred.
* @param person NodeRef from which a property (normally the userName) is used as a
* hash key to create a nested directory structure.
* @return the path to be used.
*/
List<String> getHomeFolderPath(NodeRef person);
/**
* Returns a node to copy (a template) for the home folder.
* Only used by HomeFolderProviders that create home folders rather
* than just reference existing folders.
* @return the node to copy or {@code null} if not required.
*/
NodeRef getTemplateNodeRef();
/**
* Set the authority to use as the owner of all home folder nodes.
* If {@code null} the {@link ContentModel.PROP_USERNAME} value of
* the person is used.
*/
String getOwner();
/**
* Gets the PermissionsManager used on creating the home folder
*/
PermissionsManager getOnCreatePermissionsManager();
/**
* Gets the PermissionsManager used on referencing the home folder
*/
PermissionsManager getOnReferencePermissionsManager();
/**
* Callback from {@link HomeFolderManager} to locate or create a home folder.
* Implementations normally call {@link HomeFolderManager.getHomeFolder}.
*/
HomeSpaceNodeRef getHomeFolder(NodeRef person);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,944 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.security.person;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.Set;
import javax.transaction.Status;
import javax.transaction.UserTransaction;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.tenant.Tenant;
import org.alfresco.repo.tenant.TenantAdminService;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.Path;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.PropertyMap;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ComparisonFailure;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
/**
* Integration test for HomeFolderProviderSynchronizer.
*
* @author Alan Davis
*/
public class HomeFolderProviderSynchronizerTest
{
private static final QName PROP_PARENT_PATH = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "parentPath");
private static ApplicationContext applicationContext;
private static ServiceRegistry serviceRegistry;
private static TransactionService transactionService;
private static FileFolderService fileFolderService;
private static PersonService personService;
private static NodeService nodeService;
private static ContentService contentService;
private static AuthorityService authorityService;
private static TenantAdminService tenantAdminService;
private static TenantService tenantService;
private static HomeFolderManager homeFolderManager;
private static Properties properties;
private static RegexHomeFolderProvider largeHomeFolderProvider;
private static String largeHomeFolderProviderName;
private static RegexHomeFolderProvider testHomeFolderProvider;
private static String testHomeFolderProviderName;
private static String storeUrl;
private static String origRootPath;
private static NodeRef rootNodeRef;
private static HomeFolderProviderSynchronizer homeFolderProviderSynchronizer;
private static boolean firstTest = true;
private UserTransaction trans;
@BeforeClass
public static void classSetup() throws Exception
{
applicationContext = ApplicationContextHelper.getApplicationContext();
serviceRegistry = (ServiceRegistry) applicationContext.getBean("ServiceRegistry");
transactionService = (TransactionService) applicationContext.getBean("transactionService");
fileFolderService = (FileFolderService) applicationContext.getBean("fileFolderService");
personService = (PersonService) applicationContext.getBean("personService");
nodeService = (NodeService) applicationContext.getBean("nodeService");
contentService = (ContentService) applicationContext.getBean("contentService");
authorityService = (AuthorityService) applicationContext.getBean("authorityService");
tenantAdminService = (TenantAdminService) applicationContext.getBean("tenantAdminService");
tenantService = (TenantService) applicationContext.getBean("tenantService");
homeFolderManager = (HomeFolderManager) applicationContext.getBean("homeFolderManager");
largeHomeFolderProvider = (RegexHomeFolderProvider) applicationContext.getBean("largeHomeFolderProvider");
largeHomeFolderProviderName = largeHomeFolderProvider.getName();
storeUrl = largeHomeFolderProvider.getStoreUrl();
origRootPath = largeHomeFolderProvider.getRootPath();
properties = (Properties) applicationContext.getBean("global-properties");
personService.setCreateMissingPeople(true);
// Create test home folder provider that gets its path from a property and the username
testHomeFolderProvider = new RegexHomeFolderProvider()
{
@Override
public List<String> getHomeFolderPath(NodeRef person)
{
String parentPath = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(person, PROP_PARENT_PATH));
String propPath = ((parentPath == null || parentPath.length() == 0) ? "" : parentPath+'/')+
homeFolderManager.getPersonProperty(person, ContentModel.PROP_USERNAME);
return Arrays.asList(propPath.split("/"));
}
};
testHomeFolderProvider.setPropertyName(ContentModel.PROP_USERNAME.getLocalName());
testHomeFolderProvider.setPattern("(..)");
testHomeFolderProvider.setBeanName("testHomeFolderProvider");
testHomeFolderProvider.setHomeFolderManager(homeFolderManager);
testHomeFolderProvider.setRootPath(origRootPath);
testHomeFolderProvider.setStoreUrl(storeUrl);
testHomeFolderProvider.setOnCreatePermissionsManager((PermissionsManager)applicationContext.getBean("defaultOnCreatePermissionsManager"));
testHomeFolderProvider.setOnCreatePermissionsManager((PermissionsManager)applicationContext.getBean("defaultOnCreatePermissionsManager"));
testHomeFolderProvider.setOnReferencePermissionsManager((PermissionsManager)applicationContext.getBean("defaultOnReferencePermissionsManager"));
testHomeFolderProviderName = testHomeFolderProvider.getName();
homeFolderManager.addProvider(testHomeFolderProvider);
homeFolderProviderSynchronizer = new HomeFolderProviderSynchronizer(
properties, transactionService, authorityService,
personService, fileFolderService, nodeService,
homeFolderManager, tenantAdminService);
}
@Before
public void setUp() throws Exception
{
properties.setProperty("home_folder_provider_synchronizer.enabled", "true");
properties.remove("home_folder_provider_synchronizer.override_provider");
properties.remove("home_folder_provider_synchronizer.keep_empty_parents");
largeHomeFolderProvider.setPattern("^(..)");
testHomeFolderProvider.setRootPath(origRootPath);
largeHomeFolderProvider.setRootPath(origRootPath);
// Just in case we killed a test last time - tidy up
if (firstTest)
{
firstTest = false;
AuthenticationUtil.setRunAsUserSystem();
trans = transactionService.getUserTransaction();
trans.begin();
rootNodeRef = homeFolderManager.getRootPathNodeRef(largeHomeFolderProvider);
trans.commit();
trans = null;
tearDown();
}
AuthenticationUtil.setRunAsUserSystem();
trans = transactionService.getUserTransaction();
trans.begin();
// System.out.println(NodeStoreInspector.dumpNode(nodeService, rootNodeRef));
}
@After
public void tearDown() throws Exception
{
if (trans != null)
{
try
{
trans.commit();
}
catch (Exception e)
{
if ((trans.getStatus() == Status.STATUS_ACTIVE) ||
(trans.getStatus() == Status.STATUS_MARKED_ROLLBACK))
{
trans.rollback();
trans = null;
}
}
}
trans = transactionService.getUserTransaction();
trans.begin();
Set<NodeRef> adminGuestUserHomeFolders = deleteNonAdminGuestUsers();
deleteNonAdminGuestFolders(adminGuestUserHomeFolders);
deleteAllTenants();
trans.commit();
trans = null;
AuthenticationUtil.clearCurrentSecurityContext();
}
private Set<NodeRef> deleteNonAdminGuestUsers()
{
final Set<NodeRef> adminGuestUserHomeFolders = new HashSet<NodeRef>();
for (final NodeRef nodeRef : personService.getAllPeople())
{
final String username = DefaultTypeConverter.INSTANCE.convert(String.class,
nodeService.getProperty(nodeRef, ContentModel.PROP_USERNAME));
final String domainUsername = tenantService.getBaseNameUser(username);
String tenantDomain = tenantService.getUserDomain(username);
String systemUser = tenantAdminService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain);
boolean disabled = !TenantService.DEFAULT_DOMAIN.equals(tenantDomain) &&
!tenantAdminService.isEnabledTenant(tenantDomain);
try
{
if (disabled)
{
tenantAdminService.enableTenant(tenantDomain);
}
AuthenticationUtil.runAs(new RunAsWork<Object>()
{
public Object doWork() throws Exception
{
deleteUser(adminGuestUserHomeFolders, nodeRef, username, domainUsername);
return null;
}
}, systemUser);
}
finally
{
if (disabled)
{
tenantAdminService.disableTenant(tenantDomain);
}
}
}
return adminGuestUserHomeFolders;
}
// Delete users other than admin and guest. The home folders of
// admin and guest are added to internalUserHomeFolders.
private void deleteUser(final Set<NodeRef> adminGuestUserHomeFolders,
NodeRef person, String username, String domainUsername)
{
if (!domainUsername.equals("admin") && !domainUsername.equals("guest"))
{
personService.deletePerson(person);
System.out.println("deleted user "+username);
}
else
{
NodeRef homeFolder = DefaultTypeConverter.INSTANCE.convert(
NodeRef.class, nodeService.getProperty(person,
ContentModel.PROP_HOMEFOLDER));
adminGuestUserHomeFolders.add(homeFolder);
}
}
private void deleteNonAdminGuestFolders(final Set<NodeRef> adminGuestUserHomeFolders)
{
// Delete folders from under the home folder root path in case they have been left over
// from another test. Admin and Guest home folder should not be under here, but lets
// double check.
for (ChildAssociationRef childAssocs: nodeService.getChildAssocs(rootNodeRef))
{
NodeRef nodeRef = childAssocs.getChildRef();
if (!adminGuestUserHomeFolders.contains(nodeRef))
{
System.out.println("TearDown remove '"+childAssocs.getQName().getLocalName()+
"' from under the home folder root.");
nodeService.deleteNode(nodeRef);
}
}
}
private NodeRef createUser(String parentPath, String username) throws Exception
{
return createUser(TenantService.DEFAULT_DOMAIN, parentPath, username);
}
private NodeRef createUser(String parentPath,
String username, String homeFolderProviderName, boolean createHomeDirectory) throws Exception
{
return createUser(TenantService.DEFAULT_DOMAIN, parentPath, username,
homeFolderProviderName, createHomeDirectory);
}
private NodeRef createUser(String tenantDomain, String parentPath, String username) throws Exception
{
return createUser(tenantDomain, parentPath, username, largeHomeFolderProviderName, true);
}
private NodeRef createUser(String tenantDomain, final String parentPath,
final String username, final String homeFolderProviderName,
final boolean createHomeDirectory) throws Exception
{
final String domainUsername = tenantService.getDomainUser(username, tenantDomain);
String systemUser = tenantAdminService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain);
return AuthenticationUtil.runAs(new RunAsWork<NodeRef>()
{
public NodeRef doWork() throws Exception
{
String firstName = username;
String lastName = "Smith";
String emailAddress = String.format("%s.%s@xyz.com", firstName,
lastName);
PropertyMap properties = new PropertyMap();
properties.put(ContentModel.PROP_USERNAME, domainUsername);
properties.put(ContentModel.PROP_FIRSTNAME, firstName);
properties.put(ContentModel.PROP_LASTNAME, lastName);
properties.put(ContentModel.PROP_EMAIL, emailAddress);
properties.put(ContentModel.PROP_HOME_FOLDER_PROVIDER, testHomeFolderProviderName);
properties.put(PROP_PARENT_PATH, parentPath);
homeFolderManager.setEnableHomeFolderCreationAsPeopleAreCreated(createHomeDirectory);
NodeRef person = personService.createPerson(properties);
assertNotNull("The person nodeRef for "+domainUsername+" should have been created", person);
NodeRef homeFolder = DefaultTypeConverter.INSTANCE.convert(
NodeRef.class, nodeService.getProperty(person,
ContentModel.PROP_HOMEFOLDER));
if (createHomeDirectory)
{
assertNotNull("The homeFolder for "+domainUsername+" should have been created", homeFolder);
}
else
{
assertNull("The homeFolder for "+domainUsername+" should NOT have been created", homeFolder);
}
if (!testHomeFolderProviderName.equals(homeFolderProviderName))
{
if (homeFolderProviderName == null)
{
nodeService.removeProperty(person, ContentModel.PROP_HOME_FOLDER_PROVIDER);
}
else
{
nodeService.setProperty(person, ContentModel.PROP_HOME_FOLDER_PROVIDER,
homeFolderProviderName);
}
}
return person;
}
}, systemUser);
}
private NodeRef createFolder(String path) throws Exception
{
NodeRef parent = rootNodeRef;
if (path.length() > 0)
{
StringBuilder currentPath = new StringBuilder();
for (String pathElement: path.split("/"))
{
if (currentPath.length() > 0)
{
currentPath.append("/");
}
currentPath.append(pathElement);
NodeRef nodeRef = nodeService.getChildByName(parent,
ContentModel.ASSOC_CONTAINS, pathElement);
if (nodeRef == null)
{
parent = fileFolderService.create(parent, pathElement,
ContentModel.TYPE_FOLDER).getNodeRef();
}
else
{
assertTrue("Expected "+currentPath+" to be a folder",
fileFolderService.getFileInfo(nodeRef).isFolder());
parent = nodeRef;
}
}
}
return parent;
}
private NodeRef createContent(String parentPath, String name) throws Exception
{
NodeRef parent = createFolder(parentPath);
PropertyMap propertyMap = new PropertyMap();
propertyMap.put(ContentModel.PROP_CONTENT, new ContentData(null, "text/plain",
0L, "UTF-16", Locale.ENGLISH));
propertyMap.put(ContentModel.PROP_NAME, name);
NodeRef content = nodeService.createNode(
parent,
ContentModel.ASSOC_CONTAINS,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name),
ContentModel.TYPE_CONTENT,
propertyMap).getChildRef();
ContentWriter writer = contentService.getWriter(content, ContentModel.TYPE_CONTENT, true);
writer.putContent("The cat sat on the mat.");
// System.out.println(NodeStoreInspector.dumpNode(nodeService, rootNodeRef));
return content;
}
private String toPath(NodeRef root, NodeRef homeFolder)
{
if (root == null || homeFolder == null)
{
return null;
}
Path rootPath = nodeService.getPath(root);
Path homeFolderPath = nodeService.getPath(homeFolder);
int rootSize = rootPath.size();
int homeFolderSize = homeFolderPath.size();
if (rootSize >= homeFolderSize)
{
return null;
}
StringBuilder sb = new StringBuilder("");
// Check homeFolder is under root
for (int i=0; i < rootSize; i++)
{
if (!rootPath.get(i).equals(homeFolderPath.get(i)))
{
return null;
}
}
// Build up path of sub folders
for (int i = rootSize; i < homeFolderSize; i++)
{
Path.Element element = homeFolderPath.get(i);
if (!(element instanceof Path.ChildAssocElement))
{
return null;
}
QName folderQName = ((Path.ChildAssocElement) element).getRef().getQName();
if (sb.length() > 0)
{
sb.append('/');
}
sb.append(folderQName.getLocalName());
}
return sb.toString();
}
private void createTenant(final String tenantDomain)
{
AuthenticationUtil.runAs(new RunAsWork<Object>()
{
public Object doWork() throws Exception
{
if (!tenantAdminService.existsTenant(tenantDomain))
{
tenantAdminService.createTenant(tenantDomain,
("admin "+tenantDomain).toCharArray(), null);
}
return null;
}
}, AuthenticationUtil.getSystemUserName());
}
private void deleteAllTenants() throws Exception
{
List<Tenant> tenants = tenantAdminService.getAllTenants();
for (Tenant tenant : tenants)
{
deleteTenant(tenant.getTenantDomain());
}
}
// DbNodeServiceImpl does not support deleteStore() at the moment,
// even though it supports createStore(), so just disable them for now.
private void deleteTenant(final String tenantDomain) throws Exception
{
AuthenticationUtil.runAs(new RunAsWork<Object>()
{
public Object doWork() throws Exception
{
if (tenantAdminService.existsTenant(tenantDomain))
{
// Can't delete so disable
// tenantAdminService.deleteTenant(tenantDomain);
if (tenantAdminService.isEnabledTenant(tenantDomain))
{
tenantAdminService.disableTenant(tenantDomain);
}
}
return null;
}
}, AuthenticationUtil.getSystemUserName());
}
private void assertHomeFolderLocation(String username, String expectedPath) throws Exception
{
assertHomeFolderLocation(TenantService.DEFAULT_DOMAIN, username, expectedPath);
}
private void assertHomeFolderLocation(String tenantDomain, final String username,
final String expectedPath) throws Exception
{
try
{
final String domainUsername = tenantService.getDomainUser(username, tenantDomain);
String systemUser = tenantAdminService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain);
AuthenticationUtil.runAs(new RunAsWork<Object>()
{
public NodeRef doWork() throws Exception
{
NodeRef person = personService.getPerson(domainUsername, false);
NodeRef homeFolder = DefaultTypeConverter.INSTANCE.convert(NodeRef.class,
nodeService.getProperty(person, ContentModel.PROP_HOMEFOLDER));
assertNotNull("User: "+domainUsername+" home folder should exist", homeFolder);
NodeRef rootPath = homeFolderManager.getRootPathNodeRef(largeHomeFolderProvider);
String actualPath = toPath(rootPath, homeFolder);
assertEquals("User: "+domainUsername+" home folder location", expectedPath, actualPath);
return null;
}
}, systemUser);
}
catch (RuntimeException e)
{
final Throwable cause = e.getCause();
if (cause instanceof ComparisonFailure || cause instanceof AssertionError)
{
throw (ComparisonFailure)cause;
}
else
{
throw e;
}
}
}
private boolean exists(String path) throws Exception
{
NodeRef parent = rootNodeRef;
boolean exists = true;
for (String pathElement: path.split("/"))
{
NodeRef nodeRef = nodeService.getChildByName(parent,
ContentModel.ASSOC_CONTAINS, pathElement);
if (nodeRef == null)
{
exists = false;
break;
}
else
{
parent = nodeRef;
}
}
return exists;
}
private void moveUserHomeFolders() throws Exception
{
trans.commit();
trans = null;
homeFolderProviderSynchronizer.onBootstrap(null);
trans = transactionService.getUserTransaction();
trans.begin();
}
@Test
public void testCorrectLocation() throws Exception
{
createUser("te", "tess");
moveUserHomeFolders();
assertHomeFolderLocation("tess", "te/tess");
}
@Test
public void testCreateParentFolder() throws Exception
{
createUser("", "fred");
moveUserHomeFolders();
assertHomeFolderLocation("fred", "fr/fred");
}
@Test
public void testNotEnabled() throws Exception
{
createUser("", "fred");
properties.remove("home_folder_provider_synchronizer.enabled");
moveUserHomeFolders();
// If performed, the home folder will have been moved to fr/fred
// We must force the creation of the home folder as it will not
// have been done
personService.getPerson("fred");
assertHomeFolderLocation("fred", "fred");
}
@Test
public void testHomeFolderNotYetCreated() throws Exception
{
NodeRef person = createUser("", "fred", largeHomeFolderProviderName, false);
moveUserHomeFolders();
NodeRef homeFolder = DefaultTypeConverter.INSTANCE.convert(NodeRef.class,
nodeService.getProperty(person, ContentModel.PROP_HOMEFOLDER));
assertNull("The homeFolder should NOT have been created", homeFolder);
person = personService.getPerson("fred");
homeFolder = DefaultTypeConverter.INSTANCE.convert(NodeRef.class,
nodeService.getProperty(person, ContentModel.PROP_HOMEFOLDER));
assertNotNull("The homeFolder should have been created", homeFolder);
}
@Test
public void testCreateMultipleParentFolders() throws Exception
{
largeHomeFolderProvider.setPattern("^(.?)(.?)(.?)(.?)(.?)");
createUser("", "fred");
createUser("", "peter");
createUser("", "tess");
moveUserHomeFolders();
assertHomeFolderLocation("fred", "f/r/e/d/fred");
assertHomeFolderLocation("peter", "p/e/t/e/r/peter");
assertHomeFolderLocation("tess", "t/e/s/s/tess");
}
@Test
public void testMoveToRoot() throws Exception
{
// i.e. there are no parent folders after the sync
largeHomeFolderProvider.setPattern("");
createUser("fr", "fred");
moveUserHomeFolders();
assertHomeFolderLocation("fred", "fred");
}
@Test
public void testRemoveEmptyParents() throws Exception
{
createUser("a/bb/ccc", "peter");
moveUserHomeFolders();
assertHomeFolderLocation("peter", "pe/peter");
assertFalse("Expected the empty parent 'a' to have been removed.", exists("a"));
}
@Test
public void testKeepEmptyParents() throws Exception
{
createUser("a/bb/ccc", "peter");
properties.put("home_folder_provider_synchronizer.keep_empty_parents", "true");
moveUserHomeFolders();
assertHomeFolderLocation("peter", "pe/peter");
assertTrue("Expected the empty parent 'a/bb/ccc' to still exist as global " +
"property was set.", exists("a/bb/ccc"));
}
@Test
public void testKeepNonEmptyParents() throws Exception
{
createUser("a/bb/ccc", "peter");
createFolder("a/bb/ddd");
moveUserHomeFolders();
assertHomeFolderLocation("peter", "pe/peter");
assertFalse("Expected the empty parent 'a/bb/ccc' to have been removed.", exists("a/bb/ccc"));
assertTrue("Expected the non empty parent 'a/bb' to have been kept.", exists("a/bb/ddd"));
}
@Test
public void testPathAlreadyInUseByFolder() throws Exception
{
createUser("", "fred");
createFolder("fr");
moveUserHomeFolders();
assertHomeFolderLocation("fred", "fr/fred");
}
@Test
public void testPathAlreadyInUseByContent() throws Exception
{
createUser("", "fred");
createContent("", "fr");
moveUserHomeFolders();
assertHomeFolderLocation("fred", "fred"); // unchanged
assertFalse("Did not expect there to be a folder in the prefered location.", exists("fr/fred"));
assertTrue("Expected the content to still exist.", exists("fr"));
}
@Test
public void testPathInUseByUser() throws Exception
{
// i.e. test clash between home folder names and parent folders
// which requires a temporary folder to be created
createUser("", "fr");
createUser("", "fred");
createUser("", "peter");
createUser("", "pe");
moveUserHomeFolders();
assertHomeFolderLocation("fr", "fr/fr");
assertHomeFolderLocation("fred", "fr/fred");
assertHomeFolderLocation("peter", "pe/peter");
assertHomeFolderLocation("pe", "pe/pe");
assertFalse("The Temporary1 folder should have been removed", exists("Temporary1"));
}
@Test
public void testUseFirstAvailableTemporaryFolder() throws Exception
{
createUser("", "fr");
createUser("", "fred");
createFolder("Temporary1");
createFolder("Temporary2");
createFolder("Temporary3");
// Don't delete the temporary folder
properties.put("home_folder_provider_synchronizer.keep_empty_parents", "true");
moveUserHomeFolders();
assertTrue("The existing Temporary1 folder should still exist", exists("Temporary1"));
assertTrue("The existing Temporary2 folder should still exist", exists("Temporary2"));
assertTrue("The existing Temporary3 folder should still exist", exists("Temporary3"));
assertTrue("The existing Temporary4 folder should still exist", exists("Temporary4"));
}
@Test
public void testException() throws Exception
{
// Force the need for a temporary folder
createUser("", "fr");
createUser("", "fred");
// Use up all possible temporary folder names
for (int i=1; i<=100; i++)
{
createFolder("Temporary"+i);
}
moveUserHomeFolders();
// normally would have changed to fr/fred if there had not been an exception
assertHomeFolderLocation("fred", "fred");
}
@Test
public void testMultipleRoots() throws Exception
{
createFolder("root");
String rootPath = origRootPath + "/cm:root";
testHomeFolderProvider.setRootPath(rootPath);
createUser("a/b/c", "tess", testHomeFolderProviderName, true);
createUser("a/b/c", "fred", largeHomeFolderProviderName, true);
moveUserHomeFolders();
assertHomeFolderLocation("fred", "fr/fred");
assertHomeFolderLocation("tess", "root/a/b/c/tess");
}
@Test
public void testPathNotUnderRoot() throws Exception
{
createUser("a/b/c", "fred");
createFolder("root");
String rootPath = origRootPath + "/cm:root";
largeHomeFolderProvider.setRootPath(rootPath);
assertHomeFolderLocation("fred", null);
moveUserHomeFolders();
assertHomeFolderLocation("fred", "fr/fred");
}
@Test
public void testMultipleUsers() throws Exception
{
// Tried 2000 users and the HomeFolderProviderSynchronizer.onBootstrap(null)
// took 33 seconds. The setup and tear down takes a while too.
// Use a value larger than the batch size of 20 and log every 100.
int userCount = 110;
for (int i=1; i<=userCount; i++)
{
String name = "f"+i+"red";
createUser("", name);
}
moveUserHomeFolders();
for (int i=1; i<=userCount; i++)
{
String name = "f"+i+"red";
assertHomeFolderLocation(name, name.substring(0,2)+'/'+name);
}
}
@Test
public void testOverrideProvider() throws Exception
{
NodeRef person = createUser("a/b/c", "fred");
moveUserHomeFolders();
assertHomeFolderLocation("fred", "fr/fred");
properties.put("home_folder_provider_synchronizer.override_provider",
testHomeFolderProviderName);
moveUserHomeFolders();
assertHomeFolderLocation("fred", "a/b/c/fred");
String providerName = (String) nodeService.getProperty(person,
ContentModel.PROP_HOME_FOLDER_PROVIDER);
assertEquals(testHomeFolderProviderName , providerName);
}
@Test
public void testNoOriginalProvider() throws Exception
{
createUser("a/b/c", "fred", null, true);
properties.put("home_folder_provider_synchronizer.override_provider",
largeHomeFolderProviderName);
moveUserHomeFolders();
assertHomeFolderLocation("fred", "fr/fred"); // unchanged
assertTrue("Expected the empty parent 'a/b/c' to still exist as original " +
"root was unknown, because the original home folder provider was not set.",
exists("a/b/c"));
}
@Test
@SuppressWarnings("deprecation")
public void testVersion1HomeFolderProvider() throws Exception
{
// Should just log a message to say it can't do anything
final String name = "v1Provider";
HomeFolderProvider v1Provider = new HomeFolderProvider()
{
@Override
public void onCreateNode(ChildAssociationRef childAssocRef)
{
}
@Override
public String getName()
{
return name;
}
};
homeFolderManager.addProvider(v1Provider);
createUser("a/b/c", "fred");
properties.put("home_folder_provider_synchronizer.override_provider", name);
moveUserHomeFolders();
assertHomeFolderLocation("fred", "a/b/c/fred");
}
@Test
@SuppressWarnings("deprecation")
public void testExtendsAbstractHomeFolderProvider() throws Exception
{
// Should work through the V2Adaptor
final String name = "v1Provider";
AbstractHomeFolderProvider v1Provider = new UIDBasedHomeFolderProvider();
v1Provider.setBeanName(name);
v1Provider.setHomeFolderManager(homeFolderManager);
v1Provider.setOnCreatePermissionsManager(largeHomeFolderProvider.getOnCreatePermissionsManager());
v1Provider.setOnReferencePermissionsManager(largeHomeFolderProvider.getOnReferencePermissionsManager());
v1Provider.setOwnerOnCreate(largeHomeFolderProvider.getOwner());
v1Provider.setPath(largeHomeFolderProvider.getRootPath());
v1Provider.setServiceRegistry(serviceRegistry);
v1Provider.setStoreUrl(largeHomeFolderProvider.getStoreUrl());
v1Provider.setTenantService(tenantService);
v1Provider.afterPropertiesSet();
createUser("a/b/c", "fred");
properties.put("home_folder_provider_synchronizer.override_provider", name);
moveUserHomeFolders();
assertHomeFolderLocation("fred", "fred");
}
@Test
public void testTenantService() throws Exception
{
// Only test if running multi-tenant
if (tenantAdminService.isEnabled())
{
long time = System.currentTimeMillis();
final String tenant1 = time+".tenant1";
final String tenant2 = time+".tenant2";
createTenant(tenant1);
createTenant(tenant2);
createUser("", "fred");
createUser(tenant1, "", "fred");
createUser(tenant2, "", "fred");
moveUserHomeFolders();
assertHomeFolderLocation("fred", "fr/fred");
assertHomeFolderLocation(tenant1, "fred", "fr/"+tenantService.getDomainUser("fred", tenant1));
assertHomeFolderLocation(tenant2, "fred", "fr/"+tenantService.getDomainUser("fred", tenant2));
}
}
}

View File

@@ -0,0 +1,183 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.security.person;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.FileNameValidator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Implementation that returns a tree structure for a home folder based on a property (typically userName)
* from the supplied person. The parent folder names are derived from regular expression groups matched
* against the property value. The final folder name is the full property value.<p>
*
* For example, given the value "adavis" and the regular expression <tt>"^(..)"</tt> the
* resulting home folder path would be {@code "/ad/adavis"}. However with the regular expression
* <tt>"^(.)(.?)"</tt> the home folder path would be {@code "/a/d/adavis"}. If any group matches a zero
* length string, it is just ignored.<p>
*
* Note: In order to choose an efficient distribution scheme, be aware that, when m users are
* distributed into n leaf folders, when m >> n log n the statistical maximum load is
* m/n + O( sqrt((m log n)/n)), w.h.p
*
* @author Romain Guinot, Alan Davis
*/
public class RegexHomeFolderProvider extends UsernameHomeFolderProvider
{
private static Log logger = LogFactory.getLog(RegexHomeFolderProvider.class);
private QName propertyName;
private Pattern pattern;
private List<Integer> groupOrder;
/**
* @param propertyName String the cm:person property used as the key, such as userName
* or organizationId.
*/
public void setPropertyName(String propertyName)
{
this.propertyName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, propertyName);
}
/**
* @param patternString the regex pattern against the cm:person property value. Regex
* groups define the parent folder structure.
*/
public void setPattern(String patternString)
{
pattern = getPattern(patternString);
}
/**
* @param groupOrderString String the order (as a comma separated list) in which the
* regex pattern groups should be assembled into folders (such as {@code 2,1}).
* The default ordering is as they appear.
*/
public void setGroupOrder(String groupOrderString)
{
groupOrder = getGroupOrder(groupOrderString);
}
private Pattern getPattern(String patternString)
{
if (patternString == null || patternString.trim().length() == 0)
return null;
Pattern pattern;
try
{
pattern = Pattern.compile(patternString);
logger.debug("Successfully compiled patternString : " + patternString);
} catch (PatternSyntaxException pse)
{
throw new PersonException("Pattern string :" + patternString + " does not compile", pse);
}
return pattern;
}
private List<Integer> getGroupOrder(String groupOrderString)
{
if (groupOrderString == null || groupOrderString.trim().length() == 0)
return Collections.emptyList();
String[] groupOrderStrings = groupOrderString.split(",");
List<Integer>groupOrder = new ArrayList<Integer>(groupOrderStrings.length);
for (String group : groupOrderStrings)
{
Integer i;
try
{
i = Integer.valueOf(group);
}
catch (NumberFormatException nfe)
{
throw new PersonException("groupOrdering value " + groupOrderString + " is invalid.", nfe);
}
if (groupOrder.contains(i) || i < 0)
{
throw new PersonException("groupOrdering value " + groupOrderString + " is invalid.");
}
groupOrder.add(i);
}
return groupOrder;
}
@Override
public List<String> getHomeFolderPath(NodeRef person)
{
List<String> path = new ArrayList<String>();
String key = FileNameValidator.getValidFileName(
getHomeFolderManager().getPersonProperty(person, propertyName));
if (pattern != null)
{
Matcher matcher = pattern.matcher(key);
if (matcher.find())
{
int groupCount = matcher.groupCount();
if (!groupOrder.isEmpty())
{
for (int group : groupOrder)
{
if (group > groupCount)
{
throw new PersonException("groupOrdering value "
+ group + " is out of range.");
}
addFolderToPath(path, matcher, group);
}
}
else // "natural" group ordering, i.e as they appear in the regex
{
for (int group = 1; group <= groupCount; group++)
{
addFolderToPath(path, matcher, group);
}
}
}
}
path.add(key);
if (logger.isDebugEnabled())
{
logger.debug("returning "+path+" for key: "+key);
}
return path;
}
private void addFolderToPath(List<String> path, Matcher matcher, int group)
{
String folder = matcher.group(group);
if (folder.length() > 0)
{
path.add(folder);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
@@ -18,19 +18,18 @@
*/
package org.alfresco.repo.security.person;
import java.util.ArrayList;
import java.util.List;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.model.FileNotFoundException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.util.FileNameValidator;
/**
* Create home spaces based on the UID of the user.
* Creates home folders directly under the root path, based on the username of the user.
*
* If a suitable space is found it is reused, if not it will be made.
* @deprecated
* Depreciated since 4.0. {@link UsernameHomeFolderProvider} should now be used.
*
* @author Andy Hind
*/
@@ -40,86 +39,30 @@ public class UIDBasedHomeFolderProvider extends ExistingPathBasedHomeFolderProvi
private NodeRef templateNodeRef;
public UIDBasedHomeFolderProvider()
{
super();
}
public void setTemplatePath(String templatePath)
{
this.templatePath = templatePath;
}
protected HomeSpaceNodeRef getHomeFolder(NodeRef person)
{
FileFolderService fileFolderService = getServiceRegistry().getFileFolderService();
NodeService nodeService = getServiceRegistry().getNodeService();
NodeRef existingHomeFolder = DefaultTypeConverter.INSTANCE.convert(
NodeRef.class, nodeService.getProperty(person, ContentModel.PROP_HOMEFOLDER));
if (existingHomeFolder == null)
{
String uid = DefaultTypeConverter.INSTANCE.convert(
String.class,
nodeService.getProperty(person, ContentModel.PROP_USERNAME));
if((uid == null) || (uid.length() == 0))
{
throw new PersonException("Can not create a home space when the uid is null or empty");
}
// ETHREEOH-1612: Convert the username to file- and folder-safe names
String homeFolderName = FileNameValidator.getValidFileName(uid);
FileInfo fileInfo;
// Test if it already exists
NodeRef exising = fileFolderService.searchSimple(getPathNodeRef(), homeFolderName);
if (exising != null)
{
fileInfo = fileFolderService.getFileInfo(exising);
}
else
{
if (templatePath == null)
{
fileInfo = fileFolderService.create(
getPathNodeRef(),
homeFolderName,
ContentModel.TYPE_FOLDER);
}
else
{
try
{
fileInfo = fileFolderService.copy(
getTemplateNodeRef(),
getPathNodeRef(),
homeFolderName);
}
catch (FileNotFoundException e)
{
throw new PersonException("Invalid template to create home space");
}
}
}
NodeRef homeFolderNodeRef = fileInfo.getNodeRef();
return new HomeSpaceNodeRef(homeFolderNodeRef, HomeSpaceNodeRef.Status.CREATED);
}
else
{
return new HomeSpaceNodeRef(existingHomeFolder, HomeSpaceNodeRef.Status.VALID);
}
}
protected synchronized NodeRef getTemplateNodeRef()
{
if (templateNodeRef == null)
if (templateNodeRef == null && templatePath != null)
{
templateNodeRef = resolvePath(templatePath);
}
return templateNodeRef;
}
public List<String> getHomeFolderPath(NodeRef person)
{
List<String> path = new ArrayList<String>(1);
path.add(FileNameValidator.getValidFileName(
getHomeFolderManager().getPersonProperty(person, ContentModel.PROP_USERNAME)));
return path;
}
protected HomeSpaceNodeRef getHomeFolder(NodeRef person)
{
return getHomeFolderManager().getHomeFolder(getV2Adaptor(), person, false);
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.security.person;
import java.util.ArrayList;
import java.util.List;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.util.FileNameValidator;
/**
* Creates home folders directly under the root path, based on the username of the user.
*
* @author Alan Davis (based on UIDBasedHomeFolderProvider)
*/
public class UsernameHomeFolderProvider extends AbstractHomeFolderProvider2
{
private String templatePath;
private NodeRef templateNodeRef;
public void setTemplatePath(String templatePath)
{
this.templatePath = templatePath;
}
public synchronized NodeRef getTemplateNodeRef()
{
if (templateNodeRef == null && templatePath != null)
{
templateNodeRef = getHomeFolderManager().resolvePath(this, templatePath);
}
return templateNodeRef;
}
public List<String> getHomeFolderPath(NodeRef person)
{
List<String> path = new ArrayList<String>(1);
path.add(FileNameValidator.getValidFileName(
getHomeFolderManager().getPersonProperty(person, ContentModel.PROP_USERNAME)));
return path;
}
public HomeSpaceNodeRef getHomeFolder(NodeRef person)
{
return getHomeFolderManager().getHomeFolder(this, person, false);
}
}

View File

@@ -87,7 +87,7 @@ public class AlfrescoAssignment extends JBPMSpringAssignmentHandler
if (actorValStr.startsWith("#{"))
{
String expression = actorValStr.substring(2, actorValStr.length() -1);
Object eval = AlfrescoJavaScript.executeScript(executionContext, services, expression, null);
Object eval = AlfrescoJavaScript.executeScript(executionContext, services, expression, null, null);
if (eval == null)
{
throw new WorkflowException("actor expression '" + actorValStr + "' evaluates to null");
@@ -128,7 +128,7 @@ public class AlfrescoAssignment extends JBPMSpringAssignmentHandler
if (pooledactorValStr.startsWith("#{"))
{
String expression = pooledactorValStr.substring(2, pooledactorValStr.length() -1);
Object eval = AlfrescoJavaScript.executeScript(executionContext, services, expression, null);
Object eval = AlfrescoJavaScript.executeScript(executionContext, services, expression, null, null);
if (eval == null)
{
throw new WorkflowException("pooledactors expression '" + pooledactorValStr + "' evaluates to null");

View File

@@ -20,16 +20,17 @@ package org.alfresco.repo.workflow.jbpm;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.model.Repository;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.ScriptService;
import org.alfresco.service.cmr.workflow.WorkflowException;
import org.dom4j.Element;
@@ -65,16 +66,19 @@ public class AlfrescoJavaScript extends JBPMSpringActionHandler
private static JpdlXmlReader jpdlReader = new JpdlXmlReader((InputSource)null);
private ServiceRegistry services;
private NodeRef companyHome;
private Element script;
private String runas;
/* (non-Javadoc)
* @see org.alfresco.repo.workflow.jbpm.JBPMSpringActionHandler#initialiseHandler(org.springframework.beans.factory.BeanFactory)
/**
* {@inheritDoc}
*/
@Override
protected void initialiseHandler(BeanFactory factory)
{
services = (ServiceRegistry)factory.getBean(ServiceRegistry.SERVICE_REGISTRY);
this.services = (ServiceRegistry)factory.getBean(ServiceRegistry.SERVICE_REGISTRY);
Repository repositoryHelper = (Repository)factory.getBean("repositoryHelper");
this.companyHome = repositoryHelper.getCompanyHome();
}
/* (non-Javadoc)
@@ -122,7 +126,7 @@ public class AlfrescoJavaScript extends JBPMSpringActionHandler
String user = AuthenticationUtil.getFullyAuthenticatedUser();
if (runas == null && user !=null)
{
return executeScript(executionContext, services, expression, variableAccesses);
return executeScript(executionContext, services, expression, variableAccesses, companyHome);
}
else
{
@@ -134,21 +138,20 @@ public class AlfrescoJavaScript extends JBPMSpringActionHandler
{
validateRunAsUser();
}
return executeScriptAs(runAsUser, expression, executionContext, services, variableAccesses);
return executeScriptAs(runAsUser, expression, executionContext, variableAccesses);
}
}
private static Object executeScriptAs(String runAsUser,
private Object executeScriptAs(String runAsUser,
final String expression,
final ExecutionContext executionContext,
final ServiceRegistry services,
final List<VariableAccess> variableAccesses) {
// execute as specified runAsUser
return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Object>()
{
public Object doWork() throws Exception
{
return executeScript(executionContext, services, expression, variableAccesses);
return executeScript(executionContext, services, expression, variableAccesses,companyHome);
}
}, runAsUser);
}
@@ -252,11 +255,12 @@ public class AlfrescoJavaScript extends JBPMSpringActionHandler
* @param services Alfresco service registry
* @param expression script to execute
* @param variableAccesses (optional) list of jBPM variables to map into script (all, if not supplied)
* @param companyHome TODO
* @return script result
*/
public static Object executeScript(ExecutionContext context, ServiceRegistry services, String expression, List<VariableAccess> variableAccesses)
public static Object executeScript(ExecutionContext context, ServiceRegistry services, String expression, List<VariableAccess> variableAccesses, NodeRef companyHome)
{
Map<String, Object> inputMap = createInputMap(context, services, variableAccesses);
Map<String, Object> inputMap = createInputMap(services, companyHome, context, variableAccesses);
ScriptService scriptService = services.getScriptService();
scriptService.buildCoreModel(inputMap);
Object result = scriptService.executeScriptString(expression, inputMap);
@@ -329,26 +333,25 @@ public class AlfrescoJavaScript extends JBPMSpringActionHandler
* Construct map of arguments to pass to script
*
* Based on the <variable> elements of the action configuration.
*
* @param companyHome TODO
* @param executionContext the execution context
* @param variableAccesses the variable configuration
*
* @return the map of script arguments
*/
private static Map<String, Object> createInputMap(ExecutionContext executionContext, ServiceRegistry services, List<VariableAccess> variableAccesses)
private static Map<String, Object> createInputMap(ServiceRegistry services, NodeRef companyHome, ExecutionContext executionContext, List<VariableAccess> variableAccesses)
{
Map<String, Object> inputMap = new HashMap<String, Object>();
ScriptService scriptService = services.getScriptService();
// initialise global script variables
JBPMNode personNode = getPersonNode(executionContext, services);
if (personNode != null)
NodeRef person = getPersonNode(services);
NodeRef userHome = null;
if (person != null)
{
inputMap.put("person", personNode );
NodeRef homeSpace = (NodeRef)services.getNodeService().getProperty(personNode.getNodeRef(), ContentModel.PROP_HOMEFOLDER);
if (homeSpace != null)
{
inputMap.put("userhome", new JBPMNode(homeSpace, services));
}
NodeService nodeService = services.getNodeService();
userHome = (NodeRef)nodeService.getProperty(person, ContentModel.PROP_HOMEFOLDER);
}
Map<String, Object> inputMap = scriptService.buildDefaultModel(person, companyHome, userHome, null, null, null);
// initialise process variables
Token token = executionContext.getToken();
@@ -405,15 +408,12 @@ public class AlfrescoJavaScript extends JBPMSpringActionHandler
}
private static JBPMNode getPersonNode(ExecutionContext executionContext, ServiceRegistry services) {
private static NodeRef getPersonNode(ServiceRegistry services) {
String userName = AuthenticationUtil.getFullyAuthenticatedUser();
if(userName != null)
{
NodeRef person = services.getPersonService().getPerson(userName);
if(person !=null)
{
return new JBPMNode(person, services);
}
return person;
}
return null;
}

View File

@@ -1,12 +1,16 @@
package org.alfresco.repo.workflow.jbpm;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.Serializable;
import java.util.HashMap;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.jscript.ScriptNode;
import org.alfresco.repo.model.Repository;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.scripts.ScriptException;
@@ -30,6 +34,8 @@ import org.jbpm.context.exe.ContextInstance;
import org.jbpm.graph.exe.ExecutionContext;
import org.jbpm.graph.exe.Token;
import org.jbpm.taskmgmt.exe.TaskInstance;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
public class AlfrescoJavaScriptIntegrationTest extends BaseAlfrescoSpringTest
{
@@ -193,6 +199,39 @@ public class AlfrescoJavaScriptIntegrationTest extends BaseAlfrescoSpringTest
assertEquals(docLibB, nodeService.getPrimaryParent(doc).getParentRef());
}
public void testScopeVariables() throws Exception
{
String admin = AuthenticationUtil.getAdminUserName();
AuthenticationUtil.setFullyAuthenticatedUser(admin);
NodeRef person = personService.getPerson(admin);
Serializable userHome = nodeService.getProperty(person, ContentModel.PROP_HOMEFOLDER);
AlfrescoJavaScript scriptHandler = new AlfrescoJavaScript();
String key = "result";
// Check person node set.
Element script = buildScript("executionContext.setVariable('" + key + "', person)");
scriptHandler.setScript(script);
scriptHandler.execute(context);
ScriptNode value = (ScriptNode) variables.get(key);
assertEquals(person, value.getNodeRef());
// Check user home set.
script = buildScript("executionContext.setVariable('" + key + "', userhome)");
scriptHandler.setScript(script);
scriptHandler.execute(context);
value = (ScriptNode) variables.get(key);
assertEquals(userHome, value.getNodeRef());
// Check company home set.
NodeRef companyHome = repository.getCompanyHome();
script = buildScript("executionContext.setVariable('" + key + "', companyhome)");
scriptHandler.setScript(script);
scriptHandler.execute(context);
value = (ScriptNode) variables.get(key);
assertEquals(companyHome, value.getNodeRef());
}
private Element buildScript(String expression) {
Element script = DocumentHelper.createElement("script");
script.setText(expression);
@@ -203,7 +242,8 @@ public class AlfrescoJavaScriptIntegrationTest extends BaseAlfrescoSpringTest
@SuppressWarnings("deprecation")
protected void onSetUp() throws Exception {
super.onSetUp();
services = (ServiceRegistry) applicationContext.getBean("ServiceRegistry");
this.services = (ServiceRegistry) applicationContext.getBean("ServiceRegistry");
repository = (Repository) applicationContext.getBean("repositoryHelper");
personService = services.getPersonService();
createUser(BASIC_USER);
@@ -214,7 +254,25 @@ public class AlfrescoJavaScriptIntegrationTest extends BaseAlfrescoSpringTest
when(context.getContextInstance()).thenReturn(contextInstance);
variables = new HashMap<String, Object>();
when(contextInstance.getVariables()).thenReturn(variables);
when(contextInstance.getVariables((Token) any())).thenReturn(variables);
when(contextInstance.getVariables( any(Token.class))).thenReturn(variables);
when(context.getVariable(anyString())).thenAnswer(new Answer<Object>()
{
public Object answer(InvocationOnMock invocation) throws Throwable
{
String key = (String)invocation.getArguments()[0];
return variables.get(key);
}
});
doAnswer(new Answer<Void>()
{
public Void answer(InvocationOnMock invocation) throws Throwable
{
String key = (String)invocation.getArguments()[0];
Object value= invocation.getArguments()[1];
variables.put(key, value);
return null;
}
}).when(context).setVariable(anyString(), any());
}
private void createUser(String userName)
@@ -236,9 +294,9 @@ public class AlfrescoJavaScriptIntegrationTest extends BaseAlfrescoSpringTest
public static class TestUserStore {
private String runAsUser;
private String fullUser;
private JBPMNode person = null;
private ScriptNode person = null;
public void storeUsers(JBPMNode user)
public void storeUsers(ScriptNode user)
{
fullUser = AuthenticationUtil.getFullyAuthenticatedUser();
runAsUser = AuthenticationUtil.getRunAsUser();

View File

@@ -148,7 +148,7 @@ public class ForEachFork extends JBPMSpringActionHandler
private Collection<?> evaluateForEachExpression(final ExecutionContext executionContext, String forEachText)
{
String expression = forEachText.substring(2, forEachText.length() -1);
Object result = AlfrescoJavaScript.executeScript(executionContext, services, expression, null);
Object result = AlfrescoJavaScript.executeScript(executionContext, services, expression, null, null);
if (result == null)
{
throw new WorkflowException("forEach expression '" + forEachText + "' evaluates to null");

View File

@@ -0,0 +1,152 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.wcm;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.alfresco.repo.dictionary.DictionaryDAO;
import org.alfresco.repo.dictionary.M2Aspect;
import org.alfresco.repo.dictionary.M2Model;
import org.alfresco.repo.dictionary.M2Property;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.wcm.asset.AssetInfo;
import org.alfresco.wcm.asset.AssetService;
import org.alfresco.wcm.sandbox.SandboxInfo;
import org.alfresco.wcm.sandbox.SandboxService;
import org.alfresco.wcm.webproject.WebProjectInfo;
import org.alfresco.wcm.webproject.WebProjectService;
public class WCMAspectTest extends AbstractWCMServiceImplTest
{
private AssetService assetService = null;
private WebProjectService wpService = null;
private SandboxService sbService = null;
private DictionaryDAO dictionaryDAO = null;
private final static int SIZE = 1000;
private final static String ADMIN = "admin";
private static String TEST_TYPE_NAMESPACE = "http://www.alfresco.org/model/testaspectmodel/1.0";
private static QName TEST_ASPECT_QNAME = QName.createQName(TEST_TYPE_NAMESPACE, "Aspect");
private static QName PROP_QNAME = QName.createQName(TEST_TYPE_NAMESPACE, "applications");
@Override
protected void setUp() throws Exception
{
super.setUp();
wpService = (WebProjectService) ctx.getBean("WebProjectService");
sbService = (SandboxService) ctx.getBean("SandboxService");
assetService = (AssetService) ctx.getBean("AssetService");
dictionaryDAO = (DictionaryDAO) ctx.getBean("dictionaryDAO");
}
public void testAspect() throws Exception
{
try
{
AuthenticationUtil.setFullyAuthenticatedUser(ADMIN);
WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS + "-aspectSimple", TEST_WEBPROJ_NAME + "-aspectSimple", TEST_WEBPROJ_TITLE,
TEST_WEBPROJ_DESCRIPTION, TEST_WEBPROJ_DEFAULT_WEBAPP, TEST_WEBPROJ_DONT_USE_AS_TEMPLATE, null);
String wpStoreId = wpInfo.getStoreId();
String defaultWebApp = wpInfo.getDefaultWebApp();
SandboxInfo sbInfo = sbService.getAuthorSandbox(wpStoreId);
String authorSandboxId = sbInfo.getSandboxId();
String authorSandboxPath = sbInfo.getSandboxRootPath() + "/" + defaultWebApp;
assetService.createFile(authorSandboxId, authorSandboxPath, "myFile", null);
AssetInfo assetInfo = assetService.getAsset(authorSandboxId, authorSandboxPath + "/" + "myFile");
attachAspect(assetInfo);
checkAspect(assetInfo);
}
finally
{
AuthenticationUtil.clearCurrentSecurityContext();
}
}
@SuppressWarnings("unchecked")
private void checkAspect(AssetInfo assetInfo)
{
assertTrue(assetService.hasAspect(assetInfo, TEST_ASPECT_QNAME));
Map<QName, Serializable> properties = assetService.getAssetProperties(assetInfo);
List<String> list = (List<String>) properties.get(PROP_QNAME);
assertEquals(list.size(), SIZE);
}
private void attachAspect(final AssetInfo assetInfo)
{
M2Model model = M2Model.createModel("custom:custom");
model.createNamespace(TEST_TYPE_NAMESPACE, "custom");
model.createImport(NamespaceService.DICTIONARY_MODEL_1_0_URI, NamespaceService.DICTIONARY_MODEL_PREFIX);
model.createImport(NamespaceService.SYSTEM_MODEL_1_0_URI, NamespaceService.SYSTEM_MODEL_PREFIX);
model.createImport(NamespaceService.CONTENT_MODEL_1_0_URI, NamespaceService.CONTENT_MODEL_PREFIX);
M2Aspect testMandatoryAspect = model.createAspect("custom:" + TEST_ASPECT_QNAME.getLocalName());
M2Property prop = testMandatoryAspect.createProperty("custom:" + PROP_QNAME.getLocalName());
prop.setType("d:" + DataTypeDefinition.TEXT.getLocalName());
prop.setMultiValued(true);
prop.setIndexed(true);
dictionaryDAO.putModel(model);
final Map<QName, Serializable> aspectValues = new HashMap<QName, Serializable>();
List<String> applications = new ArrayList<String>();
for (int i = 0; i < SIZE; i++)
{
applications.add("Adding " + i);
}
aspectValues.put(PROP_QNAME, (Serializable) applications);
// takes about 150 milliseconds to commit
transactionService.getRetryingTransactionHelper().doInTransaction(
new RetryingTransactionCallback<Object>()
{
@Override
public Object execute() throws Throwable
{
assetService.addAspect(assetInfo, TEST_ASPECT_QNAME, aspectValues);
return null;
}
});
}
}

View File

@@ -43,6 +43,7 @@ public class WCMTestSuite extends TestSuite
{
TestSuite suite = new TestSuite();
suite.addTestSuite(WCMAspectTest.class);
suite.addTestSuite(WebProjectServiceImplTest.class);
suite.addTestSuite(AssetServiceImplTest.class);
suite.addTestSuite(SandboxServiceImplTest.class);