From 82b9aba09acd2149761520de9015b18c50125774 Mon Sep 17 00:00:00 2001 From: Dave Ward Date: Mon, 24 Sep 2012 18:03:25 +0000 Subject: [PATCH] Merged V4.1-BUG-FIX to HEAD 41597: ALF-1907: Check out rule is active for spaces - Merged in fix from DEV - Changed List of applicable types to a Set - Added quick initial checks against the set before doing the subtype checks - Various typo fixes 41598: Add an additional line of INFO to log the database details 41599: Fix for ALF-8374 - Simple view: incorrect file type icon for *.page and *.eps files 41600: Merge DEV to V4.1-BUG-FIX 41478 : ALF-14078: CLONE - Saving files with apps on Mac OS X Lion in CIFS doesn't invoke rules 41605: Fix for ALF-14653 - Share - Page link created wrongly in wiki. Solution to split wiki page name and title from Alex Bykov. 41608: CIFS cluster saving of text file fails. ALF-14179 The client releases the oplock by closing the file, if a remote oplock break was used then a notification to the remote node was missing in this case. 41616: ALF-13577:modified the delete site REST API to set the status code to 409 if a node is locked. 41624: MT: ALF-14331 - UserUsageTrackingComponent fails if disabled (in MT config) - defensive fix (exact steps do not repeat) - may also fix ALF-15956 41652: more debug 41653: Fix for ALF-15965 - IE9 - Script error occurs when manage aspect for a document 41655: Merged BRANCHES/V4.1 to BRANCHES/DEV/V4.1-BUG-FIX (RECORD ONLY) 41654: Fix for ALF-15965 - hand merged back from rev 41653 41658: ALF-14967: Task operations through task-forms now audited 41664: Reverse-merging change 41616 out of the product due to file formatting irregularities. This checkin was a fix for ALF-13577. A subsequent check-in will add the same fix but without the formatting problems. 41665: ALF-11452: Disabling JBPM now prevents jobexecutor thread from starting and consuming unneeded resources 41671: Reimplementation of fix for ALF-13577. Slight edit on previous fix. It now lets the NodeLockedException out of the Java layer and 'catches' this exception in the JavaScript layer. The API returns a 409 (conflict) if a NodeLockedException makes it to the JavaScript - the previous fix was returning 409 for *any* AlfrescoRuntimeException thrown during a site delete. Also this checkin preserves the whitespace/file layout of the preceding version. 41688: ALF-15867 WikiServiceImplTest failing on SQL Server - Switched WikiService_transaction bean over to using RetryingTransactionInterceptor 41720: ALF-15967: Using START_USER_ID_ instead of "initiator" property to query process instances started by user X to prevent extra joins 41730: Fix for ALF-15995 NodeRef is missing in log on "Problem converting to Freemarker" error 41743: ALF-9779 REGRESSION: FTP - Failed to retrieve directory listing for tenant user 41745: Removed a HomeShareMapper - there were two 41747: Fix for ALF-15811 SOLR query increases DocBitSet inefficiently Part of ALF-14861 SOLR to scale for non-admin users in 100k sites and a subgroup of each of 1000 independent groupings with 1000 subgroups 41748: Fix for ALF-15950 Solr: throws NPE: org.alfresco.solr.SolrLuceneAnalyser.loadAnalyzer - return default analyzer 41752: Fix for ALF-13634 Re-created category won't show up again on a node in Document Library. 41779: ALF-11283 - Got the home share mapper up and working after some refactoring. 41795: Fix for ALF-14361 CMIS object type definition for Document and Folder not spec compliant 41796: ALF-16008: missing column on clean Alfresco and after upgrade from 3.4.10 (703) - Schema-Reference for DB2 referenced a non-existent column. 41802: Fixed ALF-16029: TransactionCleanupTest throws constraint violations - Fallout from ALF-12358; sys:deleted node types have a mandatory property - Before bulk-deleting nodes, we need to delete properties - Use overriding DAo for MySQL. Other DBs can use standard delete with joins - NB: Fixed TransactionalCleanupTest to double-check the purge job 41822: Refactor to both Share Mappers. 41838: BDE-85: Deploy alfresco-mmt with a proper pom.xml containing dependencies Also, refactor Maven deployment to use a macro 41858: ALF-14444 - CloudSync: Ensure unknown properties when synced to Cloud are ignored properly 41876: Fix ALF-16067 - Items with cm:versionable aspect [custom aspect added] throw errors in Share UI 41877: Updated Spring Surf libs (1148) - downgrades module deployment error messages 41878: ALF-16069 - RU: 'Web Deployed' folder is not localized if Alfresco is installed with Russian locale. 41879: Fix non-continuous build, trying to call continuous-init from maven-env-prerequisites 41918: Merged PATCHES/V4.1.1 to V4.1-BUG-FIX 41657: ALF-15965 - IE9 - Script error occurs when manage aspect for a document (correct 4.1.1 branch this time) 41834: Merged DEV to V4.1.1 (4.1.1) 41827: ALF-15983: SPP: Meeting space: all-day event is displayed on the day before specified during creation Removing of time zone has been modified to be sensitive to Outlook meeting events which are not requiring special conversion for the start and end dates 41835: Merged DEV to V4.1.1 (4.1.1) 41833: ALF-16038 : DB2: Upgrade script needed to remove ALFUSER.ACT_HI_ACTINST.OWNER_ The patch was created to remove an unnecessary column in DB2. 41845: Reverse merge: << In attempt to clear 12 new DB2 build errors - don't think it is going to work >> 41835: Merged DEV to V4.1.1 (4.1.1) 41833: ALF-16038 : DB2: Upgrade script needed to remove ALFUSER.ACT_HI_ACTINST.OWNER_ The patch was created to remove an unnecessary column in DB2. Merged V4.1-BUG-FIX (4.1.2) to V4.1.1 (4.1.1) 41337: Merged V3.4-BUG-FIX (3.4.11) to V4.1-BUG-FIX (4.1.2) 41336: TransformerDebug: Use debug rather than trace when there are transformers but they are all unavailable. 41868: Reverse the reverse merge (back to r41835 state used in build #33 given to QA) 41845: Reverse merge: << In attempt to clear 12 new DB2 build errors - don't think it is going to work >> 41835: Merged DEV to V4.1.1 (4.1.1) 41833: ALF-16038 : DB2: Upgrade script needed to remove ALFUSER.ACT_HI_ACTINST.OWNER_ The patch was created to remove an unnecessary column in DB2. Merged V4.1-BUG-FIX (4.1.2) to V4.1.1 (4.1.1) 41337: Merged V3.4-BUG-FIX (3.4.11) to V4.1-BUG-FIX (4.1.2) 41336: TransformerDebug: Use debug rather than trace when there are transformers but they are all unavailable. 41914: Merged DEV to PATCHES/V4.1.1 41909: ALF-16078 : DB2: it's impossible to upgrade Alfresco from 3.4.10 to 4.1.1 (build 33). The remove-column-activiti.sql was marked as optional. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@41919 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/action-services-context.xml | 3 + config/alfresco/bootstrap-context.xml | 4 + .../remove-column-activiti.sql | 23 + config/alfresco/form-services-context.xml | 2 +- .../node-common-SqlMap.xml | 31 ++ .../messages/bootstrap-spaces.properties | 3 + .../messages/patch-service.properties | 3 +- .../messages/schema-update.properties | 1 + .../model-specific-services-context.xml | 3 +- .../alfresco/patch/patch-services-context.xml | 11 + .../default/file-servers-context.xml | 64 ++- config/alfresco/version.properties | 2 +- config/alfresco/wiki-services-context.xml | 26 +- pom.xml | 5 + .../filesys/alfresco/HomeShareMapper.java | 214 +++++---- .../alfresco/MultiTenantShareMapper.java | 193 ++++---- .../alfresco/filesys/avm/AVMShareMapper.java | 2 +- .../config/ServerConfigurationBean.java | 43 +- .../alfresco/filesys/repo/ContentContext.java | 6 +- .../filesys/repo/HomeShareMapper.java | 420 ------------------ .../repo/action/ActionDefinitionImpl.java | 12 +- .../repo/action/ActionServiceImpl.java | 21 +- .../executer/ActionExecuterAbstractBase.java | 54 ++- .../executer/CheckOutActionExecuter.java | 3 +- .../repo/domain/node/AbstractNodeDAOImpl.java | 4 +- .../repo/domain/node/ibatis/NodeDAOImpl.java | 32 +- .../repo/domain/schema/SchemaBootstrap.java | 2 + .../FilenameFilteringInterceptor.java | 65 ++- .../node/cleanup/TransactionCleanupTest.java | 82 +++- .../repo/solr/SOLRTrackingComponentImpl.java | 7 +- .../repo/template/VersionHistoryNode.java | 10 + .../RepoTransferReceiverImplTest.java | 4 + .../usage/UserUsageTrackingComponent.java | 2 +- .../alfresco/repo/wiki/WikiServiceImpl.java | 9 +- .../activiti/ActivitiWorkflowEngine.java | 27 +- .../repo/workflow/jbpm/JBPMScheduler.java | 24 +- .../service/cmr/action/ActionDefinition.java | 6 +- 37 files changed, 694 insertions(+), 729 deletions(-) create mode 100644 config/alfresco/dbscripts/upgrade/4.1/org.hibernate.dialect.Dialect/remove-column-activiti.sql delete mode 100644 source/java/org/alfresco/filesys/repo/HomeShareMapper.java 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