From d2bce74f0b24835d10fe2d4dff7702f6c46ba061 Mon Sep 17 00:00:00 2001 From: Derek Hulley Date: Thu, 21 Sep 2006 23:35:51 +0000 Subject: [PATCH] Merge V1.4 to HEAD - Ignored Enterprise-specific changes svn merge svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@3701 svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@3703 . svn merge svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@3704 svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@3705 . svn merge svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@3707 svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@3876 . svn revert root\projects\web-client\source\web\jsp\admin\admin-console.jsp git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@3879 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/auditConfig.xml | 35 +- config/alfresco/bootstrap-context.xml | 29 + config/alfresco/bootstrap/system.xml | 2 +- config/alfresco/core-services-context.xml | 4 +- .../ldap-authentication-context.xml.sample | 30 +- .../alfresco/messages/bpm-messages.properties | 18 +- .../mimetype/openoffice-document-formats.xml | 5 +- config/alfresco/model/bpmModel.xml | 538 ++++++++++-------- config/alfresco/model/contentModel.xml | 2 +- .../model/dataTypeAnalyzers.properties | 18 +- config/alfresco/model/dictionaryModel.xml | 20 +- .../alfresco/model/permissionDefinitions.xml | 194 +++++-- config/alfresco/model/workflowModel.xml | 160 ------ config/alfresco/node-services-context.xml | 2 +- config/alfresco/version.properties | 2 +- config/alfresco/workflow-context.xml | 44 +- .../workflow/adhoc_processdefinition.xml | 65 +++ .../workflow/review_processdefinition.xml | 56 ++ .../workflow-messages.properties | 14 - config/alfresco/workflow/workflowModel.xml | 65 +++ .../auth/EnterpriseCifsAuthenticator.java | 24 +- .../server/config/ServerConfiguration.java | 2 +- .../smb/server/repo/ContentDiskDriver.java | 189 +++++- .../repo/desk/CheckInOutDesktopAction.java | 9 +- .../repo/pseudo/ContentPseudoFileImpl.java | 21 + .../action/executer/ScriptActionExecuter.java | 4 + .../executer/TransformActionExecuter.java | 13 +- .../repo/audit/AuditComponentImpl.java | 25 +- .../alfresco/repo/audit/MethodAuditModel.java | 8 + .../repo/audit/hibernate/Audit.hbm.xml | 20 +- .../repo/audit/model/AbstractAuditEntry.java | 43 ++ .../alfresco/repo/audit/model/AuditEntry.java | 19 + .../repo/audit/model/MethodAuditEntry.java | 9 + .../repo/audit/model/ServiceAuditEntry.java | 18 + .../InternalEhCacheManagerFactoryBean.java | 2 +- .../repo/content/AbstractContentStore.java | 2 +- .../alfresco/repo/content/MimetypeMap.java | 1 + .../content/cleanup/ContentStoreCleaner.java | 42 +- .../AbstractContentTransformerTest.java | 74 ++- .../OpenOfficeContentTransformerTest.java | 27 + ...AbstractImageMagickContentTransformer.java | 4 + .../repo/dictionary/DictionaryComponent.java | 2 +- .../org/alfresco/repo/dictionary/M2Model.java | 2 +- .../dictionary/dictionarydaotest_model.xml | 2 +- .../alfresco/repo/domain/PropertyValue.java | 45 +- .../repo/domain/hibernate/Node.hbm.xml | 14 +- .../repo/domain/schema/SchemaBootstrap.java | 6 +- .../java/org/alfresco/repo/jscript/Node.java | 47 +- .../filefolder/FileFolderServiceImpl.java | 6 +- .../filefolder/FileFolderServiceImplTest.java | 4 + .../repo/node/BaseNodeServiceTest.java | 4 + .../repo/node/PerformanceNodeServiceTest.java | 2 + .../node/archive/ArchiveAndRestoreTest.java | 5 +- .../repo/node/db/DbNodeServiceImpl.java | 4 - .../repo/node/db/DbNodeServiceImplTest.java | 39 +- .../alfresco/repo/node/db/NodeDaoService.java | 15 +- .../HibernateNodeDaoServiceImpl.java | 106 ++-- .../repo/node/integrity/IntegrityChecker.java | 35 +- .../repo/node/integrity/IntegrityTest.java | 12 + .../repo/ownable/impl/OwnableServiceTest.java | 8 + .../policy/TransactionBehaviourQueue.java | 3 +- .../org/alfresco/repo/search/IndexerSPI.java | 2 +- .../search/impl/lucene/LuceneAnalyser.java | 6 +- .../search/impl/lucene/LuceneIndexerImpl.java | 16 +- .../impl/lucene/LuceneIndexerImpl2.java | 13 +- .../search/impl/lucene/LuceneQueryParser.java | 41 +- .../repo/search/impl/lucene/LuceneTest2.java | 39 +- .../analysis/AlfrescoStandardAnalyser.java | 63 ++ .../analysis/AlfrescoStandardFilter.java | 138 +++++ .../lucene/fts/FullTextSearchIndexerImpl.java | 65 ++- .../search/impl/lucene/index/IndexInfo.java | 10 +- .../impl/lucene/index/IndexInfoTest.java | 14 +- .../DefaultMutableAuthenticationDao.java | 315 ++++++++-- .../ldap/LDAPGroupExportSource.java | 87 ++- .../LDAPInitialDirContextFactoryImpl.java | 124 +++- .../ldap/LDAPPersonExportSource.java | 15 + .../impl/PermissionServiceImpl.java | 2 +- .../impl/PermissionServiceTest.java | 32 +- .../impl/model/PermissionModel.java | 80 ++- .../impl/model/PermissionModelTest.java | 41 +- .../service/ServiceDescriptorRegistry.java | 11 + .../TransactionAwareSingleton.java | 143 +++++ .../TransactionAwareSingletonTest.java | 224 ++++++++ .../workflow/StartWorkflowActionExecuter.java | 40 +- .../StartWorkflowActionExecuterTest.java | 9 +- .../repo/workflow/WorkflowComponent.java | 8 + .../repo/workflow/WorkflowDeployer.java | 46 ++ .../alfresco/repo/workflow/WorkflowModel.java | 23 +- .../workflow/WorkflowPackageComponent.java | 12 +- .../repo/workflow/WorkflowPackageImpl.java | 66 ++- .../repo/workflow/WorkflowServiceImpl.java | 31 + .../workflow/WorkflowServiceImplTest.java | 51 +- .../workflow/jbpm/AlfrescoJavaScript.java | 16 +- .../repo/workflow/jbpm/JBPMEngine.java | 199 +++++-- .../repo/workflow/jbpm/JBPMEngineTest.java | 28 +- .../jbpm/JBPMTransactionTemplate.java | 225 ++++++++ .../workflow/jbpm/NodeListConverterTest.java | 2 +- .../workflow/jbpm/ReviewAndApproveTest.java | 10 +- .../jbpm/adhoc_task_processdefinition.xml | 70 --- .../review_and_approve_processdefinition.xml | 63 -- .../repo/workflow/jbpm/test_script.xml | 108 ++-- .../org/alfresco/service/ServiceRegistry.java | 9 + .../service/cmr/workflow/WorkflowService.java | 24 + 103 files changed, 3569 insertions(+), 1172 deletions(-) delete mode 100644 config/alfresco/model/workflowModel.xml create mode 100644 config/alfresco/workflow/adhoc_processdefinition.xml create mode 100644 config/alfresco/workflow/review_processdefinition.xml rename config/alfresco/{messages => workflow}/workflow-messages.properties (71%) create mode 100644 config/alfresco/workflow/workflowModel.xml create mode 100644 source/java/org/alfresco/repo/search/impl/lucene/analysis/AlfrescoStandardAnalyser.java create mode 100644 source/java/org/alfresco/repo/search/impl/lucene/analysis/AlfrescoStandardFilter.java create mode 100644 source/java/org/alfresco/repo/transaction/TransactionAwareSingleton.java create mode 100644 source/java/org/alfresco/repo/transaction/TransactionAwareSingletonTest.java create mode 100644 source/java/org/alfresco/repo/workflow/jbpm/JBPMTransactionTemplate.java delete mode 100644 source/java/org/alfresco/repo/workflow/jbpm/adhoc_task_processdefinition.xml delete mode 100644 source/java/org/alfresco/repo/workflow/jbpm/review_and_approve_processdefinition.xml diff --git a/config/alfresco/auditConfig.xml b/config/alfresco/auditConfig.xml index f2e43fbb38..e68bef07ee 100644 --- a/config/alfresco/auditConfig.xml +++ b/config/alfresco/auditConfig.xml @@ -38,7 +38,9 @@ - + + + @@ -46,14 +48,18 @@ - + + + + + - + @@ -65,7 +71,7 @@ - + @@ -90,11 +96,13 @@ - + - + + + - + @@ -178,4 +186,17 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/config/alfresco/bootstrap-context.xml b/config/alfresco/bootstrap-context.xml index d594977215..2d6444b057 100644 --- a/config/alfresco/bootstrap-context.xml +++ b/config/alfresco/bootstrap-context.xml @@ -153,6 +153,35 @@ + + + + + jbpm + alfresco/workflow/review_processdefinition.xml + text/xml + true + + + jbpm + alfresco/workflow/adhoc_processdefinition.xml + text/xml + true + + + + + + alfresco/workflow/workflowModel.xml + + + + + alfresco/workflow/workflow-messages + + + + diff --git a/config/alfresco/bootstrap/system.xml b/config/alfresco/bootstrap/system.xml index a868e910c7..501fc4324d 100644 --- a/config/alfresco/bootstrap/system.xml +++ b/config/alfresco/bootstrap/system.xml @@ -42,7 +42,7 @@ - + diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml index e444cf8ef4..46f76c6af6 100644 --- a/config/alfresco/core-services-context.xml +++ b/config/alfresco/core-services-context.xml @@ -404,13 +404,14 @@ - + alfresco/model/dictionaryModel.xml alfresco/model/systemModel.xml alfresco/model/contentModel.xml + alfresco/model/bpmModel.xml alfresco/model/applicationModel.xml alfresco/model/forumModel.xml alfresco/model/recordsModel.xml @@ -428,6 +429,7 @@ alfresco/messages/system-model alfresco/messages/dictionary-model alfresco/messages/content-model + alfresco/messages/bpm-messages alfresco/messages/application-model alfresco/messages/forum-model diff --git a/config/alfresco/extension/ldap-authentication-context.xml.sample b/config/alfresco/extension/ldap-authentication-context.xml.sample index 34dbe20833..d0259eb5d7 100644 --- a/config/alfresco/extension/ldap-authentication-context.xml.sample +++ b/config/alfresco/extension/ldap-authentication-context.xml.sample @@ -3,24 +3,14 @@ - + - - - org.alfresco.repo.security.authentication.MutableAuthenticationDao + + + true - - - - - - - - - ${server.transaction.mode.default} - - - + + @@ -317,9 +307,9 @@ - + - 30000 + 300000 @@ -347,9 +337,9 @@ - + - 30000 + 300000 diff --git a/config/alfresco/messages/bpm-messages.properties b/config/alfresco/messages/bpm-messages.properties index 8a8886cb2d..48803513dd 100644 --- a/config/alfresco/messages/bpm-messages.properties +++ b/config/alfresco/messages/bpm-messages.properties @@ -4,8 +4,8 @@ bpm_businessprocessmodel.title=Business Process Model bpm_businessprocessmodel.description=Base definitions of all Business Processes # Default transition -bpm_businessprocessmodel.transition.title=Done -bpm_businessprocessmodel.transition.description=Done +bpm_businessprocessmodel.transition.title=Task Done +bpm_businessprocessmodel.transition.description=Task Done # Base Task bpm_businessprocessmodel.type.bpm_task.title=Task @@ -36,6 +36,8 @@ bpm_businessprocessmodel.property.bpm_workflowInstanceId.title=Workflow Instance bpm_businessprocessmodel.property.bpm_workflowInstanceId.description=Workflow Instance Id bpm_businessprocessmodel.property.bpm_context.title=Task Context bpm_businessprocessmodel.property.bpm_context.description=The context within which this task has been assigned +bpm_businessprocessmodel.property.bpm_description.title=Task Description +bpm_businessprocessmodel.property.bpm_description.description=Description of what needs to be achieved bpm_businessprocessmodel.property.bpm_outcome.title=Task Outcome bpm_businessprocessmodel.property.bpm_outcome.description=Decision made on completing Task bpm_businessprocessmodel.property.bpm_completedItems.title=Completed Items @@ -48,3 +50,15 @@ bpm_businessprocessmodel.association.bpm_package.title=Content Package bpm_businessprocessmodel.association.bpm_package.description=The collection of content routed through the workflow bpm_businessprocessmodel.aspect.bpm_workflowPackage.title=Workflow Package bpm_businessprocessmodel.aspect.bpm_workflowPackage.description=The collection of content routed through the workflow + +# Workflow Start Task +bpm_businessprocessmodel.type.bpm_startTask.title=Workflow Start Task +bpm_businessprocessmodel.type.bpm_startTask.description=Task used to collect information required for initiating Workflow +bpm_businessprocessmodel.property.bpm_workflowDescription.title=Description +bpm_businessprocessmodel.property.bpm_workflowDescription.description=Description +bpm_businessprocessmodel.property.bpm_workflowDueDate.title=Workflow Due Date +bpm_businessprocessmodel.property.bpm_workflowDueDate.description=Workflow Due Date +bpm_businessprocessmodel.property.bpm_workflowPriority.title=Workflow Priority +bpm_businessprocessmodel.property.bpm_workflowPriority.description=Workflow Priority +bpm_businessprocessmodel.association.bpm_assignee.title=Workflow Assignee +bpm_businessprocessmodel.association.bpm_assignee.description=Workflow Assignee diff --git a/config/alfresco/mimetype/openoffice-document-formats.xml b/config/alfresco/mimetype/openoffice-document-formats.xml index 1c18b17845..9579d1ba0d 100644 --- a/config/alfresco/mimetype/openoffice-document-formats.xml +++ b/config/alfresco/mimetype/openoffice-document-formats.xml @@ -10,6 +10,7 @@ Presentationimpress_pdf_Export Spreadsheetcalc_pdf_Export Textwriter_pdf_Export + Htmlwriter_web_pdf_Export @@ -26,7 +27,8 @@ - 1. additional files may be generated for images and this would require extra care in a servlet environment - 2. output quality does not seem to be very good in many cases --> - HTML + Html + Html text/html html @@ -44,6 +46,7 @@ odt Textwriter8 + Htmlwriterweb8_writer diff --git a/config/alfresco/model/bpmModel.xml b/config/alfresco/model/bpmModel.xml index 45cd982c46..e4bd07cdb5 100644 --- a/config/alfresco/model/bpmModel.xml +++ b/config/alfresco/model/bpmModel.xml @@ -2,269 +2,321 @@ - Business Process Model - Alfresco - 1.0 + Business Process Model + Alfresco + 1.0 - - - - - - - + + + + + + + - - - - - - - - - - - 1 - 2 - 3 - - - - - - - - - Not Yet Started - In Progress - On Hold - Cancelled - Completed - - - - - - 0 - 100 - - - - - - - - - - - - Task - Task - cm:content - - - - - - - - - - - - - Task Identifier - d:long - true - true - - - - - - - Start Date - d:date - true - - - End Date - d:date - true - - - Due Date - d:date - + + + - - - - - Status - d:text - true - Not Yet Started - - - - - - Priority - d:int - true - 2 - - - - - - Percentage Complete - d:int - true - 0 - - - - + - - - - + + + + + 1 + 2 + 3 + + + - + + + + + Not Yet Started + In Progress + On Hold + Cancelled + Completed + + + - + + + 0 + + + 100 + + - - - Pooled Users - - false - false - - - cm:person - false - true - - + - + - - cm:ownable - - + + + + + + cm:content + + + + + + + + + + + + + d:long + true + true + + + + + d:text + + + + + + + d:date + true + + + d:date + true + + + d:date + + + + + + + d:text + true + Not Yet Started + + + + + + d:int + true + 2 + + + + + + d:int + true + 0 + + + + + + + + + + + + + + + + + + false + false + + + cm:person + false + true + + + + + + + cm:ownable + + - - - - - - Workflow Task - Task assigned via Workflow - bpm:task - - + + + - - - - Task Context - d:noderef - - - - - Task Outcome - d:text - + + bpm:task - - - d:noderef - true - + - - - d:text - + + + + d:noderef + - - - d:text - workflow_item_read_actions - - + + + d:text + - - - - Workflow Package - - false - false - - - bpm:workflowPackage - true - false - - - - - - + + + d:noderef + true + + + + + d:text + + + + + + d:text + workflow_item_read_actions + + + + + + + + + false + false + + + bpm:workflowPackage + true + false + + + + + + + + + + + + + + bpm:workflowTask + + + + + + d:text + + + + + d:date + + + + + d:int + 2 + + + + + + + + + + + + false + false + + + cm:person + true + false + + + + + + + + + workflow_collection_actions + + + + workflow_item_collection_actions + + + + + - + - - - - - - - - - - Workflow Package - + + + + + + - - - - - Workflow Definition Id - d:text - - - Workflow Definition Name - d:text - - - Workflow Instance Id - d:text - - - - - - - - - - - - - - + + + + + + + + d:text + + + d:text + + + d:text + + + + + + + + + + + + + + + + - - \ No newline at end of file diff --git a/config/alfresco/model/contentModel.xml b/config/alfresco/model/contentModel.xml index da5560951d..84bd9bbaef 100644 --- a/config/alfresco/model/contentModel.xml +++ b/config/alfresco/model/contentModel.xml @@ -49,7 +49,7 @@ false - false + true sys:base diff --git a/config/alfresco/model/dataTypeAnalyzers.properties b/config/alfresco/model/dataTypeAnalyzers.properties index 75b6672692..7529f289d2 100644 --- a/config/alfresco/model/dataTypeAnalyzers.properties +++ b/config/alfresco/model/dataTypeAnalyzers.properties @@ -1,17 +1,17 @@ # Data Type Index Analyzers -d_dictionary.datatype.d_any.analyzer=org.apache.lucene.analysis.standard.StandardAnalyzer -d_dictionary.datatype.d_text.analyzer=org.apache.lucene.analysis.standard.StandardAnalyzer -d_dictionary.datatype.d_content.analyzer=org.apache.lucene.analysis.standard.StandardAnalyzer +d_dictionary.datatype.d_any.analyzer=org.alfresco.repo.search.impl.lucene.analysis.AlfrescoStandardAnalyser +d_dictionary.datatype.d_text.analyzer=org.alfresco.repo.search.impl.lucene.analysis.AlfrescoStandardAnalyser +d_dictionary.datatype.d_content.analyzer=org.alfresco.repo.search.impl.lucene.analysis.AlfrescoStandardAnalyser d_dictionary.datatype.d_int.analyzer=org.alfresco.repo.search.impl.lucene.analysis.IntegerAnalyser d_dictionary.datatype.d_long.analyzer=org.alfresco.repo.search.impl.lucene.analysis.LongAnalyser d_dictionary.datatype.d_float.analyzer=org.alfresco.repo.search.impl.lucene.analysis.FloatAnalyser d_dictionary.datatype.d_double.analyzer=org.alfresco.repo.search.impl.lucene.analysis.DoubleAnalyser d_dictionary.datatype.d_date.analyzer=org.alfresco.repo.search.impl.lucene.analysis.DateAnalyser d_dictionary.datatype.d_datetime.analyzer=org.alfresco.repo.search.impl.lucene.analysis.DateAnalyser -d_dictionary.datatype.d_boolean.analyzer=org.apache.lucene.analysis.standard.StandardAnalyzer -d_dictionary.datatype.d_qname.analyzer=org.apache.lucene.analysis.standard.StandardAnalyzer -d_dictionary.datatype.d_guid.analyzer=org.apache.lucene.analysis.standard.StandardAnalyzer -d_dictionary.datatype.d_category.analyzer=org.apache.lucene.analysis.standard.StandardAnalyzer -d_dictionary.datatype.d_noderef.analyzer=org.apache.lucene.analysis.standard.StandardAnalyzer -d_dictionary.datatype.d_path.analyzer=org.apache.lucene.analysis.standard.StandardAnalyzer +d_dictionary.datatype.d_boolean.analyzer=org.alfresco.repo.search.impl.lucene.analysis.AlfrescoStandardAnalyser +d_dictionary.datatype.d_qname.analyzer=org.alfresco.repo.search.impl.lucene.analysis.AlfrescoStandardAnalyser +d_dictionary.datatype.d_guid.analyzer=org.alfresco.repo.search.impl.lucene.analysis.AlfrescoStandardAnalyser +d_dictionary.datatype.d_category.analyzer=org.alfresco.repo.search.impl.lucene.analysis.AlfrescoStandardAnalyser +d_dictionary.datatype.d_noderef.analyzer=org.alfresco.repo.search.impl.lucene.analysis.AlfrescoStandardAnalyser +d_dictionary.datatype.d_path.analyzer=org.alfresco.repo.search.impl.lucene.analysis.AlfrescoStandardAnalyser diff --git a/config/alfresco/model/dictionaryModel.xml b/config/alfresco/model/dictionaryModel.xml index 95fa5e2b69..2c57afdd57 100644 --- a/config/alfresco/model/dictionaryModel.xml +++ b/config/alfresco/model/dictionaryModel.xml @@ -19,17 +19,17 @@ - org.apache.lucene.analysis.standard.StandardAnalyzer + org.alfresco.repo.search.impl.lucene.analysis.AlfrescoStandardAnalyser java.lang.Object - org.apache.lucene.analysis.standard.StandardAnalyzer + org.alfresco.repo.search.impl.lucene.analysis.AlfrescoStandardAnalyser java.lang.String - org.apache.lucene.analysis.standard.StandardAnalyzer + org.alfresco.repo.search.impl.lucene.analysis.AlfrescoStandardAnalyser org.alfresco.service.cmr.repository.ContentData @@ -64,37 +64,37 @@ - org.apache.lucene.analysis.standard.StandardAnalyzer + org.alfresco.repo.search.impl.lucene.analysis.AlfrescoStandardAnalyser java.lang.Boolean - org.apache.lucene.analysis.standard.StandardAnalyzer + org.alfresco.repo.search.impl.lucene.analysis.AlfrescoStandardAnalyser org.alfresco.service.namespace.QName - org.apache.lucene.analysis.standard.StandardAnalyzer + org.alfresco.repo.search.impl.lucene.analysis.AlfrescoStandardAnalyser org.alfresco.service.cmr.repository.NodeRef - org.apache.lucene.analysis.standard.StandardAnalyzer + org.alfresco.repo.search.impl.lucene.analysis.AlfrescoStandardAnalyser org.alfresco.service.cmr.repository.ChildAssociationRef - org.apache.lucene.analysis.standard.StandardAnalyzer + org.alfresco.repo.search.impl.lucene.analysis.AlfrescoStandardAnalyser org.alfresco.service.cmr.repository.AssociationRef - org.apache.lucene.analysis.standard.StandardAnalyzer + org.alfresco.repo.search.impl.lucene.analysis.AlfrescoStandardAnalyser org.alfresco.service.cmr.repository.Path - org.apache.lucene.analysis.standard.StandardAnalyzer + org.alfresco.repo.search.impl.lucene.analysis.AlfrescoStandardAnalyser org.alfresco.service.cmr.repository.NodeRef diff --git a/config/alfresco/model/permissionDefinitions.xml b/config/alfresco/model/permissionDefinitions.xml index 85d907b5c3..741ed672d8 100644 --- a/config/alfresco/model/permissionDefinitions.xml +++ b/config/alfresco/model/permissionDefinitions.xml @@ -62,11 +62,48 @@ - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -80,10 +117,10 @@ - - + + @@ -93,10 +130,10 @@ - - + + @@ -106,10 +143,10 @@ - - + + @@ -124,42 +161,42 @@ - - + + - - + + - - + + - - + + - + @@ -167,52 +204,55 @@ - - + + - - + + - - + + - + + - + + - + + @@ -222,17 +262,19 @@ - + + - + + @@ -284,6 +326,21 @@ + + + + + + + + + + + + + + + @@ -299,6 +356,7 @@ + @@ -309,16 +367,20 @@ - + + + + + - - + + - + @@ -334,24 +396,34 @@ - + + + - + + + - + + + + + + + + - - + + - - - + + diff --git a/config/alfresco/model/workflowModel.xml b/config/alfresco/model/workflowModel.xml deleted file mode 100644 index 0185344dee..0000000000 --- a/config/alfresco/model/workflowModel.xml +++ /dev/null @@ -1,160 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - Submit Review Task - bpm:workflowTask - - - - - Review Due Date - d:date - - - - Review Priority - d:int - 2 - - - - - - - - - - - Reviewer - - false - false - - - cm:person - true - false - - - - - - - - - workflow_collection_actions - - - - workflow_item_collection_actions - - - - - - - - - Review Task - bpm:workflowTask - - - - - workflow_item_edit_actions - - - - - - - - - - - - - - - bpm:workflowTask - - - - Description - d:text - - - - - - Submit Adhoc Task - wf:baseAdhocTask - - - - Due Date - d:date - - - - Priority - d:int - 2 - - - - - - - Email Notification - d:boolean - - - - - - Assignee - - false - false - - - cm:person - true - false - - - - - - - Adhoc Task - wf:baseAdhocTask - - - - Completed Adhoc Task - wf:baseAdhocTask - - - - - \ No newline at end of file diff --git a/config/alfresco/node-services-context.xml b/config/alfresco/node-services-context.xml index d1c98514ea..7a4a7a5f1e 100644 --- a/config/alfresco/node-services-context.xml +++ b/config/alfresco/node-services-context.xml @@ -86,7 +86,7 @@ false - false + true 5 diff --git a/config/alfresco/version.properties b/config/alfresco/version.properties index 87530803c5..681bec09e2 100644 --- a/config/alfresco/version.properties +++ b/config/alfresco/version.properties @@ -7,7 +7,7 @@ version.major=1 version.minor=4 version.revision=0 -version.label=Preview +version.label=RC1 # Edition label diff --git a/config/alfresco/workflow-context.xml b/config/alfresco/workflow-context.xml index 097521554a..468f5d350a 100644 --- a/config/alfresco/workflow-context.xml +++ b/config/alfresco/workflow-context.xml @@ -3,44 +3,6 @@ - - - - - - - - - jbpm - org/alfresco/repo/workflow/jbpm/review_and_approve_processdefinition.xml - text/xml - false - - - jbpm - org/alfresco/repo/workflow/jbpm/adhoc_task_processdefinition.xml - text/xml - false - - - - - - - - - alfresco/model/bpmModel.xml - alfresco/model/workflowModel.xml - - - - - alfresco/messages/bpm-messages - alfresco/messages/workflow-messages - - - - @@ -49,6 +11,7 @@ + @@ -58,7 +21,6 @@ - @@ -70,7 +32,7 @@ - false + true @@ -103,7 +65,7 @@ - + diff --git a/config/alfresco/workflow/adhoc_processdefinition.xml b/config/alfresco/workflow/adhoc_processdefinition.xml new file mode 100644 index 0000000000..10f7f7038a --- /dev/null +++ b/config/alfresco/workflow/adhoc_processdefinition.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/config/alfresco/workflow/review_processdefinition.xml b/config/alfresco/workflow/review_processdefinition.xml new file mode 100644 index 0000000000..049a9b459d --- /dev/null +++ b/config/alfresco/workflow/review_processdefinition.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/config/alfresco/messages/workflow-messages.properties b/config/alfresco/workflow/workflow-messages.properties similarity index 71% rename from config/alfresco/messages/workflow-messages.properties rename to config/alfresco/workflow/workflow-messages.properties index c6053ad28e..8a93442cef 100644 --- a/config/alfresco/messages/workflow-messages.properties +++ b/config/alfresco/workflow/workflow-messages.properties @@ -11,13 +11,6 @@ wf_review.workflow.description=Review & approval of content wf_workflowmodel.type.wf_submitReviewTask.title=Submit Review wf_workflowmodel.type.wf_submitReviewTask.description=Submit documents for review & approval -wf_workflowmodel.property.wf_reviewDueDate.title=Review Due Date -wf_workflowmodel.property.wf_reviewDueDate.description=Review Due Date -wf_workflowmodel.property.wf_reviewPriority.title=Review Priority -wf_workflowmodel.property.wf_reviewPriority.description=Review Priority -wf_workflowmodel.association.wf_reviewer.title=Reviewer -wf_workflowmodel.association.wf_reviewer.description=Reviewer - wf_workflowmodel.type.wf_reviewTask.title=Review wf_workflowmodel.type.wf_reviewTask.description=Review Documents to Approve or Reject them @@ -42,7 +35,6 @@ wf_review.task.wf_approvedTask.description=Approved wf_review.node.end.title=End wf_review.node.end.description=End - # # Adhoc Task Workflow # @@ -54,14 +46,8 @@ wf_adhoc.workflow.description=Assign task to colleague wf_workflowmodel.type.wf_submitAdhocTask.title=Submit Adhoc Task wf_workflowmodel.type.wf_submitAdhocTask.description=Allocate task to colleague -wf_workflowmodel.property.wf_adhocDescription.title=Task Description -wf_workflowmodel.property.wf_adhocDescription.description=Description of what needs to be achieved -wf_workflowmodel.property.wf_adhocDueDate.description=Task Due Date -wf_workflowmodel.property.wf_adhocPriority.title=Task Priority wf_workflowmodel.property.wf_notifyMe.title=Notify Me wf_workflowmodel.property.wf_notifyMe.description=Notify me when task is complete -wf_workflowmodel.association.wf_assignee.title=Assignee -wf_workflowmodel.association.wf_assignee.description=Who's doing the task wf_workflowmodel.type.wf_adhocTask.title=Adhoc Task wf_workflowmodel.type.wf_adhocTask.description=Adhoc Task allocated by colleague wf_workflowmodel.type.wf_completedAdhocTask.title=Adhoc Task Completed diff --git a/config/alfresco/workflow/workflowModel.xml b/config/alfresco/workflow/workflowModel.xml new file mode 100644 index 0000000000..73fdd9e553 --- /dev/null +++ b/config/alfresco/workflow/workflowModel.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + bpm:startTask + + + + bpm:workflowTask + + + + + workflow_item_edit_actions + + + + + + + + + + + + + + bpm:startTask + + + + d:boolean + false + + + + + + + bpm:workflowTask + + + + bpm:workflowTask + + + + + \ No newline at end of file diff --git a/source/java/org/alfresco/filesys/server/auth/EnterpriseCifsAuthenticator.java b/source/java/org/alfresco/filesys/server/auth/EnterpriseCifsAuthenticator.java index 0b973efc20..c5647faff4 100644 --- a/source/java/org/alfresco/filesys/server/auth/EnterpriseCifsAuthenticator.java +++ b/source/java/org/alfresco/filesys/server/auth/EnterpriseCifsAuthenticator.java @@ -608,10 +608,28 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticator implements Ca // Process the security blob byte[] respBlob = null; - + boolean isNTLMSSP = false; + try { - if ( useRawNTLMSSP()) + + // Check if the blob has the NTLMSSP signature + + if ( secBlobLen >= NTLM.Signature.length) { + + // Check for the NTLMSSP signature + + int idx = 0; + while ( idx < NTLM.Signature.length && buf[secBlobPos + idx] == NTLM.Signature[ idx]) + idx++; + + if ( idx == NTLM.Signature.length) + isNTLMSSP = true; + } + + // Process the security blob + + if ( isNTLMSSP == true) { // Process an NTLMSSP security blob @@ -657,7 +675,7 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticator implements Ca // Check if there is/was a session setup object stored in the session, this indicates a multi-stage session // setup so set the status code accordingly - if ( useRawNTLMSSP() || sess.hasSetupObject() || setupObj != null) + if ( useRawNTLMSSP() || isNTLMSSP == true || sess.hasSetupObject() || setupObj != null) { // NTLMSSP has two stages, if there is a stored setup object then indicate more processing // required diff --git a/source/java/org/alfresco/filesys/server/config/ServerConfiguration.java b/source/java/org/alfresco/filesys/server/config/ServerConfiguration.java index 6895b50b97..46c7e16839 100644 --- a/source/java/org/alfresco/filesys/server/config/ServerConfiguration.java +++ b/source/java/org/alfresco/filesys/server/config/ServerConfiguration.java @@ -1774,7 +1774,7 @@ public class ServerConfiguration implements ApplicationListener // Check if the appropriate authentication component type is configured if ( ntlmMode != NTLMMode.NONE) - throw new AlfrescoRuntimeException("Wrong authentication setup for passthru authenticator"); + throw new AlfrescoRuntimeException("Wrong authentication setup for passthru authenticator (can only be used with LDAP/JAAS auth component)"); // Load the passthru authenticator dynamically diff --git a/source/java/org/alfresco/filesys/smb/server/repo/ContentDiskDriver.java b/source/java/org/alfresco/filesys/smb/server/repo/ContentDiskDriver.java index d01bee8699..e598b5d543 100644 --- a/source/java/org/alfresco/filesys/smb/server/repo/ContentDiskDriver.java +++ b/source/java/org/alfresco/filesys/smb/server/repo/ContentDiskDriver.java @@ -831,20 +831,108 @@ public class ContentDiskDriver implements DiskInterface, IOCtlInterface } else { - // Create the transaction - - sess.beginTransaction(transactionService, true); - - // Get the file information to check if the file/folder exists - - FileInfo info = getFileInformation(sess, tree, name); - if (info.isDirectory()) + // Check if pseudo files are enabled + + if ( hasPseudoFileInterface(ctx)) { - status = FileStatus.DirectoryExists; + // Check if the file name is a pseudo file name + + if ( getPseudoFileInterface( ctx).isPseudoFile(sess, tree, name)) { + // Make sure the parent folder has a file state, and the path exists + + String[] paths = FileName.splitPath( name); + fstate = ctx.getStateTable().findFileState( paths[0]); + + if ( fstate == null) { + + // Check if the path exists + + if ( fileExists( sess, tree, paths[0]) == FileStatus.DirectoryExists) + { + // Create the file state + + fstate = ctx.getStateTable().findFileState( paths[0], true, true); + + fstate.setFileStatus( FileStatus.DirectoryExists); + + // Get the node for the folder path + + sess.beginTransaction(transactionService, true); + fstate.setNodeRef( getNodeForPath( tree, paths[0])); + + // Add pseudo files to the folder + + getPseudoFileInterface( ctx).addPseudoFilesToFolder( sess, tree, paths[0]); + + // Debug + + if ( logger.isInfoEnabled()) + logger.info( "Added file state for pseudo files folder (exists) - " + paths[0]); + } + } + else if ( fstate.hasPseudoFiles() == false) + { + // Make sure the file state has the node ref + + if ( fstate.hasNodeRef() == false) + { + // Create the transaction + + sess.beginTransaction(transactionService, true); + + // Get the node for the folder path + + fstate.setNodeRef( getNodeForPath( tree, paths[0])); + } + + // Add pseudo files for the parent folder + + getPseudoFileInterface( ctx).addPseudoFilesToFolder( sess, tree, paths[0]); + + // Debug + + if ( logger.isInfoEnabled()) + logger.info( "Added pseudo files for folder (exists) - " + paths[0]); + } + + // Check if the path is to a pseudo file + + PseudoFile pfile = getPseudoFileInterface(ctx).getPseudoFile( sess, tree, name); + if ( pfile != null) + { + // Indicate that the file exists + + status = FileStatus.FileExists; + } + else + { + // Failed to find pseudo file + + if ( logger.isInfoEnabled()) + logger.info( "Failed to find pseudo file (exists) - " + name); + } + } } - else + + // If the file is not a pseudo file then search for the file + + if ( status == FileStatus.Unknown) { - status = FileStatus.FileExists; + // Create the transaction + + sess.beginTransaction(transactionService, true); + + // Get the file information to check if the file/folder exists + + FileInfo info = getFileInformation(sess, tree, name); + if (info.isDirectory()) + { + status = FileStatus.DirectoryExists; + } + else + { + status = FileStatus.FileExists; + } } } } @@ -861,13 +949,17 @@ public class ContentDiskDriver implements DiskInterface, IOCtlInterface status = FileStatus.NotExist; } - // done + // Debug + if (logger.isDebugEnabled()) { logger.debug("File status determined: \n" + " name: " + name + "\n" + " status: " + status); } + + // Return the file/folder status + return status; } @@ -896,15 +988,65 @@ public class ContentDiskDriver implements DiskInterface, IOCtlInterface if ( hasPseudoFileInterface(ctx)) { - // Check if the path is to a pseudo file - - PseudoFile pfile = getPseudoFileInterface(ctx).getPseudoFile( sess, tree, params.getPath()); - if ( pfile != null) - { - // Create a network file to access the pseudo file data - - return pfile.getFile( params.getPath()); - } + // Check if the file name is a pseudo file name + + String path = params.getPath(); + + if ( getPseudoFileInterface( ctx).isPseudoFile(sess, tree, path)) { + + // Make sure the parent folder has a file state, and the path exists + + String[] paths = FileName.splitPath( path); + FileState fstate = ctx.getStateTable().findFileState( paths[0]); + + if ( fstate == null) { + + // Check if the path exists + + if ( fileExists( sess, tree, paths[0]) == FileStatus.DirectoryExists) + { + // Create the file state and add any pseudo files + + fstate = ctx.getStateTable().findFileState( paths[0], true, true); + + fstate.setFileStatus( FileStatus.DirectoryExists); + getPseudoFileInterface( ctx).addPseudoFilesToFolder( sess, tree, paths[0]); + + // Debug + + if ( logger.isInfoEnabled()) + logger.info( "Added file state for pseudo files folder (open) - " + paths[0]); + } + } + else if ( fstate.hasPseudoFiles() == false) + { + // Add pseudo files for the parent folder + + getPseudoFileInterface( ctx).addPseudoFilesToFolder( sess, tree, paths[0]); + + // Debug + + if ( logger.isInfoEnabled()) + logger.info( "Added pseudo files for folder (open) - " + paths[0]); + } + + // Check if the path is to a pseudo file + + PseudoFile pfile = getPseudoFileInterface(ctx).getPseudoFile( sess, tree, params.getPath()); + if ( pfile != null) + { + // Create a network file to access the pseudo file data + + return pfile.getFile( params.getPath()); + } + else + { + // Failed to find pseudo file + + if ( logger.isInfoEnabled()) + logger.info( "Failed to find pseudo file (open) - " + params.getPath()); + } + } } // Not a pseudo file, try and open a normal file/folder node @@ -1045,7 +1187,7 @@ public class ContentDiskDriver implements DiskInterface, IOCtlInterface try { - // get the device root + // Get the device root ContentContext ctx = (ContentContext) tree.getContext(); NodeRef deviceRootNodeRef = ctx.getRootNode(); @@ -1888,7 +2030,8 @@ public class ContentDiskDriver implements DiskInterface, IOCtlInterface FileState fstate = ctx.getStateTable().findFileState(path); if ( fstate != null && fstate.hasNodeRef() && fstate.exists() ) { - // check that the node exists + // Check that the node exists + if (nodeService.exists(fstate.getNodeRef())) { // Bump the file states expiry time diff --git a/source/java/org/alfresco/filesys/smb/server/repo/desk/CheckInOutDesktopAction.java b/source/java/org/alfresco/filesys/smb/server/repo/desk/CheckInOutDesktopAction.java index fe39a6b277..5f2651b1b4 100644 --- a/source/java/org/alfresco/filesys/smb/server/repo/desk/CheckInOutDesktopAction.java +++ b/source/java/org/alfresco/filesys/smb/server/repo/desk/CheckInOutDesktopAction.java @@ -16,6 +16,10 @@ */ package org.alfresco.filesys.smb.server.repo.desk; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + import org.alfresco.filesys.server.filesys.FileName; import org.alfresco.filesys.server.filesys.NotifyChange; import org.alfresco.filesys.smb.server.repo.DesktopAction; @@ -81,9 +85,10 @@ public class CheckInOutDesktopAction extends DesktopAction { { try { - // Check in the file + // Check in the file, pass an empty version properties so that veriosnable nodes create a new version - getCheckInOutService().checkin( target.getNode(), null, null, false); + Map versionProperties = new HashMap(); + getCheckInOutService().checkin( target.getNode(), versionProperties, null, false); // Check if there are any file/directory change notify requests active diff --git a/source/java/org/alfresco/filesys/smb/server/repo/pseudo/ContentPseudoFileImpl.java b/source/java/org/alfresco/filesys/smb/server/repo/pseudo/ContentPseudoFileImpl.java index 24f8e08896..ab38534a88 100644 --- a/source/java/org/alfresco/filesys/smb/server/repo/pseudo/ContentPseudoFileImpl.java +++ b/source/java/org/alfresco/filesys/smb/server/repo/pseudo/ContentPseudoFileImpl.java @@ -68,6 +68,27 @@ public class ContentPseudoFileImpl implements PseudoFileInterface if ( pfile != null) isPseudo = true; } + else + { + // Check if the file name matches a pseudo-file name in the desktop actions list + + if ( ctx.hasDesktopActions()) + { + DesktopActionTable actions = ctx.getDesktopActions(); + if ( actions.getActionViaPseudoName( paths[1]) != null) + isPseudo = true; + } + + // Check if the URL file is enabled + + if ( isPseudo == false && ctx.hasURLFile()) + { + // Check if it is the URL file name + + if ( ctx.getURLFileName().equals( paths[1])) + isPseudo = true; + } + } // Return the pseudo file status diff --git a/source/java/org/alfresco/repo/action/executer/ScriptActionExecuter.java b/source/java/org/alfresco/repo/action/executer/ScriptActionExecuter.java index 2a49156dee..1505941515 100644 --- a/source/java/org/alfresco/repo/action/executer/ScriptActionExecuter.java +++ b/source/java/org/alfresco/repo/action/executer/ScriptActionExecuter.java @@ -84,6 +84,10 @@ public class ScriptActionExecuter extends ActionExecuterAbstractBase { NodeRef scriptRef = (NodeRef)action.getParameterValue(PARAM_SCRIPTREF); NodeRef spaceRef = this.serviceRegistry.getRuleService().getOwningNodeRef(action); + if (spaceRef == null) + { + spaceRef = nodeService.getPrimaryParent(actionedUponNodeRef).getParentRef(); + } if (nodeService.exists(scriptRef)) { diff --git a/source/java/org/alfresco/repo/action/executer/TransformActionExecuter.java b/source/java/org/alfresco/repo/action/executer/TransformActionExecuter.java index 89c040ca15..28fafbcc91 100644 --- a/source/java/org/alfresco/repo/action/executer/TransformActionExecuter.java +++ b/source/java/org/alfresco/repo/action/executer/TransformActionExecuter.java @@ -240,7 +240,10 @@ public class TransformActionExecuter extends ActionExecuterAbstractBase contentWriter.setMimetype(mimeType); // new mimetype contentWriter.setEncoding(contentReader.getEncoding()); // original encoding - // Try and transform the content + // Try and transform the content - failures are caught and allowed to fail silently. + // This is unique to this action, and is essentially a broken pattern. + // Clients should rather get the exception and then decide to replay with rules/actions turned off or not. + // TODO: Check failure patterns for actions. try { doTransform(ruleAction, contentReader, contentWriter); @@ -258,8 +261,16 @@ public class TransformActionExecuter extends ActionExecuterAbstractBase } } + /** + * Executed in a new transaction so that failures don't cause the entire transaction to rollback. + */ protected void doTransform(Action ruleAction, ContentReader contentReader, ContentWriter contentWriter) { + // try to pre-empt the lack of a transformer + if (!this.contentService.isTransformable(contentReader, contentWriter)) + { + throw new NoTransformerException(contentReader.getMimetype(), contentWriter.getMimetype()); + } this.contentService.transform(contentReader, contentWriter); } diff --git a/source/java/org/alfresco/repo/audit/AuditComponentImpl.java b/source/java/org/alfresco/repo/audit/AuditComponentImpl.java index 6ee47e0c6d..240bebf881 100644 --- a/source/java/org/alfresco/repo/audit/AuditComponentImpl.java +++ b/source/java/org/alfresco/repo/audit/AuditComponentImpl.java @@ -23,6 +23,7 @@ import java.net.UnknownHostException; import java.util.Date; import java.util.List; +import org.alfresco.repo.audit.model.TrueFalseUnset; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.service.Auditable; @@ -123,13 +124,26 @@ public class AuditComponentImpl implements AuditComponent { if ((auditFlag.get() == null) || (!auditFlag.get().booleanValue())) { + boolean auditInternal = (auditModel.getAuditInternalServiceMethods(mi) == TrueFalseUnset.TRUE); try { - auditFlag.set(Boolean.TRUE); - Method method = mi.getMethod(); String methodName = method.getName(); String serviceName = publicServiceIdentifier.getPublicServiceName(mi); + + if (!auditInternal) + { + auditFlag.set(Boolean.TRUE); + } + else + { + if (s_logger.isDebugEnabled()) + { + s_logger.debug("Auditing internal service use for - " + serviceName + "." + methodName); + } + } + + if (method.isAnnotationPresent(Auditable.class)) { @@ -170,7 +184,10 @@ public class AuditComponentImpl implements AuditComponent } finally { - auditFlag.set(Boolean.FALSE); + if (!auditInternal) + { + auditFlag.set(Boolean.FALSE); + } } } else @@ -272,7 +289,7 @@ public class AuditComponentImpl implements AuditComponent } else if (returnObject instanceof StoreRef) { - auditInfo.setKeyStore((StoreRef)returnObject); + auditInfo.setKeyStore((StoreRef) returnObject); } } } diff --git a/source/java/org/alfresco/repo/audit/MethodAuditModel.java b/source/java/org/alfresco/repo/audit/MethodAuditModel.java index 37b1200173..62791ca5e6 100644 --- a/source/java/org/alfresco/repo/audit/MethodAuditModel.java +++ b/source/java/org/alfresco/repo/audit/MethodAuditModel.java @@ -16,6 +16,7 @@ */ package org.alfresco.repo.audit; +import org.alfresco.repo.audit.model.TrueFalseUnset; import org.aopalliance.intercept.MethodInvocation; public interface MethodAuditModel @@ -54,4 +55,11 @@ public interface MethodAuditModel * @return */ public RecordOptions getAuditRecordOptions(MethodInvocation mi); + + /** + * Should internal service class be logged. + * + * @return + */ + public TrueFalseUnset getAuditInternalServiceMethods(MethodInvocation mi); } diff --git a/source/java/org/alfresco/repo/audit/hibernate/Audit.hbm.xml b/source/java/org/alfresco/repo/audit/hibernate/Audit.hbm.xml index 9e40c7a787..acc837c69a 100644 --- a/source/java/org/alfresco/repo/audit/hibernate/Audit.hbm.xml +++ b/source/java/org/alfresco/repo/audit/hibernate/Audit.hbm.xml @@ -67,7 +67,7 @@ - + @@ -135,24 +135,34 @@ select - audit_store + audit_store_byid + from + org.alfresco.repo.audit.hibernate.AuditSourceImpl as audit_store_byid + where + audit_store_byid = + (select max(audit_store.id) from org.alfresco.repo.audit.hibernate.AuditSourceImpl as audit_store where audit_store.application = :application and audit_store.service is null and - audit_store.method is null + audit_store.method is null) select - audit_store + audit_store_byid + from + org.alfresco.repo.audit.hibernate.AuditSourceImpl as audit_store_byid + where + audit_store_byid = + (select max(audit_store.id) from org.alfresco.repo.audit.hibernate.AuditSourceImpl as audit_store where audit_store.application = :application and audit_store.service = :service and - audit_store.method = :method + audit_store.method = :method) diff --git a/source/java/org/alfresco/repo/audit/model/AbstractAuditEntry.java b/source/java/org/alfresco/repo/audit/model/AbstractAuditEntry.java index c99fd0bb7c..d747a482d3 100644 --- a/source/java/org/alfresco/repo/audit/model/AbstractAuditEntry.java +++ b/source/java/org/alfresco/repo/audit/model/AbstractAuditEntry.java @@ -136,6 +136,49 @@ public abstract class AbstractAuditEntry { return recordOptions; } + + + protected TrueFalseUnset getEffectiveAuditInternal() + { + TrueFalseUnset auditInternal; + if (checkEnabled() == TrueFalseUnset.TRUE) + { + auditInternal = getAuditInternalOrParentAuditInternal(); + } + else + { + auditInternal = TrueFalseUnset.FALSE; + } + if(s_logger.isDebugEnabled()) + { + s_logger.debug("... Effective audit internal is = "+auditInternal); + } + return auditInternal; + } + + private TrueFalseUnset getAuditInternalOrParentAuditInternal() + { + TrueFalseUnset auditInternal = getAuditInternal(); + if(s_logger.isDebugEnabled()) + { + s_logger.debug("... ... audit internal is = "+auditInternal); + } + if (auditInternal == TrueFalseUnset.UNSET) + { + if (getParent() == null) + { + return TrueFalseUnset.UNSET; + } + else + { + return getParent().getAuditInternalOrParentAuditInternal(); + } + } + else + { + return auditInternal; + } + } protected AuditMode getEffectiveAuditMode() { diff --git a/source/java/org/alfresco/repo/audit/model/AuditEntry.java b/source/java/org/alfresco/repo/audit/model/AuditEntry.java index 68678d200c..613da6578d 100644 --- a/source/java/org/alfresco/repo/audit/model/AuditEntry.java +++ b/source/java/org/alfresco/repo/audit/model/AuditEntry.java @@ -217,4 +217,23 @@ public class AuditEntry extends AbstractAuditEntry implements InitializingBean, throw new UnsupportedOperationException(); } + public TrueFalseUnset getAuditInternalServiceMethods( MethodInvocation mi) + { + String serviceName = getPublicServiceIdentifier().getPublicServiceName(mi); + ServiceAuditEntry service = services.get(serviceName); + if(service != null) + { + return service.getAuditInternalServiceMethods( mi); + } + else + { + if(s_logger.isDebugEnabled()) + { + s_logger.debug("No specific audit entry for service "+serviceName); + } + return getEffectiveAuditInternal(); + + } + } + } diff --git a/source/java/org/alfresco/repo/audit/model/MethodAuditEntry.java b/source/java/org/alfresco/repo/audit/model/MethodAuditEntry.java index a59d6b7a72..c38d36e5db 100644 --- a/source/java/org/alfresco/repo/audit/model/MethodAuditEntry.java +++ b/source/java/org/alfresco/repo/audit/model/MethodAuditEntry.java @@ -56,4 +56,13 @@ public class MethodAuditEntry extends AbstractNamedAuditEntry implements MethodA throw new UnsupportedOperationException(); } + public TrueFalseUnset getAuditInternalServiceMethods(MethodInvocation mi) + { + if(s_logger.isDebugEnabled()) + { + s_logger.debug("Evaluating if method is internally audited ..."+((ServiceAuditEntry)getParent()).getName()+"."+getName()); + } + return getEffectiveAuditInternal(); + } + } diff --git a/source/java/org/alfresco/repo/audit/model/ServiceAuditEntry.java b/source/java/org/alfresco/repo/audit/model/ServiceAuditEntry.java index 764f004044..e4458bb5ff 100644 --- a/source/java/org/alfresco/repo/audit/model/ServiceAuditEntry.java +++ b/source/java/org/alfresco/repo/audit/model/ServiceAuditEntry.java @@ -97,4 +97,22 @@ public class ServiceAuditEntry extends AbstractNamedAuditEntry implements Method throw new UnsupportedOperationException(); } + public TrueFalseUnset getAuditInternalServiceMethods(MethodInvocation mi) + { + String methodName = mi.getMethod().getName(); + MethodAuditEntry method = methods.get(methodName); + if (method != null) + { + return method.getAuditInternalServiceMethods(mi); + } + else + { + if(s_logger.isDebugEnabled()) + { + s_logger.debug("Evaluating if service is internally audited (no specific setting) for "+getName()+"."+methodName); + } + return getEffectiveAuditInternal(); + } + } + } diff --git a/source/java/org/alfresco/repo/cache/InternalEhCacheManagerFactoryBean.java b/source/java/org/alfresco/repo/cache/InternalEhCacheManagerFactoryBean.java index 3830803457..cf87c94d1f 100644 --- a/source/java/org/alfresco/repo/cache/InternalEhCacheManagerFactoryBean.java +++ b/source/java/org/alfresco/repo/cache/InternalEhCacheManagerFactoryBean.java @@ -44,7 +44,7 @@ import org.springframework.util.ResourceUtils; * For Alfresco purposes, there are two files that are looked for: *
    *
  • classpath:alfresco/extension/ehcache-custom.xml, which will take precedence
  • - *
  • classpath:alfresco/ehcache.xml, which is the default shipped with Alfresco
  • + *
  • classpath:alfresco/ehcache-default.xml, which is the default shipped with Alfresco
  • *
*

* The EHCache static singleton instance is used but ensuring that all access to the diff --git a/source/java/org/alfresco/repo/content/AbstractContentStore.java b/source/java/org/alfresco/repo/content/AbstractContentStore.java index 76ff24e12b..db7336a57c 100644 --- a/source/java/org/alfresco/repo/content/AbstractContentStore.java +++ b/source/java/org/alfresco/repo/content/AbstractContentStore.java @@ -109,7 +109,7 @@ public abstract class AbstractContentStore implements ContentStore // extract the relative part of the URL String path = contentUrl.substring(index); // more extensive checks can be added in, but it seems overkill - if (path.length() < 10) + if (path.length() < 8) { throw new AlfrescoRuntimeException( "The content URL is invalid: \n" + diff --git a/source/java/org/alfresco/repo/content/MimetypeMap.java b/source/java/org/alfresco/repo/content/MimetypeMap.java index 272c1cb89d..473dea5253 100644 --- a/source/java/org/alfresco/repo/content/MimetypeMap.java +++ b/source/java/org/alfresco/repo/content/MimetypeMap.java @@ -56,6 +56,7 @@ public class MimetypeMap implements MimetypeService public static final String MIMETYPE_IMAGE_GIF = "image/gif"; public static final String MIMETYPE_IMAGE_JPEG = "image/jpeg"; public static final String MIMETYPE_IMAGE_RGB = "image/x-rgb"; + public static final String MIMETYPE_IMAGE_SVG = "image/svg"; public static final String MIMETYPE_JAVASCRIPT = "application/x-javascript"; public static final String MIMETYPE_ZIP = "application/zip"; // Open Document diff --git a/source/java/org/alfresco/repo/content/cleanup/ContentStoreCleaner.java b/source/java/org/alfresco/repo/content/cleanup/ContentStoreCleaner.java index adfedf0767..de57d173c7 100644 --- a/source/java/org/alfresco/repo/content/cleanup/ContentStoreCleaner.java +++ b/source/java/org/alfresco/repo/content/cleanup/ContentStoreCleaner.java @@ -16,6 +16,7 @@ */ package org.alfresco.repo.content.cleanup; +import java.io.Serializable; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; @@ -27,10 +28,12 @@ import org.alfresco.repo.content.ContentStore; import org.alfresco.repo.node.db.NodeDaoService; import org.alfresco.repo.transaction.TransactionUtil; import org.alfresco.repo.transaction.TransactionUtil.TransactionWork; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.PropertyCheck; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -116,26 +119,10 @@ public class ContentStoreCleaner */ private void checkProperties() { - if (dictionaryService == null) - { - throw new AlfrescoRuntimeException("Property 'dictionaryService' not set"); - } - if (nodeDaoService == null) - { - throw new AlfrescoRuntimeException("Property 'nodeDaoService' not set"); - } - if (transactionService == null) - { - throw new AlfrescoRuntimeException("Property 'transactionService' not set"); - } - if (stores == null || stores.size() == 0) - { - throw new AlfrescoRuntimeException("Property 'stores' not set"); - } - if (listeners == null) - { - throw new AlfrescoRuntimeException("Property 'listeners' not set"); - } + PropertyCheck.mandatory(this, "dictionaryService", dictionaryService); + PropertyCheck.mandatory(this, "nodeDaoService", nodeDaoService); + PropertyCheck.mandatory(this, "transactionService", transactionService); + PropertyCheck.mandatory(this, "listeners", listeners); // check the protect days if (protectDays < 0) @@ -152,26 +139,27 @@ public class ContentStoreCleaner private Set getValidUrls() { + final DataTypeDefinition contentDataType = dictionaryService.getDataType(DataTypeDefinition.CONTENT); // wrap to make the request in a transaction - TransactionWork> getUrlsWork = new TransactionWork>() + TransactionWork> getUrlsWork = new TransactionWork>() { - public List doWork() throws Exception + public List doWork() throws Exception { - return nodeDaoService.getContentDataStrings(); + return nodeDaoService.getPropertyValuesByActualType(contentDataType); }; }; // execute in READ-ONLY txn - List contentDataStrings = TransactionUtil.executeInUserTransaction( + List values = TransactionUtil.executeInUserTransaction( transactionService, getUrlsWork, true); // get all valid URLs - Set validUrls = new HashSet(contentDataStrings.size()); + Set validUrls = new HashSet(values.size()); // convert the strings to objects and extract the URL - for (String contentDataString : contentDataStrings) + for (Serializable value : values) { - ContentData contentData = ContentData.createContentProperty(contentDataString); + ContentData contentData = (ContentData) value; if (contentData.getContentUrl() != null) { // a URL was present diff --git a/source/java/org/alfresco/repo/content/transform/AbstractContentTransformerTest.java b/source/java/org/alfresco/repo/content/transform/AbstractContentTransformerTest.java index 468ba3e5f0..42ba8f27d3 100644 --- a/source/java/org/alfresco/repo/content/transform/AbstractContentTransformerTest.java +++ b/source/java/org/alfresco/repo/content/transform/AbstractContentTransformerTest.java @@ -19,7 +19,10 @@ package org.alfresco.repo.content.transform; import java.io.File; import java.io.IOException; import java.net.URL; -import java.util.List; +import java.util.Date; +import java.util.HashSet; +import java.util.Set; +import java.util.TreeSet; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.content.filestore.FileContentReader; @@ -120,45 +123,71 @@ public abstract class AbstractContentTransformerTest extends BaseSpringTest * case where optimizations are being done around the selection of the most * appropriate transformer, different transformers could be used during the iteration * process. + *

+ * Results for the transformations are dumped to a temporary file named + * AbstractContentTransformerTest-results-1234.txt. */ public void testAllConversions() throws Exception { + StringBuilder sb = new StringBuilder(2048); + sb.append("Mimetype Conversion Tests \n") + .append("========================= \n") + .append(" Date: ").append(new Date()).append("\n") + .append("\n"); + // get all mimetypes - List mimetypes = mimetypeMap.getMimetypes(); + Set mimetypes = new TreeSet(mimetypeMap.getMimetypes()); for (String sourceMimetype : mimetypes) { // attempt to get a source file for each mimetype String sourceExtension = mimetypeMap.getExtension(sourceMimetype); - File sourceFile = AbstractContentTransformerTest.loadQuickTestFile(sourceExtension); - if (sourceFile == null) - { - continue; // no test file available for that extension - } + + sb.append(" Source Extension: ").append(sourceExtension).append("\n"); // attempt to convert to every other mimetype for (String targetMimetype : mimetypes) { ContentWriter targetWriter = null; // construct a reader onto the source file + String targetExtension = mimetypeMap.getExtension(targetMimetype); + + // must we test the transformation? + ContentTransformer transformer = getTransformer(sourceMimetype, targetMimetype); + if (transformer == null || transformer.getReliability(sourceMimetype, targetMimetype) <= 0.0) + { + // no transformer + continue; + } + + // dump + sb.append(" Target Extension: ").append(targetExtension); + sb.append(" <").append(transformer.getClass().getSimpleName()).append(">"); + + // is there a test file for this conversion? + File sourceFile = AbstractContentTransformerTest.loadQuickTestFile(sourceExtension); + if (sourceFile == null) + { + sb.append(" \n"); + continue; // no test file available for that extension + } ContentReader sourceReader = new FileContentReader(sourceFile); // perform the transformation several times so that we get a good idea of performance int count = 0; + long before = System.currentTimeMillis(); + Set transformerClasses = new HashSet(2); for (int i = 0; i < 5; i++) { - // must we test the transformation? - ContentTransformer transformer = getTransformer(sourceMimetype, targetMimetype); - if (transformer == null) + // get the transformer repeatedly as it might be different each time around + transformer = getTransformer(sourceMimetype, targetMimetype); + // must we report on this class? + if (!transformerClasses.contains(transformer.getClass().getName())) { - break; // test is not required + transformerClasses.add(transformer.getClass().getName()); + sb.append(" <").append(transformer.getClass().getSimpleName()).append(">"); } - else if (transformer.getReliability(sourceMimetype, targetMimetype) <= 0.0) - { - break; // not reliable for this transformation - } - + // make a writer for the target file - String targetExtension = mimetypeMap.getExtension(targetMimetype); File targetFile = TempFileProvider.createTempFile( getClass().getSimpleName() + "_" + getName() + "_" + sourceExtension + "_", "." + targetExtension); @@ -198,6 +227,11 @@ public abstract class AbstractContentTransformerTest extends BaseSpringTest // increment count count++; } + long after = System.currentTimeMillis(); + double average = (double) (after - before) / (double) count; + + // dump + sb.append(String.format(" average %10.0f ms", average)).append("\n"); if (logger.isDebugEnabled()) { @@ -209,5 +243,11 @@ public abstract class AbstractContentTransformerTest extends BaseSpringTest } } } + + // dump to file + File outputFile = TempFileProvider.createTempFile("AbstractContentTransformerTest-results-", ".txt"); + ContentWriter outputWriter = new FileContentWriter(outputFile); + outputWriter.setEncoding("UTF8"); + outputWriter.putContent(sb.toString()); } } diff --git a/source/java/org/alfresco/repo/content/transform/OpenOfficeContentTransformerTest.java b/source/java/org/alfresco/repo/content/transform/OpenOfficeContentTransformerTest.java index 12d016db23..a70d31d106 100644 --- a/source/java/org/alfresco/repo/content/transform/OpenOfficeContentTransformerTest.java +++ b/source/java/org/alfresco/repo/content/transform/OpenOfficeContentTransformerTest.java @@ -16,9 +16,16 @@ */ package org.alfresco.repo.content.transform; +import java.io.File; + import net.sf.jooreports.openoffice.connection.OpenOfficeConnection; import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.content.filestore.FileContentReader; +import org.alfresco.repo.content.filestore.FileContentWriter; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.util.TempFileProvider; /** * @see org.alfresco.repo.content.transform.OpenOfficeContentTransformer @@ -75,4 +82,24 @@ public class OpenOfficeContentTransformerTest extends AbstractContentTransformer reliability = transformer.getReliability(MimetypeMap.MIMETYPE_WORD, MimetypeMap.MIMETYPE_TEXT_PLAIN); assertEquals("Mimetype should be supported", 1.0, reliability); } + + /** + * Test what is up with HTML to PDF + */ + public void testHtmlToPdf() throws Exception + { + if (!transformer.isConnected()) + { + // no connection + return; + } + File htmlSourceFile = loadQuickTestFile("html"); + File pdfTargetFile = TempFileProvider.createTempFile(getName() + "-target-", ".pdf"); + ContentReader reader = new FileContentReader(htmlSourceFile); + reader.setMimetype(MimetypeMap.MIMETYPE_HTML); + ContentWriter writer = new FileContentWriter(pdfTargetFile); + writer.setMimetype(MimetypeMap.MIMETYPE_PDF); + + transformer.transform(reader, writer); + } } diff --git a/source/java/org/alfresco/repo/content/transform/magick/AbstractImageMagickContentTransformer.java b/source/java/org/alfresco/repo/content/transform/magick/AbstractImageMagickContentTransformer.java index 852f9194b0..bb11c7d662 100644 --- a/source/java/org/alfresco/repo/content/transform/magick/AbstractImageMagickContentTransformer.java +++ b/source/java/org/alfresco/repo/content/transform/magick/AbstractImageMagickContentTransformer.java @@ -145,6 +145,10 @@ public abstract class AbstractImageMagickContentTransformer extends AbstractCont { return false; // rgb extension doesn't work } + else if (mimetype.equals(MimetypeMap.MIMETYPE_IMAGE_SVG)) + { + return false; // svg extension doesn't work + } else { return true; diff --git a/source/java/org/alfresco/repo/dictionary/DictionaryComponent.java b/source/java/org/alfresco/repo/dictionary/DictionaryComponent.java index 2b9722b49e..f9cd844d6c 100644 --- a/source/java/org/alfresco/repo/dictionary/DictionaryComponent.java +++ b/source/java/org/alfresco/repo/dictionary/DictionaryComponent.java @@ -81,7 +81,7 @@ public class DictionaryComponent implements DictionaryService Collection propertyTypes = new ArrayList(); for (QName model : getAllModels()) { - propertyTypes.addAll(getAspects(model)); + propertyTypes.addAll(getDataTypes(model)); } return propertyTypes; } diff --git a/source/java/org/alfresco/repo/dictionary/M2Model.java b/source/java/org/alfresco/repo/dictionary/M2Model.java index 4dad74222a..c4c8752aa4 100644 --- a/source/java/org/alfresco/repo/dictionary/M2Model.java +++ b/source/java/org/alfresco/repo/dictionary/M2Model.java @@ -202,7 +202,7 @@ public class M2Model M2Type type = getType(name); if (type != null) { - types.remove(types); + types.remove(type); } } diff --git a/source/java/org/alfresco/repo/dictionary/dictionarydaotest_model.xml b/source/java/org/alfresco/repo/dictionary/dictionarydaotest_model.xml index fc8748f662..ba51937ef7 100644 --- a/source/java/org/alfresco/repo/dictionary/dictionarydaotest_model.xml +++ b/source/java/org/alfresco/repo/dictionary/dictionarydaotest_model.xml @@ -16,7 +16,7 @@ - org.apache.lucene.analysis.standard.StandardAnalyzer + org.alfresco.repo.search.impl.lucene.analysis.AlfrescoStandardAnalyser java.lang.Object diff --git a/source/java/org/alfresco/repo/domain/PropertyValue.java b/source/java/org/alfresco/repo/domain/PropertyValue.java index f4fad1fa2a..bd1803d20f 100644 --- a/source/java/org/alfresco/repo/domain/PropertyValue.java +++ b/source/java/org/alfresco/repo/domain/PropertyValue.java @@ -19,6 +19,7 @@ package org.alfresco.repo.domain; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -438,19 +439,32 @@ public class PropertyValue implements Cloneable, Serializable * * @return Returns the ValueType - never null */ - private ValueType makeValueType(QName typeQName) + private static ValueType makeValueType(QName typeQName) { ValueType valueType = valueTypesByPropertyType.get(typeQName); if (valueType == null) { throw new AlfrescoRuntimeException( "Property type not recognised: \n" + - " type: " + typeQName + "\n" + - " property: " + this); + " type: " + typeQName); } return valueType; } + /** + * Given an actual type qualified name, returns the String that represents it in + * the database. + * + * @param typeQName the type qualified name + * @return Returns the String representation of the type, + * e.g. CONTENT for type d:content. + */ + public static String getActualTypeString(QName typeQName) + { + ValueType valueType = makeValueType(typeQName); + return valueType.toString(); + } + @Override public boolean equals(Object obj) { @@ -632,15 +646,16 @@ public class PropertyValue implements Cloneable, Serializable * @return Returns the value of this property as the desired type, or a Collection * of values of the required type * - * @throws java.lang.UnsupportedOperationException if the value cannot be converted to the - * type given + * @throws AlfrescoRuntimeException + * if the type given is not recognized + * @throws org.alfresco.service.cmr.repository.datatype.TypeConversionException + * if the conversion to the required type fails * * @see DataTypeDefinition#ANY The static qualified names for the types */ public Serializable getValue(QName typeQName) { // first check for null - ValueType requiredType = makeValueType(typeQName); if (requiredType == ValueType.SERIALIZABLE) { @@ -680,6 +695,24 @@ public class PropertyValue implements Cloneable, Serializable return ret; } + /** + * Gets the value or values as a guaranteed collection. + * + * @see #getValue(QName) + */ + public Collection getCollection(QName typeQName) + { + Serializable value = getValue(typeQName); + if (value instanceof Collection) + { + return (Collection) value; + } + else + { + return Collections.singletonList(value); + } + } + public boolean getBooleanValue() { if (booleanValue == null) diff --git a/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml b/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml index 15fe0ed614..96fd1aee85 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml +++ b/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml @@ -364,15 +364,19 @@ transaction.changeTxnId = :changeTxnId - - select distinct - props.stringValue + + select + node from org.alfresco.repo.domain.hibernate.NodeImpl as node join - node.properties props + node.properties prop where - props.stringValue like 'contentUrl%' + ( + prop.actualType = :actualTypeString or + prop.actualType = 'SERIALIZABLE' + ) and + prop.persistedType != 'NULL' diff --git a/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java b/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java index c3822a51bc..64d718da6f 100644 --- a/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java +++ b/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java @@ -314,9 +314,13 @@ public class SchemaBootstrap implements ApplicationListener } if (create) { + // Get the dialect + final Dialect dialect = Dialect.getDialect(cfg.getProperties()); + String dialectStr = dialect.getClass().getName(); + // the applied patch table is missing - we assume that all other tables are missing // perform a full update using Hibernate-generated statements - File tempFile = TempFileProvider.createTempFile("AlfrescoSchemaCreate", ".sql"); + File tempFile = TempFileProvider.createTempFile("AlfrescoSchemaCreate-" + dialectStr + "-", ".sql"); dumpSchemaCreate(cfg, tempFile); FileInputStream tempInputStream = new FileInputStream(tempFile); executeScriptFile(cfg, connection, tempInputStream, tempFile.getPath()); diff --git a/source/java/org/alfresco/repo/jscript/Node.java b/source/java/org/alfresco/repo/jscript/Node.java index 25226c1629..daeb82ed3f 100644 --- a/source/java/org/alfresco/repo/jscript/Node.java +++ b/source/java/org/alfresco/repo/jscript/Node.java @@ -79,6 +79,11 @@ import org.springframework.util.StringUtils; */ public class Node implements Serializable, Scopeable { + /** + * Comment for serialVersionUID + */ + private static final long serialVersionUID = -3378946227712939600L; + private static Log logger = LogFactory.getLog(Node.class); private final static String NAMESPACE_BEGIN = "" + QName.NAMESPACE_BEGIN; @@ -885,6 +890,43 @@ public class Node implements Serializable, Scopeable this.services.getPermissionService().deletePermission(this.nodeRef, authority, permission); } + // ------------- + // Ownership API + + /** + * Set the owner of the node + */ + public void setOwner(String userId) + { + this.services.getOwnableService().setOwner(this.nodeRef, userId); + } + + /** + * Take ownership of the node. + */ + public void takeOwnership() + { + this.services.getOwnableService().takeOwnership(this.nodeRef); + } + + /** + * Get the owner of the node. + * @return + */ + public String getOwner() + { + return this.services.getOwnableService().getOwner(this.nodeRef); + } + + /** + * Make owner available as a property. + * + * @return + */ + public String jsGet_owner() + { + return getOwner(); + } // ------------------------------------------------------------------------------ // Create and Modify API @@ -1645,8 +1687,11 @@ public class Node implements Serializable, Scopeable { if (this.nodeService.exists(nodeRef)) { + // TODO: DC: Allow debug output of property values - for now it's disabled as this could potentially + // follow a large network of nodes. Unfortunately, JBPM issues unprotected debug statements + // where node.toString is used - will request this is fixed in next release of JBPM. return "Node Type: " + getType() + - "\nNode Properties: " + this.getProperties().toString() + + "\nNode Properties: " + this.getProperties().size() + "\nNode Aspects: " + this.getAspects().toString(); } else diff --git a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java index 4fe89d582b..6d5eff338d 100644 --- a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java +++ b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java @@ -73,7 +73,11 @@ public class FileFolderServiceImpl implements FileFolderService /** Shallow search for all files and folders */ private static final String LUCENE_QUERY_SHALLOW_ALL = "+PARENT:\"${cm:parent}\"" + - "-TYPE:\"" + ContentModel.TYPE_SYSTEM_FOLDER + "\" "; + "-TYPE:\"" + ContentModel.TYPE_SYSTEM_FOLDER + "\" " + + "+(" + + "TYPE:\"" + ContentModel.TYPE_CONTENT + "\" " + + "TYPE:\"" + ContentModel.TYPE_FOLDER + "\" " + + ")"; /** Shallow search for all files and folders */ private static final String LUCENE_QUERY_SHALLOW_FOLDERS = diff --git a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java index 279cdb5922..c06659c665 100644 --- a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java +++ b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java @@ -28,6 +28,7 @@ import junit.framework.TestCase; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; +import org.alfresco.repo.node.integrity.IntegrityChecker; import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.model.FileExistsException; @@ -91,6 +92,9 @@ public class FileFolderServiceImplTest extends TestCase txn = transactionService.getUserTransaction(); txn.begin(); + // downgrade integrity + IntegrityChecker.setWarnInTransaction(); + // authenticate authenticationComponent.setCurrentUser(authenticationComponent.getSystemUserName()); diff --git a/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java b/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java index f6de57d293..6dd08e7ca8 100644 --- a/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java +++ b/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java @@ -35,6 +35,7 @@ import org.alfresco.repo.dictionary.M2Model; import org.alfresco.repo.domain.hibernate.ChildAssocImpl; import org.alfresco.repo.domain.hibernate.NodeImpl; import org.alfresco.repo.node.db.NodeDaoService; +import org.alfresco.repo.node.integrity.IntegrityChecker; import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.security.authentication.AuthenticationComponent; @@ -161,6 +162,9 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.currentTimeMillis()); rootNodeRef = nodeService.getRootNode(storeRef); + + // downgrade integrity checks + IntegrityChecker.setWarnInTransaction(); } @Override diff --git a/source/java/org/alfresco/repo/node/PerformanceNodeServiceTest.java b/source/java/org/alfresco/repo/node/PerformanceNodeServiceTest.java index 8935297d7c..51810f9c1f 100644 --- a/source/java/org/alfresco/repo/node/PerformanceNodeServiceTest.java +++ b/source/java/org/alfresco/repo/node/PerformanceNodeServiceTest.java @@ -27,6 +27,7 @@ import org.alfresco.model.ContentModel; import org.alfresco.repo.dictionary.DictionaryComponent; import org.alfresco.repo.dictionary.DictionaryDAO; import org.alfresco.repo.dictionary.M2Model; +import org.alfresco.repo.node.integrity.IntegrityChecker; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.TransactionUtil; import org.alfresco.repo.transaction.TransactionUtil.TransactionWork; @@ -160,6 +161,7 @@ public class PerformanceNodeServiceTest extends TestCase { public Object doWork() { + IntegrityChecker.setWarnInTransaction(); buildNodeChildren(rootNodeRef, 1, testDepth, testChildCount); return null; } diff --git a/source/java/org/alfresco/repo/node/archive/ArchiveAndRestoreTest.java b/source/java/org/alfresco/repo/node/archive/ArchiveAndRestoreTest.java index 489b65a72f..b20eedd85e 100644 --- a/source/java/org/alfresco/repo/node/archive/ArchiveAndRestoreTest.java +++ b/source/java/org/alfresco/repo/node/archive/ArchiveAndRestoreTest.java @@ -29,6 +29,7 @@ import junit.framework.TestCase; import org.alfresco.model.ContentModel; import org.alfresco.repo.node.StoreArchiveMap; import org.alfresco.repo.node.archive.RestoreNodeReport.RestoreStatus; +import org.alfresco.repo.node.integrity.IntegrityChecker; import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.service.ServiceRegistry; @@ -37,7 +38,6 @@ 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.AccessStatus; import org.alfresco.service.cmr.security.AuthenticationService; import org.alfresco.service.cmr.security.OwnableService; import org.alfresco.service.cmr.security.PermissionService; @@ -114,6 +114,9 @@ public class ArchiveAndRestoreTest extends TestCase txn = transactionService.getUserTransaction(); txn.begin(); + // downgrade integrity checks + IntegrityChecker.setWarnInTransaction(); + try { authenticationComponent.setSystemUserAsCurrentUser(); diff --git a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java index 907d1e65b5..d4efe11708 100644 --- a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java +++ b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java @@ -40,7 +40,6 @@ import org.alfresco.repo.domain.Store; import org.alfresco.repo.node.AbstractNodeServiceImpl; import org.alfresco.repo.node.StoreArchiveMap; import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.AssociationDefinition; import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition; @@ -1461,15 +1460,12 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl nodeToMove.setStore(store); NodeRef newNodeRef = nodeToMove.getNodeRef(); - String txnId = AlfrescoTransactionSupport.getTransactionId(); // update old status NodeStatus oldNodeStatus = nodeDaoService.getNodeStatus(oldNodeRef, true); oldNodeStatus.setNode(null); - oldNodeStatus.getTransaction().setChangeTxnId(txnId); // create the new status NodeStatus newNodeStatus = nodeDaoService.getNodeStatus(newNodeRef, true); newNodeStatus.setNode(nodeToMove); - newNodeStatus.getTransaction().setChangeTxnId(txnId); } } diff --git a/source/java/org/alfresco/repo/node/db/DbNodeServiceImplTest.java b/source/java/org/alfresco/repo/node/db/DbNodeServiceImplTest.java index 43341b58c3..d99ddb00fe 100644 --- a/source/java/org/alfresco/repo/node/db/DbNodeServiceImplTest.java +++ b/source/java/org/alfresco/repo/node/db/DbNodeServiceImplTest.java @@ -17,6 +17,7 @@ package org.alfresco.repo.node.db; import java.io.Serializable; +import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; @@ -30,6 +31,8 @@ import org.alfresco.repo.node.BaseNodeServiceTest; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.TransactionUtil; import org.alfresco.repo.transaction.TransactionUtil.TransactionWork; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.NodeRef; @@ -47,6 +50,7 @@ public class DbNodeServiceImplTest extends BaseNodeServiceTest { private TransactionService txnService; private NodeDaoService nodeDaoService; + private DictionaryService dictionaryService; protected NodeService getNodeService() { @@ -59,6 +63,7 @@ public class DbNodeServiceImplTest extends BaseNodeServiceTest super.onSetUpInTransaction(); txnService = (TransactionService) applicationContext.getBean("transactionComponent"); nodeDaoService = (NodeDaoService) applicationContext.getBean("nodeDaoService"); + dictionaryService = (DictionaryService) applicationContext.getBean("dictionaryService"); } /** @@ -258,18 +263,34 @@ public class DbNodeServiceImplTest extends BaseNodeServiceTest /** * Checks that the string_value retrieval against a property type is working */ - public void testGetContentDataStringValues() throws Exception + public void testGetContentDataValues() throws Exception { - ContentData contentData = new ContentData("abc", MimetypeMap.MIMETYPE_TEXT_PLAIN, 0L, null); - // put this in as a random property + final DataTypeDefinition contentDataType = dictionaryService.getDataType(DataTypeDefinition.CONTENT); + + ContentData contentDataSingle = new ContentData("url-single", MimetypeMap.MIMETYPE_TEXT_PLAIN, 0L, null); + ContentData contentDataMultiple = new ContentData("url-multiple", MimetypeMap.MIMETYPE_TEXT_PLAIN, 0L, null); + // put this in as a random single property nodeService.setProperty( rootNodeRef, - QName.createQName(NAMESPACE, "random"), - contentData); + QName.createQName(NAMESPACE, "random-single"), + contentDataSingle); + + // create a collection of mixed types + ArrayList collection = new ArrayList(3); + collection.add("abc"); + collection.add(new Integer(123)); + collection.add(contentDataMultiple); + nodeService.setProperty( + rootNodeRef, + QName.createQName(NAMESPACE, "random-multiple"), + collection); + // get a list of all content values - List contentDataStrings = nodeDaoService.getContentDataStrings(); - assertNotNull(contentDataStrings); - assertTrue("ContentData not represented as a String in results", - contentDataStrings.contains(contentData.toString())); + List allContentDatas = nodeDaoService.getPropertyValuesByActualType(contentDataType); + assertTrue("At least two instances expected", allContentDatas.size() >= 2); + assertTrue("Single content data not present in results", + allContentDatas.contains(contentDataSingle)); + assertTrue("Multi-valued buried content data not present in results", + allContentDatas.contains(contentDataMultiple)); } } diff --git a/source/java/org/alfresco/repo/node/db/NodeDaoService.java b/source/java/org/alfresco/repo/node/db/NodeDaoService.java index 3fba9af4f9..25e90f432a 100644 --- a/source/java/org/alfresco/repo/node/db/NodeDaoService.java +++ b/source/java/org/alfresco/repo/node/db/NodeDaoService.java @@ -16,6 +16,7 @@ */ package org.alfresco.repo.node.db; +import java.io.Serializable; import java.util.Collection; import java.util.List; @@ -24,6 +25,7 @@ import org.alfresco.repo.domain.Node; import org.alfresco.repo.domain.NodeAssoc; import org.alfresco.repo.domain.NodeStatus; import org.alfresco.repo.domain.Store; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.InvalidTypeException; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; @@ -76,11 +78,12 @@ public interface NodeDaoService * null is returned. * * @param nodeRef the node reference - * @param create true to create the entity if it doesn't exist + * @param create true if the node status is to be updated in the transaction, i.e. + * the current transaction must be assigned to the status * @return Returns the node status if the node exists or once existed, otherwise * returns null if create == false */ - public NodeStatus getNodeStatus(NodeRef nodeRef, boolean create); + public NodeStatus getNodeStatus(NodeRef nodeRef, boolean update); /** * Sets the current transaction ID on the node status. Note that the node @@ -224,10 +227,10 @@ public interface NodeDaoService public void deleteNodeAssoc(NodeAssoc assoc); /** - * Fetch all content data strings. These are all string values that begin - * with contentUrl=. + * Fetch all property values for the given type definition. This will also dig out values that + * were persisted as type d:any. * - * @return Returns the string values for content data + * @return Returns the values for the given type definition */ - public List getContentDataStrings(); + public List getPropertyValuesByActualType(DataTypeDefinition actualDataTypeDefinition); } diff --git a/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java b/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java index c46fecd530..3c9f349502 100644 --- a/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java +++ b/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java @@ -22,10 +22,8 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import java.util.zip.CRC32; import org.alfresco.error.AlfrescoRuntimeException; @@ -35,6 +33,7 @@ import org.alfresco.repo.domain.Node; import org.alfresco.repo.domain.NodeAssoc; import org.alfresco.repo.domain.NodeKey; import org.alfresco.repo.domain.NodeStatus; +import org.alfresco.repo.domain.PropertyValue; import org.alfresco.repo.domain.Server; import org.alfresco.repo.domain.Store; import org.alfresco.repo.domain.StoreKey; @@ -48,19 +47,25 @@ import org.alfresco.repo.domain.hibernate.StoreImpl; import org.alfresco.repo.domain.hibernate.TransactionImpl; import org.alfresco.repo.node.db.NodeDaoService; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; +import org.alfresco.repo.transaction.TransactionAwareSingleton; import org.alfresco.repo.transaction.TransactionalDao; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.InvalidTypeException; import org.alfresco.service.cmr.repository.AssociationExistsException; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.DuplicateChildNodeNameException; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.cmr.repository.datatype.TypeConverter; import org.alfresco.service.namespace.QName; import org.alfresco.util.GUID; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hibernate.ObjectDeletedException; import org.hibernate.Query; +import org.hibernate.ScrollMode; +import org.hibernate.ScrollableResults; import org.hibernate.Session; import org.springframework.dao.DataAccessException; import org.springframework.dao.DataIntegrityViolationException; @@ -83,7 +88,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements private static final String QUERY_GET_NODE_ASSOCS_TO_AND_FROM = "node.GetNodeAssocsToAndFrom"; private static final String QUERY_GET_TARGET_ASSOCS = "node.GetTargetAssocs"; private static final String QUERY_GET_SOURCE_ASSOCS = "node.GetSourceAssocs"; - private static final String QUERY_GET_CONTENT_DATA_STRINGS = "node.GetContentDataStrings"; + private static final String QUERY_GET_NODES_WITH_PROPERTY_VALUES_BY_ACTUAL_TYPE = "node.GetNodesWithPropertyValuesByActualType"; private static final String QUERY_GET_SERVER_BY_IPADDRESS = "server.getServerByIpAddress"; private static Log logger = LogFactory.getLog(HibernateNodeDaoServiceImpl.class); @@ -91,9 +96,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements /** a uuid identifying this unique instance */ private final String uuid; - private final ReadLock serverReadLock; - private final WriteLock serverWriteLock; - private Server server; + private static TransactionAwareSingleton serverIdSingleton = new TransactionAwareSingleton(); /** * @@ -101,10 +104,6 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements public HibernateNodeDaoServiceImpl() { this.uuid = GUID.generate(); - - ReentrantReadWriteLock serverReadWriteLock = new ReentrantReadWriteLock(); - serverReadLock = serverReadWriteLock.readLock(); - serverWriteLock = serverReadWriteLock.writeLock(); } /** @@ -137,21 +136,16 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements */ private Server getServer() { - // get readlock - serverReadLock.lock(); - try + Long serverId = serverIdSingleton.get(); + Server server = null; + if (serverId != null) { + server = (Server) getSession().get(ServerImpl.class, serverId); if (server != null) { return server; } } - finally - { - serverReadLock.unlock(); - } - // get the write lock - serverWriteLock.lock(); try { final String ipAddress = InetAddress.getLocalHost().getHostAddress(); @@ -185,16 +179,15 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements } } } + // push the value into the singleton + serverIdSingleton.put(server.getId()); + return server; } catch (Exception e) { throw new AlfrescoRuntimeException("Failed to create server instance", e); } - finally - { - serverWriteLock.unlock(); - } } private static final String RESOURCE_KEY_TRANSACTION_ID = "hibernate.transaction.id"; @@ -307,7 +300,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements /** * Fetch the node status, if it exists */ - public NodeStatus getNodeStatus(NodeRef nodeRef, boolean create) + public NodeStatus getNodeStatus(NodeRef nodeRef, boolean update) { NodeKey nodeKey = new NodeKey(nodeRef); NodeStatus status = null; @@ -325,13 +318,18 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements throw e; } // create if necessary - if (status == null && create) + if (status == null && update) { status = new NodeStatusImpl(); status.setKey(nodeKey); status.setTransaction(getCurrentTransaction()); getHibernateTemplate().save(status); } + else if (status != null && update) + { + // update the transaction + status.setTransaction(getCurrentTransaction()); + } // done return status; } @@ -934,18 +932,62 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements getSession().flush(); } - @SuppressWarnings("unchecked") - public List getContentDataStrings() + public List getPropertyValuesByActualType(DataTypeDefinition actualDataTypeDefinition) { + // get the in-database string representation of the actual type + QName typeQName = actualDataTypeDefinition.getName(); + final String actualTypeString = PropertyValue.getActualTypeString(typeQName); HibernateCallback callback = new HibernateCallback() { public Object doInHibernate(Session session) { - Query query = session.getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_CONTENT_DATA_STRINGS); - return query.list(); + Query query = session + .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_NODES_WITH_PROPERTY_VALUES_BY_ACTUAL_TYPE) + .setString("actualTypeString", actualTypeString); + return query.scroll(ScrollMode.FORWARD_ONLY); } }; - List queryResults = (List) getHibernateTemplate().execute(callback); - return queryResults; + ScrollableResults results = (ScrollableResults) getHibernateTemplate().execute(callback); + // Loop through, extracting content URLs + List convertedValues = new ArrayList(1000); + TypeConverter converter = DefaultTypeConverter.INSTANCE; + while(results.next()) + { + Node node = (Node) results.get()[0]; + // loop through all the node properties + Map properties = node.getProperties(); + for (PropertyValue propertyValue : properties.values()) + { + // ignore nulls + if (propertyValue == null) + { + continue; + } + // Get the actual value(s) as a collection + Collection values = propertyValue.getCollection(DataTypeDefinition.ANY); + // attempt to convert instance in the collection + for (Serializable value : values) + { + // ignore nulls (null entries in collections) + if (value == null) + { + continue; + } + try + { + Serializable convertedValue = (Serializable) converter.convert(actualDataTypeDefinition, value); + // it converted, so add it + convertedValues.add(convertedValue); + } + catch (Throwable e) + { + // The value can't be converted - forget it + } + } + } + // evict all data from the session + getSession().clear(); + } + return convertedValues; } } \ No newline at end of file diff --git a/source/java/org/alfresco/repo/node/integrity/IntegrityChecker.java b/source/java/org/alfresco/repo/node/integrity/IntegrityChecker.java index e93ff18425..6f7be2bda8 100644 --- a/source/java/org/alfresco/repo/node/integrity/IntegrityChecker.java +++ b/source/java/org/alfresco/repo/node/integrity/IntegrityChecker.java @@ -88,6 +88,8 @@ public class IntegrityChecker /** key against which the set of events is stored in the current transaction */ private static final String KEY_EVENT_SET = "IntegrityChecker.EventSet"; + /** key to store the local flag to disable integrity errors, i.e. downgrade to warnings */ + private static final String KEY_WARN_IN_TRANSACTION = "IntegrityChecker.WarnInTransaction"; private PolicyComponent policyComponent; private DictionaryService dictionaryService; @@ -97,6 +99,36 @@ public class IntegrityChecker private int maxErrorsPerTransaction; private boolean traceOn; + /** + * Downgrade violations to warnings within the current transaction. This is temporary and + * is dependent on there being a current transaction active against the + * current thread. When set, this will override the global + * {@link #setFailOnViolation(boolean) failure behaviour}. + */ + public static void setWarnInTransaction() + { + AlfrescoTransactionSupport.bindResource(KEY_WARN_IN_TRANSACTION, Boolean.TRUE); + } + + /** + * @return Returns true if the current transaction should only warn on violations. + * If false, the global setting will take effect. + * + * @see #setWarnInTransaction() + */ + public static boolean isWarnInTransaction() + { + Boolean warnInTransaction = (Boolean) AlfrescoTransactionSupport.getResource(KEY_WARN_IN_TRANSACTION); + if (warnInTransaction == null || warnInTransaction == Boolean.FALSE) + { + return false; + } + else + { + return true; + } + } + /** */ public IntegrityChecker() @@ -572,7 +604,8 @@ public class IntegrityChecker } sb.append("\n").append(failure); } - if (failOnViolation) + boolean warnOnly = IntegrityChecker.isWarnInTransaction(); + if (failOnViolation && !warnOnly) { logger.error(sb.toString()); throw new IntegrityException(failures); diff --git a/source/java/org/alfresco/repo/node/integrity/IntegrityTest.java b/source/java/org/alfresco/repo/node/integrity/IntegrityTest.java index 93c9ac253f..724393d11d 100644 --- a/source/java/org/alfresco/repo/node/integrity/IntegrityTest.java +++ b/source/java/org/alfresco/repo/node/integrity/IntegrityTest.java @@ -198,6 +198,18 @@ public class IntegrityTest extends TestCase assertNotNull("Static IntegrityChecker not created", integrityChecker); } + public void testTemporaryDowngrading() throws Exception + { + assertEquals("Per-transaction override not correct", false, IntegrityChecker.isWarnInTransaction()); + // create bad node + NodeRef nodeRef = createNode("abc", TEST_TYPE_WITH_PROPERTIES, null); + // switch it off + IntegrityChecker.setWarnInTransaction(); + assertEquals("Per-transaction override not correct", true, IntegrityChecker.isWarnInTransaction()); + // now, only warnings should occur + checkIntegrityNoFailure(); + } + public void testCreateWithoutProperties() throws Exception { NodeRef nodeRef = createNode("abc", TEST_TYPE_WITH_PROPERTIES, null); diff --git a/source/java/org/alfresco/repo/ownable/impl/OwnableServiceTest.java b/source/java/org/alfresco/repo/ownable/impl/OwnableServiceTest.java index 79b87c0336..088524f6fc 100644 --- a/source/java/org/alfresco/repo/ownable/impl/OwnableServiceTest.java +++ b/source/java/org/alfresco/repo/ownable/impl/OwnableServiceTest.java @@ -141,6 +141,14 @@ public class OwnableServiceTest extends TestCase permissionService.setInheritParentPermissions(testNode, false); + + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(rootNodeRef, PermissionService.TAKE_OWNERSHIP)); + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(rootNodeRef, PermissionService.SET_OWNER)); + assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(testNode, PermissionService.TAKE_OWNERSHIP)); + assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(testNode, PermissionService.SET_OWNER)); + + permissionService.setPermission(rootNodeRef, "andy", PermissionService.WRITE_PROPERTIES, true); + assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(rootNodeRef, PermissionService.TAKE_OWNERSHIP)); assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(rootNodeRef, PermissionService.SET_OWNER)); assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(testNode, PermissionService.TAKE_OWNERSHIP)); diff --git a/source/java/org/alfresco/repo/policy/TransactionBehaviourQueue.java b/source/java/org/alfresco/repo/policy/TransactionBehaviourQueue.java index bf46e807aa..f317dd14ef 100644 --- a/source/java/org/alfresco/repo/policy/TransactionBehaviourQueue.java +++ b/source/java/org/alfresco/repo/policy/TransactionBehaviourQueue.java @@ -25,7 +25,6 @@ import java.util.Queue; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.repo.policy.Policy.Arg; -import org.alfresco.repo.rule.RuleTransactionListener; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.TransactionListener; import org.alfresco.util.GUID; @@ -214,7 +213,7 @@ public class TransactionBehaviourQueue implements TransactionListener { return true; } - if (obj instanceof RuleTransactionListener) + if (obj instanceof TransactionBehaviourQueue) { TransactionBehaviourQueue that = (TransactionBehaviourQueue) obj; return (this.id.equals(that.id)); diff --git a/source/java/org/alfresco/repo/search/IndexerSPI.java b/source/java/org/alfresco/repo/search/IndexerSPI.java index 82a6e311ee..695c177901 100644 --- a/source/java/org/alfresco/repo/search/IndexerSPI.java +++ b/source/java/org/alfresco/repo/search/IndexerSPI.java @@ -22,6 +22,6 @@ public interface IndexerSPI extends Indexer { public void registerCallBack(FTSIndexerAware callBack); - public void updateFullTextSearch(int i); + public int updateFullTextSearch(int i); } diff --git a/source/java/org/alfresco/repo/search/impl/lucene/LuceneAnalyser.java b/source/java/org/alfresco/repo/search/impl/lucene/LuceneAnalyser.java index d810438117..593436647a 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/LuceneAnalyser.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/LuceneAnalyser.java @@ -29,13 +29,13 @@ import org.alfresco.service.namespace.QName; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.WhitespaceAnalyzer; -import org.apache.lucene.analysis.standard.StandardAnalyzer; +import org.alfresco.repo.search.impl.lucene.analysis.AlfrescoStandardAnalyser; /** * Analyse properties according to the property definition. * * The default is to use the standard tokeniser. The tokeniser should not have - * been called when indexeing properties that require no tokenisation. (tokenise + * been called when indexing properties that require no tokenisation. (tokenise * should be set to false when adding the field to the document) * * @author andyh @@ -60,7 +60,7 @@ public class LuceneAnalyser extends Analyzer */ public LuceneAnalyser(DictionaryService dictionaryService) { - this(new StandardAnalyzer()); + this(new AlfrescoStandardAnalyser()); this.dictionaryService = dictionaryService; } diff --git a/source/java/org/alfresco/repo/search/impl/lucene/LuceneIndexerImpl.java b/source/java/org/alfresco/repo/search/impl/lucene/LuceneIndexerImpl.java index fc1c41ec58..91310ecfcd 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/LuceneIndexerImpl.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/LuceneIndexerImpl.java @@ -1696,13 +1696,13 @@ public class LuceneIndexerImpl extends LuceneBase implements LuceneIndexer return false; } - public void updateFullTextSearch(int size) throws LuceneIndexException + public int updateFullTextSearch(int size) throws LuceneIndexException { checkAbleToDoWork(true, false); if (!mainIndexExists()) { remainingCount = size; - return; + return 0; } try { @@ -1723,7 +1723,7 @@ public class LuceneIndexerImpl extends LuceneBase implements LuceneIndexer if(searcher == null) { remainingCount = size; - return; + return 0; } Hits hits; try @@ -1817,7 +1817,9 @@ public class LuceneIndexerImpl extends LuceneBase implements LuceneIndexer } } - remainingCount = count - writer.docCount(); + int done = writer.docCount(); + remainingCount = count - done; + return done; } catch (LuceneIndexException e) { @@ -1825,8 +1827,14 @@ public class LuceneIndexerImpl extends LuceneBase implements LuceneIndexer { closeDeltaWriter(); } + return 0; } } + else + { + return 0; + } + } catch (LuceneIndexException e) { diff --git a/source/java/org/alfresco/repo/search/impl/lucene/LuceneIndexerImpl2.java b/source/java/org/alfresco/repo/search/impl/lucene/LuceneIndexerImpl2.java index 8639fbf790..d0f6038d94 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/LuceneIndexerImpl2.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/LuceneIndexerImpl2.java @@ -1748,7 +1748,7 @@ public class LuceneIndexerImpl2 extends LuceneBase2 implements LuceneIndexer2 return false; } - public void updateFullTextSearch(int size) throws LuceneIndexException + public int updateFullTextSearch(int size) throws LuceneIndexException { checkAbleToDoWork(true, false); // if (!mainIndexExists()) @@ -1775,7 +1775,7 @@ public class LuceneIndexerImpl2 extends LuceneBase2 implements LuceneIndexer2 if (searcher == null) { remainingCount = size; - return; + return 0; } Hits hits; try @@ -1869,7 +1869,9 @@ public class LuceneIndexerImpl2 extends LuceneBase2 implements LuceneIndexer2 } } - remainingCount = count - writer.docCount(); + int done = writer.docCount(); + remainingCount = count - done; + return done; } catch (LuceneIndexException e) { @@ -1877,8 +1879,13 @@ public class LuceneIndexerImpl2 extends LuceneBase2 implements LuceneIndexer2 { closeDeltaWriter(); } + return 0; } } + else + { + return 0; + } } catch (IOException e) { diff --git a/source/java/org/alfresco/repo/search/impl/lucene/LuceneQueryParser.java b/source/java/org/alfresco/repo/search/impl/lucene/LuceneQueryParser.java index 8f2f6d70a4..aa7315956f 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/LuceneQueryParser.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/LuceneQueryParser.java @@ -156,7 +156,25 @@ public class LuceneQueryParser extends QueryParser } else if (field.equals("TYPE")) { - TypeDefinition target = dictionaryService.getType(QName.createQName(queryText)); + TypeDefinition target; + if(queryText.startsWith("{")) + { + target = dictionaryService.getType(QName.createQName(queryText)); + } + else + { + int colonPosition = queryText.indexOf(':'); + if (colonPosition == -1) + { + // use the default namespace + target = dictionaryService.getType(QName.createQName(namespacePrefixResolver.getNamespaceURI(""), queryText)); + } + else + { + // find the prefix + target = dictionaryService.getType(QName.createQName(namespacePrefixResolver.getNamespaceURI(queryText.substring(0, colonPosition)), queryText.substring(colonPosition + 1))); + } + } if (target == null) { throw new SearcherException("Invalid type: " + queryText); @@ -186,7 +204,26 @@ public class LuceneQueryParser extends QueryParser } else if (field.equals("ASPECT")) { - AspectDefinition target = dictionaryService.getAspect(QName.createQName(queryText)); + AspectDefinition target; + if(queryText.startsWith("{")) + { + target = dictionaryService.getAspect(QName.createQName(queryText)); + } + else + { + int colonPosition = queryText.indexOf(':'); + if (colonPosition == -1) + { + // use the default namespace + target = dictionaryService.getAspect(QName.createQName(namespacePrefixResolver.getNamespaceURI(""), queryText)); + } + else + { + // find the prefix + target = dictionaryService.getAspect(QName.createQName(namespacePrefixResolver.getNamespaceURI(queryText.substring(0, colonPosition)), queryText.substring(colonPosition + 1))); + } + } + QName targetQName = target.getName(); HashSet subclasses = new HashSet(); for (QName classRef : dictionaryService.getAllAspects()) diff --git a/source/java/org/alfresco/repo/search/impl/lucene/LuceneTest2.java b/source/java/org/alfresco/repo/search/impl/lucene/LuceneTest2.java index 92a10d0ea9..d44f13a5ee 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/LuceneTest2.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/LuceneTest2.java @@ -37,7 +37,9 @@ import junit.framework.TestCase; import org.alfresco.model.ContentModel; import org.alfresco.repo.dictionary.DictionaryDAO; +import org.alfresco.repo.dictionary.DictionaryNamespaceComponent; import org.alfresco.repo.dictionary.M2Model; +import org.alfresco.repo.dictionary.NamespaceDAOImpl; import org.alfresco.repo.node.BaseNodeServiceTest; import org.alfresco.repo.search.QueryParameterDefImpl; import org.alfresco.repo.search.QueryRegisterComponent; @@ -159,7 +161,7 @@ public class LuceneTest2 extends TestCase private QueryRegisterComponent queryRegisterComponent; - private NamespacePrefixResolver namespacePrefixResolver; + private DictionaryNamespaceComponent namespacePrefixResolver; private LuceneIndexerAndSearcher indexerAndSearcher; @@ -171,6 +173,8 @@ public class LuceneTest2 extends TestCase private NodeRef[] documentOrder; + private NamespaceDAOImpl namespaceDao; + public LuceneTest2() { super(); @@ -185,10 +189,13 @@ public class LuceneTest2 extends TestCase luceneFTS = (FullTextSearchIndexer) ctx.getBean("LuceneFullTextSearchIndexer"); contentService = (ContentService) ctx.getBean("contentService"); queryRegisterComponent = (QueryRegisterComponent) ctx.getBean("queryRegisterComponent"); - namespacePrefixResolver = (NamespacePrefixResolver) ctx.getBean("namespaceService"); + namespacePrefixResolver = (DictionaryNamespaceComponent) ctx.getBean("namespaceService"); indexerAndSearcher = (LuceneIndexerAndSearcher) ctx.getBean("luceneIndexerAndSearcherFactory"); transactionService = (TransactionService) ctx.getBean("transactionComponent"); serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY); + + namespaceDao = (NamespaceDAOImpl) ctx.getBean("namespaceDAO"); + this.authenticationComponent = (AuthenticationComponent) ctx.getBean("authenticationComponent"); @@ -208,7 +215,9 @@ public class LuceneTest2 extends TestCase assertNotNull(modelStream); M2Model model = M2Model.createModel(modelStream); dictionaryDAO.putModel(model); - + + namespaceDao.addPrefix("test", TEST_NAMESPACE); + StoreRef storeRef = nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.currentTimeMillis()); rootNodeRef = nodeService.getRootNode(storeRef); @@ -1861,19 +1870,39 @@ public class LuceneTest2 extends TestCase null); assertEquals(1, results.length()); results.close(); + + results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "TYPE:\"" + testType.toPrefixString(namespacePrefixResolver) + "\"", null, + null); + assertEquals(1, results.length()); + results.close(); results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "TYPE:\"" + testSuperType.toString() + "\"", null, null); assertEquals(13, results.length()); results.close(); + + results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "TYPE:\"" + testSuperType.toPrefixString(namespacePrefixResolver) + "\"", + null, null); + assertEquals(13, results.length()); + results.close(); results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "ASPECT:\"" - + ISO9075.getXPathName(testAspect) + "\"", null, null); + + testAspect.toString() + "\"", null, null); + assertEquals(1, results.length()); + results.close(); + + results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "ASPECT:\"" + + testAspect.toPrefixString(namespacePrefixResolver) + "\"", null, null); assertEquals(1, results.length()); results.close(); results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "ASPECT:\"" - + ISO9075.getXPathName(testSuperAspect) + "\"", null, null); + + testAspect.toString() + "\"", null, null); + assertEquals(1, results.length()); + results.close(); + + results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "ASPECT:\"" + + testAspect.toPrefixString(namespacePrefixResolver) + "\"", null, null); assertEquals(1, results.length()); results.close(); diff --git a/source/java/org/alfresco/repo/search/impl/lucene/analysis/AlfrescoStandardAnalyser.java b/source/java/org/alfresco/repo/search/impl/lucene/analysis/AlfrescoStandardAnalyser.java new file mode 100644 index 0000000000..2f67d5567c --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/lucene/analysis/AlfrescoStandardAnalyser.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.search.impl.lucene.analysis; + +import java.io.Reader; +import java.util.Set; + +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.LowerCaseFilter; +import org.apache.lucene.analysis.StopAnalyzer; +import org.apache.lucene.analysis.StopFilter; +import org.apache.lucene.analysis.TokenStream; +import org.apache.lucene.analysis.standard.StandardFilter; +import org.apache.lucene.analysis.standard.StandardTokenizer; + + +public class AlfrescoStandardAnalyser extends Analyzer +{ + private Set stopSet; + + /** + * An array containing some common English words that are usually not useful for searching. + */ + public static final String[] STOP_WORDS = StopAnalyzer.ENGLISH_STOP_WORDS; + + /** Builds an analyzer. */ + public AlfrescoStandardAnalyser() + { + this(STOP_WORDS); + } + + /** Builds an analyzer with the given stop words. */ + public AlfrescoStandardAnalyser(String[] stopWords) + { + stopSet = StopFilter.makeStopSet(stopWords); + } + + /** + * Constructs a {@link StandardTokenizer} filtered by a {@link StandardFilter}, a {@link LowerCaseFilter} and a {@link StopFilter}. + */ + public TokenStream tokenStream(String fieldName, Reader reader) + { + TokenStream result = new StandardTokenizer(reader); + result = new AlfrescoStandardFilter(result); + result = new LowerCaseFilter(result); + result = new StopFilter(result, stopSet); + return result; + } +} diff --git a/source/java/org/alfresco/repo/search/impl/lucene/analysis/AlfrescoStandardFilter.java b/source/java/org/alfresco/repo/search/impl/lucene/analysis/AlfrescoStandardFilter.java new file mode 100644 index 0000000000..6006e4442c --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/lucene/analysis/AlfrescoStandardFilter.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.search.impl.lucene.analysis; + +import java.util.LinkedList; +import java.util.Queue; +import java.util.Stack; +import java.util.StringTokenizer; + +import org.apache.lucene.analysis.TokenFilter; +import org.apache.lucene.analysis.TokenStream; +import org.apache.lucene.analysis.standard.StandardTokenizerConstants; + +public class AlfrescoStandardFilter extends TokenFilter implements StandardTokenizerConstants +{ + + /** Construct filtering in. */ + public AlfrescoStandardFilter(TokenStream in) + { + super(in); + } + + private static final String APOSTROPHE_TYPE = tokenImage[APOSTROPHE]; + + private static final String ACRONYM_TYPE = tokenImage[ACRONYM]; + + private static final String HOST_TYPE = tokenImage[HOST]; + + private static final String ALPHANUM_TYPE = tokenImage[ALPHANUM]; + + private Queue hostTokens = null; + + /** + * Returns the next token in the stream, or null at EOS. + *

+ * Removes 's from the end of words. + *

+ * Removes dots from acronyms. + *

+ * Splits host names ... + */ + public final org.apache.lucene.analysis.Token next() throws java.io.IOException + { + if (hostTokens == null) + { + org.apache.lucene.analysis.Token t = input.next(); + + if (t == null) + return null; + + String text = t.termText(); + String type = t.type(); + + if (type == APOSTROPHE_TYPE && // remove 's + (text.endsWith("'s") || text.endsWith("'S"))) + { + return new org.apache.lucene.analysis.Token(text.substring(0, text.length() - 2), t.startOffset(), t + .endOffset(), type); + + } + else if (type == ACRONYM_TYPE) + { // remove dots + StringBuffer trimmed = new StringBuffer(); + for (int i = 0; i < text.length(); i++) + { + char c = text.charAt(i); + if (c != '.') + trimmed.append(c); + } + return new org.apache.lucene.analysis.Token(trimmed.toString(), t.startOffset(), t.endOffset(), type); + + } + else if (type == HOST_TYPE) + { + // ("." )+ > + // There must be at least two tokens .... + hostTokens = new LinkedList(); + StringTokenizer tokeniser = new StringTokenizer(text, "."); + int start = t.startOffset(); + int end; + while (tokeniser.hasMoreTokens()) + { + String token = tokeniser.nextToken(); + end = start + token.length(); + hostTokens.offer(new org.apache.lucene.analysis.Token(token, start, end, ALPHANUM_TYPE)); + start = end + 1; + } + // check if we have an acronym ..... yes a.b.c ends up here ... + + if (text.length() == hostTokens.size() * 2 - 1) + { + hostTokens = null; + // acronym + StringBuffer trimmed = new StringBuffer(); + for (int i = 0; i < text.length(); i++) + { + char c = text.charAt(i); + if (c != '.') + trimmed.append(c); + } + return new org.apache.lucene.analysis.Token(trimmed.toString(), t.startOffset(), t.endOffset(), + ALPHANUM_TYPE); + } + else + { + return hostTokens.remove(); + } + } + else + { + return t; + } + } + else + { + org.apache.lucene.analysis.Token token = hostTokens.remove(); + if (hostTokens.isEmpty()) + { + hostTokens = null; + } + return token; + } + } +} diff --git a/source/java/org/alfresco/repo/search/impl/lucene/fts/FullTextSearchIndexerImpl.java b/source/java/org/alfresco/repo/search/impl/lucene/fts/FullTextSearchIndexerImpl.java index def19125b7..edce57ef06 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/fts/FullTextSearchIndexerImpl.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/fts/FullTextSearchIndexerImpl.java @@ -31,7 +31,8 @@ import org.springframework.context.support.ClassPathXmlApplicationContext; public class FullTextSearchIndexerImpl implements FTSIndexerAware, FullTextSearchIndexer { - private enum State { + private enum State + { ACTIVE, PAUSING, PAUSED }; @@ -48,7 +49,7 @@ public class FullTextSearchIndexerImpl implements FTSIndexerAware, FullTextSearc public FullTextSearchIndexerImpl() { super(); - //System.out.println("Created id is "+this); + // System.out.println("Created id is "+this); } /* @@ -64,8 +65,7 @@ public class FullTextSearchIndexerImpl implements FTSIndexerAware, FullTextSearc /* * (non-Javadoc) * - * @see org.alfresco.repo.search.impl.lucene.fts.FullTextSearchIndexer#indexCompleted(org.alfresco.repo.ref.StoreRef, - * int, java.lang.Exception) + * @see org.alfresco.repo.search.impl.lucene.fts.FullTextSearchIndexer#indexCompleted(org.alfresco.repo.ref.StoreRef, int, java.lang.Exception) */ public synchronized void indexCompleted(StoreRef storeRef, int remaining, Exception e) { @@ -83,7 +83,7 @@ public class FullTextSearchIndexerImpl implements FTSIndexerAware, FullTextSearc } finally { - //System.out.println("..Index Complete: id is "+this); + // System.out.println("..Index Complete: id is "+this); this.notifyAll(); } } @@ -96,19 +96,19 @@ public class FullTextSearchIndexerImpl implements FTSIndexerAware, FullTextSearc public synchronized void pause() throws InterruptedException { pauseCount++; - //System.out.println("..Waiting "+pauseCount+" id is "+this); + // System.out.println("..Waiting "+pauseCount+" id is "+this); while ((indexing.size() > 0)) { - //System.out.println("Pause: Waiting with count of "+indexing.size()+" id is "+this); + // System.out.println("Pause: Waiting with count of "+indexing.size()+" id is "+this); this.wait(); } pauseCount--; - if(pauseCount == 0) + if (pauseCount == 0) { paused = true; this.notifyAll(); // only resumers } - //System.out.println("..Remaining "+pauseCount +" paused = "+paused+" id is "+this); + // System.out.println("..Remaining "+pauseCount +" paused = "+paused+" id is "+this); } /* @@ -118,16 +118,16 @@ public class FullTextSearchIndexerImpl implements FTSIndexerAware, FullTextSearc */ public synchronized void resume() throws InterruptedException { - if(pauseCount == 0) + if (pauseCount == 0) { - //System.out.println("Direct resume"+" id is "+this); + // System.out.println("Direct resume"+" id is "+this); paused = false; } - else + else { - while(pauseCount > 0) + while (pauseCount > 0) { - //System.out.println("Reusme waiting on "+pauseCount+" id is "+this); + // System.out.println("Reusme waiting on "+pauseCount+" id is "+this); this.wait(); } paused = false; @@ -136,13 +136,13 @@ public class FullTextSearchIndexerImpl implements FTSIndexerAware, FullTextSearc private synchronized boolean isPaused() throws InterruptedException { - if(pauseCount == 0) + if (pauseCount == 0) { - return paused; + return paused; } - else + else { - while(pauseCount > 0) + while (pauseCount > 0) { this.wait(); } @@ -160,17 +160,22 @@ public class FullTextSearchIndexerImpl implements FTSIndexerAware, FullTextSearc // Use the calling thread to index // Parallel indexing via multiple Quartz thread initiating indexing - StoreRef toIndex = getNextRef(); - if (toIndex != null) + int done = 0; + while (done == 0) { - //System.out.println("Indexing "+toIndex+" at "+(new java.util.Date())); - IndexerSPI indexer = luceneIndexerAndSearcherFactory.getIndexer(toIndex); - indexer.registerCallBack(this); - indexer.updateFullTextSearch(1000); - } - else - { - //System.out.println("Nothing to Indexing at "+(new java.util.Date())); + StoreRef toIndex = getNextRef(); + if (toIndex != null) + { + // System.out.println("Indexing "+toIndex+" at "+(new java.util.Date())); + IndexerSPI indexer = luceneIndexerAndSearcherFactory.getIndexer(toIndex); + indexer.registerCallBack(this); + done += indexer.updateFullTextSearch(1000); + } + else + { + break; + // System.out.println("Nothing to Indexing at "+(new java.util.Date())); + } } } @@ -178,7 +183,7 @@ public class FullTextSearchIndexerImpl implements FTSIndexerAware, FullTextSearc { if (paused || (pauseCount > 0)) { - //System.out.println("Indexing suspended"+" id is "+this); + // System.out.println("Indexing suspended"+" id is "+this); return null; } @@ -189,6 +194,8 @@ public class FullTextSearchIndexerImpl implements FTSIndexerAware, FullTextSearc if (!indexing.contains(ref)) { nextStoreRef = ref; + // FIFO + break; } } diff --git a/source/java/org/alfresco/repo/search/impl/lucene/index/IndexInfo.java b/source/java/org/alfresco/repo/search/impl/lucene/index/IndexInfo.java index 0408317939..d049131c30 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/index/IndexInfo.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/index/IndexInfo.java @@ -52,7 +52,7 @@ import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.util.GUID; import org.apache.log4j.Logger; import org.apache.lucene.analysis.Analyzer; -import org.apache.lucene.analysis.standard.StandardAnalyzer; +import org.alfresco.repo.search.impl.lucene.analysis.AlfrescoStandardAnalyser; import org.apache.lucene.document.Document; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexWriter; @@ -306,7 +306,7 @@ public class IndexInfo IndexWriter writer; try { - writer = new IndexWriter(oldIndex, new StandardAnalyzer(), false); + writer = new IndexWriter(oldIndex, new AlfrescoStandardAnalyser(), false); writer.setUseCompoundFile(writerUseCompoundFile); writer.minMergeDocs = writerMinMergeDocs; writer.mergeFactor = writerMergeFactor; @@ -442,7 +442,7 @@ public class IndexInfo IndexWriter writer; try { - writer = new IndexWriter(emptyIndex, new StandardAnalyzer(), true); + writer = new IndexWriter(emptyIndex, new AlfrescoStandardAnalyser(), true); writer.setUseCompoundFile(writerUseCompoundFile); writer.minMergeDocs = writerMinMergeDocs; writer.mergeFactor = writerMergeFactor; @@ -2424,11 +2424,11 @@ public class IndexInfo if (docCount < maxDocsForInMemoryMerge) { ramDirectory = new RAMDirectory(); - writer = new IndexWriter(ramDirectory, new StandardAnalyzer(), true); + writer = new IndexWriter(ramDirectory, new AlfrescoStandardAnalyser(), true); } else { - writer = new IndexWriter(location, new StandardAnalyzer(), true); + writer = new IndexWriter(location, new AlfrescoStandardAnalyser(), true); } writer.setUseCompoundFile(mergerUseCompoundFile); diff --git a/source/java/org/alfresco/repo/search/impl/lucene/index/IndexInfoTest.java b/source/java/org/alfresco/repo/search/impl/lucene/index/IndexInfoTest.java index 3770a4f39e..7f48f2748a 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/index/IndexInfoTest.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/index/IndexInfoTest.java @@ -25,7 +25,7 @@ import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.util.GUID; import org.alfresco.util.TempFileProvider; -import org.apache.lucene.analysis.standard.StandardAnalyzer; +import org.alfresco.repo.search.impl.lucene.analysis.AlfrescoStandardAnalyser; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.index.IndexReader; @@ -95,7 +95,7 @@ public static final String[] UPDATE_LIST_2 = { "alpha2", "bravo2", "charlie2", " String guid = GUID.generate(); ii.setStatus(guid, TransactionStatus.ACTIVE, null, null); - IndexWriter writer = ii.getDeltaIndexWriter(guid, new StandardAnalyzer()); + IndexWriter writer = ii.getDeltaIndexWriter(guid, new AlfrescoStandardAnalyser()); Document doc = new Document(); for (int k = 0; k < 15; k++) @@ -193,7 +193,7 @@ public static final String[] UPDATE_LIST_2 = { "alpha2", "bravo2", "charlie2", " String guid = GUID.generate(); ii.setStatus(guid, TransactionStatus.ACTIVE, null, null); - IndexWriter writer = ii.getDeltaIndexWriter(guid, new StandardAnalyzer()); + IndexWriter writer = ii.getDeltaIndexWriter(guid, new AlfrescoStandardAnalyser()); Document doc = new Document(); for (int k = 0; k < 15; k++) @@ -383,7 +383,7 @@ public static final String[] UPDATE_LIST_2 = { "alpha2", "bravo2", "charlie2", " String guid = GUID.generate(); ii.setStatus(guid, TransactionStatus.ACTIVE, null, null); - IndexWriter writer = ii.getDeltaIndexWriter(guid, new StandardAnalyzer()); + IndexWriter writer = ii.getDeltaIndexWriter(guid, new AlfrescoStandardAnalyser()); Document doc = new Document(); for (int k = 0; k < 15; k++) @@ -469,7 +469,7 @@ public static final String[] UPDATE_LIST_2 = { "alpha2", "bravo2", "charlie2", " String guid = GUID.generate(); ii.setStatus(guid, TransactionStatus.ACTIVE, null, null); - IndexWriter writer = ii.getDeltaIndexWriter(guid, new StandardAnalyzer()); + IndexWriter writer = ii.getDeltaIndexWriter(guid, new AlfrescoStandardAnalyser()); Document doc = new Document(); for (int k = 0; k < 15; k++) @@ -649,7 +649,7 @@ public static final String[] UPDATE_LIST_2 = { "alpha2", "bravo2", "charlie2", " String guid = GUID.generate(); ii.setStatus(guid, TransactionStatus.ACTIVE, null, null); - IndexWriter writer = ii.getDeltaIndexWriter(guid, new StandardAnalyzer()); + IndexWriter writer = ii.getDeltaIndexWriter(guid, new AlfrescoStandardAnalyser()); Document doc = new Document(); for (int k = 0; k < 15; k++) @@ -740,7 +740,7 @@ public static final String[] UPDATE_LIST_2 = { "alpha2", "bravo2", "charlie2", " String guid = GUID.generate(); ii.setStatus(guid, TransactionStatus.ACTIVE, null, null); - IndexWriter writer = ii.getDeltaIndexWriter(guid, new StandardAnalyzer()); + IndexWriter writer = ii.getDeltaIndexWriter(guid, new AlfrescoStandardAnalyser()); Document doc = new Document(); for (int k = 0; k < 15; k++) diff --git a/source/java/org/alfresco/repo/security/authentication/DefaultMutableAuthenticationDao.java b/source/java/org/alfresco/repo/security/authentication/DefaultMutableAuthenticationDao.java index 553c548ec2..c30669cc67 100644 --- a/source/java/org/alfresco/repo/security/authentication/DefaultMutableAuthenticationDao.java +++ b/source/java/org/alfresco/repo/security/authentication/DefaultMutableAuthenticationDao.java @@ -25,219 +25,359 @@ import org.alfresco.error.AlfrescoRuntimeException; import org.springframework.dao.DataAccessException; /** - * An authority DAO that has no implementation and should not be called. + * An authority DAO that has no implementation. + * + * By default it will throw an exception if any method is called. + * + * Any of the getter/setter methods can be enabled with a no action implementation. + * + * This can support deleting users via the UI for LDAP and NTLM. The Alfresco person object is deleted from the UI. + * The call to delete the user will return with no action. + * + * The following methods will always fail. + * + * getMD4HashedPassword(String userName) + * loadUserByUsername(String arg0) + * getSalt(UserDetails user) * * @author Andy Hind */ public class DefaultMutableAuthenticationDao implements MutableAuthenticationDao { - + private boolean allowCreateUser = false; + + private boolean allowUpdateUser = false; + + private boolean allowDeleteUser = false; + + private boolean allowSetEnabled = false; + + private boolean allowGetEnabled = false; + + private boolean allowSetAccountExpires = false; + + private boolean allowGetAccountHasExpired = false; + + private boolean allowSetCredentialsExpire = false; + + private boolean allowGetCredentialsExpire = false; + + private boolean allowGetCredentialsHaveExpired = false; + + private boolean allowSetAccountLocked = false; + + private boolean allowGetAccountLocked = false; + + private boolean allowSetAccountExpiryDate = false; + + private boolean allowGetAccountExpiryDate = false; + + private boolean allowSetCredentialsExpiryDate = false; + + private boolean allowGetCredentialsExpiryDate = false; /** * Create a user with the given userName and password * + * If enabled does nothing. + * * @param userName * @param rawPassword * @throws AuthenticationException */ public void createUser(String userName, char[] rawPassword) throws AuthenticationException { - throw new AlfrescoRuntimeException("Not implemented"); + if (!allowCreateUser) + { + throw new AlfrescoRuntimeException("Create User is not supported"); + } } - + /** * Update a user's password. * + * If enabled does nothing. + * * @param userName * @param rawPassword * @throws AuthenticationException */ public void updateUser(String userName, char[] rawPassword) throws AuthenticationException { - throw new AlfrescoRuntimeException("Not implemented"); + if (!allowUpdateUser) + { + throw new AlfrescoRuntimeException("Update user is not supported"); + } } - + /** * Delete a user. * + * If enabled does nothing. + * * @param userName * @throws AuthenticationException */ public void deleteUser(String userName) throws AuthenticationException { - throw new AlfrescoRuntimeException("Not implemented"); + if (!allowDeleteUser) + { + throw new AlfrescoRuntimeException("Delete user is not supported"); + } } - + /** * Check is a user exists. * + * If enabled returns true. + * * @param userName * @return */ public boolean userExists(String userName) { + // All users may exist return true; } - + /** * Enable/disable a user. * + * If enabled does nothing. + * * @param userName * @param enabled */ public void setEnabled(String userName, boolean enabled) { - throw new AlfrescoRuntimeException("Not implemented"); + if (!allowSetEnabled) + { + throw new AlfrescoRuntimeException("Set enabled is not supported"); + } } - + /** * Getter for user enabled * + * If enabled returns true. + * * @param userName * @return */ public boolean getEnabled(String userName) { - throw new AlfrescoRuntimeException("Not implemented"); - + if (!allowGetEnabled) + { + throw new AlfrescoRuntimeException("Get enabled is not supported"); + } + return true; } - + /** * Set if the account should expire * + * If enabled does nothing. + * * @param userName * @param expires */ public void setAccountExpires(String userName, boolean expires) { - throw new AlfrescoRuntimeException("Not implemented"); + if (!allowSetAccountExpires) + { + throw new AlfrescoRuntimeException("Set account expires is not supported"); + } } - + /** * Does the account expire? * + * If enabled returns false. + * * @param userName * @return */ - + public boolean getAccountExpires(String userName) { - throw new AlfrescoRuntimeException("Not implemented"); + if (!allowSetAccountExpires) + { + throw new AlfrescoRuntimeException("Get account expires is not supported"); + } + return false; } - + /** * Has the account expired? * + * If enabled returns false. + * * @param userName * @return */ public boolean getAccountHasExpired(String userName) { - throw new AlfrescoRuntimeException("Not implemented"); + if (!allowGetAccountHasExpired) + { + throw new AlfrescoRuntimeException("Get account has expired is not supported"); + } + return false; } - + /** * Set if the password expires. * + * If enabled does nothing. + * * @param userName * @param expires */ public void setCredentialsExpire(String userName, boolean expires) { - throw new AlfrescoRuntimeException("Not implemented"); + if (!allowSetCredentialsExpire) + { + throw new AlfrescoRuntimeException("Set credentials expire is not supported"); + } } - + /** * Do the credentials for the user expire? * + * If enabled returns false. + * * @param userName * @return */ public boolean getCredentialsExpire(String userName) { - throw new AlfrescoRuntimeException("Not implemented"); + if (!allowGetCredentialsExpire) + { + throw new AlfrescoRuntimeException("Get credentials expire is not supported"); + } + return false; } - + /** * Have the credentials for the user expired? * + * If enabled returns false. + * * @param userName * @return */ public boolean getCredentialsHaveExpired(String userName) { - throw new AlfrescoRuntimeException("Not implemented"); + if (!allowGetCredentialsHaveExpired) + { + throw new AlfrescoRuntimeException("Get credentials have expired is not supported"); + } + return false; } - + /** * Set if the account is locked. * + * If enabled does nothing. + * * @param userName * @param locked */ public void setLocked(String userName, boolean locked) { - throw new AlfrescoRuntimeException("Not implemented"); + if (!allowSetAccountLocked) + { + throw new AlfrescoRuntimeException("Set account locked is not supported"); + } } - + /** * Is the account locked? * + * If enabled returns false. + * * @param userName * @return */ public boolean getAccountlocked(String userName) { - throw new AlfrescoRuntimeException("Not implemented"); + if (!allowGetAccountLocked) + { + throw new AlfrescoRuntimeException("Get account locked is not supported"); + } + return false; } - + /** * Set the date on which the account expires * + * If enabled does nothing. + * * @param userName * @param exipryDate */ public void setAccountExpiryDate(String userName, Date exipryDate) { - throw new AlfrescoRuntimeException("Not implemented"); + if (!allowSetAccountExpiryDate) + { + throw new AlfrescoRuntimeException("Set account expiry date is not supported"); + } } - - /** + + /** * Get the date when this account expires. * + * If enabled returns null. + * * @param userName * @return */ public Date getAccountExpiryDate(String userName) { - throw new AlfrescoRuntimeException("Not implemented"); + if (!allowGetAccountExpiryDate) + { + throw new AlfrescoRuntimeException("Get account expiry date is not supported"); + } + return null; } - + /** * Set the date when credentials expire. * + * If enabled does nothing. + * * @param userName * @param exipryDate */ public void setCredentialsExpiryDate(String userName, Date exipryDate) { - throw new AlfrescoRuntimeException("Not implemented"); + if (!allowSetCredentialsExpiryDate) + { + throw new AlfrescoRuntimeException("Set credentials expiry date is not supported"); + } } - + /** * Get the date when the credentials/password expire. * + * If enabled returns null. + * * @param userName * @return */ public Date getCredentialsExpiryDate(String userName) { - throw new AlfrescoRuntimeException("Not implemented"); + if (!allowGetCredentialsExpiryDate) + { + throw new AlfrescoRuntimeException("Get credentials expiry date is not supported"); + } + return null; } - + /** * Get the MD4 password hash * + * Always throws an exception. + * * @param userName * @return */ @@ -249,7 +389,10 @@ public class DefaultMutableAuthenticationDao implements MutableAuthenticationDao /** * Return the user details for the specified user * - * @param user String + * Always throws an exception. + * + * @param user + * String * @return UserDetails * @exception UsernameNotFoundException * @exception DataAccessException @@ -262,11 +405,99 @@ public class DefaultMutableAuthenticationDao implements MutableAuthenticationDao /** * Return salt for user * - * @param user UserDetails + * Always throws an exception. + * + * @param user + * UserDetails * @return Object */ public Object getSalt(UserDetails user) { throw new AlfrescoRuntimeException("Not implemented"); } + + + // -------- // + // Bean IOC // + // -------- // + + public void setAllowCreateUser(boolean allowCreateUser) + { + this.allowCreateUser = allowCreateUser; + } + + public void setAllowDeleteUser(boolean allowDeleteUser) + { + this.allowDeleteUser = allowDeleteUser; + } + + public void setAllowGetAccountExpiryDate(boolean allowGetAccountExpiryDate) + { + this.allowGetAccountExpiryDate = allowGetAccountExpiryDate; + } + + public void setAllowGetAccountHasExpired(boolean allowGetAccountHasExpired) + { + this.allowGetAccountHasExpired = allowGetAccountHasExpired; + } + + public void setAllowGetAccountLocked(boolean allowGetAccountLocked) + { + this.allowGetAccountLocked = allowGetAccountLocked; + } + + public void setAllowGetCredentialsExpire(boolean allowGetCredentialsExpire) + { + this.allowGetCredentialsExpire = allowGetCredentialsExpire; + } + + public void setAllowGetCredentialsExpiryDate(boolean allowGetCredentialsExpiryDate) + { + this.allowGetCredentialsExpiryDate = allowGetCredentialsExpiryDate; + } + + public void setAllowGetCredentialsHaveExpired(boolean allowGetCredentialsHaveExpired) + { + this.allowGetCredentialsHaveExpired = allowGetCredentialsHaveExpired; + } + + public void setAllowGetEnabled(boolean allowGetEnabled) + { + this.allowGetEnabled = allowGetEnabled; + } + + public void setAllowSetAccountExpires(boolean allowSetAccountExpires) + { + this.allowSetAccountExpires = allowSetAccountExpires; + } + + public void setAllowSetAccountExpiryDate(boolean allowSetAccountExpiryDate) + { + this.allowSetAccountExpiryDate = allowSetAccountExpiryDate; + } + + public void setAllowSetAccountLocked(boolean allowSetAccountLocked) + { + this.allowSetAccountLocked = allowSetAccountLocked; + } + + public void setAllowSetCredentialsExpire(boolean allowSetCredentialsExpire) + { + this.allowSetCredentialsExpire = allowSetCredentialsExpire; + } + + public void setAllowSetCredentialsExpiryDate(boolean allowSetCredentialsExpiryDate) + { + this.allowSetCredentialsExpiryDate = allowSetCredentialsExpiryDate; + } + + public void setAllowSetEnabled(boolean allowSetEnabled) + { + this.allowSetEnabled = allowSetEnabled; + } + + public void setAllowUpdateUser(boolean allowUpdateUser) + { + this.allowUpdateUser = allowUpdateUser; + } } diff --git a/source/java/org/alfresco/repo/security/authentication/ldap/LDAPGroupExportSource.java b/source/java/org/alfresco/repo/security/authentication/ldap/LDAPGroupExportSource.java index 07a6ba87fd..171dc4f4c0 100644 --- a/source/java/org/alfresco/repo/security/authentication/ldap/LDAPGroupExportSource.java +++ b/source/java/org/alfresco/repo/security/authentication/ldap/LDAPGroupExportSource.java @@ -91,6 +91,10 @@ public class LDAPGroupExportSource implements ExportSource, InitializingBean private AuthorityDAO authorityDAO; + private boolean errorOnMissingGID; + + private boolean errorOnMissingUID; + public LDAPGroupExportSource() { super(); @@ -146,6 +150,16 @@ public class LDAPGroupExportSource implements ExportSource, InitializingBean this.errorOnMissingMembers = errorOnMissingMembers; } + public void setErrorOnMissingGID(boolean errorOnMissingGID) + { + this.errorOnMissingGID = errorOnMissingGID; + } + + public void setErrorOnMissingUID(boolean errorOnMissingUID) + { + this.errorOnMissingUID = errorOnMissingUID; + } + public void setAuthorityDAO(AuthorityDAO authorityDAO) { this.authorityDAO = authorityDAO; @@ -279,7 +293,7 @@ public class LDAPGroupExportSource implements ExportSource, InitializingBean ContentModel.TYPE_AUTHORITY_CONTAINER.getLocalName(), ContentModel.TYPE_AUTHORITY_CONTAINER .toPrefixString(namespaceService), attrs); - if ((authorityDAO != null ) && authorityDAO.authorityExists(group.gid)) + if ((authorityDAO != null) && authorityDAO.authorityExists(group.gid)) { NodeRef authNodeRef = authorityDAO.getAuthorityNodeRefOrNull(group.gid); if (authNodeRef != null) @@ -295,7 +309,7 @@ public class LDAPGroupExportSource implements ExportSource, InitializingBean .toPrefixString(namespaceService)); } } - + writer.startElement(ContentModel.PROP_AUTHORITY_NAME.getNamespaceURI(), ContentModel.PROP_AUTHORITY_NAME .getLocalName(), ContentModel.PROP_AUTHORITY_NAME.toPrefixString(namespaceService), new AttributesImpl()); @@ -368,8 +382,17 @@ public class LDAPGroupExportSource implements ExportSource, InitializingBean Attribute gidAttribute = attributes.get(groupIdAttributeName); if (gidAttribute == null) { - throw new ExportSourceImporterException( - "Group returned by group search does not have mandatory group id attribute " + attributes); + if (errorOnMissingGID) + { + throw new ExportSourceImporterException( + "Group returned by group search does not have mandatory group id attribute " + + attributes); + } + else + { + s_logger.warn("Missing GID on " + attributes); + continue; + } } String gid = (String) gidAttribute.get(0); @@ -453,7 +476,15 @@ public class LDAPGroupExportSource implements ExportSource, InitializingBean Attribute objectclass = attributes.get("objectclass"); if (objectclass == null) { - throw new ExportSourceImporterException("Failed to find attribute objectclass for DN " + dn); + if (errorOnMissingMembers) + { + throw new ExportSourceImporterException("Failed to find attribute objectclass for DN " + + dn); + } + else + { + continue; + } } for (int i = 0; i < objectclass.size(); i++) { @@ -479,8 +510,18 @@ public class LDAPGroupExportSource implements ExportSource, InitializingBean Attribute groupIdAttribute = attributes.get(groupIdAttributeName); if (groupIdAttribute == null) { - throw new ExportSourceImporterException("Group missing group id attribute DN =" - + dn + " att = " + groupIdAttributeName); + if (errorOnMissingGID) + { + throw new ExportSourceImporterException( + "Group missing group id attribute DN =" + + dn + " att = " + groupIdAttributeName); + } + else + { + s_logger.warn("Group missing group id attribute DN =" + + dn + " att = " + groupIdAttributeName); + continue; + } } id = (String) groupIdAttribute.get(0); } @@ -504,8 +545,18 @@ public class LDAPGroupExportSource implements ExportSource, InitializingBean Attribute userIdAttribute = attributes.get(userIdAttributeName); if (userIdAttribute == null) { - throw new ExportSourceImporterException("User missing user id attribute DN =" - + dn + " att = " + userIdAttributeName); + if (errorOnMissingUID) + { + throw new ExportSourceImporterException( + "User missing user id attribute DN =" + + dn + " att = " + userIdAttributeName); + } + else + { + s_logger.warn("User missing user id attribute DN =" + + dn + " att = " + userIdAttributeName); + continue; + } } id = (String) userIdAttribute.get(0); } @@ -527,7 +578,14 @@ public class LDAPGroupExportSource implements ExportSource, InitializingBean { if (isGroup == null) { - throw new ExportSourceImporterException("Type not recognised for DN" + dn); + if (errorOnMissingMembers) + { + throw new ExportSourceImporterException("Type not recognised for DN" + dn); + } + else + { + continue; + } } else if (isGroup) { @@ -538,7 +596,14 @@ public class LDAPGroupExportSource implements ExportSource, InitializingBean Group child = lookup.get("GROUP_" + id); if (child == null) { + if (errorOnMissingMembers) + { throw new ExportSourceImporterException("Failed to find child group " + id); + } + else + { + continue; + } } if (rootGroups.contains(child)) { @@ -688,7 +753,7 @@ public class LDAPGroupExportSource implements ExportSource, InitializingBean TransactionService txs = (TransactionService) ctx.getBean("transactionComponent"); UserTransaction tx = txs.getUserTransaction(); tx.begin(); - + File file = new File(args[0]); Writer writer = new BufferedWriter(new FileWriter(file)); XMLWriter xmlWriter = createXMLExporter(writer); diff --git a/source/java/org/alfresco/repo/security/authentication/ldap/LDAPInitialDirContextFactoryImpl.java b/source/java/org/alfresco/repo/security/authentication/ldap/LDAPInitialDirContextFactoryImpl.java index af6c4612de..302ea2f178 100644 --- a/source/java/org/alfresco/repo/security/authentication/ldap/LDAPInitialDirContextFactoryImpl.java +++ b/source/java/org/alfresco/repo/security/authentication/ldap/LDAPInitialDirContextFactoryImpl.java @@ -30,17 +30,22 @@ import javax.naming.directory.InitialDirContext; import org.alfresco.repo.security.authentication.AuthenticationException; import org.alfresco.util.ApplicationContextHelper; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; -public class LDAPInitialDirContextFactoryImpl implements LDAPInitialDirContextFactory +public class LDAPInitialDirContextFactoryImpl implements LDAPInitialDirContextFactory, InitializingBean { + private static final Log logger = LogFactory.getLog(LDAPInitialDirContextFactoryImpl.class); + private Map initialDirContextEnvironment = Collections. emptyMap(); static { System.setProperty("javax.security.auth.useSubjectCredentialsOnly", "false"); } - + public LDAPInitialDirContextFactoryImpl() { super(); @@ -87,11 +92,22 @@ public class LDAPInitialDirContextFactoryImpl implements LDAPInitialDirContextFa { throw new AuthenticationException("Null user name provided."); } + + if (principal.length() == 0) + { + throw new AuthenticationException("Empty user name provided."); + } if (credentials == null) { throw new AuthenticationException("No credentials provided."); } + + if (credentials.length() == 0) + { + throw new AuthenticationException("Empty credentials provided."); + } + Hashtable env = new Hashtable(initialDirContextEnvironment.size()); env.putAll(initialDirContextEnvironment); env.put(Context.SECURITY_PRINCIPAL, principal); @@ -187,4 +203,108 @@ public class LDAPInitialDirContextFactoryImpl implements LDAPInitialDirContextFa } + public void afterPropertiesSet() throws Exception + { + // Check Anonymous bind + + Hashtable env = new Hashtable(initialDirContextEnvironment.size()); + env.putAll(initialDirContextEnvironment); + env.remove(Context.SECURITY_PRINCIPAL); + env.remove(Context.SECURITY_CREDENTIALS); + try + { + new InitialDirContext(env); + + logger.warn("LDAP server supports anonymous bind " + env.get(Context.PROVIDER_URL)); + } + catch (javax.naming.AuthenticationException ax) + { + + } + catch (NamingException nx) + { + throw new AuthenticationException("Unable to connect to LDAP Server; check LDAP configuration", nx); + } + + // Simple DN and password + + env = new Hashtable(initialDirContextEnvironment.size()); + env.putAll(initialDirContextEnvironment); + env.put(Context.SECURITY_PRINCIPAL, "daftAsABrush"); + env.put(Context.SECURITY_CREDENTIALS, "daftAsABrush"); + try + { + + new InitialDirContext(env); + + throw new AuthenticationException( + "The ldap server at " + + env.get(Context.PROVIDER_URL) + + " falls back to use anonymous bind if invalid security credentials are presented. This is not supported."); + } + catch (javax.naming.AuthenticationException ax) + { + logger.info("LDAP server does not fall back to anonymous bind for a string uid and password at " + env.get(Context.PROVIDER_URL)); + } + catch (NamingException nx) + { + logger.info("LDAP server does not support simple string user ids and invalid credentials at "+ env.get(Context.PROVIDER_URL)); + } + + // DN and password + + env = new Hashtable(initialDirContextEnvironment.size()); + env.putAll(initialDirContextEnvironment); + env.put(Context.SECURITY_PRINCIPAL, "cn=daftAsABrush,dc=woof"); + env.put(Context.SECURITY_CREDENTIALS, "daftAsABrush"); + try + { + + new InitialDirContext(env); + + throw new AuthenticationException( + "The ldap server at " + + env.get(Context.PROVIDER_URL) + + " falls back to use anonymous bind if invalid security credentials are presented. This is not supported."); + } + catch (javax.naming.AuthenticationException ax) + { + logger.info("LDAP server does not fall back to anonymous bind for a simple dn and password at " + env.get(Context.PROVIDER_URL)); + } + catch (NamingException nx) + { + logger.info("LDAP server does not support simple DN and invalid password at "+ env.get(Context.PROVIDER_URL)); + } + + // Check more if we have a real principal we expect to work + + env = new Hashtable(initialDirContextEnvironment.size()); + env.putAll(initialDirContextEnvironment); + if(env.get(Context.SECURITY_PRINCIPAL) != null) + { + // Correct principal invalid password + + env = new Hashtable(initialDirContextEnvironment.size()); + env.putAll(initialDirContextEnvironment); + env.put(Context.SECURITY_CREDENTIALS, "sdasdasdasdasd123123123"); + try + { + + new InitialDirContext(env); + + throw new AuthenticationException( + "The ldap server at " + + env.get(Context.PROVIDER_URL) + + " falls back to use anonymous bind for a known principal if invalid security credentials are presented. This is not supported."); + } + catch (javax.naming.AuthenticationException ax) + { + logger.info("LDAP server does not fall back to anonymous bind for known principal and invalid credentials at " + env.get(Context.PROVIDER_URL)); + } + catch (NamingException nx) + { + // already donw + } + } + } } diff --git a/source/java/org/alfresco/repo/security/authentication/ldap/LDAPPersonExportSource.java b/source/java/org/alfresco/repo/security/authentication/ldap/LDAPPersonExportSource.java index 422871d5ae..cf383c4dd3 100644 --- a/source/java/org/alfresco/repo/security/authentication/ldap/LDAPPersonExportSource.java +++ b/source/java/org/alfresco/repo/security/authentication/ldap/LDAPPersonExportSource.java @@ -67,6 +67,8 @@ public class LDAPPersonExportSource implements ExportSource private NamespaceService namespaceService; private String defaultHomeFolder; + + private boolean errorOnMissingUID; public LDAPPersonExportSource() { @@ -113,6 +115,11 @@ public class LDAPPersonExportSource implements ExportSource this.attributeMapping = attributeMapping; } + public void setErrorOnMissingUID(boolean errorOnMissingUID) + { + this.errorOnMissingUID = errorOnMissingUID; + } + public void generateExport(XMLWriter writer) { QName nodeUUID = QName.createQName("sys:node-uuid", namespaceService); @@ -161,8 +168,16 @@ public class LDAPPersonExportSource implements ExportSource Attribute uidAttribute = attributes.get(userIdAttributeName); if (uidAttribute == null) { + if(errorOnMissingUID) + { throw new ExportSourceImporterException( "User returned by user search does not have mandatory user id attribute " + attributes); + } + else + { + s_logger.warn("User returned by user search does not have mandatory user id attribute " + attributes); + continue; + } } String uid = (String) uidAttribute.get(0); diff --git a/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceImpl.java b/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceImpl.java index db1725cab5..656967357c 100644 --- a/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceImpl.java +++ b/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceImpl.java @@ -210,7 +210,7 @@ public class PermissionServiceImpl implements PermissionServiceSPI, Initializing throw new IllegalArgumentException("Property 'policyComponent' has not been set"); } - policyComponent.bindClassBehaviour(QName.createQName(NamespaceService.ALFRESCO_URI, "onMoveNode"), ContentModel.ASPECT_AUDITABLE, new JavaBehaviour(this, "onMoveNode")); + policyComponent.bindClassBehaviour(QName.createQName(NamespaceService.ALFRESCO_URI, "onMoveNode"), ContentModel.TYPE_BASE, new JavaBehaviour(this, "onMoveNode")); } diff --git a/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceTest.java b/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceTest.java index 4a620b9173..8f2acca924 100644 --- a/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceTest.java +++ b/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceTest.java @@ -83,6 +83,16 @@ public class PermissionServiceTest extends AbstractPermissionTest allowAndyReadChildren = new SimplePermissionEntry(rootNodeRef, getPermission(PermissionService.READ_CHILDREN), "andy", AccessStatus.ALLOWED); } + + public void testWeSetConsumerOnRootIsNotSupportedByHasPermisssionAsItIsTheWrongType() + { + runAs("andy"); + assertEquals(0, permissionService.getSetPermissions(rootNodeRef).getPermissionEntries().size()); + permissionService.setPermission(new SimplePermissionEntry(rootNodeRef, getPermission(PermissionService.CONSUMER), + "andy", AccessStatus.ALLOWED)); + assertEquals(1, permissionService.getSetPermissions(rootNodeRef).getPermissionEntries().size()); + assertEquals(permissionService.hasPermission(rootNodeRef, (PermissionService.CONSUMER)), AccessStatus.DENIED); + } public void testGetAllSetPermissions() { @@ -417,7 +427,7 @@ public class PermissionServiceTest extends AbstractPermissionTest { Set answer = permissionService.getSettablePermissions(QName.createQName("sys", "base", namespacePrefixResolver)); - assertEquals(21, answer.size()); + assertEquals(36, answer.size()); answer = permissionService.getSettablePermissions(QName.createQName("cm", "ownable", namespacePrefixResolver)); assertEquals(0, answer.size()); @@ -427,29 +437,33 @@ public class PermissionServiceTest extends AbstractPermissionTest answer = permissionService.getSettablePermissions(QName.createQName("cm", "folder", namespacePrefixResolver)); assertEquals(5, answer.size()); + + answer = permissionService.getSettablePermissions(QName.createQName("cm", "monkey", namespacePrefixResolver)); + assertEquals(0, answer.size()); } + public void testGetSettablePermissionsForNode() { QName ownable = QName.createQName("cm", "ownable", namespacePrefixResolver); Set answer = permissionService.getSettablePermissions(rootNodeRef); - assertEquals(25, answer.size()); + assertEquals(42, answer.size()); nodeService.addAspect(rootNodeRef, ownable, null); answer = permissionService.getSettablePermissions(rootNodeRef); - assertEquals(25, answer.size()); + assertEquals(42, answer.size()); nodeService.removeAspect(rootNodeRef, ownable); answer = permissionService.getSettablePermissions(rootNodeRef); - assertEquals(25, answer.size()); + assertEquals(42, answer.size()); } public void testSimplePermissionOnRoot() { runAs("andy"); - assertEquals(25, permissionService.getPermissions(rootNodeRef).size()); + assertEquals(42, permissionService.getPermissions(rootNodeRef).size()); assertEquals(0, countGranted(permissionService.getPermissions(rootNodeRef))); assertEquals(0, permissionService.getAllSetPermissions(rootNodeRef).size()); @@ -462,8 +476,8 @@ public class PermissionServiceTest extends AbstractPermissionTest assertEquals(1, permissionService.getAllSetPermissions(rootNodeRef).size()); runAs("andy"); - assertEquals(25, permissionService.getPermissions(rootNodeRef).size()); - assertEquals(1, countGranted(permissionService.getPermissions(rootNodeRef))); + assertEquals(42, permissionService.getPermissions(rootNodeRef).size()); + assertEquals(2, countGranted(permissionService.getPermissions(rootNodeRef))); assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_PROPERTIES)) == AccessStatus.ALLOWED); runAs("lemur"); @@ -578,8 +592,8 @@ public class PermissionServiceTest extends AbstractPermissionTest permissionService.setPermission(allowAndyRead); runAs("andy"); - assertEquals(25, permissionService.getPermissions(rootNodeRef).size()); - assertEquals(4, countGranted(permissionService.getPermissions(rootNodeRef))); + assertEquals(42, permissionService.getPermissions(rootNodeRef).size()); + assertEquals(7, countGranted(permissionService.getPermissions(rootNodeRef))); assertEquals(1, permissionService.getAllSetPermissions(rootNodeRef).size()); assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ)) == AccessStatus.ALLOWED); diff --git a/source/java/org/alfresco/repo/security/permissions/impl/model/PermissionModel.java b/source/java/org/alfresco/repo/security/permissions/impl/model/PermissionModel.java index e374595c68..d7c6938916 100644 --- a/source/java/org/alfresco/repo/security/permissions/impl/model/PermissionModel.java +++ b/source/java/org/alfresco/repo/security/permissions/impl/model/PermissionModel.java @@ -50,11 +50,7 @@ import org.dom4j.io.SAXReader; import org.springframework.beans.factory.InitializingBean; /** - * The implementation of the model DAO - * - * Reads and stores the top level model information - * - * Encapsulates access to this information + * The implementation of the model DAO Reads and stores the top level model information Encapsulates access to this information * * @author andyh */ @@ -97,16 +93,13 @@ public class PermissionModel implements ModelDAO, InitializingBean private AccessStatus defaultPermission; // Cache granting permissions - private HashMap> grantingPermissions = - new HashMap>(); + private HashMap> grantingPermissions = new HashMap>(); // Cache grantees - private HashMap> granteePermissions = - new HashMap>(); + private HashMap> granteePermissions = new HashMap>(); // Cache the mapping of extended groups to the base - private HashMap groupsToBaseGroup = - new HashMap(); + private HashMap groupsToBaseGroup = new HashMap(); private HashMap uniqueMap; @@ -115,13 +108,13 @@ public class PermissionModel implements ModelDAO, InitializingBean private HashMap permissionGroupMap; private HashMap permissionReferenceMap; - - private Map> cachedTypePermissionsExposed = - new HashMap>(128, 1.0f); - - private Map> cachedTypePermissionsUnexposed = - new HashMap>(128, 1.0f); - + + private Map> cachedTypePermissionsExposed = new HashMap>( + 128, 1.0f); + + private Map> cachedTypePermissionsUnexposed = new HashMap>( + 128, 1.0f); + public PermissionModel() { super(); @@ -145,9 +138,7 @@ public class PermissionModel implements ModelDAO, InitializingBean } /* - * Initialise from file - * - * (non-Javadoc) + * Initialise from file (non-Javadoc) * * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() */ @@ -283,7 +274,7 @@ public class PermissionModel implements ModelDAO, InitializingBean { return getAllPermissionsImpl(type, true); } - + @SuppressWarnings("unchecked") private Set getAllPermissionsImpl(QName type, boolean exposedOnly) { @@ -300,18 +291,22 @@ public class PermissionModel implements ModelDAO, InitializingBean if (permissions == null) { permissions = new LinkedHashSet(); - if (dictionaryService.getClass(type).isAspect()) + ClassDefinition cd = dictionaryService.getClass(type); + if (cd != null) { - addAspectPermissions(type, permissions, exposedOnly); - } - else - { - mergeGeneralAspectPermissions(permissions, exposedOnly); - addTypePermissions(type, permissions, exposedOnly); + if (cd.isAspect()) + { + addAspectPermissions(type, permissions, exposedOnly); + } + else + { + mergeGeneralAspectPermissions(permissions, exposedOnly); + addTypePermissions(type, permissions, exposedOnly); + } } cache.put(type, permissions); } - return (Set)permissions.clone(); + return (Set) permissions.clone(); } /** @@ -401,10 +396,10 @@ public class PermissionModel implements ModelDAO, InitializingBean } } } - + private void mergeGeneralAspectPermissions(Set target, boolean exposedOnly) { - for(QName aspect : dictionaryService.getAllAspects()) + for (QName aspect : dictionaryService.getAllAspects()) { mergePermissions(target, aspect, exposedOnly, false); } @@ -424,10 +419,10 @@ public class PermissionModel implements ModelDAO, InitializingBean { // // TODO: cache permissions based on type and exposed flag - // create JMeter test to see before/after effect! + // create JMeter test to see before/after effect! // QName typeName = nodeService.getType(nodeRef); - + Set permissions = getAllPermissions(typeName); mergeGeneralAspectPermissions(permissions, exposedOnly); // Add non mandatory aspects... @@ -663,9 +658,7 @@ public class PermissionModel implements ModelDAO, InitializingBean } /** - * Query the model for a base permission group - * - * Uses the Data Dictionary to reolve inheritance + * Query the model for a base permission group Uses the Data Dictionary to reolve inheritance * * @param pg * @return @@ -775,8 +768,9 @@ public class PermissionModel implements ModelDAO, InitializingBean { for (PermissionGroup pg : ps.getPermissionGroups()) { - if (target.equals(getBasePermissionGroupOrNull(pg)) - && isPartOfDynamicPermissionGroup(pg, qName, aspectQNames)) + PermissionGroup base = getBasePermissionGroupOrNull(pg); + if (target.equals(base) + && (!base.isTypeRequired() || isPartOfDynamicPermissionGroup(pg, qName, aspectQNames))) { // Add includes for (PermissionReference pr : pg.getIncludedPermissionGroups()) @@ -791,7 +785,8 @@ public class PermissionModel implements ModelDAO, InitializingBean for (PermissionReference grantedTo : p.getGrantedToGroups()) { PermissionGroup base = getBasePermissionGroupOrNull(getPermissionGroupOrNull(grantedTo)); - if (target.equals(base) && (!base.isTypeRequired() || isPartOfDynamicPermissionGroup(grantedTo, qName, aspectQNames))) + if (target.equals(base) + && (!base.isTypeRequired() || isPartOfDynamicPermissionGroup(grantedTo, qName, aspectQNames))) { if (on == RequiredPermission.On.NODE) { @@ -887,7 +882,7 @@ public class PermissionModel implements ModelDAO, InitializingBean public PermissionReference getPermissionReference(QName qname, String permissionName) { - if(permissionName == null) + if (permissionName == null) { return null; } @@ -963,7 +958,8 @@ public class PermissionModel implements ModelDAO, InitializingBean + PermissionService.ALL_PERMISSIONS); } uniqueMap.put(PermissionService.ALL_PERMISSIONS, new SimplePermissionReference(QName.createQName( - NamespaceService.SECURITY_MODEL_1_0_URI, PermissionService.ALL_PERMISSIONS), PermissionService.ALL_PERMISSIONS)); + NamespaceService.SECURITY_MODEL_1_0_URI, PermissionService.ALL_PERMISSIONS), + PermissionService.ALL_PERMISSIONS)); } diff --git a/source/java/org/alfresco/repo/security/permissions/impl/model/PermissionModelTest.java b/source/java/org/alfresco/repo/security/permissions/impl/model/PermissionModelTest.java index 861d8678eb..758e3d94d7 100644 --- a/source/java/org/alfresco/repo/security/permissions/impl/model/PermissionModelTest.java +++ b/source/java/org/alfresco/repo/security/permissions/impl/model/PermissionModelTest.java @@ -22,6 +22,7 @@ import org.alfresco.repo.security.permissions.PermissionEntry; import org.alfresco.repo.security.permissions.PermissionReference; import org.alfresco.repo.security.permissions.impl.AbstractPermissionTest; import org.alfresco.repo.security.permissions.impl.SimplePermissionReference; +import org.alfresco.repo.security.permissions.impl.RequiredPermission.On; import org.alfresco.service.namespace.QName; public class PermissionModelTest extends AbstractPermissionTest @@ -32,12 +33,21 @@ public class PermissionModelTest extends AbstractPermissionTest super(); } + public void testWoof() + { + QName typeQname = nodeService.getType(rootNodeRef); + Set aspectQNames = nodeService.getAspects(rootNodeRef); + PermissionReference ref = permissionModelDAO.getPermissionReference(null, "CheckOut"); + Set answer = permissionModelDAO.getRequiredPermissions(ref, typeQname, aspectQNames, On.NODE); + assertEquals(1, answer.size()); + } + public void testIncludePermissionGroups() { Set grantees = permissionModelDAO.getGranteePermissions(new SimplePermissionReference(QName.createQName("cm", "cmobject", namespacePrefixResolver), "Consumer")); - assertEquals(5, grantees.size()); + assertEquals(8, grantees.size()); } public void testIncludePermissionGroups2() @@ -45,7 +55,7 @@ public class PermissionModelTest extends AbstractPermissionTest Set grantees = permissionModelDAO.getGranteePermissions(new SimplePermissionReference(QName.createQName("cm", "cmobject", namespacePrefixResolver), "Contributor")); - assertEquals(11, grantees.size()); + assertEquals(17, grantees.size()); } public void testIncludePermissionGroups3() @@ -53,7 +63,7 @@ public class PermissionModelTest extends AbstractPermissionTest Set grantees = permissionModelDAO.getGranteePermissions(new SimplePermissionReference(QName.createQName("cm", "cmobject", namespacePrefixResolver), "Editor")); - assertEquals(11, grantees.size()); + assertEquals(17, grantees.size()); } public void testIncludePermissionGroups4() @@ -61,14 +71,34 @@ public class PermissionModelTest extends AbstractPermissionTest Set grantees = permissionModelDAO.getGranteePermissions(new SimplePermissionReference(QName.createQName("cm", "cmobject", namespacePrefixResolver), "Collaborator")); - assertEquals(16, grantees.size()); + assertEquals(24, grantees.size()); + } + + public void testIncludePermissionGroups5() + { + Set grantees = permissionModelDAO.getGranteePermissions(new SimplePermissionReference(QName.createQName("cm", "cmobject", + namespacePrefixResolver), "Coordinator")); + + assertEquals(59, grantees.size()); + } + + public void testIncludePermissionGroups6() + { + Set grantees = permissionModelDAO.getGranteePermissions(new SimplePermissionReference(QName.createQName("cm", "cmobject", + namespacePrefixResolver), "RecordAdministrator")); + + assertEquals(19, grantees.size()); } public void testGetGrantingPermissions() { Set granters = permissionModelDAO.getGrantingPermissions(new SimplePermissionReference(QName.createQName("sys", "base", namespacePrefixResolver), "ReadProperties")); - assertEquals(9, granters.size()); + assertEquals(10, granters.size()); + + granters = permissionModelDAO.getGrantingPermissions(new SimplePermissionReference(QName.createQName("sys", "base", + namespacePrefixResolver), "_ReadProperties")); + assertEquals(11, granters.size()); } public void testGlobalPermissions() @@ -76,4 +106,5 @@ public class PermissionModelTest extends AbstractPermissionTest Set globalPermissions = permissionModelDAO.getGlobalPermissionEntries(); assertEquals(5, globalPermissions.size()); } + } diff --git a/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java b/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java index c705ca22d2..3bc2971b7d 100644 --- a/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java +++ b/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java @@ -41,6 +41,7 @@ import org.alfresco.service.cmr.search.CategoryService; import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.security.AuthenticationService; import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.OwnableService; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.version.VersionService; import org.alfresco.service.cmr.view.ExporterService; @@ -331,4 +332,14 @@ public class ServiceDescriptorRegistry { return (AuditService)getService(AUDIT_SERVICE); } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getOwnableService() + */ + public OwnableService getOwnableService() + { + return (OwnableService)getService(OWNABLE_SERVICE); + } + + } diff --git a/source/java/org/alfresco/repo/transaction/TransactionAwareSingleton.java b/source/java/org/alfresco/repo/transaction/TransactionAwareSingleton.java new file mode 100644 index 0000000000..c383a2fc26 --- /dev/null +++ b/source/java/org/alfresco/repo/transaction/TransactionAwareSingleton.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2006 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.transaction; + +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; + +/** + * A transactionally-safe storage class for singleton objects. Changes to the singleton + * are only visibly promoted when the transaction is committed. + *

+ * + * private static final TransactionAwareSingleton MY_SINGLETON = new TransactionAwareSingleton(); + * + *

+ * All modifications to the singleton via {@link #get()} and {@link #put(T)} are made in a + * transaction-local manner and promoted to the shared value in a thread-safe manner upon + * transacton completion. Transaction-local changes take precedence over the shared value. + * + * @see org.alfresco.repo.transaction.AlfrescoTransactionSupport + * + * @author Derek Hulley + */ +public class TransactionAwareSingleton extends TransactionListenerAdapter +{ + private static final String TRANSACTION_KEY = "TransactionAwareSingleton.storage"; + + private final ReadLock singletonReadLock; + private final WriteLock singletonWriteLock; + private Object singletonValue; + + public TransactionAwareSingleton() + { + ReentrantReadWriteLock serverReadWriteLock = new ReentrantReadWriteLock(); + singletonReadLock = serverReadWriteLock.readLock(); + singletonWriteLock = serverReadWriteLock.writeLock(); + } + + private void setValue(Object value) + { + // get a write lock + singletonWriteLock.lock(); + try + { + singletonValue = value; + } + finally + { + singletonWriteLock.unlock(); + } + } + + private Object getValue() + { + // get a read lock + singletonReadLock.lock(); + try + { + return singletonValue; + } + finally + { + singletonReadLock.unlock(); + } + } + + /** + * @return Returns the transaction- and thread-safe wrapped instance + */ + @SuppressWarnings("unchecked") + public T get() + { + // an in-transaction value overrides the singleton + TransactionStorage storage = (TransactionStorage) AlfrescoTransactionSupport.getResource(TRANSACTION_KEY); + if (storage != null) + { + return (T) storage.newValue; + } + else + { + return (T) getValue(); + } + } + + /** + * Store the value in a transaction- and thread-safe manner. It will only be persisted + * at the end of the transaction but will be visible to the current transaction from + * this call onwards. + * + * @param value the value to store + */ + public void put(T value) + { + // the value is changing + TransactionStorage storage = (TransactionStorage) AlfrescoTransactionSupport.getResource(TRANSACTION_KEY); + if (storage == null) + { + // it has not changed before + storage = new TransactionStorage(); + AlfrescoTransactionSupport.bindResource(TRANSACTION_KEY, storage); + // listen to the transaction + AlfrescoTransactionSupport.bindListener(this); + } + storage.newValue = value; + } + + /** + * Promotes the storage value to the single value, if required + */ + public void afterCommit() + { + TransactionStorage storage = (TransactionStorage) AlfrescoTransactionSupport.getResource(TRANSACTION_KEY); + if (storage != null) + { + // the value was overridden + setValue(storage.newValue); + } + } + + /** + * In-transaction storage of the altered value + * @author Derek Hulley + */ + private static class TransactionStorage + { + public Object newValue; + } +} diff --git a/source/java/org/alfresco/repo/transaction/TransactionAwareSingletonTest.java b/source/java/org/alfresco/repo/transaction/TransactionAwareSingletonTest.java new file mode 100644 index 0000000000..546c9bcefa --- /dev/null +++ b/source/java/org/alfresco/repo/transaction/TransactionAwareSingletonTest.java @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2006 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.transaction; + +import java.util.Random; + +import javax.transaction.UserTransaction; + +import junit.framework.TestCase; + +import org.alfresco.repo.transaction.TransactionUtil.TransactionWork; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.ApplicationContextHelper; +import org.springframework.context.ApplicationContext; + +/** + * @see org.alfresco.repo.transaction.TransactionAwareSingleton + * + * @author Derek Hulley + */ +public class TransactionAwareSingletonTest extends TestCase +{ + private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); + private static Random rand = new Random(); + + /** the instance to test */ + private TransactionAwareSingleton singleton = new TransactionAwareSingleton(); + private static final Integer INTEGER_ONE = new Integer(1); + private static final Integer INTEGER_TWO = new Integer(2); + + private TransactionService transactionService; + + public void setUp() throws Exception + { + transactionService = (TransactionService) ctx.getBean("transactionComponent"); + } + + public void testCommit() throws Throwable + { + UserTransaction txn = transactionService.getUserTransaction(); + try + { + txn.begin(); + + singleton.put(INTEGER_ONE); + check(INTEGER_ONE, true); + check(null, false); + + // commit + txn.commit(); + } + catch (Throwable e) + { + try { txn.rollback(); } catch (Throwable ee) {} + throw e; + } + check(INTEGER_ONE, true); + check(INTEGER_ONE, false); + } + + public void testRollback() throws Throwable + { + UserTransaction txn = transactionService.getUserTransaction(); + try + { + txn.begin(); + + singleton.put(INTEGER_TWO); + check(INTEGER_TWO, true); + check(null, false); + + // rollback + txn.rollback(); + } + catch (Throwable e) + { + try { txn.rollback(); } catch (Throwable ee) {} + throw e; + } + check(null, true); + check(null, false); + } + + private static final int THREAD_COUNT = 20; + public void testThreadsCommit() throws Throwable + { + TestThread[] threads = new TestThread[THREAD_COUNT]; + for (int i = 0; i < THREAD_COUNT; i++) + { + TestThread thread = new TestThread(true); + thread.start(); + threads[i] = thread; + } + // wait for them to complete + for (int i = 0; i < THREAD_COUNT; i++) + { + while (threads[i].finished == false) + { + synchronized(this) + { + try { wait(20); } catch (Throwable e) {} + } + } + if (threads[i].error != null) + { + throw threads[i].error; + } + } + } + public void testThreadsRollback() throws Throwable + { + TestThread[] threads = new TestThread[THREAD_COUNT]; + for (int i = 0; i < THREAD_COUNT; i++) + { + TestThread thread = new TestThread(false); + thread.start(); + threads[i] = thread; + } + } + + /** + * Dumps random values into + * @author Derek Hulley + */ + private class TestThread extends Thread + { + private boolean finished = false; + private Throwable error; + private boolean commit; + private Integer value = new Integer((int)System.nanoTime()); + + public TestThread(boolean commit) + { + this.commit = commit; + } + @Override + public synchronized void run() + { + UserTransaction txn = transactionService.getUserTransaction(); + try + { + txn.begin(); + + singleton.put(value); + + // wait for some random time + try + { + wait((long)(rand.nextDouble() * 1000.0)); // wait up to a second + } + catch (InterruptedException e) + { + // ignore + } + + // check + check(value, true); + + if (commit) + { + txn.commit(); + } + else + { + // rollback + txn.rollback(); + } + } + catch (Throwable e) + { + try { txn.rollback(); } catch (Throwable ee) {} + this.error = e; + } + if (!commit) + { + try + { + // no thread changes + check(null, false); + } + catch (Throwable e) + { + error = e; + } + } + finished = true; + } + } + + private void check(final Integer expected, boolean inTransaction) + { + TransactionWork checkWork = new TransactionWork() + { + public Object doWork() throws Exception + { + Integer actual = singleton.get(); + assertTrue("Values don't match: " + expected + " != " + actual, actual == expected); + return null; + } + }; + if (inTransaction) + { + TransactionUtil.executeInUserTransaction(transactionService, checkWork); + } + else + { + TransactionUtil.executeInNonPropagatingUserTransaction(transactionService, checkWork); + } + } +} diff --git a/source/java/org/alfresco/repo/workflow/StartWorkflowActionExecuter.java b/source/java/org/alfresco/repo/workflow/StartWorkflowActionExecuter.java index 0aa6c80b6d..eb4f2297af 100644 --- a/source/java/org/alfresco/repo/workflow/StartWorkflowActionExecuter.java +++ b/source/java/org/alfresco/repo/workflow/StartWorkflowActionExecuter.java @@ -31,20 +31,24 @@ 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.workflow.WorkflowDefinition; +import org.alfresco.service.cmr.workflow.WorkflowPath; import org.alfresco.service.cmr.workflow.WorkflowService; +import org.alfresco.service.cmr.workflow.WorkflowTask; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; /** * Simple workflow action executor * - * @author Roy Wetherall + * @author David Caruana */ public class StartWorkflowActionExecuter extends ActionExecuterAbstractBase { public static final String NAME = "start-workflow"; - public static final String PARAM_WORKFLOW_NAME = "workflowName"; + public static final String PARAM_END_START_TASK = "endStartTask"; + public static final String PARAM_START_TASK_TRANSITION = "startTaskTransition"; + // action dependencies private NamespaceService namespaceService; @@ -94,7 +98,9 @@ public class StartWorkflowActionExecuter extends ActionExecuterAbstractBase protected void addParameterDefinitions(List paramList) { paramList.add(new ParameterDefinitionImpl(PARAM_WORKFLOW_NAME, DataTypeDefinition.TEXT, false, getParamDisplayLabel(PARAM_WORKFLOW_NAME))); - // TODO: Start Task Template parameter + paramList.add(new ParameterDefinitionImpl(PARAM_END_START_TASK, DataTypeDefinition.BOOLEAN, false, getParamDisplayLabel(PARAM_END_START_TASK))); + paramList.add(new ParameterDefinitionImpl(PARAM_START_TASK_TRANSITION, DataTypeDefinition.TEXT, false, getParamDisplayLabel(PARAM_START_TASK_TRANSITION))); + // TODO: start task node parameter } @@ -127,9 +133,31 @@ public class StartWorkflowActionExecuter extends ActionExecuterAbstractBase workflowParameters.put(qname, value); } } - + + // provide a default context, if one is not specified + Serializable context = workflowParameters.get(WorkflowModel.PROP_CONTEXT); + if (context == null) + { + workflowParameters.put(WorkflowModel.PROP_CONTEXT, childAssoc.getParentRef()); + } + // start the workflow - workflowService.startWorkflow(def.id, workflowParameters); + WorkflowPath path = workflowService.startWorkflow(def.id, workflowParameters); + + // determine whether to auto-end the start task + Boolean endStartTask = (Boolean)ruleAction.getParameterValue(PARAM_END_START_TASK); + String startTaskTransition = (String)ruleAction.getParameterValue(PARAM_START_TASK_TRANSITION); + endStartTask = (endStartTask == null) ? true : false; + startTaskTransition = (startTaskTransition == null) ? "" : startTaskTransition; + + // auto-end the start task with the provided transition (if one) + if (endStartTask) + { + List tasks = workflowService.getTasksForWorkflowPath(path.id); + for (WorkflowTask task : tasks) + { + workflowService.endTask(task.id, startTaskTransition); + } + } } - } diff --git a/source/java/org/alfresco/repo/workflow/StartWorkflowActionExecuterTest.java b/source/java/org/alfresco/repo/workflow/StartWorkflowActionExecuterTest.java index 8a635cbab3..369d30e706 100644 --- a/source/java/org/alfresco/repo/workflow/StartWorkflowActionExecuterTest.java +++ b/source/java/org/alfresco/repo/workflow/StartWorkflowActionExecuterTest.java @@ -31,9 +31,9 @@ import org.alfresco.util.BaseSpringTest; import org.alfresco.util.GUID; /** - * Add features action execution test + * Start Advanced Workflow action execution test * - * @author Roy Wetherall + * @author David Caruana */ public class StartWorkflowActionExecuterTest extends BaseSpringTest { @@ -78,9 +78,10 @@ public class StartWorkflowActionExecuterTest extends BaseSpringTest // Execute the action ActionImpl action = new ActionImpl(null, GUID.generate(), StartWorkflowActionExecuter.NAME, null); action.setParameterValue(StartWorkflowActionExecuter.PARAM_WORKFLOW_NAME, "jbpm$wf:review"); - action.setParameterValue(WorkflowModel.PROP_REVIEW_DUE_DATE.toPrefixString(namespaceService), new Date()); + action.setParameterValue(WorkflowModel.PROP_WORKFLOW_DUE_DATE.toPrefixString(namespaceService), new Date()); NodeRef reviewer = personService.getPerson("admin"); - action.setParameterValue(WorkflowModel.ASSOC_REVIEWER.toPrefixString(namespaceService), reviewer); + action.setParameterValue(WorkflowModel.ASSOC_ASSIGNEE.toPrefixString(namespaceService), reviewer); executer.execute(action, this.nodeRef); } + } diff --git a/source/java/org/alfresco/repo/workflow/WorkflowComponent.java b/source/java/org/alfresco/repo/workflow/WorkflowComponent.java index ea0065cc5c..15aebb5ec5 100644 --- a/source/java/org/alfresco/repo/workflow/WorkflowComponent.java +++ b/source/java/org/alfresco/repo/workflow/WorkflowComponent.java @@ -118,6 +118,14 @@ public interface WorkflowComponent */ public List getActiveWorkflows(String workflowDefinitionId); + /** + * Gets a specific workflow instances + * + * @param workflowId the id of the workflow to retrieve + * @return the workflow instance + */ + public WorkflowInstance getWorkflowById(String workflowId); + /** * Gets all Paths for the specified Workflow instance * diff --git a/source/java/org/alfresco/repo/workflow/WorkflowDeployer.java b/source/java/org/alfresco/repo/workflow/WorkflowDeployer.java index e0020f37c8..0755746654 100644 --- a/source/java/org/alfresco/repo/workflow/WorkflowDeployer.java +++ b/source/java/org/alfresco/repo/workflow/WorkflowDeployer.java @@ -16,12 +16,15 @@ */ package org.alfresco.repo.workflow; +import java.util.ArrayList; import java.util.List; import java.util.Properties; import javax.transaction.UserTransaction; import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.repo.dictionary.DictionaryBootstrap; +import org.alfresco.repo.dictionary.DictionaryDAO; import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.service.cmr.view.ImporterException; import org.alfresco.service.cmr.workflow.WorkflowDeployment; @@ -56,7 +59,10 @@ public class WorkflowDeployer implements ApplicationListener private TransactionService transactionService; private WorkflowService workflowService; private AuthenticationComponent authenticationComponent; + private DictionaryDAO dictionaryDAO; private List workflowDefinitions; + private List models = new ArrayList(); + private List resourceBundles = new ArrayList(); /** @@ -89,6 +95,16 @@ public class WorkflowDeployer implements ApplicationListener this.authenticationComponent = authenticationComponent; } + /** + * Sets the Dictionary DAO + * + * @param dictionaryDAO + */ + public void setDictionaryDAO(DictionaryDAO dictionaryDAO) + { + this.dictionaryDAO = dictionaryDAO; + } + /** * Sets the Workflow Definitions * @@ -99,6 +115,26 @@ public class WorkflowDeployer implements ApplicationListener this.workflowDefinitions = workflowDefinitions; } + /** + * Sets the initial list of Workflow models to bootstrap with + * + * @param modelResources the model names + */ + public void setModels(List modelResources) + { + this.models = modelResources; + } + + /** + * Sets the initial list of Workflow reosurce bundles to bootstrap with + * + * @param modelResources the model names + */ + public void setLabels(List labels) + { + this.resourceBundles = labels; + } + /** * Deploy the Workflow Definitions */ @@ -124,6 +160,16 @@ public class WorkflowDeployer implements ApplicationListener { userTransaction.begin(); + // bootstrap the workflow models and labels + if (models != null && resourceBundles != null) + { + DictionaryBootstrap dictionaryBootstrap = new DictionaryBootstrap(); + dictionaryBootstrap.setDictionaryDAO(dictionaryDAO); + dictionaryBootstrap.setModels(models); + dictionaryBootstrap.setLabels(resourceBundles); + dictionaryBootstrap.bootstrap(); + } + // bootstrap the workflow definitions if (workflowDefinitions != null) { diff --git a/source/java/org/alfresco/repo/workflow/WorkflowModel.java b/source/java/org/alfresco/repo/workflow/WorkflowModel.java index 9f72746f5c..5180ac8950 100644 --- a/source/java/org/alfresco/repo/workflow/WorkflowModel.java +++ b/source/java/org/alfresco/repo/workflow/WorkflowModel.java @@ -45,26 +45,23 @@ public interface WorkflowModel // workflow task contstants static final QName TYPE_WORKFLOW_TASK = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "workflowTask"); static final QName PROP_CONTEXT = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "context"); + static final QName PROP_DESCRIPTION = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "description"); static final QName PROP_OUTCOME = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "outcome"); static final QName PROP_PACKAGE_ACTION_GROUP = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "packageActionGroup"); static final QName PROP_PACKAGE_ITEM_ACTION_GROUP = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "packageItemActionGroup"); static final QName ASSOC_PACKAGE = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "package"); - + + // workflow task contstants + static final QName TYPE_START_TASK = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "startTask"); + static final QName PROP_WORKFLOW_DESCRIPTION = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "workflowDescription"); + static final QName PROP_WORKFLOW_PRIORITY = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "workflowPriority"); + static final QName PROP_WORKFLOW_DUE_DATE = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "workflowDueDate"); + static final QName ASSOC_ASSIGNEE = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "assignee"); + // workflow package static final QName ASPECT_WORKFLOW_PACKAGE = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "workflowPackage"); static final QName PROP_WORKFLOW_DEFINITION_ID = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "workflowDefinitionId"); static final QName PROP_WORKFLOW_DEFINITION_NAME = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "workflowDefinitionName"); static final QName PROP_WORKFLOW_INSTANCE_ID = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "workflowInstanceId"); - - - // - // Workflow Models - // - - // review & approve - static final QName TYPE_SUBMITREVIEW_TASK = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "submitReviewTask"); - static final QName PROP_REVIEW_PRIORITY = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "reviewPriority"); - static final QName PROP_REVIEW_DUE_DATE = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "reviewDueDate"); - static final QName ASSOC_REVIEWER = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "reviewer"); - + } \ No newline at end of file diff --git a/source/java/org/alfresco/repo/workflow/WorkflowPackageComponent.java b/source/java/org/alfresco/repo/workflow/WorkflowPackageComponent.java index 8a4675b3d2..8610904abf 100644 --- a/source/java/org/alfresco/repo/workflow/WorkflowPackageComponent.java +++ b/source/java/org/alfresco/repo/workflow/WorkflowPackageComponent.java @@ -16,6 +16,8 @@ */ package org.alfresco.repo.workflow; +import java.util.List; + import org.alfresco.service.cmr.repository.NodeRef; @@ -38,6 +40,14 @@ public interface WorkflowPackageComponent */ public NodeRef createPackage(NodeRef container); - // TODO: Support for finding packages via meta-data of WorkflowPackage aspect + // TODO: Further support for finding packages via meta-data of WorkflowPackage aspect + + /** + * Gets the Workflows that act upon the specified Repository content. + * + * @param packageItem the repository content item to get workflows for + * @return list of workflows which act upon the specified content + */ + public List getWorkflowIdsForContent(NodeRef packageItem); } diff --git a/source/java/org/alfresco/repo/workflow/WorkflowPackageImpl.java b/source/java/org/alfresco/repo/workflow/WorkflowPackageImpl.java index 3f15a29649..e247bed859 100644 --- a/source/java/org/alfresco/repo/workflow/WorkflowPackageImpl.java +++ b/source/java/org/alfresco/repo/workflow/WorkflowPackageImpl.java @@ -21,8 +21,6 @@ import java.util.List; import org.alfresco.model.ContentModel; import org.alfresco.repo.importer.ImporterBootstrap; -import org.alfresco.service.cmr.model.FileFolderService; -import org.alfresco.service.cmr.model.FileInfo; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; @@ -31,6 +29,7 @@ import org.alfresco.service.cmr.workflow.WorkflowException; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.util.GUID; +import org.alfresco.util.ParameterCheck; /** @@ -41,14 +40,13 @@ import org.alfresco.util.GUID; */ public class WorkflowPackageImpl implements WorkflowPackageComponent { - private final static String PACKAGE_FOLDER = "Workflow Packages"; + private final static String PACKAGE_FOLDER = "packages"; // service dependencies private ImporterBootstrap bootstrap; private SearchService searchService; private NodeService nodeService; private NamespaceService namespaceService; - private FileFolderService fileFolderService; private NodeRef systemWorkflowContainer = null; @@ -60,14 +58,6 @@ public class WorkflowPackageImpl implements WorkflowPackageComponent this.bootstrap = bootstrap; } - /** - * @param fileFolderService file folder service - */ - public void setFileFolderService(FileFolderService fileFolderService) - { - this.fileFolderService = fileFolderService; - } - /** * @param searchService search service */ @@ -105,11 +95,23 @@ public class WorkflowPackageImpl implements WorkflowPackageComponent NodeRef system = getSystemWorkflowContainer(); // TODO: Consider structuring this folder, if number of children becomes an issue - List folders = new ArrayList(); - folders.add(PACKAGE_FOLDER); - folders.add(GUID.generate()); - FileInfo containerFolder = fileFolderService.makeFolders(system, folders, ContentModel.TYPE_FOLDER); - container = containerFolder.getNodeRef(); + NodeRef packages = null; + List results = searchService.selectNodes(system, "./" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + PACKAGE_FOLDER, null, namespaceService, false); + if (results.size() > 0) + { + packages = results.get(0); + } + else + { + QName qname = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, PACKAGE_FOLDER); + ChildAssociationRef childRef = nodeService.createNode(system, ContentModel.ASSOC_CHILDREN, qname, ContentModel.TYPE_SYSTEM_FOLDER); + packages = childRef.getChildRef(); + } + + String containerName = "pkg_" + GUID.generate(); + QName qname = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, containerName); + ChildAssociationRef childRef = nodeService.createNode(packages, ContentModel.ASSOC_CONTAINS, qname, ContentModel.TYPE_SYSTEM_FOLDER); + container = childRef.getChildRef(); } // attach workflow package @@ -123,6 +125,32 @@ public class WorkflowPackageImpl implements WorkflowPackageComponent return container; } + /* (non-Javadoc) + * @see org.alfresco.repo.workflow.WorkflowPackageComponent#getWorkflowIdsForContent(org.alfresco.service.cmr.repository.NodeRef, boolean) + */ + public List getWorkflowIdsForContent(NodeRef packageItem) + { + ParameterCheck.mandatory("packageItem", packageItem); + List workflowIds = new ArrayList(); + if (nodeService.exists(packageItem)) + { + List packageItemParents = nodeService.getParentAssocs(packageItem); + for (ChildAssociationRef packageItemParent : packageItemParents) + { + NodeRef parentRef = packageItemParent.getParentRef(); + if (nodeService.hasAspect(parentRef, WorkflowModel.ASPECT_WORKFLOW_PACKAGE)) + { + String workflowInstance = (String)nodeService.getProperty(parentRef, WorkflowModel.PROP_WORKFLOW_INSTANCE_ID); + if (workflowInstance != null && workflowInstance.length() > 0) + { + workflowIds.add(workflowInstance); + } + } + } + } + return workflowIds; + } + /** * Gets the system workflow container for storing workflow related items @@ -201,10 +229,10 @@ public class WorkflowPackageImpl implements WorkflowPackageComponent { String name = bootstrap.getConfiguration().getProperty("system.workflow_container.childname"); QName qname = QName.createQName(name, namespaceService); - ChildAssociationRef childRef = nodeService.createNode(systemContainer, ContentModel.ASSOC_CHILDREN, qname, ContentModel.TYPE_FOLDER); + ChildAssociationRef childRef = nodeService.createNode(systemContainer, ContentModel.ASSOC_CHILDREN, qname, ContentModel.TYPE_CONTAINER); systemWorkflowContainer = childRef.getChildRef(); } return systemWorkflowContainer; } - + } diff --git a/source/java/org/alfresco/repo/workflow/WorkflowServiceImpl.java b/source/java/org/alfresco/repo/workflow/WorkflowServiceImpl.java index bdaac4c483..0773adba3b 100644 --- a/source/java/org/alfresco/repo/workflow/WorkflowServiceImpl.java +++ b/source/java/org/alfresco/repo/workflow/WorkflowServiceImpl.java @@ -185,6 +185,16 @@ public class WorkflowServiceImpl implements WorkflowService return component.getActiveWorkflows(workflowDefinitionId); } + /* (non-Javadoc) + * @see org.alfresco.service.cmr.workflow.WorkflowService#getWorkflowById(java.lang.String) + */ + public WorkflowInstance getWorkflowById(String workflowId) + { + String engineId = BPMEngineRegistry.getEngineId(workflowId); + WorkflowComponent component = getWorkflowComponent(engineId); + return component.getWorkflowById(workflowId); + } + /* (non-Javadoc) * @see org.alfresco.service.cmr.workflow.WorkflowService#getWorkflowPaths(java.lang.String) */ @@ -296,6 +306,27 @@ public class WorkflowServiceImpl implements WorkflowService { return workflowPackageComponent.createPackage(container); } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.workflow.WorkflowService#getWorkflowsForContent(org.alfresco.service.cmr.repository.NodeRef, boolean) + */ + public List getWorkflowsForContent(NodeRef packageItem, boolean active) + { + List workflowIds = workflowPackageComponent.getWorkflowIdsForContent(packageItem); + List workflowInstances = new ArrayList(workflowIds.size()); + for (String workflowId : workflowIds) + { + String engineId = BPMEngineRegistry.getEngineId(workflowId); + WorkflowComponent component = getWorkflowComponent(engineId); + WorkflowInstance instance = component.getWorkflowById(workflowId); + if (instance.active == active) + { + workflowInstances.add(instance); + } + } + return workflowInstances; + } + /** * Gets the Workflow Component registered against the specified BPM Engine Id diff --git a/source/java/org/alfresco/repo/workflow/WorkflowServiceImplTest.java b/source/java/org/alfresco/repo/workflow/WorkflowServiceImplTest.java index 5747b33103..00214e2b6e 100644 --- a/source/java/org/alfresco/repo/workflow/WorkflowServiceImplTest.java +++ b/source/java/org/alfresco/repo/workflow/WorkflowServiceImplTest.java @@ -16,15 +16,24 @@ */ package org.alfresco.repo.workflow; +import java.io.Serializable; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import org.alfresco.model.ContentModel; import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.workflow.WorkflowDefinition; +import org.alfresco.service.cmr.workflow.WorkflowInstance; import org.alfresco.service.cmr.workflow.WorkflowPath; import org.alfresco.service.cmr.workflow.WorkflowService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; import org.alfresco.util.BaseSpringTest; @@ -43,7 +52,7 @@ public class WorkflowServiceImplTest extends BaseSpringTest { workflowService = (WorkflowService)applicationContext.getBean(ServiceRegistry.WORKFLOW_SERVICE.getLocalName()); nodeService = (NodeService)applicationContext.getBean(ServiceRegistry.NODE_SERVICE.getLocalName()); - + // authenticate AuthenticationComponent auth = (AuthenticationComponent) applicationContext.getBean("authenticationComponent"); auth.setSystemUserAsCurrentUser(); @@ -77,4 +86,44 @@ public class WorkflowServiceImplTest extends BaseSpringTest assertTrue(nodeService.hasAspect(nodeRef, WorkflowModel.ASPECT_WORKFLOW_PACKAGE)); } + public void testAssociateWorkflowPackage() + { + // create workflow package + NodeRef rootRef = nodeService.getRootNode(new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "spacesStore")); + NodeRef nodeRef = workflowService.createPackage(null); + assertNotNull(nodeRef); + assertTrue(nodeService.hasAspect(nodeRef, WorkflowModel.ASPECT_WORKFLOW_PACKAGE)); + ChildAssociationRef childAssoc = nodeService.createNode(rootRef, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_PREFIX, "test"), ContentModel.TYPE_CONTENT, null); + nodeService.addChild(nodeRef, childAssoc.getChildRef(), ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_PREFIX, "test")); + + // start workflow + List workflowDefs = workflowService.getDefinitions(); + assertNotNull(workflowDefs); + assertTrue(workflowDefs.size() > 0); + WorkflowDefinition workflowDef = workflowDefs.get(0); + Map parameters = new HashMap(); + parameters.put(WorkflowModel.ASSOC_PACKAGE, nodeRef); + WorkflowPath path = workflowService.startWorkflow(workflowDef.id, parameters); + assertNotNull(path); + assertTrue(path.active); + assertNotNull(path.node); + assertNotNull(path.instance); + assertEquals(workflowDef.id, path.instance.definition.id); + String workflowDefId = (String)nodeService.getProperty(nodeRef, WorkflowModel.PROP_WORKFLOW_DEFINITION_ID); + assertEquals(workflowDefId, workflowDef.id); + String workflowDefName = (String)nodeService.getProperty(nodeRef, WorkflowModel.PROP_WORKFLOW_DEFINITION_NAME); + assertEquals(workflowDefName, workflowDef.name); + String workflowInstanceId = (String)nodeService.getProperty(nodeRef, WorkflowModel.PROP_WORKFLOW_INSTANCE_ID); + assertEquals(workflowInstanceId, path.instance.id); + + // get workflows for content + List instances = workflowService.getWorkflowsForContent(childAssoc.getChildRef(), true); + assertNotNull(instances); + assertEquals(1, instances.size()); + assertEquals(instances.get(0).id, path.instance.id); + List completedInstances = workflowService.getWorkflowsForContent(childAssoc.getChildRef(), false); + assertNotNull(completedInstances); + assertEquals(0, completedInstances.size()); + } + } diff --git a/source/java/org/alfresco/repo/workflow/jbpm/AlfrescoJavaScript.java b/source/java/org/alfresco/repo/workflow/jbpm/AlfrescoJavaScript.java index b3bab2b2db..59f09962af 100644 --- a/source/java/org/alfresco/repo/workflow/jbpm/AlfrescoJavaScript.java +++ b/source/java/org/alfresco/repo/workflow/jbpm/AlfrescoJavaScript.java @@ -23,6 +23,7 @@ import java.util.Map; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.repository.ScriptService; +import org.alfresco.service.cmr.workflow.WorkflowException; import org.dom4j.Element; import org.jbpm.context.def.VariableAccess; import org.jbpm.context.exe.ContextInstance; @@ -36,7 +37,7 @@ import org.xml.sax.InputSource; * A jBPM Action Handler for executing Alfresco Script * * The configuration of this action is as follows: - * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/source/java/org/alfresco/repo/workflow/jbpm/review_and_approve_processdefinition.xml b/source/java/org/alfresco/repo/workflow/jbpm/review_and_approve_processdefinition.xml deleted file mode 100644 index db8f6309be..0000000000 --- a/source/java/org/alfresco/repo/workflow/jbpm/review_and_approve_processdefinition.xml +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/source/java/org/alfresco/repo/workflow/jbpm/test_script.xml b/source/java/org/alfresco/repo/workflow/jbpm/test_script.xml index 907e9558e8..5f3ebc986f 100644 --- a/source/java/org/alfresco/repo/workflow/jbpm/test_script.xml +++ b/source/java/org/alfresco/repo/workflow/jbpm/test_script.xml @@ -1,57 +1,65 @@ - + - - - + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/source/java/org/alfresco/service/ServiceRegistry.java b/source/java/org/alfresco/service/ServiceRegistry.java index d98cf4fda1..103031e19f 100644 --- a/source/java/org/alfresco/service/ServiceRegistry.java +++ b/source/java/org/alfresco/service/ServiceRegistry.java @@ -35,6 +35,7 @@ import org.alfresco.service.cmr.search.CategoryService; import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.security.AuthenticationService; import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.OwnableService; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.version.VersionService; import org.alfresco.service.cmr.view.ExporterService; @@ -86,6 +87,7 @@ public interface ServiceRegistry static final QName SCRIPT_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ScriptService"); static final QName WORKFLOW_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "WorkflowService"); static final QName AUDIT_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "AuditService"); + static final QName OWNABLE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "OwnableService"); /** * Get the list of services provided by the Repository @@ -271,4 +273,11 @@ public interface ServiceRegistry */ @NotAuditable AuditService getAuditService(); + + /** + * Get the ownable service (or null if one is not provided) + * @return + */ + @NotAuditable + OwnableService getOwnableService(); } diff --git a/source/java/org/alfresco/service/cmr/workflow/WorkflowService.java b/source/java/org/alfresco/service/cmr/workflow/WorkflowService.java index 80938eefb3..65322854f5 100644 --- a/source/java/org/alfresco/service/cmr/workflow/WorkflowService.java +++ b/source/java/org/alfresco/service/cmr/workflow/WorkflowService.java @@ -148,6 +148,15 @@ public interface WorkflowService */ @Auditable(parameters = {"workflowDefinitionId"}) public List getActiveWorkflows(String workflowDefinitionId); + + /** + * Gets a specific workflow instances + * + * @param workflowId the id of the workflow to retrieve + * @return the workflow instance + */ + @Auditable(parameters = {"workflowId"}) + public WorkflowInstance getWorkflowById(String workflowId); /** * Gets all Paths for the specified Workflow instance @@ -241,6 +250,11 @@ public interface WorkflowService @Auditable(parameters = {"taskId", "transitionId"}) public WorkflowTask endTask(String taskId, String transitionId); + + // + // Package Management + // + /** * Create a Workflow Package (a container of content to route through the Workflow). * @@ -251,5 +265,15 @@ public interface WorkflowService */ @Auditable(key = Auditable.Key.ARG_0, parameters = {"container"}) public NodeRef createPackage(NodeRef container); + + /** + * Gets the Workflows that act upon the specified Repository content. + * + * @param packageItem the repository content item to get workflows for + * @param active true => active workflows only, false => completed workflows only + * @return list of workflows which act upon the specified content + */ + @Auditable(key = Auditable.Key.ARG_0, parameters = {"packageItem", "active"}) + public List getWorkflowsForContent(NodeRef packageItem, boolean active); }