From cf86bf466c25828e920dc6c95aa14ba5ac020a24 Mon Sep 17 00:00:00 2001 From: Derek Hulley Date: Wed, 11 Mar 2009 13:19:59 +0000 Subject: [PATCH] Merged V3.1 to HEAD 13077: Abstracted ContentStore MBean operations 13099: Merge V3.0 to V3.1 13096 Merged V2.2 to V3.0 13071: Fix ETWOTWO-1058: Hibernate exception while concurrently submitting from and updating same user sandbox. 13079: Fix ETWOTWO-1117: Misleading exceptions reported during AVM flatten and update 13102: [no comment] 13112: Merged V3.0 to V3.1 13111: Merged V2.2 to V3.0 13110: Fix 2.1 -> 2.2 upgrade on Postgres 13114: Build/test fix (Enterprise Remote API project does not yet have any Java files to generate Javadoc) 13117: DM Index Check - unit test improvements 13123: *RECORD ONLY* Removed svn:mergeinfo fluff 13124: Used newer, more efficient NodeService.addProperties method instead of many NodeService.setProperty calls 13125: Added M2Binding for 'child-association': propagateTimestamps' 13126: WCM unit tests - reduce build/test time to check (async) submits 13127: Minor test fix - to allow it to run locally (on Mac OS X) 13130: Support for 'maxRetries' of zero or less 13131: Merged V3.0 to V3.1 13025 *RECORD-ONLY*: Removed unnecessary svn:mergeinfo 13026: Merged V2.2 to V3.0 12964: Fixed ETWOTWO-968: Space rules are not run when saving from MS Word 12993 *RECORD-ONLY*: added openoffice bootstrap context to sample-extensions 13009 *RECORD-ONLY*: Avoid default OOo config from causing problems on zip/gz installs 13132: Updated svn:mergeinfo 13134: ETHREEOH-1202 - initial fix and unit tests ___________________________________________________________________ Modified: svn:mergeinfo Merged /alfresco/BRANCHES/V3.0:r13005,13025-13026,13030,13039,13042,13050,13053,13096,13098,13111 Merged /alfresco/BRANCHES/V2.2:r12964,12993,13009,13071,13079,13110 Merged /alfresco/BRANCHES/V3.1:r13077,13099,13102,13112,13114,13117,13123-13127,13130-13132,13134 git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@13564 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../auth/cifs/AlfrescoCifsAuthenticator.java | 4 +- .../auth/cifs/CifsAuthenticatorBase.java | 23 +- .../cifs/EnterpriseCifsAuthenticator.java | 4 +- .../auth/cifs/PassthruCifsAuthenticator.java | 4 +- .../auth/ftp/FTPAuthenticatorBase.java | 389 +- .../auth/nfs/AlfrescoRpcAuthenticator.java | 946 ++--- .../org/alfresco/filesys/repo/CifsHelper.java | 1380 ++++--- .../admin/patch/impl/AVMPermissionsPatch.java | 51 +- .../admin/patch/impl/DmPermissionsPatch.java | 53 +- .../MoveWCMToGroupBasedPermissionsPatch.java | 162 +- .../admin/patch/impl/WCMPermissionPatch.java | 98 +- .../alfresco/repo/avm/AVMSyncServiceImpl.java | 27 - .../repo/content/AbstractContentStore.java | 16 + .../content/AbstractRoutingContentStore.java | 16 + .../AbstractWritableContentStoreTest.java | 19 + .../alfresco/repo/content/ContentStore.java | 22 + ...ent.java => ContentStoreCreatedEvent.java} | 24 +- .../repo/content/RoutingContentService.java | 77 +- .../content/filestore/FileContentStore.java | 57 +- .../filestore/FileContentStoreTest.java | 19 + ...ntimeExecutableContentTransformerTest.java | 3 +- .../RepositoryDescriptorDAOImpl.java | 24 +- .../repo/dictionary/DictionaryDAOTest.java | 17 + .../repo/dictionary/M2ChildAssociation.java | 10 + .../M2ChildAssociationDefinition.java | 13 +- .../dictionary/dictionarydaotest_model.xml | 14 + .../alfresco/repo/dictionary/m2binding.xml | 1 + .../filefolder/TempFileMarkerInterceptor.java | 15 +- .../RetryingTransactionHelper.java | 988 ++--- .../RetryingTransactionHelperTest.java | 42 + .../workflow/jbpm/AlfrescoJobExecutor.java | 5 +- .../jbpm/AlfrescoJobExecutorThread.java | 56 +- .../repo/workflow/jbpm/AlfrescoTimer.java | 12 +- .../alfresco/repo/workflow/jbpm/jbpm.cfg.xml | 13 +- .../ChildAssociationDefinition.java | 4 + .../service/cmr/repository/ContentData.java | 15 + .../wcm/AbstractWCMServiceImplTest.java | 210 +- .../alfresco/wcm/sandbox/SandboxFactory.java | 47 +- .../alfresco/wcm/sandbox/SandboxService.java | 2 +- .../wcm/sandbox/SandboxServiceImpl.java | 110 +- .../wcm/sandbox/SandboxServiceImplTest.java | 3603 +++++++++-------- .../wcm/webproject/WebProjectService.java | 16 + .../wcm/webproject/WebProjectServiceImpl.java | 33 +- .../webproject/WebProjectServiceImplTest.java | 49 +- .../jbpmresources/test_timers.xml | 14 +- source/test-resources/wcm/jbpm.cfg.xml | 86 +- 46 files changed, 4765 insertions(+), 4028 deletions(-) rename source/java/org/alfresco/repo/content/{filestore/FileContentStoreCreatedEvent.java => ContentStoreCreatedEvent.java} (75%) diff --git a/source/java/org/alfresco/filesys/auth/cifs/AlfrescoCifsAuthenticator.java b/source/java/org/alfresco/filesys/auth/cifs/AlfrescoCifsAuthenticator.java index c04f9c45a5..f512a7c3ca 100644 --- a/source/java/org/alfresco/filesys/auth/cifs/AlfrescoCifsAuthenticator.java +++ b/source/java/org/alfresco/filesys/auth/cifs/AlfrescoCifsAuthenticator.java @@ -166,7 +166,7 @@ public class AlfrescoCifsAuthenticator extends CifsAuthenticatorBase { // Start a transaction - tx = getTransactionService().getUserTransaction( false); + tx = createTransaction(); tx.begin(); // Perform local MD4 password check @@ -177,7 +177,7 @@ public class AlfrescoCifsAuthenticator extends CifsAuthenticatorBase { // Start a transaction - tx = getTransactionService().getUserTransaction( false); + tx = createTransaction(); tx.begin(); // Perform passthru authentication password check diff --git a/source/java/org/alfresco/filesys/auth/cifs/CifsAuthenticatorBase.java b/source/java/org/alfresco/filesys/auth/cifs/CifsAuthenticatorBase.java index 2beead17e2..7175c7aa7e 100644 --- a/source/java/org/alfresco/filesys/auth/cifs/CifsAuthenticatorBase.java +++ b/source/java/org/alfresco/filesys/auth/cifs/CifsAuthenticatorBase.java @@ -369,7 +369,7 @@ public abstract class CifsAuthenticatorBase extends CifsAuthenticator // Check if the user name is an administrator - UserTransaction tx = getTransactionService().getUserTransaction(); + UserTransaction tx = createTransaction(); try { tx.begin(); @@ -400,4 +400,25 @@ public abstract class CifsAuthenticatorBase extends CifsAuthenticator } } } + + /** + * Create a transaction, this will be a wrteable transaction unless the system is in read-only mode. + * + * return UserTransaction + */ + protected final UserTransaction createTransaction() + { + // Get the transaction service + + TransactionService txService = getTransactionService(); + + // DEBUG + + if ( logger.isDebugEnabled()) + logger.debug("Using " + (txService.isReadOnly() ? "ReadOnly" : "Write") + " transaction"); + + // Create the transaction + + return txService.getUserTransaction( txService.isReadOnly() ? true : false); + } } \ No newline at end of file diff --git a/source/java/org/alfresco/filesys/auth/cifs/EnterpriseCifsAuthenticator.java b/source/java/org/alfresco/filesys/auth/cifs/EnterpriseCifsAuthenticator.java index 72c6623956..33a2eca33f 100644 --- a/source/java/org/alfresco/filesys/auth/cifs/EnterpriseCifsAuthenticator.java +++ b/source/java/org/alfresco/filesys/auth/cifs/EnterpriseCifsAuthenticator.java @@ -561,7 +561,7 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement { // Start a transaction - tx = getTransactionService().getUserTransaction( false); + tx = createTransaction(); tx.begin(); // Process the hashed password session setup @@ -718,7 +718,7 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement // Start a transaction - tx = getTransactionService().getUserTransaction( false); + tx = createTransaction(); tx.begin(); // Process the security blob diff --git a/source/java/org/alfresco/filesys/auth/cifs/PassthruCifsAuthenticator.java b/source/java/org/alfresco/filesys/auth/cifs/PassthruCifsAuthenticator.java index 98e369aac6..3a59a7cb4c 100644 --- a/source/java/org/alfresco/filesys/auth/cifs/PassthruCifsAuthenticator.java +++ b/source/java/org/alfresco/filesys/auth/cifs/PassthruCifsAuthenticator.java @@ -174,7 +174,7 @@ public class PassthruCifsAuthenticator extends CifsAuthenticatorBase implements // Start a transaction - UserTransaction tx = getTransactionService().getUserTransaction( false); + UserTransaction tx = createTransaction(); int authSts = AUTH_DISALLOW; try @@ -1015,7 +1015,7 @@ public class PassthruCifsAuthenticator extends CifsAuthenticatorBase implements { // Wrap the service calls in a transaction - UserTransaction tx = getTransactionService().getUserTransaction( false); + UserTransaction tx = createTransaction(); try { diff --git a/source/java/org/alfresco/filesys/auth/ftp/FTPAuthenticatorBase.java b/source/java/org/alfresco/filesys/auth/ftp/FTPAuthenticatorBase.java index ed0fc898ad..37acaa2b45 100644 --- a/source/java/org/alfresco/filesys/auth/ftp/FTPAuthenticatorBase.java +++ b/source/java/org/alfresco/filesys/auth/ftp/FTPAuthenticatorBase.java @@ -1,191 +1,198 @@ -/* - * Copyright (C) 2006-2008 Alfresco Software Limited. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program 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 General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: - * http://www.alfresco.com/legal/licensing" - */ - -package org.alfresco.filesys.auth.ftp; - -import javax.transaction.UserTransaction; - -import org.alfresco.config.ConfigElement; -import org.alfresco.filesys.AlfrescoConfigSection; -import org.alfresco.jlan.ftp.FTPAuthenticator; -import org.alfresco.jlan.ftp.FTPSrvSession; -import org.alfresco.jlan.server.auth.ClientInfo; -import org.alfresco.jlan.server.config.InvalidConfigurationException; -import org.alfresco.jlan.server.config.ServerConfiguration; -import org.alfresco.repo.security.authentication.AuthenticationComponent; -import org.alfresco.service.cmr.security.AuthenticationService; -import org.alfresco.service.cmr.security.AuthorityService; -import org.alfresco.service.transaction.TransactionService; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * - * @author gkspencer - */ -public abstract class FTPAuthenticatorBase implements FTPAuthenticator { - - // Logging - - protected static final Log logger = LogFactory.getLog("org.alfresco.ftp.protocol.auth"); - - // Alfresco configuration section - - private AlfrescoConfigSection m_alfrescoConfig; - - /** - * Default constructor - */ - public FTPAuthenticatorBase() { - } - - /** - * Initialize the authenticator - * - * @param config ServerConfiguration - * @param params ConfigElement - * @exception InvalidConfigurationException - */ - public void initialize(ServerConfiguration config, ConfigElement params) - throws InvalidConfigurationException { - - // Get the alfresco configuration section, required to get hold of various - // services/components - - m_alfrescoConfig = (AlfrescoConfigSection) config.getConfigSection(AlfrescoConfigSection.SectionName); - - // Check that the required authentication classes are available - - if ( m_alfrescoConfig == null || getAuthenticationComponent() == null) - throw new InvalidConfigurationException("Authentication component not available"); - } - - /** - * Authenticate the user - * - * @param client ClientInfo - * @param sess FTPSrvSession - * @return boolean - */ - public abstract boolean authenticateUser(ClientInfo info, FTPSrvSession sess); - - /** - * Close the authenticator, perform any cleanup - */ - public void closeAuthenticator() - { - } - - /** - * Return the authentication componenet - * - * @return AuthenticationComponent - */ - protected final AuthenticationComponent getAuthenticationComponent() { - return m_alfrescoConfig.getAuthenticationComponent(); - } - - /** - * Return the authentication service - * - * @return AuthenticationService - */ - protected final AuthenticationService getAuthenticationService() { - return m_alfrescoConfig.getAuthenticationService(); - } - - /** - * Return the transaction service - * - * @return TransactionService - */ - protected final TransactionService getTransactionService() { - return m_alfrescoConfig.getTransactionService(); - } - - /** - * Return the authority service - * - * @return AuthorityService - */ - protected final AuthorityService getAuthorityService() { - return m_alfrescoConfig.getAuthorityService(); - } - - /** - * Check if the user is an administrator user name - * - * @param cInfo ClientInfo - */ - protected final void checkForAdminUserName(ClientInfo cInfo) { - - // Check if the user name is an administrator - - UserTransaction tx = getTransactionService().getUserTransaction(); - - try { - tx.begin(); - - if ( cInfo.getLogonType() == ClientInfo.LogonNormal && getAuthorityService().isAdminAuthority(cInfo.getUserName())) { - - // Indicate that this is an administrator logon - - cInfo.setLogonType(ClientInfo.LogonAdmin); - } - tx.commit(); - } - catch (Throwable ex) { - try { - tx.rollback(); - } - catch (Throwable ex2) { - logger.error("Failed to rollback transaction", ex2); - } - - // Re-throw the exception - - if ( ex instanceof RuntimeException) { - throw (RuntimeException) ex; - } - else { - throw new RuntimeException("Error during execution of transaction.", ex); - } - } - } - - /** - * Create a transaction, this will be a wrteable transaction unless the system is in read-only mode. - * - * return UserTransaction - */ - protected final UserTransaction createTransaction() - { - // Get the transaction service - - TransactionService txService = getTransactionService(); - - return txService.getUserTransaction( txService.isReadOnly() ? true : false); - } -} +/* + * Copyright (C) 2006-2008 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ + +package org.alfresco.filesys.auth.ftp; + +import javax.transaction.UserTransaction; + +import org.alfresco.config.ConfigElement; +import org.alfresco.filesys.AlfrescoConfigSection; +import org.alfresco.jlan.ftp.FTPAuthenticator; +import org.alfresco.jlan.ftp.FTPSrvSession; +import org.alfresco.jlan.server.auth.ClientInfo; +import org.alfresco.jlan.server.config.InvalidConfigurationException; +import org.alfresco.jlan.server.config.ServerConfiguration; +import org.alfresco.repo.security.authentication.AuthenticationComponent; +import org.alfresco.service.cmr.security.AuthenticationService; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.transaction.TransactionService; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * + * @author gkspencer + */ +public abstract class FTPAuthenticatorBase implements FTPAuthenticator { + + // Logging + + protected static final Log logger = LogFactory.getLog("org.alfresco.ftp.protocol.auth"); + + // Alfresco configuration section + + private AlfrescoConfigSection m_alfrescoConfig; + + /** + * Default constructor + */ + public FTPAuthenticatorBase() { + } + + /** + * Initialize the authenticator + * + * @param config ServerConfiguration + * @param params ConfigElement + * @exception InvalidConfigurationException + */ + public void initialize(ServerConfiguration config, ConfigElement params) + throws InvalidConfigurationException { + + // Get the alfresco configuration section, required to get hold of various + // services/components + + m_alfrescoConfig = (AlfrescoConfigSection) config.getConfigSection(AlfrescoConfigSection.SectionName); + + // Check that the required authentication classes are available + + if ( m_alfrescoConfig == null || getAuthenticationComponent() == null) + throw new InvalidConfigurationException("Authentication component not available"); + } + + /** + * Authenticate the user + * + * @param client ClientInfo + * @param sess FTPSrvSession + * @return boolean + */ + public abstract boolean authenticateUser(ClientInfo info, FTPSrvSession sess); + + /** + * Close the authenticator, perform any cleanup + */ + public void closeAuthenticator() + { + } + + /** + * Return the authentication componenet + * + * @return AuthenticationComponent + */ + protected final AuthenticationComponent getAuthenticationComponent() { + return m_alfrescoConfig.getAuthenticationComponent(); + } + + /** + * Return the authentication service + * + * @return AuthenticationService + */ + protected final AuthenticationService getAuthenticationService() { + return m_alfrescoConfig.getAuthenticationService(); + } + + /** + * Return the transaction service + * + * @return TransactionService + */ + protected final TransactionService getTransactionService() { + return m_alfrescoConfig.getTransactionService(); + } + + /** + * Return the authority service + * + * @return AuthorityService + */ + protected final AuthorityService getAuthorityService() { + return m_alfrescoConfig.getAuthorityService(); + } + + /** + * Check if the user is an administrator user name + * + * @param cInfo ClientInfo + */ + protected final void checkForAdminUserName(ClientInfo cInfo) { + + // Check if the user name is an administrator + + UserTransaction tx = getTransactionService().getUserTransaction(); + + try { + tx.begin(); + + if ( cInfo.getLogonType() == ClientInfo.LogonNormal && getAuthorityService().isAdminAuthority(cInfo.getUserName())) { + + // Indicate that this is an administrator logon + + cInfo.setLogonType(ClientInfo.LogonAdmin); + } + tx.commit(); + } + catch (Throwable ex) { + try { + tx.rollback(); + } + catch (Throwable ex2) { + logger.error("Failed to rollback transaction", ex2); + } + + // Re-throw the exception + + if ( ex instanceof RuntimeException) { + throw (RuntimeException) ex; + } + else { + throw new RuntimeException("Error during execution of transaction.", ex); + } + } + } + + /** + * Create a transaction, this will be a wrteable transaction unless the system is in read-only mode. + * + * return UserTransaction + */ + protected final UserTransaction createTransaction() + { + // Get the transaction service + + TransactionService txService = getTransactionService(); + + // DEBUG + + if ( logger.isDebugEnabled()) + logger.debug("Using " + (txService.isReadOnly() ? "ReadOnly" : "Write") + " transaction"); + + // Create the transaction + + return txService.getUserTransaction( txService.isReadOnly() ? true : false); + } +} diff --git a/source/java/org/alfresco/filesys/auth/nfs/AlfrescoRpcAuthenticator.java b/source/java/org/alfresco/filesys/auth/nfs/AlfrescoRpcAuthenticator.java index a3e60a532b..424f3b7440 100644 --- a/source/java/org/alfresco/filesys/auth/nfs/AlfrescoRpcAuthenticator.java +++ b/source/java/org/alfresco/filesys/auth/nfs/AlfrescoRpcAuthenticator.java @@ -1,462 +1,484 @@ -/* - * Copyright (C) 2005-2007 Alfresco Software Limited. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program 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 General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: - * http://www.alfresco.com/legal/licensing" - */ -package org.alfresco.filesys.auth.nfs; - -import java.util.HashMap; -import java.util.List; - -import javax.transaction.Status; -import javax.transaction.UserTransaction; - -import org.alfresco.config.ConfigElement; -import org.alfresco.filesys.AlfrescoConfigSection; -import org.alfresco.filesys.alfresco.AlfrescoClientInfo; -import org.alfresco.jlan.oncrpc.AuthType; -import org.alfresco.jlan.oncrpc.Rpc; -import org.alfresco.jlan.oncrpc.RpcAuthenticationException; -import org.alfresco.jlan.oncrpc.RpcAuthenticator; -import org.alfresco.jlan.oncrpc.RpcPacket; -import org.alfresco.jlan.oncrpc.nfs.NFS; -import org.alfresco.jlan.server.SrvSession; -import org.alfresco.jlan.server.auth.ClientInfo; -import org.alfresco.jlan.server.config.InvalidConfigurationException; -import org.alfresco.jlan.server.config.ServerConfiguration; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * Alfresco RPC Authenticator Class - * - *

Provides authentication support for the NFS server. - * - * @author gkspencer - */ -public class AlfrescoRpcAuthenticator implements RpcAuthenticator { - - // Debug logging - - private static final Log logger = LogFactory.getLog("org.alfresco.nfs.protocol.auth"); - - // Authentication types aupported by this implementation - - private int[] _authTypes = { AuthType.Unix }; - - // UID/GID to username conversions - - private HashMap m_idMap; - - // Alfresco configuration - - protected AlfrescoConfigSection m_alfrescoConfig; - - /** - * Authenticate an RPC client and create a unique session id key. - * - * @param authType int - * @param rpc RpcPacket - * @return Object - * @throws RpcAuthenticationException - */ - public Object authenticateRpcClient(int authType, RpcPacket rpc) - throws RpcAuthenticationException { - - // Create a unique session key depending on the authentication type - - Object sessKey = null; - - if (authType == AuthType.Unix) { - - // Get the gid and uid from the credentials data in the request - - rpc.positionAtCredentialsData(); - rpc.skipBytes(4); - int nameLen = rpc.unpackInt(); - rpc.skipBytes(nameLen); - - int uid = rpc.unpackInt(); - int gid = rpc.unpackInt(); - - // DEBUG - - if ( logger.isDebugEnabled()) - logger.debug( "RpcAuth: Type=Unix uid=" + uid + ", gid=" + gid); - - // Check that there is a user name mapping for the uid/gid - - Integer idKey = new Integer((gid << 16) + uid); - String userName = m_idMap.get( idKey); - - if ( userName == null) - throw new RpcAuthenticationException( NFS.StsAccess); - - // Check if the Unix authentication session table is valid - - sessKey = new Long((((long) rpc.getClientAddress().hashCode()) << 32) + (gid << 16) + uid); - } - else if ( authType == AuthType.Null) - { - // Set the session key for the null authentication - - sessKey = new Integer(rpc.getClientAddress().hashCode()); - - // DEBUG - - if ( logger.isDebugEnabled()) - logger.debug( "RpcAuth: Type=Null client=" + rpc.getClientAddress()); - } - - // Check if the session key is valid, if not then the authentication - // type is unsupported - - if (sessKey == null) - throw new RpcAuthenticationException(Rpc.AuthBadCred, "Unsupported auth type, " + authType); - - // DEBUG - - if (logger.isDebugEnabled()) - logger.debug("RpcAuth: RPC from " + rpc.getClientDetails() - + ", authType=" + AuthType.getTypeAsString(authType) - + ", sessKey=" + sessKey); - - // Return the session key - - return sessKey; - } - - /** - * Return the authentication types that are supported by this - * implementation. - * - * @return int[] - */ - public int[] getRpcAuthenticationTypes() { - return _authTypes; - } - - /** - * Return the client information for the specified RPC request - * - * @param sessKey Object - * @param rpc RpcPacket - * @return ClientInfo - */ - public ClientInfo getRpcClientInformation(Object sessKey, RpcPacket rpc) - { - // Create a client information object to hold the client details - - ClientInfo cInfo = null; - - // Get the authentication type - - int authType = rpc.getCredentialsType(); - - // Unpack the client details from the RPC request - - if ( authType == AuthType.Unix) { - - // Unpack the credentials data - - rpc.positionAtCredentialsData(); - rpc.skipBytes(4); // stamp id - - String clientAddr = rpc.unpackString(); - int uid = rpc.unpackInt(); - int gid = rpc.unpackInt(); - - // Check for an additional groups list - - int grpLen = rpc.unpackInt(); - int[] groups = null; - - if (grpLen > 0) { - groups = new int[grpLen]; - rpc.unpackIntArray(groups); - } - - // Get the user name mapping for the uid/gid and authenticate - - Integer idKey = new Integer((gid << 16) + uid); - String userName = m_idMap.get( idKey); - - // DEBUG - - if ( logger.isDebugEnabled()) - logger.debug( "RpcClientInfo: username=" + userName + ", uid=" + uid + ", gid=" + gid); - - // Create the client information if there is a valid mapping - - if ( userName != null) - { - // Create the client information and fill in relevant fields - - cInfo = ClientInfo.getFactory().createInfo( userName, null); - - cInfo.setNFSAuthenticationType( authType); - cInfo.setClientAddress( clientAddr); - cInfo.setUid( uid); - cInfo.setGid( gid); - - cInfo.setGroupsList(groups); - } - - // DEBUG - - if (logger.isDebugEnabled()) - logger.debug("RpcAuth: Client info, type=" + AuthType.getTypeAsString(authType) + ", name=" - + clientAddr + ", uid=" + uid + ", gid=" + gid + ", groups=" + grpLen); - } - else if ( authType == AuthType.Null) - { - // Create the client information - - cInfo = ClientInfo.getFactory().createInfo( "", null); - cInfo.setClientAddress(rpc.getClientAddress().getHostAddress()); - - // DEBUG - - if (logger.isDebugEnabled()) - logger.debug("RpcAuth: Client info, type=" + AuthType.getTypeAsString(authType) + ", addr=" - + rpc.getClientAddress().getHostAddress()); - } - - // Return the client information - - return cInfo; - } - - /** - * Set the current authenticated user context for this thread - * - * @param sess SrvSession - * @param client ClientInfo - */ - public void setCurrentUser( SrvSession sess, ClientInfo client) - { - // Start a transaction - - UserTransaction tx = m_alfrescoConfig.getTransactionService().getUserTransaction( false); - - try - { - // start the transaction - - tx.begin(); - - // Check the account type and setup the authentication context - - if ( client == null || client.isNullSession() || client instanceof AlfrescoClientInfo == false) - { - // Clear the authentication, null user should not be allowed to do any service calls - - m_alfrescoConfig.getAuthenticationComponent().clearCurrentSecurityContext(); - - // DEBUG - - if ( logger.isDebugEnabled()) - logger.debug("Clear security context, client=" + client); - } - else if ( client.isGuest() == false) - { - // Access the Alfresco client - - AlfrescoClientInfo alfClient = (AlfrescoClientInfo) client; - - // Check if the authentication token has been set for the client - - if ( alfClient.hasAuthenticationToken() == false) - { - // Set the current user and retrieve the authentication token - - m_alfrescoConfig.getAuthenticationComponent().setCurrentUser( client.getUserName()); - alfClient.setAuthenticationToken( m_alfrescoConfig.getAuthenticationComponent().getCurrentAuthentication()); - - // DEBUG - - if ( logger.isDebugEnabled()) - logger.debug("Set user name=" + client.getUserName() + ", token=" + alfClient.getAuthenticationToken()); - } - else - { - // Set the authentication context for the request - - m_alfrescoConfig.getAuthenticationComponent().setCurrentAuthentication( alfClient.getAuthenticationToken()); - - // DEBUG - - if ( logger.isDebugEnabled()) - logger.debug("Set user using auth token, token=" + alfClient.getAuthenticationToken()); - } - } - else - { - // Enable guest access for the request - - m_alfrescoConfig.getAuthenticationComponent().setGuestUserAsCurrentUser(); - - // DEBUG - - if ( logger.isDebugEnabled()) - logger.debug("Set guest user"); - } - } - catch ( Exception ex) - { - if ( logger.isDebugEnabled()) - logger.debug( ex); - } - finally - { - // Commit the transaction - - if ( tx != null) - { - try - { - // Commit or rollback the transaction - - if ( tx.getStatus() == Status.STATUS_MARKED_ROLLBACK) - { - // Transaction is marked for rollback - - tx.rollback(); - } - else - { - // Commit the transaction - - tx.commit(); - } - } - catch ( Exception ex) - { - } - } - } - } - - /** - * Initialize the RPC authenticator - * - * @param config ServerConfiguration - * @param params NameValueList - * @throws InvalidConfigurationException - */ - public void initialize(ServerConfiguration config, ConfigElement params) - throws InvalidConfigurationException { - - // Get the Alfresco configuration section, for access to services - - m_alfrescoConfig = (AlfrescoConfigSection) config.getConfigSection( AlfrescoConfigSection.SectionName); - - // Check for the user mappings - - ConfigElement userMappings = params.getChild("userMappings"); - if ( userMappings != null) - { - // Allocate the id mappings table - - m_idMap = new HashMap(); - - // Get the user map elements - - List userMaps = userMappings.getChildren(); - - // Process the user list - - for ( ConfigElement userElem : userMaps) - { - // Validate the element type - - if ( userElem.getName().equalsIgnoreCase( "user")) - { - // Get the user name, user id and group id - - String userName = userElem.getAttribute("name"); - String uidStr = userElem.getAttribute("uid"); - String gidStr = userElem.getAttribute("gid"); - - if ( userName == null || userName.length() == 0) - throw new InvalidConfigurationException("Empty user name, or name not specified"); - - if ( uidStr == null || uidStr.length() == 0) - throw new InvalidConfigurationException("Invalid uid, or uid not specified, for user " + userName); - - if ( gidStr == null || gidStr.length() == 0) - throw new InvalidConfigurationException("Invalid gid, or gid not specified, for user " + userName); - - // Parse the uid/gid - - int uid = -1; - int gid = -1; - - try - { - uid = Integer.parseInt( uidStr); - } - catch ( NumberFormatException ex) - { - throw new InvalidConfigurationException("Invalid uid value, " + uidStr + " for user " + userName); - } - - try - { - gid = Integer.parseInt( gidStr); - } - catch ( NumberFormatException ex) - { - throw new InvalidConfigurationException("Invalid gid value, " + gidStr + " for user " + userName); - } - - // Check if the mapping already exists - - Integer idKey = new Integer(( gid << 16) + uid); - if ( m_idMap.containsKey( idKey) == false) - { - // Add the username uid/gid mapping - - m_idMap.put( idKey, userName); - - // DEBUG - - if ( logger.isDebugEnabled()) - logger.debug("Added RPC user mapping for user " + userName + " uid=" + uid + ", gid=" + gid); - } - else if ( logger.isDebugEnabled()) - { - // DEBUG - - logger.debug("Ignored duplicate mapping for uid=" + uid + ", gid=" + gid); - } - } - else - throw new InvalidConfigurationException( "Invalid user mapping, " + userElem.getName()); - } - } - - // Make sure there are some user mappings - - if ( m_idMap == null || m_idMap.size() == 0) - throw new InvalidConfigurationException("No user mappings for RPC authenticator"); - } -} +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.filesys.auth.nfs; + +import java.util.HashMap; +import java.util.List; + +import javax.transaction.Status; +import javax.transaction.UserTransaction; + +import org.alfresco.config.ConfigElement; +import org.alfresco.filesys.AlfrescoConfigSection; +import org.alfresco.filesys.alfresco.AlfrescoClientInfo; +import org.alfresco.jlan.oncrpc.AuthType; +import org.alfresco.jlan.oncrpc.Rpc; +import org.alfresco.jlan.oncrpc.RpcAuthenticationException; +import org.alfresco.jlan.oncrpc.RpcAuthenticator; +import org.alfresco.jlan.oncrpc.RpcPacket; +import org.alfresco.jlan.oncrpc.nfs.NFS; +import org.alfresco.jlan.server.SrvSession; +import org.alfresco.jlan.server.auth.ClientInfo; +import org.alfresco.jlan.server.config.InvalidConfigurationException; +import org.alfresco.jlan.server.config.ServerConfiguration; +import org.alfresco.service.transaction.TransactionService; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Alfresco RPC Authenticator Class + * + *

Provides authentication support for the NFS server. + * + * @author gkspencer + */ +public class AlfrescoRpcAuthenticator implements RpcAuthenticator { + + // Debug logging + + private static final Log logger = LogFactory.getLog("org.alfresco.nfs.protocol.auth"); + + // Authentication types aupported by this implementation + + private int[] _authTypes = { AuthType.Unix }; + + // UID/GID to username conversions + + private HashMap m_idMap; + + // Alfresco configuration + + protected AlfrescoConfigSection m_alfrescoConfig; + + /** + * Authenticate an RPC client and create a unique session id key. + * + * @param authType int + * @param rpc RpcPacket + * @return Object + * @throws RpcAuthenticationException + */ + public Object authenticateRpcClient(int authType, RpcPacket rpc) + throws RpcAuthenticationException { + + // Create a unique session key depending on the authentication type + + Object sessKey = null; + + if (authType == AuthType.Unix) { + + // Get the gid and uid from the credentials data in the request + + rpc.positionAtCredentialsData(); + rpc.skipBytes(4); + int nameLen = rpc.unpackInt(); + rpc.skipBytes(nameLen); + + int uid = rpc.unpackInt(); + int gid = rpc.unpackInt(); + + // DEBUG + + if ( logger.isDebugEnabled()) + logger.debug( "RpcAuth: Type=Unix uid=" + uid + ", gid=" + gid); + + // Check that there is a user name mapping for the uid/gid + + Integer idKey = new Integer((gid << 16) + uid); + String userName = m_idMap.get( idKey); + + if ( userName == null) + throw new RpcAuthenticationException( NFS.StsAccess); + + // Check if the Unix authentication session table is valid + + sessKey = new Long((((long) rpc.getClientAddress().hashCode()) << 32) + (gid << 16) + uid); + } + else if ( authType == AuthType.Null) + { + // Set the session key for the null authentication + + sessKey = new Integer(rpc.getClientAddress().hashCode()); + + // DEBUG + + if ( logger.isDebugEnabled()) + logger.debug( "RpcAuth: Type=Null client=" + rpc.getClientAddress()); + } + + // Check if the session key is valid, if not then the authentication + // type is unsupported + + if (sessKey == null) + throw new RpcAuthenticationException(Rpc.AuthBadCred, "Unsupported auth type, " + authType); + + // DEBUG + + if (logger.isDebugEnabled()) + logger.debug("RpcAuth: RPC from " + rpc.getClientDetails() + + ", authType=" + AuthType.getTypeAsString(authType) + + ", sessKey=" + sessKey); + + // Return the session key + + return sessKey; + } + + /** + * Return the authentication types that are supported by this + * implementation. + * + * @return int[] + */ + public int[] getRpcAuthenticationTypes() { + return _authTypes; + } + + /** + * Return the client information for the specified RPC request + * + * @param sessKey Object + * @param rpc RpcPacket + * @return ClientInfo + */ + public ClientInfo getRpcClientInformation(Object sessKey, RpcPacket rpc) + { + // Create a client information object to hold the client details + + ClientInfo cInfo = null; + + // Get the authentication type + + int authType = rpc.getCredentialsType(); + + // Unpack the client details from the RPC request + + if ( authType == AuthType.Unix) { + + // Unpack the credentials data + + rpc.positionAtCredentialsData(); + rpc.skipBytes(4); // stamp id + + String clientAddr = rpc.unpackString(); + int uid = rpc.unpackInt(); + int gid = rpc.unpackInt(); + + // Check for an additional groups list + + int grpLen = rpc.unpackInt(); + int[] groups = null; + + if (grpLen > 0) { + groups = new int[grpLen]; + rpc.unpackIntArray(groups); + } + + // Get the user name mapping for the uid/gid and authenticate + + Integer idKey = new Integer((gid << 16) + uid); + String userName = m_idMap.get( idKey); + + // DEBUG + + if ( logger.isDebugEnabled()) + logger.debug( "RpcClientInfo: username=" + userName + ", uid=" + uid + ", gid=" + gid); + + // Create the client information if there is a valid mapping + + if ( userName != null) + { + // Create the client information and fill in relevant fields + + cInfo = ClientInfo.getFactory().createInfo( userName, null); + + cInfo.setNFSAuthenticationType( authType); + cInfo.setClientAddress( clientAddr); + cInfo.setUid( uid); + cInfo.setGid( gid); + + cInfo.setGroupsList(groups); + } + + // DEBUG + + if (logger.isDebugEnabled()) + logger.debug("RpcAuth: Client info, type=" + AuthType.getTypeAsString(authType) + ", name=" + + clientAddr + ", uid=" + uid + ", gid=" + gid + ", groups=" + grpLen); + } + else if ( authType == AuthType.Null) + { + // Create the client information + + cInfo = ClientInfo.getFactory().createInfo( "", null); + cInfo.setClientAddress(rpc.getClientAddress().getHostAddress()); + + // DEBUG + + if (logger.isDebugEnabled()) + logger.debug("RpcAuth: Client info, type=" + AuthType.getTypeAsString(authType) + ", addr=" + + rpc.getClientAddress().getHostAddress()); + } + + // Return the client information + + return cInfo; + } + + /** + * Set the current authenticated user context for this thread + * + * @param sess SrvSession + * @param client ClientInfo + */ + public void setCurrentUser( SrvSession sess, ClientInfo client) + { + // Start a transaction + + UserTransaction tx = createTransaction(); + + try + { + // start the transaction + + tx.begin(); + + // Check the account type and setup the authentication context + + if ( client == null || client.isNullSession() || client instanceof AlfrescoClientInfo == false) + { + // Clear the authentication, null user should not be allowed to do any service calls + + m_alfrescoConfig.getAuthenticationComponent().clearCurrentSecurityContext(); + + // DEBUG + + if ( logger.isDebugEnabled()) + logger.debug("Clear security context, client=" + client); + } + else if ( client.isGuest() == false) + { + // Access the Alfresco client + + AlfrescoClientInfo alfClient = (AlfrescoClientInfo) client; + + // Check if the authentication token has been set for the client + + if ( alfClient.hasAuthenticationToken() == false) + { + // Set the current user and retrieve the authentication token + + m_alfrescoConfig.getAuthenticationComponent().setCurrentUser( client.getUserName()); + alfClient.setAuthenticationToken( m_alfrescoConfig.getAuthenticationComponent().getCurrentAuthentication()); + + // DEBUG + + if ( logger.isDebugEnabled()) + logger.debug("Set user name=" + client.getUserName() + ", token=" + alfClient.getAuthenticationToken()); + } + else + { + // Set the authentication context for the request + + m_alfrescoConfig.getAuthenticationComponent().setCurrentAuthentication( alfClient.getAuthenticationToken()); + + // DEBUG + + if ( logger.isDebugEnabled()) + logger.debug("Set user using auth token, token=" + alfClient.getAuthenticationToken()); + } + } + else + { + // Enable guest access for the request + + m_alfrescoConfig.getAuthenticationComponent().setGuestUserAsCurrentUser(); + + // DEBUG + + if ( logger.isDebugEnabled()) + logger.debug("Set guest user"); + } + } + catch ( Exception ex) + { + if ( logger.isDebugEnabled()) + logger.debug( ex); + } + finally + { + // Commit the transaction + + if ( tx != null) + { + try + { + // Commit or rollback the transaction + + if ( tx.getStatus() == Status.STATUS_MARKED_ROLLBACK) + { + // Transaction is marked for rollback + + tx.rollback(); + } + else + { + // Commit the transaction + + tx.commit(); + } + } + catch ( Exception ex) + { + } + } + } + } + + /** + * Initialize the RPC authenticator + * + * @param config ServerConfiguration + * @param params NameValueList + * @throws InvalidConfigurationException + */ + public void initialize(ServerConfiguration config, ConfigElement params) + throws InvalidConfigurationException { + + // Get the Alfresco configuration section, for access to services + + m_alfrescoConfig = (AlfrescoConfigSection) config.getConfigSection( AlfrescoConfigSection.SectionName); + + // Check for the user mappings + + ConfigElement userMappings = params.getChild("userMappings"); + if ( userMappings != null) + { + // Allocate the id mappings table + + m_idMap = new HashMap(); + + // Get the user map elements + + List userMaps = userMappings.getChildren(); + + // Process the user list + + for ( ConfigElement userElem : userMaps) + { + // Validate the element type + + if ( userElem.getName().equalsIgnoreCase( "user")) + { + // Get the user name, user id and group id + + String userName = userElem.getAttribute("name"); + String uidStr = userElem.getAttribute("uid"); + String gidStr = userElem.getAttribute("gid"); + + if ( userName == null || userName.length() == 0) + throw new InvalidConfigurationException("Empty user name, or name not specified"); + + if ( uidStr == null || uidStr.length() == 0) + throw new InvalidConfigurationException("Invalid uid, or uid not specified, for user " + userName); + + if ( gidStr == null || gidStr.length() == 0) + throw new InvalidConfigurationException("Invalid gid, or gid not specified, for user " + userName); + + // Parse the uid/gid + + int uid = -1; + int gid = -1; + + try + { + uid = Integer.parseInt( uidStr); + } + catch ( NumberFormatException ex) + { + throw new InvalidConfigurationException("Invalid uid value, " + uidStr + " for user " + userName); + } + + try + { + gid = Integer.parseInt( gidStr); + } + catch ( NumberFormatException ex) + { + throw new InvalidConfigurationException("Invalid gid value, " + gidStr + " for user " + userName); + } + + // Check if the mapping already exists + + Integer idKey = new Integer(( gid << 16) + uid); + if ( m_idMap.containsKey( idKey) == false) + { + // Add the username uid/gid mapping + + m_idMap.put( idKey, userName); + + // DEBUG + + if ( logger.isDebugEnabled()) + logger.debug("Added RPC user mapping for user " + userName + " uid=" + uid + ", gid=" + gid); + } + else if ( logger.isDebugEnabled()) + { + // DEBUG + + logger.debug("Ignored duplicate mapping for uid=" + uid + ", gid=" + gid); + } + } + else + throw new InvalidConfigurationException( "Invalid user mapping, " + userElem.getName()); + } + } + + // Make sure there are some user mappings + + if ( m_idMap == null || m_idMap.size() == 0) + throw new InvalidConfigurationException("No user mappings for RPC authenticator"); + } + + /** + * Create a transaction, this will be a wrteable transaction unless the system is in read-only mode. + * + * return UserTransaction + */ + protected final UserTransaction createTransaction() + { + // Get the transaction service + + TransactionService txService = m_alfrescoConfig.getTransactionService(); + + // DEBUG + + if ( logger.isDebugEnabled()) + logger.debug("Using " + (txService.isReadOnly() ? "ReadOnly" : "Write") + " transaction"); + + // Create the transaction + + return txService.getUserTransaction( txService.isReadOnly() ? true : false); + } +} diff --git a/source/java/org/alfresco/filesys/repo/CifsHelper.java b/source/java/org/alfresco/filesys/repo/CifsHelper.java index 1e15df2925..53be8fab68 100644 --- a/source/java/org/alfresco/filesys/repo/CifsHelper.java +++ b/source/java/org/alfresco/filesys/repo/CifsHelper.java @@ -1,691 +1,689 @@ -/* - * Copyright (C) 2005-2007 Alfresco Software Limited. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program 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 General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: - * http://www.alfresco.com/legal/licensing" */ -package org.alfresco.filesys.repo; - -import java.io.FileNotFoundException; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Stack; -import java.util.StringTokenizer; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.jlan.server.filesys.FileAttribute; -import org.alfresco.jlan.server.filesys.FileExistsException; -import org.alfresco.jlan.server.filesys.FileName; -import org.alfresco.jlan.util.WildCard; -import org.alfresco.model.ContentModel; -import org.alfresco.repo.model.filefolder.FileFolderServiceImpl; -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.cmr.model.FileFolderService; -import org.alfresco.service.cmr.model.FileInfo; -import org.alfresco.service.cmr.repository.ContentData; -import org.alfresco.service.cmr.repository.InvalidNodeRefException; -import org.alfresco.service.cmr.repository.MimetypeService; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; -import org.alfresco.service.cmr.security.AccessStatus; -import org.alfresco.service.cmr.security.PermissionService; -import org.alfresco.service.namespace.QName; -import org.alfresco.util.SearchLanguageConversion; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * Class with supplying helper methods and potentially acting as a cache for - * queries. - * - * @author derekh - */ -public class CifsHelper -{ - // Logging - private static Log logger = LogFactory.getLog(CifsHelper.class); - - // Services - private DictionaryService dictionaryService; - private NodeService nodeService; - private FileFolderService fileFolderService; - private MimetypeService mimetypeService; - private PermissionService permissionService; - private boolean isReadOnly; - - // Mark locked files as offline - - private boolean lockedFilesAsOffline; - - /** - * Class constructor - */ - public CifsHelper() - { - isReadOnly = false; - } - - public void setDictionaryService(DictionaryService dictionaryService) - { - this.dictionaryService = dictionaryService; - } - - public void setNodeService(NodeService nodeService) - { - this.nodeService = nodeService; - } - - public void setFileFolderService(FileFolderService fileFolderService) - { - this.fileFolderService = fileFolderService; - } - - public void setMimetypeService(MimetypeService mimetypeService) - { - this.mimetypeService = mimetypeService; - } - - public void setPermissionService(PermissionService permissionService) - { - this.permissionService = permissionService; - } - - /** - * @return Returns true if all files/folders should be treated as read-only - */ - public boolean isReadOnly() - { - return isReadOnly; - } - - /** - * Set whether the system allows files to be edited or not. The default is - * to allow writes. - * @param allowWrites true to allow writes, otherwise false for read-only mode - */ - public void setAllowWrites(boolean allowWrites) - { - this.isReadOnly = !allowWrites; - } - - /** - * Enable marking of locked files as offline - * - * @param ena boolean - */ - public final void setMarkLockedFilesAsOffline(boolean ena) - { - lockedFilesAsOffline = ena; - } - - /** - * Check if locked files should be marked as offline - * - * @return boolean - */ - public final boolean hasLockedFilesAsOffline() - { - return lockedFilesAsOffline; - } - - /** - * @param serviceRegistry for repo connection - * @param nodeRef - * @return Returns true if the node is a subtype of {@link ContentModel#TYPE_FOLDER folder} - * @throws AlfrescoRuntimeException if the type is neither related to a folder or content - */ - public boolean isDirectory(NodeRef nodeRef) - { - QName nodeTypeQName = nodeService.getType(nodeRef); - if (dictionaryService.isSubClass(nodeTypeQName, ContentModel.TYPE_FOLDER)) - { - return true; - } - else if (dictionaryService.isSubClass(nodeTypeQName, ContentModel.TYPE_CONTENT)) - { - return false; - } - else - { - // it is not a directory, but what is it? - return false; - } - } - - /** - * Extract a single node's file info, where the node is reference by - * a path relative to an ancestor node. - * - * @param pathRootNodeRef - * @param path - * @return Returns the existing node reference - * @throws FileNotFoundException - */ - public ContentFileInfo getFileInformation(NodeRef pathRootNodeRef, String path) throws FileNotFoundException - { - // get the node being referenced - NodeRef nodeRef = getNodeRef(pathRootNodeRef, path); - - return getFileInformation(nodeRef); - } - - /** - * Helper method to extract file info from a specific node. - *

- * This method goes direct to the repo for all information and no data is - * cached here. - * - * @param nodeRef the node that the path is relative to - * @param path the path to get info for - * @return Returns the file information pertinent to the node - * @throws FileNotFoundException if the path refers to a non-existent file - */ - public ContentFileInfo getFileInformation(NodeRef nodeRef) throws FileNotFoundException - { - // get the file info - org.alfresco.service.cmr.model.FileInfo fileFolderInfo = fileFolderService.getFileInfo(nodeRef); - - // retrieve required properties and create file info - ContentFileInfo fileInfo = new ContentFileInfo(); - fileInfo.setNodeRef(nodeRef); - - // unset all attribute flags - int fileAttributes = 0; - fileInfo.setFileAttributes(fileAttributes); - - if (fileFolderInfo.isFolder()) - { - // add directory attribute - fileAttributes |= FileAttribute.Directory; - fileInfo.setFileAttributes(fileAttributes); - } - else - { - Map nodeProperties = fileFolderInfo.getProperties(); - - // Get the file size from the content - - ContentData contentData = (ContentData) nodeProperties.get(ContentModel.PROP_CONTENT); - long size = 0L; - if (contentData != null) - { - size = contentData.getSize(); - } - fileInfo.setSize(size); - - // Set the allocation size by rounding up the size to a 512 byte block boundary - - if ( size > 0) - fileInfo.setAllocationSize((size + 512L) & 0xFFFFFFFFFFFFFE00L); - - // Check the lock status of the file - - String lockTypeStr = (String) nodeProperties.get(ContentModel.PROP_LOCK_TYPE); - - if ( lockTypeStr != null ) - { - // File is locked so mark it as read-only and offline - - int attr = fileInfo.getFileAttributes(); - - if (( attr & FileAttribute.ReadOnly) == 0) - attr += FileAttribute.ReadOnly; - - if ( hasLockedFilesAsOffline()) - attr += FileAttribute.NTOffline; - - fileInfo.setFileAttributes( attr); - } - - // Check if it is a link node - - if ( fileFolderInfo.isLink()) - fileInfo.setLinkNodeRef( fileFolderInfo.getLinkNodeRef()); - } - - // created - Date createdDate = fileFolderInfo.getCreatedDate(); - if (createdDate != null) - { - long created = DefaultTypeConverter.INSTANCE.longValue(createdDate); - fileInfo.setCreationDateTime(created); - } - // modified - Date modifiedDate = fileFolderInfo.getModifiedDate(); - if (modifiedDate != null) - { - long modified = DefaultTypeConverter.INSTANCE.longValue(modifiedDate); - fileInfo.setModifyDateTime(modified); - fileInfo.setAccessDateTime(modified); - } - // name - String name = fileFolderInfo.getName(); - if (name != null) - { - fileInfo.setFileName(name); - } - - // Read/write access - - boolean deniedPermission = permissionService.hasPermission(nodeRef, PermissionService.WRITE) == AccessStatus.DENIED; - if (isReadOnly || deniedPermission) - { - int attr = fileInfo.getFileAttributes(); - if (( attr & FileAttribute.ReadOnly) == 0) - { - attr += FileAttribute.ReadOnly; - fileInfo.setFileAttributes(attr); - } - } - - // Set the normal file attribute if no other attributes are set - - if ( fileInfo.getFileAttributes() == 0) - fileInfo.setFileAttributes(FileAttribute.NTNormal); - - // Debug - - if (logger.isDebugEnabled()) - { - logger.debug("Fetched file info: \n" + - " info: " + fileInfo); - } - - // Return the file information - - return fileInfo; - } - - /** - * Creates a file or directory using the given paths. - *

- * If the directory path doesn't exist, then all the parent directories will be created. - * If the file path is null, then the file will not be created - * - * @param rootNodeRef the root node of the path - * @param path the path to a node - * @param isFile true if the node to be created must be a file - * @return Returns a newly created file or folder node - * @throws FileExistsException if the file or folder already exists - */ - public NodeRef createNode(NodeRef rootNodeRef, String path, boolean isFile) throws FileExistsException - { - // split the path up into its constituents - StringTokenizer tokenizer = new StringTokenizer(path, FileName.DOS_SEPERATOR_STR, false); - List folderPathElements = new ArrayList(10); - String name = null; - while (tokenizer.hasMoreTokens()) - { - String pathElement = tokenizer.nextToken(); - - if (!tokenizer.hasMoreTokens()) - { - // the last token becomes the name - name = pathElement; - } - else - { - // add the path element to the parent folder path - folderPathElements.add(pathElement); - } - } - // ensure that the folder path exists - NodeRef parentFolderNodeRef = rootNodeRef; - if (folderPathElements.size() > 0) - { - parentFolderNodeRef = FileFolderServiceImpl.makeFolders( - fileFolderService, - rootNodeRef, - folderPathElements, - ContentModel.TYPE_FOLDER).getNodeRef(); - } - // add the file or folder - QName typeQName = isFile ? ContentModel.TYPE_CONTENT : ContentModel.TYPE_FOLDER; - try - { - NodeRef nodeRef = fileFolderService.create(parentFolderNodeRef, name, typeQName).getNodeRef(); - - // done - if (logger.isDebugEnabled()) - { - logger.debug("Created node: \n" + - " device root: " + rootNodeRef + "\n" + - " path: " + path + "\n" + - " is file: " + isFile + "\n" + - " new node: " + nodeRef); - } - return nodeRef; - } - catch (org.alfresco.service.cmr.model.FileExistsException e) - { - throw new FileExistsException(path); - } - } - - private void addDescendents(List pathRootNodeRefs, Stack pathElements, List results) - { - if (pathElements.isEmpty()) - { - // if this method is called with an empty path element stack, then the - // current context nodes are the results to be added - results.addAll(pathRootNodeRefs); - return; - } - - // take the first path element off the stack - String pathElement = pathElements.pop(); - - // iterate over each path root node - for (NodeRef pathRootNodeRef : pathRootNodeRefs) - { - // deal with cyclic relationships by not traversing down any node already in the results - if (results.contains(pathRootNodeRef)) - { - continue; - } - // get direct descendents along the path - List directDescendents = getDirectDescendents(pathRootNodeRef, pathElement); - // recurse onto the descendents - addDescendents(directDescendents, pathElements, results); - } - - // restore the path element stack - pathElements.push(pathElement); - } - - /** - * Searches for the node or nodes that match the path element for the given parent node - */ - private List getDirectDescendents(NodeRef pathRootNodeRef, String pathElement) - { - if (logger.isDebugEnabled()) - { - logger.debug("Getting direct descendents: \n" + - " Path Root: " + pathRootNodeRef + "\n" + - " Path Element: " + pathElement); - } - List results = null; - // if this contains no wildcards, then we can fasttrack it - if (!WildCard.containsWildcards(pathElement)) - { - // a specific name is required - NodeRef foundNodeRef = fileFolderService.searchSimple(pathRootNodeRef, pathElement); - if (foundNodeRef == null) - { - results = Collections.emptyList(); - } - else - { - results = Collections.singletonList(foundNodeRef); - } - } - else - { - // escape for the Lucene syntax search - String escapedPathElement = SearchLanguageConversion.convertCifsToLucene(pathElement); - // do the lookup - List childInfos = fileFolderService.search( - pathRootNodeRef, - escapedPathElement, - false); - // convert to noderefs - results = new ArrayList(childInfos.size()); - for (org.alfresco.service.cmr.model.FileInfo info : childInfos) - { - results.add(info.getNodeRef()); - } - } - // done - return results; - } - - /** - * Finds the nodes being reference by the given directory and file paths. - *

- * Examples of the path are: - *

- * - * @param searchRootNodeRef the node from which to start the path search - * @param path the search path to either a folder or file - * @return Returns references to all matching nodes - */ - public List getNodeRefs(NodeRef pathRootNodeRef, String path) - { - // tokenize the path and push into a stack in reverse order so that - // the root directory gets popped first - StringTokenizer tokenizer = new StringTokenizer(path, FileName.DOS_SEPERATOR_STR, false); - String[] tokens = new String[tokenizer.countTokens()]; - int count = 0; - while(tokenizer.hasMoreTokens()) - { - tokens[count] = tokenizer.nextToken(); - count++; - } - Stack pathElements = new Stack(); - for (int i = tokens.length - 1; i >= 0; i--) - { - pathElements.push(tokens[i]); - } - - // start with a single parent node - List pathRootNodeRefs = Collections.singletonList(pathRootNodeRef); - - // result storage - List results = new ArrayList(5); - - // kick off the path walking - addDescendents(pathRootNodeRefs, pathElements, results); - - // done - if (logger.isDebugEnabled()) - { - logger.debug("Retrieved node references for path: \n" + - " path root: " + pathRootNodeRef + "\n" + - " path: " + path + "\n" + - " results: " + results); - } - return results; - } - - /** - * Attempts to fetch a specific single node at the given path. - * - * @throws FileNotFoundException if the path can't be resolved to a node - * - * @see #getNodeRefs(NodeRef, String) - */ - public NodeRef getNodeRef(NodeRef pathRootNodeRef, String path) throws FileNotFoundException - { - // attempt to get the file/folder node using hierarchy walking - List nodeRefs = getNodeRefs(pathRootNodeRef, path); - if (nodeRefs.size() == 0) - { - throw new FileNotFoundException(path); - } - else if (nodeRefs.size() > 1) - { - logger.warn("Multiple matching nodes: \n" + - " search root: " + pathRootNodeRef + "\n" + - " path: " + path); - } - // take the first one - not sure if it is possible for the path to refer to more than one - NodeRef nodeRef = nodeRefs.get(0); - // done - return nodeRef; - } - - /** - * Relink the content data from a new node to an existing node to preserve the version history. - * - * @param oldNodeRef NodeRef - * @param newNodeRef NodeRef - */ - public void relinkNode(NodeRef tempNodeRef, NodeRef nodeToMoveRef, NodeRef newParentNodeRef, String newName) - throws FileNotFoundException, FileExistsException - { - // Get the properties for the old and new nodes - org.alfresco.service.cmr.model.FileInfo tempFileInfo = fileFolderService.getFileInfo(tempNodeRef); - org.alfresco.service.cmr.model.FileInfo fileToMoveInfo = fileFolderService.getFileInfo(nodeToMoveRef); - - // Save the current name of the old node - String tempName = tempFileInfo.getName(); - - try - { - // remove the tempory aspects from the nodes, this will be reapplied if the new name dictates it - nodeService.removeAspect(tempNodeRef, ContentModel.ASPECT_TEMPORARY); - - // rename temp file to the new name - fileFolderService.rename(tempNodeRef, newName); - - // rename new file to old name - fileFolderService.rename(nodeToMoveRef, tempName); - this.nodeService.addAspect(nodeToMoveRef, ContentModel.ASPECT_TEMPORARY, null); - } - catch (org.alfresco.service.cmr.model.FileNotFoundException e) - { - throw new FileNotFoundException(e.getMessage()); - } - catch (org.alfresco.service.cmr.model.FileExistsException e) - { - throw new FileExistsException(e.getMessage()); - } - - if (!tempFileInfo.isFolder() && !fileToMoveInfo.isFolder()) - { - // swap the content between the two - ContentData oldContentData = tempFileInfo.getContentData(); - if (oldContentData == null) - { - String mimetype = mimetypeService.guessMimetype(tempName); - oldContentData = ContentData.setMimetype(null, mimetype); - } - - ContentData newContentData = fileToMoveInfo.getContentData(); - - // Reset the mime type - - String mimetype = mimetypeService.guessMimetype(newName); - newContentData = ContentData.setMimetype(newContentData, mimetype); - - nodeService.setProperty(tempNodeRef, ContentModel.PROP_CONTENT, newContentData); - nodeService.setProperty(nodeToMoveRef, ContentModel.PROP_CONTENT, oldContentData); - } - } - - /** - * Move a node - * - * @param nodeToMoveRef Node to be moved - * @param newParentNodeRef New parent folder node - * @param newName New name for the moved node - * @throws FileExistsException - */ - public void move(NodeRef nodeToMoveRef, NodeRef newParentNodeRef, String newName) throws FileExistsException - { - try - { - fileFolderService.move(nodeToMoveRef, newParentNodeRef, newName); - } - catch (org.alfresco.service.cmr.model.FileExistsException e) - { - throw new FileExistsException(newName); - } - catch (Throwable e) - { - throw new AlfrescoRuntimeException("Move failed: \n" + - " node to move: " + nodeToMoveRef + "\n" + - " new parent: " + newParentNodeRef + "\n" + - " new name: " + newName, - e); - } - } - - /** - * Rename a node - * - * @param nodeToRenameRef Node to be renamed - * @param newName New name for the node - * @throws FileExistsException - */ - public void rename(NodeRef nodeToRenameRef, String newName) throws FileExistsException - { - try - { - fileFolderService.rename(nodeToRenameRef, newName); - } - catch (org.alfresco.service.cmr.model.FileExistsException e) - { - throw new FileExistsException(newName); - } - catch (Throwable e) - { - throw new AlfrescoRuntimeException("Rename failed: \n" + - " node to rename: " + nodeToRenameRef + "\n" + - " new name: " + newName, - e); - } - } - - /** - * Return the file name for a node - * - * @param node NodeRef - * @return String - * @throws FileNotFoundException - */ - public String getFileName(NodeRef node) - { - String fname = null; - - try - { - fname = (String) nodeService.getProperty( node, ContentModel.PROP_NAME); - } - catch (InvalidNodeRefException ex) - { - } - - return fname; - } - - /** - * Check if the folder node is empty - * - * @param folderNode NodeRef - * @return boolean - */ - public boolean isFolderEmpty( NodeRef folderNode) { - - // Check if the node has any child files/folders - - List files = fileFolderService.listFiles( folderNode); - if ( files == null || files.size() == 0) - return true; - return false; - } -} +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" */ +package org.alfresco.filesys.repo; + +import java.io.FileNotFoundException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Stack; +import java.util.StringTokenizer; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.jlan.server.filesys.FileAttribute; +import org.alfresco.jlan.server.filesys.FileExistsException; +import org.alfresco.jlan.server.filesys.FileName; +import org.alfresco.jlan.util.WildCard; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.model.filefolder.FileFolderServiceImpl; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.InvalidNodeRefException; +import org.alfresco.service.cmr.repository.MimetypeService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.SearchLanguageConversion; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Class with supplying helper methods and potentially acting as a cache for + * queries. + * + * @author derekh + */ +public class CifsHelper +{ + // Logging + private static Log logger = LogFactory.getLog(CifsHelper.class); + + // Services + private DictionaryService dictionaryService; + private NodeService nodeService; + private FileFolderService fileFolderService; + private MimetypeService mimetypeService; + private PermissionService permissionService; + private boolean isReadOnly; + + // Mark locked files as offline + + private boolean lockedFilesAsOffline; + + /** + * Class constructor + */ + public CifsHelper() + { + isReadOnly = false; + } + + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setFileFolderService(FileFolderService fileFolderService) + { + this.fileFolderService = fileFolderService; + } + + public void setMimetypeService(MimetypeService mimetypeService) + { + this.mimetypeService = mimetypeService; + } + + public void setPermissionService(PermissionService permissionService) + { + this.permissionService = permissionService; + } + + /** + * @return Returns true if all files/folders should be treated as read-only + */ + public boolean isReadOnly() + { + return isReadOnly; + } + + /** + * Set whether the system allows files to be edited or not. The default is + * to allow writes. + * @param allowWrites true to allow writes, otherwise false for read-only mode + */ + public void setAllowWrites(boolean allowWrites) + { + this.isReadOnly = !allowWrites; + } + + /** + * Enable marking of locked files as offline + * + * @param ena boolean + */ + public final void setMarkLockedFilesAsOffline(boolean ena) + { + lockedFilesAsOffline = ena; + } + + /** + * Check if locked files should be marked as offline + * + * @return boolean + */ + public final boolean hasLockedFilesAsOffline() + { + return lockedFilesAsOffline; + } + + /** + * @param serviceRegistry for repo connection + * @param nodeRef + * @return Returns true if the node is a subtype of {@link ContentModel#TYPE_FOLDER folder} + * @throws AlfrescoRuntimeException if the type is neither related to a folder or content + */ + public boolean isDirectory(NodeRef nodeRef) + { + QName nodeTypeQName = nodeService.getType(nodeRef); + if (dictionaryService.isSubClass(nodeTypeQName, ContentModel.TYPE_FOLDER)) + { + return true; + } + else if (dictionaryService.isSubClass(nodeTypeQName, ContentModel.TYPE_CONTENT)) + { + return false; + } + else + { + // it is not a directory, but what is it? + return false; + } + } + + /** + * Extract a single node's file info, where the node is reference by + * a path relative to an ancestor node. + * + * @param pathRootNodeRef + * @param path + * @return Returns the existing node reference + * @throws FileNotFoundException + */ + public ContentFileInfo getFileInformation(NodeRef pathRootNodeRef, String path) throws FileNotFoundException + { + // get the node being referenced + NodeRef nodeRef = getNodeRef(pathRootNodeRef, path); + + return getFileInformation(nodeRef); + } + + /** + * Helper method to extract file info from a specific node. + *

+ * This method goes direct to the repo for all information and no data is + * cached here. + * + * @param nodeRef the node that the path is relative to + * @param path the path to get info for + * @return Returns the file information pertinent to the node + * @throws FileNotFoundException if the path refers to a non-existent file + */ + public ContentFileInfo getFileInformation(NodeRef nodeRef) throws FileNotFoundException + { + // get the file info + org.alfresco.service.cmr.model.FileInfo fileFolderInfo = fileFolderService.getFileInfo(nodeRef); + + // retrieve required properties and create file info + ContentFileInfo fileInfo = new ContentFileInfo(); + fileInfo.setNodeRef(nodeRef); + + // unset all attribute flags + int fileAttributes = 0; + fileInfo.setFileAttributes(fileAttributes); + + if (fileFolderInfo.isFolder()) + { + // add directory attribute + fileAttributes |= FileAttribute.Directory; + fileInfo.setFileAttributes(fileAttributes); + } + else + { + Map nodeProperties = fileFolderInfo.getProperties(); + + // Get the file size from the content + + ContentData contentData = (ContentData) nodeProperties.get(ContentModel.PROP_CONTENT); + long size = 0L; + if (contentData != null) + { + size = contentData.getSize(); + } + fileInfo.setSize(size); + + // Set the allocation size by rounding up the size to a 512 byte block boundary + + if ( size > 0) + fileInfo.setAllocationSize((size + 512L) & 0xFFFFFFFFFFFFFE00L); + + // Check the lock status of the file + + String lockTypeStr = (String) nodeProperties.get(ContentModel.PROP_LOCK_TYPE); + + if ( lockTypeStr != null ) + { + // File is locked so mark it as read-only and offline + + int attr = fileInfo.getFileAttributes(); + + if (( attr & FileAttribute.ReadOnly) == 0) + attr += FileAttribute.ReadOnly; + + if ( hasLockedFilesAsOffline()) + attr += FileAttribute.NTOffline; + + fileInfo.setFileAttributes( attr); + } + + // Check if it is a link node + + if ( fileFolderInfo.isLink()) + fileInfo.setLinkNodeRef( fileFolderInfo.getLinkNodeRef()); + } + + // created + Date createdDate = fileFolderInfo.getCreatedDate(); + if (createdDate != null) + { + long created = DefaultTypeConverter.INSTANCE.longValue(createdDate); + fileInfo.setCreationDateTime(created); + } + // modified + Date modifiedDate = fileFolderInfo.getModifiedDate(); + if (modifiedDate != null) + { + long modified = DefaultTypeConverter.INSTANCE.longValue(modifiedDate); + fileInfo.setModifyDateTime(modified); + fileInfo.setAccessDateTime(modified); + } + // name + String name = fileFolderInfo.getName(); + if (name != null) + { + fileInfo.setFileName(name); + } + + // Read/write access + + boolean deniedPermission = permissionService.hasPermission(nodeRef, PermissionService.WRITE) == AccessStatus.DENIED; + if (isReadOnly || deniedPermission) + { + int attr = fileInfo.getFileAttributes(); + if (( attr & FileAttribute.ReadOnly) == 0) + { + attr += FileAttribute.ReadOnly; + fileInfo.setFileAttributes(attr); + } + } + + // Set the normal file attribute if no other attributes are set + + if ( fileInfo.getFileAttributes() == 0) + fileInfo.setFileAttributes(FileAttribute.NTNormal); + + // Debug + + if (logger.isDebugEnabled()) + { + logger.debug("Fetched file info: \n" + + " info: " + fileInfo); + } + + // Return the file information + + return fileInfo; + } + + /** + * Creates a file or directory using the given paths. + *

+ * If the directory path doesn't exist, then all the parent directories will be created. + * If the file path is null, then the file will not be created + * + * @param rootNodeRef the root node of the path + * @param path the path to a node + * @param isFile true if the node to be created must be a file + * @return Returns a newly created file or folder node + * @throws FileExistsException if the file or folder already exists + */ + public NodeRef createNode(NodeRef rootNodeRef, String path, boolean isFile) throws FileExistsException + { + // split the path up into its constituents + StringTokenizer tokenizer = new StringTokenizer(path, FileName.DOS_SEPERATOR_STR, false); + List folderPathElements = new ArrayList(10); + String name = null; + while (tokenizer.hasMoreTokens()) + { + String pathElement = tokenizer.nextToken(); + + if (!tokenizer.hasMoreTokens()) + { + // the last token becomes the name + name = pathElement; + } + else + { + // add the path element to the parent folder path + folderPathElements.add(pathElement); + } + } + // ensure that the folder path exists + NodeRef parentFolderNodeRef = rootNodeRef; + if (folderPathElements.size() > 0) + { + parentFolderNodeRef = FileFolderServiceImpl.makeFolders( + fileFolderService, + rootNodeRef, + folderPathElements, + ContentModel.TYPE_FOLDER).getNodeRef(); + } + // add the file or folder + QName typeQName = isFile ? ContentModel.TYPE_CONTENT : ContentModel.TYPE_FOLDER; + try + { + NodeRef nodeRef = fileFolderService.create(parentFolderNodeRef, name, typeQName).getNodeRef(); + + // done + if (logger.isDebugEnabled()) + { + logger.debug("Created node: \n" + + " device root: " + rootNodeRef + "\n" + + " path: " + path + "\n" + + " is file: " + isFile + "\n" + + " new node: " + nodeRef); + } + return nodeRef; + } + catch (org.alfresco.service.cmr.model.FileExistsException e) + { + throw new FileExistsException(path); + } + } + + private void addDescendents(List pathRootNodeRefs, Stack pathElements, List results) + { + if (pathElements.isEmpty()) + { + // if this method is called with an empty path element stack, then the + // current context nodes are the results to be added + results.addAll(pathRootNodeRefs); + return; + } + + // take the first path element off the stack + String pathElement = pathElements.pop(); + + // iterate over each path root node + for (NodeRef pathRootNodeRef : pathRootNodeRefs) + { + // deal with cyclic relationships by not traversing down any node already in the results + if (results.contains(pathRootNodeRef)) + { + continue; + } + // get direct descendents along the path + List directDescendents = getDirectDescendents(pathRootNodeRef, pathElement); + // recurse onto the descendents + addDescendents(directDescendents, pathElements, results); + } + + // restore the path element stack + pathElements.push(pathElement); + } + + /** + * Searches for the node or nodes that match the path element for the given parent node + */ + private List getDirectDescendents(NodeRef pathRootNodeRef, String pathElement) + { + if (logger.isDebugEnabled()) + { + logger.debug("Getting direct descendents: \n" + + " Path Root: " + pathRootNodeRef + "\n" + + " Path Element: " + pathElement); + } + List results = null; + // if this contains no wildcards, then we can fasttrack it + if (!WildCard.containsWildcards(pathElement)) + { + // a specific name is required + NodeRef foundNodeRef = fileFolderService.searchSimple(pathRootNodeRef, pathElement); + if (foundNodeRef == null) + { + results = Collections.emptyList(); + } + else + { + results = Collections.singletonList(foundNodeRef); + } + } + else + { + // escape for the Lucene syntax search + String escapedPathElement = SearchLanguageConversion.convertCifsToLucene(pathElement); + // do the lookup + List childInfos = fileFolderService.search( + pathRootNodeRef, + escapedPathElement, + false); + // convert to noderefs + results = new ArrayList(childInfos.size()); + for (org.alfresco.service.cmr.model.FileInfo info : childInfos) + { + results.add(info.getNodeRef()); + } + } + // done + return results; + } + + /** + * Finds the nodes being reference by the given directory and file paths. + *

+ * Examples of the path are: + *

    + *
  • \New Folder\New Text Document.txt
  • + *
  • \New Folder\Sub Folder
  • + *
+ * + * @param searchRootNodeRef the node from which to start the path search + * @param path the search path to either a folder or file + * @return Returns references to all matching nodes + */ + public List getNodeRefs(NodeRef pathRootNodeRef, String path) + { + // tokenize the path and push into a stack in reverse order so that + // the root directory gets popped first + StringTokenizer tokenizer = new StringTokenizer(path, FileName.DOS_SEPERATOR_STR, false); + String[] tokens = new String[tokenizer.countTokens()]; + int count = 0; + while(tokenizer.hasMoreTokens()) + { + tokens[count] = tokenizer.nextToken(); + count++; + } + Stack pathElements = new Stack(); + for (int i = tokens.length - 1; i >= 0; i--) + { + pathElements.push(tokens[i]); + } + + // start with a single parent node + List pathRootNodeRefs = Collections.singletonList(pathRootNodeRef); + + // result storage + List results = new ArrayList(5); + + // kick off the path walking + addDescendents(pathRootNodeRefs, pathElements, results); + + // done + if (logger.isDebugEnabled()) + { + logger.debug("Retrieved node references for path: \n" + + " path root: " + pathRootNodeRef + "\n" + + " path: " + path + "\n" + + " results: " + results); + } + return results; + } + + /** + * Attempts to fetch a specific single node at the given path. + * + * @throws FileNotFoundException if the path can't be resolved to a node + * + * @see #getNodeRefs(NodeRef, String) + */ + public NodeRef getNodeRef(NodeRef pathRootNodeRef, String path) throws FileNotFoundException + { + // attempt to get the file/folder node using hierarchy walking + List nodeRefs = getNodeRefs(pathRootNodeRef, path); + if (nodeRefs.size() == 0) + { + throw new FileNotFoundException(path); + } + else if (nodeRefs.size() > 1) + { + logger.warn("Multiple matching nodes: \n" + + " search root: " + pathRootNodeRef + "\n" + + " path: " + path); + } + // take the first one - not sure if it is possible for the path to refer to more than one + NodeRef nodeRef = nodeRefs.get(0); + // done + return nodeRef; + } + + /** + * Relink the content data from a new node to an existing node to preserve the version history. + * + * @param oldNodeRef NodeRef + * @param newNodeRef NodeRef + */ + public void relinkNode(NodeRef tempNodeRef, NodeRef nodeToMoveRef, NodeRef newParentNodeRef, String newName) + throws FileNotFoundException, FileExistsException + { + // Get the properties for the old and new nodes + org.alfresco.service.cmr.model.FileInfo tempFileInfo = fileFolderService.getFileInfo(tempNodeRef); + org.alfresco.service.cmr.model.FileInfo fileToMoveInfo = fileFolderService.getFileInfo(nodeToMoveRef); + + // Save the current name of the old node + String tempName = tempFileInfo.getName(); + + try + { + // Rename operation will add or remove the sys:temporary aspect appropriately + + // rename temp file to the new name + fileFolderService.rename(tempNodeRef, newName); + + // rename new file to old name + fileFolderService.rename(nodeToMoveRef, tempName); + } + catch (org.alfresco.service.cmr.model.FileNotFoundException e) + { + throw new FileNotFoundException(e.getMessage()); + } + catch (org.alfresco.service.cmr.model.FileExistsException e) + { + throw new FileExistsException(e.getMessage()); + } + + if (!tempFileInfo.isFolder() && !fileToMoveInfo.isFolder()) + { + // swap the content between the two + ContentData oldContentData = tempFileInfo.getContentData(); + if (oldContentData == null) + { + String mimetype = mimetypeService.guessMimetype(tempName); + oldContentData = ContentData.setMimetype(null, mimetype); + } + + ContentData newContentData = fileToMoveInfo.getContentData(); + + // Reset the mime type + + String mimetype = mimetypeService.guessMimetype(newName); + newContentData = ContentData.setMimetype(newContentData, mimetype); + + nodeService.setProperty(tempNodeRef, ContentModel.PROP_CONTENT, newContentData); + nodeService.setProperty(nodeToMoveRef, ContentModel.PROP_CONTENT, oldContentData); + } + } + + /** + * Move a node + * + * @param nodeToMoveRef Node to be moved + * @param newParentNodeRef New parent folder node + * @param newName New name for the moved node + * @throws FileExistsException + */ + public void move(NodeRef nodeToMoveRef, NodeRef newParentNodeRef, String newName) throws FileExistsException + { + try + { + fileFolderService.move(nodeToMoveRef, newParentNodeRef, newName); + } + catch (org.alfresco.service.cmr.model.FileExistsException e) + { + throw new FileExistsException(newName); + } + catch (Throwable e) + { + throw new AlfrescoRuntimeException("Move failed: \n" + + " node to move: " + nodeToMoveRef + "\n" + + " new parent: " + newParentNodeRef + "\n" + + " new name: " + newName, + e); + } + } + + /** + * Rename a node + * + * @param nodeToRenameRef Node to be renamed + * @param newName New name for the node + * @throws FileExistsException + */ + public void rename(NodeRef nodeToRenameRef, String newName) throws FileExistsException + { + try + { + fileFolderService.rename(nodeToRenameRef, newName); + } + catch (org.alfresco.service.cmr.model.FileExistsException e) + { + throw new FileExistsException(newName); + } + catch (Throwable e) + { + throw new AlfrescoRuntimeException("Rename failed: \n" + + " node to rename: " + nodeToRenameRef + "\n" + + " new name: " + newName, + e); + } + } + + /** + * Return the file name for a node + * + * @param node NodeRef + * @return String + * @throws FileNotFoundException + */ + public String getFileName(NodeRef node) + { + String fname = null; + + try + { + fname = (String) nodeService.getProperty( node, ContentModel.PROP_NAME); + } + catch (InvalidNodeRefException ex) + { + } + + return fname; + } + + /** + * Check if the folder node is empty + * + * @param folderNode NodeRef + * @return boolean + */ + public boolean isFolderEmpty( NodeRef folderNode) { + + // Check if the node has any child files/folders + + List files = fileFolderService.listFiles( folderNode); + if ( files == null || files.size() == 0) + return true; + return false; + } +} diff --git a/source/java/org/alfresco/repo/admin/patch/impl/AVMPermissionsPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/AVMPermissionsPatch.java index d317444504..fd4f4b99ed 100644 --- a/source/java/org/alfresco/repo/admin/patch/impl/AVMPermissionsPatch.java +++ b/source/java/org/alfresco/repo/admin/patch/impl/AVMPermissionsPatch.java @@ -41,34 +41,32 @@ public class AVMPermissionsPatch extends AbstractPatch { private static final String MSG_SUCCESS = "patch.updateAvmPermissions.result"; - + private AccessControlListDAO accessControlListDao; private AclDaoComponentImpl aclDaoComponent; - + @Override protected String applyInternal() throws Exception { Thread progressThread = null; if (aclDaoComponent.supportsProgressTracking()) { - Long toDo = aclDaoComponent.getAVMHeadNodeCount(); - Long maxId = aclDaoComponent.getMaxAclId(); - - progressThread = new Thread(new ProgressWatcher(toDo, maxId), "WCMPactchProgressWatcher"); + progressThread = new Thread(new ProgressWatcher(), "WCMPactchProgressWatcher"); progressThread.start(); } - - Map summary = accessControlListDao.patchAcls(); - + + Map summary = this.accessControlListDao.patchAcls(); + if (progressThread != null) { progressThread.interrupt(); progressThread.join(); } - + // build the result message - String msg = I18NUtil.getMessage(MSG_SUCCESS, summary.get(ACLType.DEFINING), summary.get(ACLType.LAYERED)); + String msg = I18NUtil.getMessage(AVMPermissionsPatch.MSG_SUCCESS, summary.get(ACLType.DEFINING), summary + .get(ACLType.LAYERED)); // done return msg; } @@ -82,8 +80,7 @@ public class AVMPermissionsPatch extends AbstractPatch { this.aclDaoComponent = aclDaoComponent; } - - + private class ProgressWatcher implements Runnable { private boolean running = true; @@ -92,15 +89,9 @@ public class AVMPermissionsPatch extends AbstractPatch Long max; - ProgressWatcher(Long toDo, Long max) - { - this.toDo = toDo; - this.max = max; - } - public void run() { - while (running) + while (this.running) { try { @@ -108,27 +99,35 @@ public class AVMPermissionsPatch extends AbstractPatch } catch (InterruptedException e) { - running = false; + this.running = false; } - if (running) + if (this.running) { - RetryingTransactionHelper txHelper = transactionService.getRetryingTransactionHelper(); + RetryingTransactionHelper txHelper = AVMPermissionsPatch.this.transactionService + .getRetryingTransactionHelper(); txHelper.setMaxRetries(1); Long done = txHelper.doInTransaction(new RetryingTransactionCallback() { public Long execute() throws Throwable { - return aclDaoComponent.getAVMNodeCountWithNewACLS(max); + if (ProgressWatcher.this.toDo == null) + { + ProgressWatcher.this.toDo = AVMPermissionsPatch.this.aclDaoComponent + .getAVMHeadNodeCount(); + ProgressWatcher.this.max = AVMPermissionsPatch.this.aclDaoComponent.getMaxAclId(); + } + return AVMPermissionsPatch.this.aclDaoComponent + .getAVMNodeCountWithNewACLS(ProgressWatcher.this.max); } }, true, true); - reportProgress(toDo, done); + reportProgress(this.toDo, done); } } } } - + } diff --git a/source/java/org/alfresco/repo/admin/patch/impl/DmPermissionsPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/DmPermissionsPatch.java index 4497cb89e7..3ab5171b4d 100644 --- a/source/java/org/alfresco/repo/admin/patch/impl/DmPermissionsPatch.java +++ b/source/java/org/alfresco/repo/admin/patch/impl/DmPermissionsPatch.java @@ -41,34 +41,31 @@ public class DmPermissionsPatch extends AbstractPatch { private static final String MSG_SUCCESS = "patch.updateDmPermissions.result"; - + private AccessControlListDAO accessControlListDao; private AclDaoComponentImpl aclDaoComponent; - + @Override protected String applyInternal() throws Exception { Thread progressThread = null; - if (aclDaoComponent.supportsProgressTracking()) + if (this.aclDaoComponent.supportsProgressTracking()) { - Long toDo = aclDaoComponent.getDmNodeCount(); - Long maxId = aclDaoComponent.getMaxAclId(); - - progressThread = new Thread(new ProgressWatcher(toDo, maxId), "DMPatchProgressWatcher"); + progressThread = new Thread(new ProgressWatcher(), "DMPatchProgressWatcher"); progressThread.start(); } - - Map summary = accessControlListDao.patchAcls(); - + + Map summary = this.accessControlListDao.patchAcls(); + if (progressThread != null) { progressThread.interrupt(); progressThread.join(); } - + // build the result message - String msg = I18NUtil.getMessage(MSG_SUCCESS, summary.get(ACLType.DEFINING)); + String msg = I18NUtil.getMessage(DmPermissionsPatch.MSG_SUCCESS, summary.get(ACLType.DEFINING)); // done return msg; } @@ -85,14 +82,14 @@ public class DmPermissionsPatch extends AbstractPatch /** * Set the acl dao component + * * @param aclDaoComponent */ public void setAclDaoComponent(AclDaoComponentImpl aclDaoComponent) { this.aclDaoComponent = aclDaoComponent; } - - + private class ProgressWatcher implements Runnable { private boolean running = true; @@ -101,15 +98,9 @@ public class DmPermissionsPatch extends AbstractPatch Long max; - ProgressWatcher(Long toDo, Long max) - { - this.toDo = toDo; - this.max = max; - } - public void run() { - while (running) + while (this.running) { try { @@ -117,27 +108,35 @@ public class DmPermissionsPatch extends AbstractPatch } catch (InterruptedException e) { - running = false; + this.running = false; } - if (running) + if (this.running) { - RetryingTransactionHelper txHelper = transactionService.getRetryingTransactionHelper(); + RetryingTransactionHelper txHelper = DmPermissionsPatch.this.transactionService + .getRetryingTransactionHelper(); txHelper.setMaxRetries(1); Long done = txHelper.doInTransaction(new RetryingTransactionCallback() { public Long execute() throws Throwable { - return aclDaoComponent.getDmNodeCountWithNewACLS(max); + if (ProgressWatcher.this.toDo == null) + { + ProgressWatcher.this.toDo = DmPermissionsPatch.this.aclDaoComponent + .getDmNodeCount(); + ProgressWatcher.this.max = DmPermissionsPatch.this.aclDaoComponent.getMaxAclId(); + } + return DmPermissionsPatch.this.aclDaoComponent + .getDmNodeCountWithNewACLS(ProgressWatcher.this.max); } }, true, true); - reportProgress(toDo, done); + reportProgress(this.toDo, done); } } } } - + } diff --git a/source/java/org/alfresco/repo/admin/patch/impl/MoveWCMToGroupBasedPermissionsPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/MoveWCMToGroupBasedPermissionsPatch.java index c54fa710f3..df9066c77b 100644 --- a/source/java/org/alfresco/repo/admin/patch/impl/MoveWCMToGroupBasedPermissionsPatch.java +++ b/source/java/org/alfresco/repo/admin/patch/impl/MoveWCMToGroupBasedPermissionsPatch.java @@ -24,33 +24,25 @@ */ package org.alfresco.repo.admin.patch.impl; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import net.sf.acegisecurity.providers.jaas.AuthorityGranter; - import org.alfresco.i18n.I18NUtil; import org.alfresco.model.WCMAppModel; import org.alfresco.repo.admin.patch.AbstractPatch; import org.alfresco.repo.avm.AVMNodeConverter; -import org.alfresco.repo.avm.AVMRepository; import org.alfresco.repo.domain.PropertyValue; import org.alfresco.repo.domain.hibernate.AclDaoComponentImpl; import org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptor; import org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptor.StoreType; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; -import org.alfresco.service.cmr.avm.AVMNodeDescriptor; import org.alfresco.service.cmr.avm.AVMService; import org.alfresco.service.cmr.avm.AVMStoreDescriptor; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.security.AccessPermission; import org.alfresco.service.cmr.security.AuthorityService; @@ -58,7 +50,6 @@ import org.alfresco.service.cmr.security.AuthorityType; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; -import org.alfresco.service.transaction.TransactionService; /** * Remove ACLs on all but staging area stores On staging area stores, set ACls according to the users and roles as set @@ -68,8 +59,11 @@ import org.alfresco.service.transaction.TransactionService; */ public class MoveWCMToGroupBasedPermissionsPatch extends AbstractPatch { - public static final String[] PERMISSIONS = new String[] { PermissionService.WCM_CONTENT_MANAGER, PermissionService.WCM_CONTENT_PUBLISHER, - PermissionService.WCM_CONTENT_CONTRIBUTOR, PermissionService.WCM_CONTENT_REVIEWER }; + public static final String[] PERMISSIONS = new String[] + { + PermissionService.WCM_CONTENT_MANAGER, PermissionService.WCM_CONTENT_PUBLISHER, + PermissionService.WCM_CONTENT_CONTRIBUTOR, PermissionService.WCM_CONTENT_REVIEWER + }; private static final String MSG_SUCCESS = "patch.moveWCMToGroupBasedPermissionsPatch.result"; @@ -82,7 +76,7 @@ public class MoveWCMToGroupBasedPermissionsPatch extends AbstractPatch AclDaoComponentImpl aclDaoComponent; AuthorityService authorityService; - + String replaceAllWith = PermissionService.WCM_CONTENT_MANAGER; public void setAvmService(AVMService avmService) @@ -90,7 +84,8 @@ public class MoveWCMToGroupBasedPermissionsPatch extends AbstractPatch this.avmService = avmService; } - public void setAvmSnapShotTriggeredIndexingMethodInterceptor(AVMSnapShotTriggeredIndexingMethodInterceptor avmSnapShotTriggeredIndexingMethodInterceptor) + public void setAvmSnapShotTriggeredIndexingMethodInterceptor( + AVMSnapShotTriggeredIndexingMethodInterceptor avmSnapShotTriggeredIndexingMethodInterceptor) { this.avmSnapShotTriggeredIndexingMethodInterceptor = avmSnapShotTriggeredIndexingMethodInterceptor; } @@ -110,8 +105,6 @@ public class MoveWCMToGroupBasedPermissionsPatch extends AbstractPatch this.authorityService = authorityService; } - - public void setReplaceAllWith(String replaceAllWith) { this.replaceAllWith = replaceAllWith; @@ -121,20 +114,17 @@ public class MoveWCMToGroupBasedPermissionsPatch extends AbstractPatch protected String applyInternal() throws Exception { Thread progressThread = null; - if (aclDaoComponent.supportsProgressTracking()) + if (this.aclDaoComponent.supportsProgressTracking()) { - Long toDo = aclDaoComponent.getAVMHeadNodeCount(); - Long maxId = aclDaoComponent.getMaxAclId(); - - progressThread = new Thread(new ProgressWatcher(toDo, maxId), "WCMPactchProgressWatcher"); + progressThread = new Thread(new ProgressWatcher(), "WCMPactchProgressWatcher"); progressThread.start(); } - List stores = avmService.getStores(); + List stores = this.avmService.getStores(); for (AVMStoreDescriptor store : stores) { - Map storeProperties = avmService.getStoreProperties(store.getName()); + Map storeProperties = this.avmService.getStoreProperties(store.getName()); switch (StoreType.getStoreType(store.getName(), store, storeProperties)) { @@ -174,14 +164,14 @@ public class MoveWCMToGroupBasedPermissionsPatch extends AbstractPatch } // build the result message - String msg = I18NUtil.getMessage(MSG_SUCCESS); + String msg = I18NUtil.getMessage(MoveWCMToGroupBasedPermissionsPatch.MSG_SUCCESS); // done return msg; } private boolean isPermissionSet(NodeRef nodeRef, String authority, String permission) { - Set set = permissionService.getAllSetPermissions(nodeRef); + Set set = this.permissionService.getAllSetPermissions(nodeRef); for (AccessPermission ap : set) { if (ap.getAuthority().equals(authority) && ap.isSetDirectly() && ap.getPermission().equals(permission)) @@ -194,7 +184,7 @@ public class MoveWCMToGroupBasedPermissionsPatch extends AbstractPatch private boolean isMaskSet(StoreRef storeRef, String authority, String permission) { - Set set = permissionService.getAllSetPermissions(storeRef); + Set set = this.permissionService.getAllSetPermissions(storeRef); for (AccessPermission ap : set) { if (ap.getAuthority().equals(authority) && ap.isSetDirectly() && ap.getPermission().equals(permission)) @@ -207,14 +197,14 @@ public class MoveWCMToGroupBasedPermissionsPatch extends AbstractPatch private void makeGroupsIfRequired(String stagingStoreName, NodeRef dirRef) { - for (String permission : PERMISSIONS) + for (String permission : MoveWCMToGroupBasedPermissionsPatch.PERMISSIONS) { String shortName = stagingStoreName + "-" + permission; - String group = authorityService.getName(AuthorityType.GROUP, shortName); - if (!authorityService.authorityExists(group)) + String group = this.authorityService.getName(AuthorityType.GROUP, shortName); + if (!this.authorityService.authorityExists(group)) { - String newGroup = authorityService.createAuthority(AuthorityType.GROUP, null, shortName); - permissionService.setPermission(dirRef, newGroup, permission, true); + String newGroup = this.authorityService.createAuthority(AuthorityType.GROUP, null, shortName); + this.permissionService.setPermission(dirRef, newGroup, permission, true); } } } @@ -222,11 +212,11 @@ public class MoveWCMToGroupBasedPermissionsPatch extends AbstractPatch private void addToGroupIfRequired(String stagingStoreName, String user, String permission) { String shortName = stagingStoreName + "-" + permission; - String group = authorityService.getName(AuthorityType.GROUP, shortName); - Set members = authorityService.getContainedAuthorities(AuthorityType.USER, group, true); + String group = this.authorityService.getName(AuthorityType.GROUP, shortName); + Set members = this.authorityService.getContainedAuthorities(AuthorityType.USER, group, true); if (!members.contains(user)) { - authorityService.addAuthority(group, user); + this.authorityService.addAuthority(group, user); } } @@ -246,23 +236,24 @@ public class MoveWCMToGroupBasedPermissionsPatch extends AbstractPatch { QName propQName = QName.createQName(null, ".web_project.noderef"); - PropertyValue pValue = avmService.getStoreProperty(stagingStoreName, propQName); + PropertyValue pValue = this.avmService.getStoreProperty(stagingStoreName, propQName); if (pValue != null) { NodeRef webProjectNodeRef = (NodeRef) pValue.getValue(DataTypeDefinition.NODE_REF); // Apply sepcific user permissions as set on the web project - List userInfoRefs = nodeService.getChildAssocs(webProjectNodeRef, WCMAppModel.ASSOC_WEBUSER, RegexQNamePattern.MATCH_ALL); + List userInfoRefs = this.nodeService.getChildAssocs(webProjectNodeRef, + WCMAppModel.ASSOC_WEBUSER, RegexQNamePattern.MATCH_ALL); for (ChildAssociationRef ref : userInfoRefs) { NodeRef userInfoRef = ref.getChildRef(); - String username = (String) nodeService.getProperty(userInfoRef, WCMAppModel.PROP_WEBUSERNAME); - String userrole = (String) nodeService.getProperty(userInfoRef, WCMAppModel.PROP_WEBUSERROLE); + String username = (String) this.nodeService.getProperty(userInfoRef, WCMAppModel.PROP_WEBUSERNAME); + String userrole = (String) this.nodeService.getProperty(userInfoRef, WCMAppModel.PROP_WEBUSERROLE); if (userrole.equals(PermissionService.ALL_PERMISSIONS)) { - nodeService.setProperty(userInfoRef, WCMAppModel.PROP_WEBUSERROLE, replaceAllWith); + this.nodeService.setProperty(userInfoRef, WCMAppModel.PROP_WEBUSERROLE, this.replaceAllWith); } } @@ -279,35 +270,37 @@ public class MoveWCMToGroupBasedPermissionsPatch extends AbstractPatch if (!isPermissionSet(dirRef, PermissionService.ALL_AUTHORITIES, PermissionService.READ)) { - permissionService.setPermission(dirRef, PermissionService.ALL_AUTHORITIES, PermissionService.READ, true); + this.permissionService.setPermission(dirRef, PermissionService.ALL_AUTHORITIES, PermissionService.READ, + true); } // Add group permissions - for (String permission : PERMISSIONS) + for (String permission : MoveWCMToGroupBasedPermissionsPatch.PERMISSIONS) { - String cms = authorityService.getName(AuthorityType.GROUP, store.getName() + "-" + permission); - permissionService.setPermission(dirRef, cms, permission, true); + String cms = this.authorityService.getName(AuthorityType.GROUP, store.getName() + "-" + permission); + this.permissionService.setPermission(dirRef, cms, permission, true); } - PropertyValue pValue = avmService.getStoreProperty(store.getName(), propQName); + PropertyValue pValue = this.avmService.getStoreProperty(store.getName(), propQName); if (pValue != null) { NodeRef webProjectNodeRef = (NodeRef) pValue.getValue(DataTypeDefinition.NODE_REF); // Apply sepcific user permissions as set on the web project - List userInfoRefs = nodeService.getChildAssocs(webProjectNodeRef, WCMAppModel.ASSOC_WEBUSER, RegexQNamePattern.MATCH_ALL); + List userInfoRefs = this.nodeService.getChildAssocs(webProjectNodeRef, + WCMAppModel.ASSOC_WEBUSER, RegexQNamePattern.MATCH_ALL); for (ChildAssociationRef ref : userInfoRefs) { NodeRef userInfoRef = ref.getChildRef(); - String username = (String) nodeService.getProperty(userInfoRef, WCMAppModel.PROP_WEBUSERNAME); - String userrole = (String) nodeService.getProperty(userInfoRef, WCMAppModel.PROP_WEBUSERROLE); + String username = (String) this.nodeService.getProperty(userInfoRef, WCMAppModel.PROP_WEBUSERNAME); + String userrole = (String) this.nodeService.getProperty(userInfoRef, WCMAppModel.PROP_WEBUSERROLE); // remove existing if (isPermissionSet(dirRef, username, userrole)) { - permissionService.deletePermission(dirRef, username, userrole); + this.permissionService.deletePermission(dirRef, username, userrole); } addToGroupIfRequired(store.getName(), username, userrole); @@ -322,35 +315,38 @@ public class MoveWCMToGroupBasedPermissionsPatch extends AbstractPatch if (!isMaskSet(dirRef.getStoreRef(), PermissionService.ALL_AUTHORITIES, PermissionService.READ)) { - permissionService.setPermission(dirRef.getStoreRef(), PermissionService.ALL_AUTHORITIES, PermissionService.READ, true); + this.permissionService.setPermission(dirRef.getStoreRef(), PermissionService.ALL_AUTHORITIES, + PermissionService.READ, true); } - String cms = authorityService.getName(AuthorityType.GROUP, store.getName() + "-" + PermissionService.WCM_CONTENT_MANAGER); + String cms = this.authorityService.getName(AuthorityType.GROUP, store.getName() + "-" + + PermissionService.WCM_CONTENT_MANAGER); if (!isMaskSet(dirRef.getStoreRef(), cms, PermissionService.CHANGE_PERMISSIONS)) { - permissionService.setPermission(dirRef.getStoreRef(), cms, PermissionService.CHANGE_PERMISSIONS, true); + this.permissionService.setPermission(dirRef.getStoreRef(), cms, PermissionService.CHANGE_PERMISSIONS, true); } if (!isMaskSet(dirRef.getStoreRef(), cms, PermissionService.READ_PERMISSIONS)) { - permissionService.setPermission(dirRef.getStoreRef(), cms, PermissionService.READ_PERMISSIONS, true); + this.permissionService.setPermission(dirRef.getStoreRef(), cms, PermissionService.READ_PERMISSIONS, true); } QName propQName = QName.createQName(null, ".web_project.noderef"); - PropertyValue pValue = avmService.getStoreProperty(store.getName(), propQName); + PropertyValue pValue = this.avmService.getStoreProperty(store.getName(), propQName); if (pValue != null) { NodeRef webProjectNodeRef = (NodeRef) pValue.getValue(DataTypeDefinition.NODE_REF); // Apply sepcific user permissions as set on the web project - List userInfoRefs = nodeService.getChildAssocs(webProjectNodeRef, WCMAppModel.ASSOC_WEBUSER, RegexQNamePattern.MATCH_ALL); + List userInfoRefs = this.nodeService.getChildAssocs(webProjectNodeRef, + WCMAppModel.ASSOC_WEBUSER, RegexQNamePattern.MATCH_ALL); for (ChildAssociationRef ref : userInfoRefs) { NodeRef userInfoRef = ref.getChildRef(); - String username = (String) nodeService.getProperty(userInfoRef, WCMAppModel.PROP_WEBUSERNAME); - String userrole = (String) nodeService.getProperty(userInfoRef, WCMAppModel.PROP_WEBUSERROLE); + String username = (String) this.nodeService.getProperty(userInfoRef, WCMAppModel.PROP_WEBUSERNAME); + String userrole = (String) this.nodeService.getProperty(userInfoRef, WCMAppModel.PROP_WEBUSERROLE); if (userrole.equals(PermissionService.WCM_CONTENT_MANAGER)) { @@ -358,12 +354,14 @@ public class MoveWCMToGroupBasedPermissionsPatch extends AbstractPatch if (isMaskSet(dirRef.getStoreRef(), username, PermissionService.CHANGE_PERMISSIONS)) { - permissionService.deletePermission(dirRef.getStoreRef(), username, PermissionService.CHANGE_PERMISSIONS); + this.permissionService.deletePermission(dirRef.getStoreRef(), username, + PermissionService.CHANGE_PERMISSIONS); } if (isMaskSet(dirRef.getStoreRef(), username, PermissionService.READ_PERMISSIONS)) { - permissionService.deletePermission(dirRef.getStoreRef(), username, PermissionService.READ_PERMISSIONS); + this.permissionService.deletePermission(dirRef.getStoreRef(), username, + PermissionService.READ_PERMISSIONS); } } } @@ -382,18 +380,21 @@ public class MoveWCMToGroupBasedPermissionsPatch extends AbstractPatch NodeRef dirRef = AVMNodeConverter.ToNodeRef(-1, sandBoxStore.getName() + ":/www"); - Map woof = avmService.getStoreProperties(stagingAreaName); - PropertyValue pValue = avmService.getStoreProperty(stagingAreaName, propQName); + Map woof = this.avmService.getStoreProperties(stagingAreaName); + PropertyValue pValue = this.avmService.getStoreProperty(stagingAreaName, propQName); if (!isMaskSet(dirRef.getStoreRef(), PermissionService.ALL_AUTHORITIES, PermissionService.READ)) { - permissionService.setPermission(dirRef.getStoreRef(), PermissionService.ALL_AUTHORITIES, PermissionService.READ, true); + this.permissionService.setPermission(dirRef.getStoreRef(), PermissionService.ALL_AUTHORITIES, + PermissionService.READ, true); } - String cms = authorityService.getName(AuthorityType.GROUP, stagingAreaName + "-" + PermissionService.WCM_CONTENT_MANAGER); + String cms = this.authorityService.getName(AuthorityType.GROUP, stagingAreaName + "-" + + PermissionService.WCM_CONTENT_MANAGER); if (!isMaskSet(dirRef.getStoreRef(), cms, PermissionService.WCM_CONTENT_MANAGER)) { - permissionService.setPermission(dirRef.getStoreRef(), cms, PermissionService.WCM_CONTENT_MANAGER, true); + this.permissionService + .setPermission(dirRef.getStoreRef(), cms, PermissionService.WCM_CONTENT_MANAGER, true); } if (pValue != null) @@ -401,22 +402,24 @@ public class MoveWCMToGroupBasedPermissionsPatch extends AbstractPatch NodeRef webProjectNodeRef = (NodeRef) pValue.getValue(DataTypeDefinition.NODE_REF); // Apply sepcific user permissions as set on the web project - List userInfoRefs = nodeService.getChildAssocs(webProjectNodeRef, WCMAppModel.ASSOC_WEBUSER, RegexQNamePattern.MATCH_ALL); + List userInfoRefs = this.nodeService.getChildAssocs(webProjectNodeRef, + WCMAppModel.ASSOC_WEBUSER, RegexQNamePattern.MATCH_ALL); for (ChildAssociationRef ref : userInfoRefs) { NodeRef userInfoRef = ref.getChildRef(); - String username = (String) nodeService.getProperty(userInfoRef, WCMAppModel.PROP_WEBUSERNAME); - String userrole = (String) nodeService.getProperty(userInfoRef, WCMAppModel.PROP_WEBUSERROLE); + String username = (String) this.nodeService.getProperty(userInfoRef, WCMAppModel.PROP_WEBUSERNAME); + String userrole = (String) this.nodeService.getProperty(userInfoRef, WCMAppModel.PROP_WEBUSERROLE); if (username.equals(owner)) { - permissionService.setPermission(dirRef.getStoreRef(), username, PermissionService.ALL_PERMISSIONS, true); + this.permissionService.setPermission(dirRef.getStoreRef(), username, + PermissionService.ALL_PERMISSIONS, true); } else if (userrole.equals("ContentManager")) { if (isMaskSet(dirRef.getStoreRef(), username, userrole)) { - permissionService.deletePermission(dirRef.getStoreRef(), username, userrole); + this.permissionService.deletePermission(dirRef.getStoreRef(), username, userrole); } } } @@ -456,15 +459,13 @@ public class MoveWCMToGroupBasedPermissionsPatch extends AbstractPatch Long max; - ProgressWatcher(Long toDo, Long max) + ProgressWatcher() { - this.toDo = toDo; - this.max = max; } public void run() { - while (running) + while (this.running) { try { @@ -472,23 +473,32 @@ public class MoveWCMToGroupBasedPermissionsPatch extends AbstractPatch } catch (InterruptedException e) { - running = false; + this.running = false; } - if (running) + if (this.running) { - RetryingTransactionHelper txHelper = transactionService.getRetryingTransactionHelper(); + RetryingTransactionHelper txHelper = MoveWCMToGroupBasedPermissionsPatch.this.transactionService + .getRetryingTransactionHelper(); txHelper.setMaxRetries(1); Long done = txHelper.doInTransaction(new RetryingTransactionCallback() { public Long execute() throws Throwable { - return aclDaoComponent.getAVMNodeCountWithNewACLS(max); + if (ProgressWatcher.this.toDo == null) + { + ProgressWatcher.this.toDo = MoveWCMToGroupBasedPermissionsPatch.this.aclDaoComponent + .getAVMHeadNodeCount(); + ProgressWatcher.this.max = MoveWCMToGroupBasedPermissionsPatch.this.aclDaoComponent + .getMaxAclId(); + } + return MoveWCMToGroupBasedPermissionsPatch.this.aclDaoComponent + .getAVMNodeCountWithNewACLS(org.alfresco.repo.admin.patch.impl.MoveWCMToGroupBasedPermissionsPatch.ProgressWatcher.this.max); } }, true, true); - reportProgress(toDo, done); + reportProgress(this.toDo, done); } } } diff --git a/source/java/org/alfresco/repo/admin/patch/impl/WCMPermissionPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/WCMPermissionPatch.java index f37c4ed840..cd2fc774e2 100644 --- a/source/java/org/alfresco/repo/admin/patch/impl/WCMPermissionPatch.java +++ b/source/java/org/alfresco/repo/admin/patch/impl/WCMPermissionPatch.java @@ -44,11 +44,9 @@ import org.alfresco.service.cmr.avm.AVMStoreDescriptor; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; -import org.alfresco.service.transaction.TransactionService; /** * Remove ACLs on all but staging area stores On staging area stores, set ACls according to the users and roles as set @@ -73,7 +71,8 @@ public class WCMPermissionPatch extends AbstractPatch this.avmService = avmService; } - public void setAvmSnapShotTriggeredIndexingMethodInterceptor(AVMSnapShotTriggeredIndexingMethodInterceptor avmSnapShotTriggeredIndexingMethodInterceptor) + public void setAvmSnapShotTriggeredIndexingMethodInterceptor( + AVMSnapShotTriggeredIndexingMethodInterceptor avmSnapShotTriggeredIndexingMethodInterceptor) { this.avmSnapShotTriggeredIndexingMethodInterceptor = avmSnapShotTriggeredIndexingMethodInterceptor; } @@ -92,19 +91,16 @@ public class WCMPermissionPatch extends AbstractPatch protected String applyInternal() throws Exception { Thread progressThread = null; - if (aclDaoComponent.supportsProgressTracking()) + if (this.aclDaoComponent.supportsProgressTracking()) { - Long toDo = aclDaoComponent.getAVMHeadNodeCount(); - Long maxId = aclDaoComponent.getMaxAclId(); - - progressThread = new Thread(new ProgressWatcher(toDo, maxId), "WCMPactchProgressWatcher"); + progressThread = new Thread(new ProgressWatcher(), "WCMPactchProgressWatcher"); progressThread.start(); } - List stores = avmService.getStores(); + List stores = this.avmService.getStores(); for (AVMStoreDescriptor store : stores) { - Map storeProperties = avmService.getStoreProperties(store.getName()); + Map storeProperties = this.avmService.getStoreProperties(store.getName()); switch (StoreType.getStoreType(store.getName(), store, storeProperties)) { @@ -145,20 +141,20 @@ public class WCMPermissionPatch extends AbstractPatch } // build the result message - String msg = I18NUtil.getMessage(MSG_SUCCESS); + String msg = I18NUtil.getMessage(WCMPermissionPatch.MSG_SUCCESS); // done return msg; } private void clearPermissions(AVMStoreDescriptor store) { - AVMNodeDescriptor www = avmService.lookup(-1, store.getName() + ":/www"); + AVMNodeDescriptor www = this.avmService.lookup(-1, store.getName() + ":/www"); if (www.isLayeredDirectory() && www.isPrimary()) { // throw away any acl AVMRepository.GetInstance().setACL(store.getName() + ":/www", null); // build the default layer acl - avmService.retargetLayeredDirectory(store.getName() + ":/www", www.getIndirection()); + this.avmService.retargetLayeredDirectory(store.getName() + ":/www", www.getIndirection()); } } @@ -167,23 +163,24 @@ public class WCMPermissionPatch extends AbstractPatch QName propQName = QName.createQName(null, ".web_project.noderef"); NodeRef dirRef = AVMNodeConverter.ToNodeRef(-1, store.getName() + ":/www"); - permissionService.setPermission(dirRef, PermissionService.ALL_AUTHORITIES, PermissionService.READ, true); + this.permissionService.setPermission(dirRef, PermissionService.ALL_AUTHORITIES, PermissionService.READ, true); - PropertyValue pValue = avmService.getStoreProperty(store.getName(), propQName); + PropertyValue pValue = this.avmService.getStoreProperty(store.getName(), propQName); if (pValue != null) { NodeRef webProjectNodeRef = (NodeRef) pValue.getValue(DataTypeDefinition.NODE_REF); // Apply sepcific user permissions as set on the web project - List userInfoRefs = nodeService.getChildAssocs(webProjectNodeRef, WCMAppModel.ASSOC_WEBUSER, RegexQNamePattern.MATCH_ALL); + List userInfoRefs = this.nodeService.getChildAssocs(webProjectNodeRef, + WCMAppModel.ASSOC_WEBUSER, RegexQNamePattern.MATCH_ALL); for (ChildAssociationRef ref : userInfoRefs) { NodeRef userInfoRef = ref.getChildRef(); - String username = (String) nodeService.getProperty(userInfoRef, WCMAppModel.PROP_WEBUSERNAME); - String userrole = (String) nodeService.getProperty(userInfoRef, WCMAppModel.PROP_WEBUSERROLE); + String username = (String) this.nodeService.getProperty(userInfoRef, WCMAppModel.PROP_WEBUSERNAME); + String userrole = (String) this.nodeService.getProperty(userInfoRef, WCMAppModel.PROP_WEBUSERROLE); - permissionService.setPermission(dirRef, username, userrole, true); + this.permissionService.setPermission(dirRef, username, userrole, true); } } } @@ -191,28 +188,32 @@ public class WCMPermissionPatch extends AbstractPatch private void setStagingAreaMasks(AVMStoreDescriptor store) { NodeRef dirRef = AVMNodeConverter.ToNodeRef(-1, store.getName() + ":/www"); - permissionService.setPermission(dirRef.getStoreRef(), PermissionService.ALL_AUTHORITIES, PermissionService.READ, true); + this.permissionService.setPermission(dirRef.getStoreRef(), PermissionService.ALL_AUTHORITIES, + PermissionService.READ, true); QName propQName = QName.createQName(null, ".web_project.noderef"); - PropertyValue pValue = avmService.getStoreProperty(store.getName(), propQName); + PropertyValue pValue = this.avmService.getStoreProperty(store.getName(), propQName); if (pValue != null) { NodeRef webProjectNodeRef = (NodeRef) pValue.getValue(DataTypeDefinition.NODE_REF); // Apply sepcific user permissions as set on the web project - List userInfoRefs = nodeService.getChildAssocs(webProjectNodeRef, WCMAppModel.ASSOC_WEBUSER, RegexQNamePattern.MATCH_ALL); + List userInfoRefs = this.nodeService.getChildAssocs(webProjectNodeRef, + WCMAppModel.ASSOC_WEBUSER, RegexQNamePattern.MATCH_ALL); for (ChildAssociationRef ref : userInfoRefs) { NodeRef userInfoRef = ref.getChildRef(); - String username = (String) nodeService.getProperty(userInfoRef, WCMAppModel.PROP_WEBUSERNAME); - String userrole = (String) nodeService.getProperty(userInfoRef, WCMAppModel.PROP_WEBUSERROLE); + String username = (String) this.nodeService.getProperty(userInfoRef, WCMAppModel.PROP_WEBUSERNAME); + String userrole = (String) this.nodeService.getProperty(userInfoRef, WCMAppModel.PROP_WEBUSERROLE); if (userrole.equals("ContentManager")) { - permissionService.setPermission(dirRef.getStoreRef(), username, PermissionService.CHANGE_PERMISSIONS, true); - permissionService.setPermission(dirRef.getStoreRef(), username, PermissionService.READ_PERMISSIONS, true); + this.permissionService.setPermission(dirRef.getStoreRef(), username, + PermissionService.CHANGE_PERMISSIONS, true); + this.permissionService.setPermission(dirRef.getStoreRef(), username, + PermissionService.READ_PERMISSIONS, true); } } } @@ -230,30 +231,33 @@ public class WCMPermissionPatch extends AbstractPatch NodeRef dirRef = AVMNodeConverter.ToNodeRef(-1, sandBoxStore.getName() + ":/www"); - Map woof = avmService.getStoreProperties(stagingAreaName); - PropertyValue pValue = avmService.getStoreProperty(stagingAreaName, propQName); + this.avmService.getStoreProperties(stagingAreaName); + PropertyValue pValue = this.avmService.getStoreProperty(stagingAreaName, propQName); - permissionService.setPermission(dirRef.getStoreRef(), PermissionService.ALL_AUTHORITIES, PermissionService.READ, true); + this.permissionService.setPermission(dirRef.getStoreRef(), PermissionService.ALL_AUTHORITIES, + PermissionService.READ, true); if (pValue != null) { NodeRef webProjectNodeRef = (NodeRef) pValue.getValue(DataTypeDefinition.NODE_REF); // Apply sepcific user permissions as set on the web project - List userInfoRefs = nodeService.getChildAssocs(webProjectNodeRef, WCMAppModel.ASSOC_WEBUSER, RegexQNamePattern.MATCH_ALL); + List userInfoRefs = this.nodeService.getChildAssocs(webProjectNodeRef, + WCMAppModel.ASSOC_WEBUSER, RegexQNamePattern.MATCH_ALL); for (ChildAssociationRef ref : userInfoRefs) { NodeRef userInfoRef = ref.getChildRef(); - String username = (String) nodeService.getProperty(userInfoRef, WCMAppModel.PROP_WEBUSERNAME); - String userrole = (String) nodeService.getProperty(userInfoRef, WCMAppModel.PROP_WEBUSERROLE); + String username = (String) this.nodeService.getProperty(userInfoRef, WCMAppModel.PROP_WEBUSERNAME); + String userrole = (String) this.nodeService.getProperty(userInfoRef, WCMAppModel.PROP_WEBUSERROLE); if (username.equals(owner)) { - permissionService.setPermission(dirRef.getStoreRef(), username, PermissionService.ALL_PERMISSIONS, true); + this.permissionService.setPermission(dirRef.getStoreRef(), username, + PermissionService.ALL_PERMISSIONS, true); } else if (userrole.equals("ContentManager")) { - permissionService.setPermission(dirRef.getStoreRef(), username, userrole, true); + this.permissionService.setPermission(dirRef.getStoreRef(), username, userrole, true); } } } @@ -292,15 +296,9 @@ public class WCMPermissionPatch extends AbstractPatch Long max; - ProgressWatcher(Long toDo, Long max) - { - this.toDo = toDo; - this.max = max; - } - public void run() { - while (running) + while (this.running) { try { @@ -308,23 +306,31 @@ public class WCMPermissionPatch extends AbstractPatch } catch (InterruptedException e) { - running = false; + this.running = false; } - if (running) + if (this.running) { - RetryingTransactionHelper txHelper = transactionService.getRetryingTransactionHelper(); + RetryingTransactionHelper txHelper = WCMPermissionPatch.this.transactionService + .getRetryingTransactionHelper(); txHelper.setMaxRetries(1); Long done = txHelper.doInTransaction(new RetryingTransactionCallback() { public Long execute() throws Throwable { - return aclDaoComponent.getAVMNodeCountWithNewACLS(max); + if (ProgressWatcher.this.toDo == null) + { + ProgressWatcher.this.toDo = WCMPermissionPatch.this.aclDaoComponent + .getAVMHeadNodeCount(); + ProgressWatcher.this.max = WCMPermissionPatch.this.aclDaoComponent.getMaxAclId(); + } + return WCMPermissionPatch.this.aclDaoComponent + .getAVMNodeCountWithNewACLS(ProgressWatcher.this.max); } }, true, true); - reportProgress(toDo, done); + reportProgress(this.toDo, done); } } } diff --git a/source/java/org/alfresco/repo/avm/AVMSyncServiceImpl.java b/source/java/org/alfresco/repo/avm/AVMSyncServiceImpl.java index 8a5cb7bd7d..c2b11c5eaa 100644 --- a/source/java/org/alfresco/repo/avm/AVMSyncServiceImpl.java +++ b/source/java/org/alfresco/repo/avm/AVMSyncServiceImpl.java @@ -117,14 +117,6 @@ public class AVMSyncServiceImpl implements AVMSyncService if (fgLogger.isDebugEnabled()) { fgLogger.debug(srcPath + " : " + dstPath); - try - { - throw new Exception(); - } - catch (Exception e) - { - fgLogger.debug("Stack Trace: ", e); - } } if (srcPath == null || dstPath == null) { @@ -410,17 +402,6 @@ public class AVMSyncServiceImpl implements AVMSyncService boolean overrideConflicts, boolean overrideOlder, String tag, String description) { long start = System.currentTimeMillis(); - if (fgLogger.isDebugEnabled()) - { - try - { - throw new Exception("Stack Trace."); - } - catch (Exception e) - { - fgLogger.debug("Stack trace: ", e); - } - } Map storeVersions = new HashMap(); Set destStores = new HashSet(); for (AVMDifference diff : diffList) @@ -1028,14 +1009,6 @@ public class AVMSyncServiceImpl implements AVMSyncService if (fgLogger.isDebugEnabled()) { fgLogger.debug("flatten: " + layerNode + " " + underlyingNode); - try - { - throw new Exception("Stack Trace:"); - } - catch (Exception e) - { - fgLogger.debug("Stack Trace: ", e); - } } flatten(layerNode, underlyingNode); } diff --git a/source/java/org/alfresco/repo/content/AbstractContentStore.java b/source/java/org/alfresco/repo/content/AbstractContentStore.java index 616327596c..a58bc7081a 100644 --- a/source/java/org/alfresco/repo/content/AbstractContentStore.java +++ b/source/java/org/alfresco/repo/content/AbstractContentStore.java @@ -272,4 +272,20 @@ public abstract class AbstractContentStore implements ContentStore ContentReader reader = getReader(contentUrl); return reader.exists(); } + + /** + * @return Returns -1 always + */ + public long getTotalSize() + { + return -1L; + } + + /** + * {@inheritDoc} + */ + public String getRootLocation() + { + return "."; + } } diff --git a/source/java/org/alfresco/repo/content/AbstractRoutingContentStore.java b/source/java/org/alfresco/repo/content/AbstractRoutingContentStore.java index 73dc4c1666..f661e646d3 100644 --- a/source/java/org/alfresco/repo/content/AbstractRoutingContentStore.java +++ b/source/java/org/alfresco/repo/content/AbstractRoutingContentStore.java @@ -245,6 +245,22 @@ public abstract class AbstractRoutingContentStore implements ContentStore return supported; } + /** + * @return Returns . always + */ + public String getRootLocation() + { + return "."; + } + + /** + * @return Returns -1 always + */ + public long getTotalSize() + { + return -1L; + } + /** * @see #selectReadStore(String) */ diff --git a/source/java/org/alfresco/repo/content/AbstractWritableContentStoreTest.java b/source/java/org/alfresco/repo/content/AbstractWritableContentStoreTest.java index 8ff56ced5a..72ba9213ca 100644 --- a/source/java/org/alfresco/repo/content/AbstractWritableContentStoreTest.java +++ b/source/java/org/alfresco/repo/content/AbstractWritableContentStoreTest.java @@ -102,6 +102,25 @@ public abstract class AbstractWritableContentStoreTest extends AbstractReadOnlyC ContentStore store = getStore(); assertTrue("The store cannot be read-only", store.isWriteSupported()); } + + /** + * Just check that the method doesn't blow up + */ + public void testTotalSize() throws Exception + { + ContentStore store = getStore(); + store.getTotalSize(); + } + + /** + * Just check that the method doesn't blow up + */ + public void testRootLocation() throws Exception + { + ContentStore store = getStore(); + String rootLocation = store.getRootLocation(); + assertNotNull("The root location may not be null", rootLocation); + } /** * Helper to ensure that illegal content URLs are flagged for getWriter requests diff --git a/source/java/org/alfresco/repo/content/ContentStore.java b/source/java/org/alfresco/repo/content/ContentStore.java index e10072a98e..28ae8d21d2 100644 --- a/source/java/org/alfresco/repo/content/ContentStore.java +++ b/source/java/org/alfresco/repo/content/ContentStore.java @@ -102,6 +102,28 @@ public interface ContentStore */ public boolean isWriteSupported(); + /** + * Calculates the total size of content stored. + *

+ * NOTE: For efficiency, some implementations may provide a guess. If not, this call could + * take a long time. + *

+ * Implementations should focus on calculating a size value quickly, rather than accurately. + * + * @return + * Returns the total, possibly approximate size (in bytes) of the binary data stored or -1 + * if no size data is available. + */ + public long getTotalSize(); + + /** + * Get the location where the store is rooted. The format of the returned value will depend on the + * specific implementation of the store. + * + * @return Returns the store's root location or . if no information is available + */ + public String getRootLocation(); + /** * Check for the existence of content in the store. *

diff --git a/source/java/org/alfresco/repo/content/filestore/FileContentStoreCreatedEvent.java b/source/java/org/alfresco/repo/content/ContentStoreCreatedEvent.java similarity index 75% rename from source/java/org/alfresco/repo/content/filestore/FileContentStoreCreatedEvent.java rename to source/java/org/alfresco/repo/content/ContentStoreCreatedEvent.java index 3bb3f0fa11..e1c69e9bd7 100644 --- a/source/java/org/alfresco/repo/content/filestore/FileContentStoreCreatedEvent.java +++ b/source/java/org/alfresco/repo/content/ContentStoreCreatedEvent.java @@ -22,10 +22,9 @@ * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ -package org.alfresco.repo.content.filestore; - -import java.io.File; +package org.alfresco.repo.content; +import org.alfresco.repo.content.filestore.FileContentStore; import org.springframework.context.ApplicationEvent; /** @@ -33,14 +32,12 @@ import org.springframework.context.ApplicationEvent; * purposes. * * @author dward + * @since 3.1 */ -public class FileContentStoreCreatedEvent extends ApplicationEvent +public class ContentStoreCreatedEvent extends ApplicationEvent { private static final long serialVersionUID = 7090069096441126707L; - /** The root directory of the store. */ - private final File rootDirectory; - /** * The Constructor. * @@ -49,19 +46,16 @@ public class FileContentStoreCreatedEvent extends ApplicationEvent * @param rootDirectory * the root directory */ - public FileContentStoreCreatedEvent(FileContentStore source, File rootDirectory) + public ContentStoreCreatedEvent(ContentStore source) { super(source); - this.rootDirectory = rootDirectory; } - + /** - * Gets the root directory. - * - * @return the root directory + * @return Returns the source {@link ContentStore} */ - public File getRootDirectory() + public ContentStore getContentStore() { - return this.rootDirectory; + return (ContentStore) getSource(); } } diff --git a/source/java/org/alfresco/repo/content/RoutingContentService.java b/source/java/org/alfresco/repo/content/RoutingContentService.java index fe4a9b884b..629c2e3f6a 100644 --- a/source/java/org/alfresco/repo/content/RoutingContentService.java +++ b/source/java/org/alfresco/repo/content/RoutingContentService.java @@ -200,7 +200,7 @@ public class RoutingContentService implements ContentService, ApplicationContext Map after) { boolean fire = false; - boolean newContent = false; + boolean isNewContent = false; // check if any of the content properties have changed for (QName propertyQName : after.keySet()) { @@ -221,49 +221,39 @@ public class RoutingContentService implements ContentService, ApplicationContext { ContentData beforeValue = (ContentData) before.get(propertyQName); ContentData afterValue = (ContentData) after.get(propertyQName); - if (afterValue != null && afterValue.getContentUrl() == null) + boolean hasContentBefore = ContentData.hasContent(beforeValue); + boolean hasContentAfter = ContentData.hasContent(afterValue); + + // There are some shortcuts here + if (!hasContentBefore && !hasContentAfter) { - // no URL - ignore + // Really, nothing happened + continue; } - else if (!EqualsHelper.nullSafeEquals(beforeValue, afterValue)) + else if (EqualsHelper.nullSafeEquals(beforeValue, afterValue)) { - // So debug ... - if (logger.isDebugEnabled() == true) - { - String beforeString = ""; - if (beforeValue != null) - { - beforeString = beforeValue.toString(); - } - String afterString = ""; - if (afterValue != null) - { - afterString = afterValue.toString(); - } - logger.debug("onContentUpate: before = " + beforeString + "; after = " + afterString); - } - - // Figure out if the content is new or not - String beforeContentUrl = null; - if (beforeValue != null) - { - beforeContentUrl = beforeValue.getContentUrl(); - } - String afterContentUrl = null; - if (afterValue != null) - { - afterContentUrl = afterValue.getContentUrl(); - } - if (beforeContentUrl == null && afterContentUrl != null) - { - newContent = true; - } - - // the content changed - // at the moment, we are only interested in this one change - fire = true; - break; + // Still, nothing happening + continue; } + + // Check for new content + isNewContent = !hasContentBefore && hasContentAfter; + + // So debug ... + if (logger.isDebugEnabled() == true) + { + String name = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); + logger.debug( + "onContentUpdate will fire: \n" + + " Name: " + name + "\n" + + " Is new: " + isNewContent + "\n" + + " Before: " + beforeValue + "\n" + + " After: " + afterValue); + } + + // We are interested in any content change + fire = true; + break; } catch (ClassCastException e) { @@ -278,7 +268,7 @@ public class RoutingContentService implements ContentService, ApplicationContext Set types = new HashSet(this.nodeService.getAspects(nodeRef)); types.add(this.nodeService.getType(nodeRef)); OnContentUpdatePolicy policy = this.onContentUpdateDelegate.get(nodeRef, types); - policy.onContentUpdate(nodeRef, newContent); + policy.onContentUpdate(nodeRef, isNewContent); } } @@ -327,10 +317,11 @@ public class RoutingContentService implements ContentService, ApplicationContext Serializable propValue = nodeService.getProperty(nodeRef, propertyQName); if (propValue instanceof Collection) { - Collection colPropValue = (Collection)propValue; + @SuppressWarnings("unchecked") + Collection colPropValue = (Collection)propValue; if (colPropValue.size() > 0) { - propValue = (Serializable)colPropValue.iterator().next(); + propValue = colPropValue.iterator().next(); } } diff --git a/source/java/org/alfresco/repo/content/filestore/FileContentStore.java b/source/java/org/alfresco/repo/content/filestore/FileContentStore.java index b221577067..4eeb51741a 100644 --- a/source/java/org/alfresco/repo/content/filestore/FileContentStore.java +++ b/source/java/org/alfresco/repo/content/filestore/FileContentStore.java @@ -34,6 +34,7 @@ import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.repo.content.AbstractContentStore; import org.alfresco.repo.content.ContentContext; import org.alfresco.repo.content.ContentStore; +import org.alfresco.repo.content.ContentStoreCreatedEvent; import org.alfresco.repo.content.EmptyContentReader; import org.alfresco.repo.content.UnsupportedContentUrlException; import org.alfresco.service.cmr.repository.ContentIOException; @@ -352,6 +353,53 @@ public class FileContentStore extends AbstractContentStore implements Applicatio return file.exists(); } + /** + * Performs a full, deep size calculation + */ + @Override + public long getTotalSize() + { + return calculateDirectorySize(rootDirectory); + } + + /** + * Recursive directory size calculation + */ + private long calculateDirectorySize(File dir) + { + int size = 0; + File[] files = dir.listFiles(); + for (File file : files) + { + if (file.isDirectory()) + { + size += calculateDirectorySize(file); + } + else + { + size += file.length(); + } + } + return size; + } + + /** + * @return Returns the canonical path to the root directory + */ + @Override + public String getRootLocation() + { + try + { + return rootDirectory.getCanonicalPath(); + } + catch (Throwable e) + { + logger.warn("Unabled to return root location", e); + return super.getRootLocation(); + } + } + /** * This implementation requires that the URL start with * {@link FileContentStore#STORE_PROTOCOL }. @@ -585,7 +633,7 @@ public class FileContentStore extends AbstractContentStore implements Applicatio try { // Neither context.isActive() or context.isRunning() seem to detect whether the refresh() has completed - context.publishEvent(new FileContentStoreCreatedEvent(this, rootDirectory)); + context.publishEvent(new ContentStoreCreatedEvent(this)); } catch (IllegalStateException e) { @@ -593,11 +641,6 @@ public class FileContentStore extends AbstractContentStore implements Applicatio } } - /* - * (non-Javadoc) - * @see - * org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent) - */ public void onApplicationEvent(ApplicationEvent event) { // Once the context has been refreshed, we tell other interested beans about the existence of this content store @@ -605,7 +648,7 @@ public class FileContentStore extends AbstractContentStore implements Applicatio if (event instanceof ContextRefreshedEvent) { ((ContextRefreshedEvent) event).getApplicationContext().publishEvent( - new FileContentStoreCreatedEvent(this, rootDirectory)); + new ContentStoreCreatedEvent(this)); } } } diff --git a/source/java/org/alfresco/repo/content/filestore/FileContentStoreTest.java b/source/java/org/alfresco/repo/content/filestore/FileContentStoreTest.java index 25c6c92d67..5614573ef3 100644 --- a/source/java/org/alfresco/repo/content/filestore/FileContentStoreTest.java +++ b/source/java/org/alfresco/repo/content/filestore/FileContentStoreTest.java @@ -88,4 +88,23 @@ public class FileContentStoreTest extends AbstractWritableContentStoreTest // expected } } + + @Override + public void testRootLocation() throws Exception + { + ContentStore store = getStore(); + String root = store.getRootLocation(); + assertNotNull("Root value can't be null", root); + File dir = new File(root); + assertTrue("Root location for FileContentStore must exist", dir.exists()); + } + + @Override + public void testTotalSize() throws Exception + { + ContentStore store = getStore(); + store.getWriter(new ContentContext(null, null)).putContent("Test content"); + long size = store.getTotalSize(); + assertTrue("Size must be positive", size > 0L); + } } diff --git a/source/java/org/alfresco/repo/content/transform/RuntimeExecutableContentTransformerTest.java b/source/java/org/alfresco/repo/content/transform/RuntimeExecutableContentTransformerTest.java index 32467fd151..86a8ef3f3c 100644 --- a/source/java/org/alfresco/repo/content/transform/RuntimeExecutableContentTransformerTest.java +++ b/source/java/org/alfresco/repo/content/transform/RuntimeExecutableContentTransformerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2007 Alfresco Software Limited. + * Copyright (C) 2005-2009 Alfresco Software Limited. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -56,6 +56,7 @@ public class RuntimeExecutableContentTransformerTest extends BaseAlfrescoTestCas // the command to execute RuntimeExec transformCommand = new RuntimeExec(); Map commandMap = new HashMap(5); + commandMap.put("Mac OS X", "mv -f ${source} ${target}"); commandMap.put("Linux", "mv -f ${source} ${target}"); commandMap.put(".*", "cmd /c copy /Y \"${source}\" \"${target}\""); transformCommand.setCommandMap(commandMap); diff --git a/source/java/org/alfresco/repo/descriptor/RepositoryDescriptorDAOImpl.java b/source/java/org/alfresco/repo/descriptor/RepositoryDescriptorDAOImpl.java index 4557334807..75e164174e 100644 --- a/source/java/org/alfresco/repo/descriptor/RepositoryDescriptorDAOImpl.java +++ b/source/java/org/alfresco/repo/descriptor/RepositoryDescriptorDAOImpl.java @@ -30,6 +30,7 @@ import java.io.InputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; @@ -196,20 +197,15 @@ public class RepositoryDescriptorDAOImpl implements DescriptorDAO // set the properties if (!this.transactionService.isReadOnly()) { - this.nodeService.setProperty(currentDescriptorNodeRef, ContentModel.PROP_SYS_NAME, serverDescriptor - .getName()); - this.nodeService.setProperty(currentDescriptorNodeRef, ContentModel.PROP_SYS_VERSION_MAJOR, - serverDescriptor.getVersionMajor()); - this.nodeService.setProperty(currentDescriptorNodeRef, ContentModel.PROP_SYS_VERSION_MINOR, - serverDescriptor.getVersionMinor()); - this.nodeService.setProperty(currentDescriptorNodeRef, ContentModel.PROP_SYS_VERSION_REVISION, - serverDescriptor.getVersionRevision()); - this.nodeService.setProperty(currentDescriptorNodeRef, ContentModel.PROP_SYS_VERSION_LABEL, - serverDescriptor.getVersionLabel()); - this.nodeService.setProperty(currentDescriptorNodeRef, ContentModel.PROP_SYS_VERSION_BUILD, - serverDescriptor.getVersionBuild()); - this.nodeService.setProperty(currentDescriptorNodeRef, ContentModel.PROP_SYS_VERSION_SCHEMA, - serverDescriptor.getSchema()); + Map props = new HashMap(11); + props.put(ContentModel.PROP_SYS_NAME, serverDescriptor.getName()); + props.put(ContentModel.PROP_SYS_VERSION_MAJOR, serverDescriptor.getVersionMajor()); + props.put(ContentModel.PROP_SYS_VERSION_MINOR, serverDescriptor.getVersionMinor()); + props.put(ContentModel.PROP_SYS_VERSION_REVISION, serverDescriptor.getVersionRevision()); + props.put(ContentModel.PROP_SYS_VERSION_LABEL, serverDescriptor.getVersionLabel()); + props.put(ContentModel.PROP_SYS_VERSION_BUILD, serverDescriptor.getVersionBuild()); + props.put(ContentModel.PROP_SYS_VERSION_SCHEMA, serverDescriptor.getSchema()); + this.nodeService.addProperties(currentDescriptorNodeRef, props); // The version edition property may already have been overwritten with a license, so only set the property // if it doesn't already contain ContentData diff --git a/source/java/org/alfresco/repo/dictionary/DictionaryDAOTest.java b/source/java/org/alfresco/repo/dictionary/DictionaryDAOTest.java index 44c91eb724..9bcc4cc3ef 100644 --- a/source/java/org/alfresco/repo/dictionary/DictionaryDAOTest.java +++ b/source/java/org/alfresco/repo/dictionary/DictionaryDAOTest.java @@ -39,6 +39,7 @@ import org.alfresco.repo.dictionary.constraint.StringLengthConstraint; import org.alfresco.repo.tenant.SingleTServiceImpl; import org.alfresco.repo.tenant.TenantService; import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition; import org.alfresco.service.cmr.dictionary.ClassDefinition; import org.alfresco.service.cmr.dictionary.Constraint; import org.alfresco.service.cmr.dictionary.ConstraintDefinition; @@ -355,4 +356,20 @@ public class DictionaryDAOTest extends TestCase assertEquals("three", def3); } + public void testChildAssocPropagate() + { + // Check the default value + AssociationDefinition assocDef = service.getAssociation(QName.createQName(TEST_URL, "childassoc1")); + assertNotNull("No such child association found", assocDef); + assertTrue("Expected a child association", assocDef instanceof ChildAssociationDefinition); + ChildAssociationDefinition childAssocDef = (ChildAssociationDefinition) assocDef; + assertFalse("Expected 'false' for default timestamp propagation", childAssocDef.getPropagateTimestamps()); + + // Check the explicit value + assocDef = service.getAssociation(QName.createQName(TEST_URL, "childassocPropagate")); + assertNotNull("No such child association found", assocDef); + assertTrue("Expected a child association", assocDef instanceof ChildAssociationDefinition); + childAssocDef = (ChildAssociationDefinition) assocDef; + assertTrue("Expected 'true' for timestamp propagation", childAssocDef.getPropagateTimestamps()); + } } diff --git a/source/java/org/alfresco/repo/dictionary/M2ChildAssociation.java b/source/java/org/alfresco/repo/dictionary/M2ChildAssociation.java index 3b29767875..348a591483 100644 --- a/source/java/org/alfresco/repo/dictionary/M2ChildAssociation.java +++ b/source/java/org/alfresco/repo/dictionary/M2ChildAssociation.java @@ -35,6 +35,7 @@ public class M2ChildAssociation extends M2ClassAssociation { private String requiredChildName = null; private Boolean allowDuplicateChildName = null; + private Boolean propagateTimestamps = null; /*package*/ M2ChildAssociation() @@ -71,4 +72,13 @@ public class M2ChildAssociation extends M2ClassAssociation this.allowDuplicateChildName = allowDuplicateChildName; } + public boolean isPropagateTimestamps() + { + return propagateTimestamps == null ? false : propagateTimestamps; + } + + public void setPropagateTimestamps(boolean propagateTimestamps) + { + this.propagateTimestamps = propagateTimestamps; + } } diff --git a/source/java/org/alfresco/repo/dictionary/M2ChildAssociationDefinition.java b/source/java/org/alfresco/repo/dictionary/M2ChildAssociationDefinition.java index 53038ad1b2..5a604895f8 100644 --- a/source/java/org/alfresco/repo/dictionary/M2ChildAssociationDefinition.java +++ b/source/java/org/alfresco/repo/dictionary/M2ChildAssociationDefinition.java @@ -50,21 +50,20 @@ import org.alfresco.service.namespace.NamespacePrefixResolver; } - /* (non-Javadoc) - * @see org.alfresco.repo.dictionary.ChildAssociationDefinition#getRequiredChildName() - */ public String getRequiredChildName() { return ((M2ChildAssociation)getM2Association()).getRequiredChildName(); } - /* (non-Javadoc) - * @see org.alfresco.repo.dictionary.ChildAssociationDefinition#getDuplicateChildNamesAllowed() - */ public boolean getDuplicateChildNamesAllowed() { return ((M2ChildAssociation)getM2Association()).allowDuplicateChildName(); } - + + + public boolean getPropagateTimestamps() + { + return ((M2ChildAssociation)getM2Association()).isPropagateTimestamps(); + } } diff --git a/source/java/org/alfresco/repo/dictionary/dictionarydaotest_model.xml b/source/java/org/alfresco/repo/dictionary/dictionarydaotest_model.xml index 500c04df53..cc8e44c0c7 100644 --- a/source/java/org/alfresco/repo/dictionary/dictionarydaotest_model.xml +++ b/source/java/org/alfresco/repo/dictionary/dictionarydaotest_model.xml @@ -109,6 +109,20 @@ fred true + + + true + true + + + test:referenceable + false + false + + fred + true + true + diff --git a/source/java/org/alfresco/repo/dictionary/m2binding.xml b/source/java/org/alfresco/repo/dictionary/m2binding.xml index 01b6641f8b..12223aa415 100644 --- a/source/java/org/alfresco/repo/dictionary/m2binding.xml +++ b/source/java/org/alfresco/repo/dictionary/m2binding.xml @@ -173,6 +173,7 @@ + \ No newline at end of file diff --git a/source/java/org/alfresco/repo/model/filefolder/TempFileMarkerInterceptor.java b/source/java/org/alfresco/repo/model/filefolder/TempFileMarkerInterceptor.java index 435a205db4..142d965c2c 100644 --- a/source/java/org/alfresco/repo/model/filefolder/TempFileMarkerInterceptor.java +++ b/source/java/org/alfresco/repo/model/filefolder/TempFileMarkerInterceptor.java @@ -91,6 +91,7 @@ public class TempFileMarkerInterceptor implements MethodInterceptor { FileInfo fileInfo = (FileInfo) ret; String filename = fileInfo.getName(); + NodeRef nodeRef = fileInfo.getNodeRef(); if (logger.isDebugEnabled()) { @@ -98,6 +99,7 @@ public class TempFileMarkerInterceptor implements MethodInterceptor } // check against all the regular expressions + boolean matched = false; for (String regexp : filterRegularExpressions) { if (!filename.matches(regexp)) @@ -107,8 +109,8 @@ public class TempFileMarkerInterceptor implements MethodInterceptor } else { + matched = true; // it matched, so apply the aspect - NodeRef nodeRef = fileInfo.getNodeRef(); nodeService.addAspect(nodeRef, ContentModel.ASPECT_TEMPORARY, null); // no further checking required if (logger.isDebugEnabled()) @@ -118,6 +120,17 @@ public class TempFileMarkerInterceptor implements MethodInterceptor break; } } + // If there was NOT a match then the file should not be marked as temporary + // after any of the operations in question. + if (!matched && nodeService.hasAspect(nodeRef, ContentModel.ASPECT_TEMPORARY)) + { + // Remove the aspect + nodeService.removeAspect(nodeRef, ContentModel.ASPECT_TEMPORARY); + if (logger.isDebugEnabled()) + { + logger.debug("Removed temporary marker: " + fileInfo); + } + } } // done return ret; diff --git a/source/java/org/alfresco/repo/transaction/RetryingTransactionHelper.java b/source/java/org/alfresco/repo/transaction/RetryingTransactionHelper.java index 2757a87675..42cf091efd 100644 --- a/source/java/org/alfresco/repo/transaction/RetryingTransactionHelper.java +++ b/source/java/org/alfresco/repo/transaction/RetryingTransactionHelper.java @@ -1,502 +1,502 @@ -/* - * Copyright (C) 2005-2007 Alfresco Software Limited. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program 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 General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: - * http://www.alfresco.com/legal/licensing" - */ -package org.alfresco.repo.transaction; - -import java.lang.reflect.Method; -import java.sql.BatchUpdateException; -import java.sql.SQLException; -import java.util.Random; - -import javax.transaction.RollbackException; -import javax.transaction.Status; -import javax.transaction.UserTransaction; - -import net.sf.ehcache.distribution.RemoteCacheException; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.error.ExceptionStackUtil; -import org.alfresco.repo.security.permissions.AccessDeniedException; -import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState; -import org.alfresco.service.transaction.TransactionService; +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.transaction; + +import java.lang.reflect.Method; +import java.sql.BatchUpdateException; +import java.sql.SQLException; +import java.util.Random; + +import javax.transaction.RollbackException; +import javax.transaction.Status; +import javax.transaction.UserTransaction; + +import net.sf.ehcache.distribution.RemoteCacheException; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.error.ExceptionStackUtil; +import org.alfresco.repo.security.permissions.AccessDeniedException; +import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState; +import org.alfresco.service.transaction.TransactionService; import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.hibernate.ObjectNotFoundException; -import org.hibernate.StaleObjectStateException; -import org.hibernate.StaleStateException; -import org.hibernate.cache.CacheException; -import org.hibernate.exception.ConstraintViolationException; -import org.hibernate.exception.LockAcquisitionException; +import org.apache.commons.logging.LogFactory; +import org.hibernate.ObjectNotFoundException; +import org.hibernate.StaleObjectStateException; +import org.hibernate.StaleStateException; +import org.hibernate.cache.CacheException; +import org.hibernate.exception.ConstraintViolationException; +import org.hibernate.exception.LockAcquisitionException; import org.hibernate.exception.SQLGrammarException; -import org.springframework.aop.MethodBeforeAdvice; -import org.springframework.aop.framework.ProxyFactory; -import org.springframework.dao.ConcurrencyFailureException; -import org.springframework.dao.DataIntegrityViolationException; -import org.springframework.dao.DeadlockLoserDataAccessException; -import org.springframework.jdbc.UncategorizedSQLException; - -/** - * A helper that runs a unit of work inside a UserTransaction, - * transparently retrying the unit of work if the cause of - * failure is an optimistic locking or deadlock condition. - *

- * Defaults: - *

    - *
  • maxRetries: 20
  • - *
  • minRetryWaitMs: 100
  • - *
  • maxRetryWaitMs: 2000
  • - *
  • retryWaitIncrementMs: 100
  • - *
- *

- * To get details of 'why' transactions are retried use the following log level:
- * Summary: log4j.logger.org.alfresco.repo.transaction.RetryingTransactionHelper=INFO
- * Details: log4j.logger.org.alfresco.repo.transaction.RetryingTransactionHelper=DEBUG
- * - * - * @author Derek Hulley - */ -public class RetryingTransactionHelper -{ - private static final String MSG_READ_ONLY = "permissions.err_read_only"; - private static final String KEY_ACTIVE_TRANSACTION = "RetryingTransactionHelper.ActiveTxn"; - private static Log logger = LogFactory.getLog(RetryingTransactionHelper.class); - - /** - * Exceptions that trigger retries. - */ - @SuppressWarnings("unchecked") - public static final Class[] RETRY_EXCEPTIONS; - static - { - RETRY_EXCEPTIONS = new Class[] { - ConcurrencyFailureException.class, - DeadlockLoserDataAccessException.class, - StaleObjectStateException.class, - LockAcquisitionException.class, - ConstraintViolationException.class, - UncategorizedSQLException.class, - SQLException.class, - BatchUpdateException.class, - DataIntegrityViolationException.class, - StaleStateException.class, - ObjectNotFoundException.class, - CacheException.class, // Usually a cache replication issue +import org.springframework.aop.MethodBeforeAdvice; +import org.springframework.aop.framework.ProxyFactory; +import org.springframework.dao.ConcurrencyFailureException; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.dao.DeadlockLoserDataAccessException; +import org.springframework.jdbc.UncategorizedSQLException; + +/** + * A helper that runs a unit of work inside a UserTransaction, + * transparently retrying the unit of work if the cause of + * failure is an optimistic locking or deadlock condition. + *

+ * Defaults: + *

    + *
  • maxRetries: 20
  • + *
  • minRetryWaitMs: 100
  • + *
  • maxRetryWaitMs: 2000
  • + *
  • retryWaitIncrementMs: 100
  • + *
+ *

+ * To get details of 'why' transactions are retried use the following log level:
+ * Summary: log4j.logger.org.alfresco.repo.transaction.RetryingTransactionHelper=INFO
+ * Details: log4j.logger.org.alfresco.repo.transaction.RetryingTransactionHelper=DEBUG
+ * + * + * @author Derek Hulley + */ +public class RetryingTransactionHelper +{ + private static final String MSG_READ_ONLY = "permissions.err_read_only"; + private static final String KEY_ACTIVE_TRANSACTION = "RetryingTransactionHelper.ActiveTxn"; + private static Log logger = LogFactory.getLog(RetryingTransactionHelper.class); + + /** + * Exceptions that trigger retries. + */ + @SuppressWarnings("unchecked") + public static final Class[] RETRY_EXCEPTIONS; + static + { + RETRY_EXCEPTIONS = new Class[] { + ConcurrencyFailureException.class, + DeadlockLoserDataAccessException.class, + StaleObjectStateException.class, + LockAcquisitionException.class, + ConstraintViolationException.class, + UncategorizedSQLException.class, + SQLException.class, + BatchUpdateException.class, + DataIntegrityViolationException.class, + StaleStateException.class, + ObjectNotFoundException.class, + CacheException.class, // Usually a cache replication issue RemoteCacheException.class, // A cache replication issue SQLGrammarException.class // Actually specific to MS SQL Server 2005 - we check for this - }; - } - - /** - * Reference to the TransactionService instance. - */ - private TransactionService txnService; - -// /** Performs post-failure exception neatening */ -// private ExceptionTransformer exceptionTransformer; - /** The maximum number of retries. -1 for infinity. */ - private int maxRetries; - /** The minimum time to wait between retries. */ - private int minRetryWaitMs; - /** The maximum time to wait between retries. */ - private int maxRetryWaitMs; - /** How much to increase the wait time with each retry. */ - private int retryWaitIncrementMs; - - /** - * Whether the the transactions may only be reads - */ - private boolean readOnly; - - /** - * Random number generator for retry delays. - */ - private Random random; - - /** - * Callback interface - * @author britt - */ - public interface RetryingTransactionCallback - { - /** - * Perform a unit of transactional work. - * - * @return Return the result of the unit of work - * @throws Throwable This can be anything and will guarantee either a retry or a rollback - */ - public Result execute() throws Throwable; - }; - - /** - * Default constructor. - */ - public RetryingTransactionHelper() - { - this.random = new Random(System.currentTimeMillis()); - this.maxRetries = 20; - this.minRetryWaitMs = 100; - this.maxRetryWaitMs = 2000; - this.retryWaitIncrementMs = 100; - } - - // Setters. - -// /** -// * Optionally set the component that will transform or neaten any exceptions that are -// * propagated. -// */ -// public void setExceptionTransformer(ExceptionTransformer exceptionTransformer) -// { -// this.exceptionTransformer = exceptionTransformer; -// } -// - /** - * Set the TransactionService. - */ - public void setTransactionService(TransactionService service) - { - this.txnService = service; - } - - /** - * Set the maximimum number of retries. -1 for infinity. - */ - public void setMaxRetries(int maxRetries) - { - this.maxRetries = maxRetries; - } - - public void setMinRetryWaitMs(int minRetryWaitMs) - { - this.minRetryWaitMs = minRetryWaitMs; - } - - public void setMaxRetryWaitMs(int maxRetryWaitMs) - { - this.maxRetryWaitMs = maxRetryWaitMs; - } - - public void setRetryWaitIncrementMs(int retryWaitIncrementMs) - { - this.retryWaitIncrementMs = retryWaitIncrementMs; - } - - /** - * Set whether this helper only supports read transactions. - */ - public void setReadOnly(boolean readOnly) - { - this.readOnly = readOnly; - } - - /** - * Execute a callback in a transaction until it succeeds, fails - * because of an error not the result of an optimistic locking failure, - * or a deadlock loser failure, or until a maximum number of retries have - * been attempted. - *

- * If there is already an active transaction, then the callback is merely - * executed and any retry logic is left to the caller. The transaction - * will attempt to be read-write. - * - * @param cb The callback containing the unit of work. - * @return Returns the result of the unit of work. - * @throws RuntimeException all checked exceptions are converted - */ - public R doInTransaction(RetryingTransactionCallback cb) - { - return doInTransaction(cb, false, false); - } - - /** - * Execute a callback in a transaction until it succeeds, fails - * because of an error not the result of an optimistic locking failure, - * or a deadlock loser failure, or until a maximum number of retries have - * been attempted. - *

- * If there is already an active transaction, then the callback is merely - * executed and any retry logic is left to the caller. - * - * @param cb The callback containing the unit of work. - * @param readOnly Whether this is a read only transaction. - * @return Returns the result of the unit of work. - * @throws RuntimeException all checked exceptions are converted - */ - public R doInTransaction(RetryingTransactionCallback cb, boolean readOnly) - { - return doInTransaction(cb, readOnly, false); - } - - /** - * Execute a callback in a transaction until it succeeds, fails - * because of an error not the result of an optimistic locking failure, - * or a deadlock loser failure, or until a maximum number of retries have - * been attempted. - *

- * It is possible to force a new transaction to be created or to partake in - * any existing transaction. - * - * @param cb The callback containing the unit of work. - * @param readOnly Whether this is a read only transaction. - * @param requiresNew true to force a new transaction or - * false to partake in any existing transaction. - * @return Returns the result of the unit of work. - * @throws RuntimeException all checked exceptions are converted - */ - public R doInTransaction(RetryingTransactionCallback cb, boolean readOnly, boolean requiresNew) - { - if (this.readOnly && !readOnly) - { - throw new AccessDeniedException(MSG_READ_ONLY); - } - // Track the last exception caught, so that we - // can throw it if we run out of retries. - RuntimeException lastException = null; - for (int count = 0; maxRetries < 0 || count < maxRetries; ++count) - { - UserTransaction txn = null; - try - { - if (requiresNew) - { - txn = txnService.getNonPropagatingUserTransaction(readOnly); - } - else - { - TxnReadState readState = AlfrescoTransactionSupport.getTransactionReadState(); - switch (readState) - { - case TXN_READ_ONLY: - if (!readOnly) - { - // The current transaction is read-only, but a writable transaction is requested - throw new AlfrescoRuntimeException("Read-Write transaction started within read-only transaction"); - } - // We are in a read-only transaction and this is what we require so continue with it. - break; - case TXN_READ_WRITE: - // We are in a read-write transaction. It cannot be downgraded so just continue with it. - break; - case TXN_NONE: - // There is no current transaction so we need a new one. - txn = txnService.getUserTransaction(readOnly); - break; - default: - throw new RuntimeException("Unknown transaction state: " + readState); - } - } - if (txn != null) - { - txn.begin(); - // Wrap it to protect it - UserTransactionProtectionAdvise advise = new UserTransactionProtectionAdvise(); - ProxyFactory proxyFactory = new ProxyFactory(txn); - proxyFactory.addAdvice(advise); - UserTransaction wrappedTxn = (UserTransaction) proxyFactory.getProxy(); - // Store the UserTransaction for static retrieval. There is no need to unbind it - // because the transaction management will do that for us. - AlfrescoTransactionSupport.bindResource(KEY_ACTIVE_TRANSACTION, wrappedTxn); - } - // Do the work. - R result = cb.execute(); - // Only commit if we 'own' the transaction. - if (txn != null) - { - if (txn.getStatus() == Status.STATUS_MARKED_ROLLBACK) - { - // Something caused the transaction to be marked for rollback - // There is no recovery or retrying with this - txn.rollback(); - } - else - { - // The transaction hasn't been flagged for failure so the commit - // sould still be good. - txn.commit(); - } - } - if (logger.isDebugEnabled()) - { - if (count != 0) - { - logger.debug("\n" + - "Transaction succeeded: \n" + - " Thread: " + Thread.currentThread().getName() + "\n" + - " Txn: " + txn + "\n" + - " Iteration: " + count); - } - } - return result; - } - catch (Throwable e) - { - // Somebody else 'owns' the transaction, so just rethrow. - if (txn == null) - { - RuntimeException ee = AlfrescoRuntimeException.makeRuntimeException( - e, "Exception from transactional callback: " + cb); - throw ee; - } - if (logger.isDebugEnabled()) - { - logger.debug("\n" + - "Transaction commit failed: \n" + - " Thread: " + Thread.currentThread().getName() + "\n" + - " Txn: " + txn + "\n" + - " Iteration: " + count + "\n" + - " Exception follows:", - e); - } - // Rollback if we can. - if (txn != null) - { - try - { - int txnStatus = txn.getStatus(); - // We can only rollback if a transaction was started (NOT NO_TRANSACTION) and - // if that transaction has not been rolled back (NOT ROLLEDBACK). - // If an exception occurs while the transaction is being created (e.g. no database connection) - // then the status will be NO_TRANSACTION. - if (txnStatus != Status.STATUS_NO_TRANSACTION && txnStatus != Status.STATUS_ROLLEDBACK) - { - txn.rollback(); - } - } - catch (Throwable e1) - { - // A rollback failure should not preclude a retry, but logging of the rollback failure is required - logger.error("Rollback failure. Normal retry behaviour will resume.", e1); - } - } - if (e instanceof RollbackException) - { - lastException = (e.getCause() instanceof RuntimeException) ? - (RuntimeException)e.getCause() : new AlfrescoRuntimeException("Exception in Transaction.", e.getCause()); - } - else - { - lastException = (e instanceof RuntimeException) ? - (RuntimeException)e : new AlfrescoRuntimeException("Exception in Transaction.", e); - } - // Check if there is a cause for retrying - Throwable retryCause = extractRetryCause(e); - if (retryCause != null) - { - // Sleep a random amount of time before retrying. - // The sleep interval increases with the number of retries. - int sleepIntervalRandom = count > 0 ? random.nextInt(count * retryWaitIncrementMs) : minRetryWaitMs; - int sleepInterval = Math.min(maxRetryWaitMs, sleepIntervalRandom); - sleepInterval = Math.max(sleepInterval, minRetryWaitMs); - if (logger.isInfoEnabled() && !logger.isDebugEnabled()) - { - String msg = String.format( - "Retrying %s: count %2d; wait: %1.1fs; msg: \"%s\"; exception: (%s)", - Thread.currentThread().getName(), - count, (double)sleepInterval/1000D, - retryCause.getMessage(), - retryCause.getClass().getName()); - logger.info(msg); - } - try - { - Thread.sleep(sleepInterval); - } - catch (InterruptedException ie) - { - // Do nothing. - } - // Try again - continue; - } - else - { - // It was a 'bad' exception. - throw lastException; - } - } - } - // We've worn out our welcome and retried the maximum number of times. - // So, fail. - throw lastException; - } - - /** - * Sometimes, the exception means retry and sometimes not. - * - * @param cause the cause to examine - * @return Returns the original cause if it is a valid retry cause, otherwise null - */ - @SuppressWarnings("unchecked") - public static Throwable extractRetryCause(Throwable cause) - { - Throwable retryCause = ExceptionStackUtil.getCause(cause, RETRY_EXCEPTIONS); + }; + } + + /** + * Reference to the TransactionService instance. + */ + private TransactionService txnService; + +// /** Performs post-failure exception neatening */ +// private ExceptionTransformer exceptionTransformer; + /** The maximum number of retries. -1 for infinity. */ + private int maxRetries; + /** The minimum time to wait between retries. */ + private int minRetryWaitMs; + /** The maximum time to wait between retries. */ + private int maxRetryWaitMs; + /** How much to increase the wait time with each retry. */ + private int retryWaitIncrementMs; + + /** + * Whether the the transactions may only be reads + */ + private boolean readOnly; + + /** + * Random number generator for retry delays. + */ + private Random random; + + /** + * Callback interface + * @author britt + */ + public interface RetryingTransactionCallback + { + /** + * Perform a unit of transactional work. + * + * @return Return the result of the unit of work + * @throws Throwable This can be anything and will guarantee either a retry or a rollback + */ + public Result execute() throws Throwable; + }; + + /** + * Default constructor. + */ + public RetryingTransactionHelper() + { + this.random = new Random(System.currentTimeMillis()); + this.maxRetries = 20; + this.minRetryWaitMs = 100; + this.maxRetryWaitMs = 2000; + this.retryWaitIncrementMs = 100; + } + + // Setters. + +// /** +// * Optionally set the component that will transform or neaten any exceptions that are +// * propagated. +// */ +// public void setExceptionTransformer(ExceptionTransformer exceptionTransformer) +// { +// this.exceptionTransformer = exceptionTransformer; +// } +// + /** + * Set the TransactionService. + */ + public void setTransactionService(TransactionService service) + { + this.txnService = service; + } + + /** + * Set the maximimum number of retries. -1 for infinity. + */ + public void setMaxRetries(int maxRetries) + { + this.maxRetries = maxRetries; + } + + public void setMinRetryWaitMs(int minRetryWaitMs) + { + this.minRetryWaitMs = minRetryWaitMs; + } + + public void setMaxRetryWaitMs(int maxRetryWaitMs) + { + this.maxRetryWaitMs = maxRetryWaitMs; + } + + public void setRetryWaitIncrementMs(int retryWaitIncrementMs) + { + this.retryWaitIncrementMs = retryWaitIncrementMs; + } + + /** + * Set whether this helper only supports read transactions. + */ + public void setReadOnly(boolean readOnly) + { + this.readOnly = readOnly; + } + + /** + * Execute a callback in a transaction until it succeeds, fails + * because of an error not the result of an optimistic locking failure, + * or a deadlock loser failure, or until a maximum number of retries have + * been attempted. + *

+ * If there is already an active transaction, then the callback is merely + * executed and any retry logic is left to the caller. The transaction + * will attempt to be read-write. + * + * @param cb The callback containing the unit of work. + * @return Returns the result of the unit of work. + * @throws RuntimeException all checked exceptions are converted + */ + public R doInTransaction(RetryingTransactionCallback cb) + { + return doInTransaction(cb, false, false); + } + + /** + * Execute a callback in a transaction until it succeeds, fails + * because of an error not the result of an optimistic locking failure, + * or a deadlock loser failure, or until a maximum number of retries have + * been attempted. + *

+ * If there is already an active transaction, then the callback is merely + * executed and any retry logic is left to the caller. + * + * @param cb The callback containing the unit of work. + * @param readOnly Whether this is a read only transaction. + * @return Returns the result of the unit of work. + * @throws RuntimeException all checked exceptions are converted + */ + public R doInTransaction(RetryingTransactionCallback cb, boolean readOnly) + { + return doInTransaction(cb, readOnly, false); + } + + /** + * Execute a callback in a transaction until it succeeds, fails + * because of an error not the result of an optimistic locking failure, + * or a deadlock loser failure, or until a maximum number of retries have + * been attempted. + *

+ * It is possible to force a new transaction to be created or to partake in + * any existing transaction. + * + * @param cb The callback containing the unit of work. + * @param readOnly Whether this is a read only transaction. + * @param requiresNew true to force a new transaction or + * false to partake in any existing transaction. + * @return Returns the result of the unit of work. + * @throws RuntimeException all checked exceptions are converted + */ + public R doInTransaction(RetryingTransactionCallback cb, boolean readOnly, boolean requiresNew) + { + if (this.readOnly && !readOnly) + { + throw new AccessDeniedException(MSG_READ_ONLY); + } + // Track the last exception caught, so that we + // can throw it if we run out of retries. + RuntimeException lastException = null; + for (int count = 0; count == 0 || count < maxRetries; count++) + { + UserTransaction txn = null; + try + { + if (requiresNew) + { + txn = txnService.getNonPropagatingUserTransaction(readOnly); + } + else + { + TxnReadState readState = AlfrescoTransactionSupport.getTransactionReadState(); + switch (readState) + { + case TXN_READ_ONLY: + if (!readOnly) + { + // The current transaction is read-only, but a writable transaction is requested + throw new AlfrescoRuntimeException("Read-Write transaction started within read-only transaction"); + } + // We are in a read-only transaction and this is what we require so continue with it. + break; + case TXN_READ_WRITE: + // We are in a read-write transaction. It cannot be downgraded so just continue with it. + break; + case TXN_NONE: + // There is no current transaction so we need a new one. + txn = txnService.getUserTransaction(readOnly); + break; + default: + throw new RuntimeException("Unknown transaction state: " + readState); + } + } + if (txn != null) + { + txn.begin(); + // Wrap it to protect it + UserTransactionProtectionAdvise advise = new UserTransactionProtectionAdvise(); + ProxyFactory proxyFactory = new ProxyFactory(txn); + proxyFactory.addAdvice(advise); + UserTransaction wrappedTxn = (UserTransaction) proxyFactory.getProxy(); + // Store the UserTransaction for static retrieval. There is no need to unbind it + // because the transaction management will do that for us. + AlfrescoTransactionSupport.bindResource(KEY_ACTIVE_TRANSACTION, wrappedTxn); + } + // Do the work. + R result = cb.execute(); + // Only commit if we 'own' the transaction. + if (txn != null) + { + if (txn.getStatus() == Status.STATUS_MARKED_ROLLBACK) + { + // Something caused the transaction to be marked for rollback + // There is no recovery or retrying with this + txn.rollback(); + } + else + { + // The transaction hasn't been flagged for failure so the commit + // sould still be good. + txn.commit(); + } + } + if (logger.isDebugEnabled()) + { + if (count != 0) + { + logger.debug("\n" + + "Transaction succeeded: \n" + + " Thread: " + Thread.currentThread().getName() + "\n" + + " Txn: " + txn + "\n" + + " Iteration: " + count); + } + } + return result; + } + catch (Throwable e) + { + // Somebody else 'owns' the transaction, so just rethrow. + if (txn == null) + { + RuntimeException ee = AlfrescoRuntimeException.makeRuntimeException( + e, "Exception from transactional callback: " + cb); + throw ee; + } + if (logger.isDebugEnabled()) + { + logger.debug("\n" + + "Transaction commit failed: \n" + + " Thread: " + Thread.currentThread().getName() + "\n" + + " Txn: " + txn + "\n" + + " Iteration: " + count + "\n" + + " Exception follows:", + e); + } + // Rollback if we can. + if (txn != null) + { + try + { + int txnStatus = txn.getStatus(); + // We can only rollback if a transaction was started (NOT NO_TRANSACTION) and + // if that transaction has not been rolled back (NOT ROLLEDBACK). + // If an exception occurs while the transaction is being created (e.g. no database connection) + // then the status will be NO_TRANSACTION. + if (txnStatus != Status.STATUS_NO_TRANSACTION && txnStatus != Status.STATUS_ROLLEDBACK) + { + txn.rollback(); + } + } + catch (Throwable e1) + { + // A rollback failure should not preclude a retry, but logging of the rollback failure is required + logger.error("Rollback failure. Normal retry behaviour will resume.", e1); + } + } + if (e instanceof RollbackException) + { + lastException = (e.getCause() instanceof RuntimeException) ? + (RuntimeException)e.getCause() : new AlfrescoRuntimeException("Exception in Transaction.", e.getCause()); + } + else + { + lastException = (e instanceof RuntimeException) ? + (RuntimeException)e : new AlfrescoRuntimeException("Exception in Transaction.", e); + } + // Check if there is a cause for retrying + Throwable retryCause = extractRetryCause(e); + if (retryCause != null) + { + // Sleep a random amount of time before retrying. + // The sleep interval increases with the number of retries. + int sleepIntervalRandom = count > 0 ? random.nextInt(count * retryWaitIncrementMs) : minRetryWaitMs; + int sleepInterval = Math.min(maxRetryWaitMs, sleepIntervalRandom); + sleepInterval = Math.max(sleepInterval, minRetryWaitMs); + if (logger.isInfoEnabled() && !logger.isDebugEnabled()) + { + String msg = String.format( + "Retrying %s: count %2d; wait: %1.1fs; msg: \"%s\"; exception: (%s)", + Thread.currentThread().getName(), + count, (double)sleepInterval/1000D, + retryCause.getMessage(), + retryCause.getClass().getName()); + logger.info(msg); + } + try + { + Thread.sleep(sleepInterval); + } + catch (InterruptedException ie) + { + // Do nothing. + } + // Try again + continue; + } + else + { + // It was a 'bad' exception. + throw lastException; + } + } + } + // We've worn out our welcome and retried the maximum number of times. + // So, fail. + throw lastException; + } + + /** + * Sometimes, the exception means retry and sometimes not. + * + * @param cause the cause to examine + * @return Returns the original cause if it is a valid retry cause, otherwise null + */ + @SuppressWarnings("unchecked") + public static Throwable extractRetryCause(Throwable cause) + { + Throwable retryCause = ExceptionStackUtil.getCause(cause, RETRY_EXCEPTIONS); if (retryCause == null || retryCause instanceof SQLGrammarException && ((SQLGrammarException) retryCause).getErrorCode() != 3960) - { - return null; - } + { + return null; + } // A simple match return retryCause; - } - - /** - * Utility method to get the active transaction. The transaction status can be queried and - * marked for rollback. - *

- * NOTE: Any attempt to actually commit or rollback the transaction will cause failures. - * - * @return Returns the currently active user transaction or null if - * there isn't one. - */ - public static UserTransaction getActiveUserTransaction() - { - // Dodge if there is no wrapping transaction - if (AlfrescoTransactionSupport.getTransactionReadState() == TxnReadState.TXN_NONE) - { - return null; - } - // Get the current transaction. There might not be one if the transaction was not started using - // this class i.e. it wasn't started with retries. - UserTransaction txn = (UserTransaction) AlfrescoTransactionSupport.getResource(KEY_ACTIVE_TRANSACTION); - if (txn == null) - { - return null; - } - // Done - return txn; - } - - private static class UserTransactionProtectionAdvise implements MethodBeforeAdvice - { - public void before(Method method, Object[] args, Object target) throws Throwable - { - String methodName = method.getName(); - if (methodName.equals("begin") || methodName.equals("commit") || methodName.equals("rollback")) - { - throw new IllegalAccessException( - "The user transaction cannot be manipulated from within the transactional work load"); - } - } - } -} + } + + /** + * Utility method to get the active transaction. The transaction status can be queried and + * marked for rollback. + *

+ * NOTE: Any attempt to actually commit or rollback the transaction will cause failures. + * + * @return Returns the currently active user transaction or null if + * there isn't one. + */ + public static UserTransaction getActiveUserTransaction() + { + // Dodge if there is no wrapping transaction + if (AlfrescoTransactionSupport.getTransactionReadState() == TxnReadState.TXN_NONE) + { + return null; + } + // Get the current transaction. There might not be one if the transaction was not started using + // this class i.e. it wasn't started with retries. + UserTransaction txn = (UserTransaction) AlfrescoTransactionSupport.getResource(KEY_ACTIVE_TRANSACTION); + if (txn == null) + { + return null; + } + // Done + return txn; + } + + private static class UserTransactionProtectionAdvise implements MethodBeforeAdvice + { + public void before(Method method, Object[] args, Object target) throws Throwable + { + String methodName = method.getName(); + if (methodName.equals("begin") || methodName.equals("commit") || methodName.equals("rollback")) + { + throw new IllegalAccessException( + "The user transaction cannot be manipulated from within the transactional work load"); + } + } + } +} diff --git a/source/java/org/alfresco/repo/transaction/RetryingTransactionHelperTest.java b/source/java/org/alfresco/repo/transaction/RetryingTransactionHelperTest.java index ca89b6d152..6b0db570e7 100644 --- a/source/java/org/alfresco/repo/transaction/RetryingTransactionHelperTest.java +++ b/source/java/org/alfresco/repo/transaction/RetryingTransactionHelperTest.java @@ -24,6 +24,8 @@ */ package org.alfresco.repo.transaction; +import java.util.ConcurrentModificationException; + import javax.transaction.Status; import javax.transaction.UserTransaction; @@ -43,6 +45,7 @@ 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.lang.mutable.MutableInt; import org.hibernate.SessionFactory; import org.springframework.context.ApplicationContext; import org.springframework.dao.ConcurrencyFailureException; @@ -468,11 +471,50 @@ public class RetryingTransactionHelperTest extends TestCase txnHelper.doInTransaction(killConnectionCallback); } + public void testZeroAndNegativeRetries() + { + final MutableInt callCount = new MutableInt(0); + RetryingTransactionCallback callback = new RetryingTransactionCallback() + { + public Long execute() throws Throwable + { + callCount.setValue(callCount.intValue() + 1); + throw new ConcurrentModificationException(); + } + }; + // No retries + callCount.setValue(0); + txnHelper.setMaxRetries(0); + try + { + txnHelper.doInTransaction(callback); + } + catch (ConcurrentModificationException e) + { + // Expected + } + assertEquals("Should have been called exactly once", 1, callCount.intValue()); + + // Negative retries + callCount.setValue(0); + txnHelper.setMaxRetries(-1); + try + { + txnHelper.doInTransaction(callback); + } + catch (ConcurrentModificationException e) + { + // Expected + } + assertEquals("Should have been called exactly once", 1, callCount.intValue()); + } + /** * Helper class to kill the session's DB connection */ private class HibernateConnectionKiller extends HibernateDaoSupport { + @SuppressWarnings("deprecation") private void killConnection() throws Exception { getSession().connection().rollback(); diff --git a/source/java/org/alfresco/repo/workflow/jbpm/AlfrescoJobExecutor.java b/source/java/org/alfresco/repo/workflow/jbpm/AlfrescoJobExecutor.java index 4b28a82f60..76a38a05de 100644 --- a/source/java/org/alfresco/repo/workflow/jbpm/AlfrescoJobExecutor.java +++ b/source/java/org/alfresco/repo/workflow/jbpm/AlfrescoJobExecutor.java @@ -28,6 +28,7 @@ import org.alfresco.service.ServiceRegistry; import org.alfresco.service.transaction.TransactionService; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jbpm.JbpmConfiguration; import org.jbpm.job.executor.JobExecutor; import org.springframework.beans.factory.access.BeanFactoryLocator; import org.springframework.beans.factory.access.BeanFactoryReference; @@ -45,6 +46,7 @@ public class AlfrescoJobExecutor extends JobExecutor private static Log log = LogFactory.getLog(JobExecutor.class); private TransactionService transactionService; + private JbpmConfiguration jbpmConfiguration; /** @@ -55,6 +57,7 @@ public class AlfrescoJobExecutor extends JobExecutor BeanFactoryLocator factoryLocator = new JbpmFactoryLocator(); BeanFactoryReference factory = factoryLocator.useBeanFactory(null); transactionService = (TransactionService)factory.getFactory().getBean(ServiceRegistry.TRANSACTION_SERVICE.getLocalName()); + jbpmConfiguration = (JbpmConfiguration)factory.getFactory().getBean("jbpm_configuration"); } /** @@ -74,7 +77,7 @@ public class AlfrescoJobExecutor extends JobExecutor protected synchronized void startThread() { String threadName = getNextThreadName(); - Thread thread = new AlfrescoJobExecutorThread(threadName, this, getJbpmConfiguration(), getIdleInterval(), getMaxIdleInterval(), getMaxLockTime(), getHistoryMaxSize()); + Thread thread = new AlfrescoJobExecutorThread(threadName, this, jbpmConfiguration, getIdleInterval(), getMaxIdleInterval(), getMaxLockTime(), getHistoryMaxSize()); getThreads().put(threadName, thread); log.debug("starting new job executor thread '" + threadName + "'"); thread.start(); diff --git a/source/java/org/alfresco/repo/workflow/jbpm/AlfrescoJobExecutorThread.java b/source/java/org/alfresco/repo/workflow/jbpm/AlfrescoJobExecutorThread.java index c31cf4aef8..a1d6b3c2d7 100644 --- a/source/java/org/alfresco/repo/workflow/jbpm/AlfrescoJobExecutorThread.java +++ b/source/java/org/alfresco/repo/workflow/jbpm/AlfrescoJobExecutorThread.java @@ -24,6 +24,11 @@ */ package org.alfresco.repo.workflow.jbpm; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; + +import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.jbpm.JbpmConfiguration; import org.jbpm.job.Job; @@ -38,6 +43,13 @@ import org.jbpm.job.executor.JobExecutorThread; public class AlfrescoJobExecutorThread extends JobExecutorThread { private AlfrescoJobExecutor alfrescoJobExecutor; + private boolean isActive = true; + + @Override + public void setActive(boolean isActive) + { + this.isActive = isActive; + } /** * Constructor @@ -48,15 +60,52 @@ public class AlfrescoJobExecutorThread extends JobExecutorThread this.alfrescoJobExecutor = jobExecutor; } + @Override + protected Collection acquireJobs() + { + if (!isActive) + { + return Collections.EMPTY_LIST; + } + return alfrescoJobExecutor.getTransactionService().getRetryingTransactionHelper().doInTransaction( + new RetryingTransactionHelper.RetryingTransactionCallback() { + public Collection execute() throws Throwable + { + return AlfrescoJobExecutorThread.super.acquireJobs(); + } + }); + } + + @Override + protected Date getNextDueDate() + { + if (!isActive) + { + return null; + } + return alfrescoJobExecutor.getTransactionService().getRetryingTransactionHelper().doInTransaction( + new RetryingTransactionHelper.RetryingTransactionCallback() { + public Date execute() throws Throwable + { + return AlfrescoJobExecutorThread.super.getNextDueDate(); + } + }, true); + } + /** * {@inheritDoc} */ @Override protected void executeJob(Job job) { + if (!isActive) + { + return; + } alfrescoJobExecutor.getTransactionService().getRetryingTransactionHelper().doInTransaction(new TransactionJob(job)); } + /** * Helper class for holding Job reference * @@ -76,14 +125,15 @@ public class AlfrescoJobExecutorThread extends JobExecutorThread this.job = job; } - /** - * {@inheritDoc} + /* (non-Javadoc) + * @see org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback#execute() */ public Object execute() throws Throwable { AlfrescoJobExecutorThread.super.executeJob(job); return null; } + } - + } diff --git a/source/java/org/alfresco/repo/workflow/jbpm/AlfrescoTimer.java b/source/java/org/alfresco/repo/workflow/jbpm/AlfrescoTimer.java index 5404e04bbb..d0be26505f 100644 --- a/source/java/org/alfresco/repo/workflow/jbpm/AlfrescoTimer.java +++ b/source/java/org/alfresco/repo/workflow/jbpm/AlfrescoTimer.java @@ -89,7 +89,17 @@ public class AlfrescoTimer extends Timer @SuppressWarnings("synthetic-access") public Boolean doWork() throws Exception { - return AlfrescoTimer.super.execute(jbpmContext); + boolean deleteTimer = AlfrescoTimer.super.execute(jbpmContext); + + // NOTE: there may be an issue in jBPM where a timer that causes a process to + // end is deleted twice (once via specific delete operation and once via + // delete DML statement) which causes a hibernate exception. + // Only delete timer if not at end of process + if (getProcessInstance().getEnd() != null) + { + deleteTimer = false; + } + return deleteTimer; } }, (username == null) ? "system" : username); diff --git a/source/java/org/alfresco/repo/workflow/jbpm/jbpm.cfg.xml b/source/java/org/alfresco/repo/workflow/jbpm/jbpm.cfg.xml index cebd0f5951..5711d2e273 100644 --- a/source/java/org/alfresco/repo/workflow/jbpm/jbpm.cfg.xml +++ b/source/java/org/alfresco/repo/workflow/jbpm/jbpm.cfg.xml @@ -1,7 +1,14 @@ - + - + + + + + + + + @@ -27,7 +34,7 @@ - + diff --git a/source/java/org/alfresco/service/cmr/dictionary/ChildAssociationDefinition.java b/source/java/org/alfresco/service/cmr/dictionary/ChildAssociationDefinition.java index 89f1de7134..61c5043ec4 100644 --- a/source/java/org/alfresco/service/cmr/dictionary/ChildAssociationDefinition.java +++ b/source/java/org/alfresco/service/cmr/dictionary/ChildAssociationDefinition.java @@ -43,4 +43,8 @@ public interface ChildAssociationDefinition extends AssociationDefinition */ public boolean getDuplicateChildNamesAllowed(); + /** + * @return whether timestamps should be propagated upwards along primary associations + */ + public boolean getPropagateTimestamps(); } diff --git a/source/java/org/alfresco/service/cmr/repository/ContentData.java b/source/java/org/alfresco/service/cmr/repository/ContentData.java index 1c7d0ce473..2c1947aa35 100644 --- a/source/java/org/alfresco/service/cmr/repository/ContentData.java +++ b/source/java/org/alfresco/service/cmr/repository/ContentData.java @@ -154,6 +154,21 @@ public class ContentData implements Serializable return ret; } + /** + * Helper method to determine if the data represents any physical content or not. + * + * @param contentData the content to check (may be null) + * @return true if the value is non-null and has a content URL of non-zero length + */ + public static boolean hasContent(ContentData contentData) + { + if (contentData == null) + { + return false; + } + return (contentData.contentUrl != null) && (contentData.size > 0L); + } + /** * Create a content data using the {@link I18NUtil#getLocale() default locale}. * diff --git a/source/java/org/alfresco/wcm/AbstractWCMServiceImplTest.java b/source/java/org/alfresco/wcm/AbstractWCMServiceImplTest.java index f0e9f11099..2cd9ba6557 100644 --- a/source/java/org/alfresco/wcm/AbstractWCMServiceImplTest.java +++ b/source/java/org/alfresco/wcm/AbstractWCMServiceImplTest.java @@ -1,104 +1,106 @@ -/* - * Copyright (C) 2005-2009 Alfresco Software Limited. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program 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 General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: - * http://www.alfresco.com/legal/licensing" - */ -package org.alfresco.wcm; - -import junit.framework.TestCase; - -import org.alfresco.model.ContentModel; -import org.alfresco.service.cmr.security.AuthenticationService; -import org.alfresco.service.cmr.security.PersonService; -import org.alfresco.util.ApplicationContextHelper; -import org.alfresco.util.PropertyMap; -import org.springframework.context.ApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; - -/** - * Abstract WCM Service implementation unit test - * - * @author janv - */ -public class AbstractWCMServiceImplTest extends TestCase -{ - // override jbpm.job.executor idleInterval to 10s (was 1.5m) for WCM unit tests - protected static ApplicationContext ctx =new ClassPathXmlApplicationContext( - new String[] {ApplicationContextHelper.CONFIG_LOCATIONS[0], "classpath:wcm/wcm-jbpm-context.xml"} - ); - - protected static final long SUBMIT_DELAY = 20000L; // 20s - to allow async submit direct workflow to complete (as per 10s idleInterval above) - - // - // test data - // - - protected static final String TEST_RUN = ""+System.currentTimeMillis(); - protected static final boolean CLEAN = true; // cleanup during teardown - - // - // services - // - - protected AuthenticationService authenticationService; - protected PersonService personService; - - - @Override - protected void setUp() throws Exception - { - // Get the required services - authenticationService = (AuthenticationService)ctx.getBean("AuthenticationService"); - personService = (PersonService)ctx.getBean("PersonService"); - } - - @Override - protected void tearDown() throws Exception - { - } - - protected void createUser(String userName) - { - if (authenticationService.authenticationExists(userName) == false) - { - authenticationService.createAuthentication(userName, "PWD".toCharArray()); - - PropertyMap ppOne = new PropertyMap(4); - ppOne.put(ContentModel.PROP_USERNAME, userName); - ppOne.put(ContentModel.PROP_FIRSTNAME, "firstName"); - ppOne.put(ContentModel.PROP_LASTNAME, "lastName"); - ppOne.put(ContentModel.PROP_EMAIL, "email@email.com"); - ppOne.put(ContentModel.PROP_JOBTITLE, "jobTitle"); - - personService.createPerson(ppOne); - } - } - - protected void deleteUser(String userName) - { - if (authenticationService.authenticationExists(userName) == true) - { - personService.deletePerson(userName); - authenticationService.deleteAuthentication(userName); - } - } -} +/* + * Copyright (C) 2005-2009 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.wcm; + +import junit.framework.TestCase; + +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.security.AuthenticationService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.util.ApplicationContextHelper; +import org.alfresco.util.PropertyMap; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +/** + * Abstract WCM Service implementation unit test + * + * @author janv + */ +public class AbstractWCMServiceImplTest extends TestCase +{ + // override jbpm.job.executor idleInterval to 5s (was 1.5m) for WCM unit tests + private static final String SUBMIT_CONFIG_LOCATION = "classpath:wcm/wcm-jbpm-context.xml"; + protected static final long SUBMIT_DELAY = 10000L; // (in millis) 10s - to allow async submit direct workflow to complete (as per 5s idleInterval above) + + + protected static ApplicationContext ctx =new ClassPathXmlApplicationContext( + new String[] {ApplicationContextHelper.CONFIG_LOCATIONS[0], SUBMIT_CONFIG_LOCATION} + ); + + // + // test data + // + + protected static final String TEST_RUN = ""+System.currentTimeMillis(); + protected static final boolean CLEAN = true; // cleanup during teardown + + // + // services + // + + protected AuthenticationService authenticationService; + protected PersonService personService; + + + @Override + protected void setUp() throws Exception + { + // Get the required services + authenticationService = (AuthenticationService)ctx.getBean("AuthenticationService"); + personService = (PersonService)ctx.getBean("PersonService"); + } + + @Override + protected void tearDown() throws Exception + { + } + + protected void createUser(String userName) + { + if (authenticationService.authenticationExists(userName) == false) + { + authenticationService.createAuthentication(userName, "PWD".toCharArray()); + + PropertyMap ppOne = new PropertyMap(4); + ppOne.put(ContentModel.PROP_USERNAME, userName); + ppOne.put(ContentModel.PROP_FIRSTNAME, "firstName"); + ppOne.put(ContentModel.PROP_LASTNAME, "lastName"); + ppOne.put(ContentModel.PROP_EMAIL, "email@email.com"); + ppOne.put(ContentModel.PROP_JOBTITLE, "jobTitle"); + + personService.createPerson(ppOne); + } + } + + protected void deleteUser(String userName) + { + if (authenticationService.authenticationExists(userName) == true) + { + personService.deletePerson(userName); + authenticationService.deleteAuthentication(userName); + } + } +} diff --git a/source/java/org/alfresco/wcm/sandbox/SandboxFactory.java b/source/java/org/alfresco/wcm/sandbox/SandboxFactory.java index 15087fa7d0..92c53bf7c6 100644 --- a/source/java/org/alfresco/wcm/sandbox/SandboxFactory.java +++ b/source/java/org/alfresco/wcm/sandbox/SandboxFactory.java @@ -52,7 +52,6 @@ import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.namespace.QName; import org.alfresco.util.DNSNameMangler; import org.alfresco.util.GUID; -import org.alfresco.util.ParameterCheck; import org.alfresco.wcm.util.WCMUtil; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -1013,34 +1012,26 @@ public final class SandboxFactory extends WCMUtil return workflowStoreName; } - public List listSandboxes(final String wpStoreId, String userName) + // list all sandboxes for a web project + public List listAllSandboxes(final String wpStoreId) { - ParameterCheck.mandatoryString("wpStoreId", wpStoreId); - ParameterCheck.mandatoryString("userName", userName); + List stores = avmService.getStores(); - return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork>() + List sbInfos = new ArrayList(); + for (AVMStoreDescriptor store : stores) { - public List doWork() throws Exception + String storeName = store.getName(); + + // list main stores - not preview stores or workflow stores + if ((storeName.startsWith(wpStoreId)) && + (! WCMUtil.isPreviewStore(storeName)) && + (! WCMUtil.isWorkflowStore(storeName))) { - List stores = avmService.getStores(); - - List sbInfos = new ArrayList(); - for (AVMStoreDescriptor store : stores) - { - String storeName = store.getName(); - - // list main stores - not preview stores or workflow stores - if ((storeName.startsWith(wpStoreId)) && - (! WCMUtil.isPreviewStore(storeName)) && - (! WCMUtil.isWorkflowStore(storeName))) - { - sbInfos.add(getSandbox(storeName)); - } - } - - return sbInfos; + sbInfos.add(getSandbox(storeName)); } - }, userName); + } + + return sbInfos; } public void deleteSandbox(String sbStoreId) @@ -1146,13 +1137,7 @@ public final class SandboxFactory extends WCMUtil public void updateSandboxRoles(final String wpStoreId, List usersToUpdate, Set permissionsList) { // walk existing user sandboxes and remove manager permissions to exclude old managers - List sbInfos = AuthenticationUtil.runAs(new RunAsWork>() - { - public List doWork() throws Exception - { - return listSandboxes(wpStoreId, AuthenticationUtil.getSystemUserName()); - } - }, AuthenticationUtil.getSystemUserName()); + List sbInfos = listAllSandboxes(wpStoreId); // all sandboxes for (SandboxInfo sbInfo : sbInfos) { diff --git a/source/java/org/alfresco/wcm/sandbox/SandboxService.java b/source/java/org/alfresco/wcm/sandbox/SandboxService.java index 4a99a796c1..719d43f94d 100644 --- a/source/java/org/alfresco/wcm/sandbox/SandboxService.java +++ b/source/java/org/alfresco/wcm/sandbox/SandboxService.java @@ -314,7 +314,7 @@ public interface SandboxService * * @param assets list of assets */ - public void revertListNodes(String sbStoreId, List assets); + public void revertListAssets(String sbStoreId, List assets); /** * Revert sandbox to a specific snapshot version ID (ie. for staging sandbox) diff --git a/source/java/org/alfresco/wcm/sandbox/SandboxServiceImpl.java b/source/java/org/alfresco/wcm/sandbox/SandboxServiceImpl.java index 7ad436a1bc..1932658ad5 100644 --- a/source/java/org/alfresco/wcm/sandbox/SandboxServiceImpl.java +++ b/source/java/org/alfresco/wcm/sandbox/SandboxServiceImpl.java @@ -246,7 +246,29 @@ public class SandboxServiceImpl implements SandboxService { ParameterCheck.mandatoryString("wpStoreId", wpStoreId); - return sandboxFactory.listSandboxes(wpStoreId, AuthenticationUtil.getRunAsUser()); + String currentUser = AuthenticationUtil.getRunAsUser(); + + List sbInfos = null; + + if (wpService.isContentManager(wpStoreId, currentUser)) + { + sbInfos = sandboxFactory.listAllSandboxes(wpStoreId); + } + else + { + sbInfos = new ArrayList(1); + + SandboxInfo authorSandbox = getAuthorSandbox(wpStoreId, currentUser); + + if (authorSandbox != null) + { + sbInfos.add(authorSandbox); + } + + sbInfos.add(getSandbox(WCMUtil.buildStagingStoreName(wpStoreId))); // get staging sandbox + } + + return sbInfos; } /* (non-Javadoc) @@ -262,7 +284,13 @@ public class SandboxServiceImpl implements SandboxService throw new AccessDeniedException("Only content managers may list sandboxes for '"+userName+"' (web project id: "+wpStoreId+")"); } - return sandboxFactory.listSandboxes(wpStoreId, userName); + return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork>() + { + public List doWork() throws Exception + { + return listSandboxes(wpStoreId); + } + }, userName); } /* (non-Javadoc) @@ -273,7 +301,7 @@ public class SandboxServiceImpl implements SandboxService ParameterCheck.mandatoryString("sbStoreId", sbStoreId); ParameterCheck.mandatory("sandboxType", sandboxType); - SandboxInfo sbInfo = sandboxFactory.getSandbox(sbStoreId); + SandboxInfo sbInfo = getSandbox(sbStoreId); if (sbInfo != null) { return sbInfo.getSandboxType().equals(sandboxType); @@ -288,6 +316,24 @@ public class SandboxServiceImpl implements SandboxService { ParameterCheck.mandatoryString("sbStoreId", sbStoreId); + String wpStoreId = WCMUtil.getWebProjectStoreId(sbStoreId); + + // check user has read access to web project (ie. is a web user) + if (! wpService.isWebUser(wpStoreId)) + { + return null; + } + + if (! WCMUtil.isStagingStore(sbStoreId)) + { + String currentUser = AuthenticationUtil.getRunAsUser(); + + if (! ((WCMUtil.getUserName(sbStoreId).equals(currentUser)) || (wpService.isContentManager(wpStoreId, currentUser)))) + { + throw new AccessDeniedException("Only content managers may get sandbox '"+sbStoreId+"' (web project id: "+wpStoreId+")"); + } + } + return sandboxFactory.getSandbox(sbStoreId); } @@ -310,11 +356,6 @@ public class SandboxServiceImpl implements SandboxService ParameterCheck.mandatoryString("wpStoreId", wpStoreId); ParameterCheck.mandatoryString("userName", userName); - if (! wpService.isContentManager(wpStoreId)) - { - throw new AccessDeniedException("Only content managers may get author sandbox for '"+userName+"' (web project id: "+wpStoreId+")"); - } - return getSandbox(WCMUtil.buildUserMainStoreName(WCMUtil.buildStagingStoreName(wpStoreId), userName)); } @@ -421,6 +462,10 @@ public class SandboxServiceImpl implements SandboxService ParameterCheck.mandatoryString("dstSandboxStoreId", dstSandboxStoreId); ParameterCheck.mandatoryString("dstRelativePath", dstRelativePath); + // checks sandbox access (TODO review) + getSandbox(srcSandboxStoreId); // ignore result + getSandbox(dstSandboxStoreId); // ignore result + String avmSrcPath = srcSandboxStoreId + WCMUtil.AVM_STORE_SEPARATOR + srcRelativePath; String avmDstPath = dstSandboxStoreId + WCMUtil.AVM_STORE_SEPARATOR + dstRelativePath; @@ -567,6 +612,9 @@ public class SandboxServiceImpl implements SandboxService final String submitLabel, final String submitComment, final Map expirationDates, final Date launchDate, final boolean validateLinks, final boolean autoDeploy) { + // checks sandbox access (TODO review) + getSandbox(sbStoreId); // ignore result + final String wpStoreId = WCMUtil.getWebProjectStoreId(sbStoreId); final String stagingSandboxId = WCMUtil.buildStagingStoreName(wpStoreId); @@ -881,7 +929,7 @@ public class SandboxServiceImpl implements SandboxService List assets = listChanged(sbStoreId, relativePath, true); - revertListNodes(sbStoreId, assets); + revertListAssets(sbStoreId, assets); } /* (non-Javadoc) @@ -903,16 +951,19 @@ public class SandboxServiceImpl implements SandboxService } } - revertListNodes(sbStoreId, assets); + revertListAssets(sbStoreId, assets); } /* (non-Javadoc) - * @see org.alfresco.wcm.sandbox.SandboxService#revertListNodes(java.lang.String, java.util.List) + * @see org.alfresco.wcm.sandbox.SandboxService#revertListAssets(java.lang.String, java.util.List) */ - public void revertListNodes(String sbStoreId, List assets) + public void revertListAssets(String sbStoreId, List assets) { ParameterCheck.mandatoryString("sbStoreId", sbStoreId); + // checks sandbox access (TODO review) + getSandbox(sbStoreId); // ignore result + List> versionPaths = new ArrayList>(assets.size()); List tasks = null; @@ -1072,41 +1123,6 @@ public class SandboxServiceImpl implements SandboxService } } - /** - * Recursively remove locks from a path. Walking child folders looking for files - * to remove locks from. - */ - /* - private void recursivelyRemoveLocks(String wpStoreId, int version, AVMNodeDescriptor desc, String absoluteAVMPath) - { - if (desc.isFile() || desc.isDeletedFile()) - { - avmLockingService.removeLock(wpStoreId, WCMUtil.getStoreRelativePath(absoluteAVMPath)); - } - else - { - if (desc.isDeletedDirectory()) - { - // lookup the previous child and get its contents - final List history = avmService.getHistory(desc, 2); - if (history.size() <= 1) - { - return; - } - desc = history.get(1); - } - - Map list = avmService.getDirectoryListingDirect(desc, true); - for (Map.Entry child : list.entrySet()) - { - String name = child.getKey(); - AVMNodeDescriptor childDesc = child.getValue(); - recursivelyRemoveLocks(wpStoreId, version, childDesc, absoluteAVMPath + "/" + name); - } - } - } - */ - /** * Create Sandbox Transaction listener - invoked after commit */ diff --git a/source/java/org/alfresco/wcm/sandbox/SandboxServiceImplTest.java b/source/java/org/alfresco/wcm/sandbox/SandboxServiceImplTest.java index 2e5b1eb27d..78a8a866f3 100644 --- a/source/java/org/alfresco/wcm/sandbox/SandboxServiceImplTest.java +++ b/source/java/org/alfresco/wcm/sandbox/SandboxServiceImplTest.java @@ -1,1634 +1,1969 @@ -/* - * Copyright (C) 2005-2009 Alfresco Software Limited. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program 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 General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: - * http://www.alfresco.com/legal/licensing" - */ -package org.alfresco.wcm.sandbox; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.alfresco.repo.content.MimetypeMap; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.security.permissions.AccessDeniedException; -import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; -import org.alfresco.service.cmr.avm.AVMService; -import org.alfresco.service.cmr.repository.ContentReader; -import org.alfresco.service.cmr.repository.ContentWriter; -import org.alfresco.service.namespace.QName; -import org.alfresco.service.transaction.TransactionService; -import org.alfresco.util.ApplicationContextHelper; -import org.alfresco.util.PropertyMap; -import org.alfresco.wcm.AbstractWCMServiceImplTest; -import org.alfresco.wcm.asset.AssetInfo; -import org.alfresco.wcm.asset.AssetService; -import org.alfresco.wcm.util.WCMUtil; -import org.alfresco.wcm.webproject.WebProjectInfo; -import org.alfresco.wcm.webproject.WebProjectService; - -/** - * Sandbox Service implementation unit test - * - * @author janv - */ -public class SandboxServiceImplTest extends AbstractWCMServiceImplTest -{ - // base web project - private static final String TEST_WEBPROJ_DNS = "testSandbox-"+TEST_RUN; - private static final String TEST_WEBPROJ_NAME = "testSandbox Web Project Display Name - "+TEST_RUN; - private static final String TEST_WEBPROJ_TITLE = "This is my title"; - private static final String TEST_WEBPROJ_DESCRIPTION = "This is my description"; - private static final String TEST_WEBPROJ_DEFAULT_WEBAPP = WCMUtil.DIR_ROOT; - //private static final boolean TEST_WEBPROJ_USE_AS_TEMPLATE = true; - private static final boolean TEST_WEBPROJ_DONT_USE_AS_TEMPLATE = false; - - // base sandbox - private static final String TEST_SANDBOX = TEST_WEBPROJ_DNS; - - - private static final String USER_ADMIN = "admin"; - - private static final String TEST_USER = "testSandboxUser-"+TEST_RUN; - - private static final String USER_ONE = TEST_USER+"-One"; - private static final String USER_TWO = TEST_USER+"-Two"; - private static final String USER_THREE = TEST_USER+"-Three"; - - private static final int SCALE_USERS = 5; - private static final int SCALE_WEBPROJECTS = 2; - - // - // services - // - - private WebProjectService wpService; - private SandboxService sbService; - private AssetService assetService; - - // TODO: temporary - remove from here when r13170 is merged from V3.1->HEAD - private TransactionService transactionService; - - private AVMService avmService; // non-locking-aware - - //private AVMService avmLockingAwareService; - //private AVMService avmNonLockingAwareService; - - - @Override - protected void setUp() throws Exception - { - super.setUp(); - - // Get the required services - wpService = (WebProjectService)ctx.getBean("WebProjectService"); - sbService = (SandboxService)ctx.getBean("SandboxService"); - assetService = (AssetService)ctx.getBean("AssetService"); - - avmService = (AVMService)ctx.getBean("AVMService"); - - // TODO: temporary - remove from here when r13170 is merged from V3.1->HEAD - transactionService = (TransactionService)ctx.getBean("TransactionService"); - - // WCM locking - //avmLockingAwareService = (AVMService)ctx.getBean("AVMLockingAwareService"); - - // without WCM locking - //avmNonLockingAwareService = (AVMService)ctx.getBean("AVMService"); - - // By default run as Admin - AuthenticationUtil.setFullyAuthenticatedUser(USER_ADMIN); - - createUser(USER_ONE); - createUser(USER_TWO); - createUser(USER_THREE); - } - - @Override - protected void tearDown() throws Exception - { - if (CLEAN) - { - // Switch back to Admin - AuthenticationUtil.setFullyAuthenticatedUser(USER_ADMIN); - - List webProjects = wpService.listWebProjects(); - for (final WebProjectInfo wpInfo : webProjects) - { - if (wpInfo.getStoreId().startsWith(TEST_WEBPROJ_DNS)) - { - // TODO: temporary - remove from here when r13170 is merged from V3.1->HEAD - - // note: added retry for now, due to intermittent concurrent update (during tearDown) possibly due to OrphanReaper ? - // org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [org.alfresco.repo.avm.PlainFileNodeImpl#3752] - RetryingTransactionCallback deleteWebProjectWork = new RetryingTransactionCallback() - { - public Object execute() throws Exception - { - wpService.deleteWebProject(wpInfo.getNodeRef()); - return null; - } - }; - transactionService.getRetryingTransactionHelper().doInTransaction(deleteWebProjectWork); - - } - } - - deleteUser(USER_ONE); - deleteUser(USER_TWO); - deleteUser(USER_THREE); - } - - AuthenticationUtil.clearCurrentSecurityContext(); - super.tearDown(); - } - - public void testSimple() - { - int storeCnt = avmService.getStores().size(); - - // create web project (also creates staging sandbox and admin's author sandbox) - WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-simple", TEST_WEBPROJ_NAME+"-simple", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION, TEST_WEBPROJ_DEFAULT_WEBAPP, TEST_WEBPROJ_DONT_USE_AS_TEMPLATE, null); - String wpStoreId = wpInfo.getStoreId(); - - // list 2 sandboxes - assertEquals(2, sbService.listSandboxes(wpStoreId).size()); - - // list 4 extra AVM stores (2 per sandbox) - assertEquals(storeCnt+4, avmService.getStores().size()); // 2x stating (main,preview), 2x admin author (main, preview) - - // get admin's sandbox - SandboxInfo sbInfo = sbService.getAuthorSandbox(wpStoreId); - assertNotNull(sbInfo); - - // get staging sandbox - sbInfo = sbService.getStagingSandbox(wpStoreId); - assertNotNull(sbInfo); - - // invite user one to the web project and do not implicitly create user one's sandbox - wpService.inviteWebUser(wpStoreId, USER_ONE, WCMUtil.ROLE_CONTENT_PUBLISHER, false); - assertEquals(2, sbService.listSandboxes(wpStoreId).size()); - - sbInfo = sbService.createAuthorSandbox(wpStoreId, USER_TWO); - assertEquals(3, sbService.listSandboxes(wpStoreId).size()); - - sbInfo = sbService.getSandbox(sbInfo.getSandboxId()); - sbService.deleteSandbox(sbInfo.getSandboxId()); - assertEquals(2, sbService.listSandboxes(wpStoreId).size()); - - // delete admin's sandbox - sbService.deleteSandbox(sbService.getAuthorSandbox(wpStoreId).getSandboxId()); - assertEquals(1, sbService.listSandboxes(wpStoreId).size()); - - // delete web project (also deletes staging sandbox) - wpService.deleteWebProject(wpStoreId); - - assertEquals(storeCnt, avmService.getStores().size()); - } - - public void testCreateAuthorSandbox() - { - // Create a web project - WebProjectInfo wpInfo1 = wpService.createWebProject(TEST_WEBPROJ_DNS+"-create-author", TEST_WEBPROJ_NAME+"-author", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION, TEST_WEBPROJ_DEFAULT_WEBAPP, TEST_WEBPROJ_DONT_USE_AS_TEMPLATE, null); - - String expectedUserSandboxId = TEST_SANDBOX+"-create-author" + "--" + USER_ADMIN; - - SandboxInfo sbInfo1 = sbService.getAuthorSandbox(wpInfo1.getStoreId()); - checkSandboxInfo(sbInfo1, expectedUserSandboxId, USER_ADMIN, USER_ADMIN, expectedUserSandboxId, SandboxConstants.PROP_SANDBOX_AUTHOR_MAIN); - - sbInfo1 = sbService.getAuthorSandbox(wpInfo1.getStoreId(), USER_ONE); - assertNull(sbInfo1); - - // Switch to USER_ONE - AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); - - sbInfo1 = sbService.getAuthorSandbox(wpInfo1.getStoreId()); - assertNull(sbInfo1); - - // Switch back to admin - AuthenticationUtil.setFullyAuthenticatedUser(USER_ADMIN); - - // Invite web user - wpService.inviteWebUser(wpInfo1.getStoreId(), USER_ONE, WCMUtil.ROLE_CONTENT_MANAGER); - - // Create author sandbox for user one - admin is the creator - sbInfo1 = sbService.createAuthorSandbox(wpInfo1.getStoreId(), USER_ONE); - - expectedUserSandboxId = TEST_SANDBOX+"-create-author" + "--" + USER_ONE; - - sbInfo1 = sbService.getAuthorSandbox(wpInfo1.getStoreId(), USER_ONE); - checkSandboxInfo(sbInfo1, expectedUserSandboxId, USER_ONE, USER_ADMIN, expectedUserSandboxId, SandboxConstants.PROP_SANDBOX_AUTHOR_MAIN); - - // Switch to USER_ONE - AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); - - // Get author sandbox - sbInfo1 = sbService.getAuthorSandbox(wpInfo1.getStoreId()); - checkSandboxInfo(sbInfo1, expectedUserSandboxId, USER_ONE, USER_ADMIN, expectedUserSandboxId, SandboxConstants.PROP_SANDBOX_AUTHOR_MAIN); - - String userSandboxId = sbInfo1.getSandboxId(); - - // Get (author) sandbox - sbInfo1 = sbService.getSandbox(userSandboxId); - checkSandboxInfo(sbInfo1, expectedUserSandboxId, USER_ONE, USER_ADMIN, expectedUserSandboxId, SandboxConstants.PROP_SANDBOX_AUTHOR_MAIN); - - // Should return same as before - sbInfo1 = sbService.createAuthorSandbox(wpInfo1.getStoreId()); - checkSandboxInfo(sbInfo1, expectedUserSandboxId, USER_ONE, USER_ADMIN, expectedUserSandboxId, SandboxConstants.PROP_SANDBOX_AUTHOR_MAIN); - - // Switch to USER_TWO - AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); - - try - { - // Try to create author sandbox as a non-web user (-ve test) - sbService.createAuthorSandbox(wpInfo1.getStoreId()); // ignore return - fail("Shouldn't be able to create author store since not a web user"); - } - catch (AccessDeniedException exception) - { - // Expected - } - - // Switch back to admin - AuthenticationUtil.setFullyAuthenticatedUser(USER_ADMIN); - - // Invite web user - wpService.inviteWebUser(wpInfo1.getStoreId(), USER_TWO, WCMUtil.ROLE_CONTENT_REVIEWER); - - // Switch to USER_TWO - AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); - - // Get author sandbox - sbInfo1 = sbService.getAuthorSandbox(wpInfo1.getStoreId()); - assertNull(sbInfo1); - - expectedUserSandboxId = TEST_SANDBOX+"-create-author" + "--" + USER_TWO; - - // Create own sandbox - user two is the creator - sbInfo1 = sbService.createAuthorSandbox(wpInfo1.getStoreId()); - checkSandboxInfo(sbInfo1, expectedUserSandboxId, USER_TWO, USER_TWO, expectedUserSandboxId, SandboxConstants.PROP_SANDBOX_AUTHOR_MAIN); - } - - private void checkSandboxInfo(SandboxInfo sbInfo, String expectedStoreId, String expectedName, String expectedCreator, String expectedMainStoreName, QName expectedSandboxType) - { - assertNotNull(sbInfo); - assertEquals(expectedStoreId, sbInfo.getSandboxId()); - assertEquals(expectedName, sbInfo.getName()); - assertEquals(expectedCreator, sbInfo.getCreator()); - assertNotNull(sbInfo.getCreatedDate()); - assertEquals(expectedMainStoreName, sbInfo.getMainStoreName()); - assertEquals(expectedSandboxType, sbInfo.getSandboxType()); - } - - public void testListSandboxes() throws Exception - { - // Create web project - implicitly creates staging sandbox and also author sandbox for web project creator (in this case, admin) - WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-list", TEST_WEBPROJ_NAME+" list", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); - - List sbInfos = sbService.listSandboxes(wpInfo.getStoreId()); - assertEquals(2, sbInfos.size()); // staging sandbox, author sandbox (for admin) - - String expectedUserSandboxId = TEST_SANDBOX+"-list" + "--" + USER_ADMIN; - - // Do detailed check of the sandbox info objects - for (SandboxInfo sbInfo : sbInfos) - { - QName sbType = sbInfo.getSandboxType(); - - if (sbType.equals(SandboxConstants.PROP_SANDBOX_STAGING_MAIN) == true) - { - checkSandboxInfo(sbInfo, TEST_SANDBOX+"-list", TEST_SANDBOX+"-list", USER_ADMIN, TEST_SANDBOX+"-list", SandboxConstants.PROP_SANDBOX_STAGING_MAIN); - } - else if (sbType.equals(SandboxConstants.PROP_SANDBOX_AUTHOR_MAIN) == true) - { - checkSandboxInfo(sbInfo, expectedUserSandboxId, USER_ADMIN, USER_ADMIN, expectedUserSandboxId, SandboxConstants.PROP_SANDBOX_AUTHOR_MAIN); - } - else - { - fail("The sandbox store id " + sbInfo.getSandboxId() + " is not recognised"); - } - } - - // TODO add more here - } - - public void testGetSandbox() - { - // Get a sandbox that isn't there - SandboxInfo sbInfo = sbService.getSandbox(TEST_SANDBOX+"-get"); - assertNull(sbInfo); - - // Create web project - implicitly creates staging sandbox and also admin sandbox (author sandbox for web project creator) - WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-get", TEST_WEBPROJ_NAME+" get", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); - - // Get staging sandbox - sbInfo = sbService.getStagingSandbox(wpInfo.getStoreId()); - checkSandboxInfo(sbInfo, TEST_SANDBOX+"-get", TEST_SANDBOX+"-get", USER_ADMIN, TEST_SANDBOX+"-get", SandboxConstants.PROP_SANDBOX_STAGING_MAIN); - - // Get (staging) sandbox - String stagingSandboxId = wpInfo.getStagingStoreName(); - sbInfo = sbService.getSandbox(stagingSandboxId); - checkSandboxInfo(sbInfo, TEST_SANDBOX+"-get", TEST_SANDBOX+"-get", USER_ADMIN, TEST_SANDBOX+"-get", SandboxConstants.PROP_SANDBOX_STAGING_MAIN); - } - - public void testIsSandboxType() - { - // Get a sandbox that isn't there - SandboxInfo sbInfo = sbService.getSandbox(TEST_SANDBOX+"-is"); - assertNull(sbInfo); - - // Create web project - implicitly creates staging sandbox and also admin sandbox (author sandbox for web project creator) - WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-is", TEST_WEBPROJ_NAME+" is", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); - - // Get staging sandbox - sbInfo = sbService.getStagingSandbox(wpInfo.getStoreId()); - - assertTrue(sbService.isSandboxType(sbInfo.getSandboxId(), SandboxConstants.PROP_SANDBOX_STAGING_MAIN)); - assertFalse(sbService.isSandboxType(sbInfo.getSandboxId(), SandboxConstants.PROP_SANDBOX_AUTHOR_MAIN)); - - // Get author sandbox - sbInfo = sbService.getAuthorSandbox(wpInfo.getStoreId()); - - assertTrue(sbService.isSandboxType(sbInfo.getSandboxId(), SandboxConstants.PROP_SANDBOX_AUTHOR_MAIN)); - assertFalse(sbService.isSandboxType(sbInfo.getSandboxId(), SandboxConstants.PROP_SANDBOX_STAGING_MAIN)); - } - - public void testDeleteSandbox() - { - // Create web project - implicitly creates staging sandbox and also admin sandbox (author sandbox for web project creator) - WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-delete", TEST_WEBPROJ_NAME+" delete", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); - String wpStoreId = wpInfo.getStoreId(); - - assertEquals(2, sbService.listSandboxes(wpStoreId).size()); - - // Get staging sandbox - SandboxInfo sbInfo = sbService.getStagingSandbox(wpStoreId); - - try - { - // Try to delete staging sandbox (-ve test) - sbService.deleteSandbox(sbInfo.getSandboxId()); - fail("Shouldn't be able to delete staging sandbox"); - } - catch (AccessDeniedException exception) - { - // Expected - } - - try - { - // Try to delete non-existant sandbox (-ve test) - sbService.deleteSandbox("some-random-staging-sandbox"); - fail("Shouldn't be able to delete non-existant sandbox"); - } - catch (AccessDeniedException exception) - { - // Expected - } - - // Get admin author sandbox - sbInfo = sbService.getAuthorSandbox(wpInfo.getStoreId()); - sbService.deleteSandbox(sbInfo.getSandboxId()); - - assertEquals(1, sbService.listSandboxes(wpInfo.getStoreId()).size()); - - // Invite web users - wpService.inviteWebUser(wpStoreId, USER_ONE, WCMUtil.ROLE_CONTENT_MANAGER); - wpService.inviteWebUser(wpStoreId, USER_TWO, WCMUtil.ROLE_CONTENT_PUBLISHER); - wpService.inviteWebUser(wpStoreId, USER_THREE, WCMUtil.ROLE_CONTENT_REVIEWER, true); - - assertEquals(2, sbService.listSandboxes(wpStoreId).size()); - - sbService.createAuthorSandbox(wpStoreId, USER_ONE); - sbService.createAuthorSandbox(wpStoreId, USER_TWO); - - assertEquals(4, sbService.listSandboxes(wpStoreId).size()); - - // Switch to USER_TWO - AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); - - // NOTE: content publisher can list other sandboxes - assertEquals(4, sbService.listSandboxes(wpStoreId).size()); - - sbInfo = sbService.getAuthorSandbox(wpStoreId); - assertNotNull(sbInfo); - - // can delete own sandbox - sbService.deleteSandbox(sbInfo.getSandboxId()); - - assertEquals(3, sbService.listSandboxes(wpStoreId).size()); - - sbInfo = sbService.getAuthorSandbox(wpStoreId); - assertNull(sbInfo); - - // but not others - try - { - // Try to delete another author's sandbox as a non-content manager (-ve test) - sbService.deleteSandbox(wpInfo.getStoreId()+"--"+USER_THREE); - fail("Shouldn't be able to delete another author's sandbox"); - } - catch (AccessDeniedException exception) - { - // Expected - } - - // Switch to USER_ONE - AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); - - assertEquals(3, sbService.listSandboxes(wpStoreId).size()); - - // content manager can delete others - sbInfo = sbService.getAuthorSandbox(wpStoreId, USER_THREE); - sbService.deleteSandbox(sbInfo.getSandboxId()); - - assertEquals(2, sbService.listSandboxes(wpStoreId).size()); - } - - // list changed (in this test, new) assets in user sandbox compared to staging sandbox - public void testListNewItems1() - { - WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-listNewItems1", TEST_WEBPROJ_NAME+" listNewItems1", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); - String wpStoreId = wpInfo.getStoreId(); - - assertEquals(2, sbService.listSandboxes(wpStoreId).size()); - - // add web app (in addition to default ROOT web app) - String myWebApp = "myWebApp"; - wpService.createWebApp(wpStoreId, myWebApp, "this is my web app"); - - // Invite web users - wpService.inviteWebUser(wpStoreId, USER_ONE, WCMUtil.ROLE_CONTENT_CONTRIBUTOR); - sbService.createAuthorSandbox(wpStoreId, USER_ONE); - - // Switch to USER_ONE - AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); - - SandboxInfo sbInfo = sbService.getAuthorSandbox(wpStoreId); - String sbStoreId = sbInfo.getSandboxId(); - - // no changes yet - List assets = sbService.listChangedAll(sbStoreId, true); - assertEquals(0, assets.size()); - - String authorSandboxMyWebAppRelativePath = sbInfo.getSandboxRootPath() + "/" + myWebApp; // in this case, my web app is 'myWebApp' - String authorSandboxDefaultWebAppRelativePath = sbInfo.getSandboxRootPath() + "/" + wpInfo.getDefaultWebApp(); // in this case, default web app is 'ROOT' - - assetService.createFile(sbStoreId, authorSandboxMyWebAppRelativePath, "myFile1", null); - - assets = sbService.listChangedAll(sbStoreId, false); - assertEquals(1, assets.size()); - assertEquals("myFile1", assets.get(0).getName()); - - assetService.createFolder(sbStoreId, authorSandboxDefaultWebAppRelativePath, "myDir1", null); - assetService.createFile(sbStoreId, authorSandboxDefaultWebAppRelativePath+"/myDir1", "myFile2", null); - assetService.createFolder(sbStoreId, authorSandboxDefaultWebAppRelativePath+"/myDir1", "myDir2", null); - assetService.createFile(sbStoreId, authorSandboxDefaultWebAppRelativePath+"/myDir1/myDir2", "myFile3", null); - assetService.createFile(sbStoreId, authorSandboxDefaultWebAppRelativePath+"/myDir1/myDir2", "myFile4", null); - assetService.createFolder(sbStoreId, authorSandboxDefaultWebAppRelativePath+"/myDir1", "myDir3", null); - - assets = sbService.listChangedAll(sbStoreId, false); - assertEquals(2, assets.size()); // new dir with new dirs/files is returned as single change - - for (AssetInfo asset : assets) - { - if (asset.getName().equals("myFile1") && asset.isFile()) - { - continue; - } - else if (asset.getName().equals("myDir1") && asset.isFolder()) - { - continue; - } - else - { - fail("The asset '" + asset.getName() + "' is not recognised"); - } - } - - assets = sbService.listChangedWebApp(sbStoreId, wpInfo.getDefaultWebApp(), false); - assertEquals(1, assets.size()); - - for (AssetInfo asset : assets) - { - if (asset.getName().equals("myDir1") && asset.isFolder()) - { - continue; - } - else - { - fail("The asset '" + asset.getName() + "' is not recognised"); - } - } - - assets = sbService.listChanged(sbStoreId, authorSandboxDefaultWebAppRelativePath+"/myDir1", false); - assertEquals(1, assets.size()); - - for (AssetInfo asset : assets) - { - if (asset.getName().equals("myDir1") && asset.isFolder()) - { - continue; - } - else - { - fail("The asset '" + asset.getName() + "' is not recognised"); - } - } - } - - // list changed (in this test, new) assets in two different user sandboxes compared to each other - public void testListNewItems2() - { - WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-listNewItems2", TEST_WEBPROJ_NAME+" listNewItems2", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); - String wpStoreId = wpInfo.getStoreId(); - - // Invite web users - wpService.inviteWebUser(wpStoreId, USER_ONE, WCMUtil.ROLE_CONTENT_CONTRIBUTOR, true); - wpService.inviteWebUser(wpStoreId, USER_TWO, WCMUtil.ROLE_CONTENT_CONTRIBUTOR, true); - - // Switch to USER_ONE - AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); - - SandboxInfo sbInfo1 = sbService.getAuthorSandbox(wpStoreId); - String sbStoreId = sbInfo1.getSandboxId(); - - List assets = sbService.listChangedAll(sbStoreId, true); - assertEquals(0, assets.size()); - - assetService.createFile(sbStoreId, sbInfo1.getSandboxRootPath(), "myFile1", null); - - assets = sbService.listChangedAll(sbStoreId, false); - assertEquals(1, assets.size()); - assertEquals("myFile1", assets.get(0).getName()); - - // Switch to USER_TWO - AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); - - SandboxInfo sbInfo2 = sbService.getAuthorSandbox(wpStoreId); - sbStoreId = sbInfo2.getSandboxId(); - - assets = sbService.listChangedAll(sbStoreId, true); - assertEquals(0, assets.size()); - - assetService.createFile(sbStoreId, sbInfo2.getSandboxRootPath(), "myFile2", null); - assetService.createFile(sbStoreId, sbInfo2.getSandboxRootPath(), "myFile3", null); - - assets = sbService.listChangedAll(sbStoreId, false); - assertEquals(2, assets.size()); - - for (AssetInfo asset : assets) - { - if (asset.getName().equals("myFile2") && asset.isFile()) - { - continue; - } - else if (asset.getName().equals("myFile3") && asset.isFile()) - { - continue; - } - else - { - fail("The asset '" + asset.getName() + "' is not recognised"); - } - } - - // Switch back to admin - AuthenticationUtil.setFullyAuthenticatedUser(USER_ADMIN); - - sbInfo1 = sbService.getAuthorSandbox(wpStoreId, USER_ONE); - sbInfo2 = sbService.getAuthorSandbox(wpStoreId, USER_TWO); - - assets = sbService.listChanged(sbInfo1.getSandboxId(), sbInfo1.getSandboxRootPath(), sbInfo2.getSandboxId(), sbInfo2.getSandboxRootPath(), false); - assertEquals(1, assets.size()); - assertEquals("myFile1", assets.get(0).getName()); - - assets = sbService.listChanged(sbInfo2.getSandboxId(), sbInfo2.getSandboxRootPath(), sbInfo1.getSandboxId(), sbInfo1.getSandboxRootPath(), false); - assertEquals(2, assets.size()); - - for (AssetInfo asset : assets) - { - if (asset.getName().equals("myFile2") && asset.isFile()) - { - continue; - } - else if (asset.getName().equals("myFile3") && asset.isFile()) - { - continue; - } - else - { - fail("The asset '" + asset.getName() + "' is not recognised"); - } - } - } - - /* - // list changed (in this test, new) assets in two different user sandboxes compared to each other - without locking - public void testListNewItems3() - { - WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-listNewItems2", TEST_WEBPROJ_NAME+" listNewItems2", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); - String wpStoreId = wpInfo.getStoreId(); - - // Invite web users - wpService.inviteWebUser(wpStoreId, USER_ONE, WCMUtil.ROLE_CONTENT_CONTRIBUTOR, true); - wpService.inviteWebUser(wpStoreId, USER_TWO, WCMUtil.ROLE_CONTENT_CONTRIBUTOR, true); - - // Switch to USER_ONE - AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); - - SandboxInfo sbInfo1 = sbService.getAuthorSandbox(wpStoreId); - String sbStoreId = sbInfo1.getSandboxId(); - - List assets = sbService.listChangedAll(sbStoreId, true); - assertEquals(0, assets.size()); - - String authorSandboxRootPath = sbStoreId + AVM_STORE_SEPARATOR + sbInfo1.getSandboxRootPath(); - - avmNonLockingAwareService.createFile(authorSandboxRootPath, "myFile1"); - - assets = sbService.listChangedAll(sbStoreId, false); - assertEquals(1, assets.size()); - assertEquals("myFile1", assets.get(0).getName()); - - // Switch to USER_TWO - AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); - - SandboxInfo sbInfo2 = sbService.getAuthorSandbox(wpStoreId); - sbStoreId = sbInfo2.getSandboxId(); - - assets = sbService.listChangedAll(sbStoreId, true); - assertEquals(0, assets.size()); - - authorSandboxRootPath = sbStoreId + AVM_STORE_SEPARATOR + sbInfo2.getSandboxRootPath(); - - avmNonLockingAwareService.createFile(authorSandboxRootPath, "myFile1"); // allowed, since using base (non-locking-aware) AVM service - avmNonLockingAwareService.createFile(authorSandboxRootPath, "myFile2"); - avmNonLockingAwareService.createFile(authorSandboxRootPath, "myFile3"); - - assets = sbService.listChangedAll(sbStoreId, false); - assertEquals(3, assets.size()); - - for (AssetInfo asset : assets) - { - if (asset.getName().equals("myFile1") && asset.isFile()) - { - continue; - } - else if (asset.getName().equals("myFile2") && asset.isFile()) - { - continue; - } - else if (asset.getName().equals("myFile3") && asset.isFile()) - { - continue; - } - else - { - fail("The asset '" + asset.getName() + "' is not recognised"); - } - } - - // Switch back to admin - AuthenticationUtil.setFullyAuthenticatedUser(USER_ADMIN); - - sbInfo1 = sbService.getAuthorSandbox(wpStoreId, USER_ONE); - sbInfo2 = sbService.getAuthorSandbox(wpStoreId, USER_TWO); - - assets = sbService.listChanged(sbInfo1.getSandboxId(), sbInfo1.getSandboxRootPath(), sbInfo2.getSandboxId(), sbInfo2.getSandboxRootPath(), false); - assertEquals(1, assets.size()); - assertEquals("myFile1", assets.get(0).getName()); - - assets = sbService.listChanged(sbInfo2.getSandboxId(), sbInfo1.getSandboxRootPath(), sbInfo1.getSandboxId(), sbInfo2.getSandboxRootPath(), false); - assertEquals(3, assets.size()); - - for (AssetInfo asset : assets) - { - if (asset.getName().equals("myFile1") && asset.isFile()) - { - continue; - } - else if (asset.getName().equals("myFile2") && asset.isFile()) - { - continue; - } - else if (asset.getName().equals("myFile3") && asset.isFile()) - { - continue; - } - else - { - fail("The asset '" + asset.getName() + "' is not recognised"); - } - } - } - */ - - // submit new assets in user sandbox to staging sandbox - public void testSubmitNewItems1() throws InterruptedException - { - WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-submitNewItems1", TEST_WEBPROJ_NAME+" submitNewItems1", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); - - String wpStoreId = wpInfo.getStoreId(); - String webApp = wpInfo.getDefaultWebApp(); - String stagingSandboxId = wpInfo.getStagingStoreName(); - - // Invite web user - wpService.inviteWebUser(wpStoreId, USER_ONE, WCMUtil.ROLE_CONTENT_CONTRIBUTOR, true); - - // Switch to USER_ONE - AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); - - SandboxInfo sbInfo = sbService.getAuthorSandbox(wpStoreId); - String authorSandboxId = sbInfo.getSandboxId(); - - // no changes yet - List assets = sbService.listChangedAll(authorSandboxId, true); - assertEquals(0, assets.size()); - - String authorSandboxPath = sbInfo.getSandboxRootPath() + "/" + webApp; - - assetService.createFile(authorSandboxId, authorSandboxPath, "myFile1", null); - assetService.createFolder(authorSandboxId, authorSandboxPath, "myDir1", null); - assetService.createFile(authorSandboxId, authorSandboxPath+"/myDir1", "myFile2", null); - assetService.createFolder(authorSandboxId, authorSandboxPath+"/myDir1", "myDir2", null); - assetService.createFile(authorSandboxId, authorSandboxPath+"/myDir1/myDir2", "myFile3", null); - assetService.createFile(authorSandboxId, authorSandboxPath+"/myDir1/myDir2", "myFile4", null); - assetService.createFolder(authorSandboxId, authorSandboxPath+"/myDir1", "myDir3", null); - - assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); - assertEquals(2, assets.size()); // new dir with new dirs/files is returned as single change - - // check staging before - String stagingSandboxPath = sbInfo.getSandboxRootPath() + "/" + webApp; - assertEquals(0, assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false).size()); - - // submit (new assets) ! - sbService.submitWebApp(authorSandboxId, webApp, "a submit label", "a submit comment"); - - Thread.sleep(SUBMIT_DELAY); - - assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); - assertEquals(0, assets.size()); - - // check staging after - List listing = assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false); - assertEquals(2, listing.size()); - - for (AssetInfo asset : listing) - { - if (asset.getName().equals("myFile1") && asset.isFile()) - { - continue; - } - else if (asset.getName().equals("myDir1") && asset.isFolder()) - { - continue; - } - else - { - fail("The asset '" + asset.getName() + "' is not recognised"); - } - } - } - - // submit changed assets in user sandbox to staging sandbox - public void testSubmitChangedAssets1() throws IOException, InterruptedException - { - WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-submitChangedAssets1", TEST_WEBPROJ_NAME+" submitChangedAssets1", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); - - final String wpStoreId = wpInfo.getStoreId(); - final String webApp = wpInfo.getDefaultWebApp(); - final String stagingSandboxId = wpInfo.getStagingStoreName(); - - // Invite web users - - // TODO - pending merge of ETWOTWO-1088 fix - /* - wpService.inviteWebUser(wpStoreId, USER_ONE, WCMUtil.ROLE_CONTENT_CONTRIBUTOR, true); - wpService.inviteWebUser(wpStoreId, USER_TWO, WCMUtil.ROLE_CONTENT_PUBLISHER, true); - */ - - wpService.inviteWebUser(wpStoreId, USER_ONE, WCMUtil.ROLE_CONTENT_MANAGER, true); - wpService.inviteWebUser(wpStoreId, USER_TWO, WCMUtil.ROLE_CONTENT_MANAGER, true); - - // Switch to USER_ONE - AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); - - SandboxInfo sbInfo = sbService.getAuthorSandbox(wpStoreId); - String authorSandboxId = sbInfo.getSandboxId(); - - // no changes yet - List assets = sbService.listChangedAll(authorSandboxId, true); - assertEquals(0, assets.size()); - - //String authorSandboxWebppPath = authorSandboxId + AVM_STORE_SEPARATOR + sbInfo.getSandboxRootPath() + "/" + webApp; - - final String MYFILE1 = "This is myFile1"; - ContentWriter writer = assetService.createFileWebApp(authorSandboxId, webApp, "/", "myFile1"); - writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); - writer.setEncoding("UTF-8"); - writer.putContent(MYFILE1); - - assetService.createFolderWebApp(authorSandboxId, webApp, "/", "myDir1"); - - final String MYFILE2 = "This is myFile2"; - writer = assetService.createFileWebApp(authorSandboxId, webApp, "/myDir1", "myFile2"); - writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); - writer.setEncoding("UTF-8"); - writer.putContent(MYFILE2); - - assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); - assertEquals(2, assets.size()); - - // check staging before - String stagingSandboxPath = sbInfo.getSandboxRootPath() + "/" + webApp; - assertEquals(0, assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false).size()); - - // submit (new assets) ! - sbService.submitWebApp(authorSandboxId, webApp, "a submit label", "a submit comment"); - - Thread.sleep(SUBMIT_DELAY); - - assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); - assertEquals(0, assets.size()); - - // check staging after - List listing = assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false); - assertEquals(2, listing.size()); - - // Switch to USER_TWO - AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); - - sbInfo = sbService.getAuthorSandbox(wpStoreId); - authorSandboxId = sbInfo.getSandboxId(); - - // no changes yet - assets = sbService.listChangedAll(authorSandboxId, true); - assertEquals(0, assets.size()); - - final String MYFILE1_MODIFIED = "This is myFile1 ... modified by "+USER_TWO; - - writer = assetService.getContentWriter(assetService.getAssetWebApp(authorSandboxId, webApp, "/myFile1")); - writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); - writer.setEncoding("UTF-8"); - writer.putContent(MYFILE1_MODIFIED); - - final String MYFILE2_MODIFIED = "This is myFile2 ... modified by "+USER_TWO; - writer = assetService.getContentWriter(assetService.getAssetWebApp(authorSandboxId, webApp, "/myDir1/myFile2")); - writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); - writer.setEncoding("UTF-8"); - writer.putContent(MYFILE2_MODIFIED); - - assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); - assertEquals(2, assets.size()); - - // check staging before - stagingSandboxPath = sbInfo.getSandboxRootPath() + "/" + webApp; - - ContentReader reader = assetService.getContentReader(assetService.getAsset(stagingSandboxId, -1, stagingSandboxPath+"/myFile1", false)); - InputStream in = reader.getContentInputStream(); - byte[] buff = new byte[1024]; - in.read(buff); - in.close(); - assertEquals(MYFILE1, new String(buff, 0, MYFILE1.length())); // assumes 1byte=1char - - reader = assetService.getContentReader(assetService.getAsset(stagingSandboxId, -1, stagingSandboxPath+"/myDir1/myFile2", false)); - in = reader.getContentInputStream(); - buff = new byte[1024]; - in.read(buff); - in.close(); - assertEquals(MYFILE2, new String(buff, 0, MYFILE2.length())); - - // submit (modified assets) ! - sbService.submitWebApp(authorSandboxId, webApp, "my label", null); - - Thread.sleep(SUBMIT_DELAY); - - assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); - assertEquals(0, assets.size()); - - // check staging after - reader = assetService.getContentReader(assetService.getAsset(stagingSandboxId, -1, stagingSandboxPath+"/myFile1", false)); - in = reader.getContentInputStream(); - buff = new byte[1024]; - in.read(buff); - in.close(); - assertEquals(MYFILE1_MODIFIED, new String(buff, 0, MYFILE1_MODIFIED.length())); - - reader = assetService.getContentReader(assetService.getAsset(stagingSandboxId, -1, stagingSandboxPath+"/myDir1/myFile2", false)); - in = reader.getContentInputStream(); - buff = new byte[1024]; - in.read(buff); - in.close(); - assertEquals(MYFILE2_MODIFIED, new String(buff, 0, MYFILE1_MODIFIED.length())); - } - - // submit "all" changed assets in user sandbox to staging sandbox (not using default webapp) - public void testSubmitChangedAssets2() throws IOException, InterruptedException - { - WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-submitChangedAssets1", TEST_WEBPROJ_NAME+" submitChangedAssets1", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); - - final String wpStoreId = wpInfo.getStoreId(); - final String stagingSandboxId = wpInfo.getStagingStoreName(); - - SandboxInfo sbInfo = sbService.getAuthorSandbox(wpStoreId); - String authorSandboxId = sbInfo.getSandboxId(); - - String rootPath = sbInfo.getSandboxRootPath(); // currently /www/avm_webapps - - // no changes yet - List assets = sbService.listChangedAll(authorSandboxId, true); - assertEquals(0, assets.size()); - - assetService.createFolder(authorSandboxId, rootPath, "a", null); - assetService.createFolder(authorSandboxId, rootPath+"/a", "b", null); - assetService.createFolder(authorSandboxId, rootPath+"/a/b", "c", null); - - final String MYFILE1 = "This is foo"; - ContentWriter writer = assetService.createFile(authorSandboxId, rootPath+"/a/b/c", "foo", null); - writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); - writer.setEncoding("UTF-8"); - writer.putContent(MYFILE1); - - final String MYFILE2 = "This is bar"; - writer = assetService.createFile(authorSandboxId, rootPath+"/a/b/c", "bar", null); - writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); - writer.setEncoding("UTF-8"); - writer.putContent(MYFILE2); - - assets = sbService.listChangedAll(authorSandboxId, true); - assertEquals(1, assets.size()); - - // check staging before - assertEquals(1, assetService.listAssets(stagingSandboxId, -1, rootPath, false).size()); // note: currently includes default webapp ('ROOT') - - // submit (new assets) ! - sbService.submitAll(authorSandboxId, "a submit label", "a submit comment"); - - Thread.sleep(SUBMIT_DELAY); - - // check staging after - List listing = assetService.listAssets(stagingSandboxId, -1, rootPath, false); - assertEquals(2, listing.size()); // 'a' and 'ROOT' - - // no changes in sandbox - assets = sbService.listChangedAll(authorSandboxId, true); - assertEquals(0, assets.size()); - - final String MYFILE3 = "This is figs"; - writer = assetService.createFile(authorSandboxId, rootPath, "figs", null); - writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); - writer.setEncoding("UTF-8"); - writer.putContent(MYFILE3); - - final String MYFILE1_MODIFIED = "This is foo ... modified"; - writer = assetService.getContentWriter(assetService.getAsset(authorSandboxId, rootPath+"/a/b/c/foo")); - writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); - writer.setEncoding("UTF-8"); - writer.putContent(MYFILE1_MODIFIED); - - assetService.deleteAsset(assetService.getAsset(authorSandboxId, rootPath+"/a/b/c/bar")); - - assets = sbService.listChangedAll(authorSandboxId, true); - assertEquals(3, assets.size()); - - // check staging before - listing = assetService.listAssets(stagingSandboxId, -1, rootPath, false); - assertEquals(2, listing.size()); // 'a' and 'ROOT' - - // submit all (modified assets) ! - sbService.submitAll(authorSandboxId, "my label", null); - - Thread.sleep(SUBMIT_DELAY); - - assets = sbService.listChangedAll(authorSandboxId, true); - assertEquals(0, assets.size()); - - // check staging after - listing = assetService.listAssets(stagingSandboxId, -1, rootPath, false); - assertEquals(3, listing.size()); // 'figs', 'a' and 'ROOT' - } - - // submit deleted assets in user sandbox to staging sandbox - public void testSubmitDeletedItems1() throws IOException, InterruptedException - { - WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-submitDeletedItems1", TEST_WEBPROJ_NAME+" submitDeletedItems1", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); - - final String wpStoreId = wpInfo.getStoreId(); - final String webApp = wpInfo.getDefaultWebApp(); - final String stagingSandboxId = wpInfo.getStagingStoreName(); - - // Invite web users - wpService.inviteWebUser(wpStoreId, USER_ONE, WCMUtil.ROLE_CONTENT_CONTRIBUTOR, true); - wpService.inviteWebUser(wpStoreId, USER_TWO, WCMUtil.ROLE_CONTENT_MANAGER, true); // note: publisher does not have permission to delete - - // Switch to USER_ONE - AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); - - SandboxInfo sbInfo = sbService.getAuthorSandbox(wpStoreId); - String authorSandboxId = sbInfo.getSandboxId(); - - // no changes yet - List assets = sbService.listChangedAll(authorSandboxId, true); - assertEquals(0, assets.size()); - - String authorSandboxPath = sbInfo.getSandboxRootPath() + "/" + webApp; - - final String MYFILE1 = "This is myFile1"; - ContentWriter writer = assetService.createFile(authorSandboxId, authorSandboxPath, "myFile1", null); - writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); - writer.setEncoding("UTF-8"); - writer.putContent(MYFILE1); - - assetService.createFolder(authorSandboxId, authorSandboxPath, "myDir1", null); - assetService.createFolder(authorSandboxId, authorSandboxPath+"/myDir1", "myDir2", null); - - final String MYFILE2 = "This is myFile2"; - writer = assetService.createFile(authorSandboxId, authorSandboxPath+"/myDir1", "myFile2", null); - writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); - writer.setEncoding("UTF-8"); - writer.putContent(MYFILE2); - - assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); - assertEquals(2, assets.size()); - - // check staging before - String stagingSandboxPath = sbInfo.getSandboxRootPath() + "/" + webApp; - assertEquals(0, assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false).size()); - - // submit (new assets) ! - sbService.submitWebApp(authorSandboxId, webApp, "a submit label", "a submit comment"); - - Thread.sleep(SUBMIT_DELAY); - - assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); - assertEquals(0, assets.size()); - - // check staging after - List listing = assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false); - assertEquals(2, listing.size()); - - // Switch to USER_TWO - AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); - - sbInfo = sbService.getAuthorSandbox(wpStoreId); - authorSandboxId = sbInfo.getSandboxId(); - - // no changes yet - assets = sbService.listChangedAll(authorSandboxId, true); - assertEquals(0, assets.size()); - - //authorSandboxWebppPath = authorSandboxId + AVM_STORE_SEPARATOR + sbInfo.getSandboxRootPath() + "/" + webApp; - - - assetService.deleteAsset(assetService.getAssetWebApp(authorSandboxId, webApp, "myFile1")); - assetService.deleteAsset(assetService.getAssetWebApp(authorSandboxId, webApp, "/myDir1/myDir2")); - - // do not list deleted - assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); - assertEquals(0, assets.size()); - - // do list deleted - assets = sbService.listChangedWebApp(authorSandboxId, webApp, true); - assertEquals(2, assets.size()); - - // check staging before - //stagingSandboxWebppPath = stagingSandboxId + AVM_STORE_SEPARATOR + sbInfo.getSandboxRootPath() + "/" + webApp; - - assertNotNull(assetService.getAssetWebApp(stagingSandboxId, webApp, "/myFile1")); - assertNotNull(assetService.getAssetWebApp(stagingSandboxId, webApp, "/myDir1")); - assertNotNull(assetService.getAssetWebApp(stagingSandboxId, webApp, "/myDir1/myDir2")); - assertNotNull(assetService.getAssetWebApp(stagingSandboxId, webApp, "/myDir1/myFile2")); - - // submit (deleted assets) ! - sbService.submitWebApp(authorSandboxId, webApp, "my label", null); - - Thread.sleep(SUBMIT_DELAY); - - assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); - assertEquals(0, assets.size()); - - // check staging after - assertNull(assetService.getAssetWebApp(stagingSandboxId, webApp, "/myFile1")); - assertNull(assetService.getAssetWebApp(stagingSandboxId, webApp, "/myDir1/myDir2")); - - assertNotNull(assetService.getAssetWebApp(stagingSandboxId, webApp, "/myDir1")); - assertNotNull(assetService.getAssetWebApp(stagingSandboxId, webApp, "/myDir1/myFile2")); - } - - // revert all (changed) assets in user sandbox - public void testRevertAll() throws IOException, InterruptedException - { - WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-revertChangedAssets", TEST_WEBPROJ_NAME+" revertChangedAssets", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); - - final String wpStoreId = wpInfo.getStoreId(); - final String webApp = wpInfo.getDefaultWebApp(); - final String stagingSandboxId = wpInfo.getStagingStoreName(); - - // Invite web users - wpService.inviteWebUser(wpStoreId, USER_ONE, WCMUtil.ROLE_CONTENT_CONTRIBUTOR, true); - - // TODO - pending fix for ETWOTWO-981 - //wpService.inviteWebUser(wpStoreId, USER_TWO, WCMUtil.ROLE_CONTENT_PUBLISHER, true); - - wpService.inviteWebUser(wpStoreId, USER_TWO, WCMUtil.ROLE_CONTENT_MANAGER, true); - - // Switch to USER_ONE - AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); - - SandboxInfo sbInfo = sbService.getAuthorSandbox(wpStoreId); - String authorSandboxId = sbInfo.getSandboxId(); - - // no changes yet - List assets = sbService.listChangedAll(authorSandboxId, true); - assertEquals(0, assets.size()); - - String authorSandboxPath = sbInfo.getSandboxRootPath() + "/" + webApp; - - final String MYFILE1 = "This is myFile1"; - ContentWriter writer = assetService.createFile(authorSandboxId, authorSandboxPath, "myFile1", null); - writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); - writer.setEncoding("UTF-8"); - writer.putContent(MYFILE1); - - assetService.createFolder(authorSandboxId, authorSandboxPath, "myDir1", null); - - final String MYFILE2 = "This is myFile2"; - writer = assetService.createFile(authorSandboxId, authorSandboxPath+"/myDir1", "myFile2", null); - writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); - writer.setEncoding("UTF-8"); - writer.putContent(MYFILE2); - - assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); - assertEquals(2, assets.size()); - - // check staging before - String stagingSandboxPath = sbInfo.getSandboxRootPath() + "/" + webApp; - assertEquals(0, assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false).size()); - - // submit (new assets) ! - sbService.submitWebApp(authorSandboxId, webApp, "a submit label", "a submit comment"); - - Thread.sleep(SUBMIT_DELAY); - - assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); - assertEquals(0, assets.size()); - - // check staging after - List listing = assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false); - assertEquals(2, listing.size()); - - // Switch to USER_TWO - AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); - - sbInfo = sbService.getAuthorSandbox(wpStoreId); - authorSandboxId = sbInfo.getSandboxId(); - - // no changes yet - assets = sbService.listChangedAll(authorSandboxId, true); - assertEquals(0, assets.size()); - - //authorSandboxWebppPath = authorSandboxId + AVM_STORE_SEPARATOR + sbInfo.getSandboxRootPath() + "/" + webApp; - - final String MYFILE1_MODIFIED = "This is myFile1 ... modified by "+USER_TWO; - writer = assetService.getContentWriter(assetService.getAssetWebApp(authorSandboxId, webApp, "/myFile1")); - writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); - writer.setEncoding("UTF-8"); - writer.putContent(MYFILE1_MODIFIED); - - final String MYFILE2_MODIFIED = "This is myFile2 ... modified by "+USER_TWO; - writer = assetService.getContentWriter(assetService.getAssetWebApp(authorSandboxId, webApp, "/myDir1/myFile2")); - writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); - writer.setEncoding("UTF-8"); - writer.putContent(MYFILE2_MODIFIED); - - assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); - assertEquals(2, assets.size()); - - // check staging before - stagingSandboxPath = sbInfo.getSandboxRootPath() + "/" + webApp; - - ContentReader reader = assetService.getContentReader(assetService. getAsset(stagingSandboxId, -1, stagingSandboxPath+"/myFile1", false)); - InputStream in = reader.getContentInputStream(); - byte[] buff = new byte[1024]; - in.read(buff); - in.close(); - assertEquals(MYFILE1, new String(buff, 0, MYFILE1.length())); // assumes 1byte = 1char - - reader = assetService.getContentReader(assetService. getAsset(stagingSandboxId, -1, stagingSandboxPath+"/myDir1/myFile2", false)); - in = reader.getContentInputStream(); - buff = new byte[1024]; - in.read(buff); - in.close(); - assertEquals(MYFILE2, new String(buff, 0, MYFILE2.length())); - - // revert (modified assets) ! - sbService.revertWebApp(authorSandboxId, webApp); - - assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); - assertEquals(0, assets.size()); - - // check staging after - reader = assetService.getContentReader(assetService.getAsset(stagingSandboxId, -1, stagingSandboxPath+"/myFile1", false)); - in = reader.getContentInputStream(); - buff = new byte[1024]; - in.read(buff); - in.close(); - assertEquals(MYFILE1, new String(buff, 0, MYFILE1.length())); - - reader = assetService.getContentReader(assetService.getAsset(stagingSandboxId, -1, stagingSandboxPath+"/myDir1/myFile2", false)); - in = reader.getContentInputStream(); - buff = new byte[1024]; - in.read(buff); - in.close(); - assertEquals(MYFILE2, new String(buff, 0, MYFILE2.length())); - } - - public void testListSnapshots() throws IOException, InterruptedException - { - Date fromDate = new Date(); - - WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-listSnapshots", TEST_WEBPROJ_NAME+" listSnapshots", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); - - final String wpStoreId = wpInfo.getStoreId(); - final String webApp = wpInfo.getDefaultWebApp(); - final String stagingSandboxId = wpInfo.getStagingStoreName(); - - // Invite web users - wpService.inviteWebUser(wpStoreId, USER_ONE, WCMUtil.ROLE_CONTENT_MANAGER, true); - - // Switch to USER_ONE - AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); - - SandboxInfo sbInfo = sbService.getAuthorSandbox(wpStoreId); - String authorSandboxId = sbInfo.getSandboxId(); - - // no changes yet - List assets = sbService.listChangedAll(authorSandboxId, true); - assertEquals(0, assets.size()); - - assetService.createFolderWebApp(authorSandboxId, webApp, "/", "myDir1"); - assetService.createFolderWebApp(authorSandboxId, webApp, "/", "myDir2"); - assetService.createFolderWebApp(authorSandboxId, webApp, "/", "myDir3"); - - assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); - assertEquals(3, assets.size()); - - // check staging before - String stagingSandboxPath = sbInfo.getSandboxRootPath() + "/" + webApp; - assertEquals(0, assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false).size()); - - List sbVersions = sbService.listSnapshots(stagingSandboxId, fromDate, new Date(), false); - assertEquals(0, sbVersions.size()); - - // submit (new assets) ! - sbService.submitWebApp(authorSandboxId, webApp, "a submit label", "a submit comment"); - - Thread.sleep(SUBMIT_DELAY); - - assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); - assertEquals(0, assets.size()); - - // check staging after - List listing = assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false); - assertEquals(3, listing.size()); - - sbVersions = sbService.listSnapshots(stagingSandboxId, fromDate, new Date(), false); - assertEquals(1, sbVersions.size()); - - // more changes ... - assetService.createFolderWebApp(authorSandboxId, webApp, "/", "myDir4"); - - // submit (new assets) ! - sbService.submitWebApp(authorSandboxId, webApp, "a submit label", "a submit comment"); - - Thread.sleep(SUBMIT_DELAY); - - // check staging after - listing = assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false); - assertEquals(4, listing.size()); - - sbVersions = sbService.listSnapshots(stagingSandboxId, fromDate, new Date(), false); - assertEquals(2, sbVersions.size()); - } - - public void testRevertSnapshot() throws IOException, InterruptedException - { - Date fromDate = new Date(); - - WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-revertSnapshot", TEST_WEBPROJ_NAME+" revertSnapshot", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); - - final String wpStoreId = wpInfo.getStoreId(); - final String webApp = wpInfo.getDefaultWebApp(); - final String stagingSandboxId = wpInfo.getStagingStoreName(); - - // Start: Test ETWOTWO-817 - - // Invite web users - wpService.inviteWebUser(wpStoreId, USER_ONE, WCMUtil.ROLE_CONTENT_MANAGER, true); - - // Switch to USER_ONE - AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); - - // Finish: Test ETWOTWO-817 - - SandboxInfo sbInfo = sbService.getAuthorSandbox(wpStoreId); - String authorSandboxId = sbInfo.getSandboxId(); - - // no changes yet - List assets = sbService.listChangedAll(authorSandboxId, true); - assertEquals(0, assets.size()); - - String authorSandboxPath = sbInfo.getSandboxRootPath() + "/" + webApp; - - assetService.createFolder(authorSandboxId, authorSandboxPath, "myDir1", null); - - assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); - assertEquals(1, assets.size()); - - // check staging before - String stagingSandboxPath = sbInfo.getSandboxRootPath() + "/" + webApp; - assertEquals(0, assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false).size()); - - List sbVersions = sbService.listSnapshots(stagingSandboxId, fromDate, new Date(), false); - assertEquals(0, sbVersions.size()); - - // submit (new assets) ! - sbService.submitWebApp(authorSandboxId, webApp, "a submit label", "a submit comment"); - - Thread.sleep(SUBMIT_DELAY); - - assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); - assertEquals(0, assets.size()); - - // check staging after - List listing = assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false); - assertEquals(1, listing.size()); - for (AssetInfo asset : listing) - { - if (asset.getName().equals("myDir1") && asset.isFolder()) - { - continue; - } - else - { - fail("The asset '" + asset.getName() + "' is not recognised"); - } - } - - sbVersions = sbService.listSnapshots(stagingSandboxId, fromDate, new Date(), false); - assertEquals(1, sbVersions.size()); - - // more changes ... - assetService.createFolder(authorSandboxId, authorSandboxPath, "myDir2", null); - - // submit (new assets) ! - sbService.submitWebApp(authorSandboxId, webApp, "a submit label", "a submit comment"); - - Thread.sleep(SUBMIT_DELAY); - - // check staging after - listing = assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false); - assertEquals(2, listing.size()); - for (AssetInfo asset : listing) - { - if (asset.getName().equals("myDir1") && asset.isFolder()) - { - continue; - } - else if (asset.getName().equals("myDir2") && asset.isFolder()) - { - continue; - } - else - { - fail("The asset '" + asset.getName() + "' is not recognised"); - } - } - - sbVersions = sbService.listSnapshots(stagingSandboxId, fromDate, new Date(), false); - assertEquals(2, sbVersions.size()); - - // more changes ... - assetService.createFolderWebApp(authorSandboxId, webApp, "/", "myDir3"); - - // submit (new assets) ! - sbService.submitWebApp(authorSandboxId, webApp, "a submit label", "a submit comment"); - - Thread.sleep(SUBMIT_DELAY); - - // check staging after - listing = assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false); - assertEquals(3, listing.size()); - for (AssetInfo asset : listing) - { - if (asset.getName().equals("myDir1") && asset.isFolder()) - { - continue; - } - else if (asset.getName().equals("myDir2") && asset.isFolder()) - { - continue; - } - else if (asset.getName().equals("myDir3") && asset.isFolder()) - { - continue; - } - else - { - fail("The asset '" + asset.getName() + "' is not recognised"); - } - } - - sbVersions = sbService.listSnapshots(stagingSandboxId, fromDate, new Date(), false); - assertEquals(3, sbVersions.size()); - - // revert to snapshot ... - - SandboxVersion version = sbVersions.get(1); - int versionId = version.getVersion(); - - sbService.revertSnapshot(stagingSandboxId, versionId); - - sbVersions = sbService.listSnapshots(stagingSandboxId, fromDate, new Date(), false); - assertEquals(4, sbVersions.size()); - - // check staging after - listing = assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false); - assertEquals(2, listing.size()); - for (AssetInfo asset : listing) - { - if (asset.getName().equals("myDir1") && asset.isFolder()) - { - continue; - } - else if (asset.getName().equals("myDir2") && asset.isFolder()) - { - continue; - } - else - { - fail("The asset '" + asset.getName() + "' is not recognised"); - } - } - } - - public void testPseudoScaleTest() - { - long start = System.currentTimeMillis(); - - long split = start; - - for (int i = 1; i <= SCALE_USERS; i++) - { - createUser(TEST_USER+"-"+i); - } - - System.out.println("testPseudoScaleTest: created "+SCALE_USERS+" users in "+(System.currentTimeMillis()-split)+" msecs"); - - split = System.currentTimeMillis(); - - for (int i = 1; i <= SCALE_WEBPROJECTS; i++) - { - wpService.createWebProject(TEST_WEBPROJ_DNS+"-"+i, TEST_WEBPROJ_NAME+"-"+i, TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); // ignore return - } - - System.out.println("testPseudoScaleTest: created "+SCALE_WEBPROJECTS+" web projects in "+(System.currentTimeMillis()-split)+" msecs"); - - split = System.currentTimeMillis(); - - for (int i = 1; i <= SCALE_WEBPROJECTS; i++) - { - WebProjectInfo wpInfo = wpService.getWebProject(TEST_WEBPROJ_DNS+"-"+i); - Map userRoles = new HashMap(SCALE_USERS); - for (int j = 1; j <= SCALE_USERS; j++) - { - userRoles.put(TEST_USER+"-"+j, WCMUtil.ROLE_CONTENT_MANAGER); - } - wpService.inviteWebUsersGroups(wpInfo.getNodeRef(), userRoles, true); - } - - System.out.println("testPseudoScaleTest: invited "+SCALE_USERS+" content managers (and created user sandboxes) to each of "+SCALE_WEBPROJECTS+" web projects in "+(System.currentTimeMillis()-split)+" msecs"); - - split = System.currentTimeMillis(); - - for (int i = 1; i <= SCALE_WEBPROJECTS; i++) - { - WebProjectInfo wpInfo = wpService.getWebProject(TEST_WEBPROJ_DNS+"-"+i); - assertEquals(SCALE_USERS+2, sbService.listSandboxes(wpInfo.getStoreId()).size()); // including staging sandbox and admin sandbox (web project creator) - } - - System.out.println("testPseudoScaleTest: list sandboxes for admin for each of "+SCALE_WEBPROJECTS+" web projects in "+(System.currentTimeMillis()-split)+" msecs"); - - split = System.currentTimeMillis(); - - for (int i = 1; i <= SCALE_WEBPROJECTS; i++) - { - WebProjectInfo wpInfo = wpService.getWebProject(TEST_WEBPROJ_DNS+"-"+i); - assertEquals(SCALE_USERS+1, wpService.listWebUsers(wpInfo.getStoreId()).size()); // including admin user (web project creator) - } - - System.out.println("testPseudoScaleTest: list web users for admin for each of "+SCALE_WEBPROJECTS+" web projects in "+(System.currentTimeMillis()-split)+" msecs"); - - split = System.currentTimeMillis(); - - for (int i = 1; i <= SCALE_WEBPROJECTS; i++) - { - WebProjectInfo wpInfo = wpService.getWebProject(TEST_WEBPROJ_DNS+"-"+i); - - for (int j = 1; j <= SCALE_USERS; j++) - { - AuthenticationUtil.setFullyAuthenticatedUser(TEST_USER+"-"+j); - assertEquals(SCALE_USERS+2, sbService.listSandboxes(wpInfo.getStoreId()).size()); // including staging sandbox and admin sandbox (web project creator) - } - AuthenticationUtil.setFullyAuthenticatedUser(USER_ADMIN); - } - - System.out.println("testPseudoScaleTest: list sandboxes for "+SCALE_USERS+" content managers for each of "+SCALE_WEBPROJECTS+" web projects in "+(System.currentTimeMillis()-split)+" msecs"); - - split = System.currentTimeMillis(); - - for (int i = 1; i <= SCALE_WEBPROJECTS; i++) - { - WebProjectInfo wpInfo = wpService.getWebProject(TEST_WEBPROJ_DNS+"-"+i); - - for (int j = 1; j <= SCALE_USERS; j++) - { - AuthenticationUtil.setFullyAuthenticatedUser(TEST_USER+"-"+j); - assertEquals(SCALE_USERS+1, wpService.listWebUsers(wpInfo.getStoreId()).size()); // including admin user (web project creator) - } - AuthenticationUtil.setFullyAuthenticatedUser(USER_ADMIN); - } - - System.out.println("testPseudoScaleTest: list web users for "+SCALE_USERS+" content managers for each of "+SCALE_WEBPROJECTS+" web projects in "+(System.currentTimeMillis()-split)+" msecs"); - - split = System.currentTimeMillis(); - - for (int i = 1; i <= SCALE_WEBPROJECTS; i++) - { - WebProjectInfo wpInfo = wpService.getWebProject(TEST_WEBPROJ_DNS+"-"+i); - - for (int j = 1; j <= SCALE_USERS; j++) - { - SandboxInfo sbInfo = sbService.getAuthorSandbox(wpInfo.getStoreId(), TEST_USER+"-"+j); - sbService.deleteSandbox(sbInfo.getSandboxId()); - } - } - - System.out.println("testPseudoScaleTest: deleted "+SCALE_USERS+" author sandboxes for each of "+SCALE_WEBPROJECTS+" web projects in "+(System.currentTimeMillis()-split)+" msecs"); - - split = System.currentTimeMillis(); - - for (int i = 1; i <= SCALE_WEBPROJECTS; i++) - { - WebProjectInfo wpInfo = wpService.getWebProject(TEST_WEBPROJ_DNS+"-"+i); - wpService.deleteWebProject(wpInfo.getNodeRef()); - } - - System.out.println("testPseudoScaleTest: deleted "+SCALE_WEBPROJECTS+" web projects in "+(System.currentTimeMillis()-split)+" msecs"); - - split = System.currentTimeMillis(); - - for (int i = 1; i <= SCALE_USERS; i++) - { - deleteUser(TEST_USER+"-"+i); - } - - System.out.println("testPseudoScaleTest: deleted "+SCALE_USERS+" users in "+(System.currentTimeMillis()-split)+" msecs"); - } - - - /* - // == Test the JavaScript API == - - public void testJSAPI() throws Exception - { - ScriptLocation location = new ClasspathScriptLocation("org/alfresco/wcm/script/test_sandboxService.js"); - scriptService.executeScript(location, new HashMap(0)); - } - */ -} +/* + * Copyright (C) 2005-2009 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.wcm.sandbox; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.permissions.AccessDeniedException; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.avm.AVMService; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.ApplicationContextHelper; +import org.alfresco.util.PropertyMap; +import org.alfresco.wcm.AbstractWCMServiceImplTest; +import org.alfresco.wcm.asset.AssetInfo; +import org.alfresco.wcm.asset.AssetService; +import org.alfresco.wcm.util.WCMUtil; +import org.alfresco.wcm.webproject.WebProjectInfo; +import org.alfresco.wcm.webproject.WebProjectService; + +/** + * Sandbox Service implementation unit test + * + * @author janv + */ +public class SandboxServiceImplTest extends AbstractWCMServiceImplTest +{ + // base web project + private static final String TEST_WEBPROJ_DNS = "testSandbox-"+TEST_RUN; + private static final String TEST_WEBPROJ_NAME = "testSandbox Web Project Display Name - "+TEST_RUN; + private static final String TEST_WEBPROJ_TITLE = "This is my title"; + private static final String TEST_WEBPROJ_DESCRIPTION = "This is my description"; + private static final String TEST_WEBPROJ_DEFAULT_WEBAPP = WCMUtil.DIR_ROOT; + //private static final boolean TEST_WEBPROJ_USE_AS_TEMPLATE = true; + private static final boolean TEST_WEBPROJ_DONT_USE_AS_TEMPLATE = false; + + // base sandbox + private static final String TEST_SANDBOX = TEST_WEBPROJ_DNS; + + + private static final String USER_ADMIN = "admin"; + + private static final String TEST_USER = "testSandboxUser-"+TEST_RUN; + + private static final String USER_ONE = TEST_USER+"-One"; + private static final String USER_TWO = TEST_USER+"-Two"; + private static final String USER_THREE = TEST_USER+"-Three"; + private static final String USER_FOUR = TEST_USER+"-Four"; + + private static final int SCALE_USERS = 5; + private static final int SCALE_WEBPROJECTS = 2; + + // + // services + // + + private WebProjectService wpService; + private SandboxService sbService; + private AssetService assetService; + + // TODO: temporary - remove from here when r13170 is merged from V3.1->HEAD + private TransactionService transactionService; + + private AVMService avmService; // non-locking-aware + + //private AVMService avmLockingAwareService; + //private AVMService avmNonLockingAwareService; + + + @Override + protected void setUp() throws Exception + { + super.setUp(); + + // Get the required services + wpService = (WebProjectService)ctx.getBean("WebProjectService"); + sbService = (SandboxService)ctx.getBean("SandboxService"); + assetService = (AssetService)ctx.getBean("AssetService"); + + avmService = (AVMService)ctx.getBean("AVMService"); + + // TODO: temporary - remove from here when r13170 is merged from V3.1->HEAD + transactionService = (TransactionService)ctx.getBean("TransactionService"); + + // WCM locking + //avmLockingAwareService = (AVMService)ctx.getBean("AVMLockingAwareService"); + + // without WCM locking + //avmNonLockingAwareService = (AVMService)ctx.getBean("AVMService"); + + // By default run as Admin + AuthenticationUtil.setFullyAuthenticatedUser(USER_ADMIN); + + createUser(USER_ONE); + createUser(USER_TWO); + createUser(USER_THREE); + createUser(USER_FOUR); + } + + @Override + protected void tearDown() throws Exception + { + if (CLEAN) + { + // Switch back to Admin + AuthenticationUtil.setFullyAuthenticatedUser(USER_ADMIN); + + List webProjects = wpService.listWebProjects(); + for (final WebProjectInfo wpInfo : webProjects) + { + if (wpInfo.getStoreId().startsWith(TEST_WEBPROJ_DNS)) + { + // TODO: temporary - remove from here when r13170 is merged from V3.1->HEAD + + // note: added retry for now, due to intermittent concurrent update (during tearDown) possibly due to OrphanReaper ? + // org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [org.alfresco.repo.avm.PlainFileNodeImpl#3752] + RetryingTransactionCallback deleteWebProjectWork = new RetryingTransactionCallback() + { + public Object execute() throws Exception + { + wpService.deleteWebProject(wpInfo.getNodeRef()); + return null; + } + }; + transactionService.getRetryingTransactionHelper().doInTransaction(deleteWebProjectWork); + + } + } + + deleteUser(USER_ONE); + deleteUser(USER_TWO); + deleteUser(USER_THREE); + deleteUser(USER_FOUR); + } + + AuthenticationUtil.clearCurrentSecurityContext(); + super.tearDown(); + } + + public void testSimple() + { + int storeCnt = avmService.getStores().size(); + + // create web project (also creates staging sandbox and admin's author sandbox) + WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-simple", TEST_WEBPROJ_NAME+"-simple", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION, TEST_WEBPROJ_DEFAULT_WEBAPP, TEST_WEBPROJ_DONT_USE_AS_TEMPLATE, null); + String wpStoreId = wpInfo.getStoreId(); + + // list 2 sandboxes + assertEquals(2, sbService.listSandboxes(wpStoreId).size()); + + // list 4 extra AVM stores (2 per sandbox) + assertEquals(storeCnt+4, avmService.getStores().size()); // 2x stating (main,preview), 2x admin author (main, preview) + + // get admin's sandbox + SandboxInfo sbInfo = sbService.getAuthorSandbox(wpStoreId); + assertNotNull(sbInfo); + + // get staging sandbox + sbInfo = sbService.getStagingSandbox(wpStoreId); + assertNotNull(sbInfo); + + // invite user one to the web project and do not implicitly create user one's sandbox + wpService.inviteWebUser(wpStoreId, USER_ONE, WCMUtil.ROLE_CONTENT_PUBLISHER, false); + assertEquals(2, sbService.listSandboxes(wpStoreId).size()); + + sbInfo = sbService.createAuthorSandbox(wpStoreId, USER_TWO); + assertEquals(3, sbService.listSandboxes(wpStoreId).size()); + + sbInfo = sbService.getSandbox(sbInfo.getSandboxId()); + sbService.deleteSandbox(sbInfo.getSandboxId()); + assertEquals(2, sbService.listSandboxes(wpStoreId).size()); + + // delete admin's sandbox + sbService.deleteSandbox(sbService.getAuthorSandbox(wpStoreId).getSandboxId()); + assertEquals(1, sbService.listSandboxes(wpStoreId).size()); + + // delete web project (also deletes staging sandbox) + wpService.deleteWebProject(wpStoreId); + + assertEquals(storeCnt, avmService.getStores().size()); + } + + public void testCreateAuthorSandbox() + { + // Create a web project + WebProjectInfo wpInfo1 = wpService.createWebProject(TEST_WEBPROJ_DNS+"-create-author", TEST_WEBPROJ_NAME+"-author", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION, TEST_WEBPROJ_DEFAULT_WEBAPP, TEST_WEBPROJ_DONT_USE_AS_TEMPLATE, null); + + String expectedUserSandboxId = TEST_SANDBOX+"-create-author" + "--" + USER_ADMIN; + + SandboxInfo sbInfo1 = sbService.getAuthorSandbox(wpInfo1.getStoreId()); + checkSandboxInfo(sbInfo1, expectedUserSandboxId, USER_ADMIN, USER_ADMIN, expectedUserSandboxId, SandboxConstants.PROP_SANDBOX_AUTHOR_MAIN); + + sbInfo1 = sbService.getAuthorSandbox(wpInfo1.getStoreId(), USER_ONE); + assertNull(sbInfo1); + + // Switch to USER_ONE + AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); + + sbInfo1 = sbService.getAuthorSandbox(wpInfo1.getStoreId()); + assertNull(sbInfo1); + + // Switch back to admin + AuthenticationUtil.setFullyAuthenticatedUser(USER_ADMIN); + + // Invite web user + wpService.inviteWebUser(wpInfo1.getStoreId(), USER_ONE, WCMUtil.ROLE_CONTENT_MANAGER); + + // Create author sandbox for user one - admin is the creator + sbInfo1 = sbService.createAuthorSandbox(wpInfo1.getStoreId(), USER_ONE); + + expectedUserSandboxId = TEST_SANDBOX+"-create-author" + "--" + USER_ONE; + + sbInfo1 = sbService.getAuthorSandbox(wpInfo1.getStoreId(), USER_ONE); + checkSandboxInfo(sbInfo1, expectedUserSandboxId, USER_ONE, USER_ADMIN, expectedUserSandboxId, SandboxConstants.PROP_SANDBOX_AUTHOR_MAIN); + + // Switch to USER_ONE + AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); + + // Get author sandbox + sbInfo1 = sbService.getAuthorSandbox(wpInfo1.getStoreId()); + checkSandboxInfo(sbInfo1, expectedUserSandboxId, USER_ONE, USER_ADMIN, expectedUserSandboxId, SandboxConstants.PROP_SANDBOX_AUTHOR_MAIN); + + String userSandboxId = sbInfo1.getSandboxId(); + + // Get (author) sandbox + sbInfo1 = sbService.getSandbox(userSandboxId); + checkSandboxInfo(sbInfo1, expectedUserSandboxId, USER_ONE, USER_ADMIN, expectedUserSandboxId, SandboxConstants.PROP_SANDBOX_AUTHOR_MAIN); + + // Should return same as before + sbInfo1 = sbService.createAuthorSandbox(wpInfo1.getStoreId()); + checkSandboxInfo(sbInfo1, expectedUserSandboxId, USER_ONE, USER_ADMIN, expectedUserSandboxId, SandboxConstants.PROP_SANDBOX_AUTHOR_MAIN); + + // Switch to USER_TWO + AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); + + try + { + // Try to create author sandbox as a non-web user (-ve test) + sbService.createAuthorSandbox(wpInfo1.getStoreId()); // ignore return + fail("Shouldn't be able to create author store since not a web user"); + } + catch (AccessDeniedException exception) + { + // Expected + } + + // Switch back to admin + AuthenticationUtil.setFullyAuthenticatedUser(USER_ADMIN); + + // Invite web user + wpService.inviteWebUser(wpInfo1.getStoreId(), USER_TWO, WCMUtil.ROLE_CONTENT_REVIEWER); + + // Switch to USER_TWO + AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); + + // Get author sandbox + sbInfo1 = sbService.getAuthorSandbox(wpInfo1.getStoreId()); + assertNull(sbInfo1); + + expectedUserSandboxId = TEST_SANDBOX+"-create-author" + "--" + USER_TWO; + + // Create own sandbox - user two is the creator + sbInfo1 = sbService.createAuthorSandbox(wpInfo1.getStoreId()); + checkSandboxInfo(sbInfo1, expectedUserSandboxId, USER_TWO, USER_TWO, expectedUserSandboxId, SandboxConstants.PROP_SANDBOX_AUTHOR_MAIN); + } + + private void checkSandboxInfo(SandboxInfo sbInfo, String expectedStoreId, String expectedName, String expectedCreator, String expectedMainStoreName, QName expectedSandboxType) + { + assertNotNull(sbInfo); + assertEquals(expectedStoreId, sbInfo.getSandboxId()); + assertEquals(expectedName, sbInfo.getName()); + assertEquals(expectedCreator, sbInfo.getCreator()); + assertNotNull(sbInfo.getCreatedDate()); + assertEquals(expectedMainStoreName, sbInfo.getMainStoreName()); + assertEquals(expectedSandboxType, sbInfo.getSandboxType()); + } + + public void testListSandboxes() throws Exception + { + // Create web project - implicitly creates staging sandbox and also author sandbox for web project creator (in this case, admin) + WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-list", TEST_WEBPROJ_NAME+" list", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); + String wpStoreId = wpInfo.getStoreId(); + + List sbInfos = sbService.listSandboxes(wpInfo.getStoreId()); + assertEquals(2, sbInfos.size()); // staging sandbox, author sandbox (for admin) + + String expectedUserSandboxId = TEST_SANDBOX+"-list" + "--" + USER_ADMIN; + + // Do detailed check of the sandbox info objects + for (SandboxInfo sbInfo : sbInfos) + { + QName sbType = sbInfo.getSandboxType(); + + if (sbType.equals(SandboxConstants.PROP_SANDBOX_STAGING_MAIN) == true) + { + checkSandboxInfo(sbInfo, TEST_SANDBOX+"-list", TEST_SANDBOX+"-list", USER_ADMIN, TEST_SANDBOX+"-list", SandboxConstants.PROP_SANDBOX_STAGING_MAIN); + } + else if (sbType.equals(SandboxConstants.PROP_SANDBOX_AUTHOR_MAIN) == true) + { + checkSandboxInfo(sbInfo, expectedUserSandboxId, USER_ADMIN, USER_ADMIN, expectedUserSandboxId, SandboxConstants.PROP_SANDBOX_AUTHOR_MAIN); + } + else + { + fail("The sandbox store id " + sbInfo.getSandboxId() + " is not recognised"); + } + } + + // test roles + + // Invite web users + wpService.inviteWebUser(wpStoreId, USER_ONE, WCMUtil.ROLE_CONTENT_MANAGER, true); + wpService.inviteWebUser(wpStoreId, USER_TWO, WCMUtil.ROLE_CONTENT_PUBLISHER, true); + wpService.inviteWebUser(wpStoreId, USER_THREE, WCMUtil.ROLE_CONTENT_REVIEWER, true); + wpService.inviteWebUser(wpStoreId, USER_FOUR, WCMUtil.ROLE_CONTENT_CONTRIBUTOR, true); + + // admin can list all sandboxes + sbInfos = sbService.listSandboxes(wpInfo.getStoreId()); + assertEquals(6, sbInfos.size()); + + // Switch to USER_ONE + AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); + + // content manager can list all sandboxes + sbInfos = sbService.listSandboxes(wpInfo.getStoreId()); + assertEquals(6, sbInfos.size()); + + // Switch to USER_TWO + AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); + + // Content publisher - can only list own sandbox and staging + sbInfos = sbService.listSandboxes(wpInfo.getStoreId()); + assertEquals(2, sbInfos.size()); + + // Switch to USER_THREE + AuthenticationUtil.setFullyAuthenticatedUser(USER_THREE); + + // Content reviewer - can only list own sandbox and staging + sbInfos = sbService.listSandboxes(wpInfo.getStoreId()); + assertEquals(2, sbInfos.size()); + + // Switch to USER_FOUR + AuthenticationUtil.setFullyAuthenticatedUser(USER_FOUR); + + // Content contributor - can only list own sandbox and staging + sbInfos = sbService.listSandboxes(wpInfo.getStoreId()); + assertEquals(2, sbInfos.size()); + } + + public void testGetSandbox() + { + // Get a sandbox that isn't there + SandboxInfo sbInfo = sbService.getSandbox(TEST_SANDBOX+"-get"); + assertNull(sbInfo); + + // Create web project - implicitly creates staging sandbox and also admin sandbox (author sandbox for web project creator) + WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-get", TEST_WEBPROJ_NAME+" get", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); + String wpStoreId = wpInfo.getStoreId(); + + // Get staging sandbox + sbInfo = sbService.getStagingSandbox(wpInfo.getStoreId()); + checkSandboxInfo(sbInfo, TEST_SANDBOX+"-get", TEST_SANDBOX+"-get", USER_ADMIN, TEST_SANDBOX+"-get", SandboxConstants.PROP_SANDBOX_STAGING_MAIN); + + // Get (staging) sandbox + String stagingSandboxId = wpInfo.getStagingStoreName(); + sbInfo = sbService.getSandbox(stagingSandboxId); + checkSandboxInfo(sbInfo, TEST_SANDBOX+"-get", TEST_SANDBOX+"-get", USER_ADMIN, TEST_SANDBOX+"-get", SandboxConstants.PROP_SANDBOX_STAGING_MAIN); + + // Get (author) sandbox + sbInfo = sbService.getAuthorSandbox(wpStoreId); + sbInfo = sbService.getSandbox(sbInfo.getSandboxId()); + String userSandboxId = TEST_SANDBOX+"-get" + "--" + USER_ADMIN; + checkSandboxInfo(sbInfo, userSandboxId, USER_ADMIN, USER_ADMIN, userSandboxId, SandboxConstants.PROP_SANDBOX_AUTHOR_MAIN); + + // test roles + + // Invite web users + wpService.inviteWebUser(wpStoreId, USER_ONE, WCMUtil.ROLE_CONTENT_MANAGER, true); + wpService.inviteWebUser(wpStoreId, USER_TWO, WCMUtil.ROLE_CONTENT_PUBLISHER, true); + wpService.inviteWebUser(wpStoreId, USER_THREE, WCMUtil.ROLE_CONTENT_REVIEWER, true); + wpService.inviteWebUser(wpStoreId, USER_FOUR, WCMUtil.ROLE_CONTENT_CONTRIBUTOR, true); + + // admin can get any sandbox + userSandboxId = TEST_SANDBOX+"-get" + "--" + USER_THREE; + sbInfo = sbService.getSandbox(userSandboxId); + checkSandboxInfo(sbInfo, userSandboxId, USER_THREE, USER_ADMIN, userSandboxId, SandboxConstants.PROP_SANDBOX_AUTHOR_MAIN); + + // Switch to USER_ONE + AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); + + // content manager can get any (author) sandbox + userSandboxId = TEST_SANDBOX+"-get" + "--" + USER_THREE; + sbInfo = sbService.getSandbox(userSandboxId); + checkSandboxInfo(sbInfo, userSandboxId, USER_THREE, USER_ADMIN, userSandboxId, SandboxConstants.PROP_SANDBOX_AUTHOR_MAIN); + + // Switch to USER_TWO + AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); + + try + { + // Content publisher - try to get another user's sandbox (-ve test) + userSandboxId = TEST_SANDBOX+"-get" + "--" + USER_THREE; + sbInfo = sbService.getSandbox(userSandboxId); + fail("Shouldn't be able to get another author's sandbox"); + } + catch (AccessDeniedException exception) + { + // Expected + } + + // Switch to USER_THREE + AuthenticationUtil.setFullyAuthenticatedUser(USER_THREE); + + try + { + // Content reviewer - try to get another user's sandbox (-ve test) + userSandboxId = TEST_SANDBOX+"-get" + "--" + USER_TWO; + sbInfo = sbService.getSandbox(userSandboxId); + fail("Shouldn't be able to get another author's sandbox"); + } + catch (AccessDeniedException exception) + { + // Expected + } + + // Switch to USER_FOUR + AuthenticationUtil.setFullyAuthenticatedUser(USER_FOUR); + + try + { + // Content contributor - try to get another user's sandbox (-ve test) + userSandboxId = TEST_SANDBOX+"-get" + "--" + USER_THREE; + sbInfo = sbService.getSandbox(userSandboxId); + fail("Shouldn't be able to get another author's sandbox"); + } + catch (AccessDeniedException exception) + { + // Expected + } + } + + public void testIsSandboxType() + { + // Get a sandbox that isn't there + SandboxInfo sbInfo = sbService.getSandbox(TEST_SANDBOX+"-is"); + assertNull(sbInfo); + + // Create web project - implicitly creates staging sandbox and also admin sandbox (author sandbox for web project creator) + WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-is", TEST_WEBPROJ_NAME+" is", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); + + // Get staging sandbox + sbInfo = sbService.getStagingSandbox(wpInfo.getStoreId()); + + assertTrue(sbService.isSandboxType(sbInfo.getSandboxId(), SandboxConstants.PROP_SANDBOX_STAGING_MAIN)); + assertFalse(sbService.isSandboxType(sbInfo.getSandboxId(), SandboxConstants.PROP_SANDBOX_AUTHOR_MAIN)); + + // Get author sandbox + sbInfo = sbService.getAuthorSandbox(wpInfo.getStoreId()); + + assertTrue(sbService.isSandboxType(sbInfo.getSandboxId(), SandboxConstants.PROP_SANDBOX_AUTHOR_MAIN)); + assertFalse(sbService.isSandboxType(sbInfo.getSandboxId(), SandboxConstants.PROP_SANDBOX_STAGING_MAIN)); + } + + public void testDeleteSandbox() + { + // Create web project - implicitly creates staging sandbox and also admin sandbox (author sandbox for web project creator) + WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-delete", TEST_WEBPROJ_NAME+" delete", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); + String wpStoreId = wpInfo.getStoreId(); + + assertEquals(2, sbService.listSandboxes(wpStoreId).size()); + + // Get staging sandbox + SandboxInfo sbInfo = sbService.getStagingSandbox(wpStoreId); + + try + { + // Try to delete staging sandbox (-ve test) + sbService.deleteSandbox(sbInfo.getSandboxId()); + fail("Shouldn't be able to delete staging sandbox"); + } + catch (AccessDeniedException exception) + { + // Expected + } + + try + { + // Try to delete non-existant sandbox (-ve test) + sbService.deleteSandbox("some-random-staging-sandbox"); + fail("Shouldn't be able to delete non-existant sandbox"); + } + catch (AccessDeniedException exception) + { + // Expected + } + + // Get admin author sandbox + sbInfo = sbService.getAuthorSandbox(wpInfo.getStoreId()); + sbService.deleteSandbox(sbInfo.getSandboxId()); + + assertEquals(1, sbService.listSandboxes(wpInfo.getStoreId()).size()); + + // Invite web users + wpService.inviteWebUser(wpStoreId, USER_ONE, WCMUtil.ROLE_CONTENT_MANAGER); + wpService.inviteWebUser(wpStoreId, USER_TWO, WCMUtil.ROLE_CONTENT_PUBLISHER); + wpService.inviteWebUser(wpStoreId, USER_THREE, WCMUtil.ROLE_CONTENT_REVIEWER, true); + + assertEquals(2, sbService.listSandboxes(wpStoreId).size()); + + sbService.createAuthorSandbox(wpStoreId, USER_ONE); + sbService.createAuthorSandbox(wpStoreId, USER_TWO); + + assertEquals(4, sbService.listSandboxes(wpStoreId).size()); + + // Switch to USER_TWO + AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); + + assertEquals(2, sbService.listSandboxes(wpStoreId).size()); + + sbInfo = sbService.getAuthorSandbox(wpStoreId); + assertNotNull(sbInfo); + + // can delete own sandbox + sbService.deleteSandbox(sbInfo.getSandboxId()); + + assertEquals(1, sbService.listSandboxes(wpStoreId).size()); + + sbInfo = sbService.getAuthorSandbox(wpStoreId); + assertNull(sbInfo); + + // but not others + try + { + // Try to delete another author's sandbox as a non-content manager (-ve test) + sbService.deleteSandbox(wpInfo.getStoreId()+"--"+USER_THREE); + fail("Shouldn't be able to delete another author's sandbox"); + } + catch (AccessDeniedException exception) + { + // Expected + } + + // Switch to USER_ONE + AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); + + assertEquals(3, sbService.listSandboxes(wpStoreId).size()); + + // content manager can delete others + sbInfo = sbService.getAuthorSandbox(wpStoreId, USER_THREE); + sbService.deleteSandbox(sbInfo.getSandboxId()); + + assertEquals(2, sbService.listSandboxes(wpStoreId).size()); + } + + // list changed (in this test, new) assets in user sandbox compared to staging sandbox + public void testListNewItems1() + { + WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-listNewItems1", TEST_WEBPROJ_NAME+" listNewItems1", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); + String wpStoreId = wpInfo.getStoreId(); + + assertEquals(2, sbService.listSandboxes(wpStoreId).size()); + + // add web app (in addition to default ROOT web app) + String myWebApp = "myWebApp"; + wpService.createWebApp(wpStoreId, myWebApp, "this is my web app"); + + // Invite web users + wpService.inviteWebUser(wpStoreId, USER_ONE, WCMUtil.ROLE_CONTENT_CONTRIBUTOR); + SandboxInfo sbInfo = sbService.createAuthorSandbox(wpStoreId, USER_ONE); + String userOneSandboxId = sbInfo.getSandboxId(); + + wpService.inviteWebUser(wpStoreId, USER_TWO, WCMUtil.ROLE_CONTENT_PUBLISHER); + sbInfo = sbService.createAuthorSandbox(wpStoreId, USER_TWO); + String userTwoSandboxId = sbInfo.getSandboxId(); + + wpService.inviteWebUser(wpStoreId, USER_THREE, WCMUtil.ROLE_CONTENT_MANAGER); + sbService.createAuthorSandbox(wpStoreId, USER_THREE); + + wpService.inviteWebUser(wpStoreId, USER_FOUR, WCMUtil.ROLE_CONTENT_REVIEWER, true); + + assertEquals(6, sbService.listSandboxes(wpStoreId).size()); + + // Switch to USER_ONE + AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); + + assertEquals(2, sbService.listSandboxes(wpStoreId).size()); + + sbInfo = sbService.getAuthorSandbox(wpStoreId); + String sbStoreId = sbInfo.getSandboxId(); + + // no changes yet + List assets = sbService.listChangedAll(sbStoreId, true); + assertEquals(0, assets.size()); + + String authorSandboxMyWebAppRelativePath = sbInfo.getSandboxRootPath() + "/" + myWebApp; // in this case, my web app is 'myWebApp' + String authorSandboxDefaultWebAppRelativePath = sbInfo.getSandboxRootPath() + "/" + wpInfo.getDefaultWebApp(); // in this case, default web app is 'ROOT' + + assetService.createFile(sbStoreId, authorSandboxMyWebAppRelativePath, "myFile1", null); + + assets = sbService.listChangedAll(sbStoreId, false); + assertEquals(1, assets.size()); + assertEquals("myFile1", assets.get(0).getName()); + + assetService.createFolder(sbStoreId, authorSandboxDefaultWebAppRelativePath, "myDir1", null); + assetService.createFile(sbStoreId, authorSandboxDefaultWebAppRelativePath+"/myDir1", "myFile2", null); + assetService.createFolder(sbStoreId, authorSandboxDefaultWebAppRelativePath+"/myDir1", "myDir2", null); + assetService.createFile(sbStoreId, authorSandboxDefaultWebAppRelativePath+"/myDir1/myDir2", "myFile3", null); + assetService.createFile(sbStoreId, authorSandboxDefaultWebAppRelativePath+"/myDir1/myDir2", "myFile4", null); + assetService.createFolder(sbStoreId, authorSandboxDefaultWebAppRelativePath+"/myDir1", "myDir3", null); + + assets = sbService.listChangedAll(sbStoreId, false); + assertEquals(2, assets.size()); // new dir with new dirs/files is returned as single change + + for (AssetInfo asset : assets) + { + if (asset.getName().equals("myFile1") && asset.isFile()) + { + continue; + } + else if (asset.getName().equals("myDir1") && asset.isFolder()) + { + continue; + } + else + { + fail("The asset '" + asset.getName() + "' is not recognised"); + } + } + + assets = sbService.listChangedWebApp(sbStoreId, wpInfo.getDefaultWebApp(), false); + assertEquals(1, assets.size()); + + for (AssetInfo asset : assets) + { + if (asset.getName().equals("myDir1") && asset.isFolder()) + { + continue; + } + else + { + fail("The asset '" + asset.getName() + "' is not recognised"); + } + } + + assets = sbService.listChanged(sbStoreId, authorSandboxDefaultWebAppRelativePath+"/myDir1", false); + assertEquals(1, assets.size()); + + for (AssetInfo asset : assets) + { + if (asset.getName().equals("myDir1") && asset.isFolder()) + { + continue; + } + else + { + fail("The asset '" + asset.getName() + "' is not recognised"); + } + } + + // Switch to USER_TWO + AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); + + assertEquals(2, sbService.listSandboxes(wpStoreId).size()); + + try + { + // Content Contributor should not be able to list another user's changes (-ve test) + assets = sbService.listChangedAll(userOneSandboxId, true); + fail("Shouldn't allow non-content-manager to get modified list for another sandbox"); + } + catch (AccessDeniedException exception) + { + // Expected + } + + // test roles + + // Switch to USER_ADMIN + AuthenticationUtil.setFullyAuthenticatedUser(USER_ADMIN); + + assertEquals(6, sbService.listSandboxes(wpStoreId).size()); + + // admin (Content Manager) should be able to list another user's changes + assets = sbService.listChangedAll(userOneSandboxId, true); + assertEquals(2, assets.size()); + + // Switch to USER_THREE + AuthenticationUtil.setFullyAuthenticatedUser(USER_THREE); + + assertEquals(6, sbService.listSandboxes(wpStoreId).size()); + + // Content Manager should be able to list another user's changes + assets = sbService.listChangedAll(userOneSandboxId, true); + assertEquals(2, assets.size()); + + // Switch to USER_ONE + AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); + + try + { + // Content publisher - try to list changes in another user's sandbox (-ve test) + assets = sbService.listChangedAll(userTwoSandboxId, true); + fail("Shouldn't be able to list another author's sandbox changes"); + } + catch (AccessDeniedException exception) + { + // Expected + } + + // Switch to USER_TWO + AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); + + try + { + // Content contributor - try to list changes in another user's sandbox (-ve test) + assets = sbService.listChangedAll(userOneSandboxId, true); + fail("Shouldn't be able to list another author's sandbox changes"); + } + catch (AccessDeniedException exception) + { + // Expected + } + + // Switch to USER_FOUR + AuthenticationUtil.setFullyAuthenticatedUser(USER_FOUR); + + try + { + // Content reviewer - try to list changes in another user's sandbox (-ve test) + assets = sbService.listChangedAll(userOneSandboxId, true); + fail("Shouldn't be able to list another author's sandbox changes"); + } + catch (AccessDeniedException exception) + { + // Expected + } + } + + // list changed (in this test, new) assets in two different user sandboxes compared to each other + public void testListNewItems2() + { + WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-listNewItems2", TEST_WEBPROJ_NAME+" listNewItems2", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); + String wpStoreId = wpInfo.getStoreId(); + + // Invite web users + wpService.inviteWebUser(wpStoreId, USER_ONE, WCMUtil.ROLE_CONTENT_CONTRIBUTOR, true); + wpService.inviteWebUser(wpStoreId, USER_TWO, WCMUtil.ROLE_CONTENT_CONTRIBUTOR, true); + + // Switch to USER_ONE + AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); + + SandboxInfo sbInfo1 = sbService.getAuthorSandbox(wpStoreId); + String sbStoreId = sbInfo1.getSandboxId(); + + List assets = sbService.listChangedAll(sbStoreId, true); + assertEquals(0, assets.size()); + + assetService.createFile(sbStoreId, sbInfo1.getSandboxRootPath(), "myFile1", null); + + assets = sbService.listChangedAll(sbStoreId, false); + assertEquals(1, assets.size()); + assertEquals("myFile1", assets.get(0).getName()); + + // Switch to USER_TWO + AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); + + SandboxInfo sbInfo2 = sbService.getAuthorSandbox(wpStoreId); + sbStoreId = sbInfo2.getSandboxId(); + + assets = sbService.listChangedAll(sbStoreId, true); + assertEquals(0, assets.size()); + + assetService.createFile(sbStoreId, sbInfo2.getSandboxRootPath(), "myFile2", null); + assetService.createFile(sbStoreId, sbInfo2.getSandboxRootPath(), "myFile3", null); + + assets = sbService.listChangedAll(sbStoreId, false); + assertEquals(2, assets.size()); + + for (AssetInfo asset : assets) + { + if (asset.getName().equals("myFile2") && asset.isFile()) + { + continue; + } + else if (asset.getName().equals("myFile3") && asset.isFile()) + { + continue; + } + else + { + fail("The asset '" + asset.getName() + "' is not recognised"); + } + } + + // Switch back to admin + AuthenticationUtil.setFullyAuthenticatedUser(USER_ADMIN); + + sbInfo1 = sbService.getAuthorSandbox(wpStoreId, USER_ONE); + sbInfo2 = sbService.getAuthorSandbox(wpStoreId, USER_TWO); + + assets = sbService.listChanged(sbInfo1.getSandboxId(), sbInfo1.getSandboxRootPath(), sbInfo2.getSandboxId(), sbInfo2.getSandboxRootPath(), false); + assertEquals(1, assets.size()); + assertEquals("myFile1", assets.get(0).getName()); + + assets = sbService.listChanged(sbInfo2.getSandboxId(), sbInfo2.getSandboxRootPath(), sbInfo1.getSandboxId(), sbInfo1.getSandboxRootPath(), false); + assertEquals(2, assets.size()); + + for (AssetInfo asset : assets) + { + if (asset.getName().equals("myFile2") && asset.isFile()) + { + continue; + } + else if (asset.getName().equals("myFile3") && asset.isFile()) + { + continue; + } + else + { + fail("The asset '" + asset.getName() + "' is not recognised"); + } + } + } + + /* + // list changed (in this test, new) assets in two different user sandboxes compared to each other - without locking + public void testListNewItems3() + { + WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-listNewItems2", TEST_WEBPROJ_NAME+" listNewItems2", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); + String wpStoreId = wpInfo.getStoreId(); + + // Invite web users + wpService.inviteWebUser(wpStoreId, USER_ONE, WCMUtil.ROLE_CONTENT_CONTRIBUTOR, true); + wpService.inviteWebUser(wpStoreId, USER_TWO, WCMUtil.ROLE_CONTENT_CONTRIBUTOR, true); + + // Switch to USER_ONE + AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); + + SandboxInfo sbInfo1 = sbService.getAuthorSandbox(wpStoreId); + String sbStoreId = sbInfo1.getSandboxId(); + + List assets = sbService.listChangedAll(sbStoreId, true); + assertEquals(0, assets.size()); + + String authorSandboxRootPath = sbStoreId + AVM_STORE_SEPARATOR + sbInfo1.getSandboxRootPath(); + + avmNonLockingAwareService.createFile(authorSandboxRootPath, "myFile1"); + + assets = sbService.listChangedAll(sbStoreId, false); + assertEquals(1, assets.size()); + assertEquals("myFile1", assets.get(0).getName()); + + // Switch to USER_TWO + AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); + + SandboxInfo sbInfo2 = sbService.getAuthorSandbox(wpStoreId); + sbStoreId = sbInfo2.getSandboxId(); + + assets = sbService.listChangedAll(sbStoreId, true); + assertEquals(0, assets.size()); + + authorSandboxRootPath = sbStoreId + AVM_STORE_SEPARATOR + sbInfo2.getSandboxRootPath(); + + avmNonLockingAwareService.createFile(authorSandboxRootPath, "myFile1"); // allowed, since using base (non-locking-aware) AVM service + avmNonLockingAwareService.createFile(authorSandboxRootPath, "myFile2"); + avmNonLockingAwareService.createFile(authorSandboxRootPath, "myFile3"); + + assets = sbService.listChangedAll(sbStoreId, false); + assertEquals(3, assets.size()); + + for (AssetInfo asset : assets) + { + if (asset.getName().equals("myFile1") && asset.isFile()) + { + continue; + } + else if (asset.getName().equals("myFile2") && asset.isFile()) + { + continue; + } + else if (asset.getName().equals("myFile3") && asset.isFile()) + { + continue; + } + else + { + fail("The asset '" + asset.getName() + "' is not recognised"); + } + } + + // Switch back to admin + AuthenticationUtil.setFullyAuthenticatedUser(USER_ADMIN); + + sbInfo1 = sbService.getAuthorSandbox(wpStoreId, USER_ONE); + sbInfo2 = sbService.getAuthorSandbox(wpStoreId, USER_TWO); + + assets = sbService.listChanged(sbInfo1.getSandboxId(), sbInfo1.getSandboxRootPath(), sbInfo2.getSandboxId(), sbInfo2.getSandboxRootPath(), false); + assertEquals(1, assets.size()); + assertEquals("myFile1", assets.get(0).getName()); + + assets = sbService.listChanged(sbInfo2.getSandboxId(), sbInfo1.getSandboxRootPath(), sbInfo1.getSandboxId(), sbInfo2.getSandboxRootPath(), false); + assertEquals(3, assets.size()); + + for (AssetInfo asset : assets) + { + if (asset.getName().equals("myFile1") && asset.isFile()) + { + continue; + } + else if (asset.getName().equals("myFile2") && asset.isFile()) + { + continue; + } + else if (asset.getName().equals("myFile3") && asset.isFile()) + { + continue; + } + else + { + fail("The asset '" + asset.getName() + "' is not recognised"); + } + } + } + */ + + // submit new assets in user sandbox to staging sandbox + public void testSubmitNewItems1() throws InterruptedException + { + WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-submitNewItems1", TEST_WEBPROJ_NAME+" submitNewItems1", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); + + String wpStoreId = wpInfo.getStoreId(); + String webApp = wpInfo.getDefaultWebApp(); + String stagingSandboxId = wpInfo.getStagingStoreName(); + + // Invite web users + wpService.inviteWebUser(wpStoreId, USER_ONE, WCMUtil.ROLE_CONTENT_CONTRIBUTOR, true); + wpService.inviteWebUser(wpStoreId, USER_TWO, WCMUtil.ROLE_CONTENT_PUBLISHER, true); + wpService.inviteWebUser(wpStoreId, USER_THREE, WCMUtil.ROLE_CONTENT_MANAGER, true); + wpService.inviteWebUser(wpStoreId, USER_FOUR, WCMUtil.ROLE_CONTENT_REVIEWER, true); + + // Switch to USER_ONE + AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); + + SandboxInfo sbInfo = sbService.getAuthorSandbox(wpStoreId); + String authorSandboxId = sbInfo.getSandboxId(); + + // no changes yet + List assets = sbService.listChangedAll(authorSandboxId, true); + assertEquals(0, assets.size()); + + String authorSandboxPath = sbInfo.getSandboxRootPath() + "/" + webApp; + + assetService.createFile(authorSandboxId, authorSandboxPath, "myFile1", null); + assetService.createFolder(authorSandboxId, authorSandboxPath, "myDir1", null); + assetService.createFile(authorSandboxId, authorSandboxPath+"/myDir1", "myFile2", null); + assetService.createFolder(authorSandboxId, authorSandboxPath+"/myDir1", "myDir2", null); + assetService.createFile(authorSandboxId, authorSandboxPath+"/myDir1/myDir2", "myFile3", null); + assetService.createFile(authorSandboxId, authorSandboxPath+"/myDir1/myDir2", "myFile4", null); + assetService.createFolder(authorSandboxId, authorSandboxPath+"/myDir1", "myDir3", null); + + assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); + assertEquals(2, assets.size()); // new dir with new dirs/files is returned as single change + + // check staging before + String stagingSandboxPath = sbInfo.getSandboxRootPath() + "/" + webApp; + assertEquals(0, assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false).size()); + + // submit (new assets) ! + sbService.submitWebApp(authorSandboxId, webApp, "a submit label", "a submit comment"); + + Thread.sleep(SUBMIT_DELAY); + + assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); + assertEquals(0, assets.size()); + + // check staging after + List listing = assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false); + assertEquals(2, listing.size()); + + for (AssetInfo asset : listing) + { + if (asset.getName().equals("myFile1") && asset.isFile()) + { + continue; + } + else if (asset.getName().equals("myDir1") && asset.isFolder()) + { + continue; + } + else + { + fail("The asset '" + asset.getName() + "' is not recognised"); + } + } + + // test roles + + // Switch to USER_ONE + AuthenticationUtil.setFullyAuthenticatedUser(USER_ADMIN); + + // admin (content manager) can submit any sandbox + String userSandboxId = wpStoreId + "--" + USER_THREE; + sbService.submitAll(userSandboxId, "my submit", null); + + // Switch to USER_THREE + AuthenticationUtil.setFullyAuthenticatedUser(USER_THREE); + + // content manager can submit any (author) sandbox + userSandboxId = wpStoreId + "--" + USER_ONE; + sbService.submitAll(userSandboxId, "my submit", null); + + // Switch to USER_ONE + AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); + + try + { + // Content contributor - try to submit another user's sandbox (-ve test) + userSandboxId = wpStoreId + "--" + USER_THREE; + List noAssets = new ArrayList(0); + sbService.submitListAssets(userSandboxId, noAssets, "my submit", null); + fail("Shouldn't be able to submit another author's sandbox"); + } + catch (AccessDeniedException exception) + { + // Expected + } + + // Switch to USER_TWO + AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); + + try + { + // Content publisher - try to submit another user's sandbox (-ve test) + userSandboxId = wpStoreId + "--" + USER_ONE; + sbService.submitAll(userSandboxId, "my submit", null); + fail("Shouldn't be able to submit another author's sandbox"); + } + catch (AccessDeniedException exception) + { + // Expected + } + + // Switch to USER_FOUR + AuthenticationUtil.setFullyAuthenticatedUser(USER_FOUR); + + try + { + // Content reviewer - try to submit another user's sandbox (-ve test) + userSandboxId = TEST_SANDBOX+"-get" + "--" + USER_THREE; + sbService.submitAll(userSandboxId, "my submit", null); + fail("Shouldn't be able to submit another author's sandbox"); + } + catch (AccessDeniedException exception) + { + // Expected + } + } + + // submit changed assets in user sandbox to staging sandbox + public void testSubmitChangedAssets1() throws IOException, InterruptedException + { + WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-submitChangedAssets1", TEST_WEBPROJ_NAME+" submitChangedAssets1", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); + + final String wpStoreId = wpInfo.getStoreId(); + final String webApp = wpInfo.getDefaultWebApp(); + final String stagingSandboxId = wpInfo.getStagingStoreName(); + + // Invite web users + + wpService.inviteWebUser(wpStoreId, USER_ONE, WCMUtil.ROLE_CONTENT_CONTRIBUTOR, true); + + // TODO - pending merge of ETWOTWO-1109 fix + //wpService.inviteWebUser(wpStoreId, USER_TWO, WCMUtil.ROLE_CONTENT_PUBLISHER, true); + wpService.inviteWebUser(wpStoreId, USER_TWO, WCMUtil.ROLE_CONTENT_MANAGER, true); + + // Switch to USER_ONE + AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); + + SandboxInfo sbInfo = sbService.getAuthorSandbox(wpStoreId); + String authorSandboxId = sbInfo.getSandboxId(); + + // no changes yet + List assets = sbService.listChangedAll(authorSandboxId, true); + assertEquals(0, assets.size()); + + final String MYFILE1 = "This is myFile1"; + ContentWriter writer = assetService.createFileWebApp(authorSandboxId, webApp, "/", "myFile1"); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent(MYFILE1); + + assetService.createFolderWebApp(authorSandboxId, webApp, "/", "myDir1"); + + final String MYFILE2 = "This is myFile2"; + writer = assetService.createFileWebApp(authorSandboxId, webApp, "/myDir1", "myFile2"); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent(MYFILE2); + + assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); + assertEquals(2, assets.size()); + + // check staging before + String stagingSandboxPath = sbInfo.getSandboxRootPath() + "/" + webApp; + assertEquals(0, assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false).size()); + + // submit (new assets) ! + sbService.submitWebApp(authorSandboxId, webApp, "a submit label", "a submit comment"); + + Thread.sleep(SUBMIT_DELAY); + + assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); + assertEquals(0, assets.size()); + + // check staging after + List listing = assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false); + assertEquals(2, listing.size()); + + // Switch to USER_TWO + AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); + + sbInfo = sbService.getAuthorSandbox(wpStoreId); + authorSandboxId = sbInfo.getSandboxId(); + + // no changes yet + assets = sbService.listChangedAll(authorSandboxId, true); + assertEquals(0, assets.size()); + + final String MYFILE1_MODIFIED = "This is myFile1 ... modified by "+USER_TWO; + + writer = assetService.getContentWriter(assetService.getAssetWebApp(authorSandboxId, webApp, "/myFile1")); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent(MYFILE1_MODIFIED); + + final String MYFILE2_MODIFIED = "This is myFile2 ... modified by "+USER_TWO; + writer = assetService.getContentWriter(assetService.getAssetWebApp(authorSandboxId, webApp, "/myDir1/myFile2")); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent(MYFILE2_MODIFIED); + + assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); + assertEquals(2, assets.size()); + + // check staging before + stagingSandboxPath = sbInfo.getSandboxRootPath() + "/" + webApp; + + ContentReader reader = assetService.getContentReader(assetService.getAsset(stagingSandboxId, -1, stagingSandboxPath+"/myFile1", false)); + InputStream in = reader.getContentInputStream(); + byte[] buff = new byte[1024]; + in.read(buff); + in.close(); + assertEquals(MYFILE1, new String(buff, 0, MYFILE1.length())); // assumes 1byte=1char + + reader = assetService.getContentReader(assetService.getAsset(stagingSandboxId, -1, stagingSandboxPath+"/myDir1/myFile2", false)); + in = reader.getContentInputStream(); + buff = new byte[1024]; + in.read(buff); + in.close(); + assertEquals(MYFILE2, new String(buff, 0, MYFILE2.length())); + + // submit (modified assets) ! + sbService.submitWebApp(authorSandboxId, webApp, "my label", null); + + Thread.sleep(SUBMIT_DELAY); + + assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); + assertEquals(0, assets.size()); + + // check staging after + reader = assetService.getContentReader(assetService.getAsset(stagingSandboxId, -1, stagingSandboxPath+"/myFile1", false)); + in = reader.getContentInputStream(); + buff = new byte[1024]; + in.read(buff); + in.close(); + assertEquals(MYFILE1_MODIFIED, new String(buff, 0, MYFILE1_MODIFIED.length())); + + reader = assetService.getContentReader(assetService.getAsset(stagingSandboxId, -1, stagingSandboxPath+"/myDir1/myFile2", false)); + in = reader.getContentInputStream(); + buff = new byte[1024]; + in.read(buff); + in.close(); + assertEquals(MYFILE2_MODIFIED, new String(buff, 0, MYFILE1_MODIFIED.length())); + } + + // submit "all" changed assets in user sandbox to staging sandbox (not using default webapp) + public void testSubmitChangedAssets2() throws IOException, InterruptedException + { + WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-submitChangedAssets1", TEST_WEBPROJ_NAME+" submitChangedAssets1", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); + + final String wpStoreId = wpInfo.getStoreId(); + final String stagingSandboxId = wpInfo.getStagingStoreName(); + + SandboxInfo sbInfo = sbService.getAuthorSandbox(wpStoreId); + String authorSandboxId = sbInfo.getSandboxId(); + + String rootPath = sbInfo.getSandboxRootPath(); // currently /www/avm_webapps + + // no changes yet + List assets = sbService.listChangedAll(authorSandboxId, true); + assertEquals(0, assets.size()); + + assetService.createFolder(authorSandboxId, rootPath, "a", null); + assetService.createFolder(authorSandboxId, rootPath+"/a", "b", null); + assetService.createFolder(authorSandboxId, rootPath+"/a/b", "c", null); + + final String MYFILE1 = "This is foo"; + ContentWriter writer = assetService.createFile(authorSandboxId, rootPath+"/a/b/c", "foo", null); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent(MYFILE1); + + final String MYFILE2 = "This is bar"; + writer = assetService.createFile(authorSandboxId, rootPath+"/a/b/c", "bar", null); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent(MYFILE2); + + assets = sbService.listChangedAll(authorSandboxId, true); + assertEquals(1, assets.size()); + + // check staging before + assertEquals(1, assetService.listAssets(stagingSandboxId, -1, rootPath, false).size()); // note: currently includes default webapp ('ROOT') + + // submit (new assets) ! + sbService.submitAll(authorSandboxId, "a submit label", "a submit comment"); + + Thread.sleep(SUBMIT_DELAY); + + // check staging after + List listing = assetService.listAssets(stagingSandboxId, -1, rootPath, false); + assertEquals(2, listing.size()); // 'a' and 'ROOT' + + // no changes in sandbox + assets = sbService.listChangedAll(authorSandboxId, true); + assertEquals(0, assets.size()); + + final String MYFILE3 = "This is figs"; + writer = assetService.createFile(authorSandboxId, rootPath, "figs", null); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent(MYFILE3); + + final String MYFILE1_MODIFIED = "This is foo ... modified"; + writer = assetService.getContentWriter(assetService.getAsset(authorSandboxId, rootPath+"/a/b/c/foo")); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent(MYFILE1_MODIFIED); + + assetService.deleteAsset(assetService.getAsset(authorSandboxId, rootPath+"/a/b/c/bar")); + + assets = sbService.listChangedAll(authorSandboxId, true); + assertEquals(3, assets.size()); + + // check staging before + listing = assetService.listAssets(stagingSandboxId, -1, rootPath, false); + assertEquals(2, listing.size()); // 'a' and 'ROOT' + + // submit all (modified assets) ! + sbService.submitAll(authorSandboxId, "my label", null); + + Thread.sleep(SUBMIT_DELAY); + + assets = sbService.listChangedAll(authorSandboxId, true); + assertEquals(0, assets.size()); + + // check staging after + listing = assetService.listAssets(stagingSandboxId, -1, rootPath, false); + assertEquals(3, listing.size()); // 'figs', 'a' and 'ROOT' + } + + // submit deleted assets in user sandbox to staging sandbox + public void testSubmitDeletedItems1() throws IOException, InterruptedException + { + WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-submitDeletedItems1", TEST_WEBPROJ_NAME+" submitDeletedItems1", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); + + final String wpStoreId = wpInfo.getStoreId(); + final String webApp = wpInfo.getDefaultWebApp(); + final String stagingSandboxId = wpInfo.getStagingStoreName(); + + // Invite web users + wpService.inviteWebUser(wpStoreId, USER_ONE, WCMUtil.ROLE_CONTENT_CONTRIBUTOR, true); + wpService.inviteWebUser(wpStoreId, USER_TWO, WCMUtil.ROLE_CONTENT_MANAGER, true); // note: publisher does not have permission to delete + + // Switch to USER_ONE + AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); + + SandboxInfo sbInfo = sbService.getAuthorSandbox(wpStoreId); + String authorSandboxId = sbInfo.getSandboxId(); + + // no changes yet + List assets = sbService.listChangedAll(authorSandboxId, true); + assertEquals(0, assets.size()); + + String authorSandboxPath = sbInfo.getSandboxRootPath() + "/" + webApp; + + final String MYFILE1 = "This is myFile1"; + ContentWriter writer = assetService.createFile(authorSandboxId, authorSandboxPath, "myFile1", null); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent(MYFILE1); + + assetService.createFolder(authorSandboxId, authorSandboxPath, "myDir1", null); + assetService.createFolder(authorSandboxId, authorSandboxPath+"/myDir1", "myDir2", null); + + final String MYFILE2 = "This is myFile2"; + writer = assetService.createFile(authorSandboxId, authorSandboxPath+"/myDir1", "myFile2", null); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent(MYFILE2); + + assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); + assertEquals(2, assets.size()); + + // check staging before + String stagingSandboxPath = sbInfo.getSandboxRootPath() + "/" + webApp; + assertEquals(0, assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false).size()); + + // submit (new assets) ! + sbService.submitWebApp(authorSandboxId, webApp, "a submit label", "a submit comment"); + + Thread.sleep(SUBMIT_DELAY); + + assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); + assertEquals(0, assets.size()); + + // check staging after + List listing = assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false); + assertEquals(2, listing.size()); + + // Switch to USER_TWO + AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); + + sbInfo = sbService.getAuthorSandbox(wpStoreId); + authorSandboxId = sbInfo.getSandboxId(); + + // no changes yet + assets = sbService.listChangedAll(authorSandboxId, true); + assertEquals(0, assets.size()); + + //authorSandboxWebppPath = authorSandboxId + AVM_STORE_SEPARATOR + sbInfo.getSandboxRootPath() + "/" + webApp; + + + assetService.deleteAsset(assetService.getAssetWebApp(authorSandboxId, webApp, "myFile1")); + assetService.deleteAsset(assetService.getAssetWebApp(authorSandboxId, webApp, "/myDir1/myDir2")); + + // do not list deleted + assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); + assertEquals(0, assets.size()); + + // do list deleted + assets = sbService.listChangedWebApp(authorSandboxId, webApp, true); + assertEquals(2, assets.size()); + + // check staging before + //stagingSandboxWebppPath = stagingSandboxId + AVM_STORE_SEPARATOR + sbInfo.getSandboxRootPath() + "/" + webApp; + + assertNotNull(assetService.getAssetWebApp(stagingSandboxId, webApp, "/myFile1")); + assertNotNull(assetService.getAssetWebApp(stagingSandboxId, webApp, "/myDir1")); + assertNotNull(assetService.getAssetWebApp(stagingSandboxId, webApp, "/myDir1/myDir2")); + assertNotNull(assetService.getAssetWebApp(stagingSandboxId, webApp, "/myDir1/myFile2")); + + // submit (deleted assets) ! + sbService.submitWebApp(authorSandboxId, webApp, "my label", null); + + Thread.sleep(SUBMIT_DELAY); + + assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); + assertEquals(0, assets.size()); + + // check staging after + assertNull(assetService.getAssetWebApp(stagingSandboxId, webApp, "/myFile1")); + assertNull(assetService.getAssetWebApp(stagingSandboxId, webApp, "/myDir1/myDir2")); + + assertNotNull(assetService.getAssetWebApp(stagingSandboxId, webApp, "/myDir1")); + assertNotNull(assetService.getAssetWebApp(stagingSandboxId, webApp, "/myDir1/myFile2")); + } + + // revert (changed) assets in user sandbox + public void testRevert() throws IOException, InterruptedException + { + WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-revertChangedAssets", TEST_WEBPROJ_NAME+" revertChangedAssets", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); + + final String wpStoreId = wpInfo.getStoreId(); + final String webApp = wpInfo.getDefaultWebApp(); + final String stagingSandboxId = wpInfo.getStagingStoreName(); + + // Invite web users + wpService.inviteWebUser(wpStoreId, USER_ONE, WCMUtil.ROLE_CONTENT_CONTRIBUTOR, true); + + // TODO - pending fix for ETWOTWO-981 + //wpService.inviteWebUser(wpStoreId, USER_TWO, WCMUtil.ROLE_CONTENT_PUBLISHER, true); + + wpService.inviteWebUser(wpStoreId, USER_TWO, WCMUtil.ROLE_CONTENT_MANAGER, true); + + wpService.inviteWebUser(wpStoreId, USER_THREE, WCMUtil.ROLE_CONTENT_MANAGER, true); + wpService.inviteWebUser(wpStoreId, USER_FOUR, WCMUtil.ROLE_CONTENT_REVIEWER, true); + + // Switch to USER_ONE + AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); + + SandboxInfo sbInfo = sbService.getAuthorSandbox(wpStoreId); + String authorSandboxId = sbInfo.getSandboxId(); + + // no changes yet + List assets = sbService.listChangedAll(authorSandboxId, true); + assertEquals(0, assets.size()); + + String authorSandboxPath = sbInfo.getSandboxRootPath() + "/" + webApp; + + final String MYFILE1 = "This is myFile1"; + ContentWriter writer = assetService.createFile(authorSandboxId, authorSandboxPath, "myFile1", null); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent(MYFILE1); + + assetService.createFolder(authorSandboxId, authorSandboxPath, "myDir1", null); + + final String MYFILE2 = "This is myFile2"; + writer = assetService.createFile(authorSandboxId, authorSandboxPath+"/myDir1", "myFile2", null); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent(MYFILE2); + + assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); + assertEquals(2, assets.size()); + + // check staging before + String stagingSandboxPath = sbInfo.getSandboxRootPath() + "/" + webApp; + assertEquals(0, assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false).size()); + + // submit (new assets) ! + sbService.submitWebApp(authorSandboxId, webApp, "a submit label", "a submit comment"); + + Thread.sleep(SUBMIT_DELAY); + + assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); + assertEquals(0, assets.size()); + + // check staging after + List listing = assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false); + assertEquals(2, listing.size()); + + // Switch to USER_TWO + AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); + + sbInfo = sbService.getAuthorSandbox(wpStoreId); + authorSandboxId = sbInfo.getSandboxId(); + + // no changes yet + assets = sbService.listChangedAll(authorSandboxId, true); + assertEquals(0, assets.size()); + + //authorSandboxWebppPath = authorSandboxId + AVM_STORE_SEPARATOR + sbInfo.getSandboxRootPath() + "/" + webApp; + + final String MYFILE1_MODIFIED = "This is myFile1 ... modified by "+USER_TWO; + writer = assetService.getContentWriter(assetService.getAssetWebApp(authorSandboxId, webApp, "/myFile1")); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent(MYFILE1_MODIFIED); + + final String MYFILE2_MODIFIED = "This is myFile2 ... modified by "+USER_TWO; + writer = assetService.getContentWriter(assetService.getAssetWebApp(authorSandboxId, webApp, "/myDir1/myFile2")); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + writer.putContent(MYFILE2_MODIFIED); + + assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); + assertEquals(2, assets.size()); + + // check staging before + stagingSandboxPath = sbInfo.getSandboxRootPath() + "/" + webApp; + + ContentReader reader = assetService.getContentReader(assetService. getAsset(stagingSandboxId, -1, stagingSandboxPath+"/myFile1", false)); + InputStream in = reader.getContentInputStream(); + byte[] buff = new byte[1024]; + in.read(buff); + in.close(); + assertEquals(MYFILE1, new String(buff, 0, MYFILE1.length())); // assumes 1byte = 1char + + reader = assetService.getContentReader(assetService. getAsset(stagingSandboxId, -1, stagingSandboxPath+"/myDir1/myFile2", false)); + in = reader.getContentInputStream(); + buff = new byte[1024]; + in.read(buff); + in.close(); + assertEquals(MYFILE2, new String(buff, 0, MYFILE2.length())); + + // revert (modified assets) ! + sbService.revertWebApp(authorSandboxId, webApp); + + assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); + assertEquals(0, assets.size()); + + // check staging after + reader = assetService.getContentReader(assetService.getAsset(stagingSandboxId, -1, stagingSandboxPath+"/myFile1", false)); + in = reader.getContentInputStream(); + buff = new byte[1024]; + in.read(buff); + in.close(); + assertEquals(MYFILE1, new String(buff, 0, MYFILE1.length())); + + reader = assetService.getContentReader(assetService.getAsset(stagingSandboxId, -1, stagingSandboxPath+"/myDir1/myFile2", false)); + in = reader.getContentInputStream(); + buff = new byte[1024]; + in.read(buff); + in.close(); + assertEquals(MYFILE2, new String(buff, 0, MYFILE2.length())); + + // test roles + + // Switch to USER_ONE + AuthenticationUtil.setFullyAuthenticatedUser(USER_ADMIN); + + // admin (content manager) can revert any sandbox + String userSandboxId = wpStoreId + "--" + USER_THREE; + sbService.revertAll(userSandboxId); + + // Switch to USER_THREE + AuthenticationUtil.setFullyAuthenticatedUser(USER_THREE); + + // content manager can revert any (author) sandbox + userSandboxId = wpStoreId + "--" + USER_ONE; + sbService.revertAll(userSandboxId); + + // Switch to USER_ONE + AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); + + try + { + // Content contributor - try to revert another user's sandbox (-ve test) + userSandboxId = wpStoreId + "--" + USER_THREE; + List noAssets = new ArrayList(0); + sbService.revertListAssets(userSandboxId, noAssets); + fail("Shouldn't be able to revert another author's sandbox"); + } + catch (AccessDeniedException exception) + { + // Expected + } + + // TODO - pending fix for ETWOTWO-981 - see above + /* + // Switch to USER_TWO + AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); + + try + { + // Content publisher - try to revert another user's sandbox (-ve test) + userSandboxId = wpStoreId + "--" + USER_ONE; + sbService.revertAll(userSandboxId); + fail("Shouldn't be able to revert another author's sandbox"); + } + catch (AccessDeniedException exception) + { + // Expected + } + */ + + // Switch to USER_FOUR + AuthenticationUtil.setFullyAuthenticatedUser(USER_FOUR); + + try + { + // Content reviewer - try to revert another user's sandbox (-ve test) + userSandboxId = TEST_SANDBOX+"-get" + "--" + USER_THREE; + sbService.revertAll(userSandboxId); + fail("Shouldn't be able to revert another author's sandbox"); + } + catch (AccessDeniedException exception) + { + // Expected + } + } + + public void testListSnapshots() throws IOException, InterruptedException + { + Date fromDate = new Date(); + + WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-listSnapshots", TEST_WEBPROJ_NAME+" listSnapshots", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); + + final String wpStoreId = wpInfo.getStoreId(); + final String webApp = wpInfo.getDefaultWebApp(); + final String stagingSandboxId = wpInfo.getStagingStoreName(); + + // Invite web users + wpService.inviteWebUser(wpStoreId, USER_ONE, WCMUtil.ROLE_CONTENT_MANAGER, true); + + // Switch to USER_ONE + AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); + + SandboxInfo sbInfo = sbService.getAuthorSandbox(wpStoreId); + String authorSandboxId = sbInfo.getSandboxId(); + + // no changes yet + List assets = sbService.listChangedAll(authorSandboxId, true); + assertEquals(0, assets.size()); + + assetService.createFolderWebApp(authorSandboxId, webApp, "/", "myDir1"); + assetService.createFolderWebApp(authorSandboxId, webApp, "/", "myDir2"); + assetService.createFolderWebApp(authorSandboxId, webApp, "/", "myDir3"); + + assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); + assertEquals(3, assets.size()); + + // check staging before + String stagingSandboxPath = sbInfo.getSandboxRootPath() + "/" + webApp; + assertEquals(0, assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false).size()); + + List sbVersions = sbService.listSnapshots(stagingSandboxId, fromDate, new Date(), false); + assertEquals(0, sbVersions.size()); + + // submit (new assets) ! + sbService.submitWebApp(authorSandboxId, webApp, "a submit label", "a submit comment"); + + Thread.sleep(SUBMIT_DELAY); + + assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); + assertEquals(0, assets.size()); + + // check staging after + List listing = assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false); + assertEquals(3, listing.size()); + + sbVersions = sbService.listSnapshots(stagingSandboxId, fromDate, new Date(), false); + assertEquals(1, sbVersions.size()); + + // more changes ... + assetService.createFolderWebApp(authorSandboxId, webApp, "/", "myDir4"); + + // submit (new assets) ! + sbService.submitWebApp(authorSandboxId, webApp, "a submit label", "a submit comment"); + + Thread.sleep(SUBMIT_DELAY); + + // check staging after + listing = assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false); + assertEquals(4, listing.size()); + + sbVersions = sbService.listSnapshots(stagingSandboxId, fromDate, new Date(), false); + assertEquals(2, sbVersions.size()); + } + + public void testRevertSnapshot() throws IOException, InterruptedException + { + Date fromDate = new Date(); + + WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-revertSnapshot", TEST_WEBPROJ_NAME+" revertSnapshot", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); + + final String wpStoreId = wpInfo.getStoreId(); + final String webApp = wpInfo.getDefaultWebApp(); + final String stagingSandboxId = wpInfo.getStagingStoreName(); + + // Start: Test ETWOTWO-817 + + // Invite web users + wpService.inviteWebUser(wpStoreId, USER_ONE, WCMUtil.ROLE_CONTENT_MANAGER, true); + + // Switch to USER_ONE + AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); + + // Finish: Test ETWOTWO-817 + + SandboxInfo sbInfo = sbService.getAuthorSandbox(wpStoreId); + String authorSandboxId = sbInfo.getSandboxId(); + + // no changes yet + List assets = sbService.listChangedAll(authorSandboxId, true); + assertEquals(0, assets.size()); + + String authorSandboxPath = sbInfo.getSandboxRootPath() + "/" + webApp; + + assetService.createFolder(authorSandboxId, authorSandboxPath, "myDir1", null); + + assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); + assertEquals(1, assets.size()); + + // check staging before + String stagingSandboxPath = sbInfo.getSandboxRootPath() + "/" + webApp; + assertEquals(0, assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false).size()); + + List sbVersions = sbService.listSnapshots(stagingSandboxId, fromDate, new Date(), false); + assertEquals(0, sbVersions.size()); + + // submit (new assets) ! + sbService.submitWebApp(authorSandboxId, webApp, "a submit label", "a submit comment"); + + Thread.sleep(SUBMIT_DELAY); + + assets = sbService.listChangedWebApp(authorSandboxId, webApp, false); + assertEquals(0, assets.size()); + + // check staging after + List listing = assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false); + assertEquals(1, listing.size()); + for (AssetInfo asset : listing) + { + if (asset.getName().equals("myDir1") && asset.isFolder()) + { + continue; + } + else + { + fail("The asset '" + asset.getName() + "' is not recognised"); + } + } + + sbVersions = sbService.listSnapshots(stagingSandboxId, fromDate, new Date(), false); + assertEquals(1, sbVersions.size()); + + // more changes ... + assetService.createFolder(authorSandboxId, authorSandboxPath, "myDir2", null); + + // submit (new assets) ! + sbService.submitWebApp(authorSandboxId, webApp, "a submit label", "a submit comment"); + + Thread.sleep(SUBMIT_DELAY); + + // check staging after + listing = assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false); + assertEquals(2, listing.size()); + for (AssetInfo asset : listing) + { + if (asset.getName().equals("myDir1") && asset.isFolder()) + { + continue; + } + else if (asset.getName().equals("myDir2") && asset.isFolder()) + { + continue; + } + else + { + fail("The asset '" + asset.getName() + "' is not recognised"); + } + } + + sbVersions = sbService.listSnapshots(stagingSandboxId, fromDate, new Date(), false); + assertEquals(2, sbVersions.size()); + + // more changes ... + assetService.createFolderWebApp(authorSandboxId, webApp, "/", "myDir3"); + + // submit (new assets) ! + sbService.submitWebApp(authorSandboxId, webApp, "a submit label", "a submit comment"); + + Thread.sleep(SUBMIT_DELAY); + + // check staging after + listing = assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false); + assertEquals(3, listing.size()); + for (AssetInfo asset : listing) + { + if (asset.getName().equals("myDir1") && asset.isFolder()) + { + continue; + } + else if (asset.getName().equals("myDir2") && asset.isFolder()) + { + continue; + } + else if (asset.getName().equals("myDir3") && asset.isFolder()) + { + continue; + } + else + { + fail("The asset '" + asset.getName() + "' is not recognised"); + } + } + + sbVersions = sbService.listSnapshots(stagingSandboxId, fromDate, new Date(), false); + assertEquals(3, sbVersions.size()); + + // revert to snapshot ... + + SandboxVersion version = sbVersions.get(1); + int versionId = version.getVersion(); + + sbService.revertSnapshot(stagingSandboxId, versionId); + + sbVersions = sbService.listSnapshots(stagingSandboxId, fromDate, new Date(), false); + assertEquals(4, sbVersions.size()); + + // check staging after + listing = assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false); + assertEquals(2, listing.size()); + for (AssetInfo asset : listing) + { + if (asset.getName().equals("myDir1") && asset.isFolder()) + { + continue; + } + else if (asset.getName().equals("myDir2") && asset.isFolder()) + { + continue; + } + else + { + fail("The asset '" + asset.getName() + "' is not recognised"); + } + } + } + + public void testPseudoScaleTest() + { + long start = System.currentTimeMillis(); + + long split = start; + + for (int i = 1; i <= SCALE_USERS; i++) + { + createUser(TEST_USER+"-"+i); + } + + System.out.println("testPseudoScaleTest: created "+SCALE_USERS+" users in "+(System.currentTimeMillis()-split)+" msecs"); + + split = System.currentTimeMillis(); + + for (int i = 1; i <= SCALE_WEBPROJECTS; i++) + { + wpService.createWebProject(TEST_WEBPROJ_DNS+"-"+i, TEST_WEBPROJ_NAME+"-"+i, TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); // ignore return + } + + System.out.println("testPseudoScaleTest: created "+SCALE_WEBPROJECTS+" web projects in "+(System.currentTimeMillis()-split)+" msecs"); + + split = System.currentTimeMillis(); + + for (int i = 1; i <= SCALE_WEBPROJECTS; i++) + { + WebProjectInfo wpInfo = wpService.getWebProject(TEST_WEBPROJ_DNS+"-"+i); + Map userRoles = new HashMap(SCALE_USERS); + for (int j = 1; j <= SCALE_USERS; j++) + { + userRoles.put(TEST_USER+"-"+j, WCMUtil.ROLE_CONTENT_MANAGER); + } + wpService.inviteWebUsersGroups(wpInfo.getNodeRef(), userRoles, true); + } + + System.out.println("testPseudoScaleTest: invited "+SCALE_USERS+" content managers (and created user sandboxes) to each of "+SCALE_WEBPROJECTS+" web projects in "+(System.currentTimeMillis()-split)+" msecs"); + + split = System.currentTimeMillis(); + + for (int i = 1; i <= SCALE_WEBPROJECTS; i++) + { + WebProjectInfo wpInfo = wpService.getWebProject(TEST_WEBPROJ_DNS+"-"+i); + assertEquals(SCALE_USERS+2, sbService.listSandboxes(wpInfo.getStoreId()).size()); // including staging sandbox and admin sandbox (web project creator) + } + + System.out.println("testPseudoScaleTest: list sandboxes for admin for each of "+SCALE_WEBPROJECTS+" web projects in "+(System.currentTimeMillis()-split)+" msecs"); + + split = System.currentTimeMillis(); + + for (int i = 1; i <= SCALE_WEBPROJECTS; i++) + { + WebProjectInfo wpInfo = wpService.getWebProject(TEST_WEBPROJ_DNS+"-"+i); + assertEquals(SCALE_USERS+1, wpService.listWebUsers(wpInfo.getStoreId()).size()); // including admin user (web project creator) + } + + System.out.println("testPseudoScaleTest: list web users for admin for each of "+SCALE_WEBPROJECTS+" web projects in "+(System.currentTimeMillis()-split)+" msecs"); + + split = System.currentTimeMillis(); + + for (int i = 1; i <= SCALE_WEBPROJECTS; i++) + { + WebProjectInfo wpInfo = wpService.getWebProject(TEST_WEBPROJ_DNS+"-"+i); + + for (int j = 1; j <= SCALE_USERS; j++) + { + AuthenticationUtil.setFullyAuthenticatedUser(TEST_USER+"-"+j); + assertEquals(SCALE_USERS+2, sbService.listSandboxes(wpInfo.getStoreId()).size()); // including staging sandbox and admin sandbox (web project creator) + } + AuthenticationUtil.setFullyAuthenticatedUser(USER_ADMIN); + } + + System.out.println("testPseudoScaleTest: list sandboxes for "+SCALE_USERS+" content managers for each of "+SCALE_WEBPROJECTS+" web projects in "+(System.currentTimeMillis()-split)+" msecs"); + + split = System.currentTimeMillis(); + + for (int i = 1; i <= SCALE_WEBPROJECTS; i++) + { + WebProjectInfo wpInfo = wpService.getWebProject(TEST_WEBPROJ_DNS+"-"+i); + + for (int j = 1; j <= SCALE_USERS; j++) + { + AuthenticationUtil.setFullyAuthenticatedUser(TEST_USER+"-"+j); + assertEquals(SCALE_USERS+1, wpService.listWebUsers(wpInfo.getStoreId()).size()); // including admin user (web project creator) + } + AuthenticationUtil.setFullyAuthenticatedUser(USER_ADMIN); + } + + System.out.println("testPseudoScaleTest: list web users for "+SCALE_USERS+" content managers for each of "+SCALE_WEBPROJECTS+" web projects in "+(System.currentTimeMillis()-split)+" msecs"); + + split = System.currentTimeMillis(); + + for (int i = 1; i <= SCALE_WEBPROJECTS; i++) + { + WebProjectInfo wpInfo = wpService.getWebProject(TEST_WEBPROJ_DNS+"-"+i); + + for (int j = 1; j <= SCALE_USERS; j++) + { + SandboxInfo sbInfo = sbService.getAuthorSandbox(wpInfo.getStoreId(), TEST_USER+"-"+j); + sbService.deleteSandbox(sbInfo.getSandboxId()); + } + } + + System.out.println("testPseudoScaleTest: deleted "+SCALE_USERS+" author sandboxes for each of "+SCALE_WEBPROJECTS+" web projects in "+(System.currentTimeMillis()-split)+" msecs"); + + split = System.currentTimeMillis(); + + for (int i = 1; i <= SCALE_WEBPROJECTS; i++) + { + WebProjectInfo wpInfo = wpService.getWebProject(TEST_WEBPROJ_DNS+"-"+i); + wpService.deleteWebProject(wpInfo.getNodeRef()); + } + + System.out.println("testPseudoScaleTest: deleted "+SCALE_WEBPROJECTS+" web projects in "+(System.currentTimeMillis()-split)+" msecs"); + + split = System.currentTimeMillis(); + + for (int i = 1; i <= SCALE_USERS; i++) + { + deleteUser(TEST_USER+"-"+i); + } + + System.out.println("testPseudoScaleTest: deleted "+SCALE_USERS+" users in "+(System.currentTimeMillis()-split)+" msecs"); + } + + + /* + // == Test the JavaScript API == + + public void testJSAPI() throws Exception + { + ScriptLocation location = new ClasspathScriptLocation("org/alfresco/wcm/script/test_sandboxService.js"); + scriptService.executeScript(location, new HashMap(0)); + } + */ +} diff --git a/source/java/org/alfresco/wcm/webproject/WebProjectService.java b/source/java/org/alfresco/wcm/webproject/WebProjectService.java index 0a513231f9..7139d76d2a 100644 --- a/source/java/org/alfresco/wcm/webproject/WebProjectService.java +++ b/source/java/org/alfresco/wcm/webproject/WebProjectService.java @@ -325,6 +325,22 @@ public interface WebProjectService */ public String getWebUserRole(NodeRef wpNodeRef, String userName); + /** + * Indicates whether current user is a web user of the web project or not + * + * @param store id web project store id + * @return boolean true if the current user is a web user of the web project, false otherwise + */ + public boolean isWebUser(String wpStoreId); + + /** + * Indicates whether current user is a web user of the web project or not + * + * @param wpNodeRef web project node ref + * @return boolean true if the current user is a web user of the web project, false otherwise + */ + public boolean isWebUser(NodeRef wpNodeRef); + /** * Indicates whether given user is a web user of the web project or not * diff --git a/source/java/org/alfresco/wcm/webproject/WebProjectServiceImpl.java b/source/java/org/alfresco/wcm/webproject/WebProjectServiceImpl.java index 651c1da8d1..c2f4fff474 100644 --- a/source/java/org/alfresco/wcm/webproject/WebProjectServiceImpl.java +++ b/source/java/org/alfresco/wcm/webproject/WebProjectServiceImpl.java @@ -642,7 +642,7 @@ public class WebProjectServiceImpl extends WCMUtil implements WebProjectService { public Object doWork() throws Exception { - List sbInfos = sandboxFactory.listSandboxes(wpStoreId, AuthenticationUtil.getSystemUserName()); + List sbInfos = sandboxFactory.listAllSandboxes(wpStoreId); for (SandboxInfo sbInfo : sbInfos) { @@ -699,24 +699,43 @@ public class WebProjectServiceImpl extends WCMUtil implements WebProjectService return WCMUtil.ROLE_CONTENT_MANAGER.equals(userRole); } + /* (non-Javadoc) + * @see org.alfresco.wcm.webproject.WebProjectService#isWebUser(java.lang.String) + */ + public boolean isWebUser(String wpStoreId) + { + return isWebUser(getWebProjectNodeFromStore(wpStoreId)); + } + + /* (non-Javadoc) + * @see org.alfresco.wcm.webproject.WebProjectService#isWebUser(org.alfresco.service.cmr.repository.NodeRef) + */ + public boolean isWebUser(NodeRef wpNodeRef) + { + // note: admin is an implied web user (content manager) although will not appear in listWebUsers unless explicitly invited + return (permissionService.hasPermission(wpNodeRef, PermissionService.READ) == AccessStatus.ALLOWED); + } + /* (non-Javadoc) * @see org.alfresco.wcm.webproject.WebProjectService#isWebUser(java.lang.String, java.lang.String) */ public boolean isWebUser(String wpStoreId, String username) { - ParameterCheck.mandatoryString("username", username); - return isWebUser(getWebProjectNodeFromStore(wpStoreId), username); } /* (non-Javadoc) * @see org.alfresco.wcm.webproject.WebProjectService#isWebUser(org.alfresco.service.cmr.repository.NodeRef, java.lang.String) */ - public boolean isWebUser(NodeRef wpNodeRef, String userName) + public boolean isWebUser(final NodeRef wpNodeRef, String userName) { - ParameterCheck.mandatoryString("userName", userName); - - return (getWebUserRoleImpl(wpNodeRef, userName) != null); + return AuthenticationUtil.runAs(new RunAsWork() + { + public Boolean doWork() throws Exception + { + return isWebUser(wpNodeRef); + } + }, userName); } /* (non-Javadoc) diff --git a/source/java/org/alfresco/wcm/webproject/WebProjectServiceImplTest.java b/source/java/org/alfresco/wcm/webproject/WebProjectServiceImplTest.java index fd8a794f9b..5519cb3983 100644 --- a/source/java/org/alfresco/wcm/webproject/WebProjectServiceImplTest.java +++ b/source/java/org/alfresco/wcm/webproject/WebProjectServiceImplTest.java @@ -853,7 +853,7 @@ public class WebProjectServiceImplTest extends AbstractWCMServiceImplTest } /** - * Test inviteWebUser - and listWebProjects / listWebUsers + * Test inviteWebUser - and listWebProjects / listWebUsers / isWebUser */ public void testInviteAndListWebUsers() { @@ -871,6 +871,9 @@ public class WebProjectServiceImplTest extends AbstractWCMServiceImplTest WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-inviteWebUser1", TEST_WEBPROJ_NAME+"-inviteWebUser1", TEST_TITLE, TEST_DESCRIPTION); NodeRef wpNodeRef = wpInfo.getNodeRef(); + assertTrue(wpService.isWebUser(wpNodeRef, USER_ADMIN)); + assertFalse(wpService.isWebUser(wpNodeRef, USER_ONE)); + assertEquals(1, wpService.listWebUsers(wpNodeRef).size()); assertEquals(WCMUtil.ROLE_CONTENT_MANAGER, wpService.listWebUsers(wpNodeRef).get(USER_ADMIN)); @@ -883,6 +886,9 @@ public class WebProjectServiceImplTest extends AbstractWCMServiceImplTest assertEquals(3, wpService.listWebUsers(wpNodeRef).size()); assertEquals(WCMUtil.ROLE_CONTENT_PUBLISHER, wpService.listWebUsers(wpNodeRef).get(USER_ONE)); assertEquals(WCMUtil.ROLE_CONTENT_MANAGER, wpService.listWebUsers(wpNodeRef).get(USER_TWO)); + + assertTrue(wpService.isWebUser(wpInfo.getStoreId(), USER_ONE)); + assertTrue(wpService.isWebUser(wpNodeRef, USER_TWO)); // Create another web project wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-inviteWebUser2", TEST_WEBPROJ_NAME+"-inviteWebUser2", TEST_TITLE, TEST_DESCRIPTION); @@ -891,6 +897,9 @@ public class WebProjectServiceImplTest extends AbstractWCMServiceImplTest assertEquals(1, wpService.listWebUsers(wpNodeRef2).size()); assertEquals(WCMUtil.ROLE_CONTENT_MANAGER, wpService.listWebUsers(wpNodeRef2).get(USER_ADMIN)); + assertFalse(wpService.isWebUser(wpInfo.getStoreId(), USER_ONE)); + assertFalse(wpService.isWebUser(wpNodeRef2, USER_TWO)); + wpService.inviteWebUser(wpNodeRef2, USER_TWO, WCMUtil.ROLE_CONTENT_PUBLISHER, false); wpService.inviteWebUser(wpNodeRef2, USER_ONE, WCMUtil.ROLE_CONTENT_MANAGER, false); @@ -898,6 +907,9 @@ public class WebProjectServiceImplTest extends AbstractWCMServiceImplTest assertEquals(WCMUtil.ROLE_CONTENT_PUBLISHER, wpService.listWebUsers(wpNodeRef2).get(USER_TWO)); assertEquals(WCMUtil.ROLE_CONTENT_MANAGER, wpService.listWebUsers(wpNodeRef2).get(USER_ONE)); + assertTrue(wpService.isWebUser(wpInfo.getStoreId(), USER_ONE)); + assertTrue(wpService.isWebUser(wpNodeRef2, USER_TWO)); + // Switch to USER_ONE AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); @@ -939,9 +951,13 @@ public class WebProjectServiceImplTest extends AbstractWCMServiceImplTest // Switch to USER_ONE AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); + assertFalse(wpService.isWebUser(wpNodeRef2, USER_THREE)); + // Invite web user wpService.inviteWebUser(wpNodeRef2, USER_THREE, WCMUtil.ROLE_CONTENT_REVIEWER, false); + assertTrue(wpService.isWebUser(wpNodeRef2, USER_THREE)); + // Switch back to admin AuthenticationUtil.setFullyAuthenticatedUser(USER_ADMIN); @@ -950,16 +966,16 @@ public class WebProjectServiceImplTest extends AbstractWCMServiceImplTest } /** - * Test uninviteWebUser - and listWebProjects / listWebUsers + * Test uninviteWebUser - and listWebProjects / listWebUsers / isWebUser */ public void testUninviteAndListWebUsers() { // Switch to USER_FOUR AuthenticationUtil.setFullyAuthenticatedUser(USER_FOUR); - + List webProjects = wpService.listWebProjects(); assertNotNull(webProjects); - assertEquals(0, webProjects.size()); + int userFourWebProjectCount = webProjects.size(); // Switch back to admin AuthenticationUtil.setFullyAuthenticatedUser(USER_ADMIN); @@ -971,21 +987,27 @@ public class WebProjectServiceImplTest extends AbstractWCMServiceImplTest assertEquals(1, wpService.listWebUsers(wpNodeRef).size()); assertEquals(WCMUtil.ROLE_CONTENT_MANAGER, wpService.listWebUsers(wpNodeRef).get(USER_ADMIN)); + assertTrue(wpService.isWebUser(wpNodeRef, USER_ADMIN)); + assertFalse(wpService.isWebUser(wpNodeRef, USER_FOUR)); + assertFalse(wpService.isWebUser(wpNodeRef, USER_ONE)); + wpService.inviteWebUser(wpNodeRef, USER_FOUR, WCMUtil.ROLE_CONTENT_CONTRIBUTOR, false); assertEquals(2, wpService.listWebUsers(wpNodeRef).size()); assertEquals(WCMUtil.ROLE_CONTENT_CONTRIBUTOR, wpService.listWebUsers(wpNodeRef).get(USER_FOUR)); + assertTrue(wpService.isWebUser(wpNodeRef, USER_FOUR)); wpService.inviteWebUser(wpNodeRef, USER_ONE, WCMUtil.ROLE_CONTENT_MANAGER, false); assertEquals(3, wpService.listWebUsers(wpNodeRef).size()); assertEquals(WCMUtil.ROLE_CONTENT_MANAGER, wpService.listWebUsers(wpNodeRef).get(USER_ONE)); + assertTrue(wpService.isWebUser(wpNodeRef, USER_ONE)); // Switch to USER_FOUR AuthenticationUtil.setFullyAuthenticatedUser(USER_FOUR); webProjects = wpService.listWebProjects(); - assertEquals(1, webProjects.size()); + assertEquals(userFourWebProjectCount+1, webProjects.size()); // Switch to USER_TWO AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); @@ -1023,12 +1045,13 @@ public class WebProjectServiceImplTest extends AbstractWCMServiceImplTest assertEquals(2, wpService.listWebUsers(wpNodeRef).size()); assertEquals(null, wpService.listWebUsers(wpNodeRef).get(USER_FOUR)); + assertFalse(wpService.isWebUser(wpNodeRef, USER_FOUR)); // Switch to USER_FOUR AuthenticationUtil.setFullyAuthenticatedUser(USER_FOUR); webProjects = wpService.listWebProjects(); - assertEquals(0, webProjects.size()); + assertEquals(userFourWebProjectCount, webProjects.size()); // Switch back to admin AuthenticationUtil.setFullyAuthenticatedUser(USER_ADMIN); @@ -1036,9 +1059,12 @@ public class WebProjectServiceImplTest extends AbstractWCMServiceImplTest // Content manager can uninvite themself // Uninvite web user - test using wpNodeRef wpService.uninviteWebUser(wpNodeRef, USER_ADMIN, false); - + + // Note: All admin authorities are implicitly a web user and content manager (across all web projects) even if not explicitly invited assertEquals(1, wpService.listWebUsers(wpNodeRef).size()); - assertEquals(null, wpService.listWebUsers(wpNodeRef).get(USER_ADMIN)); + assertEquals(null, wpService.listWebUsers(wpNodeRef).get(USER_ADMIN)); + assertTrue(wpService.isWebUser(wpNodeRef, USER_ADMIN)); + assertTrue(wpService.isContentManager(wpNodeRef, USER_ADMIN)); // Switch to USER_ONE AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); @@ -1048,6 +1074,7 @@ public class WebProjectServiceImplTest extends AbstractWCMServiceImplTest // Delete user (in this case, last invited content manager) wpService.uninviteWebUser(wpNodeRef, USER_ONE, false); + assertFalse(wpService.isWebUser(wpNodeRef, USER_ONE)); try { @@ -1063,10 +1090,10 @@ public class WebProjectServiceImplTest extends AbstractWCMServiceImplTest // Switch back to admin AuthenticationUtil.setFullyAuthenticatedUser(USER_ADMIN); - // Note: All admin authorities are implicitly content managers (across all web projects) even if not explicitly invited - assertTrue(wpService.isContentManager(wpInfo.getStoreId(), USER_ADMIN)); - + // Note: All admin authorities are implicitly a web user and content manager (across all web projects) even if not explicitly invited assertEquals(0, wpService.listWebUsers(wpNodeRef).size()); + assertTrue(wpService.isWebUser(wpNodeRef, USER_ADMIN)); + assertTrue(wpService.isContentManager(wpInfo.getStoreId(), USER_ADMIN)); // delete web project wpService.deleteWebProject(wpNodeRef); diff --git a/source/test-resources/jbpmresources/test_timers.xml b/source/test-resources/jbpmresources/test_timers.xml index d128f0c76b..57da7bb082 100644 --- a/source/test-resources/jbpmresources/test_timers.xml +++ b/source/test-resources/jbpmresources/test_timers.xml @@ -24,7 +24,7 @@ - + @@ -40,12 +40,12 @@ - - - + + + diff --git a/source/test-resources/wcm/jbpm.cfg.xml b/source/test-resources/wcm/jbpm.cfg.xml index 024d36fd04..7dd162ec07 100644 --- a/source/test-resources/wcm/jbpm.cfg.xml +++ b/source/test-resources/wcm/jbpm.cfg.xml @@ -1,43 +1,43 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +