From 507c4d8bf8eba0625969f7ff2bad53f4e7f79f2c Mon Sep 17 00:00:00 2001 From: Dave Ward Date: Sat, 2 Jun 2012 07:56:08 +0000 Subject: [PATCH] Merged V4.0-BUG-FIX to HEAD 37207: BDE-69: Filter more tests for minimal build plan 37253: Fix for ALF-13634 Re-created category won't show up again on a node in Document Library. - also fixes deletion of secondary associations 37265: Merged V4.0 to V4.0-BUG-FIX 37224: ALF-14174: Part 14 for ALF-14237 Upgrades from 4.0.0.x/4..0.1.0 will not fix the timestamps on acl changesets - SOLR will skip ACLs set prior to upgrade - Fix syntax error on Oracle 37250: Fix for ALF-14174 The patch adding timestamps to acl_change_set breaks SOLR tracking - better cross DB fix 37298: ALF-14365 - added hazelcastConfig.xml.sample 37323: ALF-13247: Two nodes with the same primary path. -Fixed by initializing zone before parallel batch processing begins. 37326: ALF-13933 Alfresco needs to be able to support LibreOffice for transformations ALF-13452 Open office startup from Java not working on OSX - Added code to start LibreOffice 3.5 on Mac (requires different options to the command and ure-link is a directory rather than a file on mac) - Removes $DYLD_LIBRARY_PATH from the environment when starting either openoffice or libreoffice on mac so does not need to rely on the installer moving the soffice.bin process to .soffice.bin and then creating a soffice.bin shell script that removed $DYLD_LIBRARY_PATH - Indent TransformerDebug a bit more now we have fail over transformers at the top and lower levels (saves N.N.N.N.N.N getting mixed up with text) 37340: Merged V3.4-BUG-FIX (3.4.10) to V4.0-BUG-FIX (4.0.3) RECORD ONLY 37339: ALF-13452: Merged V4.0-BUG-FIX (4.0.3) to V3.4-BUG-FIX (3.4.10) 37326: ALF-13933 Alfresco needs to be able to support LibreOffice for transformations ALF-13452 Open office startup from Java not working on OSX - Added code to start LibreOffice 3.5 on Mac (requires different options to the command and ure-link is a directory rather than a file on mac) - Removes $DYLD_LIBRARY_PATH from the environment when starting either openoffice or libreoffice on mac so does not need to rely on the installer moving the soffice.bin process to .soffice.bin and then creating a soffice.bin shell script that removed $DYLD_LIBRARY_PATH - Indent TransformerDebug a bit more now we have fail over transformers at the top and lower levels (saves N.N.N.N.N.N getting mixed up with text) 36273: ALF-13933 Alfresco needs to be able to support LibreOffice for transformations - Return a dummy OpenOffice command even when there is no OpenOffice/LibreOffice installed or on the path. 36264: ALF-13933 Alfresco needs to be able to support LibreOffice for transformations - remove old jodconverter-core-3.0-beta-3.diff 36259: ALF-13933 Alfresco needs to be able to support LibreOffice for transformations << Developed on Windows 7. Might need more work on Linux to get LibreOffice to shut down, but should be okay with OpenOffice 3.2 which was used in the previous release. >> - Updated jodconverter to latest version jodconverter-core-3.0-SNAPSHOT-patched.jar 28/4/2012 which is newer than 3.0 beta-4 - Applied patch for http://code.google.com/p/jodconverter/issues/detail?id=103 to handle setting the env for LibreOffice 3.5 - Modified code to use partial GNU style options (not used for -env!) when using LibreOffice - Added OpenOfficeCommandLine to dynamically supply OpenOffice or LibreOffice command line args for OOoDirect - Tested to work with OpenOffice 3.4 and 3.2 on Windows 7 37353: Merged V3.4-BUG-FIX (3.4.10) to V4.0-BUG-FIX (4.0.3) 37352: ALF-13452, ALF-13933 Alfresco needs to be able to support LibreOffice for transformations - Build test failure 37359: New JUnit Rule to support automatic creation and cleanup of Share sites in test code. This is required for an imminent fix to ALF-14345, but I'm checking it in separately in order to merge this general utility. 37360: Fix for ALF-14345. Site Service list method does not recognise sub-types of st:site. 37364: Merged V3.4-BUG-FIX (3.4.10) to V4.0-BUG-FIX (4.0.3) RECORD ONLY (not needed in 4.0.x) 37363: ALF-13452, ALF-13933 Alfresco needs to be able to support LibreOffice for transformations - Build test failure x2 (reference to jodconverter*jar not needed in 4.0.x) 37370: Merged V3.4-BUG-FIX: ALF-11714: Updated WCMQS to ensure all FreeMarker variables output to HTML are protected with ?html to prevent XSS 37382: Translation (DE, IT, JA, NL) updates from Gloria, based on EN rev37081 37384: Fix for ALF-14219 SolrQueryHTTPClient unable to handle long queries (4096 bytes) 37386: Merged V4.0 to V4.0-BUG-FIX 37385: ALF-14238: Fix by Dmitry to correct iteration in ImapUnsubscribedAspectPatch git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@37387 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../AclChangeSet-Tracking2.sql | 8 +- .../extension/hazelcastConfig.xml.sample | 192 ++++++++++++++++++ .../messages/patch-service_de.properties | 7 + .../messages/patch-service_it.properties | 7 + .../messages/patch-service_ja.properties | 7 + .../messages/patch-service_nl.properties | 7 + .../alfresco/patch/patch-services-context.xml | 3 + .../default/openoffice-transform-context.xml | 7 + .../impl/ImapUnsubscribedAspectPatch.java | 46 +++-- .../content/transform/TransformerDebug.java | 2 +- .../repo/domain/node/AbstractNodeDAOImpl.java | 14 ++ .../alfresco/repo/domain/node/NodeDAO.java | 10 + .../search/impl/solr/SolrQueryHTTPClient.java | 9 +- .../ChainingUserRegistrySynchronizer.java | 13 +- .../alfresco/repo/site/SiteServiceImpl.java | 3 +- .../repo/site/SiteServiceImplMoreTest.java | 121 +++++++++++ .../repo/solr/SOLRTrackingComponentImpl.java | 116 ++++++++--- .../alfresco/util/OpenOfficeCommandEnv.java | 62 ++++++ .../alfresco/util/OpenOfficeCommandLine.java | 121 ++--------- .../org/alfresco/util/OpenOfficeVariant.java | 139 +++++++++++++ .../util/test/junitrules/TemporarySites.java | 165 +++++++++++++++ .../test/junitrules/TemporarySitesTest.java | 111 ++++++++++ .../sites/test-TemporarySitesTest-context.xml | 14 ++ source/test-resources/sites/testSiteModel.xml | 26 +++ 24 files changed, 1038 insertions(+), 172 deletions(-) create mode 100644 config/alfresco/extension/hazelcastConfig.xml.sample create mode 100644 source/java/org/alfresco/repo/site/SiteServiceImplMoreTest.java create mode 100644 source/java/org/alfresco/util/OpenOfficeCommandEnv.java create mode 100644 source/java/org/alfresco/util/OpenOfficeVariant.java create mode 100644 source/java/org/alfresco/util/test/junitrules/TemporarySites.java create mode 100644 source/java/org/alfresco/util/test/junitrules/TemporarySitesTest.java create mode 100644 source/test-resources/sites/test-TemporarySitesTest-context.xml create mode 100644 source/test-resources/sites/testSiteModel.xml diff --git a/config/alfresco/dbscripts/upgrade/4.0/org.hibernate.dialect.Dialect/AclChangeSet-Tracking2.sql b/config/alfresco/dbscripts/upgrade/4.0/org.hibernate.dialect.Dialect/AclChangeSet-Tracking2.sql index 5a862266b0..4d3ad56922 100644 --- a/config/alfresco/dbscripts/upgrade/4.0/org.hibernate.dialect.Dialect/AclChangeSet-Tracking2.sql +++ b/config/alfresco/dbscripts/upgrade/4.0/org.hibernate.dialect.Dialect/AclChangeSet-Tracking2.sql @@ -8,13 +8,17 @@ -- -- Migrate data + +--ASSIGN:min_tx_ms=min_tx_ms +SELECT min(commit_time_ms) as min_tx_ms from alf_transaction; + --FOREACH alf_acl_change_set.id system.upgrade.alf_acl_change_set.batchsize UPDATE alf_acl_change_set SET - commit_time_ms = ((select min(t.commit_time_ms) from alf_transaction t) + id) + commit_time_ms = ${min_tx_ms} + id WHERE id >= ${LOWERBOUND} AND id <= ${UPPERBOUND} - AND commit_time_ms < (select min(t.commit_time_ms) from alf_transaction t) + AND commit_time_ms < ${min_tx_ms} ; diff --git a/config/alfresco/extension/hazelcastConfig.xml.sample b/config/alfresco/extension/hazelcastConfig.xml.sample new file mode 100644 index 0000000000..5cd2991026 --- /dev/null +++ b/config/alfresco/extension/hazelcastConfig.xml.sample @@ -0,0 +1,192 @@ + + + + dev + dev-pass + + + 5701 + + + 224.2.2.3 + 54327 + + + 127.0.0.1 + + + + 192.168.56.1 + + + + PBEWithMD5AndDES + + thesalt + + thepass + + 19 + + + + RSA/NONE/PKCS1PADDING + + thekeypass + + local + + JKS + + thestorepass + + keystore + + + + 16 + 64 + 60 + + + + 0 + + 0 + + + + 1 + + NONE + + 0 + + 25 + + + hz.ADD_NEW_ENTRY + + + + + + 1 + + NONE + + 0 + + 25 + + + hz.ADD_NEW_ENTRY + + + + \ No newline at end of file diff --git a/config/alfresco/messages/patch-service_de.properties b/config/alfresco/messages/patch-service_de.properties index ca54d72ba8..b328dd8a15 100755 --- a/config/alfresco/messages/patch-service_de.properties +++ b/config/alfresco/messages/patch-service_de.properties @@ -161,6 +161,8 @@ patch.wcmFolders.webprojects.result.created=The Web Projects folder was successf patch.wcmFolders.webforms.result.exists=The Web Forms folder already exists: {0} patch.wcmFolders.webforms.result.created=The Web Forms folder was successfully created: {0} +patch.wcmDeployed.description=Adds the 'WCM Deployed' space to the company home folder. + patch.linkNodeExtension.description=Fixes link node file extensions to have a .url extension. patch.linkNodeExtension.result=Fixed {0} link node file extensions. See file {1} for details. patch.linkNodeExtension.err.unable_to_fix=Auto-fixing of link node file extensions failed. See file {0} for details. @@ -470,3 +472,8 @@ patch.migrateTenantsFromAttrsToTable.result=Processed {0} tenants patch.remoteCredentialsContainer.description=Patch to add the root folder for Shared Remote Credentials patch.syncSetDefinitionsContainer.description=Patch to add the root folder for SyncSet Definitions + +patch.swsdpPatch.description=Patch to fix up the Sample: Web Site Design Project. +patch.swsdpPatch.success=Successfully patched the Sample: Web Site Design Project. +patch.swsdpPatch.skipped=Skipped, not required. +patch.swsdpPatch.missingSurfConfig=surf-config folder is not present in Sample: Web Site Design Project. \ No newline at end of file diff --git a/config/alfresco/messages/patch-service_it.properties b/config/alfresco/messages/patch-service_it.properties index ca54d72ba8..b328dd8a15 100755 --- a/config/alfresco/messages/patch-service_it.properties +++ b/config/alfresco/messages/patch-service_it.properties @@ -161,6 +161,8 @@ patch.wcmFolders.webprojects.result.created=The Web Projects folder was successf patch.wcmFolders.webforms.result.exists=The Web Forms folder already exists: {0} patch.wcmFolders.webforms.result.created=The Web Forms folder was successfully created: {0} +patch.wcmDeployed.description=Adds the 'WCM Deployed' space to the company home folder. + patch.linkNodeExtension.description=Fixes link node file extensions to have a .url extension. patch.linkNodeExtension.result=Fixed {0} link node file extensions. See file {1} for details. patch.linkNodeExtension.err.unable_to_fix=Auto-fixing of link node file extensions failed. See file {0} for details. @@ -470,3 +472,8 @@ patch.migrateTenantsFromAttrsToTable.result=Processed {0} tenants patch.remoteCredentialsContainer.description=Patch to add the root folder for Shared Remote Credentials patch.syncSetDefinitionsContainer.description=Patch to add the root folder for SyncSet Definitions + +patch.swsdpPatch.description=Patch to fix up the Sample: Web Site Design Project. +patch.swsdpPatch.success=Successfully patched the Sample: Web Site Design Project. +patch.swsdpPatch.skipped=Skipped, not required. +patch.swsdpPatch.missingSurfConfig=surf-config folder is not present in Sample: Web Site Design Project. \ No newline at end of file diff --git a/config/alfresco/messages/patch-service_ja.properties b/config/alfresco/messages/patch-service_ja.properties index ca54d72ba8..b328dd8a15 100755 --- a/config/alfresco/messages/patch-service_ja.properties +++ b/config/alfresco/messages/patch-service_ja.properties @@ -161,6 +161,8 @@ patch.wcmFolders.webprojects.result.created=The Web Projects folder was successf patch.wcmFolders.webforms.result.exists=The Web Forms folder already exists: {0} patch.wcmFolders.webforms.result.created=The Web Forms folder was successfully created: {0} +patch.wcmDeployed.description=Adds the 'WCM Deployed' space to the company home folder. + patch.linkNodeExtension.description=Fixes link node file extensions to have a .url extension. patch.linkNodeExtension.result=Fixed {0} link node file extensions. See file {1} for details. patch.linkNodeExtension.err.unable_to_fix=Auto-fixing of link node file extensions failed. See file {0} for details. @@ -470,3 +472,8 @@ patch.migrateTenantsFromAttrsToTable.result=Processed {0} tenants patch.remoteCredentialsContainer.description=Patch to add the root folder for Shared Remote Credentials patch.syncSetDefinitionsContainer.description=Patch to add the root folder for SyncSet Definitions + +patch.swsdpPatch.description=Patch to fix up the Sample: Web Site Design Project. +patch.swsdpPatch.success=Successfully patched the Sample: Web Site Design Project. +patch.swsdpPatch.skipped=Skipped, not required. +patch.swsdpPatch.missingSurfConfig=surf-config folder is not present in Sample: Web Site Design Project. \ No newline at end of file diff --git a/config/alfresco/messages/patch-service_nl.properties b/config/alfresco/messages/patch-service_nl.properties index ca54d72ba8..b328dd8a15 100755 --- a/config/alfresco/messages/patch-service_nl.properties +++ b/config/alfresco/messages/patch-service_nl.properties @@ -161,6 +161,8 @@ patch.wcmFolders.webprojects.result.created=The Web Projects folder was successf patch.wcmFolders.webforms.result.exists=The Web Forms folder already exists: {0} patch.wcmFolders.webforms.result.created=The Web Forms folder was successfully created: {0} +patch.wcmDeployed.description=Adds the 'WCM Deployed' space to the company home folder. + patch.linkNodeExtension.description=Fixes link node file extensions to have a .url extension. patch.linkNodeExtension.result=Fixed {0} link node file extensions. See file {1} for details. patch.linkNodeExtension.err.unable_to_fix=Auto-fixing of link node file extensions failed. See file {0} for details. @@ -470,3 +472,8 @@ patch.migrateTenantsFromAttrsToTable.result=Processed {0} tenants patch.remoteCredentialsContainer.description=Patch to add the root folder for Shared Remote Credentials patch.syncSetDefinitionsContainer.description=Patch to add the root folder for SyncSet Definitions + +patch.swsdpPatch.description=Patch to fix up the Sample: Web Site Design Project. +patch.swsdpPatch.success=Successfully patched the Sample: Web Site Design Project. +patch.swsdpPatch.skipped=Skipped, not required. +patch.swsdpPatch.missingSurfConfig=surf-config folder is not present in Sample: Web Site Design Project. \ No newline at end of file diff --git a/config/alfresco/patch/patch-services-context.xml b/config/alfresco/patch/patch-services-context.xml index b55cc790f8..b79259f006 100644 --- a/config/alfresco/patch/patch-services-context.xml +++ b/config/alfresco/patch/patch-services-context.xml @@ -3066,6 +3066,9 @@ + + + diff --git a/config/alfresco/subsystems/OOoDirect/default/openoffice-transform-context.xml b/config/alfresco/subsystems/OOoDirect/default/openoffice-transform-context.xml index eff8c4eaf7..013f56e5f5 100644 --- a/config/alfresco/subsystems/OOoDirect/default/openoffice-transform-context.xml +++ b/config/alfresco/subsystems/OOoDirect/default/openoffice-transform-context.xml @@ -36,6 +36,13 @@ 2 + + + + ${ooo.exe} + + + diff --git a/source/java/org/alfresco/repo/admin/patch/impl/ImapUnsubscribedAspectPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/ImapUnsubscribedAspectPatch.java index fd18573e32..da098033eb 100644 --- a/source/java/org/alfresco/repo/admin/patch/impl/ImapUnsubscribedAspectPatch.java +++ b/source/java/org/alfresco/repo/admin/patch/impl/ImapUnsubscribedAspectPatch.java @@ -21,9 +21,7 @@ package org.alfresco.repo.admin.patch.impl; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; import org.alfresco.model.ContentModel; import org.alfresco.model.ImapModel; @@ -33,6 +31,7 @@ import org.alfresco.repo.batch.BatchProcessor; import org.alfresco.repo.batch.BatchProcessor.BatchProcessWorker; import org.alfresco.repo.domain.node.NodeDAO; import org.alfresco.repo.domain.node.NodeDAO.NodeRefQueryCallback; +import org.alfresco.repo.domain.patch.PatchDAO; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.security.PersonService; @@ -45,27 +44,21 @@ public class ImapUnsubscribedAspectPatch extends AbstractPatch { private static final String MSG_NONSUBSCRIBED_ASPECT_REMOVED = "patch.imapUnsubscribedAspect.result.removed"; private static final QName ASPECT_NON_SUBSCRIBED = QName.createQName("{http://www.alfresco.org/model/imap/1.0}nonSubscribed"); - private static final String PROP_MIN_ID = "minNodeId"; private NodeDAO nodeDAO; + private PatchDAO patchDAO; private PersonService personService; - private final Map properties = new HashMap(); + private final int batchThreads = 3; + private final int batchSize = 40; + private final long count = batchThreads * batchSize; + private long minSearchNodeId = 1; - private int batchThreads = 3; - private int batchSize = 40; - private long count = batchThreads * batchSize; - - @Override - public void init() - { - super.init(); - properties.put(PROP_MIN_ID, 1L); - } @Override protected String applyInternal() throws Exception { final List users = nodeService.getChildAssocs(personService.getPeopleContainer(), ContentModel.ASSOC_CHILDREN, RegexQNamePattern.MATCH_ALL); + final long maxNodeId = patchDAO.getMaxAdmNodeID(); BatchProcessWorkProvider workProvider = new BatchProcessWorkProvider() { @@ -79,17 +72,21 @@ public class ImapUnsubscribedAspectPatch extends AbstractPatch public Collection getNextWork() { result.clear(); - nodeDAO.getNodesWithAspects(Collections.singleton(ASPECT_NON_SUBSCRIBED), properties.get(PROP_MIN_ID), count, new NodeRefQueryCallback() + while (result.isEmpty() && minSearchNodeId < maxNodeId) { + nodeDAO.getNodesWithAspects(Collections.singleton(ASPECT_NON_SUBSCRIBED), minSearchNodeId, + minSearchNodeId + count, new NodeRefQueryCallback() + { - public boolean handle(Pair nodePair) - { - properties.put(PROP_MIN_ID, nodePair.getFirst()); - result.add(nodePair.getSecond()); - return true; - } + public boolean handle(Pair nodePair) + { + result.add(nodePair.getSecond()); + return true; + } - }); + }); + minSearchNodeId = minSearchNodeId + count + 1; + } return result; } @@ -137,6 +134,11 @@ public class ImapUnsubscribedAspectPatch extends AbstractPatch this.nodeDAO = nodeDAO; } + public void setPatchDAO(PatchDAO patchDAO) + { + this.patchDAO = patchDAO; + } + public void setPersonService(PersonService personService) { this.personService = personService; diff --git a/source/java/org/alfresco/repo/content/transform/TransformerDebug.java b/source/java/org/alfresco/repo/content/transform/TransformerDebug.java index 08cc2e7186..ea6db37f68 100644 --- a/source/java/org/alfresco/repo/content/transform/TransformerDebug.java +++ b/source/java/org/alfresco/repo/content/transform/TransformerDebug.java @@ -613,7 +613,7 @@ public class TransformerDebug } if (frame != null) { - sb.append(spaces(9-sb.length()+lengthOfFirstId)); // Try to pad to level 5 + sb.append(spaces(11-sb.length()+lengthOfFirstId)); // Try to pad to level 7 } return sb.toString(); } diff --git a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java index b7439acafc..7e1a2e9993 100644 --- a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java @@ -940,6 +940,20 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO return nodePair.getSecond().getNodeStatus(); } } + + public Status getNodeIdStatus(Long nodeId) + { + Pair nodePair = nodesCache.getByKey(nodeId); + // The nodesCache gets both live and deleted nodes. + if (nodePair == null) + { + return null; + } + else + { + return nodePair.getSecond().getNodeStatus(); + } + } public Pair getNodePair(NodeRef nodeRef) { diff --git a/source/java/org/alfresco/repo/domain/node/NodeDAO.java b/source/java/org/alfresco/repo/domain/node/NodeDAO.java index bd0043725d..5669e7ee99 100644 --- a/source/java/org/alfresco/repo/domain/node/NodeDAO.java +++ b/source/java/org/alfresco/repo/domain/node/NodeDAO.java @@ -146,6 +146,16 @@ public interface NodeDAO extends NodeBulkLoader */ public NodeRef.Status getNodeRefStatus(NodeRef nodeRef); + /** + * Get the current status of the node, including deleted nodes. + * + * @param nodeId the node id + * @return Returns the current status of the reference. + * This will only be null if the node never existed or has been + * purged following deletion. + */ + public NodeRef.Status getNodeIdStatus(Long nodeId); + public Pair getNodePair(NodeRef nodeRef); public Pair getNodePair(Long nodeId); diff --git a/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java b/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java index 17f6d20eac..55e35b338d 100644 --- a/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java +++ b/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java @@ -188,11 +188,10 @@ public class SolrQueryHTTPClient implements BeanFactoryAware } url.append("/").append(languageUrlFragment); - // duplicate the query in the URL - url.append("?q="); - - url.append(encoder.encode(searchParameters.getQuery(), "UTF-8")); - url.append("&wt=").append(encoder.encode("json", "UTF-8")); + // Send the query in JSON only + // url.append("?q="); + // url.append(encoder.encode(searchParameters.getQuery(), "UTF-8")); + url.append("?wt=").append(encoder.encode("json", "UTF-8")); url.append("&fl=").append(encoder.encode("DBID,score", "UTF-8")); if (searchParameters.getMaxItems() >= 0) diff --git a/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizer.java b/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizer.java index 1df3de8633..53461085f3 100644 --- a/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizer.java +++ b/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizer.java @@ -760,6 +760,17 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl // Create a prefixed zone ID for use with the authority service final String zoneId = AuthorityService.ZONE_AUTH_EXT_PREFIX + zone; + // Ensure that the zoneId exists before multiple threads start using it + this.transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + authorityService.getOrCreateZone(zoneId); + return null; + } + }, false, splitTxns); + // The set of zones we associate with new objects (default plus registry specific) final Set zoneSet = getZones(zoneId); @@ -1856,7 +1867,7 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl * the zone id * @return the zone set */ - private Set getZones(String zoneId) + private Set getZones(final String zoneId) { Set zones = new HashSet(5); zones.add(AuthorityService.ZONE_APP_DEFAULT); diff --git a/source/java/org/alfresco/repo/site/SiteServiceImpl.java b/source/java/org/alfresco/repo/site/SiteServiceImpl.java index f60725b401..c85ef42406 100644 --- a/source/java/org/alfresco/repo/site/SiteServiceImpl.java +++ b/source/java/org/alfresco/repo/site/SiteServiceImpl.java @@ -922,7 +922,8 @@ public class SiteServiceImpl extends AbstractLifecycleBean implements SiteServic // Only search for "st:site" nodes. final Set searchTypeQNames = new HashSet(1); searchTypeQNames.add(SiteModel.TYPE_SITE); -// searchTypeQNames.addAll(dictionaryService.getSubTypes(SiteModel.TYPE_SITE, true)); + // ... and all subtypes of st:site + searchTypeQNames.addAll(dictionaryService.getSubTypes(SiteModel.TYPE_SITE, true)); // get canned query final String cQBeanName = "siteGetChildrenCannedQueryFactory"; diff --git a/source/java/org/alfresco/repo/site/SiteServiceImplMoreTest.java b/source/java/org/alfresco/repo/site/SiteServiceImplMoreTest.java new file mode 100644 index 0000000000..4144e459ce --- /dev/null +++ b/source/java/org/alfresco/repo/site/SiteServiceImplMoreTest.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.repo.site; + +import static org.junit.Assert.assertNotNull; + +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.query.PagingRequest; +import org.alfresco.query.PagingResults; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.cmr.site.SiteVisibility; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.test.junitrules.ApplicationContextInit; +import org.alfresco.util.test.junitrules.RunAsFullyAuthenticatedRule; +import org.alfresco.util.test.junitrules.TemporarySites; +import org.alfresco.util.test.junitrules.TemporarySitesTest; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.RuleChain; +import org.springframework.extensions.webscripts.GUID; + +/** + * This class contains some tests for the {@link SiteServiceImpl} - in addition to those already + * included in {@link SiteServiceImplTest}. This uses JUnit 4 annotations and JUnit Rules. + * + * TODO Refactor the two classes together into one common approach. + * + * @author Neil Mc Erlean + * @since 4.0.3 + */ +public class SiteServiceImplMoreTest +{ + // Rule to initialise the default Alfresco spring configuration + public static ApplicationContextInit APP_CONTEXT_INIT = ApplicationContextInit.createStandardContextWithOverrides("classpath:sites/test-" + + TemporarySitesTest.class.getSimpleName() + "-context.xml"); + + // A rule to manage test nodes reused across all the test methods + public static TemporarySites STATIC_TEST_SITES = new TemporarySites(APP_CONTEXT_INIT); + + // Tie them together in a static Rule Chain + @ClassRule public static RuleChain ruleChain = RuleChain.outerRule(APP_CONTEXT_INIT) + .around(STATIC_TEST_SITES); + + @Rule public RunAsFullyAuthenticatedRule runAllTestsAsAdmin = new RunAsFullyAuthenticatedRule(AuthenticationUtil.getAdminUserName()); + + // Various services + private static NamespaceService NAMESPACE_SERVICE; + private static SiteService SITE_SERVICE; + private static RetryingTransactionHelper TRANSACTION_HELPER; + + private static String TEST_SITE_NAME, TEST_SUB_SITE_NAME; + + @BeforeClass public static void initStaticData() throws Exception + { + NAMESPACE_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("namespaceService", NamespaceService.class); + SITE_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("siteService", SiteService.class); + TRANSACTION_HELPER = APP_CONTEXT_INIT.getApplicationContext().getBean("retryingTransactionHelper", RetryingTransactionHelper.class); + + // We'll create this test content as admin. + final String admin = AuthenticationUtil.getAdminUserName(); + + TEST_SITE_NAME = GUID.generate(); + TEST_SUB_SITE_NAME = GUID.generate(); + + final QName subSiteType = QName.createQName("testsite", "testSubsite", NAMESPACE_SERVICE); + + STATIC_TEST_SITES.createSite("sitePreset", TEST_SITE_NAME, "siteTitle", "siteDescription", SiteVisibility.PUBLIC, admin); + STATIC_TEST_SITES.createSite("sitePreset", TEST_SUB_SITE_NAME, "siteTitle", "siteDescription", SiteVisibility.PUBLIC, subSiteType, admin); + } + + /** + * This method ensures that {@link SiteService#listSites(String)} includes content subtypes of {@link SiteModel#TYPE_SITE st:site}. + */ + @Test public void listSitesIncludingSubTypesOfSite() throws Exception + { + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + PagingResults sites = SITE_SERVICE.listSites(null, null, new PagingRequest(0, 1024)); + + Map sitesByName = new HashMap(); + for (SiteInfo site : sites.getPage()) + { + sitesByName.put(site.getShortName(), site); + } + + assertNotNull("st:site missing.", sitesByName.get(TEST_SITE_NAME)); + assertNotNull("subtype of st:site missing.", sitesByName.get(TEST_SUB_SITE_NAME)); + + return null; + } + }); + } +} diff --git a/source/java/org/alfresco/repo/solr/SOLRTrackingComponentImpl.java b/source/java/org/alfresco/repo/solr/SOLRTrackingComponentImpl.java index 370e9534c9..cef153790f 100644 --- a/source/java/org/alfresco/repo/solr/SOLRTrackingComponentImpl.java +++ b/source/java/org/alfresco/repo/solr/SOLRTrackingComponentImpl.java @@ -52,6 +52,7 @@ import org.alfresco.service.cmr.dictionary.TypeDefinition; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.InvalidNodeRefException; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeRef.Status; import org.alfresco.service.cmr.repository.Path; import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; import org.alfresco.service.cmr.security.OwnableService; @@ -381,9 +382,40 @@ public class SOLRTrackingComponentImpl implements SOLRTrackingComponent return false; } - private Collection> getCategoryPaths(NodeRef nodeRef, Set aspects, Map properties) + static class CategoryPaths + { + Collection> paths; + List categoryParents; + + CategoryPaths( Collection> paths, List categoryParents) + { + this.paths = paths; + this.categoryParents = categoryParents; + } + + /** + * @return the paths + */ + public Collection> getPaths() + { + return paths; + } + + /** + * @return the categoryParents + */ + public List getCategoryParents() + { + return categoryParents; + } + + + } + + private CategoryPaths getCategoryPaths(NodeRef nodeRef, Set aspects, Map properties) { ArrayList> categoryPaths = new ArrayList>(); + ArrayList categoryParents = new ArrayList(); nodeDAO.setCheckNodeConsistency(); for (QName classRef : aspects) @@ -439,13 +471,16 @@ public class SOLRTrackingComponentImpl implements SOLRTrackingComponent { Path.ChildAssocElement cae = (Path.ChildAssocElement) pair.getFirst().last(); ChildAssociationRef assocRef = cae.getRef(); - pair.getFirst().append(new Path.ChildAssocElement(new ChildAssociationRef(assocRef.getTypeQName(), assocRef.getChildRef(), QName.createQName("member"), nodeRef))); + ChildAssociationRef categoryParentRef = new ChildAssociationRef(assocRef.getTypeQName(), assocRef.getChildRef(), QName.createQName("member"), nodeRef); + pair.getFirst().append(new Path.ChildAssocElement(categoryParentRef)); + categoryParents.add(categoryParentRef); } } - return categoryPaths; + return new CategoryPaths(categoryPaths, categoryParents); } + private List preCacheNodes(NodeMetaDataParameters nodeMetaDataParameters) { nodeDAO.setCheckNodeConsistency(); @@ -536,26 +571,33 @@ public class SOLRTrackingComponentImpl implements SOLRTrackingComponent for(Long nodeId : nodeIds) { - Map props = null; - Set aspects = null; - - if (!nodeDAO.exists(nodeId)) - { - // Deleted nodes have no metadata - continue; - } - + Status status = nodeDAO.getNodeIdStatus(nodeId); + NodeRef nodeRef = status.getNodeRef(); + NodeMetaData nodeMetaData = new NodeMetaData(); nodeMetaData.setNodeId(nodeId); - - Pair pair = nodeDAO.getNodePair(nodeId); - nodeMetaData.setAclId(nodeDAO.getNodeAclId(nodeId)); - + + if(includeNodeRef) + { + nodeMetaData.setNodeRef(tenantService.getBaseName(nodeRef, true)); + } + if(includeTxnId) { - nodeMetaData.setTxnId(nodeDAO.getNodeRefStatus(pair.getSecond()).getDbTxnId()); + nodeMetaData.setTxnId(status.getDbTxnId()); } + if(status.isDeleted()) + { + rowHandler.processResult(nodeMetaData); + continue; + } + + Map props = null; + Set aspects = null; + + nodeMetaData.setAclId(nodeDAO.getNodeAclId(nodeId)); + if(includeType) { QName nodeType = nodeDAO.getNodeType(nodeId); @@ -572,7 +614,10 @@ public class SOLRTrackingComponentImpl implements SOLRTrackingComponent if(includeProperties) { - props = getProperties(nodeId); + if(props == null) + { + props = getProperties(nodeId); + } nodeMetaData.setProperties(props); } else @@ -580,7 +625,7 @@ public class SOLRTrackingComponentImpl implements SOLRTrackingComponent nodeMetaData.setProperties(Collections.emptyMap()); } - if(includeAspects) + if(includeAspects || includePaths || includeParentAssociations) { aspects = new HashSet(); Set sourceAspects = nodeDAO.getNodeAspects(nodeId); @@ -595,35 +640,38 @@ public class SOLRTrackingComponentImpl implements SOLRTrackingComponent } nodeMetaData.setAspects(aspects); + CategoryPaths categoryPaths = new CategoryPaths(new ArrayList>(), new ArrayList()); + if(includePaths || includeParentAssociations) + { + if(props == null) + { + props = getProperties(nodeId); + } + categoryPaths = getCategoryPaths(status.getNodeRef(), aspects, props); + } + if(includePaths) { if(props == null) { props = getProperties(nodeId); } - Collection> categoryPaths = getCategoryPaths(pair.getSecond(), aspects, props); - List directPaths = nodeDAO.getPaths(pair, false); - Collection> paths = new ArrayList>(directPaths.size() + categoryPaths.size()); + List directPaths = nodeDAO.getPaths(new Pair(nodeId, status.getNodeRef()), false); + + Collection> paths = new ArrayList>(directPaths.size() + categoryPaths.getPaths().size()); for (Path path : directPaths) { paths.add(new Pair(path.getBaseNamePath(tenantService), null)); } - for(Pair catPair : categoryPaths) + for(Pair catPair : categoryPaths.getPaths()) { paths.add(new Pair(catPair.getFirst().getBaseNamePath(tenantService), catPair.getSecond())); } nodeMetaData.setPaths(paths); } - - NodeRef nodeRef = pair.getSecond(); - - if(includeNodeRef) - { - nodeMetaData.setNodeRef(tenantService.getBaseName(nodeRef, true)); - } - + nodeMetaData.setTenantDomain(tenantService.getDomain(nodeRef.getStoreRef().getIdentifier())); if(includeChildAssociations) @@ -722,6 +770,10 @@ public class SOLRTrackingComponentImpl implements SOLRTrackingComponent { } }); + for(ChildAssociationRef ref : categoryPaths.getCategoryParents()) + { + parentAssocs.add(tenantService.getBaseName(ref, true)); + } CRC32 crc = new CRC32(); for(ChildAssociationRef car : parentAssocs) @@ -747,7 +799,7 @@ public class SOLRTrackingComponentImpl implements SOLRTrackingComponent if(includeOwner) { // cached in OwnableService - nodeMetaData.setOwner(ownableService.getOwner(pair.getSecond())); + nodeMetaData.setOwner(ownableService.getOwner(status.getNodeRef())); } rowHandler.processResult(nodeMetaData); diff --git a/source/java/org/alfresco/util/OpenOfficeCommandEnv.java b/source/java/org/alfresco/util/OpenOfficeCommandEnv.java new file mode 100644 index 0000000000..f8b7d758aa --- /dev/null +++ b/source/java/org/alfresco/util/OpenOfficeCommandEnv.java @@ -0,0 +1,62 @@ +/* Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.io.File; +import java.io.IOException; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.util.OpenOfficeURI; +import org.alfresco.util.exec.RuntimeExec; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * A map giving the environment openoffice or libreoffice commands require to start. + * + * @author Alan Davis + */ +public class OpenOfficeCommandEnv extends AbstractMap +{ + private static final Log logger = LogFactory.getLog(OpenOfficeCommandLine.class); + private static final String DYLD_LIBRARY_PATH = "DYLD_LIBRARY_PATH"; + + private Map map = new HashMap(System.getenv()); + private OpenOfficeVariant variant = new OpenOfficeVariant(); + + public OpenOfficeCommandEnv(String exe) throws IOException + { + if (variant.isMac()) + { + map.remove(DYLD_LIBRARY_PATH); + logger.debug("Removing $DYLD_LIBRARY_PATH from the environment so that LibreOffice/OpenOffice will start on Mac."); + } + } + + @Override + public Set> entrySet() + { + return map.entrySet(); + } +} diff --git a/source/java/org/alfresco/util/OpenOfficeCommandLine.java b/source/java/org/alfresco/util/OpenOfficeCommandLine.java index ae41d230b8..ea8f36cf17 100644 --- a/source/java/org/alfresco/util/OpenOfficeCommandLine.java +++ b/source/java/org/alfresco/util/OpenOfficeCommandLine.java @@ -26,7 +26,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.util.OpenOfficeURI; import org.alfresco.util.exec.RuntimeExec; import org.apache.commons.logging.Log; @@ -40,30 +39,30 @@ import org.apache.commons.logging.LogFactory; */ public class OpenOfficeCommandLine extends AbstractMap> { - private static final String[] EXTENSIONS = new String[] {"", ".exe", ".com", ".bat", ".cmd"}; - private static final Log logger = LogFactory.getLog(OpenOfficeCommandLine.class); - - private static final String OS_NAME = System.getProperty("os.name").toLowerCase(); - private Map> map = new HashMap>(); - - private boolean windows; + private OpenOfficeVariant variant = new OpenOfficeVariant(); public OpenOfficeCommandLine(String exe, String port, String user) throws IOException { - windows = isWindows(); - File executable = findExecutable(exe); - File officeHome = getOfficeHome(executable); + File executable = variant.findExecutable(exe); + File officeHome = variant.getOfficeHome(executable); List command = new ArrayList(); String acceptValue = "socket,host=127.0.0.1,port="+port+";urp;StarOffice.ServiceManager"; String userInstallation = new OpenOfficeURI(user).toString(); command.add(executable == null ? exe : executable.getAbsolutePath()); - if (isLibreOffice3Dot5(officeHome)) + if (variant.isLibreOffice3Dot5(officeHome)) { command.add("--accept=" + acceptValue); - command.add("-env:UserInstallation=" + userInstallation); + if (variant.isMac()) + { + command.add("--env:UserInstallation=" + userInstallation); + } + else + { + command.add("-env:UserInstallation=" + userInstallation); + } command.add("--headless"); command.add("--nocrashreport"); //command.add("--nodefault"); included by JOD @@ -71,7 +70,7 @@ public class OpenOfficeCommandLine extends AbstractMap> //command.add("--nolockcheck"); included by JOD command.add("--nologo"); command.add("--norestore"); - logger.info("Using GNU based LibreOffice command: "+command); + logger.info("Using GNU based LibreOffice command"+(variant.isMac() ? " on Mac" : "")+": "+command); } else { @@ -89,100 +88,6 @@ public class OpenOfficeCommandLine extends AbstractMap> map.put(RuntimeExec.KEY_OS_DEFAULT, command); } - private File getOfficeHome(File executable) - { - // Get the grandparent - File officeHome = executable; - for (int i=1; officeHome != null && i <= 2; i++) - { - officeHome = officeHome.getParentFile(); - } - - if (officeHome == null && executable != null) - { - throw new AlfrescoRuntimeException("Did not find OppenOffice home from executable "+executable.getAbsolutePath()); - } - - return officeHome; - } - - private File findExecutable(String executableName) - { - File file = new File(executableName); - if (file.isAbsolute()) - { - file = canExecute(file); - } - else - { - file = findExecutableOnPath(executableName); - } - return file; - } - - private File findExecutableOnPath(String executableName) - { - String systemPath = System.getenv("PATH"); - systemPath = systemPath == null ? System.getenv("path") : systemPath; - String[] pathDirs = systemPath.split(File.pathSeparator); - - File fullyQualifiedExecutable = null; - for (String pathDir : pathDirs) - { - File file = canExecute(new File(pathDir, executableName)); - if (file != null) - { - fullyQualifiedExecutable = file; - break; - } - } - return fullyQualifiedExecutable; - } - - private File canExecute(File file) - { - File fullyQualifiedExecutable = null; - File dir = file.getParentFile(); - String name = file.getName(); - for (String ext: EXTENSIONS) - { - file = new File(dir, name+ext); - if (file.canExecute()) - { - fullyQualifiedExecutable = file; - break; - } - if (!windows) - { - break; - } - } - return fullyQualifiedExecutable; - } - - private boolean isLibreOffice3Dot5(File officeHome) - { - return - officeHome != null && - !new File(officeHome, "basis-link").isFile() && - new File(officeHome, "ure-link").isFile(); - } - - private static boolean isLinux() - { - return OS_NAME.startsWith("linux"); - } - - private static boolean isMac() - { - return OS_NAME.startsWith("mac"); - } - - private static boolean isWindows() - { - return OS_NAME.startsWith("windows"); - } - @Override public Set>> entrySet() { diff --git a/source/java/org/alfresco/util/OpenOfficeVariant.java b/source/java/org/alfresco/util/OpenOfficeVariant.java new file mode 100644 index 0000000000..d645cc72f6 --- /dev/null +++ b/source/java/org/alfresco/util/OpenOfficeVariant.java @@ -0,0 +1,139 @@ +/* Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.io.File; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Provides OpenOffice and LibreOffice variant information. + * + * @author Alan Davis + */ +public class OpenOfficeVariant +{ + private static final String[] EXTENSIONS = new String[] {"", ".exe", ".com", ".bat", ".cmd"}; + private static final Log logger = LogFactory.getLog(OpenOfficeCommandLine.class); + private static final String OS_NAME = System.getProperty("os.name").toLowerCase(); + + private final boolean windows = isWindows(); + + public File getOfficeHome(File executable) + { + // Get the grandparent + File officeHome = executable; + for (int i=1; officeHome != null && i <= 2; i++) + { + officeHome = officeHome.getParentFile(); + } + + if (officeHome == null && executable != null) + { + throw new AlfrescoRuntimeException("Did not find OppenOffice home from executable "+executable.getAbsolutePath()); + } + + return officeHome; + } + + public File findExecutable(String executableName) + { + File file = new File(executableName); + if (file.isAbsolute()) + { + file = canExecute(file); + } + else + { + file = findExecutableOnPath(executableName); + } + return file; + } + + private File findExecutableOnPath(String executableName) + { + String systemPath = System.getenv("PATH"); + systemPath = systemPath == null ? System.getenv("path") : systemPath; + String[] pathDirs = systemPath.split(File.pathSeparator); + + File fullyQualifiedExecutable = null; + for (String pathDir : pathDirs) + { + File file = canExecute(new File(pathDir, executableName)); + if (file != null) + { + fullyQualifiedExecutable = file; + break; + } + } + return fullyQualifiedExecutable; + } + + private File canExecute(File file) + { + File fullyQualifiedExecutable = null; + File dir = file.getParentFile(); + String name = file.getName(); + for (String ext: EXTENSIONS) + { + file = new File(dir, name+ext); + if (file.canExecute()) + { + fullyQualifiedExecutable = file; + break; + } + if (!windows) + { + break; + } + } + return fullyQualifiedExecutable; + } + + public boolean isLibreOffice3Dot5(File officeHome) + { + logger.debug("System.getProperty(\"os.name\")="+System.getProperty("os.name")); + logger.debug("officeHome="+(officeHome == null ? null : "'"+officeHome.getAbsolutePath()+"'")); + logger.debug("basis-link:"+new File(officeHome, "basis-link").isFile()); + logger.debug(" ure-link:"+new File(officeHome, "ure-link").isFile()); + logger.debug("basis-link:"+new File(officeHome, "basis-link").isDirectory()); + logger.debug(" ure-link:"+new File(officeHome, "ure-link").isDirectory()); + + return + officeHome != null && + !new File(officeHome, "basis-link").isFile() && + (new File(officeHome, "ure-link").isFile() || new File(officeHome, "ure-link").isDirectory()); + } + + public boolean isLinux() + { + return OS_NAME.startsWith("linux"); + } + + public boolean isMac() + { + return OS_NAME.startsWith("mac"); + } + + public boolean isWindows() + { + return OS_NAME.startsWith("windows"); + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/util/test/junitrules/TemporarySites.java b/source/java/org/alfresco/util/test/junitrules/TemporarySites.java new file mode 100644 index 0000000000..2b4a66c9c2 --- /dev/null +++ b/source/java/org/alfresco/util/test/junitrules/TemporarySites.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2005-2012 + Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.test.junitrules; + +import java.util.ArrayList; +import java.util.List; + +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.site.SiteModel; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.cmr.site.SiteVisibility; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.rules.ExternalResource; + +/** + * A JUnit rule designed to help with the automatic cleanup of temporary st:site nodes. + * + * @author Neil Mc Erlean + * @since 4.0.3 + */ +public class TemporarySites extends ExternalResource +{ + private static final Log log = LogFactory.getLog(TemporarySites.class); + + private final ApplicationContextInit appContextRule; + private List temporarySites = new ArrayList(); + + /** + * Constructs the rule with a reference to a {@link ApplicationContextInit rule} which can be used to retrieve the ApplicationContext. + * + * @param appContextRule a rule which can be used to retrieve the spring app context. + */ + public TemporarySites(ApplicationContextInit appContextRule) + { + this.appContextRule = appContextRule; + } + + + @Override protected void before() throws Throwable + { + // Intentionally empty + } + + @Override protected void after() + { + final RetryingTransactionHelper transactionHelper = (RetryingTransactionHelper) appContextRule.getApplicationContext().getBean("retryingTransactionHelper"); + final SiteService siteService = appContextRule.getApplicationContext().getBean("siteService", SiteService.class); + + // Run as admin to ensure all sites can be deleted irrespective of which user created them. + AuthenticationUtil.runAs(new RunAsWork() + { + @Override public Void doWork() throws Exception + { + transactionHelper.doInTransaction(new RetryingTransactionCallback() + { + @Override public Void execute() throws Throwable + { + for (SiteInfo site : temporarySites) + { + final String shortName = site.getShortName(); + if (siteService.getSite(shortName) != null) + { + log.debug("Deleting temporary site " + shortName); + siteService.deleteSite(shortName); + } + } + + return null; + } + }); + return null; + } + }, AuthenticationUtil.getAdminUserName()); + } + + /** + * Add a specified site to the list of SiteInfos to be deleted by this rule. + * + * @param temporarySite a SiteInfo + */ + public void addSite(SiteInfo temporarySite) + { + this.temporarySites.add(temporarySite); + } + + /** + * This method creates a Share Site and adds it to the internal list of NodeRefs to be tidied up by the rule. + * This method will be run in its own transaction and will be run with the specified user as the fully authenticated user, + * thus ensuring the named user is the creator of the new site. + * + * @param sitePreset the site preset + * @param siteShortName the short name of the new site + * @param siteTitle the title of the new site + * @param siteDescription the description of the new site + * @param visibility the visibility + * @param siteCreator the username of the person who will create the site + * @return the newly created SiteInfo (will be of type st:site). + */ + public SiteInfo createSite(final String sitePreset, final String siteShortName, final String siteTitle, final String siteDescription, + final SiteVisibility visibility, final String siteCreator) + { + return this.createSite(sitePreset, siteShortName, siteTitle, siteDescription, visibility, SiteModel.TYPE_SITE, siteCreator); + } + + /** + * This method creates a Share Site (or subtype) and adds it to the internal list of NodeRefs to be tidied up by the rule. + * This method will be run in its own transaction and will be run with the specified user as the fully authenticated user, + * thus ensuring the named user is the creator of the new site. + * + * @param sitePreset the site preset + * @param siteShortName the short name of the new site + * @param siteTitle the title of the new site + * @param siteDescription the description of the new site + * @param visibility the visibility + * @param node type the node type of the site (must be st:site or subtype) + * @param siteCreator the username of the person who will create the site + * @return the newly created SiteInfo. + */ + public SiteInfo createSite(final String sitePreset, final String siteShortName, final String siteTitle, final String siteDescription, + final SiteVisibility visibility, final QName siteType, final String siteCreator) + { + final RetryingTransactionHelper transactionHelper = appContextRule.getApplicationContext().getBean("retryingTransactionHelper", RetryingTransactionHelper.class); + + AuthenticationUtil.pushAuthentication(); + AuthenticationUtil.setFullyAuthenticatedUser(siteCreator); + + SiteInfo newSite = transactionHelper.doInTransaction(new RetryingTransactionCallback() + { + public SiteInfo execute() throws Throwable + { + final SiteService siteService = appContextRule.getApplicationContext().getBean("siteService", SiteService.class); + + return siteService.createSite(sitePreset, siteShortName, siteTitle, siteDescription, visibility, siteType); + } + }); + + AuthenticationUtil.popAuthentication(); + + this.temporarySites.add(newSite); + return newSite; + } +} diff --git a/source/java/org/alfresco/util/test/junitrules/TemporarySitesTest.java b/source/java/org/alfresco/util/test/junitrules/TemporarySitesTest.java new file mode 100644 index 0000000000..46cfb5fe89 --- /dev/null +++ b/source/java/org/alfresco/util/test/junitrules/TemporarySitesTest.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.util.test.junitrules; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.cmr.site.SiteVisibility; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.RuleChain; + +/** + * Test class for {@link TemporarySites}. + * + * @author Neil McErlean + * @since 4.0.3 + */ +public class TemporarySitesTest +{ + // Rule to initialise the default Alfresco spring configuration + public static ApplicationContextInit APP_CONTEXT_INIT = + ApplicationContextInit.createStandardContextWithOverrides("classpath:sites/test-" + + TemporarySitesTest.class.getSimpleName() + "-context.xml"); + + // A rule to manage test sites reused across all the test methods + public static TemporaryNodes STATIC_TEST_SITES = new TemporaryNodes(APP_CONTEXT_INIT); + + // Tie them together in a static Rule Chain + @ClassRule public static RuleChain ruleChain = RuleChain.outerRule(APP_CONTEXT_INIT) + .around(STATIC_TEST_SITES); + + // A rule to manage test sites use in each test method + @Rule public TemporarySites testSites = new TemporarySites(APP_CONTEXT_INIT); + + // A rule to allow individual test methods all to be run as "admin". + @Rule public RunAsFullyAuthenticatedRule runAsRule = new RunAsFullyAuthenticatedRule(AuthenticationUtil.getAdminUserName()); + + // Various services + private static NamespaceService NAMESPACE_SERVICE; + private static SiteService SITE_SERVICE; + private static RetryingTransactionHelper TRANSACTION_HELPER; + + // These SiteInfos are used by the test methods. + private SiteInfo testSite1, testSite2; + + @BeforeClass public static void initStaticData() throws Exception + { + NAMESPACE_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("namespaceService", NamespaceService.class); + SITE_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("siteService", SiteService.class); + TRANSACTION_HELPER = APP_CONTEXT_INIT.getApplicationContext().getBean("retryingTransactionHelper", RetryingTransactionHelper.class); + } + + @Before public void createTestContent() + { + // Create some test content + testSite1 = testSites.createSite("sitePreset", "testSite1", "t", "d", SiteVisibility.PUBLIC, AuthenticationUtil.getAdminUserName()); + final QName subSiteType = QName.createQName("testsite", "testSubsite", NAMESPACE_SERVICE); + testSite2 = testSites.createSite("sitePreset", "testSite2", "T", "D", SiteVisibility.PUBLIC, subSiteType, AuthenticationUtil.getAdminUserName()); + } + + @Test public void ensureTestSitesWereCreatedOk() throws Exception + { + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + final SiteInfo recoveredSite1 = SITE_SERVICE.getSite(testSite1.getShortName()); + final SiteInfo recoveredSite2 = SITE_SERVICE.getSite(testSite2.getShortName()); + + assertNotNull("Test site does not exist", recoveredSite1); + assertNotNull("Test site does not exist", recoveredSite2); + + assertEquals("cm:title was wrong", "t", recoveredSite1.getTitle()); + assertEquals("cm:description was wrong", "d", recoveredSite1.getDescription()); + assertEquals("preset was wrong", "sitePreset", recoveredSite1.getSitePreset()); + + assertEquals("site visibility was wrong", SiteVisibility.PUBLIC, recoveredSite1.getVisibility()); + + return null; + } + }); + } +} diff --git a/source/test-resources/sites/test-TemporarySitesTest-context.xml b/source/test-resources/sites/test-TemporarySitesTest-context.xml new file mode 100644 index 0000000000..6bebc9cd1b --- /dev/null +++ b/source/test-resources/sites/test-TemporarySitesTest-context.xml @@ -0,0 +1,14 @@ + + + + + + + + + sites/testSiteModel.xml + + + + + diff --git a/source/test-resources/sites/testSiteModel.xml b/source/test-resources/sites/testSiteModel.xml new file mode 100644 index 0000000000..ea7f2a0db8 --- /dev/null +++ b/source/test-resources/sites/testSiteModel.xml @@ -0,0 +1,26 @@ + + + + + Test Site Model + Alfresco + 1.0 + + + + + + + + + + + + + + + Test Sub-site + st:site + + + \ No newline at end of file