diff --git a/config/alfresco/action-services-context.xml b/config/alfresco/action-services-context.xml index 01782ca01c..073475f43e 100644 --- a/config/alfresco/action-services-context.xml +++ b/config/alfresco/action-services-context.xml @@ -320,6 +320,9 @@ + + + diff --git a/config/alfresco/bootstrap-context.xml b/config/alfresco/bootstrap-context.xml index 3e8f1bd319..8835867415 100644 --- a/config/alfresco/bootstrap-context.xml +++ b/config/alfresco/bootstrap-context.xml @@ -171,6 +171,7 @@ + @@ -767,6 +768,9 @@ + + ${system.workflow.engine.jbpm.enabled} + diff --git a/config/alfresco/dbscripts/upgrade/4.1/org.hibernate.dialect.Dialect/remove-column-activiti.sql b/config/alfresco/dbscripts/upgrade/4.1/org.hibernate.dialect.Dialect/remove-column-activiti.sql new file mode 100644 index 0000000000..2e38ca73ba --- /dev/null +++ b/config/alfresco/dbscripts/upgrade/4.1/org.hibernate.dialect.Dialect/remove-column-activiti.sql @@ -0,0 +1,23 @@ +-- +-- Title: Remove unnecessary column for Activiti +-- Database: Generic +-- Since: V4.1 Schema 5115 +-- Author: Alex Mukha +-- +-- Please contact support@alfresco.com if you need assistance with the upgrade. +-- +-- ALF-16038 : DB2: Upgrade script needed to remove ALFUSER.ACT_HI_ACTINST.OWNER_ + +-- Patch is applied only for DB2, see ALF-16038 + +-- +-- Record script finish +-- +DELETE FROM alf_applied_patch WHERE id = 'patch.db-V4.1-remove-column-activiti'; +INSERT INTO alf_applied_patch + (id, description, fixes_from_schema, fixes_to_schema, applied_to_schema, target_schema, applied_on_date, applied_to_server, was_executed, succeeded, report) + VALUES + ( + 'patch.db-V4.1-remove-column-activiti', 'ALF-16038 : DB2: Upgrade script to remove ALFUSER.ACT_HI_ACTINST.OWNER_', + 0, 6017, -1, 6018, null, 'UNKNOWN', ${TRUE}, ${TRUE}, 'Script completed' + ); \ No newline at end of file diff --git a/config/alfresco/form-services-context.xml b/config/alfresco/form-services-context.xml index 7e032613da..301fcf14fd 100644 --- a/config/alfresco/form-services-context.xml +++ b/config/alfresco/form-services-context.xml @@ -177,7 +177,7 @@ class="org.alfresco.repo.forms.processor.workflow.TaskFormProcessor" parent="baseFormProcessor"> - + diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-common-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-common-SqlMap.xml index c565e773a8..52f729b194 100644 --- a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-common-SqlMap.xml +++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-common-SqlMap.xml @@ -525,6 +525,37 @@ ]]> + + + + + + + + delete from alf_node_properties where diff --git a/config/alfresco/messages/bootstrap-spaces.properties b/config/alfresco/messages/bootstrap-spaces.properties index 40af1101c7..0a94e53356 100644 --- a/config/alfresco/messages/bootstrap-spaces.properties +++ b/config/alfresco/messages/bootstrap-spaces.properties @@ -76,6 +76,9 @@ spaces.wcm.description=Web Content Management Spaces spaces.wcm_content_forms.name=Web Forms spaces.wcm_content_forms.description=Web Content Forms +spaces.web_deployed.name=Web Deployed +spaces.web_deployed.description=Deployed Web Projects. Content deployed from an WCM Authoring environment. + spaces.user_homes.name=User Homes spaces.user_homes.description=User Homes diff --git a/config/alfresco/messages/patch-service.properties b/config/alfresco/messages/patch-service.properties index 0006f0d181..bd5873fe0e 100644 --- a/config/alfresco/messages/patch-service.properties +++ b/config/alfresco/messages/patch-service.properties @@ -484,4 +484,5 @@ patch.redeployParallelActivitiWorkflows.description=Patch that redeploys both pa patch.show.audit.success=show_audit.ftl was updated successfully -patch.increaseColumnSizeActiviti.description=ALF-14983 : Upgrade scripts to increase column sizes for Activiti \ No newline at end of file +patch.increaseColumnSizeActiviti.description=ALF-14983 : Upgrade scripts to increase column sizes for Activiti +patch.removeColumnActiviti.description=ALF-16038 : DB2: Upgrade script to remove ALFUSER.ACT_HI_ACTINST.OWNER_ \ No newline at end of file diff --git a/config/alfresco/messages/schema-update.properties b/config/alfresco/messages/schema-update.properties index 32c2e5a5d0..9bdd876e15 100644 --- a/config/alfresco/messages/schema-update.properties +++ b/config/alfresco/messages/schema-update.properties @@ -1,6 +1,7 @@ # Schema update messages schema.update.msg.dialect_used=Schema managed by database dialect {0}. +schema.update.msg.database_used=Connecting to database: {0} schema.update.msg.bypassing=Bypassing schema update checks. schema.update.msg.normalized_schema=Normalized schema dumped to file {0}. schema.update.msg.normalized_schema_pre=Normalized schema (pre-bootstrap) dumped to file {0}. diff --git a/config/alfresco/model-specific-services-context.xml b/config/alfresco/model-specific-services-context.xml index 17bfe65c2c..a21a4e76c5 100644 --- a/config/alfresco/model-specific-services-context.xml +++ b/config/alfresco/model-specific-services-context.xml @@ -123,7 +123,7 @@ - + @@ -141,6 +141,7 @@ + diff --git a/config/alfresco/patch/patch-services-context.xml b/config/alfresco/patch/patch-services-context.xml index c59a966f4a..0df84d8e5c 100644 --- a/config/alfresco/patch/patch-services-context.xml +++ b/config/alfresco/patch/patch-services-context.xml @@ -3361,4 +3361,15 @@ + + + + + + + + classpath:alfresco/dbscripts/upgrade/4.1/${db.script.dialect}/remove-column-activiti.sql + + + diff --git a/config/alfresco/subsystems/fileServers/default/file-servers-context.xml b/config/alfresco/subsystems/fileServers/default/file-servers-context.xml index 31d120cf9a..77f95e4d04 100644 --- a/config/alfresco/subsystems/fileServers/default/file-servers-context.xml +++ b/config/alfresco/subsystems/fileServers/default/file-servers-context.xml @@ -353,6 +353,52 @@ ${nfs.mountServerDebug} + + + + + + + + + + + HOME + + + + + + + + + + + + + + + + + + + + + + + + + ${filesystem.name} + + + ${protocols.storeName} + + + ${filesystem.rootPath} + + - diff --git a/config/alfresco/version.properties b/config/alfresco/version.properties index 7c5ccc63c8..d275d79a04 100644 --- a/config/alfresco/version.properties +++ b/config/alfresco/version.properties @@ -19,4 +19,4 @@ version.build=@build-number@ # Schema number -version.schema=6017 +version.schema=6018 diff --git a/config/alfresco/wiki-services-context.xml b/config/alfresco/wiki-services-context.xml index c4d60af7c9..8e398a941e 100644 --- a/config/alfresco/wiki-services-context.xml +++ b/config/alfresco/wiki-services-context.xml @@ -22,17 +22,21 @@ - - - - - - - ${server.transaction.mode.default} - - - + + + + + + + + + + ${server.transaction.mode.readOnly} + ${server.transaction.mode.readOnly} + ${server.transaction.mode.default} + + + diff --git a/pom.xml b/pom.xml index c8414e2ae4..4f2a0380a2 100644 --- a/pom.xml +++ b/pom.xml @@ -376,6 +376,11 @@ XmlSchema 1.4.5 + + org.jgroups + jgroups + 2.11.1.Final + diff --git a/source/java/org/alfresco/filesys/alfresco/HomeShareMapper.java b/source/java/org/alfresco/filesys/alfresco/HomeShareMapper.java index a808f294d9..b6bdb4d9ac 100644 --- a/source/java/org/alfresco/filesys/alfresco/HomeShareMapper.java +++ b/source/java/org/alfresco/filesys/alfresco/HomeShareMapper.java @@ -21,12 +21,8 @@ package org.alfresco.filesys.alfresco; import java.util.Enumeration; -import org.springframework.extensions.config.ConfigElement; -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.filesys.AlfrescoConfigSection; import org.alfresco.filesys.config.ServerConfigurationBean; import org.alfresco.filesys.repo.ContentContext; -import org.alfresco.filesys.repo.ContentDiskDriver; import org.alfresco.jlan.server.SrvSession; import org.alfresco.jlan.server.auth.InvalidUserException; import org.alfresco.jlan.server.config.InvalidConfigurationException; @@ -39,8 +35,15 @@ import org.alfresco.jlan.server.core.SharedDeviceList; import org.alfresco.jlan.server.filesys.DiskInterface; import org.alfresco.jlan.server.filesys.DiskSharedDevice; import org.alfresco.jlan.server.filesys.FilesystemsConfigSection; +import org.alfresco.jlan.server.filesys.quota.QuotaManager; +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.util.PropertyCheck; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.config.ConfigElement; /** * Home Share Mapper Class @@ -49,50 +52,47 @@ import org.apache.commons.logging.LogFactory; * configuration and provides a dynamic home share mapped to the users home node. * * @author GKSpencer + * @author mrogers */ public class HomeShareMapper implements ShareMapper { // Logging - private static final Log logger = LogFactory.getLog("org.alfresco.smb.protocol"); + private static final Log logger = LogFactory.getLog("org.alfresco.filesys.alfresco.HomeShareMapper"); // Home folder share name - public static final String HOME_FOLDER_SHARE = "HOME"; // Server configuration - private ServerConfigurationAccessor m_config; - - private DiskInterface m_repoDiskInterface; - + // Home folder share name + private String homeShareName = HOME_FOLDER_SHARE; - private String m_homeShareName = HOME_FOLDER_SHARE; + private PersonService personService; + private NodeService nodeService; + private DiskInterface repoDiskInterface; - // Debug enable flag - - private boolean m_debug; - + private QuotaManager quotaManager; // optional quota manager - public void setConfig(ServerConfiguration config) + public void init() + { + PropertyCheck.mandatory(this, "ServerConfiguration", m_config); + PropertyCheck.mandatory(this, "Home share name", homeShareName); + PropertyCheck.mandatory(this, "personService", getPersonService()); + PropertyCheck.mandatory(this, "nodeService", getNodeService()); + PropertyCheck.mandatory(this, "repoDiskInterface", getRepoDiskInterface()); + + } + + public void setServerConfiguration(ServerConfiguration config) { m_config = config; } - public void setRepoDiskInterface(DiskInterface diskInterface) - { - m_repoDiskInterface = diskInterface; - } - public void setHomeShareName(String shareName) { - m_homeShareName = shareName; - } - - public void setDebug(boolean m_debug) - { - this.m_debug = m_debug; + homeShareName = shareName; } /** @@ -112,31 +112,7 @@ public class HomeShareMapper implements ShareMapper public void initializeMapper(ServerConfiguration config, ConfigElement params) throws InvalidConfigurationException { // Save the server configuration - - setConfig(config); - - setRepoDiskInterface(((AlfrescoConfigSection) m_config.getConfigSection( AlfrescoConfigSection.SectionName)).getRepoDiskInterface()); - - // Check if the home share name has been specified - - String homeName = params.getAttribute("name"); - if ( homeName != null && homeName.length() > 0) - setHomeShareName(homeName); - - // Check if debug is enabled - - if (params != null && params.getChild("debug") != null) - setDebug(true); - } - - /** - * Check if debug output is enabled - * - * @return boolean - */ - public final boolean hasDebug() - { - return m_debug; + setServerConfiguration(config); } /** @@ -146,7 +122,7 @@ public class HomeShareMapper implements ShareMapper */ public final String getHomeFolderName() { - return m_homeShareName; + return homeShareName; } /** @@ -162,21 +138,31 @@ public class HomeShareMapper implements ShareMapper // Check if the user has a home folder, and the session does not currently have any // dynamic shares defined - if ( sess != null && sess.hasClientInformation() && sess.hasDynamicShares() == false && + if ( sess != null && + sess.hasClientInformation() && + sess.hasDynamicShares() == false && sess.getClientInformation() instanceof AlfrescoClientInfo) { AlfrescoClientInfo client = (AlfrescoClientInfo) sess.getClientInformation(); - if ( client.hasHomeFolder()) + + NodeRef personNode = getPersonService().getPerson(client.getUserName()); + + if(personNode != null) { - // Create the home folder share + NodeRef homeSpaceRef = (NodeRef)getNodeService().getProperty(personNode, ContentModel.PROP_HOMEFOLDER); + + if (homeSpaceRef != null) + { + // Create the home folder share + DiskSharedDevice homeShare = createHomeDiskShare(homeSpaceRef, client.getUserName()); + sess.addDynamicShare(homeShare); - DiskSharedDevice homeShare = createHomeDiskShare(client); - sess.addDynamicShare(homeShare); - // Debug - - if ( logger.isDebugEnabled()) - logger.debug("Added " + getHomeFolderName() + " share to list of shares for " + client.getUserName()); + if ( logger.isDebugEnabled()) + { + logger.debug("Added " + getHomeFolderName() + " share to list of shares for " + client.getUserName()); + } + } } } @@ -194,7 +180,9 @@ public class HomeShareMapper implements ShareMapper // Remove unavailable shares from the list and return the list if ( allShares == false) + { shrList.removeUnavailableShares(); + } return shrList; } @@ -231,7 +219,7 @@ public class HomeShareMapper implements ShareMapper // Check if the user has a home folder node - if ( client != null && client.hasHomeFolder()) { + if ( client != null ) { // Check if the share has already been created for the session @@ -253,23 +241,33 @@ public class HomeShareMapper implements ShareMapper // Create the home share mapped to the users home folder - DiskSharedDevice diskShare = createHomeDiskShare(client); + NodeRef personNode = getPersonService().getPerson(client.getUserName()); - // Add the new share to the sessions dynamic share list + if(personNode != null) + { + NodeRef homeSpaceRef = (NodeRef)getNodeService().getProperty(personNode, ContentModel.PROP_HOMEFOLDER); + + DiskSharedDevice diskShare = createHomeDiskShare(client.getHomeFolder(), client.getUserName()); + + // Add the new share to the sessions dynamic share list - sess.addDynamicShare(diskShare); - share = diskShare; + sess.addDynamicShare(diskShare); + share = diskShare; - // DEBUG - - if (logger.isDebugEnabled()) - logger.debug(" Mapped share " + name + " to " + client.getHomeFolder()); + if (logger.isDebugEnabled()) + { + logger.debug(" Mapped share " + name + " to " + client.getHomeFolder()); + } + } } } else + { throw new InvalidUserException("No home directory"); + } } - else { + else + { // Find the required share by name/type. Use a case sensitive search first, if that fails use a case // insensitive search. @@ -287,7 +285,9 @@ public class HomeShareMapper implements ShareMapper // Check if the share is available if ( share != null && share.getContext() != null && share.getContext().isAvailable() == false) + { share = null; + } // Return the shared device, or null if no matching device was found @@ -347,39 +347,69 @@ public class HomeShareMapper implements ShareMapper * @param client AlfrescoClientInfo * @return DiskSharedDevice */ - private final DiskSharedDevice createHomeDiskShare(AlfrescoClientInfo client) + private final DiskSharedDevice createHomeDiskShare(NodeRef homeFolderRef, String userName) { // Create the disk driver and context + logger.debug("create home share for user " + userName); - ExtendedDiskInterface diskDrv = (ExtendedDiskInterface) getRepoDiskInterface(); - ContentContext diskCtx = new ContentContext( getHomeFolderName(), "", "", client.getHomeFolder()); + DiskInterface diskDrv = getRepoDiskInterface(); + + ContentContext diskCtx = new ContentContext( getHomeFolderName(), "", "", homeFolderRef); - if(m_config instanceof ServerConfigurationBean) + if ( getQuotaManager() != null) { - ServerConfigurationBean config = (ServerConfigurationBean)m_config; - - config.initialiseRuntimeContext(diskCtx); - - // Enable file state caching - // diskCtx.enableStateCache(serverConfigurationBean, true); - } - else - { - throw new AlfrescoRuntimeException("configuration error, unknown configuration bean"); + diskCtx.setQuotaManager( getQuotaManager()); } + + ServerConfigurationBean config = (ServerConfigurationBean)m_config; + config.initialiseRuntimeContext("cifs.home." + userName, diskCtx); // Create a temporary shared device for the users home directory - return new DiskSharedDevice(getHomeFolderName(), diskDrv, diskCtx, SharedDevice.Temporary); } - protected DiskInterface getRepoDiskInterface() - { - return m_repoDiskInterface; - } - protected FilesystemsConfigSection getFilesystemsConfigSection() { return (FilesystemsConfigSection)m_config.getConfigSection(FilesystemsConfigSection.SectionName); } + + public void setPersonService(PersonService personService) + { + this.personService = personService; + } + + public PersonService getPersonService() + { + return personService; + } + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public NodeService getNodeService() + { + return nodeService; + } + + public void setRepoDiskInterface(DiskInterface repoDiskInterface) + { + this.repoDiskInterface = repoDiskInterface; + } + + public DiskInterface getRepoDiskInterface() + { + return repoDiskInterface; + } + + public void setQuotaManager(QuotaManager quotaManager) + { + this.quotaManager = quotaManager; + } + + public QuotaManager getQuotaManager() + { + return quotaManager; + } } diff --git a/source/java/org/alfresco/filesys/alfresco/MultiTenantShareMapper.java b/source/java/org/alfresco/filesys/alfresco/MultiTenantShareMapper.java index 4ae54f9dc2..e3bb276087 100644 --- a/source/java/org/alfresco/filesys/alfresco/MultiTenantShareMapper.java +++ b/source/java/org/alfresco/filesys/alfresco/MultiTenantShareMapper.java @@ -43,6 +43,7 @@ import org.alfresco.jlan.server.filesys.SrvDiskInfo; import org.alfresco.jlan.server.filesys.quota.QuotaManager; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.util.PropertyCheck; import org.springframework.beans.factory.InitializingBean; import org.alfresco.filesys.config.ServerConfigurationBean; import org.apache.commons.logging.Log; @@ -67,7 +68,7 @@ public class MultiTenantShareMapper implements ShareMapper, ConfigurationListene private String m_tenantShareName; - // Store name and root path from standard content filesystem share + // Store name and root path private String m_rootPath; private String m_storeName; @@ -79,6 +80,8 @@ public class MultiTenantShareMapper implements ShareMapper, ConfigurationListene // Quota manager to use when creating multi-tenant shares private QuotaManager m_quotaManager; + + private DiskInterface repoDiskInterface; /** * Default constructor @@ -87,29 +90,14 @@ public class MultiTenantShareMapper implements ShareMapper, ConfigurationListene { } - - public void setServerConfiguration(ServerConfiguration config) - { - this.m_config = config; - } - - - - public void setTenantShareName(String shareName) - { - m_tenantShareName = shareName; - } - - - - /** - * Set the quota manager to be used by multi-tenant shares - * - * @param quotaManager QuotaManager - */ - public void setQuotaManager( QuotaManager quotaManager) { - m_quotaManager = quotaManager; - } + public void init() + { + PropertyCheck.mandatory(this, "ServerConfiguration", m_config); + PropertyCheck.mandatory(this, "Tenant share name", m_tenantShareName); + PropertyCheck.mandatory(this, "repoDiskInterface", getRepoDiskInterface()); + PropertyCheck.mandatory(this, "Store name", m_storeName); + PropertyCheck.mandatory(this, "Root Path", m_rootPath); + } /** * Initialize the share mapper @@ -129,20 +117,6 @@ public class MultiTenantShareMapper implements ShareMapper, ConfigurationListene // Save the server configuration setServerConfiguration(config); - - // Check if a tenant share name has been specified - - ConfigElement tenantShareName = params.getChild( "TenantShareName"); - - if ( tenantShareName != null) - { - // Validate the share name - - if ( tenantShareName.getValue() != null && tenantShareName.getValue().length() > 0) - setTenantShareName(tenantShareName.getValue()); - else - throw new InvalidConfigurationException("Invalid tenant share name"); - } // Complete initialization afterPropertiesSet(); @@ -162,11 +136,6 @@ public class MultiTenantShareMapper implements ShareMapper, ConfigurationListene if ( m_filesysConfig == null || m_alfrescoConfig == null) m_config.addListener( this); - // Find the content filesystem details to be used for the tenant shares - - if ( m_filesysConfig != null) - findContentShareDetails(); - // Create the tenant share lists table m_tenantShareLists = new Hashtable(); @@ -180,7 +149,7 @@ public class MultiTenantShareMapper implements ShareMapper, ConfigurationListene * @param typ int * @param sess SrvSession * @param create boolean - * @return SharedDevice + * @return SharedDevice or null if no matching device was found * @exception InvalidUserException */ public SharedDevice findShare(String host, String name, int typ, SrvSession sess, boolean create) @@ -188,9 +157,12 @@ public class MultiTenantShareMapper implements ShareMapper, ConfigurationListene // Check if this is a tenant user - if ( m_alfrescoConfig.getTenantService().isEnabled() && m_alfrescoConfig.getTenantService().isTenantUser() && + if ( m_alfrescoConfig.getTenantService().isEnabled() && + m_alfrescoConfig.getTenantService().isTenantUser() && typ != ShareType.ADMINPIPE) + { return findTenantShare(host, name, typ, sess, create); + } // Find the required share by name/type. Use a case sensitive search first, if that fails use a case // insensitive search. @@ -261,23 +233,26 @@ public class MultiTenantShareMapper implements ShareMapper, ConfigurationListene // Check if this is a tenant user if ( m_alfrescoConfig.getTenantService().isEnabled() && m_alfrescoConfig.getTenantService().isTenantUser()) + { return getTenantShareList(host, sess, allShares); + } // Make a copy of the global share list and add the per session dynamic shares SharedDeviceList shrList = new SharedDeviceList( m_filesysConfig.getShares()); - if ( sess != null && sess.hasDynamicShares()) { - - // Add the per session dynamic shares - + if ( sess != null && sess.hasDynamicShares()) + { + // Add the per session dynamic shares shrList.addShares(sess.getDynamicShareList()); } // Remove unavailable shares from the list and return the list if ( allShares == false) - shrList.removeUnavailableShares(); + { + shrList.removeUnavailableShares(); + } return shrList; } @@ -322,10 +297,6 @@ public class MultiTenantShareMapper implements ShareMapper, ConfigurationListene return ConfigurationListener.StsAccepted; } - // Check if the tenant share template details have been set - - if ( m_rootPath == null) - findContentShareDetails(); // Return a dummy status @@ -350,9 +321,11 @@ public class MultiTenantShareMapper implements ShareMapper, ConfigurationListene SharedDeviceList shareList = getTenantShareList(host, sess, true); if ( shareList == null) + { return null; + } - // Search for the required share + // Search for the required share in the list of shares for the tennant return shareList.findShare( name, typ, true); } @@ -375,6 +348,11 @@ public class MultiTenantShareMapper implements ShareMapper, ConfigurationListene SharedDeviceList shareList = null; + if(m_tenantShareLists.containsKey(tenantDomain)) + { + shareList = m_tenantShareLists.get( tenantDomain); + } + synchronized ( m_tenantShareLists) { // Get the tenant specific share list @@ -389,7 +367,7 @@ public class MultiTenantShareMapper implements ShareMapper, ConfigurationListene // Create a tenant specific share for this domain - shareList.addShare( createTenantShare()); + shareList.addShare( createTenantShare(tenantDomain)); // Store the list for use by other members of this domain @@ -405,20 +383,25 @@ public class MultiTenantShareMapper implements ShareMapper, ConfigurationListene /** * Create a tenant domain specific share */ - private final DiskSharedDevice createTenantShare() + private final DiskSharedDevice createTenantShare(String tenantDomain) { - StoreRef storeRef = new StoreRef(m_storeName); + logger.debug("create tenant share for domain " + tenantDomain); + StoreRef storeRef = new StoreRef(getStoreName()); NodeRef rootNodeRef = new NodeRef(storeRef.getProtocol(), storeRef.getIdentifier(), "dummy"); // Root nodeRef is required for storeRef part - rootNodeRef = m_alfrescoConfig.getTenantService().getRootNode(m_alfrescoConfig.getNodeService(), m_alfrescoConfig.getSearchService(), - m_alfrescoConfig.getNamespaceService(), m_rootPath, rootNodeRef); + rootNodeRef = m_alfrescoConfig.getTenantService().getRootNode( + m_alfrescoConfig.getNodeService(), + m_alfrescoConfig.getSearchService(), + m_alfrescoConfig.getNamespaceService(), + getRootPath(), + rootNodeRef); // Create the disk driver and context - DiskInterface diskDrv = m_alfrescoConfig.getRepoDiskInterface(); - ContentContext diskCtx = new ContentContext(m_tenantShareName, "", m_rootPath, rootNodeRef); + DiskInterface diskDrv = getRepoDiskInterface(); + ContentContext diskCtx = new ContentContext(m_tenantShareName, getStoreName(), getRootPath(), rootNodeRef); // Set a quota manager for the share, if enabled @@ -431,10 +414,7 @@ public class MultiTenantShareMapper implements ShareMapper, ConfigurationListene { ServerConfigurationBean config = (ServerConfigurationBean)m_config; - config.initialiseRuntimeContext(diskCtx); - - // Enable file state caching - // diskCtx.enableStateCache(serverConfigurationBean, true); + config.initialiseRuntimeContext("cifs.tenant." + tenantDomain, diskCtx); } else { @@ -450,40 +430,53 @@ public class MultiTenantShareMapper implements ShareMapper, ConfigurationListene return new DiskSharedDevice(m_tenantShareName, diskDrv, diskCtx); } - /** - * Find the content filesystem driver details used for the tenant shares - */ - private final void findContentShareDetails() + public void setServerConfiguration(ServerConfiguration config) { - // Need the file system configuration to do the lookup - - if ( m_filesysConfig == null) - return; - - // Get the fixed share list - - SharedDeviceList shareList = m_filesysConfig.getShares(); - Enumeration shareEnum = shareList.enumerateShares(); - - while ( shareEnum.hasMoreElements()) - { - // Get the current shared device - - SharedDevice share = shareEnum.nextElement(); - if ( share.getContext() instanceof ContentContext) - { - // Found a content filesystem share - - ContentContext ctx = (ContentContext) share.getContext(); - - // Store the share details that are used for the tenant shares - - m_rootPath = ctx.getRootPath(); - m_storeName = ctx.getStoreName(); - - if ( m_tenantShareName == null) - m_tenantShareName = ctx.getDeviceName(); - } - } + this.m_config = config; } + + public void setTenantShareName(String shareName) + { + m_tenantShareName = shareName; + } + + /** + * Set the quota manager to be used by multi-tenant shares + * + * @param quotaManager QuotaManager + */ + public void setQuotaManager( QuotaManager quotaManager) + { + m_quotaManager = quotaManager; + } + + public void setRootPath(String m_rootPath) + { + this.m_rootPath = m_rootPath; + } + + public String getRootPath() + { + return m_rootPath; + } + + public void setStoreName(String m_storeName) + { + this.m_storeName = m_storeName; + } + + public String getStoreName() + { + return m_storeName; + } + + public void setRepoDiskInterface(DiskInterface repoDiskInterface) + { + this.repoDiskInterface = repoDiskInterface; + } + + public DiskInterface getRepoDiskInterface() + { + return repoDiskInterface; + } } diff --git a/source/java/org/alfresco/filesys/avm/AVMShareMapper.java b/source/java/org/alfresco/filesys/avm/AVMShareMapper.java index 59313b0440..bd72faa1f4 100644 --- a/source/java/org/alfresco/filesys/avm/AVMShareMapper.java +++ b/source/java/org/alfresco/filesys/avm/AVMShareMapper.java @@ -291,7 +291,7 @@ public class AVMShareMapper implements ShareMapper, InitializingBean { { ServerConfigurationBean config = (ServerConfigurationBean)m_config; - config.initialiseRuntimeContext(avmCtx); + config.initialiseRuntimeContext("cifs.avm" + name, avmCtx); // Enable file state caching // diskCtx.enableStateCache(serverConfigurationBean, true); diff --git a/source/java/org/alfresco/filesys/config/ServerConfigurationBean.java b/source/java/org/alfresco/filesys/config/ServerConfigurationBean.java index dcd7e239ed..d261b9096b 100644 --- a/source/java/org/alfresco/filesys/config/ServerConfigurationBean.java +++ b/source/java/org/alfresco/filesys/config/ServerConfigurationBean.java @@ -1704,7 +1704,7 @@ public class ServerConfigurationBean extends AbstractServerConfigurationBean imp { logger.debug("start hazelcast cache : " + clusterConfigBean.getClusterName() + ", shareName: "+ diskCtx.getShareName()); } - GenericConfigElement hazelConfig = createClusterConfig(diskCtx.getShareName()); + GenericConfigElement hazelConfig = createClusterConfig("cifs.avm."+diskCtx.getShareName()); HazelCastClusterFileStateCache hazel = new HazelCastClusterFileStateCache(); hazel.initializeCache(hazelConfig, this); diskCtx.setStateCache(hazel); @@ -1716,6 +1716,7 @@ public class ServerConfigurationBean extends AbstractServerConfigurationBean imp standaloneCache.initializeCache( new GenericConfigElement( ""), this); diskCtx.setStateCache(standaloneCache); } + if ( diskCtx.hasStateCache()) { // Register the state cache with the reaper thread @@ -1747,7 +1748,7 @@ public class ServerConfigurationBean extends AbstractServerConfigurationBean imp { logger.debug("start hazelcast cache : " + clusterConfigBean.getClusterName() + ", shareName: "+ filesysContext.getShareName()); } - GenericConfigElement hazelConfig = createClusterConfig(filesysContext.getShareName()); + GenericConfigElement hazelConfig = createClusterConfig("cifs.filesys."+filesysContext.getShareName()); HazelCastClusterFileStateCache hazel = new HazelCastClusterFileStateCache(); hazel.initializeCache(hazelConfig, this); filesysContext.setStateCache(hazel); @@ -1763,7 +1764,7 @@ public class ServerConfigurationBean extends AbstractServerConfigurationBean imp if ( filesysContext.hasStateCache()) { // Register the state cache with the reaper thread - + // has many side effects including initialisation of the cache fsysConfig.addFileStateCache( filesystem.getDeviceName(), filesysContext.getStateCache()); // Create the lock manager for the context. @@ -1970,19 +1971,7 @@ public class ServerConfigurationBean extends AbstractServerConfigurationBean imp // Associate the share mapper secConfig.setShareMapper(shareMapper); } - else - { - // Check if the tenant service is enabled - if (m_tenantService != null && m_tenantService.isEnabled()) - { - // Initialize the multi-tenancy share mapper - - secConfig.setShareMapper("org.alfresco.filesys.alfresco.MultiTenantShareMapper", - new GenericConfigElement("shareMapper")); - - } - } - + // Check if any domain mappings have been specified List mappings = securityConfigBean.getDomainMappings(); @@ -2256,8 +2245,10 @@ public class ServerConfigurationBean extends AbstractServerConfigurationBean imp * * @param diskCtx */ - public void initialiseRuntimeContext(AlfrescoContext diskCtx) + public void initialiseRuntimeContext(String uniqueName, AlfrescoContext diskCtx) { + logger.debug("initialiseRuntimeContext" + diskCtx); + if (diskCtx.getStateCache() == null) { // Set the state cache, use a hard coded standalone cache for now @@ -2272,9 +2263,9 @@ public class ServerConfigurationBean extends AbstractServerConfigurationBean imp { if(logger.isDebugEnabled()) { - logger.debug("start hazelcast cache : " + clusterConfigBean.getClusterName() + ", shareName: "+ diskCtx.getShareName()); + logger.debug("start hazelcast cache : " + clusterConfigBean.getClusterName() + ", uniqueName: "+ uniqueName); } - GenericConfigElement hazelConfig = createClusterConfig(diskCtx.getShareName()); + GenericConfigElement hazelConfig = createClusterConfig(uniqueName); HazelCastClusterFileStateCache hazel = new HazelCastClusterFileStateCache(); hazel.initializeCache(hazelConfig, this); diskCtx.setStateCache(hazel); @@ -2287,10 +2278,14 @@ public class ServerConfigurationBean extends AbstractServerConfigurationBean imp filesysConfig.addFileStateCache( diskCtx.getDeviceName(), standaloneCache); diskCtx.setStateCache( standaloneCache); } - - FileStateLockManager lockMgr = new FileStateLockManager(diskCtx.getStateCache()); - diskCtx.setLockManager(lockMgr); - diskCtx.setOpLockManager(lockMgr); + + // Register the state cache with the reaper thread + // has many side effects including initialisation of the cache + filesysConfig.addFileStateCache( diskCtx.getShareName(), diskCtx.getStateCache()); + + FileStateLockManager lockMgr = new FileStateLockManager(diskCtx.getStateCache()); + diskCtx.setLockManager(lockMgr); + diskCtx.setOpLockManager(lockMgr); } catch ( InvalidConfigurationException ex) { @@ -2333,7 +2328,7 @@ public class ServerConfigurationBean extends AbstractServerConfigurationBean imp } - private GenericConfigElement createClusterConfig(String topicName) throws InvalidConfigurationException + private GenericConfigElement createClusterConfig(String topicName) throws InvalidConfigurationException { GenericConfigElement config = new GenericConfigElement("hazelcastStateCache"); GenericConfigElement clusterNameCfg = new GenericConfigElement("clusterName"); diff --git a/source/java/org/alfresco/filesys/repo/ContentContext.java b/source/java/org/alfresco/filesys/repo/ContentContext.java index 6c21052a16..a806a8adc4 100644 --- a/source/java/org/alfresco/filesys/repo/ContentContext.java +++ b/source/java/org/alfresco/filesys/repo/ContentContext.java @@ -101,7 +101,7 @@ public class ContentContext extends AlfrescoContext /** * Class constructor * - *@param filesysName + *@param deviceName * String * @param storeName * String @@ -110,9 +110,9 @@ public class ContentContext extends AlfrescoContext * @param rootNodeRef * NodeRef */ - public ContentContext(String filesysName, String storeName, String rootPath, NodeRef rootNodeRef) + public ContentContext(String deviceName, String storeName, String rootPath, NodeRef rootNodeRef) { - setDeviceName(filesysName); + setDeviceName(deviceName); setStoreName(storeName); setRootPath(rootPath); setRootNodeRef(rootNodeRef); diff --git a/source/java/org/alfresco/filesys/repo/HomeShareMapper.java b/source/java/org/alfresco/filesys/repo/HomeShareMapper.java deleted file mode 100644 index 937eb0c234..0000000000 --- a/source/java/org/alfresco/filesys/repo/HomeShareMapper.java +++ /dev/null @@ -1,420 +0,0 @@ -/* - * 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 . - */ - -package org.alfresco.filesys.repo; - -import java.util.Enumeration; - -import org.alfresco.jlan.server.SrvSession; -import org.alfresco.jlan.server.auth.ClientInfo; -import org.alfresco.jlan.server.auth.InvalidUserException; -import org.alfresco.jlan.server.config.InvalidConfigurationException; -import org.alfresco.jlan.server.config.ServerConfiguration; -import org.alfresco.jlan.server.core.InvalidDeviceInterfaceException; -import org.alfresco.jlan.server.core.ShareMapper; -import org.alfresco.jlan.server.core.ShareType; -import org.alfresco.jlan.server.core.SharedDevice; -import org.alfresco.jlan.server.core.SharedDeviceList; -import org.alfresco.jlan.server.filesys.DiskSharedDevice; -import org.alfresco.jlan.server.filesys.FilesystemsConfigSection; -import org.springframework.extensions.config.ConfigElement; -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.filesys.alfresco.AlfrescoClientInfo; -import org.alfresco.filesys.config.ServerConfigurationBean; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.beans.factory.InitializingBean; - -/** - * Home Share Mapper Class - * - *

Maps disk share lookup requests to the list of shares defined in the server - * configuration and provides a dynamic home share mapped to the users home node. - * - * @author GKSpencer - */ -public class HomeShareMapper implements ShareMapper, InitializingBean -{ - // Logging - - private static final Log logger = LogFactory.getLog("org.alfresco.smb.protocol"); - - // Home folder share name - - public static final String HOME_FOLDER_SHARE = "HOME"; - - // Server configuration and sections - - private ServerConfiguration m_config; - private FilesystemsConfigSection m_filesysConfig; - - // Filesystem driver to be used to create home shares - - private ContentDiskDriver m_driver; - - // Home folder share name - - private String m_homeShareName = HOME_FOLDER_SHARE; - - // Debug enable flag - - private boolean m_debug; - - /** - * Default constructor - */ - public HomeShareMapper() - { - } - - - public void setServerConfiguration(ServerConfiguration config) - { - this.m_config = config; - } - - public void setName(String shareName) - { - m_homeShareName = shareName; - } - - public void setDebug(boolean debug) - { - this.m_debug = debug; - } - - /** - * Initialize the share mapper - * - * @param config ServerConfiguration - * @param params ConfigElement - * @exception InvalidConfigurationException - */ - public void initializeMapper(ServerConfiguration config, ConfigElement params) throws InvalidConfigurationException - { - // Save the server configuration - - setServerConfiguration(config); - - // Check if the home share name has been specified - - String homeName = params.getAttribute("name"); - if ( homeName != null && homeName.length() > 0) - setName(homeName); - - // Check if debug is enabled - - if (params != null && params.getChild("debug") != null) - setDebug(true); - - // Complete initialization - afterPropertiesSet(); - } - - - public void afterPropertiesSet() - { - // Save the server configuration - m_filesysConfig = (FilesystemsConfigSection) m_config.getConfigSection(FilesystemsConfigSection.SectionName); - - // Search for a filesystem that uses the content driver to use the driver when creating the home shares - - SharedDeviceList shares = m_filesysConfig.getShares(); - - if ( shares != null) - { - Enumeration shrEnum = shares.enumerateShares(); - - while ( shrEnum.hasMoreElements() && m_driver == null) - { - try - { - SharedDevice curShare = shrEnum.nextElement(); - if ( curShare.getInterface() instanceof ContentDiskDriver) - m_driver = (ContentDiskDriver) curShare.getInterface(); - } - catch (InvalidDeviceInterfaceException ex) - { - } - } - } - } - - /** - * Check if debug output is enabled - * - * @return boolean - */ - public final boolean hasDebug() - { - return m_debug; - } - - /** - * Return the home folder share name - * - * @return String - */ - public final String getHomeFolderName() - { - return m_homeShareName; - } - - /** - * Return the list of available shares. - * - * @param host String - * @param sess SrvSession - * @param allShares boolean - * @return SharedDeviceList - */ - public SharedDeviceList getShareList(String host, SrvSession sess, boolean allShares) - { - // Check if the user has a home folder, and the session does not currently have any - // dynamic shares defined - - if ( sess != null && sess.hasClientInformation() && sess.hasDynamicShares() == false && - sess.getClientInformation() instanceof AlfrescoClientInfo) - { - AlfrescoClientInfo alfClient = (AlfrescoClientInfo) sess.getClientInformation(); - if ( alfClient.hasHomeFolder()) - { - // Create the home folder share - - DiskSharedDevice homeShare = createHomeDiskShare( alfClient); - sess.addDynamicShare(homeShare); - - // Debug - - if ( logger.isDebugEnabled()) - logger.debug("Added " + getHomeFolderName() + " share to list of shares for " + alfClient.getUserName()); - } - } - - // Make a copy of the global share list and add the per session dynamic shares - - SharedDeviceList shrList = new SharedDeviceList(m_filesysConfig.getShares()); - - if ( sess != null && sess.hasDynamicShares()) { - - // Add the per session dynamic shares - - shrList.addShares(sess.getDynamicShareList()); - } - - // Remove unavailable shares from the list and return the list - - if ( allShares == false) - shrList.removeUnavailableShares(); - return shrList; - } - - /** - * Find a share using the name and type for the specified client. - * - * @param host String - * @param name String - * @param typ int - * @param sess SrvSession - * @param create boolean - * @return SharedDevice - * @exception InvalidUserException - */ - public SharedDevice findShare(String tohost, String name, int typ, SrvSession sess, boolean create) - throws Exception - { - - // Check for the special HOME disk share - - SharedDevice share = null; - - if (( typ == ShareType.DISK || typ == ShareType.UNKNOWN) && name.equalsIgnoreCase(getHomeFolderName()) && - sess.getClientInformation() != null && m_driver != null) { - - // Get the client details - - ClientInfo client = sess.getClientInformation(); - - // DEBUG - - if ( logger.isDebugEnabled()) - logger.debug("Map share " + name + ", type=" + ShareType.TypeAsString(typ) + ", client=" + client); - - // Check if the user has a home folder node - - if ( client != null && client instanceof AlfrescoClientInfo) { - - // Access the extended client information - - AlfrescoClientInfo alfClient = (AlfrescoClientInfo) client; - - if ( alfClient.hasHomeFolder()) { - - // Check if the share has already been created for the session - - if ( sess.hasDynamicShares()) { - - // Check if the required share exists in the sessions dynamic share list - - share = sess.getDynamicShareList().findShare(name, typ, false); - - // DEBUG - - if ( logger.isDebugEnabled()) - logger.debug(" Reusing existing dynamic share for " + name); - } - - // Check if we found a share, if not then create a new dynamic share for the home directory - - if ( share == null && create == true) { - - // Create the home share mapped to the users home folder - - DiskSharedDevice diskShare = createHomeDiskShare( alfClient); - - // Add the new share to the sessions dynamic share list - - sess.addDynamicShare(diskShare); - share = diskShare; - - // DEBUG - - if (logger.isDebugEnabled()) - logger.debug(" Mapped share " + name + " to " + alfClient.getHomeFolder()); - } - } - else - throw new InvalidUserException("No home directory"); - } - } - else { - - // Find the required share by name/type. Use a case sensitive search first, if that fails use a case - // insensitive search. - - share = m_filesysConfig.getShares().findShare(name, typ, false); - - if ( share == null) { - - // Try a case insensitive search for the required share - - share = m_filesysConfig.getShares().findShare(name, typ, true); - } - } - - // Check if the share is available - - if ( share != null && share.getContext() != null && share.getContext().isAvailable() == false) - share = null; - - // Return the shared device, or null if no matching device was found - - return share; - } - - /** - * Delete temporary shares for the specified session - * - * @param sess SrvSession - */ - public void deleteShares(SrvSession sess) - { - - // Check if the session has any dynamic shares - - if ( sess.hasDynamicShares() == false) - return; - - // Delete the dynamic shares - - SharedDeviceList shares = sess.getDynamicShareList(); - Enumeration enm = shares.enumerateShares(); - - while ( enm.hasMoreElements()) { - - // Get the current share from the list - - SharedDevice shr = enm.nextElement(); - - // Close the shared device - - shr.getContext().CloseContext(); - - // DEBUG - - if (logger.isDebugEnabled()) - logger.debug("Deleted dynamic share " + shr); - } - - // Clear the dynamic share list - - shares.removeAllShares(); - } - - /** - * Close the share mapper, release any resources. - */ - public void closeMapper() - { - // TODO Auto-generated method stub - - } - - /** - * Create a disk share for the home folder - * - * @param alfClient AlfrescoClientInfo - * @return DiskSharedDevice - */ - private final DiskSharedDevice createHomeDiskShare(AlfrescoClientInfo alfClient) - { - // Make sure the client is an Alfresco client - - if ( alfClient != null) { - - // Create the disk driver and context - - ContentContext diskCtx = new ContentContext( getHomeFolderName(), "", "", alfClient.getHomeFolder()); - - if(m_config instanceof ServerConfigurationBean) - { - ServerConfigurationBean config = (ServerConfigurationBean)m_config; - - config.initialiseRuntimeContext(diskCtx); - - // Enable file state caching - // diskCtx.enableStateCache(serverConfigurationBean, true); - } - else - { - throw new AlfrescoRuntimeException("configuration error, unknown configuration bean"); - } - - - // Create a temporary shared device for the users home directory - - return new DiskSharedDevice(getHomeFolderName(), m_driver, diskCtx, SharedDevice.Temporary); - } - - // Invalid client type - - return null; - } - -} diff --git a/source/java/org/alfresco/repo/action/ActionDefinitionImpl.java b/source/java/org/alfresco/repo/action/ActionDefinitionImpl.java index a6092ac81d..e63f9cafc3 100644 --- a/source/java/org/alfresco/repo/action/ActionDefinitionImpl.java +++ b/source/java/org/alfresco/repo/action/ActionDefinitionImpl.java @@ -18,7 +18,7 @@ */ package org.alfresco.repo.action; -import java.util.List; +import java.util.Set; import org.alfresco.service.cmr.action.ActionDefinition; import org.alfresco.service.namespace.QName; @@ -33,7 +33,7 @@ public class ActionDefinitionImpl extends ParameterizedItemDefinitionImpl implem private static final long serialVersionUID = 4048797883396863026L; private String ruleActionExecutor; - private List applicableTypes; + private Set applicableTypes; private boolean trackStatus; /** @@ -68,11 +68,11 @@ public class ActionDefinitionImpl extends ParameterizedItemDefinitionImpl implem } /** - * Gets the list of applicable types + * Gets the set of applicable types * - * @return the list of qnames + * @return the set of qnames */ - public List getApplicableTypes() + public Set getApplicableTypes() { return this.applicableTypes; } @@ -82,7 +82,7 @@ public class ActionDefinitionImpl extends ParameterizedItemDefinitionImpl implem * * @param applicableTypes the applicable types */ - public void setApplicableTypes(List applicableTypes) + public void setApplicableTypes(Set applicableTypes) { this.applicableTypes = applicableTypes; } diff --git a/source/java/org/alfresco/repo/action/ActionServiceImpl.java b/source/java/org/alfresco/repo/action/ActionServiceImpl.java index ddad5a45d0..0c5c3350c9 100644 --- a/source/java/org/alfresco/repo/action/ActionServiceImpl.java +++ b/source/java/org/alfresco/repo/action/ActionServiceImpl.java @@ -284,15 +284,24 @@ public class ActionServiceImpl implements ActionService, RuntimeActionService, A List result = new ArrayList(); for (ActionDefinition actionDefinition : getActionDefinitions()) { - List appliciableTypes = actionDefinition.getApplicableTypes(); - if (appliciableTypes != null && appliciableTypes.isEmpty() == false) + Set applicableTypes = actionDefinition.getApplicableTypes(); + if (applicableTypes != null && applicableTypes.isEmpty() == false) { - for (QName applicableType : actionDefinition.getApplicableTypes()) + // First do a short-cut check directly against the type + if (applicableTypes.contains(nodeType)) { - if (this.dictionaryService.isSubClass(nodeType, applicableType)) + result.add(actionDefinition); + } + else + { + // Have to do a check for all subtypes of the applicable types + for (QName applicableType : actionDefinition.getApplicableTypes()) { - result.add(actionDefinition); - break; + if (this.dictionaryService.isSubClass(nodeType, applicableType)) + { + result.add(actionDefinition); + break; + } } } } diff --git a/source/java/org/alfresco/repo/action/executer/ActionExecuterAbstractBase.java b/source/java/org/alfresco/repo/action/executer/ActionExecuterAbstractBase.java index 5b2dab026f..9602f59c23 100644 --- a/source/java/org/alfresco/repo/action/executer/ActionExecuterAbstractBase.java +++ b/source/java/org/alfresco/repo/action/executer/ActionExecuterAbstractBase.java @@ -18,13 +18,14 @@ */ package org.alfresco.repo.action.executer; -import java.util.ArrayList; -import java.util.List; +import java.util.HashSet; +import java.util.Set; import org.alfresco.repo.action.ActionDefinitionImpl; import org.alfresco.repo.action.ParameterizedItemAbstractBase; import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.ActionDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.lock.LockService; import org.alfresco.service.cmr.lock.LockStatus; import org.alfresco.service.cmr.repository.NodeRef; @@ -45,6 +46,7 @@ public abstract class ActionExecuterAbstractBase extends ParameterizedItemAbstra protected ActionDefinition actionDefinition; private LockService lockService; private NodeService baseNodeService; + private DictionaryService dictionaryService; /** Indicate if the action status should be tracked or not (default false) */ private boolean trackStatus = false; @@ -53,7 +55,7 @@ public abstract class ActionExecuterAbstractBase extends ParameterizedItemAbstra protected boolean publicAction = true; /** List of types and aspects for which this action is applicable */ - protected List applicableTypes = new ArrayList(); + protected Set applicableTypes = new HashSet(); /** Default queue name */ private String queueName = ""; @@ -82,6 +84,16 @@ public abstract class ActionExecuterAbstractBase extends ParameterizedItemAbstra this.baseNodeService = nodeService; } + /** + * Set the dictionary service + * + * @param dictionaryService the dictionary service + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + /** * Set whether the action is public or not. * @@ -122,7 +134,7 @@ public abstract class ActionExecuterAbstractBase extends ParameterizedItemAbstra /** * Set the list of types for which this action is applicable * - * @param applicableTypes arry of applicable types + * @param applicableTypes array of applicable types */ public void setApplicableTypes(String[] applicableTypes) { @@ -149,6 +161,40 @@ public abstract class ActionExecuterAbstractBase extends ParameterizedItemAbstra this.ignoreLock = ignoreLock; } + /** + * Check if a node is a type or subtype of the of one of the applicable types + * + * @param actionedUponNodeRef the node to check + * @return Returns true if the node is in the list of + * {@link #setApplicableTypes(String[]) applicable types} or one of the + * subtypes + */ + protected boolean isApplicableType(NodeRef actionedUponNodeRef) + { + if (this.baseNodeService.exists(actionedUponNodeRef) == true) + { + QName nodeType = baseNodeService.getType(actionedUponNodeRef); + // Quick check in the set + if (applicableTypes.contains(nodeType)) + { + return true; + } + else + { + // Have to do a long-winded check + for (QName type : applicableTypes) + { + if (this.dictionaryService.isSubClass(nodeType, type)) + { + return true; + } + // Not a subtype; keep checking + } + } + } + return false; + } + /** * Get rule action definition * diff --git a/source/java/org/alfresco/repo/action/executer/CheckOutActionExecuter.java b/source/java/org/alfresco/repo/action/executer/CheckOutActionExecuter.java index 703191ef98..a19c1df89b 100644 --- a/source/java/org/alfresco/repo/action/executer/CheckOutActionExecuter.java +++ b/source/java/org/alfresco/repo/action/executer/CheckOutActionExecuter.java @@ -89,7 +89,8 @@ public class CheckOutActionExecuter extends ActionExecuterAbstractBase public void executeImpl(Action ruleAction, NodeRef actionedUponNodeRef) { if (this.nodeService.exists(actionedUponNodeRef) == true && - this.nodeService.hasAspect(actionedUponNodeRef, ContentModel.ASPECT_WORKING_COPY) == false) + this.nodeService.hasAspect(actionedUponNodeRef, ContentModel.ASPECT_WORKING_COPY) == false && + isApplicableType(actionedUponNodeRef) == true) { // Get the destination details NodeRef destinationParent = (NodeRef)ruleAction.getParameterValue(PARAM_DESTINATION_FOLDER); diff --git a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java index ea18932679..a0525ee504 100644 --- a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java @@ -1956,7 +1956,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO @Override public int purgeNodes(long maxTxnCommitTimeMs) { - return deleteNodesByCommitTime(true, maxTxnCommitTimeMs); + return deleteNodesByCommitTime(maxTxnCommitTimeMs); } /* @@ -4616,7 +4616,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO Long optionalOldSharedAlcIdInAdditionToNull, Long newSharedAlcId); protected abstract int deleteNodeById(Long nodeId); - protected abstract int deleteNodesByCommitTime(boolean deletedOnly, long maxTxnCommitTimeMs); + protected abstract int deleteNodesByCommitTime(long maxTxnCommitTimeMs); protected abstract NodeEntity selectNodeById(Long id); protected abstract NodeEntity selectNodeByNodeRef(NodeRef nodeRef); protected abstract List selectNodesByUuids(Long storeId, SortedSet uuids); diff --git a/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java b/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java index 035314d927..0b2a31ca21 100644 --- a/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java @@ -89,6 +89,7 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl private static final String UPDATE_NODE_BULK_TOUCH = "alfresco.node.update_NodeBulkTouch"; private static final String DELETE_NODE_BY_ID = "alfresco.node.delete_NodeById"; private static final String DELETE_NODES_BY_TXN_COMMIT_TIME = "alfresco.node.delete_NodesByTxnCommitTime"; + private static final String DELETE_NODE_PROPS_BY_TXN_COMMIT_TIME = "alfresco.node.delete_NodePropsByTxnCommitTime"; private static final String SELECT_NODE_BY_ID = "alfresco.node.select_NodeById"; private static final String SELECT_NODE_BY_NODEREF = "alfresco.node.select_NodeByNodeRef"; private static final String SELECT_NODES_BY_UUIDS = "alfresco.node.select_NodesByUuids"; @@ -148,8 +149,8 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl private static final String SELECT_TXN_MAX_ID = "alfresco.node.select_TxnMaxId"; private static final String SELECT_TXN_UNUSED_MIN_COMMIT_TIME = "alfresco.node.select_TxnMinUnusedCommitTime"; - private QNameDAO qnameDAO; - private DictionaryService dictionaryService; + protected QNameDAO qnameDAO; + protected DictionaryService dictionaryService; private SqlSessionTemplate template; @@ -361,7 +362,7 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl } @Override - protected int deleteNodesByCommitTime(boolean deletedOnly, long maxTxnCommitTimeMs) + protected int deleteNodesByCommitTime(long maxTxnCommitTimeMs) { // Get the deleted nodes Pair deletedTypePair = qnameDAO.getQName(ContentModel.TYPE_DELETED); @@ -373,6 +374,10 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl TransactionQueryEntity query = new TransactionQueryEntity(); query.setTypeQNameId(deletedTypePair.getFirst()); query.setMaxCommitTime(maxTxnCommitTimeMs); + // TODO: Fix ALF-16030 Use ON DELETE CASCADE for node aspects and properties + // First clean up properties + template.delete(DELETE_NODE_PROPS_BY_TXN_COMMIT_TIME, query); + // Finally remove the nodes return template.delete(DELETE_NODES_BY_TXN_COMMIT_TIME, query); } @@ -1651,6 +1656,7 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl */ public static class MySQL extends NodeDAOImpl { + private static final String DELETE_NODE_PROPS_BY_TXN_COMMIT_TIME_MYSQL = "alfresco.node.delete_NodePropsByTxnCommitTime_MySQL"; private static final String DELETE_TXNS_UNUSED_MYSQL = "alfresco.node.delete_Txns_Unused_MySQL"; private SqlSessionTemplate template; @@ -1661,6 +1667,26 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl this.template = sqlSessionTemplate; } + @Override + protected int deleteNodesByCommitTime(long maxTxnCommitTimeMs) + { + // Get the deleted nodes + Pair deletedTypePair = qnameDAO.getQName(ContentModel.TYPE_DELETED); + if (deletedTypePair == null) + { + // Nothing to do + return 0; + } + TransactionQueryEntity query = new TransactionQueryEntity(); + query.setTypeQNameId(deletedTypePair.getFirst()); + query.setMaxCommitTime(maxTxnCommitTimeMs); + // TODO: Fix ALF-16030 Use ON DELETE CASCADE for node aspects and properties + // First clean up properties + template.delete(DELETE_NODE_PROPS_BY_TXN_COMMIT_TIME_MYSQL, query); + // Finally remove the nodes + return template.delete(DELETE_NODES_BY_TXN_COMMIT_TIME, query); + } + @Override public int deleteTxnsUnused(long fromCommitTime, long toCommitTime) { diff --git a/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java b/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java index d7b75f7627..03027e4678 100644 --- a/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java +++ b/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java @@ -132,6 +132,7 @@ public class SchemaBootstrap extends AbstractLifecycleBean private static final String PROPERTY_DEFAULT_BATCH_SIZE = "system.upgrade.default.batchsize"; private static final String MSG_DIALECT_USED = "schema.update.msg.dialect_used"; + private static final String MSG_DATABASE_USED = "schema.update.msg.database_used"; private static final String MSG_BYPASSING_SCHEMA_UPDATE = "schema.update.msg.bypassing"; private static final String MSG_NORMALIZED_SCHEMA = "schema.update.msg.normalized_schema"; private static final String MSG_NORMALIZED_SCHEMA_PRE = "schema.update.msg.normalized_schema_pre"; @@ -1505,6 +1506,7 @@ public class SchemaBootstrap extends AbstractLifecycleBean // make sure that we AUTO-COMMIT connection = dataSource.getConnection(); connection.setAutoCommit(true); + LogUtil.info(logger, MSG_DATABASE_USED, connection); Configuration cfg = localSessionFactory.getConfiguration(); diff --git a/source/java/org/alfresco/repo/model/filefolder/FilenameFilteringInterceptor.java b/source/java/org/alfresco/repo/model/filefolder/FilenameFilteringInterceptor.java index a079090bcf..a2ccebb6b3 100644 --- a/source/java/org/alfresco/repo/model/filefolder/FilenameFilteringInterceptor.java +++ b/source/java/org/alfresco/repo/model/filefolder/FilenameFilteringInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2011 Alfresco Software Limited. + * Copyright (C) 2005-2012 Alfresco Software Limited. * * This file is part of Alfresco * @@ -18,12 +18,15 @@ package org.alfresco.repo.model.filefolder; import java.util.Iterator; +import java.util.regex.Pattern; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.repo.model.filefolder.HiddenAspect.Visibility; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.Path; @@ -48,8 +51,16 @@ public class FilenameFilteringInterceptor implements MethodInterceptor { private static Log logger = LogFactory.getLog(FilenameFilteringInterceptor.class); + private static final String XLSX_MIMETYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; + + private static final String MACOS_TEMPORARY_FILE_NAME_PREFIX = "._"; + private static final Pattern XSL_MACOS_TEMPORARY_FILENAME_FITLER = Pattern.compile("^(\\" + MACOS_TEMPORARY_FILE_NAME_PREFIX + ")?[0-9,a-f]{8}$", Pattern.CASE_INSENSITIVE + | Pattern.UNICODE_CASE); + private NodeService nodeService; private PermissionService permissionService; + + private ContentService contentService; private PatternFilter temporaryFiles; private PatternFilter systemPaths; @@ -111,6 +122,16 @@ public class FilenameFilteringInterceptor implements MethodInterceptor this.permissionService = permissionService; } + public void setContentService(ContentService contentService) + { + this.contentService = contentService; + } + + public ContentService getContentService() + { + return contentService; + } + private void checkTemporaryAspect(boolean isTemporary, FileInfo fileInfo) { checkTemporaryAspect(isTemporary, fileInfo.getNodeRef()); @@ -239,7 +260,8 @@ public class FilenameFilteringInterceptor implements MethodInterceptor else { // check whether it's a temporary or hidden file - checkTemporaryAspect(temporaryFiles.isFiltered(filename), (FileInfo)ret); + FileInfo sourceInfo = (FileInfo)ret; + checkTemporaryAspect(isNameOfTmporaryObject(filename, sourceInfo.getNodeRef()), sourceInfo); hiddenAspect.checkHidden(fileInfo, false); } } @@ -250,7 +272,7 @@ public class FilenameFilteringInterceptor implements MethodInterceptor FileInfoImpl fileInfo = (FileInfoImpl)ret; - checkTemporaryAspect(temporaryFiles.isFiltered(filename), fileInfo); + checkTemporaryAspect(isNameOfTmporaryObject(filename, fileInfo.getNodeRef()), fileInfo); } } else if (methodName.startsWith("move")) @@ -263,7 +285,7 @@ public class FilenameFilteringInterceptor implements MethodInterceptor { // Name is changing // check against all the regular expressions - checkTemporaryAspect(temporaryFiles.isFiltered(newName), sourceNodeRef); + checkTemporaryAspect(isNameOfTmporaryObject(newName, sourceNodeRef), sourceNodeRef); } // now do the move @@ -287,7 +309,7 @@ public class FilenameFilteringInterceptor implements MethodInterceptor } // check against all the regular expressions - checkTemporaryAspect(temporaryFiles.isFiltered(filename), fileInfo); + checkTemporaryAspect(isNameOfTmporaryObject(filename, fileInfo.getNodeRef()), fileInfo); if(getMode() == Mode.ENHANCED) { hiddenAspect.checkHidden(fileInfo, true); @@ -315,7 +337,7 @@ public class FilenameFilteringInterceptor implements MethodInterceptor } // check against all the regular expressions - checkTemporaryAspect(temporaryFiles.isFiltered(newName), sourceNodeRef); + checkTemporaryAspect(isNameOfTmporaryObject(newName, sourceNodeRef), sourceNodeRef); ret = invocation.proceed(); @@ -347,5 +369,36 @@ public class FilenameFilteringInterceptor implements MethodInterceptor // done return ret; } + + /** + * Determines whether specified name matches any pattern of temporary file names. Also it checks special case of new XLS document creation in MacOS. See ALF-14078 (comment added on 04-September-12 04:11 PM) for more details + * + * @param name - {@link String} value which contains name of node + * @param nodeRef - {@link NodeRef} instance of the node + * @return {@link Boolean} value. true if name is name of temporary object including special case of XLSX in MacOS. false in other case + */ + private boolean isNameOfTmporaryObject(String name, NodeRef nodeRef) + { + boolean result = temporaryFiles.isFiltered(name); + + if (!result) + { + // This pattern must be validated in conjunction with mimetype validation only! + result = XSL_MACOS_TEMPORARY_FILENAME_FITLER.matcher(name).matches(); + + if (result && !name.startsWith(MACOS_TEMPORARY_FILE_NAME_PREFIX)) + { + ContentReader contentReader = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT); + + if (null != contentReader) + { + result = XLSX_MIMETYPE.equals(contentReader.getMimetype()); + } + } + } + + return result; + } } diff --git a/source/java/org/alfresco/repo/node/cleanup/TransactionCleanupTest.java b/source/java/org/alfresco/repo/node/cleanup/TransactionCleanupTest.java index ccef92cfb3..e31a984653 100644 --- a/source/java/org/alfresco/repo/node/cleanup/TransactionCleanupTest.java +++ b/source/java/org/alfresco/repo/node/cleanup/TransactionCleanupTest.java @@ -1,14 +1,18 @@ package org.alfresco.repo.node.cleanup; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; +import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.alfresco.model.ContentModel; +import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.domain.node.NodeDAO; import org.alfresco.repo.domain.node.Transaction; import org.alfresco.repo.node.db.DeletedNodeCleanupWorker; @@ -25,14 +29,22 @@ 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.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.extensions.webscripts.GUID; +/** + * @author Derek Hulley + * @since 4.0 + */ public class TransactionCleanupTest { + private static Log logger = LogFactory.getLog(TransactionCleanupTest.class); + private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); private TransactionService transactionService; @@ -40,6 +52,7 @@ public class TransactionCleanupTest private SearchService searchService; private MutableAuthenticationService authenticationService; private NodeDAO nodeDAO; + private SimpleCache nodesCache; private DeletedNodeCleanupWorker worker; private NodeRef nodeRef1; @@ -49,6 +62,7 @@ public class TransactionCleanupTest private NodeRef nodeRef5; private RetryingTransactionHelper helper; + @SuppressWarnings("unchecked") @Before public void before() { @@ -59,12 +73,13 @@ public class TransactionCleanupTest this.nodeService = serviceRegistry.getNodeService(); this.searchService = serviceRegistry.getSearchService(); this.nodeDAO = (NodeDAO)ctx.getBean("nodeDAO"); + this.nodesCache = (SimpleCache) ctx.getBean("node.nodesSharedCache"); this.worker = (DeletedNodeCleanupWorker)ctx.getBean("nodeCleanup.deletedNodeCleanup"); this.worker.setMinPurgeAgeDays(0); this.helper = transactionService.getRetryingTransactionHelper(); authenticationService.authenticate("admin", "admin".toCharArray()); - + StoreRef storeRef = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore"); NodeRef storeRoot = nodeService.getRootNode(storeRef); List nodeRefs = searchService.selectNodes( @@ -93,8 +108,8 @@ public class TransactionCleanupTest UpdateNode updateNode1 = new UpdateNode(nodeRef1); UpdateNode updateNode2 = new UpdateNode(nodeRef2); UpdateNode updateNode3 = new UpdateNode(nodeRef3); - UpdateNode updateNode4 = new UpdateNode(nodeRef4); - UpdateNode updateNode5 = new UpdateNode(nodeRef5); + DeleteNode deleteNode4 = new DeleteNode(nodeRef4); + DeleteNode deleteNode5 = new DeleteNode(nodeRef5); List txnIds1 = new ArrayList(); List txnIds2 = new ArrayList(); @@ -116,22 +131,16 @@ public class TransactionCleanupTest String txnId2 = helper.doInTransaction(updateNode2, false, true); txnIds2.add(txnId2); } - if(i == 2) + if(i == 1) { String txnId3 = helper.doInTransaction(updateNode3, false, true); txnIds3.add(txnId3); } - if(i == 3) - { - String txnId4 = helper.doInTransaction(updateNode4, false, true); - txnIds4.add(txnId4); - } - if(i == 4) - { - String txnId5 = helper.doInTransaction(updateNode5, false, true); - txnIds5.add(txnId5); - } } + String txnId4 = helper.doInTransaction(deleteNode4, false, true); + txnIds4.add(txnId4); + String txnId5 = helper.doInTransaction(deleteNode5, false, true); + txnIds5.add(txnId5); return txnIds; } @@ -165,12 +174,21 @@ public class TransactionCleanupTest final List txnIds1 = txnIds.get(nodeRef1); final List txnIds2 = txnIds.get(nodeRef2); final List txnIds3 = txnIds.get(nodeRef3); - final List txnIds4 = txnIds.get(nodeRef4); - final List txnIds5 = txnIds.get(nodeRef5); + // Pure delete: final List txnIds4 = txnIds.get(nodeRef4); + // Pure delete: final List txnIds5 = txnIds.get(nodeRef5); + + // Double-check that n4 and n5 are present in deleted form + nodesCache.clear(); + assertNotNull("Node 4 is deleted but not purged", nodeDAO.getNodeRefStatus(nodeRef4)); + assertNotNull("Node 5 is deleted but not purged", nodeDAO.getNodeRefStatus(nodeRef5)); // run the transaction cleaner worker.setPurgeSize(5); // small purge size - worker.doClean(); + List reports = worker.doClean(); + for (String report : reports) + { + logger.debug(report); + } // Get transactions committed after the test started List txns = nodeDAO.getTxnsByCommitTimeAscending(Long.valueOf(start), Long.valueOf(Long.MAX_VALUE), Integer.MAX_VALUE, null, false); @@ -182,8 +200,7 @@ public class TransactionCleanupTest expectedUsedTxnIds.add(txnIds1.get(txnIds1.size() - 1)); expectedUsedTxnIds.addAll(txnIds2); expectedUsedTxnIds.addAll(txnIds3); - expectedUsedTxnIds.addAll(txnIds4); - expectedUsedTxnIds.addAll(txnIds5); + // 4 and 5 should not be in the list because they are deletes // check that the correct transactions have been purged i.e. all except the last one to update the node // i.e. in this case, all but the last one in txnIds1 @@ -211,10 +228,15 @@ public class TransactionCleanupTest } } - assertEquals(5, numFoundUsedTxnIds); + assertEquals(3, numFoundUsedTxnIds); List txnsUnused = nodeDAO.getTxnsUnused(minTxnId, Long.MAX_VALUE, Integer.MAX_VALUE); assertEquals(0, txnsUnused.size()); + + // Double-check that n4 and n5 were removed as well + nodesCache.clear(); + assertNull("Node 4 was not cleaned up", nodeDAO.getNodeRefStatus(nodeRef4)); + assertNull("Node 5 was not cleaned up", nodeDAO.getNodeRefStatus(nodeRef5)); } @After @@ -241,4 +263,24 @@ public class TransactionCleanupTest return txnId; } }; + + private class DeleteNode implements RetryingTransactionCallback + { + private NodeRef nodeRef; + + DeleteNode(NodeRef nodeRef) + { + this.nodeRef = nodeRef; + } + + @Override + public String execute() throws Throwable + { + nodeService.addAspect(nodeRef, ContentModel.ASPECT_TEMPORARY, null); + nodeService.deleteNode(nodeRef); + String txnId = AlfrescoTransactionSupport.getTransactionId(); + + return txnId; + } + }; } diff --git a/source/java/org/alfresco/repo/solr/SOLRTrackingComponentImpl.java b/source/java/org/alfresco/repo/solr/SOLRTrackingComponentImpl.java index 1e8a6bfb63..78702b488c 100644 --- a/source/java/org/alfresco/repo/solr/SOLRTrackingComponentImpl.java +++ b/source/java/org/alfresco/repo/solr/SOLRTrackingComponentImpl.java @@ -451,9 +451,12 @@ public class SOLRTrackingComponentImpl implements SOLRTrackingComponent try { Pair pair = nodeDAO.getNodePair(catRef); - for (Path path : nodeDAO.getPaths(pair, false)) + if(pair != null) { - aspectPaths.add(new Pair(path, aspDef.getName())); + for (Path path : nodeDAO.getPaths(pair, false)) + { + aspectPaths.add(new Pair(path, aspDef.getName())); + } } } catch (InvalidNodeRefException e) diff --git a/source/java/org/alfresco/repo/template/VersionHistoryNode.java b/source/java/org/alfresco/repo/template/VersionHistoryNode.java index cc3af286d1..840b8591ff 100644 --- a/source/java/org/alfresco/repo/template/VersionHistoryNode.java +++ b/source/java/org/alfresco/repo/template/VersionHistoryNode.java @@ -110,6 +110,16 @@ public class VersionHistoryNode extends BaseContentNode implements NamespacePref return (String)this.getProperties().get(ContentModel.PROP_NAME); } + /** + * Helper method to get the item title. + * + * @return the item name + */ + public String getTitle() + { + return (String)this.getProperties().get(ContentModel.PROP_TITLE); + } + /** * Helper method to get the created date from the version property data. * diff --git a/source/java/org/alfresco/repo/transfer/RepoTransferReceiverImplTest.java b/source/java/org/alfresco/repo/transfer/RepoTransferReceiverImplTest.java index cff694086d..91ecab05d3 100644 --- a/source/java/org/alfresco/repo/transfer/RepoTransferReceiverImplTest.java +++ b/source/java/org/alfresco/repo/transfer/RepoTransferReceiverImplTest.java @@ -517,11 +517,14 @@ public class RepoTransferReceiverImplTest extends BaseAlfrescoSpringTest { receiver.saveContent(transferId, node.getUuid(), new ByteArrayInputStream(dummyContentBytes)); } + log.info("testMoreComplexCommit - commit"); receiver.commit(transferId); + log.info("testMoreComplexCommit - commited"); } finally { + log.info("testMoreComplexCommit - end"); receiver.end(transferId); endTransaction(); } @@ -529,6 +532,7 @@ public class RepoTransferReceiverImplTest extends BaseAlfrescoSpringTest startNewTransaction(); try { + log.info("testMoreComplexCommit - validate nodes"); assertTrue(nodeService.getAspects(node1.getNodeRef()).contains(ContentModel.ASPECT_ATTACHABLE)); assertFalse(nodeService.getSourceAssocs(node2.getNodeRef(), ContentModel.ASSOC_ATTACHMENTS).isEmpty()); for (TransferManifestNode node : nodes) diff --git a/source/java/org/alfresco/repo/usage/UserUsageTrackingComponent.java b/source/java/org/alfresco/repo/usage/UserUsageTrackingComponent.java index 3d9e87505e..3a0769ca0c 100644 --- a/source/java/org/alfresco/repo/usage/UserUsageTrackingComponent.java +++ b/source/java/org/alfresco/repo/usage/UserUsageTrackingComponent.java @@ -313,7 +313,7 @@ public class UserUsageTrackingComponent extends AbstractLifecycleBean implements users.put(username, new NodeRef(personStoreRef, uuid)); } }; - usageDAO.getUsersWithUsage(personStoreRef, userHandler); + usageDAO.getUsersWithUsage(tenantService.getName(personStoreRef), userHandler); return null; } diff --git a/source/java/org/alfresco/repo/wiki/WikiServiceImpl.java b/source/java/org/alfresco/repo/wiki/WikiServiceImpl.java index 65f1ae43a3..0f8065f190 100644 --- a/source/java/org/alfresco/repo/wiki/WikiServiceImpl.java +++ b/source/java/org/alfresco/repo/wiki/WikiServiceImpl.java @@ -142,6 +142,12 @@ public class WikiServiceImpl implements WikiService { // The name is based on the title, but with underscores String name = title.replace(' ', '_'); + name = name.replaceAll("\"", "%22"); + name = name.replaceAll("[*]", "%2a"); + name = name.replaceAll("<", "%3c"); + name = name.replaceAll(">", "%3e"); + name = name.replaceAll(":", "%3a"); + name = name.replaceAll("([.]?[.]+$)", "%2e"); return name; } @@ -182,7 +188,7 @@ public class WikiServiceImpl implements WikiService @Override - public WikiPageInfo getWikiPage(String siteShortName, String pageName) + public WikiPageInfo getWikiPage(String siteShortName, String pageTitle) { NodeRef container = getSiteWikiContainer(siteShortName, false); if (container == null) @@ -191,6 +197,7 @@ public class WikiServiceImpl implements WikiService return null; } + String pageName = buildName(pageTitle); NodeRef link = nodeService.getChildByName(container, ContentModel.ASSOC_CONTAINS, pageName); if (link != null) { diff --git a/source/java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowEngine.java b/source/java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowEngine.java index 52fd37f508..eaa4d12cd2 100644 --- a/source/java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowEngine.java +++ b/source/java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowEngine.java @@ -179,9 +179,12 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine private static final String ERR_END_UNEXISTING_TASK = "activiti.engine.end.task.unexisting.error"; private static final String ERR_GET_TASK_BY_ID = "activiti.engine.get.task.by.id.error"; private static final String ERR_END_TASK_INVALID_TRANSITION = "activiti.engine.end.task.invalid.transition"; - + public static final QName QNAME_INITIATOR = QName.createQName(NamespaceService.DEFAULT_URI, WorkflowConstants.PROP_INITIATOR); + private final static String WORKFLOW_TOKEN_SEPERATOR = "\\$"; + + private RepositoryService repoService; private RuntimeService runtimeService; private TaskService taskService; @@ -2285,7 +2288,27 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine if (workflowInstanceQuery.getCustomProps() != null) { - for (Map.Entry prop : workflowInstanceQuery.getCustomProps().entrySet()) + Map customProps = workflowInstanceQuery.getCustomProps(); + + // CLOUD-667: Extract initiator-property and use 'startedBy' instead + Object initiatorObject = customProps.get(QNAME_INITIATOR); + if(initiatorObject != null && initiatorObject instanceof NodeRef) + { + // Extract username from person-node + NodeRef initiator = (NodeRef) initiatorObject; + if(this.nodeService.exists(initiator)) + { + String initiatorUserName = (String) nodeService.getProperty(initiator, ContentModel.PROP_USERNAME); + query.startedBy(initiatorUserName); + + // Clone properties map and remove initiator + customProps = new HashMap(); + customProps.putAll(workflowInstanceQuery.getCustomProps()); + customProps.remove(QNAME_INITIATOR); + } + } + + for (Map.Entry prop : customProps.entrySet()) { String propertyName = factory.mapQNameToName(prop.getKey()); if (prop.getValue() == null) diff --git a/source/java/org/alfresco/repo/workflow/jbpm/JBPMScheduler.java b/source/java/org/alfresco/repo/workflow/jbpm/JBPMScheduler.java index 05d48053bf..572f553e7a 100644 --- a/source/java/org/alfresco/repo/workflow/jbpm/JBPMScheduler.java +++ b/source/java/org/alfresco/repo/workflow/jbpm/JBPMScheduler.java @@ -18,9 +18,9 @@ */ package org.alfresco.repo.workflow.jbpm; -import org.springframework.extensions.surf.util.AbstractLifecycleBean; import org.jbpm.job.executor.JobExecutor; import org.springframework.context.ApplicationEvent; +import org.springframework.extensions.surf.util.AbstractLifecycleBean; import org.springmodules.workflow.jbpm31.JbpmTemplate; @@ -35,7 +35,7 @@ public class JBPMScheduler extends AbstractLifecycleBean { private JobExecutor executor = null; private JbpmTemplate jbpmTemplate; - + private boolean JbpmEngineEnabled = false; /** * @param jbpmTemplate @@ -44,18 +44,32 @@ public class JBPMScheduler extends AbstractLifecycleBean { this.jbpmTemplate = jbpmTemplate; } + + /** + * @param jbpmEngineEnabled whether or not the JBPM-Engine is enables. Please note that we are + * not using the WorklfowAdminService since this is only initialized later in the sequence. + */ + public void setJBPMEngineEnabled(boolean jbpmEngineEnabled) { + JbpmEngineEnabled = jbpmEngineEnabled; + } @Override protected void onBootstrap(ApplicationEvent event) { - executor = jbpmTemplate.getJbpmConfiguration().getJobExecutor(); - executor.start(); + if(JbpmEngineEnabled) + { + executor = jbpmTemplate.getJbpmConfiguration().getJobExecutor(); + executor.start(); + } } @Override protected void onShutdown(ApplicationEvent event) { - executor.stop(); + if(JbpmEngineEnabled || executor.isStarted()) + { + executor.stop(); + } } } diff --git a/source/java/org/alfresco/service/cmr/action/ActionDefinition.java b/source/java/org/alfresco/service/cmr/action/ActionDefinition.java index 3daa92060c..d417085754 100644 --- a/source/java/org/alfresco/service/cmr/action/ActionDefinition.java +++ b/source/java/org/alfresco/service/cmr/action/ActionDefinition.java @@ -18,7 +18,7 @@ */ package org.alfresco.service.cmr.action; -import java.util.List; +import java.util.Set; import org.alfresco.service.namespace.QName; @@ -32,9 +32,9 @@ public interface ActionDefinition extends ParameterizedItemDefinition /** * Gets a list of the types that this action item is applicable for * - * @return list of types + * @return set of types never null */ - public List getApplicableTypes(); + public Set getApplicableTypes(); /** * Get whether the basic action definition supports action tracking