diff --git a/config/alfresco/application-context.xml b/config/alfresco/application-context.xml index 7067dc190f..422b2108a3 100644 --- a/config/alfresco/application-context.xml +++ b/config/alfresco/application-context.xml @@ -1,37 +1,36 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/alfresco/bootstrap-context.xml b/config/alfresco/bootstrap-context.xml index aa61568bb4..d594977215 100644 --- a/config/alfresco/bootstrap-context.xml +++ b/config/alfresco/bootstrap-context.xml @@ -145,6 +145,10 @@ /${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.scripts.childname} alfresco/bootstrap/example_javascripts.acp + + /${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.templates.childname} + alfresco/bootstrap/file_plan.xml + diff --git a/config/alfresco/bootstrap/Alfresco-Tutorial.pdf b/config/alfresco/bootstrap/Alfresco-Tutorial.pdf index 65c4586319..29141a034c 100644 Binary files a/config/alfresco/bootstrap/Alfresco-Tutorial.pdf and b/config/alfresco/bootstrap/Alfresco-Tutorial.pdf differ diff --git a/config/alfresco/bootstrap/categories.xml b/config/alfresco/bootstrap/categories.xml index 1b958151e0..87012bd9cc 100644 --- a/config/alfresco/bootstrap/categories.xml +++ b/config/alfresco/bootstrap/categories.xml @@ -1,4 +1,5 @@ @@ -1096,10 +1097,106 @@ + + + +Records Categoriesrm:recordCategory-root + + Review Periodrm:reviewPeriod-root + + Nonerm:reviewPeriod-0 + TBDrm:reviewPeriod-1 + Annuallyrm:reviewPeriod-2 + Calendar Year Endrm:reviewPeriod-3 + Fiscal Year Endrm:reviewPeriod-4 + Semi-Annuallyrm:reviewPeriod-5 + Quarterlyrm:reviewPeriod-6 + Monthlyrm:reviewPeriod-7 + Weeklyrm:reviewPeriod-8 + Dailyrm:reviewPeriod-9 + + + + Media Typesrm:mediaTypes-root + + TBDrm:mediaTypes-0 + Electronicrm:mediaTypes-1 + Paperrm:mediaTypes-2 + Microformrm:mediaTypes-3 + Portablerm:mediaTypes-4 + + + + Markingsrm:markings-root + + NONErm:markings-0 + + Classificationsrm:markings-1 + + UNCLASSIFIEDrm:markings-1-1 + RESTRICTEDrm:markings-1-2 + CONFIDENTIALrm:markings-1-3 + SECRETrm:markings-1-4 + TOP SECRETrm:markings-1-5 + + + + Handlingrm:markings-2 + + COMSECrm:markings-2-1 + RDrm:markings-2-2 + FRDrm:markings-2-3 + SPECAT-A SIOP-ESIrm:markings-2-4 + SPECAT-Brm:markings-2-5 + US-UK EYES ONLYrm:markings-2-6 + ATOMALrm:markings-2-7 + EXCLUSIVErm:markings-2-8 + CRYPTOrm:markings-2-9 + TRCrm:markings-2-10 + FOR OFFICIAL USE ONLYrm:markings-2-11 + SBUrm:markings-2-12 + DEA SENSITIVErm:markings-2-13 + DOD UCNIrm:markings-2-14 + EXDISrm:markings-2-15 + LIMDISrm:markings-2-16 + NODISrm:markings-2-17 + SOSUSrm:markings-2-18 + EYES ONLYrm:markings-2-19 + + + + Disseminationrm:markings-3 + + ORCONrm:markings-3-1 + PROPINrm:markings-3-2 + NOFORNrm:markings-3-3 + NOCONTRACTrm:markings-3-34 + FOUOrm:markings-3-5 + RELTOrm:markings-3-6 + EYES ONLYrm:markings-3-7 + + + + Reasonrm:markings-4 + + CLASSrm:markings-4-1 + RSNrm:markings-4-2 + DERVrm:markings-4-3 + DNGrm:markings-4-4 + DECLrm:markings-4-5 + + + + + + + + - \ No newline at end of file + + diff --git a/config/alfresco/bootstrap/example_javascripts.acp b/config/alfresco/bootstrap/example_javascripts.acp index c079c9152d..48bf6636e4 100644 Binary files a/config/alfresco/bootstrap/example_javascripts.acp and b/config/alfresco/bootstrap/example_javascripts.acp differ diff --git a/config/alfresco/bootstrap/file_plan.xml b/config/alfresco/bootstrap/file_plan.xml new file mode 100644 index 0000000000..62003b9bb5 --- /dev/null +++ b/config/alfresco/bootstrap/file_plan.xml @@ -0,0 +1,828 @@ + + + + admin + 2006-09-02T09:54:43.796+01:00 + 1.4.0 (Dev @build-number@) + /app:company_home/app:dictionary/app:space_templates/cm:File_x0020_Plan + + + + + + + + + + + + + false + N/A + ISF + /app:company_home/app:dictionary/app:content_templates/cm:records_report.ftl + space-icon-cd + 15cc634c-3a5c-11db-ae95-09a9ea11d246 + false + GRS 1 item 1 + /cm:generalclassifiable/cm:Records_x0020_Categories/cm:Review_x0020_Period/cm:Quarterly + /cm:generalclassifiable/cm:Records_x0020_Categories/cm:Media_x0020_Types/cm:Electronic + 1 + false + 2006-09-02T09:21:51.750+01:00 + false + NARA + 1.0 + admin + false + false + false + Obsolete or Superseded + File Plan + SpacesStore + /cm:generalclassifiable/cm:Records_x0020_Categories/cm:Review_x0020_Period/cm:Quarterly + File Plan Template for Records Management + 458 + File Plan Template + false + 5.0 + false + /cm:generalclassifiable/cm:Records_x0020_Categories/cm:Markings/cm:NONE + admin + + false + 2006-09-02T09:53:17.750+01:00 + Destroy/delete when superseded by a like survey or study, or when no longer needed, whichever is later. + workspace + 0000-00 + + Defense Information Systems Agency + false + 2.0 + + + + + + + + + + admin + 2006-09-02T09:41:28.718+01:00 + 7836ac9c-3a5d-11db-ae95-09a9ea11d246 + admin + 459 + workspace + 7836ac9c-3a5d-11db-ae95-09a9ea11d246 + SpacesStore + 2006-09-02T09:31:46.093+01:00 + + + + + + + + + + + Add Email Aspect + 783b678e-3a5d-11db-ae95-09a9ea11d246 + 460 + Email Handling + 2006-09-02T09:31:46.109+01:00 + admin + false + 2006-09-02T09:31:46.250+01:00 + true + admin + workspace + false + + + inbound + + + 783b678e-3a5d-11db-ae95-09a9ea11d246 + SpacesStore + + + + + + + + + + false + + + + 78343b97-3a5d-11db-ae95-09a9ea11d246 + 461 + 2006-09-02T09:31:46.140+01:00 + admin + 2006-09-02T09:31:46.250+01:00 + + + + admin + composite-action + workspace + 78343b97-3a5d-11db-ae95-09a9ea11d246 + SpacesStore + + + + + + + + + + admin + 2006-09-02T09:31:46.250+01:00 + 78343b98-3a5d-11db-ae95-09a9ea11d246 + admin + 462 + compare-mime-type + workspace + false + 78343b98-3a5d-11db-ae95-09a9ea11d246 + SpacesStore + 2006-09-02T09:31:46.171+01:00 + + + + + + + + + + + message/rfc822 + + admin + value + 2006-09-02T09:31:46.250+01:00 + 78474e6f-3a5d-11db-ae95-09a9ea11d246 + admin + 463 + workspace + 78474e6f-3a5d-11db-ae95-09a9ea11d246 + SpacesStore + 2006-09-02T09:31:46.187+01:00 + + + + + + + + + + + + + + + false + + + + 78343b99-3a5d-11db-ae95-09a9ea11d246 + 464 + 2006-09-02T09:31:46.203+01:00 + admin + 2006-09-02T09:31:46.250+01:00 + + + + admin + add-features + workspace + 78343b99-3a5d-11db-ae95-09a9ea11d246 + SpacesStore + + + + + + + + + + + {http://www.alfresco.org/model/content/1.0}emailed + + admin + aspect-name + 2006-09-02T09:31:46.250+01:00 + 784c0960-3a5d-11db-ae95-09a9ea11d246 + admin + 465 + workspace + 784c0960-3a5d-11db-ae95-09a9ea11d246 + SpacesStore + 2006-09-02T09:31:46.234+01:00 + + + + + + + + + + + + + + + + + + + + Add record aspect and set up record data + d091637d-3a5d-11db-ae95-09a9ea11d246 + 466 + Record Set-up + 2006-09-02T09:34:14.312+01:00 + admin + false + 2006-09-02T09:34:14.546+01:00 + true + admin + workspace + false + + + inbound + + + d091637d-3a5d-11db-ae95-09a9ea11d246 + SpacesStore + + + + + + + + + + false + + + + d08ca886-3a5d-11db-ae95-09a9ea11d246 + 467 + 2006-09-02T09:34:14.343+01:00 + admin + 2006-09-02T09:34:14.546+01:00 + + + + admin + composite-action + workspace + d08ca886-3a5d-11db-ae95-09a9ea11d246 + SpacesStore + + + + + + + + + + admin + 2006-09-02T09:34:14.546+01:00 + d08ca887-3a5d-11db-ae95-09a9ea11d246 + admin + 468 + is-subtype + workspace + false + d08ca887-3a5d-11db-ae95-09a9ea11d246 + SpacesStore + 2006-09-02T09:34:14.375+01:00 + + + + + + + + + + + {http://www.alfresco.org/model/content/1.0}content + + admin + type + 2006-09-02T09:34:14.546+01:00 + d09d4a5f-3a5d-11db-ae95-09a9ea11d246 + admin + 469 + workspace + d09d4a5f-3a5d-11db-ae95-09a9ea11d246 + SpacesStore + 2006-09-02T09:34:14.390+01:00 + + + + + + + + + + + + + + + false + + + + d08ca888-3a5d-11db-ae95-09a9ea11d246 + 470 + 2006-09-02T09:34:14.421+01:00 + admin + 2006-09-02T09:34:14.546+01:00 + + + + admin + add-features + workspace + d08ca888-3a5d-11db-ae95-09a9ea11d246 + SpacesStore + + + + + + + + + + + {http://www.alfresco.org/model/record/1.0}record + + admin + aspect-name + 2006-09-02T09:34:14.546+01:00 + d0a47550-3a5d-11db-ae95-09a9ea11d246 + admin + 471 + workspace + d0a47550-3a5d-11db-ae95-09a9ea11d246 + SpacesStore + 2006-09-02T09:34:14.453+01:00 + + + + + + + + + + + + + false + + + + d08ca889-3a5d-11db-ae95-09a9ea11d246 + 472 + 2006-09-02T09:34:14.484+01:00 + admin + 2006-09-02T09:34:14.546+01:00 + + + + admin + extract-metadata + workspace + d08ca889-3a5d-11db-ae95-09a9ea11d246 + SpacesStore + + + + + + + + + + false + + + + d08ca88a-3a5d-11db-ae95-09a9ea11d246 + 473 + 2006-09-02T09:34:14.515+01:00 + admin + 2006-09-02T09:34:14.546+01:00 + + + + admin + script + workspace + d08ca88a-3a5d-11db-ae95-09a9ea11d246 + SpacesStore + + + + + + + + + + + /app:company_home/app:dictionary/app:scripts/cm:record_setup.js + + admin + script-ref + 2006-09-02T09:34:14.546+01:00 + d0b2cd31-3a5d-11db-ae95-09a9ea11d246 + admin + 474 + workspace + d0b2cd31-3a5d-11db-ae95-09a9ea11d246 + SpacesStore + 2006-09-02T09:34:14.531+01:00 + + + + + + + + + + + + + + + + + + + + Set up record folder + 96342c65-3a5e-11db-ae95-09a9ea11d246 + 475 + Records Folder + 2006-09-02T09:39:45.906+01:00 + admin + false + 2006-09-02T09:39:46.093+01:00 + true + admin + workspace + false + + + inbound + + + 96342c65-3a5e-11db-ae95-09a9ea11d246 + SpacesStore + + + + + + + + + + false + + + + 9631e26f-3a5e-11db-ae95-09a9ea11d246 + 476 + 2006-09-02T09:39:45.921+01:00 + admin + 2006-09-02T09:39:46.093+01:00 + + + + admin + composite-action + workspace + 9631e26f-3a5e-11db-ae95-09a9ea11d246 + SpacesStore + + + + + + + + + + admin + 2006-09-02T09:39:46.093+01:00 + 9631e270-3a5e-11db-ae95-09a9ea11d246 + admin + 477 + is-subtype + workspace + false + 9631e270-3a5e-11db-ae95-09a9ea11d246 + SpacesStore + 2006-09-02T09:39:45.953+01:00 + + + + + + + + + + + {http://www.alfresco.org/model/content/1.0}folder + + admin + type + 2006-09-02T09:39:46.093+01:00 + 96428447-3a5e-11db-ae95-09a9ea11d246 + admin + 478 + workspace + 96428447-3a5e-11db-ae95-09a9ea11d246 + SpacesStore + 2006-09-02T09:39:45.984+01:00 + + + + + + + + + + + + + + + false + + + + 9631e271-3a5e-11db-ae95-09a9ea11d246 + 479 + 2006-09-02T09:39:46.000+01:00 + admin + 2006-09-02T09:39:46.093+01:00 + + + + admin + add-features + workspace + 9631e271-3a5e-11db-ae95-09a9ea11d246 + SpacesStore + + + + + + + + + + + {http://www.alfresco.org/model/record/1.0}record + + admin + aspect-name + 2006-09-02T09:39:46.093+01:00 + 9649b038-3a5e-11db-ae95-09a9ea11d246 + admin + 480 + workspace + 9649b038-3a5e-11db-ae95-09a9ea11d246 + SpacesStore + 2006-09-02T09:39:46.031+01:00 + + + + + + + + + + + + + false + + + + 9631e272-3a5e-11db-ae95-09a9ea11d246 + 481 + 2006-09-02T09:39:46.046+01:00 + admin + 2006-09-02T09:39:46.093+01:00 + + + + admin + script + workspace + 9631e272-3a5e-11db-ae95-09a9ea11d246 + SpacesStore + + + + + + + + + + + /app:company_home/app:dictionary/app:scripts/cm:record_folder.js + + admin + script-ref + 2006-09-02T09:39:46.093+01:00 + 964e6b29-3a5e-11db-ae95-09a9ea11d246 + admin + 482 + workspace + 964e6b29-3a5e-11db-ae95-09a9ea11d246 + SpacesStore + 2006-09-02T09:39:46.078+01:00 + + + + + + + + + + + + + + + + + + + + Process record lifecycle based upon the file plan + d36b6897-3a5e-11db-ae95-09a9ea11d246 + 483 + Record Lifecycle + 2006-09-02T09:41:28.609+01:00 + admin + false + 2006-09-02T09:41:28.718+01:00 + true + admin + workspace + false + + + update + + + d36b6897-3a5e-11db-ae95-09a9ea11d246 + SpacesStore + + + + + + + + + + false + + + + d36b6892-3a5e-11db-ae95-09a9ea11d246 + 484 + 2006-09-02T09:41:28.640+01:00 + admin + 2006-09-02T09:41:28.734+01:00 + + + + admin + composite-action + workspace + d36b6892-3a5e-11db-ae95-09a9ea11d246 + SpacesStore + + + + + + + + + + admin + 2006-09-02T09:41:28.734+01:00 + d36b6893-3a5e-11db-ae95-09a9ea11d246 + admin + 485 + no-condition + workspace + false + d36b6893-3a5e-11db-ae95-09a9ea11d246 + SpacesStore + 2006-09-02T09:41:28.656+01:00 + + + + + + + + + + + + false + + + + d36b6894-3a5e-11db-ae95-09a9ea11d246 + 486 + 2006-09-02T09:41:28.687+01:00 + admin + 2006-09-02T09:41:28.734+01:00 + + + + admin + script + workspace + d36b6894-3a5e-11db-ae95-09a9ea11d246 + SpacesStore + + + + + + + + + + + /app:company_home/app:dictionary/app:scripts/cm:record_lifecycle.js + + admin + script-ref + 2006-09-02T09:41:28.734+01:00 + d37c3179-3a5e-11db-ae95-09a9ea11d246 + admin + 487 + workspace + d37c3179-3a5e-11db-ae95-09a9ea11d246 + SpacesStore + 2006-09-02T09:41:28.703+01:00 + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/config/alfresco/content-services-context.xml b/config/alfresco/content-services-context.xml index a230559c59..97c936a8b5 100644 --- a/config/alfresco/content-services-context.xml +++ b/config/alfresco/content-services-context.xml @@ -1,247 +1,247 @@ - - - - - - - - ${dir.contentstore} - - - - - - - ${dir.contentstore.deleted} - - - - - - - - - - - - - - - - - - - - - - - - 14 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - classpath:alfresco/mimetype/mimetype-map.xml - classpath:alfresco/mimetype/mimetype-map-openoffice.xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - application/pdf - text/plain - - - - - - - - - - - - application/msword - text/plain - - - - - - - - - - - - - classpath:alfresco/mimetype/openoffice-document-formats.xml - - - - - - - - - - - - - application/pdf - - - - - - - - - - - - - imconvert "${source}" ${options} "${target}" - - - convert ${source} ${options} ${target} - - - - - - - - - - - - - + + + + + + + + ${dir.contentstore} + + + + + + + ${dir.contentstore.deleted} + + + + + + + + + + + + + + + + + + + + + + + + 14 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + classpath:alfresco/mimetype/mimetype-map.xml + classpath:alfresco/mimetype/mimetype-map-openoffice.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + application/pdf + text/plain + + + + + + + + + + + + application/msword + text/plain + + + + + + + + + + + + + classpath:alfresco/mimetype/openoffice-document-formats.xml + + + + + + + + + + + + + application/pdf + + + + + + + + + + + + + imconvert "${source}" ${options} "${target}" + + + convert ${source} ${options} ${target} + + + + + + + + + + + + + diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml index 1963c14b43..e2df751357 100644 --- a/config/alfresco/core-services-context.xml +++ b/config/alfresco/core-services-context.xml @@ -419,6 +419,7 @@ alfresco/model/contentModel.xml alfresco/model/applicationModel.xml alfresco/model/forumModel.xml + alfresco/model/recordsModel.xml org/alfresco/repo/security/authentication/userModel.xml diff --git a/config/alfresco/desktop/Alfresco.exe b/config/alfresco/desktop/Alfresco.exe index 94aee691ab..2bc8239ab1 100644 Binary files a/config/alfresco/desktop/Alfresco.exe and b/config/alfresco/desktop/Alfresco.exe differ diff --git a/config/alfresco/domain/hibernate-cfg.properties b/config/alfresco/domain/hibernate-cfg.properties index 8fe1d521d4..68da4ebd1c 100644 --- a/config/alfresco/domain/hibernate-cfg.properties +++ b/config/alfresco/domain/hibernate-cfg.properties @@ -1,16 +1,16 @@ -# -# Hibernate configuration -# -hibernate.dialect=org.hibernate.dialect.MySQLInnoDBDialect - -hibernate.jdbc.use_streams_for_binary=true -hibernate.hbm2ddl.auto=update -hibernate.show_sql=false -hibernate.cache.use_query_cache=true -hibernate.max_fetch_depth=10 -hibernate.cache.provider_class=org.alfresco.repo.cache.InternalEhCacheManagerFactoryBean -hibernate.cache.use_second_level_cache=true -hibernate.default_batch_fetch_size=1 -hibernate.jdbc.batch_size=32 -hibernate.connection.release_mode=auto +# +# Hibernate configuration +# +hibernate.dialect=org.hibernate.dialect.MySQLInnoDBDialect + +hibernate.jdbc.use_streams_for_binary=true +hibernate.hbm2ddl.auto=update +hibernate.show_sql=false +hibernate.cache.use_query_cache=true +hibernate.max_fetch_depth=10 +hibernate.cache.provider_class=org.alfresco.repo.cache.InternalEhCacheManagerFactoryBean +hibernate.cache.use_second_level_cache=true +hibernate.default_batch_fetch_size=1 +hibernate.jdbc.batch_size=32 +hibernate.connection.release_mode=auto hibernate.connection.isolation=4 \ No newline at end of file diff --git a/config/alfresco/ehcache-default.xml b/config/alfresco/ehcache-default.xml index 84eeb38518..a280c23786 100644 --- a/config/alfresco/ehcache-default.xml +++ b/config/alfresco/ehcache-default.xml @@ -1,144 +1,184 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/config/alfresco/hibernate-context.xml b/config/alfresco/hibernate-context.xml index 3875f3c997..4cbfd5fe4d 100644 --- a/config/alfresco/hibernate-context.xml +++ b/config/alfresco/hibernate-context.xml @@ -1,277 +1,277 @@ - - - - - - - - - - classpath:alfresco/domain/hibernate-cfg.properties - - - - - - - true - - - - classpath:alfresco/domain/cache-strategies.properties - - - - - - - - - - - - - false - - - - - - - - org/alfresco/repo/domain/hibernate/Node.hbm.xml - org/alfresco/repo/domain/hibernate/Store.hbm.xml - org/alfresco/repo/domain/hibernate/Transaction.hbm.xml - org/alfresco/repo/domain/hibernate/VersionCount.hbm.xml - org/alfresco/repo/domain/hibernate/AppliedPatch.hbm.xml - org/alfresco/repo/domain/hibernate/Permission.hbm.xml - org/alfresco/repo/avm/hibernate/AVM.hbm.xml - - - - org/alfresco/repo/audit/hibernate/Audit.hbm.xml - - - - - - - - org/jbpm/graph/action/Script.hbm.xml - org/jbpm/db/hibernate.queries.hbm.xml - org/jbpm/graph/def/ProcessDefinition.hbm.xml - org/jbpm/graph/def/Node.hbm.xml - org/jbpm/graph/def/Transition.hbm.xml - org/jbpm/graph/def/Event.hbm.xml - org/jbpm/graph/def/Action.hbm.xml - org/jbpm/graph/def/SuperState.hbm.xml - org/jbpm/graph/def/ExceptionHandler.hbm.xml - org/jbpm/instantiation/Delegation.hbm.xml - org/jbpm/graph/node/StartState.hbm.xml - org/jbpm/graph/node/EndState.hbm.xml - org/jbpm/graph/node/ProcessState.hbm.xml - org/jbpm/graph/node/Decision.hbm.xml - org/jbpm/graph/node/Fork.hbm.xml - org/jbpm/graph/node/Join.hbm.xml - org/jbpm/graph/node/State.hbm.xml - org/jbpm/graph/node/TaskNode.hbm.xml - org/jbpm/context/def/ContextDefinition.hbm.xml - org/jbpm/context/def/VariableAccess.hbm.xml - org/jbpm/taskmgmt/def/TaskMgmtDefinition.hbm.xml - org/jbpm/taskmgmt/def/Swimlane.hbm.xml - org/jbpm/taskmgmt/def/Task.hbm.xml - org/jbpm/taskmgmt/def/TaskController.hbm.xml - org/jbpm/module/def/ModuleDefinition.hbm.xml - org/jbpm/bytes/ByteArray.hbm.xml - org/jbpm/file/def/FileDefinition.hbm.xml - org/jbpm/scheduler/def/CreateTimerAction.hbm.xml - org/jbpm/scheduler/def/CancelTimerAction.hbm.xml - org/jbpm/graph/exe/Comment.hbm.xml - org/jbpm/graph/exe/ProcessInstance.hbm.xml - org/jbpm/graph/exe/Token.hbm.xml - org/jbpm/graph/exe/RuntimeAction.hbm.xml - org/jbpm/module/exe/ModuleInstance.hbm.xml - org/jbpm/context/exe/ContextInstance.hbm.xml - org/jbpm/context/exe/TokenVariableMap.hbm.xml - org/jbpm/context/exe/VariableInstance.hbm.xml - org/jbpm/context/exe/variableinstance/ByteArrayInstance.hbm.xml - org/jbpm/context/exe/variableinstance/DateInstance.hbm.xml - org/jbpm/context/exe/variableinstance/DoubleInstance.hbm.xml - org/jbpm/context/exe/variableinstance/HibernateLongInstance.hbm.xml - org/jbpm/context/exe/variableinstance/HibernateStringInstance.hbm.xml - org/jbpm/context/exe/variableinstance/LongInstance.hbm.xml - org/jbpm/context/exe/variableinstance/NullInstance.hbm.xml - org/jbpm/context/exe/variableinstance/StringInstance.hbm.xml - org/jbpm/msg/Message.hbm.xml - org/jbpm/msg/db/TextMessage.hbm.xml - org/jbpm/command/ExecuteActionCommand.hbm.xml - org/jbpm/command/ExecuteNodeCommand.hbm.xml - org/jbpm/command/SignalCommand.hbm.xml - org/jbpm/command/TaskInstanceEndCommand.hbm.xml - org/jbpm/taskmgmt/exe/TaskMgmtInstance.hbm.xml - org/jbpm/taskmgmt/exe/TaskInstance.hbm.xml - org/jbpm/taskmgmt/exe/PooledActor.hbm.xml - org/jbpm/taskmgmt/exe/SwimlaneInstance.hbm.xml - org/jbpm/scheduler/exe/Timer.hbm.xml - org/jbpm/logging/log/ProcessLog.hbm.xml - org/jbpm/logging/log/MessageLog.hbm.xml - org/jbpm/logging/log/CompositeLog.hbm.xml - org/jbpm/graph/log/ActionLog.hbm.xml - org/jbpm/graph/log/NodeLog.hbm.xml - org/jbpm/graph/log/ProcessInstanceCreateLog.hbm.xml - org/jbpm/graph/log/ProcessInstanceEndLog.hbm.xml - org/jbpm/graph/log/SignalLog.hbm.xml - org/jbpm/graph/log/TokenCreateLog.hbm.xml - org/jbpm/graph/log/TokenEndLog.hbm.xml - org/jbpm/graph/log/TransitionLog.hbm.xml - org/jbpm/context/log/VariableLog.hbm.xml - org/jbpm/context/log/VariableCreateLog.hbm.xml - org/jbpm/context/log/VariableDeleteLog.hbm.xml - org/jbpm/context/log/VariableUpdateLog.hbm.xml - org/jbpm/context/log/variableinstance/ByteArrayUpdateLog.hbm.xml - org/jbpm/context/log/variableinstance/DateUpdateLog.hbm.xml - org/jbpm/context/log/variableinstance/DoubleUpdateLog.hbm.xml - org/jbpm/context/log/variableinstance/HibernateLongUpdateLog.hbm.xml - org/jbpm/context/log/variableinstance/HibernateStringUpdateLog.hbm.xml - org/jbpm/context/log/variableinstance/LongUpdateLog.hbm.xml - org/jbpm/context/log/variableinstance/StringUpdateLog.hbm.xml - org/jbpm/taskmgmt/log/TaskLog.hbm.xml - org/jbpm/taskmgmt/log/TaskCreateLog.hbm.xml - org/jbpm/taskmgmt/log/TaskAssignLog.hbm.xml - org/jbpm/taskmgmt/log/TaskEndLog.hbm.xml - org/jbpm/taskmgmt/log/SwimlaneLog.hbm.xml - org/jbpm/taskmgmt/log/SwimlaneCreateLog.hbm.xml - org/jbpm/taskmgmt/log/SwimlaneAssignLog.hbm.xml - - - org/alfresco/repo/workflow/jbpm/WorkflowTaskInstance.hbm.xml - - - - - - ${cache.strategy} - ${cache.strategy} - ${cache.strategy} - ${cache.strategy} - ${cache.strategy} - ${cache.strategy} - ${cache.strategy} - ${cache.strategy} - ${cache.strategy} - - ${cache.strategy} - ${cache.strategy} - ${cache.strategy} - ${cache.strategy} - - ${cache.strategy} - ${cache.strategy} - ${cache.strategy} - - - - - ${cache.strategy} - ${cache.strategy} - ${cache.strategy} - - ${cache.strategy} - ${cache.strategy} - - - - - - - - SYNCHRONIZATION_ALWAYS - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - org.alfresco.repo.node.db.NodeDaoService - - - - - - - dbNodeDaoServiceTxnRegistration - - - - - - - - - - - - - - + + + + + + + + + + classpath:alfresco/domain/hibernate-cfg.properties + + + + + + + true + + + + classpath:alfresco/domain/cache-strategies.properties + + + + + + + + + + + + + false + + + + + + + + org/alfresco/repo/domain/hibernate/Node.hbm.xml + org/alfresco/repo/domain/hibernate/Store.hbm.xml + org/alfresco/repo/domain/hibernate/Transaction.hbm.xml + org/alfresco/repo/domain/hibernate/VersionCount.hbm.xml + org/alfresco/repo/domain/hibernate/AppliedPatch.hbm.xml + org/alfresco/repo/domain/hibernate/Permission.hbm.xml + org/alfresco/repo/avm/hibernate/AVM.hbm.xml + + + + org/alfresco/repo/audit/hibernate/Audit.hbm.xml + + + + + + + + org/jbpm/graph/action/Script.hbm.xml + org/jbpm/db/hibernate.queries.hbm.xml + org/jbpm/graph/def/ProcessDefinition.hbm.xml + org/jbpm/graph/def/Node.hbm.xml + org/jbpm/graph/def/Transition.hbm.xml + org/jbpm/graph/def/Event.hbm.xml + org/jbpm/graph/def/Action.hbm.xml + org/jbpm/graph/def/SuperState.hbm.xml + org/jbpm/graph/def/ExceptionHandler.hbm.xml + org/jbpm/instantiation/Delegation.hbm.xml + org/jbpm/graph/node/StartState.hbm.xml + org/jbpm/graph/node/EndState.hbm.xml + org/jbpm/graph/node/ProcessState.hbm.xml + org/jbpm/graph/node/Decision.hbm.xml + org/jbpm/graph/node/Fork.hbm.xml + org/jbpm/graph/node/Join.hbm.xml + org/jbpm/graph/node/State.hbm.xml + org/jbpm/graph/node/TaskNode.hbm.xml + org/jbpm/context/def/ContextDefinition.hbm.xml + org/jbpm/context/def/VariableAccess.hbm.xml + org/jbpm/taskmgmt/def/TaskMgmtDefinition.hbm.xml + org/jbpm/taskmgmt/def/Swimlane.hbm.xml + org/jbpm/taskmgmt/def/Task.hbm.xml + org/jbpm/taskmgmt/def/TaskController.hbm.xml + org/jbpm/module/def/ModuleDefinition.hbm.xml + org/jbpm/bytes/ByteArray.hbm.xml + org/jbpm/file/def/FileDefinition.hbm.xml + org/jbpm/scheduler/def/CreateTimerAction.hbm.xml + org/jbpm/scheduler/def/CancelTimerAction.hbm.xml + org/jbpm/graph/exe/Comment.hbm.xml + org/jbpm/graph/exe/ProcessInstance.hbm.xml + org/jbpm/graph/exe/Token.hbm.xml + org/jbpm/graph/exe/RuntimeAction.hbm.xml + org/jbpm/module/exe/ModuleInstance.hbm.xml + org/jbpm/context/exe/ContextInstance.hbm.xml + org/jbpm/context/exe/TokenVariableMap.hbm.xml + org/jbpm/context/exe/VariableInstance.hbm.xml + org/jbpm/context/exe/variableinstance/ByteArrayInstance.hbm.xml + org/jbpm/context/exe/variableinstance/DateInstance.hbm.xml + org/jbpm/context/exe/variableinstance/DoubleInstance.hbm.xml + org/jbpm/context/exe/variableinstance/HibernateLongInstance.hbm.xml + org/jbpm/context/exe/variableinstance/HibernateStringInstance.hbm.xml + org/jbpm/context/exe/variableinstance/LongInstance.hbm.xml + org/jbpm/context/exe/variableinstance/NullInstance.hbm.xml + org/jbpm/context/exe/variableinstance/StringInstance.hbm.xml + org/jbpm/msg/Message.hbm.xml + org/jbpm/msg/db/TextMessage.hbm.xml + org/jbpm/command/ExecuteActionCommand.hbm.xml + org/jbpm/command/ExecuteNodeCommand.hbm.xml + org/jbpm/command/SignalCommand.hbm.xml + org/jbpm/command/TaskInstanceEndCommand.hbm.xml + org/jbpm/taskmgmt/exe/TaskMgmtInstance.hbm.xml + org/jbpm/taskmgmt/exe/TaskInstance.hbm.xml + org/jbpm/taskmgmt/exe/PooledActor.hbm.xml + org/jbpm/taskmgmt/exe/SwimlaneInstance.hbm.xml + org/jbpm/scheduler/exe/Timer.hbm.xml + org/jbpm/logging/log/ProcessLog.hbm.xml + org/jbpm/logging/log/MessageLog.hbm.xml + org/jbpm/logging/log/CompositeLog.hbm.xml + org/jbpm/graph/log/ActionLog.hbm.xml + org/jbpm/graph/log/NodeLog.hbm.xml + org/jbpm/graph/log/ProcessInstanceCreateLog.hbm.xml + org/jbpm/graph/log/ProcessInstanceEndLog.hbm.xml + org/jbpm/graph/log/SignalLog.hbm.xml + org/jbpm/graph/log/TokenCreateLog.hbm.xml + org/jbpm/graph/log/TokenEndLog.hbm.xml + org/jbpm/graph/log/TransitionLog.hbm.xml + org/jbpm/context/log/VariableLog.hbm.xml + org/jbpm/context/log/VariableCreateLog.hbm.xml + org/jbpm/context/log/VariableDeleteLog.hbm.xml + org/jbpm/context/log/VariableUpdateLog.hbm.xml + org/jbpm/context/log/variableinstance/ByteArrayUpdateLog.hbm.xml + org/jbpm/context/log/variableinstance/DateUpdateLog.hbm.xml + org/jbpm/context/log/variableinstance/DoubleUpdateLog.hbm.xml + org/jbpm/context/log/variableinstance/HibernateLongUpdateLog.hbm.xml + org/jbpm/context/log/variableinstance/HibernateStringUpdateLog.hbm.xml + org/jbpm/context/log/variableinstance/LongUpdateLog.hbm.xml + org/jbpm/context/log/variableinstance/StringUpdateLog.hbm.xml + org/jbpm/taskmgmt/log/TaskLog.hbm.xml + org/jbpm/taskmgmt/log/TaskCreateLog.hbm.xml + org/jbpm/taskmgmt/log/TaskAssignLog.hbm.xml + org/jbpm/taskmgmt/log/TaskEndLog.hbm.xml + org/jbpm/taskmgmt/log/SwimlaneLog.hbm.xml + org/jbpm/taskmgmt/log/SwimlaneCreateLog.hbm.xml + org/jbpm/taskmgmt/log/SwimlaneAssignLog.hbm.xml + + + org/alfresco/repo/workflow/jbpm/WorkflowTaskInstance.hbm.xml + + + + + + ${cache.strategy} + ${cache.strategy} + ${cache.strategy} + ${cache.strategy} + ${cache.strategy} + ${cache.strategy} + ${cache.strategy} + ${cache.strategy} + ${cache.strategy} + + ${cache.strategy} + ${cache.strategy} + ${cache.strategy} + ${cache.strategy} + + ${cache.strategy} + ${cache.strategy} + ${cache.strategy} + + + + + ${cache.strategy} + ${cache.strategy} + ${cache.strategy} + + ${cache.strategy} + ${cache.strategy} + + + + + + + + SYNCHRONIZATION_ALWAYS + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.repo.node.db.NodeDaoService + + + + + + + dbNodeDaoServiceTxnRegistration + + + + + + + + + + + + + + \ No newline at end of file diff --git a/config/alfresco/messages/schema-update.properties b/config/alfresco/messages/schema-update.properties index cbf989adc2..5cebba18a2 100644 --- a/config/alfresco/messages/schema-update.properties +++ b/config/alfresco/messages/schema-update.properties @@ -1,6 +1,6 @@ # Schema update messages -schema.update.msg.executing_script=Executing schema upgrade script: {0} +schema.update.msg.executing_script=Executing database script: {0} schema.update.err.update_failed=Schema auto-update failed schema.update.err.validation_failed=Schema validation failed schema.update.err.update_script_not_run=The following schema upgrade script needs to be executed manually: {0} diff --git a/config/alfresco/model/recordsModel.xml b/config/alfresco/model/recordsModel.xml new file mode 100644 index 0000000000..cf135f6a5a --- /dev/null +++ b/config/alfresco/model/recordsModel.xml @@ -0,0 +1,458 @@ + + + + + + + + + Records Management Model + John Newton + 1.0 + + + + + + + + + + + + + + + + + + + + + + RM: User Specified Data + + + Privacy Act System + d:text + false + + + + + + + + RM: File Plan + + + Record Category Identifier + d:text + true + + + Disposition Authority + d:text + true + + + Permanent Record Indicator + d:boolean + true + + + Disposition Instructions + d:text + true + + + Contains Records Folders + d:boolean + true + + + Name of Folders + d:text + false + + + Default Media Format + d:category + false + + + Default Marking List + d:category + false + true + + + Default Originating Org + d:text + false + + + + + Vital Record Indicator + d:boolean + true + + + Vital Record Review Period + d:category + true + + + + + Process Cutoff + d:boolean + false + + + Event Trigger + d:text + false + + + Cutoff Period + d:category + false + + + Cutoff When Obsolete + d:boolean + false + + + Cutoff When Superseded + d:boolean + false + + + + + Process Hold + d:boolean + false + + + Hold Period in Years + d:float + false + + + Discretionary Hold + d:boolean + false + + + + + Process Transfer + d:boolean + false + + + Transfer Location + d:text + false + + + Transfer Blocksize in Years + d:float + false + + + + + Process Accession + d:boolean + false + + + Time to Accession in Years + d:float + false + + + Accession Location + d:text + false + + + Accession Blocksize in Years + d:float + false + + + + + Process Destruction + d:boolean + false + + + Destruction Location + d:text + false + + + + + Note + d:text + false + + + Record Counter + d:int + false + + + + rma:userSpecifiedData + + + + + + RM: Superseded + + + Superceded By + + rma:supersededBy + false + true + + + cm:content + rma:supersedes + false + true + + + + + + + + + RM: Record + + + Unique Record Identifier + d:text + true + + + Subject + d:text + + + Format + d:text + true + + + Media Format + d:category + true + + + Date Filed + d:datetime + true + + + Publication Date + d:datetime + true + + + Date Received + d:datetime + false + + + Originator + d:text + true + + + Originating Organization + d:text + true + + + Addressee + d:text + true + + + Other Addressees + d:text + false + + + Supplemental Marking List + d:category + false + true + + + Obsolete + d:boolean + false + + + Note + d:text + false + + + + cm:auditable + cm:author + rma:userSpecifiedData + cm:referencing + rma:superseded + + + + + + RM: Vital Record + + + Vital Record + d:boolean + false + + + Last Review Date + d:datetime + false + + + Next Review Date + d:datetime + false + + + + + + + + RM: Cutoffable + + + Cutoff Executed + d:boolean + false + + + Cutoff Now + d:boolean + false + + + Cutoff Date + d:datetime + false + + + Cutoff Event + d:text + false + + + + + + + + RM: Holdable + + + Hold Executed + d:boolean + false + + + Hold Until + d:datetime + false + + + Hold Until Event + d:text + false + + + Freeze + d:boolean + false + + + + + + + + RM: Transferable + + + Transfer Executed + d:boolean + false + + + Transfer Date + d:datetime + false + + + + + + + + RM: Accessionable + + + Accession Executed + d:boolean + false + + + Accession Date + d:datetime + false + + + + + + + + RM: Destroyable + + + Destruction Date + d:datetime + false + + + + + sys:temporary + + + + + + \ No newline at end of file diff --git a/config/alfresco/public-services-context.xml b/config/alfresco/public-services-context.xml index 8c8d044cb8..4b5432d7fc 100644 --- a/config/alfresco/public-services-context.xml +++ b/config/alfresco/public-services-context.xml @@ -1,1136 +1,1136 @@ - - - - - - - - - - http://www.alfresco.org - - - - - - - - org.alfresco.service.ServiceRegistry - - - - - - - - - - - - - - org.alfresco.service.ServiceRegistry - - - Repository service registry - - - - - - - - - - - - org.alfresco.service.descriptor.DescriptorService - - - - - - - - - - - - - - - org.alfresco.service.descriptor.DescriptorService - - - Descriptor service - - - - - - - - - - - false - - - - - - - - - org.alfresco.service.namespace.NamespaceService - - - - - - - - - - - - - - - - - - - - - - ${server.transaction.mode.default} - - - - - - - org.alfresco.service.namespace.NamespaceService - - - Namespace service - - - - - - - - - - - - org.alfresco.service.cmr.dictionary.DictionaryService - - - - - - - - - - - - - - - - - - - - - - ${server.transaction.mode.default} - - - - - - - org.alfresco.service.cmr.dictionary.DictionaryService - - - Dictionary Service - - - - - - - - - org.alfresco.service.ServiceDescriptor - org.alfresco.service.cmr.repository.NodeService - - - - - - - - - - - - - - - - - - - - - - - ${server.transaction.mode.readOnly} - ${server.transaction.mode.readOnly} - ${server.transaction.mode.readOnly} - ${server.transaction.mode.default} - - - - - - - org.alfresco.service.cmr.repository.NodeService - - - Node Service - - - - - - - - org.alfresco.service.cmr.repository.ContentService - - - - - - - - - - - - - - - - - - - - - - ${server.transaction.mode.default} - - - - - - - org.alfresco.service.cmr.repository.ContentService - - - Content Service - - - - - - - - org.alfresco.service.cmr.repository.MimetypeService - - - - - - - - - - - - - - - - - org.alfresco.service.cmr.repository.MimetypeService - - - Mime Type Service - - - - - - - - org.alfresco.service.cmr.search.SearchService - - - - - - - - - - - - - - - - - - - - - - ${server.transaction.mode.default} - - - - - - - org.alfresco.service.cmr.search.SearchService - - - Search Service - - - - - - - - org.alfresco.service.cmr.search.CategoryService - - - - - - - - - - - - - - - - - - - - - - ${server.transaction.mode.default} - - - - - - - org.alfresco.service.cmr.search.CategoryService - - - Category Service - - - - - - - - org.alfresco.service.cmr.repository.CopyService - - - - - - - - - - - - - - - - - - - - - - ${server.transaction.mode.default} - - - - - - - org.alfresco.service.cmr.repository.CopyService - - - Copy Service - - - - - - - - org.alfresco.service.cmr.lock.LockService - - - - - - - - - - - - - - - - - - - - - - ${server.transaction.mode.default} - - - - - - - org.alfresco.service.cmr.lock.LockService - - - Lock Service - - - - - - - - org.alfresco.service.cmr.version.VersionService - - - - - - - - - - - - - - - - - - - - - - ${server.transaction.mode.default} - - - - - - - org.alfresco.service.cmr.version.VersionService - - - Version Service - - - - - - - - org.alfresco.service.cmr.coci.CheckOutCheckInService - - - - - - - - - - - - - - - - - - - - - - ${server.transaction.mode.default} - - - - - - - org.alfresco.service.cmr.coci.CheckOutCheckInService - - - Version Service - - - - - - - - org.alfresco.service.cmr.rule.RuleService - - - - - - - - - - - - - - - - - - - - - - ${server.transaction.mode.default} - - - - - - - org.alfresco.service.cmr.rule.RuleService - - - Rule Service - - - - - - - - org.alfresco.service.cmr.view.ImporterService - - - - - - - - - - - - - - - - - - - - - - ${server.transaction.mode.default} - - - - - - - org.alfresco.service.cmr.view.ImporterService - - - Importer Service - - - - - - - - org.alfresco.service.cmr.view.ExporterService - - - - - - - - - - - - - - - - - - - - ${server.transaction.mode.default} - - - - - - - org.alfresco.service.cmr.view.ExporterService - - - Exporter Service - - - - - - - - org.alfresco.service.cmr.action.ActionService - - - - - - - - - - - - - - - - - - - - - - ${server.transaction.mode.default} - - - - - - - org.alfresco.service.cmr.action.ActionService - - - Action Service - - - - - - - - org.alfresco.service.cmr.security.PermissionService - - - - - - - - - - - - - - - - - - - - - - ${server.transaction.mode.default} - - - - - - - org.alfresco.service.cmr.security.PermissionService - - - Permission Service - - - - - - - - org.alfresco.service.cmr.security.AuthorityService - - - - - - - - - - - - - - - - - - - - - - ${server.transaction.mode.default} - - - - - - - org.alfresco.service.cmr.security.AuthorityService - - - Authority Service - - - - - - - - org.alfresco.service.cmr.security.OwnableService - - - - - - - - - - - - - - - - - - - - - - ${server.transaction.mode.default} - - - - - - - org.alfresco.service.cmr.security.OwnableService - - - OwnableService Service - - - - - - - - org.alfresco.service.cmr.security.PersonService - - - - - - - - - - - - - - - - - - - - - - ${server.transaction.mode.default} - - - - - - - org.alfresco.service.cmr.security.PersonService - - - PersonService Service - - - - - - - - org.alfresco.service.cmr.security.AuthenticationService - - - - - - - - - - - - - - - - - - - - - - PROPAGATION_NOT_SUPPORTED, readOnly - PROPAGATION_NOT_SUPPORTED, readOnly - PROPAGATION_NOT_SUPPORTED, readOnly - PROPAGATION_NOT_SUPPORTED, readOnly - PROPAGATION_NOT_SUPPORTED, readOnly - PROPAGATION_NOT_SUPPORTED, readOnly - ${server.transaction.mode.default} - - - - - - - org.alfresco.service.cmr.security.AuthenticationService - - - AuthenticationService Service - - - - - - - - org.alfresco.service.cmr.repository.TemplateService - - - - - - - - - - - - - - - - - - - - - - ${server.transaction.mode.default} - - - - - - - org.alfresco.service.cmr.repository.TemplateService - - - TemplateService Service - - - - - - - - org.alfresco.service.cmr.repository.ScriptService - - - - - - - - - - - - - - - - - - - - - - ${server.transaction.mode.default} - - - - - - - org.alfresco.service.cmr.repository.ScriptService - - - ScriptService Service - - - - - - - - org.alfresco.service.cmr.model.FileFolderService - - - - - - - - - - - - - - - - - - - - - - ${server.transaction.mode.readOnly} - ${server.transaction.mode.readOnly} - ${server.transaction.mode.readOnly} - ${server.transaction.mode.readOnly} - ${server.transaction.mode.default} - - - - - - - org.alfresco.service.cmr.model.FileFolderService - - - FileFolderService Service - - - - - - - - - - - - - - org.alfresco.service.cmr.avm.AVMService - - - - - - - - - - - - - - - org.alfresco.service.cmr.avm.AVMService - - - AVM Service - - - - - - - - - - ${server.transaction.mode.readOnly} - ${server.transaction.mode.default} - ${server.transaction.mode.default} - ${server.transaction.mode.readOnly} - ${server.transaction.mode.readOnly} - ${server.transaction.mode.default} - - - - - - - - - org.alfresco.service.cmr.workflow.WorkflowService - - - - - - - - - - - - - - - - - - - - - - ${server.transaction.mode.default} - - - - - - - org.alfresco.service.cmr.workflow.WorkflowService - - - Workflow Service - - - - - - - - - org.alfresco.service.cmr.audit.AuditService - - - - - - - - - - - - - - - - - - - - - - ${server.transaction.mode.default} - - - - - - - org.alfresco.service.cmr.audit.AuditService - - - Audit Service - - - - + + + + + + + + + + http://www.alfresco.org + + + + + + + + org.alfresco.service.ServiceRegistry + + + + + + + + + + + + + + org.alfresco.service.ServiceRegistry + + + Repository service registry + + + + + + + + + + + + org.alfresco.service.descriptor.DescriptorService + + + + + + + + + + + + + + + org.alfresco.service.descriptor.DescriptorService + + + Descriptor service + + + + + + + + + + + false + + + + + + + + + org.alfresco.service.namespace.NamespaceService + + + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + org.alfresco.service.namespace.NamespaceService + + + Namespace service + + + + + + + + + + + + org.alfresco.service.cmr.dictionary.DictionaryService + + + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + org.alfresco.service.cmr.dictionary.DictionaryService + + + Dictionary Service + + + + + + + + + org.alfresco.service.ServiceDescriptor + org.alfresco.service.cmr.repository.NodeService + + + + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.readOnly} + ${server.transaction.mode.readOnly} + ${server.transaction.mode.readOnly} + ${server.transaction.mode.default} + + + + + + + org.alfresco.service.cmr.repository.NodeService + + + Node Service + + + + + + + + org.alfresco.service.cmr.repository.ContentService + + + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + org.alfresco.service.cmr.repository.ContentService + + + Content Service + + + + + + + + org.alfresco.service.cmr.repository.MimetypeService + + + + + + + + + + + + + + + + + org.alfresco.service.cmr.repository.MimetypeService + + + Mime Type Service + + + + + + + + org.alfresco.service.cmr.search.SearchService + + + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + org.alfresco.service.cmr.search.SearchService + + + Search Service + + + + + + + + org.alfresco.service.cmr.search.CategoryService + + + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + org.alfresco.service.cmr.search.CategoryService + + + Category Service + + + + + + + + org.alfresco.service.cmr.repository.CopyService + + + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + org.alfresco.service.cmr.repository.CopyService + + + Copy Service + + + + + + + + org.alfresco.service.cmr.lock.LockService + + + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + org.alfresco.service.cmr.lock.LockService + + + Lock Service + + + + + + + + org.alfresco.service.cmr.version.VersionService + + + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + org.alfresco.service.cmr.version.VersionService + + + Version Service + + + + + + + + org.alfresco.service.cmr.coci.CheckOutCheckInService + + + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + org.alfresco.service.cmr.coci.CheckOutCheckInService + + + Version Service + + + + + + + + org.alfresco.service.cmr.rule.RuleService + + + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + org.alfresco.service.cmr.rule.RuleService + + + Rule Service + + + + + + + + org.alfresco.service.cmr.view.ImporterService + + + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + org.alfresco.service.cmr.view.ImporterService + + + Importer Service + + + + + + + + org.alfresco.service.cmr.view.ExporterService + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + org.alfresco.service.cmr.view.ExporterService + + + Exporter Service + + + + + + + + org.alfresco.service.cmr.action.ActionService + + + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + org.alfresco.service.cmr.action.ActionService + + + Action Service + + + + + + + + org.alfresco.service.cmr.security.PermissionService + + + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + org.alfresco.service.cmr.security.PermissionService + + + Permission Service + + + + + + + + org.alfresco.service.cmr.security.AuthorityService + + + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + org.alfresco.service.cmr.security.AuthorityService + + + Authority Service + + + + + + + + org.alfresco.service.cmr.security.OwnableService + + + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + org.alfresco.service.cmr.security.OwnableService + + + OwnableService Service + + + + + + + + org.alfresco.service.cmr.security.PersonService + + + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + org.alfresco.service.cmr.security.PersonService + + + PersonService Service + + + + + + + + org.alfresco.service.cmr.security.AuthenticationService + + + + + + + + + + + + + + + + + + + + + + PROPAGATION_NOT_SUPPORTED, readOnly + PROPAGATION_NOT_SUPPORTED, readOnly + PROPAGATION_NOT_SUPPORTED, readOnly + PROPAGATION_NOT_SUPPORTED, readOnly + PROPAGATION_NOT_SUPPORTED, readOnly + PROPAGATION_NOT_SUPPORTED, readOnly + ${server.transaction.mode.default} + + + + + + + org.alfresco.service.cmr.security.AuthenticationService + + + AuthenticationService Service + + + + + + + + org.alfresco.service.cmr.repository.TemplateService + + + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + org.alfresco.service.cmr.repository.TemplateService + + + TemplateService Service + + + + + + + + org.alfresco.service.cmr.repository.ScriptService + + + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + org.alfresco.service.cmr.repository.ScriptService + + + ScriptService Service + + + + + + + + org.alfresco.service.cmr.model.FileFolderService + + + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.readOnly} + ${server.transaction.mode.readOnly} + ${server.transaction.mode.readOnly} + ${server.transaction.mode.readOnly} + ${server.transaction.mode.default} + + + + + + + org.alfresco.service.cmr.model.FileFolderService + + + FileFolderService Service + + + + + + + + + + + + + + org.alfresco.service.cmr.avm.AVMService + + + + + + + + + + + + + + + org.alfresco.service.cmr.avm.AVMService + + + AVM Service + + + + + + + + + + ${server.transaction.mode.readOnly} + ${server.transaction.mode.default} + ${server.transaction.mode.default} + ${server.transaction.mode.readOnly} + ${server.transaction.mode.readOnly} + ${server.transaction.mode.default} + + + + + + + + + org.alfresco.service.cmr.workflow.WorkflowService + + + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + org.alfresco.service.cmr.workflow.WorkflowService + + + Workflow Service + + + + + + + + + org.alfresco.service.cmr.audit.AuditService + + + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + org.alfresco.service.cmr.audit.AuditService + + + Audit Service + + + + diff --git a/config/alfresco/templates/content/examples/records_report.ftl b/config/alfresco/templates/content/examples/records_report.ftl new file mode 100644 index 0000000000..0d1878a933 --- /dev/null +++ b/config/alfresco/templates/content/examples/records_report.ftl @@ -0,0 +1,149 @@ +<#-- Records Report - Template to apply to a records space to report on status of records --> + + + +<#assign datetimeformat="dd MMM yyyy HH:mm"> +<#assign xqueryformat="'yyyy-MM-dd'T'HH:mm:ss.000'Z'"> +<#-- space.childrenByXPath[".//*[@rma:nextReviewDate < '${date?string(xqueryformat)}']"]?sort_by(['properties', 'rma:nextReviewDate']) --> + +<#macro standardHeaders title extra=""> +

${title}

+ + + ID + + Title + + + File Plan + Originator + Date Filed + ${extra} + + + +<#macro standardProperties child extraProperty=""> + + + <#assign childRef=child.nodeRef> + <#assign childWorkspace=childRef[0..childRef?index_of("://")-1]> + <#assign childStorenode=childRef[childRef?index_of("://")+3..]> + <#assign childPath="${childWorkspace}/${childStorenode}"> + + <#if child.parent.hasAspect("rma:filePlan")> + <#assign fileplan=child.parent> + <#elseif child.parent.parent?exists && child.parent.hasAspect("rma:filePlan")> + <#assign fileplan=child.parent.parent> + <#else> + <#assign fileplan=child.parent> + + + <#assign fpRef=fileplan.nodeRef> + <#assign fpWorkspace=fpRef[0..fpRef?index_of("://")-1]> + <#assign fpStorenode=fpRef[fpRef?index_of("://")+3..]> + <#assign fileplanPath="${fpWorkspace}/${fpStorenode}"> + + <#-- Record properties icon --> + + Record Details + + <#-- Record identifier --> + + ${child.properties["rma:recordIdentifier"]} + + <#-- Record icon --> + + View Record + + <#-- Record title --> + + ${child.properties["cm:title"]} + + <#-- Fileplan icon --> + + Fileplan Contents + + <#-- Fileplan properties icon --> + + Fileplan Details + + + + ${fileplan.name} + + + ${child.properties["rma:originator"]} + + + ${child.properties["rma:dateFiled"]?string(datetimeformat)} + + + ${extraProperty} + + + + +<#macro standardFooters> +
+ + + + + + +<@standardHeaders title="Recent Records" extra="Date"/> +<#list space.childrenByXPath[".//*[@rma:dateReceived]"]?sort_by(['properties', 'rma:dateReceived']) as child> + <#if (dateCompare(child.properties["cm:modified"], date, 1000*60*60*24*7) == 1) || (dateCompare(child.properties["cm:created"], date, 1000*60*60*24*7) == 1)> + <@standardProperties child=child extraProperty=child.properties["rma:dateReceived"]?string(datetimeformat) /> + + +<@standardFooters/> + + +<@standardHeaders title="Vital Records Due for Review" extra="Next Review Due"/> +<#list space.childrenByXPath[".//*[@rma:nextReviewDate]"]?sort_by(['properties', 'rma:nextReviewDate']) as child> + <#if (dateCompare(date, child.properties["rma:nextReviewDate"], 1000*60*60*24*7) == 1)> + <@standardProperties child=child extraProperty=child.properties["rma:nextReviewDate"]?string(datetimeformat) /> + + +<@standardFooters/> + + +<@standardHeaders title="Records Due for Cutoff" extra="Cutoff Date"/> +<#list space.childrenByXPath[".//*[@rma:cutoffDateTime]"]?sort_by(['properties', 'rma:cutoffDateTime']) as child> + <#if (dateCompare(date, child.properties["rma:cutoffDateTime"], 1000*60*60*24*7) == 1)> + <@standardProperties child=child extraProperty=child.properties["rma:cutoffDateTime"]?string(datetimeformat) /> + + +<@standardFooters/> + + +<@standardHeaders title="Records Retention Due for Expiry" extra="Expiry Date"/> +<#list space.childrenByXPath[".//*[@rma:holdUntil]"]?sort_by(['properties', 'rma:holdUntil']) as child> + <#if (dateCompare(date, child.properties["rma:holdUntil"], 1000*60*60*24*7) == 1)> + <@standardProperties child=child extraProperty=child.properties["rma:holdUntil"]?string(datetimeformat) /> + + +<@standardFooters/> + + +<@standardHeaders title="Records Due for Transfer" extra="Transfer Date"/> +<#list space.childrenByXPath[".//*[@rma:transferDate]"]?sort_by(['properties', 'rma:transferDate']) as child> + <#if (dateCompare(date, child.properties["rma:transferDate"], 1000*60*60*24*7) == 1)> + <@standardProperties child=child extraProperty=child.properties["rma:transferDate"]?string(datetimeformat) /> + + +<@standardFooters/> + + +<@standardHeaders title="Records Due for Destruction" extra="Destruction Due Date"/> +<#list space.childrenByXPath[".//*[@rma:destructionDate]"]?sort_by(['properties', 'rma:destructionDate']) as child> + <#if (dateCompare(date, child.properties["rma:destructionDate"], 1000*60*60*24*7) == 1)> + <@standardProperties child=child extraProperty=child.properties["rma:destructionDate"]?string(datetimeformat) /> + + +<@standardFooters/> + +
\ No newline at end of file diff --git a/config/alfresco/templates/content_template_examples.xml b/config/alfresco/templates/content_template_examples.xml index e0ae7574bc..85ac61c026 100644 --- a/config/alfresco/templates/content_template_examples.xml +++ b/config/alfresco/templates/content_template_examples.xml @@ -126,4 +126,18 @@ + + + + + + + true + Displays the current state of records in a file plan space or a space containing a file plan. + contentUrl=classpath:alfresco/templates/content/examples/records_report.ftl|mimetype=text/plain|size=6134|encoding=UTF-8 + records_report.ftl + records_report.ftl + + + \ No newline at end of file diff --git a/source/cpp/CAlfrescoApp/CAlfrescoApp.cpp b/source/cpp/CAlfrescoApp/CAlfrescoApp.cpp index 2120081d24..94aa66b959 100644 --- a/source/cpp/CAlfrescoApp/CAlfrescoApp.cpp +++ b/source/cpp/CAlfrescoApp/CAlfrescoApp.cpp @@ -37,14 +37,14 @@ using namespace Alfresco; // CCAlfrescoAppApp -BEGIN_MESSAGE_MAP(CCAlfrescoAppApp, CWinApp) +BEGIN_MESSAGE_MAP(CAlfrescoApp, CWinApp) ON_COMMAND(ID_HELP, CWinApp::OnHelp) END_MESSAGE_MAP() -// CCAlfrescoAppApp construction +// CCAlfrescoApp construction -CCAlfrescoAppApp::CCAlfrescoAppApp() +CAlfrescoApp::CAlfrescoApp() { // TODO: add construction code here, // Place all significant initialization in InitInstance @@ -53,12 +53,12 @@ CCAlfrescoAppApp::CCAlfrescoAppApp() // The one and only CCAlfrescoAppApp object -CCAlfrescoAppApp theApp; +CAlfrescoApp theApp; // CCAlfrescoAppApp initialization -BOOL CCAlfrescoAppApp::InitInstance() +BOOL CAlfrescoApp::InitInstance() { // InitCommonControls() is required on Windows XP if an application // manifest specifies use of ComCtl32.dll version 6 or later to enable @@ -181,8 +181,8 @@ BOOL CCAlfrescoAppApp::InitInstance() * @param params DesktopParams& * @return bool */ -bool CCAlfrescoAppApp::buildDesktopParameters( AlfrescoInterface& alfresco, StringList& paths, AlfrescoActionInfo& actionInfo, - DesktopParams& params) { +bool CAlfrescoApp::buildDesktopParameters( AlfrescoInterface& alfresco, StringList& paths, AlfrescoActionInfo& actionInfo, + DesktopParams& params) { // If there are no paths then just return a success @@ -401,7 +401,7 @@ bool CCAlfrescoAppApp::buildDesktopParameters( AlfrescoInterface& alfresco, Stri * @param aborted bool& * @return bool */ -bool CCAlfrescoAppApp::copyFilesUsingShell(const String& fromFileFolder, const String& toFolder, bool& aborted) { +bool CAlfrescoApp::copyFilesUsingShell(const String& fromFileFolder, const String& toFolder, bool& aborted) { // Build the from/to paths, must be double null terminated @@ -456,7 +456,7 @@ bool CCAlfrescoAppApp::copyFilesUsingShell(const String& fromFileFolder, const S * @param actionInfo AlfrescoActionInfo& * @return bool */ -bool CCAlfrescoAppApp::runAction( AlfrescoInterface& alfresco, StringList& pathList, AlfrescoActionInfo& actionInfo) { +bool CAlfrescoApp::runAction( AlfrescoInterface& alfresco, StringList& pathList, AlfrescoActionInfo& actionInfo) { // Build the desktop action parameter list, perform any file copying of local files @@ -484,24 +484,9 @@ bool CCAlfrescoAppApp::runAction( AlfrescoInterface& alfresco, StringList& pathL if ( response.getStatus() == StsCommandLine) { - // Initialize the startup information - - STARTUPINFO startupInfo; - memset(&startupInfo, 0, sizeof(STARTUPINFO)); - // Launch a process using the command line - PROCESS_INFORMATION processInfo; - memset(&processInfo, 0, sizeof(PROCESS_INFORMATION)); - - if ( CreateProcess( response.getStatusMessage().data(), NULL, NULL, NULL, true, 0, NULL, NULL, - &startupInfo, &processInfo) == false) { - CString msg; - msg.FormatMessage( L"Failed to launch command line\n\n%1\n\nError %2!d!", response.getStatusMessage().data(), GetLastError()); - AfxMessageBox( msg, MB_OK | MB_ICONERROR); - } - else - sts = true; + sts = doCommandLine( alfresco, response.getStatusMessage()); } // Check if a web browser should be launched with a URL @@ -510,14 +495,7 @@ bool CCAlfrescoAppApp::runAction( AlfrescoInterface& alfresco, StringList& pathL // Use the Windows shell to open the URL - HINSTANCE shellSts = ShellExecute( NULL, NULL, response.getStatusMessage().data(), NULL, NULL, SW_SHOWNORMAL); - if (( int) shellSts < 32) { - CString msg; - msg.FormatMessage( L"Failed to launch URL\n\n%1", response.getStatusMessage().data()); - AfxMessageBox( msg, MB_OK | MB_ICONERROR); - } - else - sts = true; + sts = doURL( alfresco, response.getStatusMessage()); } // Error status @@ -572,3 +550,168 @@ bool CCAlfrescoAppApp::runAction( AlfrescoInterface& alfresco, StringList& pathL return sts; } + +/** + * Launch a command line + * + * @param alfresco AlfrescoInterface& + * @param cmdStr const String& + * @return bool + */ +bool CAlfrescoApp::doCommandLine( AlfrescoInterface& alfresco, const String& cmdStr) { + + // Check if the command line contains any environment variables/tokens + + String cmdLine = cmdStr; + int pos = cmdLine.indexOf( L'%'); + + if ( pos != -1) { + + // Command line contains environment variables or other tokens that must be replaced + + String newCmdLine = L""; + if (pos > 0) + newCmdLine = cmdLine.substring( 0, pos); + + wchar_t envBuf[256]; + size_t envLen; + + while ( pos != -1) { + + // Find the end of the current token + + int endPos = cmdLine.indexOf ( L'%', pos + 1); + + if ( endPos == -1) { + CString msg; + msg.FormatMessage( L"Bad token in command line\n\n%1", cmdLine.data()); + AfxMessageBox( msg, MB_OK | MB_ICONERROR); + + return false; + } + + // Extract the token + + String token = cmdLine.substring( pos + 1, endPos); + + // Replace the token with an environment variable value or other values + + if ( token.equals( L"AlfrescoDir")) { + + // Use the local path to the Alfresco folder that the application is running from + + newCmdLine.append( alfresco.getUNCPath()); + } + else { + + // Find the environment variable value + + envLen = sizeof( envBuf)/sizeof(wchar_t); + const wchar_t* pEnvName = token.data(); + + if ( _wgetenv_s( &envLen, envBuf, envLen, pEnvName) == 0) { + + // Append the environment variable value + + newCmdLine.append( envBuf); + } + else { + + // Error converting the environment variable + + CString msg; + msg.FormatMessage( L"Failed to convert environment variable\n\n%1\n\n%2", token.data(), cmdLine.data()); + AfxMessageBox( msg, MB_OK | MB_ICONERROR); + + return false; + } + } + + // Update the token search position + + pos = endPos + 1; + + if (( unsigned int) pos < cmdStr.length()) { + + // Search for the next token + + pos = cmdLine.indexOf( L'%', pos); + } + else { + + // End of string, finish the token search + + pos = -1; + } + + // Append the normal string between tokens + + if ( pos > (endPos + 1)) { + + // Get the between token sting + + String filler = cmdLine.substring( endPos + 1, pos); + newCmdLine.append( filler); + } + else if ( pos == -1) { + + // Append the remaining string + + String filler = cmdLine.substring( endPos + 1); + newCmdLine.append( filler); + } + } + + // Update the command line + + cmdLine = newCmdLine; + } + + // Initialize the startup information + + STARTUPINFO startupInfo; + memset(&startupInfo, 0, sizeof(STARTUPINFO)); + + // Launch a process using the command line + + PROCESS_INFORMATION processInfo; + memset(&processInfo, 0, sizeof(PROCESS_INFORMATION)); + + bool sts = false; + + if ( CreateProcess( NULL, (LPWSTR) cmdLine.data(), NULL, NULL, true, 0, NULL, NULL, + &startupInfo, &processInfo) == false) { + CString msg; + msg.FormatMessage( L"Failed to launch command line\n\n%1\n\nError %2!d!", cmdLine.data(), GetLastError()); + AfxMessageBox( msg, MB_OK | MB_ICONERROR); + } + else + sts = true; + + return sts; +} + +/** + * Browse to a URL + * + * @param alfresco AlfrescoInterface& + * @param url const String& + * @return bool + */ +bool CAlfrescoApp::doURL( AlfrescoInterface& alfresco, const String& url) { + + // Use the Windows shell to open the URL + + bool sts = false; + + HINSTANCE shellSts = ShellExecute( NULL, NULL, url.data(), NULL, NULL, SW_SHOWNORMAL); + if (( int) shellSts < 32) { + CString msg; + msg.FormatMessage( L"Failed to launch URL\n\n%1", url.data()); + AfxMessageBox( msg, MB_OK | MB_ICONERROR); + } + else + sts = true; + + return sts; +} diff --git a/source/cpp/CAlfrescoApp/CAlfrescoApp.h b/source/cpp/CAlfrescoApp/CAlfrescoApp.h index 39ac0c6018..590ac23244 100644 --- a/source/cpp/CAlfrescoApp/CAlfrescoApp.h +++ b/source/cpp/CAlfrescoApp/CAlfrescoApp.h @@ -30,14 +30,14 @@ using namespace Alfresco; -// CCAlfrescoAppApp: +// CAlfrescoApp: // See CAlfrescoApp.cpp for the implementation of this class // -class CCAlfrescoAppApp : public CWinApp +class CAlfrescoApp : public CWinApp { public: - CCAlfrescoAppApp(); + CAlfrescoApp(); // Overrides public: @@ -59,6 +59,11 @@ private: // Run the action bool runAction( AlfrescoInterface& alfresco, StringList& pathList, AlfrescoActionInfo& actionInfo); + + // Post-process actions, command line launch and browse to URL + + bool doCommandLine( AlfrescoInterface& alfresco, const String& cmdLine); + bool doURL( AlfrescoInterface& alfresco, const String& url); }; -extern CCAlfrescoAppApp theApp; \ No newline at end of file +extern CAlfrescoApp theApp; \ No newline at end of file diff --git a/source/cpp/CAlfrescoApp/source/util/String.cpp b/source/cpp/CAlfrescoApp/source/util/String.cpp index 27ddbe069d..3723818db3 100644 --- a/source/cpp/CAlfrescoApp/source/util/String.cpp +++ b/source/cpp/CAlfrescoApp/source/util/String.cpp @@ -676,7 +676,8 @@ void String::append ( const char* str) { * @param str const wchar_t* */ void String::append (const wchar_t* str) { - m_string += str; + while ( *str != 0) + m_string += *str++; } /** diff --git a/source/java/org/alfresco/filesys/ftp/FTPDate.java b/source/java/org/alfresco/filesys/ftp/FTPDate.java index 7436de2880..c43a561e67 100644 --- a/source/java/org/alfresco/filesys/ftp/FTPDate.java +++ b/source/java/org/alfresco/filesys/ftp/FTPDate.java @@ -41,10 +41,10 @@ public class FTPDate * Pack a date string in Unix format The format is 'Mmm dd hh:mm' if the file is less than six * months old, else the format is 'Mmm dd yyyy'. * - * @param buf StringBuffer + * @param buf StringBuilder * @param dt Date */ - public final static void packUnixDate(StringBuffer buf, Date dt) + public final static void packUnixDate(StringBuilder buf, Date dt) { // Check if the date is valid diff --git a/source/java/org/alfresco/filesys/ftp/FTPSrvSession.java b/source/java/org/alfresco/filesys/ftp/FTPSrvSession.java index 9b59e7db2d..16a9daa11c 100644 --- a/source/java/org/alfresco/filesys/ftp/FTPSrvSession.java +++ b/source/java/org/alfresco/filesys/ftp/FTPSrvSession.java @@ -1320,11 +1320,10 @@ public class FTPSrvSession extends SrvSession implements Runnable // Output the file information to the client - StringBuffer str = new StringBuffer(256); + StringBuilder str = new StringBuilder(256); for (FileInfo finfo : files) { - // Build the output record str.setLength(0); diff --git a/source/java/org/alfresco/filesys/server/auth/passthru/PassthruAuthenticator.java b/source/java/org/alfresco/filesys/server/auth/passthru/PassthruAuthenticator.java index 7d539310e9..00e0fb61f5 100644 --- a/source/java/org/alfresco/filesys/server/auth/passthru/PassthruAuthenticator.java +++ b/source/java/org/alfresco/filesys/server/auth/passthru/PassthruAuthenticator.java @@ -180,7 +180,7 @@ public class PassthruAuthenticator extends CifsAuthenticator implements SessionL // using the session that has already been setup. AuthenticateSession authSess = passDetails.getAuthenticateSession(); - authSess.doSessionSetup(client.getUserName(), client.getANSIPassword(), client.getPassword()); + authSess.doSessionSetup(client.getDomain(), client.getUserName(), null, client.getANSIPassword(), client.getPassword()); // Check if the user has been logged on as a guest 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 0f98e425e3..fe39a6b277 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 @@ -113,6 +113,11 @@ public class CheckInOutDesktopAction extends DesktopAction { } catch (Exception ex) { + // Dump the error + + if ( logger.isErrorEnabled()) + logger.error("Desktop action error", ex); + // Return an error status and message response.setStatus(StsError, "Checkin failed for " + target.getTarget() + ", " + ex.getMessage()); @@ -122,6 +127,19 @@ public class CheckInOutDesktopAction extends DesktopAction { { try { + // Check if the file is locked + + if ( getNodeService().hasAspect( target.getNode(), ContentModel.ASPECT_LOCKABLE)) { + + // Get the lock type + + String lockTypeStr = (String) getNodeService().getProperty( target.getNode(), ContentModel.PROP_LOCK_TYPE); + if ( lockTypeStr != null) { + response.setStatus(StsError, "Checkout failed, file is locked"); + return response; + } + } + // Check out the file NodeRef workingCopyNode = getCheckInOutService().checkout( target.getNode()); @@ -149,6 +167,11 @@ public class CheckInOutDesktopAction extends DesktopAction { } catch (Exception ex) { + // Dump the error + + if ( logger.isErrorEnabled()) + logger.error("Desktop action error", ex); + // Return an error status and message response.setStatus(StsError, "Failed to checkout " + target.getTarget() + ", " + ex.getMessage()); diff --git a/source/java/org/alfresco/filesys/smb/server/repo/desk/CmdLineDesktopAction.java b/source/java/org/alfresco/filesys/smb/server/repo/desk/CmdLineDesktopAction.java index 6744204fdf..42625ea6a2 100644 --- a/source/java/org/alfresco/filesys/smb/server/repo/desk/CmdLineDesktopAction.java +++ b/source/java/org/alfresco/filesys/smb/server/repo/desk/CmdLineDesktopAction.java @@ -47,6 +47,6 @@ public class CmdLineDesktopAction extends DesktopAction { // Return a URL in the status message - return new DesktopResponse(StsCommandLine, "C:\\Windows\\notepad.exe"); + return new DesktopResponse(StsCommandLine, "%SystemRoot%\\notepad.exe"); } } diff --git a/source/java/org/alfresco/model/ContentModel.java b/source/java/org/alfresco/model/ContentModel.java index 1fdaaef974..c2c78a1653 100644 --- a/source/java/org/alfresco/model/ContentModel.java +++ b/source/java/org/alfresco/model/ContentModel.java @@ -1,272 +1,268 @@ -/* - * 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.model; - -import org.alfresco.service.namespace.NamespaceService; -import org.alfresco.service.namespace.QName; - - -/** - * Content Model Constants - */ -public interface ContentModel -{ - // - // System Model Definitions - // - - // base type constants - static final QName TYPE_BASE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "base"); - static final QName ASPECT_REFERENCEABLE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "referenceable"); - static final QName PROP_STORE_PROTOCOL = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "store-protocol"); - static final QName PROP_STORE_IDENTIFIER = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "store-identifier"); - static final QName PROP_NODE_UUID = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "node-uuid"); - static final QName PROP_NODE_DBID = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "node-dbid"); - - // tag for incomplete nodes - static final QName ASPECT_INCOMPLETE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "incomplete"); - - // tag for temporary nodes - static final QName ASPECT_TEMPORARY = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "temporary"); - - // archived nodes aspect constants - static final QName ASPECT_ARCHIVED = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archived"); - static final QName PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedOriginalParentAssoc"); - static final QName PROP_ARCHIVED_BY = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedBy"); - static final QName PROP_ARCHIVED_DATE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedDate"); - static final QName PROP_ARCHIVED_ORIGINAL_OWNER = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedOriginalOwner"); - static final QName ASPECT_ARCHIVED_ASSOCS = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archived-assocs"); - static final QName PROP_ARCHIVED_PARENT_ASSOCS = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedParentAssocs"); - static final QName PROP_ARCHIVED_CHILD_ASSOCS = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedChildAssocs"); - static final QName PROP_ARCHIVED_SOURCE_ASSOCS = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedSourceAssocs"); - static final QName PROP_ARCHIVED_TARGET_ASSOCS = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedTargetAssocs"); - - // referenceable aspect constants - static final QName TYPE_REFERENCE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "reference"); - static final QName PROP_REFERENCE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "reference"); - - // container type constants - static final QName TYPE_CONTAINER = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "container"); - /** child association type supported by {@link #TYPE_CONTAINER} */ - static final QName ASSOC_CHILDREN =QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "children"); - - // roots - static final QName ASPECT_ROOT = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "aspect_root"); - static final QName TYPE_STOREROOT = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "store_root"); - - // descriptor properties - static final QName PROP_SYS_VERSION_MAJOR = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "versionMajor"); - static final QName PROP_SYS_VERSION_MINOR = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "versionMinor"); - static final QName PROP_SYS_VERSION_REVISION = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "versionRevision"); - static final QName PROP_SYS_VERSION_LABEL = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "versionLabel"); - static final QName PROP_SYS_VERSION_BUILD = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "versionBuild"); - static final QName PROP_SYS_VERSION_SCHEMA = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "versionSchema"); - static final QName PROP_SYS_VERSION_EDITION = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "versionEdition"); - - - // - // Content Model Definitions - // - - // content management type constants - static final QName TYPE_CMOBJECT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "cmobject"); - static final QName PROP_NAME = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "name"); - - // copy aspect constants - static final QName ASPECT_COPIEDFROM = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "copiedfrom"); - static final QName PROP_COPY_REFERENCE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "source"); - - // working copy aspect contants - static final QName ASPECT_WORKING_COPY = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "workingcopy"); - static final QName PROP_WORKING_COPY_OWNER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "workingCopyOwner"); - - // content type and aspect constants - static final QName TYPE_CONTENT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "content"); - static final QName PROP_CONTENT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "content"); - static final QName TYPE_AVM_CONTENT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "avmcontent"); - static final QName TYPE_AVM_PLAIN_CONTENT = - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "avmplaincontent"); - static final QName TYPE_AVM_LAYERED_CONTENT = - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "avmlayeredcontent"); - static final QName PROP_AVM_FILE_INDIRECTION = - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "avmfileindirection"); - - // title aspect - static final QName ASPECT_TITLED = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "titled"); - static final QName PROP_TITLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "title"); - static final QName PROP_DESCRIPTION = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "description"); - - // auditable aspect - static final QName ASPECT_AUDITABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "auditable"); - static final QName PROP_CREATED = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "created"); - static final QName PROP_CREATOR = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "creator"); - static final QName PROP_MODIFIED = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "modified"); - static final QName PROP_MODIFIER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "modifier"); - static final QName PROP_ACCESSED = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "accessed"); - - // author aspect - static final QName ASPECT_AUTHOR = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "author"); - static final QName PROP_AUTHOR = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "author"); - - // categories - static final QName TYPE_CATEGORYROOT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "category_root"); - static final QName ASPECT_CLASSIFIABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "classifiable"); - //static final QName ASPECT_CATEGORISATION = QName.createQName(NamespaceService.ALFRESCO_URI, "aspect_categorisation"); - static final QName ASPECT_GEN_CLASSIFIABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "generalclassifiable"); - static final QName TYPE_CATEGORY = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "category"); - static final QName PROP_CATEGORIES = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "categories"); - static final QName ASSOC_CATEGORIES = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "categories"); - static final QName ASSOC_SUBCATEGORIES = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "subcategories"); - - // lock aspect - public final static QName ASPECT_LOCKABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "lockable"); - public final static QName PROP_LOCK_OWNER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "lockOwner"); - public final static QName PROP_LOCK_TYPE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "lockType"); - public final static QName PROP_EXPIRY_DATE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "expiryDate"); - - // version aspect - static final QName ASPECT_VERSIONABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "versionable"); - static final QName PROP_VERSION_LABEL = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "versionLabel"); - static final QName PROP_INITIAL_VERSION = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "initialVersion"); - static final QName PROP_AUTO_VERSION = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "autoVersion"); - - // folders - static final QName TYPE_SYSTEM_FOLDER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "systemfolder"); - static final QName TYPE_FOLDER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "folder"); - /** child association type supported by {@link #TYPE_FOLDER} */ - static final QName ASSOC_CONTAINS = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "contains"); - static final QName TYPE_AVM_FOLDER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "avmfolder"); - static final QName TYPE_AVM_PLAIN_FOLDER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "avmplainfolder"); - static final QName TYPE_AVM_LAYERED_FOLDER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "avmlayeredfolder"); - static final QName PROP_AVM_DIR_INDIRECTION = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "avmdirindirection"); - - // person - static final QName TYPE_PERSON = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "person"); - static final QName PROP_USERNAME = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "userName"); - static final QName PROP_HOMEFOLDER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "homeFolder"); - static final QName PROP_FIRSTNAME = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "firstName"); - static final QName PROP_LASTNAME = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "lastName"); - static final QName PROP_EMAIL = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "email"); - static final QName PROP_ORGID = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "organizationId"); - - // Ownable aspect - static final QName ASPECT_OWNABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "ownable"); - static final QName PROP_OWNER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "owner"); - - // Templatable aspect - static final QName ASPECT_TEMPLATABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "templatable"); - static final QName PROP_TEMPLATE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "template"); - - // Dictionary model - public static final QName TYPE_DICTIONARY_MODEL = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "dictionaryModel"); - public static final QName PROP_MODEL_NAME = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "modelName"); - public static final QName PROP_MODEL_DESCRIPTION = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "modelDescription"); - public static final QName PROP_MODEL_AUTHOR = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "modelAuthor"); - public static final QName PROP_MODEL_PUBLISHED_DATE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "modelPublishedDate"); - public static final QName PROP_MODEL_VERSION = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "modelVersion"); - public static final QName PROP_MODEL_ACTIVE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "modelActive"); - - // referencing aspect - public static final QName ASPECT_REFERENCING = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "referencing"); - public static final QName ASSOC_REFERENCES = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "references"); - - // link object - public static final QName TYPE_LINK = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "link"); - public static final QName PROP_LINK_DESTINATION = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "destination"); - - // email aspect - public static final QName ASPECT_MAILED = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "emailed"); - public static final QName PROP_SENTDATE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "sentdate"); - public static final QName PROP_ORIGINATOR = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "originator"); - public static final QName PROP_ADDRESSEE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "addressee"); - public static final QName PROP_ADDRESSEES = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "addressees"); - public static final QName PROP_SUBJECT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "subjectline"); - - // mounted aspect - public static final QName ASPECT_MOUNTED = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "mounted"); - public static final QName PROP_MOUNTPOINT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "mountpoint"); - - // countable aspect - public static final QName ASPECT_COUNTABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "countable"); - public static final QName PROP_HITS = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "hits"); - public static final QName PROP_COUNTER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "counter"); - - - // - // Application Model Definitions - // - - // workflow - static final QName ASPECT_SIMPLE_WORKFLOW = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "simpleworkflow"); - static final QName PROP_APPROVE_STEP = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "approveStep"); - static final QName PROP_APPROVE_FOLDER = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "approveFolder"); - static final QName PROP_APPROVE_MOVE = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "approveMove"); - static final QName PROP_REJECT_STEP = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "rejectStep"); - static final QName PROP_REJECT_FOLDER = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "rejectFolder"); - static final QName PROP_REJECT_MOVE = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "rejectMove"); - - // ui facets aspect - static final QName ASPECT_UIFACETS = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "uifacets"); - static final QName PROP_ICON = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "icon"); - - // inlineeditable aspect - static final QName ASPECT_INLINEEDITABLE = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "inlineeditable"); - static final QName PROP_EDITINLINE = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "editInline"); - - // configurable aspect - static final QName ASPECT_CONFIGURABLE = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "configurable"); - static final QName TYPE_CONFIGURATIONS = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "configurations"); - static final QName ASSOC_CONFIGURATIONS = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "configurations"); - - // object links - static final QName TYPE_FILELINK = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "filelink"); - static final QName TYPE_FOLDERLINK = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "folderlink"); - - // feed source aspect - static final QName ASPECT_FEEDSOURCE = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "feedsource"); - static final QName PROP_FEEDTEMPLATE = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "template"); - - // AVM web folder - static final QName TYPE_AVMWEBFOLDER = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "webfolder"); - static final QName PROP_AVMSTORE = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "avmstore"); - - - // - // User Model Definitions - // - - static final String USER_MODEL_URI = "http://www.alfresco.org/model/user/1.0"; - static final String USER_MODEL_PREFIX = "usr"; - - static final QName TYPE_USER = QName.createQName(USER_MODEL_URI, "user"); - static final QName PROP_USER_USERNAME = QName.createQName(USER_MODEL_URI, "username"); - static final QName PROP_PASSWORD = QName.createQName(USER_MODEL_URI, "password"); - static final QName PROP_ENABLED = QName.createQName(USER_MODEL_URI, "enabled"); - static final QName PROP_ACCOUNT_EXPIRES = QName.createQName(USER_MODEL_URI, "accountExpires"); - static final QName PROP_ACCOUNT_EXPIRY_DATE = QName.createQName(USER_MODEL_URI, "accountExpiryDate"); - static final QName PROP_CREDENTIALS_EXPIRE = QName.createQName(USER_MODEL_URI, "credentialsExpire"); - static final QName PROP_CREDENTIALS_EXPIRY_DATE = QName.createQName(USER_MODEL_URI, "credentialsExpiryDate"); - static final QName PROP_ACCOUNT_LOCKED = QName.createQName(USER_MODEL_URI, "accountLocked"); - static final QName PROP_SALT = QName.createQName(USER_MODEL_URI, "salt"); - - static final QName TYPE_AUTHORITY = QName.createQName(USER_MODEL_URI, "authority"); - - static final QName TYPE_AUTHORITY_CONTAINER = QName.createQName(USER_MODEL_URI, "authorityContainer"); - static final QName PROP_AUTHORITY_NAME = QName.createQName(USER_MODEL_URI, "authorityName"); - static final QName ASSOC_MEMBER = QName.createQName(USER_MODEL_URI, "member"); - static final QName PROP_MEMBERS = QName.createQName(USER_MODEL_URI, "members"); -} +/* + * 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.model; + +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + + +/** + * Content Model Constants + */ +public interface ContentModel +{ + // + // System Model Definitions + // + + // base type constants + static final QName TYPE_BASE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "base"); + static final QName ASPECT_REFERENCEABLE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "referenceable"); + static final QName PROP_STORE_PROTOCOL = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "store-protocol"); + static final QName PROP_STORE_IDENTIFIER = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "store-identifier"); + static final QName PROP_NODE_UUID = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "node-uuid"); + static final QName PROP_NODE_DBID = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "node-dbid"); + + // tag for incomplete nodes + static final QName ASPECT_INCOMPLETE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "incomplete"); + + // tag for temporary nodes + static final QName ASPECT_TEMPORARY = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "temporary"); + + // archived nodes aspect constants + static final QName ASPECT_ARCHIVED = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archived"); + static final QName PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedOriginalParentAssoc"); + static final QName PROP_ARCHIVED_BY = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedBy"); + static final QName PROP_ARCHIVED_DATE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedDate"); + static final QName PROP_ARCHIVED_ORIGINAL_OWNER = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedOriginalOwner"); + static final QName ASPECT_ARCHIVED_ASSOCS = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archived-assocs"); + static final QName PROP_ARCHIVED_PARENT_ASSOCS = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedParentAssocs"); + static final QName PROP_ARCHIVED_CHILD_ASSOCS = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedChildAssocs"); + static final QName PROP_ARCHIVED_SOURCE_ASSOCS = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedSourceAssocs"); + static final QName PROP_ARCHIVED_TARGET_ASSOCS = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedTargetAssocs"); + + // referenceable aspect constants + static final QName TYPE_REFERENCE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "reference"); + static final QName PROP_REFERENCE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "reference"); + + // container type constants + static final QName TYPE_CONTAINER = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "container"); + /** child association type supported by {@link #TYPE_CONTAINER} */ + static final QName ASSOC_CHILDREN =QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "children"); + + // roots + static final QName ASPECT_ROOT = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "aspect_root"); + static final QName TYPE_STOREROOT = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "store_root"); + + // descriptor properties + static final QName PROP_SYS_VERSION_MAJOR = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "versionMajor"); + static final QName PROP_SYS_VERSION_MINOR = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "versionMinor"); + static final QName PROP_SYS_VERSION_REVISION = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "versionRevision"); + static final QName PROP_SYS_VERSION_LABEL = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "versionLabel"); + static final QName PROP_SYS_VERSION_BUILD = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "versionBuild"); + static final QName PROP_SYS_VERSION_SCHEMA = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "versionSchema"); + static final QName PROP_SYS_VERSION_EDITION = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "versionEdition"); + + + // + // Content Model Definitions + // + + // content management type constants + static final QName TYPE_CMOBJECT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "cmobject"); + static final QName PROP_NAME = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "name"); + + // copy aspect constants + static final QName ASPECT_COPIEDFROM = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "copiedfrom"); + static final QName PROP_COPY_REFERENCE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "source"); + + // working copy aspect contants + static final QName ASPECT_WORKING_COPY = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "workingcopy"); + static final QName PROP_WORKING_COPY_OWNER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "workingCopyOwner"); + + // content type and aspect constants + static final QName TYPE_CONTENT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "content"); + static final QName PROP_CONTENT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "content"); + static final QName TYPE_AVM_CONTENT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "avmcontent"); + static final QName TYPE_AVM_PLAIN_CONTENT = + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "avmplaincontent"); + static final QName TYPE_AVM_LAYERED_CONTENT = + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "avmlayeredcontent"); + static final QName PROP_AVM_FILE_INDIRECTION = + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "avmfileindirection"); + + // title aspect + static final QName ASPECT_TITLED = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "titled"); + static final QName PROP_TITLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "title"); + static final QName PROP_DESCRIPTION = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "description"); + + // auditable aspect + static final QName ASPECT_AUDITABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "auditable"); + static final QName PROP_CREATED = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "created"); + static final QName PROP_CREATOR = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "creator"); + static final QName PROP_MODIFIED = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "modified"); + static final QName PROP_MODIFIER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "modifier"); + static final QName PROP_ACCESSED = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "accessed"); + + // author aspect + static final QName ASPECT_AUTHOR = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "author"); + static final QName PROP_AUTHOR = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "author"); + + // categories + static final QName TYPE_CATEGORYROOT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "category_root"); + static final QName ASPECT_CLASSIFIABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "classifiable"); + //static final QName ASPECT_CATEGORISATION = QName.createQName(NamespaceService.ALFRESCO_URI, "aspect_categorisation"); + static final QName ASPECT_GEN_CLASSIFIABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "generalclassifiable"); + static final QName TYPE_CATEGORY = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "category"); + static final QName PROP_CATEGORIES = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "categories"); + static final QName ASSOC_CATEGORIES = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "categories"); + static final QName ASSOC_SUBCATEGORIES = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "subcategories"); + + // lock aspect + public final static QName ASPECT_LOCKABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "lockable"); + public final static QName PROP_LOCK_OWNER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "lockOwner"); + public final static QName PROP_LOCK_TYPE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "lockType"); + public final static QName PROP_EXPIRY_DATE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "expiryDate"); + + // version aspect + static final QName ASPECT_VERSIONABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "versionable"); + static final QName PROP_VERSION_LABEL = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "versionLabel"); + static final QName PROP_INITIAL_VERSION = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "initialVersion"); + static final QName PROP_AUTO_VERSION = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "autoVersion"); + + // folders + static final QName TYPE_SYSTEM_FOLDER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "systemfolder"); + static final QName TYPE_FOLDER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "folder"); + /** child association type supported by {@link #TYPE_FOLDER} */ + static final QName ASSOC_CONTAINS = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "contains"); + static final QName TYPE_AVM_FOLDER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "avmfolder"); + static final QName TYPE_AVM_PLAIN_FOLDER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "avmplainfolder"); + static final QName TYPE_AVM_LAYERED_FOLDER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "avmlayeredfolder"); + static final QName PROP_AVM_DIR_INDIRECTION = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "avmdirindirection"); + + // person + static final QName TYPE_PERSON = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "person"); + static final QName PROP_USERNAME = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "userName"); + static final QName PROP_HOMEFOLDER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "homeFolder"); + static final QName PROP_FIRSTNAME = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "firstName"); + static final QName PROP_LASTNAME = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "lastName"); + static final QName PROP_EMAIL = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "email"); + static final QName PROP_ORGID = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "organizationId"); + + // Ownable aspect + static final QName ASPECT_OWNABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "ownable"); + static final QName PROP_OWNER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "owner"); + + // Templatable aspect + static final QName ASPECT_TEMPLATABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "templatable"); + static final QName PROP_TEMPLATE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "template"); + + // Dictionary model + public static final QName TYPE_DICTIONARY_MODEL = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "dictionaryModel"); + public static final QName PROP_MODEL_NAME = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "modelName"); + public static final QName PROP_MODEL_DESCRIPTION = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "modelDescription"); + public static final QName PROP_MODEL_AUTHOR = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "modelAuthor"); + public static final QName PROP_MODEL_PUBLISHED_DATE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "modelPublishedDate"); + public static final QName PROP_MODEL_VERSION = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "modelVersion"); + public static final QName PROP_MODEL_ACTIVE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "modelActive"); + + // referencing aspect + public static final QName ASPECT_REFERENCING = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "referencing"); + public static final QName ASSOC_REFERENCES = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "references"); + + // link object + public static final QName TYPE_LINK = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "link"); + public static final QName PROP_LINK_DESTINATION = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "destination"); + + // email aspect + public static final QName ASPECT_MAILED = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "emailed"); + public static final QName PROP_SENTDATE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "sentdate"); + public static final QName PROP_ORIGINATOR = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "originator"); + public static final QName PROP_ADDRESSEE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "addressee"); + public static final QName PROP_ADDRESSEES = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "addressees"); + public static final QName PROP_SUBJECT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "subjectline"); + + // countable aspect + public static final QName ASPECT_COUNTABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "countable"); + public static final QName PROP_HITS = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "hits"); + public static final QName PROP_COUNTER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "counter"); + + + // + // Application Model Definitions + // + + // workflow + static final QName ASPECT_SIMPLE_WORKFLOW = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "simpleworkflow"); + static final QName PROP_APPROVE_STEP = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "approveStep"); + static final QName PROP_APPROVE_FOLDER = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "approveFolder"); + static final QName PROP_APPROVE_MOVE = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "approveMove"); + static final QName PROP_REJECT_STEP = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "rejectStep"); + static final QName PROP_REJECT_FOLDER = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "rejectFolder"); + static final QName PROP_REJECT_MOVE = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "rejectMove"); + + // ui facets aspect + static final QName ASPECT_UIFACETS = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "uifacets"); + static final QName PROP_ICON = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "icon"); + + // inlineeditable aspect + static final QName ASPECT_INLINEEDITABLE = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "inlineeditable"); + static final QName PROP_EDITINLINE = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "editInline"); + + // configurable aspect + static final QName ASPECT_CONFIGURABLE = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "configurable"); + static final QName TYPE_CONFIGURATIONS = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "configurations"); + static final QName ASSOC_CONFIGURATIONS = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "configurations"); + + // object links + static final QName TYPE_FILELINK = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "filelink"); + static final QName TYPE_FOLDERLINK = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "folderlink"); + + // feed source aspect + static final QName ASPECT_FEEDSOURCE = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "feedsource"); + static final QName PROP_FEEDTEMPLATE = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "template"); + + // AVM web folder + static final QName TYPE_AVMWEBFOLDER = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "webfolder"); + static final QName PROP_AVMSTORE = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "avmstore"); + + + // + // User Model Definitions + // + + static final String USER_MODEL_URI = "http://www.alfresco.org/model/user/1.0"; + static final String USER_MODEL_PREFIX = "usr"; + + static final QName TYPE_USER = QName.createQName(USER_MODEL_URI, "user"); + static final QName PROP_USER_USERNAME = QName.createQName(USER_MODEL_URI, "username"); + static final QName PROP_PASSWORD = QName.createQName(USER_MODEL_URI, "password"); + static final QName PROP_ENABLED = QName.createQName(USER_MODEL_URI, "enabled"); + static final QName PROP_ACCOUNT_EXPIRES = QName.createQName(USER_MODEL_URI, "accountExpires"); + static final QName PROP_ACCOUNT_EXPIRY_DATE = QName.createQName(USER_MODEL_URI, "accountExpiryDate"); + static final QName PROP_CREDENTIALS_EXPIRE = QName.createQName(USER_MODEL_URI, "credentialsExpire"); + static final QName PROP_CREDENTIALS_EXPIRY_DATE = QName.createQName(USER_MODEL_URI, "credentialsExpiryDate"); + static final QName PROP_ACCOUNT_LOCKED = QName.createQName(USER_MODEL_URI, "accountLocked"); + static final QName PROP_SALT = QName.createQName(USER_MODEL_URI, "salt"); + + static final QName TYPE_AUTHORITY = QName.createQName(USER_MODEL_URI, "authority"); + + static final QName TYPE_AUTHORITY_CONTAINER = QName.createQName(USER_MODEL_URI, "authorityContainer"); + static final QName PROP_AUTHORITY_NAME = QName.createQName(USER_MODEL_URI, "authorityName"); + static final QName ASSOC_MEMBER = QName.createQName(USER_MODEL_URI, "member"); + static final QName PROP_MEMBERS = QName.createQName(USER_MODEL_URI, "members"); +} diff --git a/source/java/org/alfresco/repo/audit/AuditComponent.java b/source/java/org/alfresco/repo/audit/AuditComponent.java index 3b13c7e5f5..4fde6c0461 100644 --- a/source/java/org/alfresco/repo/audit/AuditComponent.java +++ b/source/java/org/alfresco/repo/audit/AuditComponent.java @@ -16,6 +16,9 @@ */ package org.alfresco.repo.audit; +import java.util.List; + +import org.alfresco.service.cmr.audit.AuditInfo; import org.alfresco.service.cmr.repository.NodeRef; import org.aopalliance.intercept.MethodInvocation; @@ -47,6 +50,14 @@ public interface AuditComponent * an arbitrary list of parameters */ public void audit(String source, String description, NodeRef key, Object... args); + + /** + * Get the audit trail for a node. + * + * @param nodeRef + * @return + */ + public List getAuditTrail(NodeRef nodeRef); } diff --git a/source/java/org/alfresco/repo/audit/AuditComponentImpl.java b/source/java/org/alfresco/repo/audit/AuditComponentImpl.java index e476907417..6ee47e0c6d 100644 --- a/source/java/org/alfresco/repo/audit/AuditComponentImpl.java +++ b/source/java/org/alfresco/repo/audit/AuditComponentImpl.java @@ -21,12 +21,15 @@ import java.lang.reflect.Method; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Date; +import java.util.List; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.service.Auditable; import org.alfresco.service.NotAuditable; +import org.alfresco.service.cmr.audit.AuditInfo; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; import org.aopalliance.intercept.MethodInvocation; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -181,7 +184,7 @@ public class AuditComponentImpl implements AuditComponent */ public Object auditImpl(MethodInvocation mi) throws Throwable { - AuditInfo auditInfo = new AuditInfo(auditConfiguration); + AuditState auditInfo = new AuditState(auditConfiguration); // RecordOptions recordOptions = auditModel.getAuditRecordOptions(mi); AuditMode auditMode = AuditMode.UNSET; try @@ -221,7 +224,7 @@ public class AuditComponentImpl implements AuditComponent * @param t * @return */ - private AuditMode onError(AuditMode auditMode, AuditInfo auditInfo, MethodInvocation mi, Throwable t) + private AuditMode onError(AuditMode auditMode, AuditState auditInfo, MethodInvocation mi, Throwable t) { if ((auditMode == AuditMode.ALL) || (auditMode == AuditMode.FAIL)) { @@ -241,7 +244,7 @@ public class AuditComponentImpl implements AuditComponent * @param returnObject * @return */ - private AuditMode postInvocation(AuditMode auditMode, AuditInfo auditInfo, MethodInvocation mi, Object returnObject) + private AuditMode postInvocation(AuditMode auditMode, AuditState auditInfo, MethodInvocation mi, Object returnObject) { if (returnObject == null) { @@ -255,6 +258,33 @@ public class AuditComponentImpl implements AuditComponent { auditInfo.setReturnObject(returnObject.toString()); } + + Auditable auditable = mi.getMethod().getAnnotation(Auditable.class); + if (auditable.key() == Auditable.Key.RETURN) + { + if (returnObject != null) + { + if (returnObject instanceof NodeRef) + { + NodeRef key = (NodeRef) returnObject; + auditInfo.setKeyStore(key.getStoreRef()); + auditInfo.setKeyGUID(key.getId()); + } + else if (returnObject instanceof StoreRef) + { + auditInfo.setKeyStore((StoreRef)returnObject); + } + } + } + + // If the user name is not set, try and set it after the method call. + // This covers authentication when the user is only known after the call. + + if (auditInfo.getUserIdentifier() == null) + { + auditInfo.setUserIdentifier(AuthenticationUtil.getCurrentUserName()); + } + return auditMode; } @@ -266,7 +296,7 @@ public class AuditComponentImpl implements AuditComponent * @param mi * @return */ - private AuditMode beforeInvocation(AuditMode auditMode, AuditInfo auditInfo, MethodInvocation mi) + private AuditMode beforeInvocation(AuditMode auditMode, AuditState auditInfo, MethodInvocation mi) { AuditMode effectiveAuditMode = auditModel.beforeExecution(auditMode, mi); @@ -283,27 +313,83 @@ public class AuditComponentImpl implements AuditComponent auditInfo.setFail(false); auditInfo.setFiltered(false); auditInfo.setHostAddress(auditHost); - auditInfo.setKeyGUID(null); + Auditable auditable = mi.getMethod().getAnnotation(Auditable.class); + Object key = null; + switch (auditable.key()) + { + case ARG_0: + key = mi.getArguments()[0]; + break; + case ARG_1: + key = mi.getArguments()[1]; + break; + case ARG_2: + key = mi.getArguments()[2]; + break; + case ARG_3: + key = mi.getArguments()[3]; + break; + case ARG_4: + key = mi.getArguments()[4]; + break; + case ARG_5: + key = mi.getArguments()[5]; + break; + case ARG_6: + key = mi.getArguments()[6]; + break; + case ARG_7: + key = mi.getArguments()[7]; + break; + case ARG_8: + key = mi.getArguments()[8]; + break; + case ARG_9: + key = mi.getArguments()[9]; + break; + case NO_KEY: + default: + break; + } + if (key != null) + { + if (key instanceof NodeRef) + { + auditInfo.setKeyStore(((NodeRef) key).getStoreRef()); + auditInfo.setKeyGUID(((NodeRef) key).getId()); + } + else if (key instanceof StoreRef) + { + auditInfo.setKeyStore((StoreRef) key); + } + } auditInfo.setKeyPropertiesAfter(null); auditInfo.setKeyPropertiesBefore(null); - auditInfo.setKeyStore(null); auditInfo.setMessage(null); if (mi.getArguments() != null) { Serializable[] serArgs = new Serializable[mi.getArguments().length]; for (int i = 0; i < mi.getArguments().length; i++) { - if (mi.getArguments()[i] == null) + if ((auditable.recordable() == null) + || (auditable.recordable().length <= i) || auditable.recordable()[i]) { - serArgs[i] = null; - } - else if (mi.getArguments()[i] instanceof Serializable) - { - serArgs[i] = (Serializable) mi.getArguments()[i]; + if (mi.getArguments()[i] == null) + { + serArgs[i] = null; + } + else if (mi.getArguments()[i] instanceof Serializable) + { + serArgs[i] = (Serializable) mi.getArguments()[i]; + } + else + { + serArgs[i] = mi.getArguments()[i].toString(); + } } else { - serArgs[i] = mi.getArguments()[i].toString(); + serArgs[i] = "********"; } } auditInfo.setMethodArguments(serArgs); @@ -322,9 +408,9 @@ public class AuditComponentImpl implements AuditComponent /** * A simple audit entry Currently we ignore filtering here. */ - public void audit(String source, String description, NodeRef key, Object... args) + public void audit(String source, String description, NodeRef key, Object... args) { - AuditInfo auditInfo = new AuditInfo(auditConfiguration); + AuditState auditInfo = new AuditState(auditConfiguration); // RecordOptions recordOptions = auditModel.getAuditRecordOptions(mi); AuditMode auditMode = AuditMode.UNSET; try @@ -353,18 +439,23 @@ public class AuditComponentImpl implements AuditComponent } } - private AuditMode onApplicationAudit(AuditMode auditMode, AuditInfo auditInfo, String source, String description, + public List getAuditTrail(NodeRef nodeRef) + { + return auditDAO.getAuditTrail(nodeRef); + } + + private AuditMode onApplicationAudit(AuditMode auditMode, AuditState auditInfo, String source, String description, NodeRef key, Object... args) { AuditMode effectiveAuditMode = auditModel.beforeExecution(auditMode, source, description, key, args); if (auditMode != AuditMode.NONE) { - if(source.equals(SYSTEM_APPLICATION)) + if (source.equals(SYSTEM_APPLICATION)) { - throw new AuditException("Application audit can not use the reserved identifier "+SYSTEM_APPLICATION); + throw new AuditException("Application audit can not use the reserved identifier " + SYSTEM_APPLICATION); } - + auditInfo.setAuditApplication(source); auditInfo.setAuditConfiguration(auditConfiguration); auditInfo.setAuditMethod(null); @@ -374,10 +465,13 @@ public class AuditComponentImpl implements AuditComponent auditInfo.setFail(false); auditInfo.setFiltered(false); auditInfo.setHostAddress(auditHost); - auditInfo.setKeyGUID(null); + if (key != null) + { + auditInfo.setKeyStore(key.getStoreRef()); + auditInfo.setKeyGUID(key.getId()); + } auditInfo.setKeyPropertiesAfter(null); auditInfo.setKeyPropertiesBefore(null); - auditInfo.setKeyStore(null); auditInfo.setMessage(description); if (args != null) { @@ -409,9 +503,9 @@ public class AuditComponentImpl implements AuditComponent return effectiveAuditMode; } - - private AuditMode onError(AuditMode auditMode, AuditInfo auditInfo, Throwable t, String source, String description, - NodeRef key, Object... args) + + private AuditMode onError(AuditMode auditMode, AuditState auditInfo, Throwable t, String source, + String description, NodeRef key, Object... args) { if ((auditMode == AuditMode.ALL) || (auditMode == AuditMode.FAIL)) { diff --git a/source/java/org/alfresco/repo/audit/AuditDAO.java b/source/java/org/alfresco/repo/audit/AuditDAO.java index ef307c5f93..e429784550 100644 --- a/source/java/org/alfresco/repo/audit/AuditDAO.java +++ b/source/java/org/alfresco/repo/audit/AuditDAO.java @@ -16,6 +16,11 @@ */ package org.alfresco.repo.audit; +import java.util.List; + +import org.alfresco.service.cmr.audit.AuditInfo; +import org.alfresco.service.cmr.repository.NodeRef; + /** * The interface to persist audit information. * @@ -28,5 +33,13 @@ public interface AuditDAO * * @param auditInfo */ - public void audit(AuditInfo auditInfo); + public void audit(AuditState auditInfo); + + /** + * Get the audit trail for a node. + * + * @param nodeRef + * @return + */ + public List getAuditTrail(NodeRef nodeRef); } diff --git a/source/java/org/alfresco/repo/audit/AuditServiceImpl.java b/source/java/org/alfresco/repo/audit/AuditServiceImpl.java index b89f855f3c..34c108e0c2 100644 --- a/source/java/org/alfresco/repo/audit/AuditServiceImpl.java +++ b/source/java/org/alfresco/repo/audit/AuditServiceImpl.java @@ -16,8 +16,12 @@ */ package org.alfresco.repo.audit; +import java.util.List; + import javax.transaction.UserTransaction; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.audit.AuditInfo; import org.alfresco.service.cmr.audit.AuditService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.StoreRef; @@ -64,24 +68,49 @@ public class AuditServiceImpl implements AuditService auditComponent.audit(source, description, key, args); } + public List getAuditTrail(NodeRef nodeRef) + { + return auditComponent.getAuditTrail(nodeRef); + } + public static void main(String[] args) throws Exception { + ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); AuditService as = (AuditService) ctx.getBean("AuditService"); TransactionService txs = (TransactionService) ctx.getBean("transactionComponent"); UserTransaction tx = txs.getUserTransaction(); tx.begin(); - as.audit("AuditedApp", "First"); - as.audit("AuditedApp", "Second", new NodeRef(new StoreRef("test", "audit"), "id")); - as.audit("AuditedApp", "Third", new Object[]{"one", "two", "three"}); - as.audit("AuditedApp", "Fourth", new NodeRef(new StoreRef("test", "audit"), "id"), new Object[]{"one", "two", "three"}); - - as.audit("UnAuditedApp", "First"); - as.audit("UnAuditedApp", "Second", new NodeRef(new StoreRef("test", "audit"), "id")); - as.audit("UnAuditedApp", "Third", new Object[]{"one", "two", "three"}); - as.audit("UnAuditedApp", "Fourth", new NodeRef(new StoreRef("test", "audit"), "id"), new Object[]{"one", "two", "three"}); + AuthenticationUtil.setSystemUserAsCurrentUser(); + try + { + + NodeRef nodeRef = new NodeRef(new StoreRef("test", "audit"), "id"); + as.audit("AuditedApp", "First"); + System.out.println("Audit entries for node "+as.getAuditTrail(nodeRef).size()); + as.audit("AuditedApp", "Second", nodeRef); + System.out.println("Audit entries for node "+as.getAuditTrail(nodeRef).size()); + as.audit("AuditedApp", "Third", new Object[] { "one", "two", "three" }); + System.out.println("Audit entries for node "+as.getAuditTrail(nodeRef).size()); + as.audit("AuditedApp", "Fourth",nodeRef, new Object[] { "one", + "two", "three" }); + System.out.println("Audit entries for node "+as.getAuditTrail(nodeRef).size()); + as.audit("UnAuditedApp", "First"); + System.out.println("Audit entries for node "+as.getAuditTrail(nodeRef).size()); + as.audit("UnAuditedApp", "Second", nodeRef); + System.out.println("Audit entries for node "+as.getAuditTrail(nodeRef).size()); + as.audit("UnAuditedApp", "Third", new Object[] { "one", "two", "three" }); + System.out.println("Audit entries for node "+as.getAuditTrail(nodeRef).size()); + as.audit("UnAuditedApp", "Fourth", nodeRef, new Object[] { "one", + "two", "three" }); + System.out.println("Audit entries for node "+as.getAuditTrail(nodeRef).size()); + } + finally + { + AuthenticationUtil.clearCurrentSecurityContext(); + } tx.commit(); } diff --git a/source/java/org/alfresco/repo/audit/AuditServiceTest.java b/source/java/org/alfresco/repo/audit/AuditServiceTest.java new file mode 100644 index 0000000000..fd1c205371 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/AuditServiceTest.java @@ -0,0 +1,291 @@ +/* + * 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.audit; + +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.audit.model.AuditEntry; +import org.alfresco.repo.audit.model.TrueFalseUnset; +import org.alfresco.repo.security.authentication.AuthenticationComponent; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.MutableAuthenticationDao; +import org.alfresco.repo.security.permissions.PermissionServiceSPI; +import org.alfresco.repo.security.permissions.impl.ModelDAO; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.audit.AuditInfo; +import org.alfresco.service.cmr.audit.AuditService; +import org.alfresco.service.cmr.dictionary.DictionaryService; +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.AuthenticationService; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.BaseSpringTest; + +public class AuditServiceTest extends BaseSpringTest +{ + + private NodeService nodeService; + + private DictionaryService dictionaryService; + + private PermissionServiceSPI permissionService; + + private NamespacePrefixResolver namespacePrefixResolver; + + private AuthenticationService authenticationService; + + private AuthenticationComponent authenticationComponent; + + private ServiceRegistry serviceRegistry; + + private ModelDAO permissionModelDAO; + + private PersonService personService; + + private AuthorityService authorityService; + + private MutableAuthenticationDao authenticationDAO; + + private NodeRef rootNodeRef; + + private NodeRef systemNodeRef; + + private AuditService auditService; + + private AuditEntry auditEntry; + + private NodeRef typesNodeRef; + + private QName children; + + private QName system; + + private QName container; + + private QName types; + + public AuditServiceTest() + { + super(); + } + + protected void onSetUpInTransaction() throws Exception + { + nodeService = (NodeService) applicationContext.getBean("nodeService"); + dictionaryService = (DictionaryService) applicationContext.getBean(ServiceRegistry.DICTIONARY_SERVICE + .getLocalName()); + permissionService = (PermissionServiceSPI) applicationContext.getBean("permissionService"); + namespacePrefixResolver = (NamespacePrefixResolver) applicationContext + .getBean(ServiceRegistry.NAMESPACE_SERVICE.getLocalName()); + authenticationService = (AuthenticationService) applicationContext.getBean("authenticationService"); + authenticationComponent = (AuthenticationComponent) applicationContext.getBean("authenticationComponent"); + serviceRegistry = (ServiceRegistry) applicationContext.getBean(ServiceRegistry.SERVICE_REGISTRY); + permissionModelDAO = (ModelDAO) applicationContext.getBean("permissionsModelDAO"); + personService = (PersonService) applicationContext.getBean("personService"); + authorityService = (AuthorityService) applicationContext.getBean("authorityService"); + + authenticationComponent.setCurrentUser(authenticationComponent.getSystemUserName()); + authenticationDAO = (MutableAuthenticationDao) applicationContext.getBean("alfDaoImpl"); + + auditService = (AuditService) applicationContext.getBean("AuditService"); + auditEntry = (AuditEntry) applicationContext.getBean("auditModel"); + + StoreRef storeRef = nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.nanoTime()); + rootNodeRef = nodeService.getRootNode(storeRef); + + children = ContentModel.ASSOC_CHILDREN; + system = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "system"); + container = ContentModel.TYPE_CONTAINER; + types = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "people"); + + systemNodeRef = nodeService.createNode(rootNodeRef, children, system, container).getChildRef(); + typesNodeRef = nodeService.createNode(systemNodeRef, children, types, container).getChildRef(); + Map props = createPersonProperties("andy"); + nodeService.createNode(typesNodeRef, children, ContentModel.TYPE_PERSON, container, props).getChildRef(); + props = createPersonProperties("lemur"); + nodeService.createNode(typesNodeRef, children, ContentModel.TYPE_PERSON, container, props).getChildRef(); + + // create an authentication object e.g. the user + if (authenticationDAO.userExists("andy")) + { + authenticationService.deleteAuthentication("andy"); + } + authenticationService.createAuthentication("andy", "andy".toCharArray()); + + if (authenticationDAO.userExists("lemur")) + { + authenticationService.deleteAuthentication("lemur"); + } + authenticationService.createAuthentication("lemur", "lemur".toCharArray()); + + if (authenticationDAO.userExists("admin")) + { + authenticationService.deleteAuthentication("admin"); + } + authenticationService.createAuthentication("admin", "admin".toCharArray()); + + authenticationComponent.clearCurrentSecurityContext(); + } + + public void testApplicationAudit() + { + AuthenticationUtil.setSystemUserAsCurrentUser(); + try + { + + NodeRef nodeRef = new NodeRef(new StoreRef("test", "audit"), "id"); + int start = auditService.getAuditTrail(nodeRef).size(); + int increment = auditEntry.getEnabled() == TrueFalseUnset.TRUE ? 1 : 0; + auditService.audit("AuditedApp", "First"); + assertEquals(start, auditService.getAuditTrail(nodeRef).size()); + auditService.audit("AuditedApp", "Second", nodeRef); + assertEquals(start + (1 * increment), auditService.getAuditTrail(nodeRef).size()); + auditService.audit("AuditedApp", "Third", new Object[] { "one", "two", "three" }); + assertEquals(start + (1 * increment), auditService.getAuditTrail(nodeRef).size()); + auditService.audit("AuditedApp", "Fourth", nodeRef, new Object[] { "one", "two", "three" }); + assertEquals(start + (2 * increment), auditService.getAuditTrail(nodeRef).size()); + auditService.audit("UnAuditedApp", "First"); + assertEquals(start + (2 * increment), auditService.getAuditTrail(nodeRef).size()); + auditService.audit("UnAuditedApp", "Second", nodeRef); + assertEquals(start + (3 * increment), auditService.getAuditTrail(nodeRef).size()); + auditService.audit("UnAuditedApp", "Third", new Object[] { "one", "two", "three" }); + assertEquals(start + (3 * increment), auditService.getAuditTrail(nodeRef).size()); + auditService.audit("UnAuditedApp", "Fourth", nodeRef, new Object[] { "one", "two", "three" }); + assertEquals(start + (4 * increment), auditService.getAuditTrail(nodeRef).size()); + } + finally + { + AuthenticationUtil.clearCurrentSecurityContext(); + } + } + + public void testNodeServiceAudit() + { + AuthenticationUtil.setSystemUserAsCurrentUser(); + try + { + int start = auditService.getAuditTrail(typesNodeRef).size(); + int increment = auditEntry.getEnabled() == TrueFalseUnset.TRUE ? 1 : 0; + + // Create + + Map props = createPersonProperties("woof"); + NodeRef created = serviceRegistry.getNodeService().createNode(typesNodeRef, children, + ContentModel.TYPE_PERSON, container, props).getChildRef(); + assertEquals(start + (1 * increment), auditService.getAuditTrail(typesNodeRef).size()); + List list = auditService.getAuditTrail(typesNodeRef); + assertEquals((1 * increment), auditService.getAuditTrail(created).size()); + + // Update + + serviceRegistry.getNodeService().setProperty(created, ContentModel.PROP_FIRSTNAME, "New First Name"); + assertEquals((2 * increment), auditService.getAuditTrail(created).size()); + + // Update + + serviceRegistry.getNodeService().setProperty(created, ContentModel.PROP_FIRSTNAME, "Next First Name"); + assertEquals((3 * increment), auditService.getAuditTrail(created).size()); + + // Delete + + serviceRegistry.getNodeService().deleteNode(created); + assertEquals((4 * increment), auditService.getAuditTrail(created).size()); + + list = auditService.getAuditTrail(created); + assertNotNull(list); + } + finally + { + AuthenticationUtil.clearCurrentSecurityContext(); + } + } + + public void xtestCreateStore() + { + + AuthenticationUtil.setSystemUserAsCurrentUser(); + try + { + serviceRegistry.getNodeService() + .createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_Audit_" + System.nanoTime()); + // Should have a query to support this - check direct in the DB + } + finally + { + AuthenticationUtil.clearCurrentSecurityContext(); + } + setComplete(); + } + + public void xtestAuthenticartionDoesNotReportPasswords() + { + // Should have a query to support this - check direct in the DB + AuthenticationUtil.setSystemUserAsCurrentUser(); + try + { + serviceRegistry.getAuthenticationService().createAuthentication("cabbage", "cabbage".toCharArray()); + serviceRegistry.getAuthenticationService().updateAuthentication("cabbage", "cabbage".toCharArray(), + "red".toCharArray()); + } + finally + { + AuthenticationUtil.clearCurrentSecurityContext(); + } + + try + { + serviceRegistry.getAuthenticationService().authenticate("cabbage", "red".toCharArray()); + } + finally + { + serviceRegistry.getAuthenticationService().clearCurrentSecurityContext(); + } + setComplete(); + } + + public void xtestAuthenticartionFailure() + { + // Should have a query to support this - check direct in the DB + AuthenticationUtil.setSystemUserAsCurrentUser(); + + serviceRegistry.getAuthenticationService().createAuthentication("woof", "cabbage".toCharArray()); + serviceRegistry.getAuthenticationService().authenticate("woof", "red".toCharArray()); + + } + + public void testThereIsAnAuditService() + { + assertNotNull(serviceRegistry.getAuditService()); + } + + private Map createPersonProperties(String userName) + { + HashMap properties = new HashMap(); + properties.put(ContentModel.PROP_USERNAME, userName); + return properties; + } + +} diff --git a/source/java/org/alfresco/repo/audit/AuditState.java b/source/java/org/alfresco/repo/audit/AuditState.java new file mode 100644 index 0000000000..e12773aac9 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/AuditState.java @@ -0,0 +1,385 @@ +/* + * 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.audit; + +import java.io.Serializable; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Date; +import java.util.Map; + +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.AlfrescoTransactionSupport; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.QName; +import org.apache.log4j.Logger; + +/** + * A class to encapsulate audit information supplied to the DAO layer. + * + * Null entries should be stored. + * + * @author Andy Hind + */ +public class AuditState +{ + private static Logger s_logger = Logger.getLogger(AuditState.class); + + /** + * The user identifier for the person who caused this audit entry + */ + private String userIdentifier; + + /** + * The date for this audit entry + */ + private Date date; + + /** + * The transaction id in which this entry was made + */ + private String txId; + + /** + * The session is for this action + */ + private String sessionId; + + /** + * The store in which the action occured. + */ + private StoreRef keyStore; + + /** + * For a node ref, the node for the action. + */ + private String keyGUID; + + /** + * The path of the key + */ + private String keyPath; + + /** + * The audit application + * Internal uses the "System" key and will only audit method information. + */ + private String auditApplication; + + /** + * The service holding the audited method. + */ + private String auditService; + + /** + * The name of the audited method. + */ + private String auditMethod; + + /** + * Did this entry passa filter? + * If false - all entries were being recorded. + */ + private boolean filtered; + + /** + * The audit configuration in use at the time. + */ + private AuditConfiguration auditConfiguration; + + /** + * The object returned by the audited method. + */ + private Serializable returnObject; + + /** + * The arguments to the audited method. + */ + private Serializable[] methodArguments; + + /** + * Any Exception thrown by the audited method. + */ + private Throwable throwable; + + /** + * Did the audited method throw an exception? + */ + private boolean fail; + + /** + * The host address for where the audit was generated. + */ + private InetAddress hostAddress; + + private static InetAddress s_hostAddress; + + /** + * The client address causing the audit + */ + private InetAddress clientAddress; + + /** + * The properties of the key node before the method execution. + */ + private Map keyPropertiesBefore; + + /** + * The properties of the key node after the method execution. + */ + private Map keyPropertiesAfter; + + /** + * For general auditing - the audit message. + */ + private String message; + + static + { + try + { + s_hostAddress = InetAddress.getLocalHost(); + } + catch (UnknownHostException e) + { + s_logger.error(e); + s_hostAddress = null; + } + } + + public AuditState(AuditConfiguration auditConfiguration) + { + super(); + // Add default information + userIdentifier = AuthenticationUtil.getCurrentUserName(); + date = new Date(); + txId = AlfrescoTransactionSupport.getTransactionId(); + sessionId = "Unavailable"; + hostAddress = s_hostAddress; + } + + public String getAuditApplication() + { + return auditApplication; + } + + public void setAuditApplication(String auditApplication) + { + this.auditApplication = auditApplication; + } + + public AuditConfiguration getAuditConfiguration() + { + return auditConfiguration; + } + + public void setAuditConfiguration(AuditConfiguration auditConfiguration) + { + this.auditConfiguration = auditConfiguration; + } + + public String getAuditMethod() + { + return auditMethod; + } + + public void setAuditMethod(String auditMethod) + { + this.auditMethod = auditMethod; + } + + public String getAuditService() + { + return auditService; + } + + public void setAuditService(String auditService) + { + this.auditService = auditService; + } + + public InetAddress getClientAddress() + { + return clientAddress; + } + + public void setClientAddress(InetAddress clientAddress) + { + this.clientAddress = clientAddress; + } + + public Date getDate() + { + return date; + } + + public void setDate(Date date) + { + this.date = date; + } + + public boolean isFail() + { + return fail; + } + + public void setFail(boolean fail) + { + this.fail = fail; + } + + public boolean isFiltered() + { + return filtered; + } + + public void setFiltered(boolean filtered) + { + this.filtered = filtered; + } + + public InetAddress getHostAddress() + { + return hostAddress; + } + + public void setHostAddress(InetAddress hostAddress) + { + this.hostAddress = hostAddress; + } + + public String getKeyGUID() + { + return keyGUID; + } + + public void setKeyGUID(String keyGUID) + { + this.keyGUID = keyGUID; + } + + public Map getKeyPropertiesAfter() + { + return keyPropertiesAfter; + } + + public void setKeyPropertiesAfter(Map keyPropertiesAfter) + { + this.keyPropertiesAfter = keyPropertiesAfter; + } + + public Map getKeyPropertiesBefore() + { + return keyPropertiesBefore; + } + + public void setKeyPropertiesBefore(Map keyPropertiesBefore) + { + this.keyPropertiesBefore = keyPropertiesBefore; + } + + public StoreRef getKeyStore() + { + return keyStore; + } + + public void setKeyStore(StoreRef keyStore) + { + this.keyStore = keyStore; + } + + public String getMessage() + { + return message; + } + + public void setMessage(String message) + { + this.message = message; + } + + public Serializable[] getMethodArguments() + { + return methodArguments; + } + + public void setMethodArguments(Serializable[] methodArguments) + { + this.methodArguments = methodArguments; + } + + public String getPath() + { + return keyPath; + } + + public void setPath(String keyPath) + { + this.keyPath = keyPath; + } + + public Serializable getReturnObject() + { + return returnObject; + } + + public void setReturnObject(Serializable returnObject) + { + this.returnObject = returnObject; + } + + public String getSessionId() + { + return sessionId; + } + + public void setSessionId(String sessionId) + { + this.sessionId = sessionId; + } + + public Throwable getThrowable() + { + return throwable; + } + + public void setThrowable(Throwable throwable) + { + this.throwable = throwable; + } + + public String getTxId() + { + return txId; + } + + public void setTxId(String txId) + { + this.txId = txId; + } + + public String getUserIdentifier() + { + return userIdentifier; + } + + public void setUserIdentifier(String userIdentifier) + { + this.userIdentifier = userIdentifier; + } + +} 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 17ff8452cd..9e40c7a787 100644 --- a/source/java/org/alfresco/repo/audit/hibernate/Audit.hbm.xml +++ b/source/java/org/alfresco/repo/audit/hibernate/Audit.hbm.xml @@ -109,9 +109,9 @@ - - - + + + @@ -155,4 +155,30 @@ audit_store.method = :method + + select + audit_fact + from + org.alfresco.repo.audit.hibernate.AuditFactImpl as audit_fact + where + (audit_fact.storeProtocol = :protocol and + audit_fact.storeId = :store_id and + audit_fact.nodeUUID = :node_id) + or + arg1 like :nodeRef + or + arg2 like :nodeRef + or + arg3 like :nodeRef + or + arg4 like :nodeRef + or + arg5 like :nodeRef + or + returnValue like :nodeRef + order by + audit_fact.date asc + + + \ No newline at end of file diff --git a/source/java/org/alfresco/repo/audit/hibernate/AuditConfigImpl.java b/source/java/org/alfresco/repo/audit/hibernate/AuditConfigImpl.java index 6e86b0b8ee..adbb284e22 100644 --- a/source/java/org/alfresco/repo/audit/hibernate/AuditConfigImpl.java +++ b/source/java/org/alfresco/repo/audit/hibernate/AuditConfigImpl.java @@ -87,10 +87,10 @@ public class AuditConfigImpl implements AuditConfig, InitializingBean /** * Helper method to get the latest audit config */ - public static AuditConfigImpl getLatestConfig(Session session) + public static AuditConfig getLatestConfig(Session session) { Query query = session.getNamedQuery(HibernateAuditDAO.QUERY_LAST_AUDIT_CONFIG); - return (AuditConfigImpl) query.uniqueResult(); + return (AuditConfig) query.uniqueResult(); } @Override diff --git a/source/java/org/alfresco/repo/audit/hibernate/AuditDateImpl.java b/source/java/org/alfresco/repo/audit/hibernate/AuditDateImpl.java index a15e9c85da..0b79abc8bc 100644 --- a/source/java/org/alfresco/repo/audit/hibernate/AuditDateImpl.java +++ b/source/java/org/alfresco/repo/audit/hibernate/AuditDateImpl.java @@ -280,10 +280,10 @@ public class AuditDateImpl implements AuditDate /** * Helper method to get the latest audit date */ - public static AuditDateImpl getLatestDate(Session session) + public static AuditDate getLatestDate(Session session) { Query query = session.getNamedQuery(HibernateAuditDAO.QUERY_LAST_AUDIT_DATE); - return (AuditDateImpl) query.uniqueResult(); + return (AuditDate) query.uniqueResult(); } } diff --git a/source/java/org/alfresco/repo/audit/hibernate/AuditFactImpl.java b/source/java/org/alfresco/repo/audit/hibernate/AuditFactImpl.java index 3ec7f4a4ad..800da9a4b2 100644 --- a/source/java/org/alfresco/repo/audit/hibernate/AuditFactImpl.java +++ b/source/java/org/alfresco/repo/audit/hibernate/AuditFactImpl.java @@ -17,6 +17,11 @@ package org.alfresco.repo.audit.hibernate; import java.util.Date; +import java.util.List; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.hibernate.Query; +import org.hibernate.Session; /** * An Audit fact Rely on standard equals and hash code as they should all be unique. @@ -575,4 +580,17 @@ public class AuditFactImpl implements AuditFact this.userId = userId; } + /** + * Helper method to get all the audit entries for a node. + */ + @SuppressWarnings("unchecked") + public static List getAuditTrail(Session session, NodeRef nodeRef) + { + Query query = session.getNamedQuery(HibernateAuditDAO.QUERY_AUDIT_TRAIL); + query.setParameter(HibernateAuditDAO.QUERY_AUDIT_PROTOCOL, nodeRef.getStoreRef().getProtocol()); + query.setParameter(HibernateAuditDAO.QUERY_AUDIT_STORE_ID, nodeRef.getStoreRef().getIdentifier()); + query.setParameter(HibernateAuditDAO.QUERY_AUDIT_NODE_ID, nodeRef.getId()); + query.setParameter(HibernateAuditDAO.QUERY_AUDIT_NODE_REF, "%"+nodeRef.toString()+"%"); + return (List) query.list(); + } } diff --git a/source/java/org/alfresco/repo/audit/hibernate/AuditInfoImpl.java b/source/java/org/alfresco/repo/audit/hibernate/AuditInfoImpl.java new file mode 100644 index 0000000000..09bdcbcd0e --- /dev/null +++ b/source/java/org/alfresco/repo/audit/hibernate/AuditInfoImpl.java @@ -0,0 +1,231 @@ +/* + * 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.audit.hibernate; + +import java.io.Serializable; +import java.util.Date; +import java.util.Map; + +import org.alfresco.service.cmr.audit.AuditInfo; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.QName; + +public class AuditInfoImpl implements AuditInfo +{ + private String auditApplication; + + private String auditMethod; + + private String auditService; + + private String clientAddress; + + private Date date; + + private boolean fail; + + private boolean filtered; + + private String hostAddress; + + private String keyGUID; + + private Map keyPropertiesAfter; + + private Map keyPropertiesBefore; + + private StoreRef keyStore; + + private String message; + + private Serializable[] methodArguments; + + private String[] methodArgumentsAsStrings; + + private String path; + + private Serializable returnObject; + + private String returnObjectAsString; + + private String sessionId; + + private Throwable throwable; + + private String throwableAsString; + + private String txId; + + private String userIdentifier; + + public AuditInfoImpl(AuditFact auditFact) + { + super(); + this.auditApplication = auditFact.getAuditSource().getApplication(); + this.auditMethod = auditFact.getAuditSource().getMethod(); + this.auditService = auditFact.getAuditSource().getService(); + this.clientAddress = auditFact.getClientInetAddress(); + this.date = auditFact.getDate(); + this.fail = auditFact.isFail(); + this.filtered = auditFact.isFiltered(); + this.hostAddress= auditFact.getHostInetAddress(); + this.keyGUID = auditFact.getNodeUUID(); + this.keyPropertiesAfter = null; + this.keyPropertiesBefore = null; + if((auditFact.getStoreProtocol() != null) && (auditFact.getStoreId() != null)) + { + this.keyStore = new StoreRef(auditFact.getStoreProtocol(), auditFact.getStoreId()); + } + else + { + this.keyStore = null; + } + this.message = auditFact.getMessage(); + this.methodArguments = null; + this.methodArgumentsAsStrings = new String[5]; + this.methodArgumentsAsStrings[0] = auditFact.getArg1(); + this.methodArgumentsAsStrings[1] = auditFact.getArg2(); + this.methodArgumentsAsStrings[2] = auditFact.getArg3(); + this.methodArgumentsAsStrings[3] = auditFact.getArg4(); + this.methodArgumentsAsStrings[4] = auditFact.getArg5(); + this.path = auditFact.getPath(); + this.returnObject = null; + this.returnObjectAsString = auditFact.getReturnValue(); + this.sessionId = auditFact.getSessionId(); + this.throwable = null; + this.throwableAsString = auditFact.getException(); + this.txId = auditFact.getTransactionId(); + this.userIdentifier = auditFact.getUserId(); + } + + public String getAuditApplication() + { + return auditApplication; + } + + public String getAuditMethod() + { + return auditMethod; + } + + public String getAuditService() + { + return auditService; + } + + public String getClientAddress() + { + return clientAddress; + } + + public Date getDate() + { + return date; + } + + public boolean isFail() + { + return fail; + } + + public boolean isFiltered() + { + return filtered; + } + + public String getHostAddress() + { + return hostAddress; + } + + public String getKeyGUID() + { + return keyGUID; + } + + public Map getKeyPropertiesAfter() + { + return keyPropertiesAfter; + } + + public Map getKeyPropertiesBefore() + { + return keyPropertiesBefore; + } + + public StoreRef getKeyStore() + { + return keyStore; + } + + public String getMessage() + { + return message; + } + + public Serializable[] getMethodArguments() + { + return methodArguments; + } + + public String[] getMethodArgumentsAsStrings() + { + return methodArgumentsAsStrings; + } + + public String getPath() + { + return path; + } + + public Serializable getReturnObject() + { + return returnObject; + } + + public String getReturnObjectAsString() + { + return returnObjectAsString; + } + + public String getSessionId() + { + return sessionId; + } + + public Throwable getThrowable() + { + return throwable; + } + + public String getThrowableAsString() + { + return throwableAsString; + } + + public String getTxId() + { + return txId; + } + + public String getUserIdentifier() + { + return userIdentifier; + } + + +} diff --git a/source/java/org/alfresco/repo/audit/hibernate/AuditSource.java b/source/java/org/alfresco/repo/audit/hibernate/AuditSource.java index 17ac1c2ef0..0bab03f211 100644 --- a/source/java/org/alfresco/repo/audit/hibernate/AuditSource.java +++ b/source/java/org/alfresco/repo/audit/hibernate/AuditSource.java @@ -26,4 +26,10 @@ public interface AuditSource public String getService(); + void setApplication(String auditApplication); + + void setService(String auditService); + + void setMethod(String auditMethod); + } \ No newline at end of file diff --git a/source/java/org/alfresco/repo/audit/hibernate/AuditSourceImpl.java b/source/java/org/alfresco/repo/audit/hibernate/AuditSourceImpl.java index 927d042317..5cf50a6cd3 100644 --- a/source/java/org/alfresco/repo/audit/hibernate/AuditSourceImpl.java +++ b/source/java/org/alfresco/repo/audit/hibernate/AuditSourceImpl.java @@ -87,21 +87,21 @@ public class AuditSourceImpl implements AuditSource this.service = service; } - public static AuditSourceImpl getApplicationSource(Session session, String application) + public static AuditSource getApplicationSource(Session session, String application) { Query query = session.getNamedQuery(HibernateAuditDAO.QUERY_AUDIT_APP_SOURCE); query.setParameter(HibernateAuditDAO.QUERY_AUDIT_APP_SOURCE_APP, application); - return (AuditSourceImpl) query.uniqueResult(); + return (AuditSource) query.uniqueResult(); } - public static AuditSourceImpl getApplicationSource(Session session, String application, String service, + public static AuditSource getApplicationSource(Session session, String application, String service, String method) { Query query = session.getNamedQuery(HibernateAuditDAO.QUERY_AUDIT_METHOD_SOURCE); query.setParameter(HibernateAuditDAO.QUERY_AUDIT_APP_SOURCE_APP, application); query.setParameter(HibernateAuditDAO.QUERY_AUDIT_APP_SOURCE_SER, service); query.setParameter(HibernateAuditDAO.QUERY_AUDIT_APP_SOURCE_MET, method); - return (AuditSourceImpl) query.uniqueResult(); + return (AuditSource) query.uniqueResult(); } @Override diff --git a/source/java/org/alfresco/repo/audit/hibernate/HibernateAuditDAO.java b/source/java/org/alfresco/repo/audit/hibernate/HibernateAuditDAO.java index b4bf563e18..3e7b61f091 100644 --- a/source/java/org/alfresco/repo/audit/hibernate/HibernateAuditDAO.java +++ b/source/java/org/alfresco/repo/audit/hibernate/HibernateAuditDAO.java @@ -20,21 +20,28 @@ import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; +import java.util.ArrayList; import java.util.Calendar; +import java.util.Collections; import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; +import java.util.List; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.repo.audit.AuditConfiguration; import org.alfresco.repo.audit.AuditDAO; -import org.alfresco.repo.audit.AuditInfo; +import org.alfresco.repo.audit.AuditException; +import org.alfresco.repo.audit.AuditState; import org.alfresco.repo.content.AbstractContentStore; import org.alfresco.repo.content.ContentStore; import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.transaction.TransactionalDao; +import org.alfresco.service.cmr.audit.AuditInfo; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.datatype.Duration; import org.alfresco.util.EqualsHelper; import org.alfresco.util.GUID; @@ -63,6 +70,16 @@ public class HibernateAuditDAO extends HibernateDaoSupport implements AuditDAO, public static final String QUERY_AUDIT_APP_SOURCE_MET = "method"; + public static final String QUERY_AUDIT_TRAIL = "audit.GetAuditTrailForNode"; + + public static final String QUERY_AUDIT_PROTOCOL = "protocol"; + + public static final String QUERY_AUDIT_STORE_ID = "store_id"; + + public static final String QUERY_AUDIT_NODE_ID = "node_id"; + + public static final String QUERY_AUDIT_NODE_REF = "nodeRef"; + /** a uuid identifying this unique instance */ private String uuid; @@ -92,16 +109,40 @@ public class HibernateAuditDAO extends HibernateDaoSupport implements AuditDAO, this.contentStore = contentStore; } - public void audit(AuditInfo auditInfo) + public void audit(AuditState auditInfo) + { + if(auditInfo.getUserIdentifier() == null) + { + auditInfo.setUserIdentifier(AuthenticationUtil.getSystemUserName()); + } + if(AuthenticationUtil.getCurrentUserName() == null) + { + AuthenticationUtil.setSystemUserAsCurrentUser(); + try + { + audit0(auditInfo); + } + finally + { + AuthenticationUtil.clearCurrentSecurityContext(); + } + } + else + { + audit0(auditInfo); + } + } + + private void audit0(AuditState auditInfo) { // Find/Build the configuraton entry - AuditConfigImpl auditConfig = getAuditConfig(auditInfo); + AuditConfig auditConfig = getAuditConfig(auditInfo); // Find/Build any dates - AuditDateImpl auditDate = getAuditDate(auditInfo); + AuditDate auditDate = getAuditDate(auditInfo); // Find/Build the source - AuditSourceImpl auditSource = getAuditSource(auditInfo); + AuditSource auditSource = getAuditSource(auditInfo); // Build the new audit fact information AuditFactImpl auditFact = new AuditFactImpl(); @@ -170,9 +211,9 @@ public class HibernateAuditDAO extends HibernateDaoSupport implements AuditDAO, } } - private AuditSourceImpl getAuditSource(AuditInfo auditInfo) + private AuditSource getAuditSource(AuditState auditInfo) { - AuditSourceImpl auditSourceImpl; + AuditSource auditSourceImpl; SourceKey sourceKey = new SourceKey(auditInfo.getAuditApplication(), auditInfo.getAuditService(), auditInfo.getAuditMethod()); if(sourceIds.get() == null) @@ -182,7 +223,7 @@ public class HibernateAuditDAO extends HibernateDaoSupport implements AuditDAO, Long id = sourceIds.get().get(sourceKey); if(id != null) { - auditSourceImpl = (AuditSourceImpl) getSession().get(AuditSourceImpl.class, id.longValue()); + auditSourceImpl = (AuditSource) getSession().get(AuditSourceImpl.class, id.longValue()); if(auditSourceImpl != null) { return auditSourceImpl; @@ -218,7 +259,7 @@ public class HibernateAuditDAO extends HibernateDaoSupport implements AuditDAO, return auditSourceImpl; } - private AuditDateImpl getAuditDate(AuditInfo auditInfo) + private AuditDate getAuditDate(AuditState auditInfo) { Calendar cal = GregorianCalendar.getInstance(); cal.setTime(auditInfo.getDate()); @@ -228,7 +269,7 @@ public class HibernateAuditDAO extends HibernateDaoSupport implements AuditDAO, cal.set(Calendar.HOUR_OF_DAY, 0); Date required = cal.getTime(); - AuditDateImpl auditDate; + AuditDate auditDate; if (auditDateImplId.get() == null) { auditDate = AuditDateImpl.getLatestDate(getSession()); @@ -242,7 +283,7 @@ public class HibernateAuditDAO extends HibernateDaoSupport implements AuditDAO, } else { - auditDate = (AuditDateImpl) getSession().get(AuditDateImpl.class, auditDateImplId.get().longValue()); + auditDate = (AuditDate) getSession().get(AuditDateImpl.class, auditDateImplId.get().longValue()); if ((auditDate == null) || (!required.equals(auditDate.getDate()))) { auditDate = AuditDateImpl.getLatestDate(getSession()); @@ -265,9 +306,9 @@ public class HibernateAuditDAO extends HibernateDaoSupport implements AuditDAO, return auditDate; } - private AuditConfigImpl getAuditConfig(AuditInfo auditInfo) + private AuditConfig getAuditConfig(AuditState auditInfo) { - AuditConfigImpl auditConfig; + AuditConfig auditConfig; if ((auditConfiguration.get() == null) || (auditConfiguration.get() != auditInfo.getAuditConfiguration())) { auditConfig = AuditConfigImpl.getLatestConfig(getSession()); @@ -313,7 +354,7 @@ public class HibernateAuditDAO extends HibernateDaoSupport implements AuditDAO, } else { - auditConfig = (AuditConfigImpl) getSession() + auditConfig = (AuditConfig) getSession() .get(AuditConfigImpl.class, auditConfigImplId.get().longValue()); if (auditConfig == null) { @@ -323,7 +364,7 @@ public class HibernateAuditDAO extends HibernateDaoSupport implements AuditDAO, return auditConfig; } - private AuditConfigImpl createNewAuditConfigImpl(AuditInfo auditInfo) + private AuditConfigImpl createNewAuditConfigImpl(AuditState auditInfo) { AuditConfigImpl auditConfig = new AuditConfigImpl(); InputStream is = new BufferedInputStream(auditInfo.getAuditConfiguration().getInputStream()); @@ -436,4 +477,23 @@ public class HibernateAuditDAO extends HibernateDaoSupport implements AuditDAO, return hash; } } + + public List getAuditTrail(NodeRef nodeRef) + { + if(nodeRef == null) + { + return Collections.emptyList(); + } + List internalTrail = AuditFactImpl.getAuditTrail(getSession(), nodeRef); + + ArrayList answer = new ArrayList(internalTrail.size()); + for(AuditFact auditFact : internalTrail) + { + AuditInfo info = new AuditInfoImpl(auditFact); + answer.add(info); + } + return answer; + } + + } diff --git a/source/java/org/alfresco/repo/audit/model/AbstractAuditEntry.java b/source/java/org/alfresco/repo/audit/model/AbstractAuditEntry.java index 7349e44be0..c99fd0bb7c 100644 --- a/source/java/org/alfresco/repo/audit/model/AbstractAuditEntry.java +++ b/source/java/org/alfresco/repo/audit/model/AbstractAuditEntry.java @@ -117,7 +117,7 @@ public abstract class AbstractAuditEntry return auditMode; } - /* package */TrueFalseUnset getEnabled() + public TrueFalseUnset getEnabled() { return enabled; } diff --git a/source/java/org/alfresco/repo/cache/EhCacheAdapter.java b/source/java/org/alfresco/repo/cache/EhCacheAdapter.java index 46f01e3bbd..5f2c37b9bf 100644 --- a/source/java/org/alfresco/repo/cache/EhCacheAdapter.java +++ b/source/java/org/alfresco/repo/cache/EhCacheAdapter.java @@ -16,7 +16,6 @@ */ package org.alfresco.repo.cache; -import java.io.IOException; import java.io.Serializable; import net.sf.ehcache.Cache; diff --git a/source/java/org/alfresco/repo/content/RoutingContentService.java b/source/java/org/alfresco/repo/content/RoutingContentService.java index 7e1901d2a7..5b169b6d0d 100644 --- a/source/java/org/alfresco/repo/content/RoutingContentService.java +++ b/source/java/org/alfresco/repo/content/RoutingContentService.java @@ -1,505 +1,506 @@ -/* - * 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.content; - -import java.io.Serializable; -import java.util.Collection; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.model.ContentModel; -import org.alfresco.repo.avm.AVMNodeConverter; -import org.alfresco.repo.content.ContentServicePolicies.OnContentReadPolicy; -import org.alfresco.repo.content.ContentServicePolicies.OnContentUpdatePolicy; -import org.alfresco.repo.content.filestore.FileContentStore; -import org.alfresco.repo.content.transform.ContentTransformer; -import org.alfresco.repo.content.transform.ContentTransformerRegistry; -import org.alfresco.repo.content.transform.magick.ImageMagickContentTransformer; -import org.alfresco.repo.policy.ClassPolicyDelegate; -import org.alfresco.repo.policy.JavaBehaviour; -import org.alfresco.repo.policy.PolicyComponent; -import org.alfresco.service.cmr.avm.AVMService; -import org.alfresco.service.cmr.dictionary.DataTypeDefinition; -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.cmr.dictionary.InvalidTypeException; -import org.alfresco.service.cmr.dictionary.PropertyDefinition; -import org.alfresco.service.cmr.repository.ContentData; -import org.alfresco.service.cmr.repository.ContentIOException; -import org.alfresco.service.cmr.repository.ContentReader; -import org.alfresco.service.cmr.repository.ContentService; -import org.alfresco.service.cmr.repository.ContentStreamListener; -import org.alfresco.service.cmr.repository.ContentWriter; -import org.alfresco.service.cmr.repository.NoTransformerException; -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.namespace.NamespaceService; -import org.alfresco.service.namespace.QName; -import org.alfresco.service.transaction.TransactionService; -import org.alfresco.util.EqualsHelper; -import org.alfresco.util.TempFileProvider; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - - -/** - * A content service that determines at runtime the store that the - * content associated with a node should be routed to. - * - * @author Derek Hulley - */ -public class RoutingContentService implements ContentService -{ - private static Log logger = LogFactory.getLog(RoutingContentService.class); - - private TransactionService transactionService; - private DictionaryService dictionaryService; - private NodeService nodeService; - private AVMService avmService; - - /** a registry of all available content transformers */ - private ContentTransformerRegistry transformerRegistry; - /** TEMPORARY until we have a map to choose from at runtime */ - private ContentStore store; - /** the store for all temporarily created content */ - private ContentStore tempStore; - private ImageMagickContentTransformer imageMagickContentTransformer; - - /** - * The policy component - */ - private PolicyComponent policyComponent; - - /** - * Policies delegate - */ - ClassPolicyDelegate onContentUpdateDelegate; - ClassPolicyDelegate onContentReadDelegate; - - /** - * Default constructor sets up a temporary store - */ - public RoutingContentService() - { - this.tempStore = new FileContentStore(TempFileProvider.getTempDir().getAbsolutePath()); - } - - public void setTransactionService(TransactionService transactionService) - { - this.transactionService = transactionService; - } - - public void setDictionaryService(DictionaryService dictionaryService) - { - this.dictionaryService = dictionaryService; - } - - public void setNodeService(NodeService nodeService) - { - this.nodeService = nodeService; - } - - public void setTransformerRegistry(ContentTransformerRegistry transformerRegistry) - { - this.transformerRegistry = transformerRegistry; - } - - public void setStore(ContentStore store) - { - this.store = store; - } - - public void setPolicyComponent(PolicyComponent policyComponent) - { - this.policyComponent = policyComponent; - } - - public void setAvmService(AVMService service) - { - this.avmService = service; - } - - public void setImageMagickContentTransformer(ImageMagickContentTransformer imageMagickContentTransformer) - { - this.imageMagickContentTransformer = imageMagickContentTransformer; - } - - /** - * Service initialise - */ - public void init() - { - // Bind on update properties behaviour - this.policyComponent.bindClassBehaviour( - QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"), - this, - new JavaBehaviour(this, "onUpdateProperties")); - - // Register on content update policy - this.onContentUpdateDelegate = this.policyComponent.registerClassPolicy(OnContentUpdatePolicy.class); - this.onContentReadDelegate = this.policyComponent.registerClassPolicy(OnContentReadPolicy.class); - } - - /** - * Update properties policy behaviour - * - * @param nodeRef the node reference - * @param before the before values of the properties - * @param after the after values of the properties - */ - public void onUpdateProperties( - NodeRef nodeRef, - Map before, - Map after) - { - boolean fire = false; - boolean newContent = false; - // check if any of the content properties have changed - for (QName propertyQName : after.keySet()) - { - // is this a content property? - PropertyDefinition propertyDef = dictionaryService.getProperty(propertyQName); - if (propertyDef == null) - { - // the property is not recognised - continue; - } - if (!propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT)) - { - // not a content type - continue; - } - - try - { - ContentData beforeValue = (ContentData) before.get(propertyQName); - ContentData afterValue = (ContentData) after.get(propertyQName); - if (afterValue != null && afterValue.getContentUrl() == null) - { - // no URL - ignore - } - else if (!EqualsHelper.nullSafeEquals(beforeValue, afterValue)) - { - // So debug ... - if (logger.isDebugEnabled() == true) - { - String beforeString = ""; - if (beforeValue != null) - { - beforeString = beforeValue.toString(); - } - String afterString = ""; - if (afterValue != null) - { - afterString = afterValue.toString(); - } - logger.debug("onContentUpate: before = " + beforeString + "; after = " + afterString); - } - - // Figure out if the content is new or not - String beforeContentUrl = null; - if (beforeValue != null) - { - beforeContentUrl = beforeValue.getContentUrl(); - } - String afterContentUrl = null; - if (afterValue != null) - { - afterContentUrl = afterValue.getContentUrl(); - } - if (beforeContentUrl == null && afterContentUrl != null) - { - newContent = true; - } - - // the content changed - // at the moment, we are only interested in this one change - fire = true; - break; - } - } - catch (ClassCastException e) - { - // properties don't conform to model - continue; - } - } - // fire? - if (fire) - { - // Fire the content update policy - Set types = new HashSet(this.nodeService.getAspects(nodeRef)); - types.add(this.nodeService.getType(nodeRef)); - OnContentUpdatePolicy policy = this.onContentUpdateDelegate.get(nodeRef, types); - policy.onContentUpdate(nodeRef, newContent); - } - } - - public ContentReader getReader(NodeRef nodeRef, QName propertyQName) - { - return getReader(nodeRef, propertyQName, true); - } - - private ContentReader getReader(NodeRef nodeRef, QName propertyQName, boolean fireContentReadPolicy) - { - ContentData contentData = null; - Serializable propValue = nodeService.getProperty(nodeRef, propertyQName); - if (propValue instanceof Collection) - { - Collection colPropValue = (Collection)propValue; - if (colPropValue.size() > 0) - { - propValue = (Serializable)colPropValue.iterator().next(); - } - } - - if (propValue instanceof ContentData) - { - contentData = (ContentData)propValue; - } - - if (contentData == null) - { - // if no value or a value other content, and a property definition has been provided, ensure that it's CONTENT or ANY - PropertyDefinition contentPropDef = dictionaryService.getProperty(propertyQName); - if (contentPropDef != null && - (!(contentPropDef.getDataType().getName().equals(DataTypeDefinition.CONTENT) || - contentPropDef.getDataType().getName().equals(DataTypeDefinition.ANY)))) - { - throw new InvalidTypeException("The node property must be of type content: \n" + - " node: " + nodeRef + "\n" + - " property name: " + propertyQName + "\n" + - " property type: " + ((contentPropDef == null) ? "unknown" : contentPropDef.getDataType()), - propertyQName); - } - } - - // check that the URL is available - if (contentData == null || contentData.getContentUrl() == null) - { - // there is no URL - the interface specifies that this is not an error condition - return null; - } - String contentUrl = contentData.getContentUrl(); - - // TODO: Choose the store to read from at runtime - ContentReader reader = store.getReader(contentUrl); - - // set extra data on the reader - reader.setMimetype(contentData.getMimetype()); - reader.setEncoding(contentData.getEncoding()); - - // Fire the content read policy - if (reader != null && fireContentReadPolicy == true) - { - // Fire the content update policy - Set types = new HashSet(this.nodeService.getAspects(nodeRef)); - types.add(this.nodeService.getType(nodeRef)); - OnContentReadPolicy policy = this.onContentReadDelegate.get(nodeRef, types); - policy.onContentRead(nodeRef); - } - - // we don't listen for anything - // result may be null - but interface contract says we may return null - return reader; - } - - public ContentWriter getWriter(NodeRef nodeRef, QName propertyQName, boolean update) - { - - // check for an existing URL - the get of the reader will perform type checking - ContentReader existingContentReader = getReader(nodeRef, propertyQName, false); - - // TODO: Choose the store to write to at runtime - - // get the content using the (potentially) existing content - the new content - // can be wherever the store decides. - ContentWriter writer = store.getWriter(existingContentReader, null); - - // Special case for AVM repository. - Serializable contentValue = null; - if (nodeRef.getStoreRef().getProtocol().equals(StoreRef.PROTOCOL_AVM)) - { - Object [] avmVersionPath = AVMNodeConverter.ToAVMVersionPath(nodeRef); - contentValue = avmService.getContentDataForWrite((String)avmVersionPath[1]); - } - else - { - contentValue = nodeService.getProperty(nodeRef, propertyQName); - } - // set extra data on the reader if the property is pre-existing - if (contentValue != null && contentValue instanceof ContentData) - { - ContentData contentData = (ContentData)contentValue; - writer.setMimetype(contentData.getMimetype()); - writer.setEncoding(contentData.getEncoding()); - } - - // attach a listener if required - if (update) - { - // need a listener to update the node when the stream closes - WriteStreamListener listener = new WriteStreamListener(nodeService, nodeRef, propertyQName, writer); - writer.addListener(listener); - writer.setTransactionService(transactionService); - } - - // give back to the client - return writer; - } - - /** - * @return Returns a writer to an anonymous location - */ - public ContentWriter getTempWriter() - { - // there is no existing content and we don't specify the location of the new content - return tempStore.getWriter(null, null); - } - - /** - * @see org.alfresco.repo.content.transform.ContentTransformerRegistry - * @see org.alfresco.repo.content.transform.ContentTransformer - */ - public void transform(ContentReader reader, ContentWriter writer) - throws NoTransformerException, ContentIOException - { - // check that source and target mimetypes are available - String sourceMimetype = reader.getMimetype(); - if (sourceMimetype == null) - { - throw new AlfrescoRuntimeException("The content reader mimetype must be set: " + reader); - } - String targetMimetype = writer.getMimetype(); - if (targetMimetype == null) - { - throw new AlfrescoRuntimeException("The content writer mimetype must be set: " + writer); - } - // look for a transformer - ContentTransformer transformer = transformerRegistry.getTransformer(sourceMimetype, targetMimetype); - if (transformer == null) - { - throw new NoTransformerException(sourceMimetype, targetMimetype); - } - // we have a transformer, so do it - transformer.transform(reader, writer); - // done - } - - /** - * @see org.alfresco.repo.content.transform.ContentTransformerRegistry - * @see org.alfresco.repo.content.transform.ContentTransformer - */ - public ContentTransformer getTransformer(String sourceMimetype, String targetMimetype) - { - // look for a transformer - ContentTransformer transformer = transformerRegistry.getTransformer(sourceMimetype, targetMimetype); - // done - return transformer; - } - - /** - * @see org.alfresco.service.cmr.repository.ContentService#getImageTransformer() - */ - public ContentTransformer getImageTransformer() - { - return imageMagickContentTransformer; - } - - /** - * @see org.alfresco.repo.content.transform.ContentTransformerRegistry - * @see org.alfresco.repo.content.transform.ContentTransformer - */ - public boolean isTransformable(ContentReader reader, ContentWriter writer) - { - // check that source and target mimetypes are available - String sourceMimetype = reader.getMimetype(); - if (sourceMimetype == null) - { - throw new AlfrescoRuntimeException("The content reader mimetype must be set: " + reader); - } - String targetMimetype = writer.getMimetype(); - if (targetMimetype == null) - { - throw new AlfrescoRuntimeException("The content writer mimetype must be set: " + writer); - } - - // look for a transformer - ContentTransformer transformer = transformerRegistry.getTransformer(sourceMimetype, targetMimetype); - return (transformer != null); - } - - /** - * Ensures that, upon closure of the output stream, the node is updated with - * the latest URL of the content to which it refers. - *

- * The listener close operation does not need a transaction as the - * ContentWriter takes care of that. - * - * @author Derek Hulley - */ - private static class WriteStreamListener implements ContentStreamListener - { - private NodeService nodeService; - private NodeRef nodeRef; - private QName propertyQName; - private ContentWriter writer; - - public WriteStreamListener( - NodeService nodeService, - NodeRef nodeRef, - QName propertyQName, - ContentWriter writer) - { - this.nodeService = nodeService; - this.nodeRef = nodeRef; - this.propertyQName = propertyQName; - this.writer = writer; - } - - public void contentStreamClosed() throws ContentIOException - { - try - { - // set the full content property - ContentData contentData = writer.getContentData(); - nodeService.setProperty( - nodeRef, - propertyQName, - contentData); - // done - if (logger.isDebugEnabled()) - { - logger.debug("Stream listener updated node: \n" + - " node: " + nodeRef + "\n" + - " property: " + propertyQName + "\n" + - " value: " + contentData); - } - } - catch (Throwable e) - { - throw new ContentIOException("Failed to set content property on stream closure: \n" + - " node: " + nodeRef + "\n" + - " property: " + propertyQName + "\n" + - " writer: " + writer, - e); - } - } - } -} +/* + * 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.content; + +import java.io.Serializable; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.avm.AVMNodeConverter; +import org.alfresco.repo.content.ContentServicePolicies.OnContentReadPolicy; +import org.alfresco.repo.content.ContentServicePolicies.OnContentUpdatePolicy; +import org.alfresco.repo.content.filestore.FileContentStore; +import org.alfresco.repo.content.transform.ContentTransformer; +import org.alfresco.repo.content.transform.ContentTransformerRegistry; +import org.alfresco.repo.content.transform.magick.ImageMagickContentTransformer; +import org.alfresco.repo.policy.ClassPolicyDelegate; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.service.cmr.avm.AVMService; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.InvalidTypeException; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.ContentIOException; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentStreamListener; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NoTransformerException; +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.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.EqualsHelper; +import org.alfresco.util.TempFileProvider; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + + +/** + * A content service that determines at runtime the store that the + * content associated with a node should be routed to. + * + * @author Derek Hulley + */ +public class RoutingContentService implements ContentService +{ + private static Log logger = LogFactory.getLog(RoutingContentService.class); + + private TransactionService transactionService; + private DictionaryService dictionaryService; + private NodeService nodeService; + private AVMService avmService; + + /** a registry of all available content transformers */ + private ContentTransformerRegistry transformerRegistry; + /** TEMPORARY until we have a map to choose from at runtime */ + private ContentStore store; + /** the store for all temporarily created content */ + private ContentStore tempStore; + private ImageMagickContentTransformer imageMagickContentTransformer; + + /** + * The policy component + */ + private PolicyComponent policyComponent; + + /** + * Policies delegate + */ + ClassPolicyDelegate onContentUpdateDelegate; + ClassPolicyDelegate onContentReadDelegate; + + /** + * Default constructor sets up a temporary store + */ + public RoutingContentService() + { + this.tempStore = new FileContentStore(TempFileProvider.getTempDir().getAbsolutePath()); + } + + public void setTransactionService(TransactionService transactionService) + { + this.transactionService = transactionService; + } + + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setTransformerRegistry(ContentTransformerRegistry transformerRegistry) + { + this.transformerRegistry = transformerRegistry; + } + + public void setStore(ContentStore store) + { + this.store = store; + } + + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + public void setAvmService(AVMService service) + { + this.avmService = service; + } + + public void setImageMagickContentTransformer(ImageMagickContentTransformer imageMagickContentTransformer) + { + this.imageMagickContentTransformer = imageMagickContentTransformer; + } + + /** + * Service initialise + */ + public void init() + { + // Bind on update properties behaviour + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"), + this, + new JavaBehaviour(this, "onUpdateProperties")); + + // Register on content update policy + this.onContentUpdateDelegate = this.policyComponent.registerClassPolicy(OnContentUpdatePolicy.class); + this.onContentReadDelegate = this.policyComponent.registerClassPolicy(OnContentReadPolicy.class); + } + + /** + * Update properties policy behaviour + * + * @param nodeRef the node reference + * @param before the before values of the properties + * @param after the after values of the properties + */ + public void onUpdateProperties( + NodeRef nodeRef, + Map before, + Map after) + { + boolean fire = false; + boolean newContent = false; + // check if any of the content properties have changed + for (QName propertyQName : after.keySet()) + { + // is this a content property? + PropertyDefinition propertyDef = dictionaryService.getProperty(propertyQName); + if (propertyDef == null) + { + // the property is not recognised + continue; + } + if (!propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT)) + { + // not a content type + continue; + } + + try + { + ContentData beforeValue = (ContentData) before.get(propertyQName); + ContentData afterValue = (ContentData) after.get(propertyQName); + if (afterValue != null && afterValue.getContentUrl() == null) + { + // no URL - ignore + } + else if (!EqualsHelper.nullSafeEquals(beforeValue, afterValue)) + { + // So debug ... + if (logger.isDebugEnabled() == true) + { + String beforeString = ""; + if (beforeValue != null) + { + beforeString = beforeValue.toString(); + } + String afterString = ""; + if (afterValue != null) + { + afterString = afterValue.toString(); + } + logger.debug("onContentUpate: before = " + beforeString + "; after = " + afterString); + } + + // Figure out if the content is new or not + String beforeContentUrl = null; + if (beforeValue != null) + { + beforeContentUrl = beforeValue.getContentUrl(); + } + String afterContentUrl = null; + if (afterValue != null) + { + afterContentUrl = afterValue.getContentUrl(); + } + if (beforeContentUrl == null && afterContentUrl != null) + { + newContent = true; + } + + // the content changed + // at the moment, we are only interested in this one change + fire = true; + break; + } + } + catch (ClassCastException e) + { + // properties don't conform to model + continue; + } + } + // fire? + if (fire) + { + // Fire the content update policy + Set types = new HashSet(this.nodeService.getAspects(nodeRef)); + types.add(this.nodeService.getType(nodeRef)); + OnContentUpdatePolicy policy = this.onContentUpdateDelegate.get(nodeRef, types); + policy.onContentUpdate(nodeRef, newContent); + } + } + + public ContentReader getReader(NodeRef nodeRef, QName propertyQName) + { + return getReader(nodeRef, propertyQName, true); + } + + private ContentReader getReader(NodeRef nodeRef, QName propertyQName, boolean fireContentReadPolicy) + { + ContentData contentData = null; + Serializable propValue = nodeService.getProperty(nodeRef, propertyQName); + if (propValue instanceof Collection) + { + Collection colPropValue = (Collection)propValue; + if (colPropValue.size() > 0) + { + propValue = (Serializable)colPropValue.iterator().next(); + } + } + + if (propValue instanceof ContentData) + { + contentData = (ContentData)propValue; + } + + if (contentData == null) + { + // if no value or a value other content, and a property definition has been provided, ensure that it's CONTENT or ANY + PropertyDefinition contentPropDef = dictionaryService.getProperty(propertyQName); + if (contentPropDef != null && + (!(contentPropDef.getDataType().getName().equals(DataTypeDefinition.CONTENT) || + contentPropDef.getDataType().getName().equals(DataTypeDefinition.ANY)))) + { + throw new InvalidTypeException("The node property must be of type content: \n" + + " node: " + nodeRef + "\n" + + " property name: " + propertyQName + "\n" + + " property type: " + ((contentPropDef == null) ? "unknown" : contentPropDef.getDataType()), + propertyQName); + } + } + + // check that the URL is available + if (contentData == null || contentData.getContentUrl() == null) + { + // there is no URL - the interface specifies that this is not an error condition + return null; + } + String contentUrl = contentData.getContentUrl(); + + // TODO: Choose the store to read from at runtime + ContentReader reader = store.getReader(contentUrl); + + // set extra data on the reader + reader.setMimetype(contentData.getMimetype()); + reader.setEncoding(contentData.getEncoding()); + + // Fire the content read policy + if (reader != null && fireContentReadPolicy == true) + { + // Fire the content update policy + Set types = new HashSet(this.nodeService.getAspects(nodeRef)); + types.add(this.nodeService.getType(nodeRef)); + OnContentReadPolicy policy = this.onContentReadDelegate.get(nodeRef, types); + policy.onContentRead(nodeRef); + } + + // we don't listen for anything + // result may be null - but interface contract says we may return null + return reader; + } + + public ContentWriter getWriter(NodeRef nodeRef, QName propertyQName, boolean update) + { + + // check for an existing URL - the get of the reader will perform type checking + ContentReader existingContentReader = getReader(nodeRef, propertyQName, false); + + // TODO: Choose the store to write to at runtime + + // get the content using the (potentially) existing content - the new content + // can be wherever the store decides. + ContentWriter writer = store.getWriter(existingContentReader, null); + + // Special case for AVM repository. + Serializable contentValue = null; + if (nodeRef.getStoreRef().getProtocol().equals(StoreRef.PROTOCOL_AVM)) + { + Object [] avmVersionPath = AVMNodeConverter.ToAVMVersionPath(nodeRef); + contentValue = avmService.getContentDataForWrite((String)avmVersionPath[1]); + } + else + { + contentValue = nodeService.getProperty(nodeRef, propertyQName); + } + + // set extra data on the reader if the property is pre-existing + if (contentValue != null && contentValue instanceof ContentData) + { + ContentData contentData = (ContentData)contentValue; + writer.setMimetype(contentData.getMimetype()); + writer.setEncoding(contentData.getEncoding()); + } + + // attach a listener if required + if (update) + { + // need a listener to update the node when the stream closes + WriteStreamListener listener = new WriteStreamListener(nodeService, nodeRef, propertyQName, writer); + writer.addListener(listener); + writer.setTransactionService(transactionService); + } + + // give back to the client + return writer; + } + + /** + * @return Returns a writer to an anonymous location + */ + public ContentWriter getTempWriter() + { + // there is no existing content and we don't specify the location of the new content + return tempStore.getWriter(null, null); + } + + /** + * @see org.alfresco.repo.content.transform.ContentTransformerRegistry + * @see org.alfresco.repo.content.transform.ContentTransformer + */ + public void transform(ContentReader reader, ContentWriter writer) + throws NoTransformerException, ContentIOException + { + // check that source and target mimetypes are available + String sourceMimetype = reader.getMimetype(); + if (sourceMimetype == null) + { + throw new AlfrescoRuntimeException("The content reader mimetype must be set: " + reader); + } + String targetMimetype = writer.getMimetype(); + if (targetMimetype == null) + { + throw new AlfrescoRuntimeException("The content writer mimetype must be set: " + writer); + } + // look for a transformer + ContentTransformer transformer = transformerRegistry.getTransformer(sourceMimetype, targetMimetype); + if (transformer == null) + { + throw new NoTransformerException(sourceMimetype, targetMimetype); + } + // we have a transformer, so do it + transformer.transform(reader, writer); + // done + } + + /** + * @see org.alfresco.repo.content.transform.ContentTransformerRegistry + * @see org.alfresco.repo.content.transform.ContentTransformer + */ + public ContentTransformer getTransformer(String sourceMimetype, String targetMimetype) + { + // look for a transformer + ContentTransformer transformer = transformerRegistry.getTransformer(sourceMimetype, targetMimetype); + // done + return transformer; + } + + /** + * @see org.alfresco.service.cmr.repository.ContentService#getImageTransformer() + */ + public ContentTransformer getImageTransformer() + { + return imageMagickContentTransformer; + } + + /** + * @see org.alfresco.repo.content.transform.ContentTransformerRegistry + * @see org.alfresco.repo.content.transform.ContentTransformer + */ + public boolean isTransformable(ContentReader reader, ContentWriter writer) + { + // check that source and target mimetypes are available + String sourceMimetype = reader.getMimetype(); + if (sourceMimetype == null) + { + throw new AlfrescoRuntimeException("The content reader mimetype must be set: " + reader); + } + String targetMimetype = writer.getMimetype(); + if (targetMimetype == null) + { + throw new AlfrescoRuntimeException("The content writer mimetype must be set: " + writer); + } + + // look for a transformer + ContentTransformer transformer = transformerRegistry.getTransformer(sourceMimetype, targetMimetype); + return (transformer != null); + } + + /** + * Ensures that, upon closure of the output stream, the node is updated with + * the latest URL of the content to which it refers. + *

+ * The listener close operation does not need a transaction as the + * ContentWriter takes care of that. + * + * @author Derek Hulley + */ + private static class WriteStreamListener implements ContentStreamListener + { + private NodeService nodeService; + private NodeRef nodeRef; + private QName propertyQName; + private ContentWriter writer; + + public WriteStreamListener( + NodeService nodeService, + NodeRef nodeRef, + QName propertyQName, + ContentWriter writer) + { + this.nodeService = nodeService; + this.nodeRef = nodeRef; + this.propertyQName = propertyQName; + this.writer = writer; + } + + public void contentStreamClosed() throws ContentIOException + { + try + { + // set the full content property + ContentData contentData = writer.getContentData(); + nodeService.setProperty( + nodeRef, + propertyQName, + contentData); + // done + if (logger.isDebugEnabled()) + { + logger.debug("Stream listener updated node: \n" + + " node: " + nodeRef + "\n" + + " property: " + propertyQName + "\n" + + " value: " + contentData); + } + } + catch (Throwable e) + { + throw new ContentIOException("Failed to set content property on stream closure: \n" + + " node: " + nodeRef + "\n" + + " property: " + propertyQName + "\n" + + " writer: " + writer, + e); + } + } + } +} diff --git a/source/java/org/alfresco/repo/copy/CopyServiceImpl.java b/source/java/org/alfresco/repo/copy/CopyServiceImpl.java index 8172f86235..d9a0f7e59e 100644 --- a/source/java/org/alfresco/repo/copy/CopyServiceImpl.java +++ b/source/java/org/alfresco/repo/copy/CopyServiceImpl.java @@ -193,17 +193,17 @@ public class CopyServiceImpl implements CopyService */ public NodeRef copy( NodeRef sourceNodeRef, - NodeRef destinationParent, + NodeRef destinationParentRef, QName destinationAssocTypeQName, QName destinationQName, boolean copyChildren) { // Check that all the passed values are not null ParameterCheck.mandatory("Source Node", sourceNodeRef); - ParameterCheck.mandatory("Destination Parent", destinationParent); + ParameterCheck.mandatory("Destination Parent", destinationParentRef); ParameterCheck.mandatory("Destination Association Name", destinationQName); - if (sourceNodeRef.getStoreRef().equals(destinationParent.getStoreRef()) == false) + if (sourceNodeRef.getStoreRef().equals(destinationParentRef.getStoreRef()) == false) { // TODO We need to create a new node in the other store with the same id as the source @@ -211,9 +211,19 @@ public class CopyServiceImpl implements CopyService throw new UnsupportedOperationException("Copying nodes across stores is not currently supported."); } + // Get the original parent reference + NodeRef sourceParentRef = nodeService.getPrimaryParent(sourceNodeRef).getParentRef(); // Recursively copy node Map copiedChildren = new HashMap(); - NodeRef copy = recursiveCopy(sourceNodeRef, destinationParent, destinationAssocTypeQName, destinationQName, copyChildren, copiedChildren); + NodeRef copy = recursiveCopy( + sourceNodeRef, + sourceParentRef, + destinationParentRef, + destinationAssocTypeQName, + destinationQName, + copyChildren, + true, // top-level copy drops the name, if the parent is different + copiedChildren); // Foreach of the newly created copies call the copy complete policy for (Map.Entry entry : copiedChildren.entrySet()) @@ -360,20 +370,16 @@ public class CopyServiceImpl implements CopyService /** * Recursive copy algorithm * - * @param sourceNodeRef - * @param destinationParent - * @param destinationAssocTypeQName - * @param destinationQName - * @param copyChildren - * @param copiedChildren - * @return + * @param dropName drop the name property when associations don't allow duplicately named children */ private NodeRef recursiveCopy( NodeRef sourceNodeRef, - NodeRef destinationParent, + NodeRef sourceParentRef, + NodeRef destinationParentRef, QName destinationAssocTypeQName, QName destinationQName, boolean copyChildren, + boolean dropName, Map copiedChildren) { // Extract Type Definition @@ -385,7 +391,7 @@ public class CopyServiceImpl implements CopyService } // Establish the scope of the copy - PolicyScope copyDetails = getCopyDetails(sourceNodeRef, destinationParent.getStoreRef(), true); + PolicyScope copyDetails = getCopyDetails(sourceNodeRef, destinationParentRef.getStoreRef(), true); // Create collection of properties for type and mandatory aspects Map typeProps = copyDetails.getProperties(); @@ -403,8 +409,8 @@ public class CopyServiceImpl implements CopyService } } - // if the parent node is the same, then remove the name property - it will have to - // be changed by the client code + // Drop the name property, if required. This prevents duplicate names and leaves it up to the client + // to assign a new name. AssociationDefinition assocDef = dictionaryService.getAssociation(destinationAssocTypeQName); if (!assocDef.isChild()) { @@ -413,7 +419,7 @@ public class CopyServiceImpl implements CopyService else { ChildAssociationDefinition childAssocDef = (ChildAssociationDefinition) assocDef; - if (!childAssocDef.getDuplicateChildNamesAllowed()) + if (dropName && !childAssocDef.getDuplicateChildNamesAllowed()) { // duplicate children are not allowed. properties.remove(ContentModel.PROP_NAME); @@ -422,14 +428,14 @@ public class CopyServiceImpl implements CopyService // Create the new node ChildAssociationRef destinationChildAssocRef = this.nodeService.createNode( - destinationParent, + destinationParentRef, destinationAssocTypeQName, destinationQName, sourceTypeRef, properties); NodeRef destinationNodeRef = destinationChildAssocRef.getChildRef(); copiedChildren.put(sourceNodeRef, destinationNodeRef); - + // Prevent any rules being fired on the new destination node this.ruleService.disableRules(destinationNodeRef); try @@ -736,9 +742,9 @@ public class CopyServiceImpl implements CopyService */ private void copyChildAssociations( QName classRef, - NodeRef destinationNodeRef, + NodeRef destinationNodeRef, PolicyScope copyDetails, - boolean copyChildren, + boolean copyChildren, Map copiedChildren) { List childAssocs = copyDetails.getChildAssociations(classRef); @@ -756,11 +762,13 @@ public class CopyServiceImpl implements CopyService { // Copy the child recursiveCopy( - childAssoc.getChildRef(), + childAssoc.getChildRef(), + childAssoc.getParentRef(), destinationNodeRef, childAssoc.getTypeQName(), childAssoc.getQName(), copyChildren, + false, // the target and source parents can't be the same copiedChildren); } } @@ -784,11 +792,13 @@ public class CopyServiceImpl implements CopyService { // Always recursivly copy configuration folders recursiveCopy( - childRef, + childRef, + childAssoc.getParentRef(), destinationNodeRef, childAssoc.getTypeQName(), childAssoc.getQName(), true, + false, // the target and source parents can't be the same copiedChildren); } } diff --git a/source/java/org/alfresco/repo/domain/DbAccessControlList.java b/source/java/org/alfresco/repo/domain/DbAccessControlList.java index 327ed47347..da378dd6c0 100644 --- a/source/java/org/alfresco/repo/domain/DbAccessControlList.java +++ b/source/java/org/alfresco/repo/domain/DbAccessControlList.java @@ -1,83 +1,83 @@ -/* - * 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.domain; - -import java.util.Set; - -import org.alfresco.repo.domain.hibernate.DbAccessControlEntryImpl; - - -/** - * The interface to support persistence of node access control entries in hibernate - * - * @author andyh - */ -public interface DbAccessControlList -{ - public long getId(); - - /** - * - * @return Returns the access control entries for this access control list - */ - public Set getEntries(); - - /** - * Get inheritance behaviour - * @return Returns the inheritance status of this list - */ - public boolean getInherits(); - - /** - * Set inheritance behaviour - * @param inherits true to set the permissions to inherit - */ - public void setInherits(boolean inherits); - - public int deleteEntriesForAuthority(String authorityKey); - - public int deleteEntriesForPermission(DbPermissionKey permissionKey); - - public int deleteEntry(String authorityKey, DbPermissionKey permissionKey); - - /** - * Delete the entries related to this access control list - * - * @return Returns the number of entries deleted - */ - public int deleteEntries(); - - public DbAccessControlEntry getEntry(String authorityKey, DbPermissionKey permissionKey); - - /** - * Factory method to create an entry and wire it up. - * Note that the returned value may still be transient. Saving it should be fine, but - * is not required. - * - * @param permission the mandatory permission association with this entry - * @param authority the mandatory authority. Must not be transient. - * @param allowed allowed or disallowed. Must not be transient. - * @return Returns the new entry - */ - public DbAccessControlEntryImpl newEntry(DbPermission permission, DbAuthority authority, boolean allowed); - - /** - * Make a copy of this ACL (persistently) - * @return The copy. - */ - public DbAccessControlList getCopy(); -} +/* + * 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.domain; + +import java.util.Set; + +import org.alfresco.repo.domain.hibernate.DbAccessControlEntryImpl; + + +/** + * The interface to support persistence of node access control entries in hibernate + * + * @author andyh + */ +public interface DbAccessControlList +{ + public long getId(); + + /** + * + * @return Returns the access control entries for this access control list + */ + public Set getEntries(); + + /** + * Get inheritance behaviour + * @return Returns the inheritance status of this list + */ + public boolean getInherits(); + + /** + * Set inheritance behaviour + * @param inherits true to set the permissions to inherit + */ + public void setInherits(boolean inherits); + + public int deleteEntriesForAuthority(String authorityKey); + + public int deleteEntriesForPermission(DbPermissionKey permissionKey); + + public int deleteEntry(String authorityKey, DbPermissionKey permissionKey); + + /** + * Delete the entries related to this access control list + * + * @return Returns the number of entries deleted + */ + public int deleteEntries(); + + public DbAccessControlEntry getEntry(String authorityKey, DbPermissionKey permissionKey); + + /** + * Factory method to create an entry and wire it up. + * Note that the returned value may still be transient. Saving it should be fine, but + * is not required. + * + * @param permission the mandatory permission association with this entry + * @param authority the mandatory authority. Must not be transient. + * @param allowed allowed or disallowed. Must not be transient. + * @return Returns the new entry + */ + public DbAccessControlEntryImpl newEntry(DbPermission permission, DbAuthority authority, boolean allowed); + + /** + * Make a copy of this ACL (persistently) + * @return The copy. + */ + public DbAccessControlList getCopy(); +} diff --git a/source/java/org/alfresco/repo/domain/hibernate/ChildAssocImpl.java b/source/java/org/alfresco/repo/domain/hibernate/ChildAssocImpl.java index 46dafacc2d..8df8f271a2 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/ChildAssocImpl.java +++ b/source/java/org/alfresco/repo/domain/hibernate/ChildAssocImpl.java @@ -148,24 +148,12 @@ public class ChildAssocImpl implements ChildAssoc, Serializable return false; } ChildAssoc that = (ChildAssoc) obj; - if (EqualsHelper.nullSafeEquals(id, that.getId())) - { - return true; - } - else - { - return ( - EqualsHelper.nullSafeEquals(this.getChild().getId(), that.getChild().getId()) - && EqualsHelper.nullSafeEquals(this.getQname(), that.getQname()) - && EqualsHelper.nullSafeEquals(this.getParent().getId(), that.getParent().getId()) - && EqualsHelper.nullSafeEquals(this.getTypeQName(), that.getTypeQName()) - ); - } + return EqualsHelper.nullSafeEquals(id, that.getId()); } public int hashCode() { - return (qName == null ? 0 : qName.hashCode()); + return (id == null ? 0 : id.hashCode()); } /** diff --git a/source/java/org/alfresco/repo/domain/hibernate/DbAccessControlListImpl.java b/source/java/org/alfresco/repo/domain/hibernate/DbAccessControlListImpl.java index 7139d6a8bc..6576a88117 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/DbAccessControlListImpl.java +++ b/source/java/org/alfresco/repo/domain/hibernate/DbAccessControlListImpl.java @@ -1,250 +1,250 @@ -/* - * 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.domain.hibernate; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.alfresco.repo.domain.DbAccessControlEntry; -import org.alfresco.repo.domain.DbAccessControlList; -import org.alfresco.repo.domain.DbAuthority; -import org.alfresco.repo.domain.DbPermission; -import org.alfresco.repo.domain.DbPermissionKey; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.hibernate.Session; - -/** - * The hibernate persisted class for node permission entries. - * - * @author andyh - */ -public class DbAccessControlListImpl extends LifecycleAdapter - implements DbAccessControlList, Serializable -{ - private static final long serialVersionUID = 3123277428227075648L; - - private static Log logger = LogFactory.getLog(DbAccessControlListImpl.class); - - private long id; - private Set entries; - private boolean inherits; - - public DbAccessControlListImpl() - { - entries = new HashSet(5); - } - - @Override - public String toString() - { - StringBuilder sb = new StringBuilder(128); - sb.append("DbAccessControlListImpl") - .append("[ id=").append(id) - .append(", entries=").append(entries.size()) - .append(", inherits=").append(inherits) - .append("]"); - return sb.toString(); - } - - @Override - public boolean equals(Object o) - { - if (this == o) - { - return true; - } - if (!(o instanceof DbAccessControlList)) - { - return false; - } - DbAccessControlList other = (DbAccessControlList) o; - - return (this.inherits == other.getInherits()); - } - - @Override - public int hashCode() - { - return (inherits == false ? 0 : 17); - } - - public long getId() - { - return id; - } - - /** - * Hibernate use - */ - @SuppressWarnings("unused") - private void setId(long id) - { - this.id = id; - } - - public Set getEntries() - { - return entries; - } - - /** - * For Hibernate use - */ - @SuppressWarnings("unused") - private void setEntries(Set entries) - { - this.entries = entries; - } - - public boolean getInherits() - { - return inherits; - } - - public void setInherits(boolean inherits) - { - this.inherits = inherits; - } - - /** - * @see #deleteEntry(String, DbPermissionKey) - */ - public int deleteEntriesForAuthority(String authority) - { - return deleteEntry(authority, null); - } - - /** - * @see #deleteEntry(String, DbPermissionKey) - */ - public int deleteEntriesForPermission(DbPermissionKey permissionKey) - { - return deleteEntry(null, permissionKey); - } - - public int deleteEntry(String authority, DbPermissionKey permissionKey) - { - List toDelete = new ArrayList(2); - for (DbAccessControlEntry entry : entries) - { - if (authority != null && !authority.equals(entry.getAuthority().getRecipient())) - { - // authority is not a match - continue; - } - else if (permissionKey != null && !permissionKey.equals(entry.getPermission().getKey())) - { - // permission is not a match - continue; - } - toDelete.add(entry); - } - // delete them - for (DbAccessControlEntry entry : toDelete) - { - // remove from the entry list - entry.delete(); - } - // done - if (logger.isDebugEnabled()) - { - logger.debug("Deleted " + toDelete.size() + " access entries: \n" + - " access control list: " + id + "\n" + - " authority: " + authority + "\n" + - " permission: " + permissionKey); - } - return toDelete.size(); - } - - public int deleteEntries() - { - /* - * We don't do the full delete-remove-from-set thing here. Just delete each child entity - * and then clear the entry set. - */ - - Session session = getSession(); - List toDelete = new ArrayList(entries); - // delete each entry - for (DbAccessControlEntry entry : toDelete) - { - session.delete(entry); - } - // clear the list - int count = entries.size(); - entries.clear(); - // done - if (logger.isDebugEnabled()) - { - logger.debug("Deleted " + count + " access entries for access control list " + this.id); - } - return count; - } - - public DbAccessControlEntry getEntry(String authority, DbPermissionKey permissionKey) - { - for (DbAccessControlEntry entry : entries) - { - DbAuthority authorityEntity = entry.getAuthority(); - DbPermission permissionEntity = entry.getPermission(); - // check for a match - if (authorityEntity.getRecipient().equals(authority) - && permissionEntity.getKey().equals(permissionKey)) - { - // found it - return entry; - } - } - return null; - } - - public DbAccessControlEntryImpl newEntry(DbPermission permission, DbAuthority authority, boolean allowed) - { - DbAccessControlEntryImpl accessControlEntry = new DbAccessControlEntryImpl(); - // fill - accessControlEntry.setAccessControlList(this); - accessControlEntry.setPermission(permission); - accessControlEntry.setAuthority(authority); - accessControlEntry.setAllowed(allowed); - // save it - getSession().save(accessControlEntry); - // maintain inverse set on the acl - getEntries().add(accessControlEntry); - // done - return accessControlEntry; - } - - /** - * Make a copy of this ACL. - * @return The copy. - */ - public DbAccessControlList getCopy() - { - DbAccessControlList newAcl = - new DbAccessControlListImpl(); - getSession().save(newAcl); - for (DbAccessControlEntry entry : entries) - { - newAcl.newEntry(entry.getPermission(), entry.getAuthority(), entry.isAllowed()); - } - return newAcl; - } -} +/* + * 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.domain.hibernate; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.alfresco.repo.domain.DbAccessControlEntry; +import org.alfresco.repo.domain.DbAccessControlList; +import org.alfresco.repo.domain.DbAuthority; +import org.alfresco.repo.domain.DbPermission; +import org.alfresco.repo.domain.DbPermissionKey; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hibernate.Session; + +/** + * The hibernate persisted class for node permission entries. + * + * @author andyh + */ +public class DbAccessControlListImpl extends LifecycleAdapter + implements DbAccessControlList, Serializable +{ + private static final long serialVersionUID = 3123277428227075648L; + + private static Log logger = LogFactory.getLog(DbAccessControlListImpl.class); + + private long id; + private Set entries; + private boolean inherits; + + public DbAccessControlListImpl() + { + entries = new HashSet(5); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(128); + sb.append("DbAccessControlListImpl") + .append("[ id=").append(id) + .append(", entries=").append(entries.size()) + .append(", inherits=").append(inherits) + .append("]"); + return sb.toString(); + } + + @Override + public boolean equals(Object o) + { + if (this == o) + { + return true; + } + if (!(o instanceof DbAccessControlList)) + { + return false; + } + DbAccessControlList other = (DbAccessControlList) o; + + return (this.inherits == other.getInherits()); + } + + @Override + public int hashCode() + { + return (inherits == false ? 0 : 17); + } + + public long getId() + { + return id; + } + + /** + * Hibernate use + */ + @SuppressWarnings("unused") + private void setId(long id) + { + this.id = id; + } + + public Set getEntries() + { + return entries; + } + + /** + * For Hibernate use + */ + @SuppressWarnings("unused") + private void setEntries(Set entries) + { + this.entries = entries; + } + + public boolean getInherits() + { + return inherits; + } + + public void setInherits(boolean inherits) + { + this.inherits = inherits; + } + + /** + * @see #deleteEntry(String, DbPermissionKey) + */ + public int deleteEntriesForAuthority(String authority) + { + return deleteEntry(authority, null); + } + + /** + * @see #deleteEntry(String, DbPermissionKey) + */ + public int deleteEntriesForPermission(DbPermissionKey permissionKey) + { + return deleteEntry(null, permissionKey); + } + + public int deleteEntry(String authority, DbPermissionKey permissionKey) + { + List toDelete = new ArrayList(2); + for (DbAccessControlEntry entry : entries) + { + if (authority != null && !authority.equals(entry.getAuthority().getRecipient())) + { + // authority is not a match + continue; + } + else if (permissionKey != null && !permissionKey.equals(entry.getPermission().getKey())) + { + // permission is not a match + continue; + } + toDelete.add(entry); + } + // delete them + for (DbAccessControlEntry entry : toDelete) + { + // remove from the entry list + entry.delete(); + } + // done + if (logger.isDebugEnabled()) + { + logger.debug("Deleted " + toDelete.size() + " access entries: \n" + + " access control list: " + id + "\n" + + " authority: " + authority + "\n" + + " permission: " + permissionKey); + } + return toDelete.size(); + } + + public int deleteEntries() + { + /* + * We don't do the full delete-remove-from-set thing here. Just delete each child entity + * and then clear the entry set. + */ + + Session session = getSession(); + List toDelete = new ArrayList(entries); + // delete each entry + for (DbAccessControlEntry entry : toDelete) + { + session.delete(entry); + } + // clear the list + int count = entries.size(); + entries.clear(); + // done + if (logger.isDebugEnabled()) + { + logger.debug("Deleted " + count + " access entries for access control list " + this.id); + } + return count; + } + + public DbAccessControlEntry getEntry(String authority, DbPermissionKey permissionKey) + { + for (DbAccessControlEntry entry : entries) + { + DbAuthority authorityEntity = entry.getAuthority(); + DbPermission permissionEntity = entry.getPermission(); + // check for a match + if (authorityEntity.getRecipient().equals(authority) + && permissionEntity.getKey().equals(permissionKey)) + { + // found it + return entry; + } + } + return null; + } + + public DbAccessControlEntryImpl newEntry(DbPermission permission, DbAuthority authority, boolean allowed) + { + DbAccessControlEntryImpl accessControlEntry = new DbAccessControlEntryImpl(); + // fill + accessControlEntry.setAccessControlList(this); + accessControlEntry.setPermission(permission); + accessControlEntry.setAuthority(authority); + accessControlEntry.setAllowed(allowed); + // save it + getSession().save(accessControlEntry); + // maintain inverse set on the acl + getEntries().add(accessControlEntry); + // done + return accessControlEntry; + } + + /** + * Make a copy of this ACL. + * @return The copy. + */ + public DbAccessControlList getCopy() + { + DbAccessControlList newAcl = + new DbAccessControlListImpl(); + getSession().save(newAcl); + for (DbAccessControlEntry entry : entries) + { + newAcl.newEntry(entry.getPermission(), entry.getAuthority(), entry.isAllowed()); + } + return newAcl; + } +} diff --git a/source/java/org/alfresco/repo/domain/hibernate/HibernateNodeTest.java b/source/java/org/alfresco/repo/domain/hibernate/HibernateNodeTest.java index fc9f9535ba..1419a3c413 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/HibernateNodeTest.java +++ b/source/java/org/alfresco/repo/domain/hibernate/HibernateNodeTest.java @@ -1,456 +1,456 @@ -/* - * 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.domain.hibernate; - -import java.io.Serializable; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import javax.transaction.UserTransaction; - -import org.alfresco.model.ContentModel; -import org.alfresco.repo.domain.ChildAssoc; -import org.alfresco.repo.domain.Node; -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; -import org.alfresco.repo.domain.Transaction; -import org.alfresco.repo.transaction.AlfrescoTransactionSupport; -import org.alfresco.service.cmr.dictionary.DataTypeDefinition; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.namespace.QName; -import org.alfresco.service.transaction.TransactionService; -import org.alfresco.util.BaseSpringTest; -import org.alfresco.util.GUID; -import org.hibernate.CacheMode; -import org.hibernate.exception.ConstraintViolationException; - -/** - * Test persistence and retrieval of Hibernate-specific implementations of the - * {@link org.alfresco.repo.domain.Node} interface - * - * @author Derek Hulley - */ -@SuppressWarnings("unused") -public class HibernateNodeTest extends BaseSpringTest -{ - private static final String TEST_NAMESPACE = "http://www.alfresco.org/test/HibernateNodeTest"; - private static int i = 0; - - private Store store; - private Server server; - private Transaction transaction; - - public HibernateNodeTest() - { - } - - protected void onSetUpInTransaction() throws Exception - { - store = new StoreImpl(); - StoreKey storeKey = new StoreKey(StoreRef.PROTOCOL_WORKSPACE, - "TestWorkspace@" + System.currentTimeMillis() + " - " + System.nanoTime()); - store.setKey(storeKey); - // persist so that it is present in the hibernate cache - getSession().save(store); - - server = (Server) getSession().get(ServerImpl.class, new Long(1)); - if (server == null) - { - server = new ServerImpl(); - server.setIpAddress("" + "i_" + System.currentTimeMillis()); - getSession().save(server); - } - transaction = new TransactionImpl(); - transaction.setServer(server); - transaction.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId()); - getSession().save(transaction); - } - - protected void onTearDownInTransaction() - { - // force a flush to ensure that the database updates succeed - getSession().flush(); - getSession().clear(); - } - - public void testSetUp() throws Exception - { - assertNotNull("Workspace not initialised", store); - } - - public void testGetStore() throws Exception - { - // create a new Node - Node node = new NodeImpl(); - node.setStore(store); - node.setUuid(GUID.generate()); - node.setTypeQName(ContentModel.TYPE_CONTAINER); - - // now it should work - Serializable id = getSession().save(node); - - // throw the reference away and get the a new one for the id - node = (Node) getSession().load(NodeImpl.class, id); - assertNotNull("Node not found", node); - // check that the store has been loaded - Store loadedStore = node.getStore(); - assertNotNull("Store not present on node", loadedStore); - assertEquals("Incorrect store key", store, loadedStore); - } - - public void testNodeStatus() - { - NodeKey key = new NodeKey(store.getKey(), "AAA"); - // create the node status - NodeStatus nodeStatus = new NodeStatusImpl(); - nodeStatus.setKey(key); - nodeStatus.setTransaction(transaction); - getSession().save(nodeStatus); - - // create a new Node - Node node = new NodeImpl(); - node.setStore(store); - node.setUuid(GUID.generate()); - node.setTypeQName(ContentModel.TYPE_CONTAINER); - Serializable nodeId = getSession().save(node); - - // This should all be fine. The node does not HAVE to have a status. - flushAndClear(); - - // set the node - nodeStatus = (NodeStatus) getSession().get(NodeStatusImpl.class, key); - nodeStatus.setNode(node); - flushAndClear(); - - // is the node retrievable? - nodeStatus = (NodeStatus) getSession().get(NodeStatusImpl.class, key); - node = nodeStatus.getNode(); - assertNotNull("Node was not attached to status", node); - // change the values - transaction.setChangeTxnId("txn:456"); - // delete the node - getSession().delete(node); - - try - { - flushAndClear(); - fail("Node status may not refer to non-existent node"); - } - catch(ConstraintViolationException e) - { - // expected - } - } - - /** - * Check that properties can be persisted and retrieved - */ - public void testProperties() throws Exception - { - // create a new Node - Node node = new NodeImpl(); - node.setStore(store); - node.setUuid(GUID.generate()); - node.setTypeQName(ContentModel.TYPE_CONTAINER); - // give it a property map - Map propertyMap = new HashMap(5); - QName propertyQName = QName.createQName("{}A"); - PropertyValue propertyValue = new PropertyValue(DataTypeDefinition.TEXT, "AAA"); - propertyMap.put(propertyQName, propertyValue); - node.getProperties().putAll(propertyMap); - // persist it - Serializable id = getSession().save(node); - - // throw the reference away and get the a new one for the id - node = (Node) getSession().load(NodeImpl.class, id); - assertNotNull("Node not found", node); - // extract the Map - propertyMap = node.getProperties(); - assertNotNull("Map not persisted", propertyMap); - // ensure that the value is present - assertNotNull("Property value not present in map", QName.createQName("{}A")); - } - - /** - * Check that aspect qnames can be added and removed from a node and that they - * are persisted correctly - */ - public void testAspects() throws Exception - { - // make a real node - Node node = new NodeImpl(); - node.setStore(store); - node.setUuid(GUID.generate()); - node.setTypeQName(ContentModel.TYPE_CMOBJECT); - - // add some aspects - QName aspect1 = QName.createQName(TEST_NAMESPACE, "1"); - QName aspect2 = QName.createQName(TEST_NAMESPACE, "2"); - QName aspect3 = QName.createQName(TEST_NAMESPACE, "3"); - QName aspect4 = QName.createQName(TEST_NAMESPACE, "4"); - Set aspects = node.getAspects(); - aspects.add(aspect1); - aspects.add(aspect2); - aspects.add(aspect3); - aspects.add(aspect4); - assertFalse("Set did not eliminate duplicate aspect qname", aspects.add(aspect4)); - - // persist - Serializable id = getSession().save(node); - - // flush and clear - flushAndClear(); - - // get node and check aspects - node = (Node) getSession().get(NodeImpl.class, id); - assertNotNull("Node not persisted", node); - aspects = node.getAspects(); - assertEquals("Not all aspects persisted", 4, aspects.size()); - } - - public void testChildAssoc() throws Exception - { - // make a content node - Node contentNode = new NodeImpl(); - contentNode.setStore(store); - contentNode.setUuid(GUID.generate()); - contentNode.setTypeQName(ContentModel.TYPE_CONTENT); - Serializable contentNodeId = getSession().save(contentNode); - - // make a container node - Node containerNode = new NodeImpl(); - containerNode.setStore(store); - containerNode.setUuid(GUID.generate()); - containerNode.setTypeQName(ContentModel.TYPE_CONTAINER); - Serializable containerNodeId = getSession().save(containerNode); - // create an association to the content - ChildAssoc assoc1 = new ChildAssocImpl(); - assoc1.setIsPrimary(true); - assoc1.setTypeQName(QName.createQName(null, "type1")); - assoc1.setQname(QName.createQName(null, "number1")); - assoc1.setChildNodeName("number1"); - assoc1.setChildNodeNameCrc(1); - assoc1.buildAssociation(containerNode, contentNode); - getSession().save(assoc1); - - // make another association between the same two parent and child nodes - ChildAssoc assoc2 = new ChildAssocImpl(); - assoc2.setIsPrimary(true); - assoc2.setTypeQName(QName.createQName(null, "type2")); - assoc2.setQname(QName.createQName(null, "number2")); - assoc2.setChildNodeName("number2"); - assoc2.setChildNodeNameCrc(2); - assoc2.buildAssociation(containerNode, contentNode); - getSession().save(assoc2); - - assertFalse("Hashcode incorrent", assoc2.hashCode() == 0); - assertNotSame("Assoc equals failure", assoc1, assoc2); - - // reload the container - containerNode = (Node) getSession().get(NodeImpl.class, containerNodeId); - assertNotNull("Node not found", containerNode); - - // check that we can traverse the association from the child - Collection parentAssocs = contentNode.getParentAssocs(); - assertEquals("Expected exactly 2 parent assocs", 2, parentAssocs.size()); - parentAssocs = new HashSet(parentAssocs); - for (ChildAssoc assoc : parentAssocs) - { - // maintain inverse assoc sets - assoc.removeAssociation(); - // remove the assoc - getSession().delete(assoc); - } - - // check that the child now has zero parents - parentAssocs = contentNode.getParentAssocs(); - assertEquals("Expected exactly 0 parent assocs", 0, parentAssocs.size()); - } - - /** - * Allows tracing of L2 cache - */ - public void testCaching() throws Exception - { - // make a node - Node node = new NodeImpl(); - node.setStore(store); - node.setUuid(GUID.generate()); - node.setTypeQName(ContentModel.TYPE_CONTENT); - Serializable nodeId = getSession().save(node); - - // add some aspects to the node - Set aspects = node.getAspects(); - aspects.add(ContentModel.ASPECT_AUDITABLE); - - // add some properties - Map properties = node.getProperties(); - properties.put(ContentModel.PROP_NAME, new PropertyValue(DataTypeDefinition.TEXT, "ABC")); - - // check that the session hands back the same instance - Node checkNode = (Node) getSession().get(NodeImpl.class, nodeId); - assertNotNull(checkNode); - assertTrue("Node retrieved was not same instance", checkNode == node); - - Set checkAspects = checkNode.getAspects(); - assertTrue("Aspect set retrieved was not the same instance", checkAspects == aspects); - assertEquals("Incorrect number of aspects", 1, checkAspects.size()); - QName checkQName = (QName) checkAspects.toArray()[0]; - assertTrue("QName retrieved was not the same instance", checkQName == ContentModel.ASPECT_AUDITABLE); - - Map checkProperties = checkNode.getProperties(); - assertTrue("Propery map retrieved was not the same instance", checkProperties == properties); - assertTrue("Property not found", checkProperties.containsKey(ContentModel.PROP_NAME)); - - flushAndClear(); - // commit the transaction - setComplete(); - endTransaction(); - - TransactionService transactionService = (TransactionService) applicationContext.getBean("transactionComponent"); - UserTransaction txn = transactionService.getUserTransaction(); - try - { - txn.begin(); - - // check that the L2 cache hands back the same instance - checkNode = (Node) getSession().get(NodeImpl.class, nodeId); - assertNotNull(checkNode); - checkAspects = checkNode.getAspects(); - - txn.commit(); - } - catch (Throwable e) - { - txn.rollback(); - } - } - - /** - * Create some simple parent-child relationships and flush them. Then read them back in without - * using the L2 cache. - */ - public void testQueryJoins() throws Exception - { - getSession().setCacheMode(CacheMode.IGNORE); - - // make a container node - Node containerNode = new NodeImpl(); - containerNode.setStore(store); - containerNode.setUuid(GUID.generate()); - containerNode.setTypeQName(ContentModel.TYPE_CONTAINER); - containerNode.getProperties().put(ContentModel.PROP_AUTHOR, new PropertyValue(DataTypeDefinition.TEXT, "ABC")); - containerNode.getProperties().put(ContentModel.PROP_ARCHIVED_BY, new PropertyValue(DataTypeDefinition.TEXT, "ABC")); - containerNode.getAspects().add(ContentModel.ASPECT_AUDITABLE); - Serializable containerNodeId = getSession().save(containerNode); - NodeKey containerNodeKey = new NodeKey(containerNode.getNodeRef()); - NodeStatus containerNodeStatus = new NodeStatusImpl(); - containerNodeStatus.setKey(containerNodeKey); - containerNodeStatus.setNode(containerNode); - containerNodeStatus.setTransaction(transaction); - getSession().save(containerNodeStatus); - // make content node 1 - Node contentNode1 = new NodeImpl(); - contentNode1.setStore(store); - contentNode1.setUuid(GUID.generate()); - contentNode1.setTypeQName(ContentModel.TYPE_CONTENT); - contentNode1.getProperties().put(ContentModel.PROP_AUTHOR, new PropertyValue(DataTypeDefinition.TEXT, "ABC")); - contentNode1.getProperties().put(ContentModel.PROP_ARCHIVED_BY, new PropertyValue(DataTypeDefinition.TEXT, "ABC")); - contentNode1.getAspects().add(ContentModel.ASPECT_AUDITABLE); - Serializable contentNode1Id = getSession().save(contentNode1); - NodeKey contentNodeKey1 = new NodeKey(contentNode1.getNodeRef()); - NodeStatus contentNodeStatus1 = new NodeStatusImpl(); - contentNodeStatus1.setKey(contentNodeKey1); - contentNodeStatus1.setNode(contentNode1); - contentNodeStatus1.setTransaction(transaction); - getSession().save(contentNodeStatus1); - // make content node 2 - Node contentNode2 = new NodeImpl(); - contentNode2.setStore(store); - contentNode2.setUuid(GUID.generate()); - contentNode2.setTypeQName(ContentModel.TYPE_CONTENT); - Serializable contentNode2Id = getSession().save(contentNode2); - contentNode2.getProperties().put(ContentModel.PROP_AUTHOR, new PropertyValue(DataTypeDefinition.TEXT, "ABC")); - contentNode2.getProperties().put(ContentModel.PROP_ARCHIVED_BY, new PropertyValue(DataTypeDefinition.TEXT, "ABC")); - contentNode2.getAspects().add(ContentModel.ASPECT_AUDITABLE); - NodeKey contentNodeKey2 = new NodeKey(contentNode2.getNodeRef()); - NodeStatus contentNodeStatus2 = new NodeStatusImpl(); - contentNodeStatus2.setKey(contentNodeKey2); - contentNodeStatus2.setNode(contentNode2); - contentNodeStatus2.setTransaction(transaction); - getSession().save(contentNodeStatus2); - // create an association to content 1 - ChildAssoc assoc1 = new ChildAssocImpl(); - assoc1.setIsPrimary(true); - assoc1.setTypeQName(QName.createQName(null, "type1")); - assoc1.setQname(QName.createQName(null, "number1")); - assoc1.setChildNodeName("number1"); - assoc1.setChildNodeNameCrc(1); - assoc1.buildAssociation(containerNode, contentNode1); - getSession().save(assoc1); - // create an association to content 2 - ChildAssoc assoc2 = new ChildAssocImpl(); - assoc2.setIsPrimary(true); - assoc2.setTypeQName(QName.createQName(null, "type2")); - assoc2.setQname(QName.createQName(null, "number2")); - assoc2.setChildNodeName("number2"); - assoc2.setChildNodeNameCrc(2); - assoc2.buildAssociation(containerNode, contentNode2); - getSession().save(assoc2); - - // make sure that there are no entities cached in either L1 or L2 - getSession().flush(); - getSession().clear(); - - // now read the structure back in from the container down - containerNodeStatus = (NodeStatus) getSession().get(NodeStatusImpl.class, containerNodeKey); - containerNode = containerNodeStatus.getNode(); - - // clear out again - getSession().clear(); - - // expect that just the specific property gets removed in the delete statement - getSession().flush(); - getSession().clear(); - - // Create a second association to content 2 - // create an association to content 2 - containerNodeStatus = (NodeStatus) getSession().get(NodeStatusImpl.class, containerNodeKey); - containerNode = containerNodeStatus.getNode(); - contentNodeStatus2 = (NodeStatus) getSession().get(NodeStatusImpl.class, contentNodeKey2); - contentNode2 = contentNodeStatus2.getNode(); - ChildAssoc assoc3 = new ChildAssocImpl(); - assoc3.setIsPrimary(false); - assoc3.setTypeQName(QName.createQName(null, "type3")); - assoc3.setQname(QName.createQName(null, "number3")); - assoc3.setChildNodeName("number3"); - assoc3.setChildNodeNameCrc(2); - assoc3.buildAssociation(containerNode, contentNode2); // check whether the children are pulled in for this - getSession().save(assoc3); - - // flush it - getSession().flush(); - getSession().clear(); - } -} \ No newline at end of file +/* + * 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.domain.hibernate; + +import java.io.Serializable; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.transaction.UserTransaction; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.domain.ChildAssoc; +import org.alfresco.repo.domain.Node; +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; +import org.alfresco.repo.domain.Transaction; +import org.alfresco.repo.transaction.AlfrescoTransactionSupport; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.BaseSpringTest; +import org.alfresco.util.GUID; +import org.hibernate.CacheMode; +import org.hibernate.exception.ConstraintViolationException; + +/** + * Test persistence and retrieval of Hibernate-specific implementations of the + * {@link org.alfresco.repo.domain.Node} interface + * + * @author Derek Hulley + */ +@SuppressWarnings("unused") +public class HibernateNodeTest extends BaseSpringTest +{ + private static final String TEST_NAMESPACE = "http://www.alfresco.org/test/HibernateNodeTest"; + private static int i = 0; + + private Store store; + private Server server; + private Transaction transaction; + + public HibernateNodeTest() + { + } + + protected void onSetUpInTransaction() throws Exception + { + store = new StoreImpl(); + StoreKey storeKey = new StoreKey(StoreRef.PROTOCOL_WORKSPACE, + "TestWorkspace@" + System.currentTimeMillis() + " - " + System.nanoTime()); + store.setKey(storeKey); + // persist so that it is present in the hibernate cache + getSession().save(store); + + server = (Server) getSession().get(ServerImpl.class, new Long(1)); + if (server == null) + { + server = new ServerImpl(); + server.setIpAddress("" + "i_" + System.currentTimeMillis()); + getSession().save(server); + } + transaction = new TransactionImpl(); + transaction.setServer(server); + transaction.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId()); + getSession().save(transaction); + } + + protected void onTearDownInTransaction() + { + // force a flush to ensure that the database updates succeed + getSession().flush(); + getSession().clear(); + } + + public void testSetUp() throws Exception + { + assertNotNull("Workspace not initialised", store); + } + + public void testGetStore() throws Exception + { + // create a new Node + Node node = new NodeImpl(); + node.setStore(store); + node.setUuid(GUID.generate()); + node.setTypeQName(ContentModel.TYPE_CONTAINER); + + // now it should work + Serializable id = getSession().save(node); + + // throw the reference away and get the a new one for the id + node = (Node) getSession().load(NodeImpl.class, id); + assertNotNull("Node not found", node); + // check that the store has been loaded + Store loadedStore = node.getStore(); + assertNotNull("Store not present on node", loadedStore); + assertEquals("Incorrect store key", store, loadedStore); + } + + public void testNodeStatus() + { + NodeKey key = new NodeKey(store.getKey(), "AAA"); + // create the node status + NodeStatus nodeStatus = new NodeStatusImpl(); + nodeStatus.setKey(key); + nodeStatus.setTransaction(transaction); + getSession().save(nodeStatus); + + // create a new Node + Node node = new NodeImpl(); + node.setStore(store); + node.setUuid(GUID.generate()); + node.setTypeQName(ContentModel.TYPE_CONTAINER); + Serializable nodeId = getSession().save(node); + + // This should all be fine. The node does not HAVE to have a status. + flushAndClear(); + + // set the node + nodeStatus = (NodeStatus) getSession().get(NodeStatusImpl.class, key); + nodeStatus.setNode(node); + flushAndClear(); + + // is the node retrievable? + nodeStatus = (NodeStatus) getSession().get(NodeStatusImpl.class, key); + node = nodeStatus.getNode(); + assertNotNull("Node was not attached to status", node); + // change the values + transaction.setChangeTxnId("txn:456"); + // delete the node + getSession().delete(node); + + try + { + flushAndClear(); + fail("Node status may not refer to non-existent node"); + } + catch(ConstraintViolationException e) + { + // expected + } + } + + /** + * Check that properties can be persisted and retrieved + */ + public void testProperties() throws Exception + { + // create a new Node + Node node = new NodeImpl(); + node.setStore(store); + node.setUuid(GUID.generate()); + node.setTypeQName(ContentModel.TYPE_CONTAINER); + // give it a property map + Map propertyMap = new HashMap(5); + QName propertyQName = QName.createQName("{}A"); + PropertyValue propertyValue = new PropertyValue(DataTypeDefinition.TEXT, "AAA"); + propertyMap.put(propertyQName, propertyValue); + node.getProperties().putAll(propertyMap); + // persist it + Serializable id = getSession().save(node); + + // throw the reference away and get the a new one for the id + node = (Node) getSession().load(NodeImpl.class, id); + assertNotNull("Node not found", node); + // extract the Map + propertyMap = node.getProperties(); + assertNotNull("Map not persisted", propertyMap); + // ensure that the value is present + assertNotNull("Property value not present in map", QName.createQName("{}A")); + } + + /** + * Check that aspect qnames can be added and removed from a node and that they + * are persisted correctly + */ + public void testAspects() throws Exception + { + // make a real node + Node node = new NodeImpl(); + node.setStore(store); + node.setUuid(GUID.generate()); + node.setTypeQName(ContentModel.TYPE_CMOBJECT); + + // add some aspects + QName aspect1 = QName.createQName(TEST_NAMESPACE, "1"); + QName aspect2 = QName.createQName(TEST_NAMESPACE, "2"); + QName aspect3 = QName.createQName(TEST_NAMESPACE, "3"); + QName aspect4 = QName.createQName(TEST_NAMESPACE, "4"); + Set aspects = node.getAspects(); + aspects.add(aspect1); + aspects.add(aspect2); + aspects.add(aspect3); + aspects.add(aspect4); + assertFalse("Set did not eliminate duplicate aspect qname", aspects.add(aspect4)); + + // persist + Serializable id = getSession().save(node); + + // flush and clear + flushAndClear(); + + // get node and check aspects + node = (Node) getSession().get(NodeImpl.class, id); + assertNotNull("Node not persisted", node); + aspects = node.getAspects(); + assertEquals("Not all aspects persisted", 4, aspects.size()); + } + + public void testChildAssoc() throws Exception + { + // make a content node + Node contentNode = new NodeImpl(); + contentNode.setStore(store); + contentNode.setUuid(GUID.generate()); + contentNode.setTypeQName(ContentModel.TYPE_CONTENT); + Serializable contentNodeId = getSession().save(contentNode); + + // make a container node + Node containerNode = new NodeImpl(); + containerNode.setStore(store); + containerNode.setUuid(GUID.generate()); + containerNode.setTypeQName(ContentModel.TYPE_CONTAINER); + Serializable containerNodeId = getSession().save(containerNode); + // create an association to the content + ChildAssoc assoc1 = new ChildAssocImpl(); + assoc1.setIsPrimary(true); + assoc1.setTypeQName(QName.createQName(null, "type1")); + assoc1.setQname(QName.createQName(null, "number1")); + assoc1.setChildNodeName("number1"); + assoc1.setChildNodeNameCrc(1); + getSession().save(assoc1); + assoc1.buildAssociation(containerNode, contentNode); + + // make another association between the same two parent and child nodes + ChildAssoc assoc2 = new ChildAssocImpl(); + assoc2.setIsPrimary(true); + assoc2.setTypeQName(QName.createQName(null, "type2")); + assoc2.setQname(QName.createQName(null, "number2")); + assoc2.setChildNodeName("number2"); + assoc2.setChildNodeNameCrc(2); + getSession().save(assoc2); + assoc2.buildAssociation(containerNode, contentNode); + + assertFalse("Hashcode incorrent", assoc2.hashCode() == 0); + assertNotSame("Assoc equals failure", assoc1, assoc2); + + // reload the container + containerNode = (Node) getSession().get(NodeImpl.class, containerNodeId); + assertNotNull("Node not found", containerNode); + + // check that we can traverse the association from the child + Collection parentAssocs = contentNode.getParentAssocs(); + assertEquals("Expected exactly 2 parent assocs", 2, parentAssocs.size()); + parentAssocs = new HashSet(parentAssocs); + for (ChildAssoc assoc : parentAssocs) + { + // maintain inverse assoc sets + assoc.removeAssociation(); + // remove the assoc + getSession().delete(assoc); + } + + // check that the child now has zero parents + parentAssocs = contentNode.getParentAssocs(); + assertEquals("Expected exactly 0 parent assocs", 0, parentAssocs.size()); + } + + /** + * Allows tracing of L2 cache + */ + public void testCaching() throws Exception + { + // make a node + Node node = new NodeImpl(); + node.setStore(store); + node.setUuid(GUID.generate()); + node.setTypeQName(ContentModel.TYPE_CONTENT); + Serializable nodeId = getSession().save(node); + + // add some aspects to the node + Set aspects = node.getAspects(); + aspects.add(ContentModel.ASPECT_AUDITABLE); + + // add some properties + Map properties = node.getProperties(); + properties.put(ContentModel.PROP_NAME, new PropertyValue(DataTypeDefinition.TEXT, "ABC")); + + // check that the session hands back the same instance + Node checkNode = (Node) getSession().get(NodeImpl.class, nodeId); + assertNotNull(checkNode); + assertTrue("Node retrieved was not same instance", checkNode == node); + + Set checkAspects = checkNode.getAspects(); + assertTrue("Aspect set retrieved was not the same instance", checkAspects == aspects); + assertEquals("Incorrect number of aspects", 1, checkAspects.size()); + QName checkQName = (QName) checkAspects.toArray()[0]; + assertTrue("QName retrieved was not the same instance", checkQName == ContentModel.ASPECT_AUDITABLE); + + Map checkProperties = checkNode.getProperties(); + assertTrue("Propery map retrieved was not the same instance", checkProperties == properties); + assertTrue("Property not found", checkProperties.containsKey(ContentModel.PROP_NAME)); + + flushAndClear(); + // commit the transaction + setComplete(); + endTransaction(); + + TransactionService transactionService = (TransactionService) applicationContext.getBean("transactionComponent"); + UserTransaction txn = transactionService.getUserTransaction(); + try + { + txn.begin(); + + // check that the L2 cache hands back the same instance + checkNode = (Node) getSession().get(NodeImpl.class, nodeId); + assertNotNull(checkNode); + checkAspects = checkNode.getAspects(); + + txn.commit(); + } + catch (Throwable e) + { + txn.rollback(); + } + } + + /** + * Create some simple parent-child relationships and flush them. Then read them back in without + * using the L2 cache. + */ + public void testQueryJoins() throws Exception + { + getSession().setCacheMode(CacheMode.IGNORE); + + // make a container node + Node containerNode = new NodeImpl(); + containerNode.setStore(store); + containerNode.setUuid(GUID.generate()); + containerNode.setTypeQName(ContentModel.TYPE_CONTAINER); + containerNode.getProperties().put(ContentModel.PROP_AUTHOR, new PropertyValue(DataTypeDefinition.TEXT, "ABC")); + containerNode.getProperties().put(ContentModel.PROP_ARCHIVED_BY, new PropertyValue(DataTypeDefinition.TEXT, "ABC")); + containerNode.getAspects().add(ContentModel.ASPECT_AUDITABLE); + Serializable containerNodeId = getSession().save(containerNode); + NodeKey containerNodeKey = new NodeKey(containerNode.getNodeRef()); + NodeStatus containerNodeStatus = new NodeStatusImpl(); + containerNodeStatus.setKey(containerNodeKey); + containerNodeStatus.setNode(containerNode); + containerNodeStatus.setTransaction(transaction); + getSession().save(containerNodeStatus); + // make content node 1 + Node contentNode1 = new NodeImpl(); + contentNode1.setStore(store); + contentNode1.setUuid(GUID.generate()); + contentNode1.setTypeQName(ContentModel.TYPE_CONTENT); + contentNode1.getProperties().put(ContentModel.PROP_AUTHOR, new PropertyValue(DataTypeDefinition.TEXT, "ABC")); + contentNode1.getProperties().put(ContentModel.PROP_ARCHIVED_BY, new PropertyValue(DataTypeDefinition.TEXT, "ABC")); + contentNode1.getAspects().add(ContentModel.ASPECT_AUDITABLE); + Serializable contentNode1Id = getSession().save(contentNode1); + NodeKey contentNodeKey1 = new NodeKey(contentNode1.getNodeRef()); + NodeStatus contentNodeStatus1 = new NodeStatusImpl(); + contentNodeStatus1.setKey(contentNodeKey1); + contentNodeStatus1.setNode(contentNode1); + contentNodeStatus1.setTransaction(transaction); + getSession().save(contentNodeStatus1); + // make content node 2 + Node contentNode2 = new NodeImpl(); + contentNode2.setStore(store); + contentNode2.setUuid(GUID.generate()); + contentNode2.setTypeQName(ContentModel.TYPE_CONTENT); + Serializable contentNode2Id = getSession().save(contentNode2); + contentNode2.getProperties().put(ContentModel.PROP_AUTHOR, new PropertyValue(DataTypeDefinition.TEXT, "ABC")); + contentNode2.getProperties().put(ContentModel.PROP_ARCHIVED_BY, new PropertyValue(DataTypeDefinition.TEXT, "ABC")); + contentNode2.getAspects().add(ContentModel.ASPECT_AUDITABLE); + NodeKey contentNodeKey2 = new NodeKey(contentNode2.getNodeRef()); + NodeStatus contentNodeStatus2 = new NodeStatusImpl(); + contentNodeStatus2.setKey(contentNodeKey2); + contentNodeStatus2.setNode(contentNode2); + contentNodeStatus2.setTransaction(transaction); + getSession().save(contentNodeStatus2); + // create an association to content 1 + ChildAssoc assoc1 = new ChildAssocImpl(); + assoc1.setIsPrimary(true); + assoc1.setTypeQName(QName.createQName(null, "type1")); + assoc1.setQname(QName.createQName(null, "number1")); + assoc1.setChildNodeName("number1"); + assoc1.setChildNodeNameCrc(1); + assoc1.buildAssociation(containerNode, contentNode1); + getSession().save(assoc1); + // create an association to content 2 + ChildAssoc assoc2 = new ChildAssocImpl(); + assoc2.setIsPrimary(true); + assoc2.setTypeQName(QName.createQName(null, "type2")); + assoc2.setQname(QName.createQName(null, "number2")); + assoc2.setChildNodeName("number2"); + assoc2.setChildNodeNameCrc(2); + assoc2.buildAssociation(containerNode, contentNode2); + getSession().save(assoc2); + + // make sure that there are no entities cached in either L1 or L2 + getSession().flush(); + getSession().clear(); + + // now read the structure back in from the container down + containerNodeStatus = (NodeStatus) getSession().get(NodeStatusImpl.class, containerNodeKey); + containerNode = containerNodeStatus.getNode(); + + // clear out again + getSession().clear(); + + // expect that just the specific property gets removed in the delete statement + getSession().flush(); + getSession().clear(); + + // Create a second association to content 2 + // create an association to content 2 + containerNodeStatus = (NodeStatus) getSession().get(NodeStatusImpl.class, containerNodeKey); + containerNode = containerNodeStatus.getNode(); + contentNodeStatus2 = (NodeStatus) getSession().get(NodeStatusImpl.class, contentNodeKey2); + contentNode2 = contentNodeStatus2.getNode(); + ChildAssoc assoc3 = new ChildAssocImpl(); + assoc3.setIsPrimary(false); + assoc3.setTypeQName(QName.createQName(null, "type3")); + assoc3.setQname(QName.createQName(null, "number3")); + assoc3.setChildNodeName("number3"); + assoc3.setChildNodeNameCrc(2); + assoc3.buildAssociation(containerNode, contentNode2); // check whether the children are pulled in for this + getSession().save(assoc3); + + // flush it + getSession().flush(); + getSession().clear(); + } +} diff --git a/source/java/org/alfresco/repo/domain/hibernate/PermissionsDaoComponentImpl.java b/source/java/org/alfresco/repo/domain/hibernate/PermissionsDaoComponentImpl.java index 8e2a68ccfb..3879739aff 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/PermissionsDaoComponentImpl.java +++ b/source/java/org/alfresco/repo/domain/hibernate/PermissionsDaoComponentImpl.java @@ -1,600 +1,600 @@ -/* - * 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.domain.hibernate; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import org.alfresco.repo.domain.AccessControlListDAO; -import org.alfresco.repo.domain.DbAccessControlEntry; -import org.alfresco.repo.domain.DbAccessControlList; -import org.alfresco.repo.domain.DbAuthority; -import org.alfresco.repo.domain.DbPermission; -import org.alfresco.repo.domain.DbPermissionKey; -import org.alfresco.repo.security.permissions.NodePermissionEntry; -import org.alfresco.repo.security.permissions.PermissionEntry; -import org.alfresco.repo.security.permissions.PermissionReference; -import org.alfresco.repo.security.permissions.impl.PermissionsDaoComponent; -import org.alfresco.repo.security.permissions.impl.SimpleNodePermissionEntry; -import org.alfresco.repo.security.permissions.impl.SimplePermissionEntry; -import org.alfresco.repo.security.permissions.impl.SimplePermissionReference; -import org.alfresco.repo.transaction.TransactionalDao; -import org.alfresco.service.cmr.repository.InvalidNodeRefException; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.security.AccessStatus; -import org.alfresco.service.namespace.QName; -import org.alfresco.util.GUID; -import org.hibernate.Query; -import org.hibernate.Session; -import org.springframework.orm.hibernate3.HibernateCallback; -import org.springframework.orm.hibernate3.support.HibernateDaoSupport; - -/** - * Support for accessing persisted permission information. - * - * This class maps between persisted objects and the external API defined in the - * PermissionsDAO interface. - * - * @author andyh - */ -public class PermissionsDaoComponentImpl extends HibernateDaoSupport implements PermissionsDaoComponent, TransactionalDao -{ - private static final boolean INHERIT_PERMISSIONS_DEFAULT = true; - public static final String QUERY_GET_PERMISSION = "permission.GetPermission"; - public static final String QUERY_GET_AC_ENTRIES_FOR_AUTHORITY = "permission.GetAccessControlEntriesForAuthority"; - public static final String QUERY_GET_AC_ENTRIES_FOR_PERMISSION = "permission.GetAccessControlEntriesForPermission"; - - private Map fProtocolToACLDAO; - - private AccessControlListDAO fDefaultACLDAO; - - /** a uuid identifying this unique instance */ - private String uuid; - - /** - * - */ - public PermissionsDaoComponentImpl() - { - this.uuid = GUID.generate(); - } - - /** - * Checks equality by type and uuid - */ - public boolean equals(Object obj) - { - if (obj == null) - { - return false; - } - else if (!(obj instanceof PermissionsDaoComponentImpl)) - { - return false; - } - PermissionsDaoComponentImpl that = (PermissionsDaoComponentImpl) obj; - return this.uuid.equals(that.uuid); - } - - /** - * @see #uuid - */ - public int hashCode() - { - return uuid.hashCode(); - } - - /** - * Does this Session contain any changes which must be - * synchronized with the store? - * - * @return true => changes are pending - */ - public boolean isDirty() - { - // create a callback for the task - HibernateCallback callback = new HibernateCallback() - { - public Object doInHibernate(Session session) - { - return session.isDirty(); - } - }; - // execute the callback - return ((Boolean)getHibernateTemplate().execute(callback)).booleanValue(); - } - - /** - * Just flushes the session - */ - public void flush() - { - getSession().flush(); - } - - public void setProtocolToACLDAO(Map map) - { - fProtocolToACLDAO = map; - } - - public void setDefaultACLDAO(AccessControlListDAO defaultACLDAO) - { - fDefaultACLDAO = defaultACLDAO; - } - - public NodePermissionEntry getPermissions(NodeRef nodeRef) - { - // Create the object if it is not found. - // Null objects are not cached in hibernate - // If the object does not exist it will repeatedly query to check its - // non existence. - NodePermissionEntry npe = null; - DbAccessControlList acl = null; - try - { - acl = getAccessControlList(nodeRef, false); - } - catch (InvalidNodeRefException e) - { - // Do nothing. - } - if (acl == null) - { - // there isn't an access control list for the node - spoof a null one - SimpleNodePermissionEntry snpe = new SimpleNodePermissionEntry( - nodeRef, - true, - Collections. emptySet()); - npe = snpe; - } - else - { - npe = createSimpleNodePermissionEntry(nodeRef); - } - // done - if (logger.isDebugEnabled()) - { - logger.debug( - "Created access control list for node: \n" + - " node: " + nodeRef + "\n" + - " acl: " + npe); - } - return npe; - } - - /** - * Get the persisted access control list or create it if required. - * - * @param nodeRef - the node for which to create the list - * @param create - create the object if it is missing - * @return Returns the current access control list or null if not found - */ - private DbAccessControlList getAccessControlList(NodeRef nodeRef, boolean create) - { - DbAccessControlList acl = - getACLDAO(nodeRef).getAccessControlList(nodeRef); - if (acl == null && create) - { - acl = createAccessControlList(nodeRef); - } - // done - if (logger.isDebugEnabled()) - { - logger.debug("Retrieved access control list: \n" + - " node: " + nodeRef + "\n" + - " list: " + acl); - } - return acl; - } - - /** - * Creates an access control list for the node and removes the entry from - * the nullPermsionCache. - */ - private DbAccessControlList createAccessControlList(NodeRef nodeRef) - { - DbAccessControlList acl = new DbAccessControlListImpl(); - acl.setInherits(INHERIT_PERMISSIONS_DEFAULT); - getHibernateTemplate().save(acl); - - // maintain inverse - getACLDAO(nodeRef).setAccessControlList(nodeRef, acl); - - // done - if (logger.isDebugEnabled()) - { - logger.debug("Created Access Control List: \n" + - " node: " + nodeRef + "\n" + - " list: " + acl); - } - return acl; - } - - public void deletePermissions(NodeRef nodeRef) - { - DbAccessControlList acl = null; - try - { - acl = getAccessControlList(nodeRef, false); - } - catch (InvalidNodeRefException e) - { - return; - } - if (acl != null) - { - // maintain referencial integrity - getACLDAO(nodeRef).setAccessControlList(nodeRef, null); - // delete the access control list - it will cascade to the entries - getHibernateTemplate().delete(acl); - } - } - - @SuppressWarnings("unchecked") - public void deletePermissions(final String authority) - { - // get the authority - HibernateCallback callback = new HibernateCallback() - { - public Object doInHibernate(Session session) - { - Query query = session - .getNamedQuery(QUERY_GET_AC_ENTRIES_FOR_AUTHORITY) - .setString("authorityRecipient", authority); - return (Integer) HibernateHelper.deleteDbAccessControlEntries(session, query); - } - }; - Integer deletedCount = (Integer) getHibernateTemplate().execute(callback); - // done - if (logger.isDebugEnabled()) - { - logger.debug("Deleted " + deletedCount + " entries for authority " + authority); - } - } - - public void deletePermissions(final NodeRef nodeRef, final String authority) - { - DbAccessControlList acl = null; - try - { - acl = getACLDAO(nodeRef).getAccessControlList(nodeRef); - } - catch (InvalidNodeRefException e) - { - return; - } - int deletedCount = 0; - if (acl != null) - { - deletedCount = acl.deleteEntriesForAuthority(authority); - } - // done - if (logger.isDebugEnabled()) - { - logger.debug("Deleted " + deletedCount + "entries for criteria: \n" + - " node: " + nodeRef + "\n" + - " authority: " + authority); - } - } - - /** - * Deletes all permission entries (access control list entries) that match - * the given criteria. Note that the access control list for the node is - * not deleted. - */ - public void deletePermission(NodeRef nodeRef, String authority, PermissionReference permission) - { - DbAccessControlList acl = null; - try - { - acl = getACLDAO(nodeRef).getAccessControlList(nodeRef); - } - catch (InvalidNodeRefException e) - { - return; - } - int deletedCount = 0; - if (acl != null) - { - DbPermissionKey permissionKey = new DbPermissionKey(permission.getQName(), permission.getName()); - deletedCount = acl.deleteEntry(authority, permissionKey); - } - // done - if (logger.isDebugEnabled()) - { - logger.debug("Deleted " + deletedCount + "entries for criteria: \n" + - " node: " + nodeRef + "\n" + - " permission: " + permission + "\n" + - " authority: " + authority); - } - } - - public void setPermission(NodeRef nodeRef, String authority, PermissionReference permission, boolean allow) - { - // get the entry - DbAccessControlEntry entry = getAccessControlEntry(nodeRef, authority, permission); - if (entry == null) - { - // need to create it - DbAccessControlList dbAccessControlList = getAccessControlList(nodeRef, true); - DbPermission dbPermission = getPermission(permission, true); - DbAuthority dbAuthority = getAuthority(authority, true); - // set persistent objects - entry = dbAccessControlList.newEntry(dbPermission, dbAuthority, allow); - // done - if (logger.isDebugEnabled()) - { - logger.debug("Created new access control entry: " + entry); - } - } - else - { - entry.setAllowed(allow); - // done - if (logger.isDebugEnabled()) - { - logger.debug("Updated access control entry: " + entry); - } - } - } - - /** - * @param nodeRef the node against which to join - * @param authority the authority against which to join - * @param perm the permission against which to join - * @return Returns all access control entries that match the criteria - */ - private DbAccessControlEntry getAccessControlEntry( - NodeRef nodeRef, - String authority, - PermissionReference permission) - { - DbAccessControlList acl = getAccessControlList(nodeRef, false); - DbAccessControlEntry entry = null; - if (acl != null) - { - DbPermissionKey permissionKey = new DbPermissionKey(permission.getQName(), permission.getName()); - entry = acl.getEntry(authority, permissionKey); - } - // done - if (logger.isDebugEnabled()) - { - logger.debug("" + (entry == null ? "Did not find" : "Found") + " entry for criteria: \n" + - " node: " + nodeRef + "\n" + - " authority: " + authority + "\n" + - " permission: " + permission); - } - return entry; - } - - /** - * Utility method to find or create a persisted authority - */ - private DbAuthority getAuthority(String authority, boolean create) - { - DbAuthority entity = (DbAuthority) getHibernateTemplate().get(DbAuthorityImpl.class, authority); - if ((entity == null) && create) - { - entity = new DbAuthorityImpl(); - entity.setRecipient(authority); - getHibernateTemplate().save(entity); - return entity; - } - else - { - return entity; - } - } - - /** - * Utility method to find and optionally create a persisted permission. - */ - private DbPermission getPermission(PermissionReference permissionRef, final boolean create) - { - final QName qname = permissionRef.getQName(); - final String name = permissionRef.getName(); - Session session = getSession(); - - DbPermission dbPermission = DbPermissionImpl.find(session, qname, name); - - // create if necessary - if ((dbPermission == null) && create) - { - dbPermission = new DbPermissionImpl(); - dbPermission.setTypeQname(qname); - dbPermission.setName(name); - getHibernateTemplate().save(dbPermission); - } - return dbPermission; - } - - public void setPermission(PermissionEntry permissionEntry) - { - setPermission( - permissionEntry.getNodeRef(), - permissionEntry.getAuthority(), - permissionEntry.getPermissionReference(), - permissionEntry.isAllowed()); - } - - public void setPermission(NodePermissionEntry nodePermissionEntry) - { - NodeRef nodeRef = nodePermissionEntry.getNodeRef(); - - // Get the access control list - // Note the logic here requires to know whether it was created or not - DbAccessControlList acl = getAccessControlList(nodeRef, false); - if (acl != null) - { - // maintain referencial integrity - getACLDAO(nodeRef).setAccessControlList(nodeRef, null); - // drop the list - getHibernateTemplate().delete(acl); - } - // create the access control list - acl = createAccessControlList(nodeRef); - - // set attributes - acl.setInherits(nodePermissionEntry.inheritPermissions()); - - // add all entries - for (PermissionEntry pe : nodePermissionEntry.getPermissionEntries()) - { - PermissionReference permission = pe.getPermissionReference(); - String authority = pe.getAuthority(); - boolean isAllowed = pe.isAllowed(); - - DbPermission permissionEntity = getPermission(permission, true); - DbAuthority authorityEntity = getAuthority(authority, true); - - @SuppressWarnings("unused") - DbAccessControlEntryImpl entry = acl.newEntry(permissionEntity, authorityEntity, isAllowed); - } - } - - public void setInheritParentPermissions(NodeRef nodeRef, boolean inheritParentPermissions) - { - DbAccessControlList acl = null; - if (!inheritParentPermissions) - { - // Inheritance == true is the default, so only force a create of the ACL if the value false - acl = getAccessControlList(nodeRef, true); - acl.setInherits(false); - } - else - { - acl = getAccessControlList(nodeRef, false); - if (acl != null) - { - acl.setInherits(true); - } - } - } - - public boolean getInheritParentPermissions(NodeRef nodeRef) - { - DbAccessControlList acl = null; - try - { - acl = getAccessControlList(nodeRef, false); - } - catch (InvalidNodeRefException e) - { - return INHERIT_PERMISSIONS_DEFAULT; - } - if (acl == null) - { - return true; - } - else - { - return acl.getInherits(); - } - } - - // Utility methods to create simple detached objects for the outside world - // We do not pass out the hibernate objects - - private SimpleNodePermissionEntry createSimpleNodePermissionEntry(NodeRef nodeRef) - { - DbAccessControlList acl = - getACLDAO(nodeRef).getAccessControlList(nodeRef); - if (acl == null) - { - // there isn't an access control list for the node - spoof a null one - SimpleNodePermissionEntry snpe = new SimpleNodePermissionEntry( - nodeRef, - true, - Collections. emptySet()); - return snpe; - } - else - { - Set entries = acl.getEntries(); - SimpleNodePermissionEntry snpe = new SimpleNodePermissionEntry( - nodeRef, - acl.getInherits(), - createSimplePermissionEntries(nodeRef, entries)); - return snpe; - } - } - - /** - * @param entries access control entries - * @return Returns a unique set of entries that can be given back to the outside world - */ - private Set createSimplePermissionEntries(NodeRef nodeRef, - Collection entries) - { - if (entries == null) - { - return null; - } - HashSet spes = new HashSet(entries.size(), 1.0f); - if (entries.size() != 0) - { - for (DbAccessControlEntry entry : entries) - { - spes.add(createSimplePermissionEntry(nodeRef, entry)); - } - } - return spes; - } - - private static SimplePermissionEntry createSimplePermissionEntry(NodeRef nodeRef, - DbAccessControlEntry ace) - { - if (ace == null) - { - return null; - } - return new SimplePermissionEntry( - nodeRef, - createSimplePermissionReference(ace.getPermission()), - ace.getAuthority().getRecipient(), - ace.isAllowed() ? AccessStatus.ALLOWED : AccessStatus.DENIED); - } - - private static SimplePermissionReference createSimplePermissionReference(DbPermission perm) - { - if (perm == null) - { - return null; - } - return new SimplePermissionReference( - perm.getTypeQname(), - perm.getName()); - } - - /** - * Helper to choose appropriate NodeService for the given NodeRef - * @param nodeRef The NodeRef to dispatch from. - * @return The appropriate NodeService. - */ - private AccessControlListDAO getACLDAO(NodeRef nodeRef) - { - AccessControlListDAO ret = fProtocolToACLDAO.get(nodeRef.getStoreRef().getProtocol()); - if (ret == null) - { - return fDefaultACLDAO; - } - return ret; - } -} +/* + * 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.domain.hibernate; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.AccessControlListDAO; +import org.alfresco.repo.domain.DbAccessControlEntry; +import org.alfresco.repo.domain.DbAccessControlList; +import org.alfresco.repo.domain.DbAuthority; +import org.alfresco.repo.domain.DbPermission; +import org.alfresco.repo.domain.DbPermissionKey; +import org.alfresco.repo.security.permissions.NodePermissionEntry; +import org.alfresco.repo.security.permissions.PermissionEntry; +import org.alfresco.repo.security.permissions.PermissionReference; +import org.alfresco.repo.security.permissions.impl.PermissionsDaoComponent; +import org.alfresco.repo.security.permissions.impl.SimpleNodePermissionEntry; +import org.alfresco.repo.security.permissions.impl.SimplePermissionEntry; +import org.alfresco.repo.security.permissions.impl.SimplePermissionReference; +import org.alfresco.repo.transaction.TransactionalDao; +import org.alfresco.service.cmr.repository.InvalidNodeRefException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.GUID; +import org.hibernate.Query; +import org.hibernate.Session; +import org.springframework.orm.hibernate3.HibernateCallback; +import org.springframework.orm.hibernate3.support.HibernateDaoSupport; + +/** + * Support for accessing persisted permission information. + * + * This class maps between persisted objects and the external API defined in the + * PermissionsDAO interface. + * + * @author andyh + */ +public class PermissionsDaoComponentImpl extends HibernateDaoSupport implements PermissionsDaoComponent, TransactionalDao +{ + private static final boolean INHERIT_PERMISSIONS_DEFAULT = true; + public static final String QUERY_GET_PERMISSION = "permission.GetPermission"; + public static final String QUERY_GET_AC_ENTRIES_FOR_AUTHORITY = "permission.GetAccessControlEntriesForAuthority"; + public static final String QUERY_GET_AC_ENTRIES_FOR_PERMISSION = "permission.GetAccessControlEntriesForPermission"; + + private Map fProtocolToACLDAO; + + private AccessControlListDAO fDefaultACLDAO; + + /** a uuid identifying this unique instance */ + private String uuid; + + /** + * + */ + public PermissionsDaoComponentImpl() + { + this.uuid = GUID.generate(); + } + + /** + * Checks equality by type and uuid + */ + public boolean equals(Object obj) + { + if (obj == null) + { + return false; + } + else if (!(obj instanceof PermissionsDaoComponentImpl)) + { + return false; + } + PermissionsDaoComponentImpl that = (PermissionsDaoComponentImpl) obj; + return this.uuid.equals(that.uuid); + } + + /** + * @see #uuid + */ + public int hashCode() + { + return uuid.hashCode(); + } + + /** + * Does this Session contain any changes which must be + * synchronized with the store? + * + * @return true => changes are pending + */ + public boolean isDirty() + { + // create a callback for the task + HibernateCallback callback = new HibernateCallback() + { + public Object doInHibernate(Session session) + { + return session.isDirty(); + } + }; + // execute the callback + return ((Boolean)getHibernateTemplate().execute(callback)).booleanValue(); + } + + /** + * Just flushes the session + */ + public void flush() + { + getSession().flush(); + } + + public void setProtocolToACLDAO(Map map) + { + fProtocolToACLDAO = map; + } + + public void setDefaultACLDAO(AccessControlListDAO defaultACLDAO) + { + fDefaultACLDAO = defaultACLDAO; + } + + public NodePermissionEntry getPermissions(NodeRef nodeRef) + { + // Create the object if it is not found. + // Null objects are not cached in hibernate + // If the object does not exist it will repeatedly query to check its + // non existence. + NodePermissionEntry npe = null; + DbAccessControlList acl = null; + try + { + acl = getAccessControlList(nodeRef, false); + } + catch (InvalidNodeRefException e) + { + // Do nothing. + } + if (acl == null) + { + // there isn't an access control list for the node - spoof a null one + SimpleNodePermissionEntry snpe = new SimpleNodePermissionEntry( + nodeRef, + true, + Collections. emptySet()); + npe = snpe; + } + else + { + npe = createSimpleNodePermissionEntry(nodeRef); + } + // done + if (logger.isDebugEnabled()) + { + logger.debug( + "Created access control list for node: \n" + + " node: " + nodeRef + "\n" + + " acl: " + npe); + } + return npe; + } + + /** + * Get the persisted access control list or create it if required. + * + * @param nodeRef - the node for which to create the list + * @param create - create the object if it is missing + * @return Returns the current access control list or null if not found + */ + private DbAccessControlList getAccessControlList(NodeRef nodeRef, boolean create) + { + DbAccessControlList acl = + getACLDAO(nodeRef).getAccessControlList(nodeRef); + if (acl == null && create) + { + acl = createAccessControlList(nodeRef); + } + // done + if (logger.isDebugEnabled()) + { + logger.debug("Retrieved access control list: \n" + + " node: " + nodeRef + "\n" + + " list: " + acl); + } + return acl; + } + + /** + * Creates an access control list for the node and removes the entry from + * the nullPermsionCache. + */ + private DbAccessControlList createAccessControlList(NodeRef nodeRef) + { + DbAccessControlList acl = new DbAccessControlListImpl(); + acl.setInherits(INHERIT_PERMISSIONS_DEFAULT); + getHibernateTemplate().save(acl); + + // maintain inverse + getACLDAO(nodeRef).setAccessControlList(nodeRef, acl); + + // done + if (logger.isDebugEnabled()) + { + logger.debug("Created Access Control List: \n" + + " node: " + nodeRef + "\n" + + " list: " + acl); + } + return acl; + } + + public void deletePermissions(NodeRef nodeRef) + { + DbAccessControlList acl = null; + try + { + acl = getAccessControlList(nodeRef, false); + } + catch (InvalidNodeRefException e) + { + return; + } + if (acl != null) + { + // maintain referencial integrity + getACLDAO(nodeRef).setAccessControlList(nodeRef, null); + // delete the access control list - it will cascade to the entries + getHibernateTemplate().delete(acl); + } + } + + @SuppressWarnings("unchecked") + public void deletePermissions(final String authority) + { + // get the authority + HibernateCallback callback = new HibernateCallback() + { + public Object doInHibernate(Session session) + { + Query query = session + .getNamedQuery(QUERY_GET_AC_ENTRIES_FOR_AUTHORITY) + .setString("authorityRecipient", authority); + return (Integer) HibernateHelper.deleteDbAccessControlEntries(session, query); + } + }; + Integer deletedCount = (Integer) getHibernateTemplate().execute(callback); + // done + if (logger.isDebugEnabled()) + { + logger.debug("Deleted " + deletedCount + " entries for authority " + authority); + } + } + + public void deletePermissions(final NodeRef nodeRef, final String authority) + { + DbAccessControlList acl = null; + try + { + acl = getACLDAO(nodeRef).getAccessControlList(nodeRef); + } + catch (InvalidNodeRefException e) + { + return; + } + int deletedCount = 0; + if (acl != null) + { + deletedCount = acl.deleteEntriesForAuthority(authority); + } + // done + if (logger.isDebugEnabled()) + { + logger.debug("Deleted " + deletedCount + "entries for criteria: \n" + + " node: " + nodeRef + "\n" + + " authority: " + authority); + } + } + + /** + * Deletes all permission entries (access control list entries) that match + * the given criteria. Note that the access control list for the node is + * not deleted. + */ + public void deletePermission(NodeRef nodeRef, String authority, PermissionReference permission) + { + DbAccessControlList acl = null; + try + { + acl = getACLDAO(nodeRef).getAccessControlList(nodeRef); + } + catch (InvalidNodeRefException e) + { + return; + } + int deletedCount = 0; + if (acl != null) + { + DbPermissionKey permissionKey = new DbPermissionKey(permission.getQName(), permission.getName()); + deletedCount = acl.deleteEntry(authority, permissionKey); + } + // done + if (logger.isDebugEnabled()) + { + logger.debug("Deleted " + deletedCount + "entries for criteria: \n" + + " node: " + nodeRef + "\n" + + " permission: " + permission + "\n" + + " authority: " + authority); + } + } + + public void setPermission(NodeRef nodeRef, String authority, PermissionReference permission, boolean allow) + { + // get the entry + DbAccessControlEntry entry = getAccessControlEntry(nodeRef, authority, permission); + if (entry == null) + { + // need to create it + DbAccessControlList dbAccessControlList = getAccessControlList(nodeRef, true); + DbPermission dbPermission = getPermission(permission, true); + DbAuthority dbAuthority = getAuthority(authority, true); + // set persistent objects + entry = dbAccessControlList.newEntry(dbPermission, dbAuthority, allow); + // done + if (logger.isDebugEnabled()) + { + logger.debug("Created new access control entry: " + entry); + } + } + else + { + entry.setAllowed(allow); + // done + if (logger.isDebugEnabled()) + { + logger.debug("Updated access control entry: " + entry); + } + } + } + + /** + * @param nodeRef the node against which to join + * @param authority the authority against which to join + * @param perm the permission against which to join + * @return Returns all access control entries that match the criteria + */ + private DbAccessControlEntry getAccessControlEntry( + NodeRef nodeRef, + String authority, + PermissionReference permission) + { + DbAccessControlList acl = getAccessControlList(nodeRef, false); + DbAccessControlEntry entry = null; + if (acl != null) + { + DbPermissionKey permissionKey = new DbPermissionKey(permission.getQName(), permission.getName()); + entry = acl.getEntry(authority, permissionKey); + } + // done + if (logger.isDebugEnabled()) + { + logger.debug("" + (entry == null ? "Did not find" : "Found") + " entry for criteria: \n" + + " node: " + nodeRef + "\n" + + " authority: " + authority + "\n" + + " permission: " + permission); + } + return entry; + } + + /** + * Utility method to find or create a persisted authority + */ + private DbAuthority getAuthority(String authority, boolean create) + { + DbAuthority entity = (DbAuthority) getHibernateTemplate().get(DbAuthorityImpl.class, authority); + if ((entity == null) && create) + { + entity = new DbAuthorityImpl(); + entity.setRecipient(authority); + getHibernateTemplate().save(entity); + return entity; + } + else + { + return entity; + } + } + + /** + * Utility method to find and optionally create a persisted permission. + */ + private DbPermission getPermission(PermissionReference permissionRef, final boolean create) + { + final QName qname = permissionRef.getQName(); + final String name = permissionRef.getName(); + Session session = getSession(); + + DbPermission dbPermission = DbPermissionImpl.find(session, qname, name); + + // create if necessary + if ((dbPermission == null) && create) + { + dbPermission = new DbPermissionImpl(); + dbPermission.setTypeQname(qname); + dbPermission.setName(name); + getHibernateTemplate().save(dbPermission); + } + return dbPermission; + } + + public void setPermission(PermissionEntry permissionEntry) + { + setPermission( + permissionEntry.getNodeRef(), + permissionEntry.getAuthority(), + permissionEntry.getPermissionReference(), + permissionEntry.isAllowed()); + } + + public void setPermission(NodePermissionEntry nodePermissionEntry) + { + NodeRef nodeRef = nodePermissionEntry.getNodeRef(); + + // Get the access control list + // Note the logic here requires to know whether it was created or not + DbAccessControlList acl = getAccessControlList(nodeRef, false); + if (acl != null) + { + // maintain referencial integrity + getACLDAO(nodeRef).setAccessControlList(nodeRef, null); + // drop the list + getHibernateTemplate().delete(acl); + } + // create the access control list + acl = createAccessControlList(nodeRef); + + // set attributes + acl.setInherits(nodePermissionEntry.inheritPermissions()); + + // add all entries + for (PermissionEntry pe : nodePermissionEntry.getPermissionEntries()) + { + PermissionReference permission = pe.getPermissionReference(); + String authority = pe.getAuthority(); + boolean isAllowed = pe.isAllowed(); + + DbPermission permissionEntity = getPermission(permission, true); + DbAuthority authorityEntity = getAuthority(authority, true); + + @SuppressWarnings("unused") + DbAccessControlEntryImpl entry = acl.newEntry(permissionEntity, authorityEntity, isAllowed); + } + } + + public void setInheritParentPermissions(NodeRef nodeRef, boolean inheritParentPermissions) + { + DbAccessControlList acl = null; + if (!inheritParentPermissions) + { + // Inheritance == true is the default, so only force a create of the ACL if the value false + acl = getAccessControlList(nodeRef, true); + acl.setInherits(false); + } + else + { + acl = getAccessControlList(nodeRef, false); + if (acl != null) + { + acl.setInherits(true); + } + } + } + + public boolean getInheritParentPermissions(NodeRef nodeRef) + { + DbAccessControlList acl = null; + try + { + acl = getAccessControlList(nodeRef, false); + } + catch (InvalidNodeRefException e) + { + return INHERIT_PERMISSIONS_DEFAULT; + } + if (acl == null) + { + return true; + } + else + { + return acl.getInherits(); + } + } + + // Utility methods to create simple detached objects for the outside world + // We do not pass out the hibernate objects + + private SimpleNodePermissionEntry createSimpleNodePermissionEntry(NodeRef nodeRef) + { + DbAccessControlList acl = + getACLDAO(nodeRef).getAccessControlList(nodeRef); + if (acl == null) + { + // there isn't an access control list for the node - spoof a null one + SimpleNodePermissionEntry snpe = new SimpleNodePermissionEntry( + nodeRef, + true, + Collections. emptySet()); + return snpe; + } + else + { + Set entries = acl.getEntries(); + SimpleNodePermissionEntry snpe = new SimpleNodePermissionEntry( + nodeRef, + acl.getInherits(), + createSimplePermissionEntries(nodeRef, entries)); + return snpe; + } + } + + /** + * @param entries access control entries + * @return Returns a unique set of entries that can be given back to the outside world + */ + private Set createSimplePermissionEntries(NodeRef nodeRef, + Collection entries) + { + if (entries == null) + { + return null; + } + HashSet spes = new HashSet(entries.size(), 1.0f); + if (entries.size() != 0) + { + for (DbAccessControlEntry entry : entries) + { + spes.add(createSimplePermissionEntry(nodeRef, entry)); + } + } + return spes; + } + + private static SimplePermissionEntry createSimplePermissionEntry(NodeRef nodeRef, + DbAccessControlEntry ace) + { + if (ace == null) + { + return null; + } + return new SimplePermissionEntry( + nodeRef, + createSimplePermissionReference(ace.getPermission()), + ace.getAuthority().getRecipient(), + ace.isAllowed() ? AccessStatus.ALLOWED : AccessStatus.DENIED); + } + + private static SimplePermissionReference createSimplePermissionReference(DbPermission perm) + { + if (perm == null) + { + return null; + } + return new SimplePermissionReference( + perm.getTypeQname(), + perm.getName()); + } + + /** + * Helper to choose appropriate NodeService for the given NodeRef + * @param nodeRef The NodeRef to dispatch from. + * @return The appropriate NodeService. + */ + private AccessControlListDAO getACLDAO(NodeRef nodeRef) + { + AccessControlListDAO ret = fProtocolToACLDAO.get(nodeRef.getStoreRef().getProtocol()); + if (ret == null) + { + return fDefaultACLDAO; + } + return ret; + } +} diff --git a/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java b/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java index e17a13e6ae..c3822a51bc 100644 --- a/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java +++ b/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java @@ -20,8 +20,8 @@ import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.FileWriter; +import java.io.InputStream; import java.io.InputStreamReader; import java.io.Writer; import java.sql.Connection; @@ -47,8 +47,10 @@ import org.springframework.beans.BeansException; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.orm.hibernate3.LocalSessionFactoryBean; -import org.springframework.util.ResourceUtils; /** * Bootstraps the schema and schema update. The schema is considered missing if the applied patch table @@ -316,7 +318,8 @@ public class SchemaBootstrap implements ApplicationListener // perform a full update using Hibernate-generated statements File tempFile = TempFileProvider.createTempFile("AlfrescoSchemaCreate", ".sql"); dumpSchemaCreate(cfg, tempFile); - executeScriptFile(cfg, connection, tempFile); + FileInputStream tempInputStream = new FileInputStream(tempFile); + executeScriptFile(cfg, connection, tempInputStream, tempFile.getPath()); // execute post-create scripts (not patches) for (String scriptUrl : this.postCreateScriptUrls) { @@ -357,7 +360,8 @@ public class SchemaBootstrap implements ApplicationListener // execute if there were changes raised by Hibernate if (tempFile != null) { - executeScriptFile(cfg, connection, tempFile); + InputStream tempInputStream = new FileInputStream(tempFile); + executeScriptFile(cfg, connection, tempInputStream, tempFile.getPath()); } } } @@ -406,14 +410,14 @@ public class SchemaBootstrap implements ApplicationListener private void executeScriptUrl(Configuration cfg, Connection connection, String scriptUrl) throws Exception { Dialect dialect = Dialect.getDialect(cfg.getProperties()); - File scriptFile = getScriptFile(dialect.getClass(), scriptUrl); + InputStream scriptInputStream = getScriptInputStream(dialect.getClass(), scriptUrl); // check that it exists - if (scriptFile == null) + if (scriptInputStream == null) { throw AlfrescoRuntimeException.create(ERR_SCRIPT_NOT_FOUND, scriptUrl); } // now execute it - executeScriptFile(cfg, connection, scriptFile); + executeScriptFile(cfg, connection, scriptInputStream, scriptUrl); } /** @@ -421,45 +425,46 @@ public class SchemaBootstrap implements ApplicationListener * it. If not found, the dialect hierarchy will be walked until a compatible script is * found. This makes it possible to have scripts that are generic to all dialects. * - * @return Returns the file if found, otherwise null + * @return Returns an input stream onto the script, otherwise null */ - private File getScriptFile(Class dialectClazz, String scriptUrl) throws Exception + private InputStream getScriptInputStream(Class dialectClazz, String scriptUrl) throws Exception { // replace the dialect placeholder String dialectScriptUrl = scriptUrl.replaceAll(PLACEHOLDER_SCRIPT_DIALECT, dialectClazz.getName()); // get a handle on the resource - try + ResourcePatternResolver rpr = new PathMatchingResourcePatternResolver(this.getClass().getClassLoader()); + Resource resource = rpr.getResource(dialectScriptUrl); + if (!resource.exists()) { - File scriptFile = ResourceUtils.getFile(dialectScriptUrl); - if (scriptFile.exists()) + // it wasn't found. Get the superclass of the dialect and try again + Class superClazz = dialectClazz.getSuperclass(); + if (Dialect.class.isAssignableFrom(superClazz)) { - // found a compatible dialect version - return scriptFile; + // we still have a Dialect - try again + return getScriptInputStream(superClazz, scriptUrl); + } + else + { + // we have exhausted all options + return null; } - } - catch (FileNotFoundException e) - { - // doesn't exist - } - // it wasn't found. Get the superclass of the dialect and try again - Class superClazz = dialectClazz.getSuperclass(); - if (Dialect.class.isAssignableFrom(superClazz)) - { - // we still have a Dialect - try again - return getScriptFile(superClazz, scriptUrl); } else { - // we have exhausted all options - return null; + // we have a handle to it + return resource.getInputStream(); } } - private void executeScriptFile(Configuration cfg, Connection connection, File scriptFile) throws Exception + private void executeScriptFile( + Configuration cfg, + Connection connection, + InputStream scriptInputStream, + String scriptUrl) throws Exception { - logger.info(I18NUtil.getMessage(MSG_EXECUTING_SCRIPT, scriptFile)); + logger.info(I18NUtil.getMessage(MSG_EXECUTING_SCRIPT, scriptUrl)); - BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(scriptFile), "UTF8")); + BufferedReader reader = new BufferedReader(new InputStreamReader(scriptInputStream, "UTF8")); try { int line = 0; @@ -486,7 +491,7 @@ public class SchemaBootstrap implements ApplicationListener if (sb.length() > 0) { // we have an unterminated statement - throw AlfrescoRuntimeException.create(ERR_STATEMENT_TERMINATOR, (line - 1), scriptFile); + throw AlfrescoRuntimeException.create(ERR_STATEMENT_TERMINATOR, (line - 1), scriptUrl); } // there has not been anything to execute - it's just a comment line continue; @@ -524,6 +529,7 @@ public class SchemaBootstrap implements ApplicationListener finally { try { reader.close(); } catch (Throwable e) {} + try { scriptInputStream.close(); } catch (Throwable e) {} } } } diff --git a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java index 92a4abbf4f..de3710f9ae 100644 --- a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java +++ b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java @@ -1,1805 +1,1829 @@ -/* - * 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.node.db; - -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.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.Stack; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.model.ContentModel; -import org.alfresco.repo.domain.ChildAssoc; -import org.alfresco.repo.domain.Node; -import org.alfresco.repo.domain.NodeAssoc; -import org.alfresco.repo.domain.NodeStatus; -import org.alfresco.repo.domain.PropertyValue; -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; -import org.alfresco.service.cmr.dictionary.ClassDefinition; -import org.alfresco.service.cmr.dictionary.DataTypeDefinition; -import org.alfresco.service.cmr.dictionary.InvalidAspectException; -import org.alfresco.service.cmr.dictionary.InvalidTypeException; -import org.alfresco.service.cmr.dictionary.PropertyDefinition; -import org.alfresco.service.cmr.dictionary.TypeDefinition; -import org.alfresco.service.cmr.repository.AssociationExistsException; -import org.alfresco.service.cmr.repository.AssociationRef; -import org.alfresco.service.cmr.repository.ChildAssociationRef; -import org.alfresco.service.cmr.repository.CyclicChildRelationshipException; -import org.alfresco.service.cmr.repository.InvalidChildAssociationRefException; -import org.alfresco.service.cmr.repository.InvalidNodeRefException; -import org.alfresco.service.cmr.repository.InvalidStoreRefException; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.Path; -import org.alfresco.service.cmr.repository.StoreExistsException; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.cmr.repository.NodeRef.Status; -import org.alfresco.service.namespace.NamespaceService; -import org.alfresco.service.namespace.QName; -import org.alfresco.service.namespace.QNamePattern; -import org.alfresco.util.ParameterCheck; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.dao.DataIntegrityViolationException; -import org.springframework.util.Assert; - -/** - * Node service using database persistence layer to fulfill functionality - * - * @author Derek Hulley - */ -public class DbNodeServiceImpl extends AbstractNodeServiceImpl -{ - private static Log logger = LogFactory.getLog(DbNodeServiceImpl.class); - - private NodeDaoService nodeDaoService; - private StoreArchiveMap storeArchiveMap; - private NodeService avmNodeService; - - public DbNodeServiceImpl() - { - storeArchiveMap = new StoreArchiveMap(); // in case it is not set - } - - public void setNodeDaoService(NodeDaoService nodeDaoService) - { - this.nodeDaoService = nodeDaoService; - } - - public void setStoreArchiveMap(StoreArchiveMap storeArchiveMap) - { - this.storeArchiveMap = storeArchiveMap; - } - - public void setAvmNodeService(NodeService avmNodeService) - { - this.avmNodeService = avmNodeService; - } - - /** - * Performs a null-safe get of the node - * - * @param nodeRef the node to retrieve - * @return Returns the node entity (never null) - * @throws InvalidNodeRefException if the referenced node could not be found - */ - private Node getNodeNotNull(NodeRef nodeRef) throws InvalidNodeRefException - { - Node unchecked = nodeDaoService.getNode(nodeRef); - if (unchecked == null) - { - throw new InvalidNodeRefException("Node does not exist: " + nodeRef, nodeRef); - } - return unchecked; - } - - public boolean exists(StoreRef storeRef) - { - Store store = nodeDaoService.getStore(storeRef.getProtocol(), storeRef.getIdentifier()); - boolean exists = (store != null); - // done - return exists; - } - - public boolean exists(NodeRef nodeRef) - { - Node node = nodeDaoService.getNode(nodeRef); - boolean exists = (node != null); - // done - return exists; - } - - public Status getNodeStatus(NodeRef nodeRef) - { - NodeStatus nodeStatus = nodeDaoService.getNodeStatus(nodeRef, false); - if (nodeStatus == null) // node never existed - { - return null; - } - else - { - return new NodeRef.Status( - nodeStatus.getTransaction().getChangeTxnId(), - nodeStatus.isDeleted()); - } - } - - /** - * @see NodeDaoService#getStores() - */ - public List getStores() - { - List stores = nodeDaoService.getStores(); - List storeRefs = new ArrayList(stores.size()); - for (Store store : stores) - { - storeRefs.add(store.getStoreRef()); - } - // Now get the AVMStores. - List avmStores = avmNodeService.getStores(); - storeRefs.addAll(avmStores); - // Return them all. - return storeRefs; - } - - /** - * Defers to the typed service - * @see StoreDaoService#createWorkspace(String) - */ - public StoreRef createStore(String protocol, String identifier) - { - StoreRef storeRef = new StoreRef(protocol, identifier); - // check that the store does not already exist - Store store = nodeDaoService.getStore(protocol, identifier); - if (store != null) - { - throw new StoreExistsException("Unable to create a store that already exists: " + storeRef, storeRef); - } - - // invoke policies - invokeBeforeCreateStore(ContentModel.TYPE_STOREROOT, storeRef); - - // create a new one - store = nodeDaoService.createStore(protocol, identifier); - // get the root node - Node rootNode = store.getRootNode(); - // assign the root aspect - this is expected of all roots, even store roots - addAspect(rootNode.getNodeRef(), - ContentModel.ASPECT_ROOT, - Collections.emptyMap()); - - // invoke policies - invokeOnCreateStore(rootNode.getNodeRef()); - - // done - if (!store.getStoreRef().equals(storeRef)) - { - throw new RuntimeException("Incorrect store reference"); - } - return storeRef; - } - - public NodeRef getRootNode(StoreRef storeRef) throws InvalidStoreRefException - { - Store store = nodeDaoService.getStore(storeRef.getProtocol(), storeRef.getIdentifier()); - if (store == null) - { - throw new InvalidStoreRefException("Store does not exist", storeRef); - } - // get the root - Node node = store.getRootNode(); - if (node == null) - { - throw new InvalidStoreRefException("Store does not have a root node", storeRef); - } - NodeRef nodeRef = node.getNodeRef(); - // done - return nodeRef; - } - - /** - * @see #createNode(NodeRef, QName, QName, QName, Map) - */ - public ChildAssociationRef createNode( - NodeRef parentRef, - QName assocTypeQName, - QName assocQName, - QName nodeTypeQName) - { - return this.createNode(parentRef, assocTypeQName, assocQName, nodeTypeQName, null); - } - - /** - * @see org.alfresco.service.cmr.repository.NodeService#createNode(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName, org.alfresco.service.namespace.QName, org.alfresco.service.namespace.QName, java.util.Map) - */ - public ChildAssociationRef createNode( - NodeRef parentRef, - QName assocTypeQName, - QName assocQName, - QName nodeTypeQName, - Map properties) - { - Assert.notNull(parentRef); - Assert.notNull(assocTypeQName); - Assert.notNull(assocQName); - - // null property map is allowed - if (properties == null) - { - properties = new HashMap(); - } - else - { - // Copy the incomming property map since we may need to modify it later - properties = new HashMap(properties); - } - - // Invoke policy behaviour - invokeBeforeUpdateNode(parentRef); - invokeBeforeCreateNode(parentRef, assocTypeQName, assocQName, nodeTypeQName); - - // get the store that the parent belongs to - StoreRef storeRef = parentRef.getStoreRef(); - Store store = nodeDaoService.getStore(storeRef.getProtocol(), storeRef.getIdentifier()); - if (store == null) - { - throw new RuntimeException("No store found for parent node: " + parentRef); - } - - // check the node type - TypeDefinition nodeTypeDef = dictionaryService.getType(nodeTypeQName); - if (nodeTypeDef == null) - { - throw new InvalidTypeException(nodeTypeQName); - } - - // get/generate an ID for the node - String newId = generateGuid(properties); - - // create the node instance - Node childNode = nodeDaoService.newNode(store, newId, nodeTypeQName); - - // Set the default property values - addDefaultPropertyValues(nodeTypeDef, properties); - - // Add the default aspects to the node - addDefaultAspects(nodeTypeDef, childNode, properties); - - // set the properties - it is a new node so only set properties if there are any - Map propertiesBefore = getPropertiesImpl(childNode); - Map propertiesAfter = null; - if (properties.size() > 0) - { - propertiesAfter = setPropertiesImpl(childNode, properties); - } - - // get the parent node - Node parentNode = getNodeNotNull(parentRef); - - // create the association - ChildAssoc childAssoc = nodeDaoService.newChildAssoc( - parentNode, - childNode, - true, - assocTypeQName, - assocQName); - setChildUniqueName(childNode); // ensure uniqueness - ChildAssociationRef childAssocRef = childAssoc.getChildAssocRef(); - - // Invoke policy behaviour - invokeOnCreateNode(childAssocRef); - invokeOnUpdateNode(parentRef); - if (propertiesAfter != null) - { - invokeOnUpdateProperties(childAssocRef.getChildRef(), propertiesBefore, propertiesAfter); - } - - // done - return childAssocRef; - } - - /** - * Add the default aspects to a given node - * - * @param nodeTypeDef - */ - private void addDefaultAspects(ClassDefinition classDefinition, Node node, Map properties) - { - NodeRef nodeRef = node.getNodeRef(); - - // get the mandatory aspects for the node type - List defaultAspectDefs = classDefinition.getDefaultAspects(); - - // add all the aspects to the node - Set nodeAspects = node.getAspects(); - for (AspectDefinition defaultAspectDef : defaultAspectDefs) - { - invokeBeforeAddAspect(nodeRef, defaultAspectDef.getName()); - nodeAspects.add(defaultAspectDef.getName()); - addDefaultPropertyValues(defaultAspectDef, properties); - invokeOnAddAspect(nodeRef, defaultAspectDef.getName()); - - // Now add any default aspects for this aspect - addDefaultAspects(defaultAspectDef, node, properties); - } - } - - - /** - * Drops the old primary association and creates a new one - */ - public ChildAssociationRef moveNode( - NodeRef nodeToMoveRef, - NodeRef newParentRef, - QName assocTypeQName, - QName assocQName) - throws InvalidNodeRefException - { - Assert.notNull(nodeToMoveRef); - Assert.notNull(newParentRef); - Assert.notNull(assocTypeQName); - Assert.notNull(assocQName); - - // check the node references - Node nodeToMove = getNodeNotNull(nodeToMoveRef); - Node newParentNode = getNodeNotNull(newParentRef); - // get the primary parent assoc - ChildAssoc oldAssoc = nodeDaoService.getPrimaryParentAssoc(nodeToMove); - ChildAssociationRef oldAssocRef = oldAssoc.getChildAssocRef(); - // get the old parent - Node oldParentNode = oldAssoc.getParent(); - - boolean movingStore = !nodeToMoveRef.getStoreRef().equals(newParentRef.getStoreRef()); - - // data needed for policy invocation - QName nodeToMoveTypeQName = nodeToMove.getTypeQName(); - Set nodeToMoveAspects = nodeToMove.getAspects(); - - // Invoke policy behaviour - if (movingStore) - { - invokeBeforeDeleteNode(nodeToMoveRef); - invokeBeforeCreateNode(newParentRef, assocTypeQName, assocQName, nodeToMoveTypeQName); - } - else - { - invokeBeforeDeleteChildAssociation(oldAssocRef); - invokeBeforeCreateChildAssociation(newParentRef, nodeToMoveRef, assocTypeQName, assocQName); - invokeBeforeUpdateNode(oldParentNode.getNodeRef()); // old parent will be updated - invokeBeforeUpdateNode(newParentRef); // new parent ditto - } - - // remove the child assoc from the old parent - // don't cascade as we will still need the node afterwards - nodeDaoService.deleteChildAssoc(oldAssoc, false); - - // create a new assoc - ChildAssoc newAssoc = nodeDaoService.newChildAssoc( - newParentNode, - nodeToMove, - true, - assocTypeQName, - assocQName); - setChildUniqueName(nodeToMove); // ensure uniqueness - ChildAssociationRef newAssocRef = newAssoc.getChildAssocRef(); - - // If the node is moving stores, then drag the node hierarchy with it - if (movingStore) - { - // do the move - Store newStore = newParentNode.getStore(); - moveNodeToStore(nodeToMove, newStore); - // the node reference will have changed too - nodeToMoveRef = nodeToMove.getNodeRef(); - } - - // check that no cyclic relationships have been created - getPaths(nodeToMoveRef, false); - - // invoke policy behaviour - if (movingStore) - { - // TODO for now indicate that the node has been archived to prevent the version history from being removed - // in the future a onMove policy could be added and remove the need for onDelete and onCreate to be fired here - invokeOnDeleteNode(oldAssocRef, nodeToMoveTypeQName, nodeToMoveAspects, true); - invokeOnCreateNode(newAssoc.getChildAssocRef()); - } - else - { - invokeOnCreateChildAssociation(newAssoc.getChildAssocRef()); - invokeOnDeleteChildAssociation(oldAssoc.getChildAssocRef()); - invokeOnUpdateNode(oldParentNode.getNodeRef()); - invokeOnUpdateNode(newParentRef); - } - invokeOnMoveNode(oldAssocRef, newAssocRef); - - // update the node status - nodeDaoService.recordChangeId(nodeToMoveRef); - - // done - return newAssoc.getChildAssocRef(); - } - - public void setChildAssociationIndex(ChildAssociationRef childAssocRef, int index) - { - // get nodes - Node parentNode = getNodeNotNull(childAssocRef.getParentRef()); - Node childNode = getNodeNotNull(childAssocRef.getChildRef()); - - ChildAssoc assoc = nodeDaoService.getChildAssoc( - parentNode, - childNode, - childAssocRef.getTypeQName(), - childAssocRef.getQName()); - if (assoc == null) - { - throw new InvalidChildAssociationRefException("Unable to set child association index: \n" + - " assoc: " + childAssocRef + "\n" + - " index: " + index, - childAssocRef); - } - // set the index - assoc.setIndex(index); - } - - public QName getType(NodeRef nodeRef) throws InvalidNodeRefException - { - Node node = getNodeNotNull(nodeRef); - return node.getTypeQName(); - } - - /** - * @see org.alfresco.service.cmr.repository.NodeService#setType(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) - */ - public void setType(NodeRef nodeRef, QName typeQName) throws InvalidNodeRefException - { - // check the node type - TypeDefinition nodeTypeDef = dictionaryService.getType(typeQName); - if (nodeTypeDef == null) - { - throw new InvalidTypeException(typeQName); - } - - // Invoke policies - invokeBeforeUpdateNode(nodeRef); - - // Get the node and set the new type - Node node = getNodeNotNull(nodeRef); - node.setTypeQName(typeQName); - - // Add the default aspects to the node (update the properties with any new default values) - Map properties = this.getPropertiesImpl(node); - addDefaultAspects(nodeTypeDef, node, properties); - this.setProperties(nodeRef, properties); - - // Invoke policies - invokeOnUpdateNode(nodeRef); - } - - /** - * @see Node#getAspects() - */ - public void addAspect( - NodeRef nodeRef, - QName aspectTypeQName, - Map aspectProperties) - throws InvalidNodeRefException, InvalidAspectException - { - // check that the aspect is legal - AspectDefinition aspectDef = dictionaryService.getAspect(aspectTypeQName); - if (aspectDef == null) - { - throw new InvalidAspectException("The aspect is invalid: " + aspectTypeQName, aspectTypeQName); - } - - // Invoke policy behaviours - invokeBeforeUpdateNode(nodeRef); - invokeBeforeAddAspect(nodeRef, aspectTypeQName); - - Node node = getNodeNotNull(nodeRef); - - // attach the properties to the current node properties - Map nodeProperties = getPropertiesImpl(node); - - if (aspectProperties != null) - { - nodeProperties.putAll(aspectProperties); - } - - // Set any default property values that appear on the aspect - addDefaultPropertyValues(aspectDef, nodeProperties); - - // Add any dependant aspect - addDefaultAspects(aspectDef, node, nodeProperties); - - // Set the property values back on the node - setProperties(nodeRef, nodeProperties); - - // physically attach the aspect to the node - if (node.getAspects().add(aspectTypeQName) == true) - { - // Invoke policy behaviours - invokeOnUpdateNode(nodeRef); - invokeOnAddAspect(nodeRef, aspectTypeQName); - - // update the node status - nodeDaoService.recordChangeId(nodeRef); - } - } - - /** - * @see Node#getAspects() - */ - public void removeAspect(NodeRef nodeRef, QName aspectTypeQName) - throws InvalidNodeRefException, InvalidAspectException - { - // Invoke policy behaviours - invokeBeforeUpdateNode(nodeRef); - invokeBeforeRemoveAspect(nodeRef, aspectTypeQName); - - // get the aspect - AspectDefinition aspectDef = dictionaryService.getAspect(aspectTypeQName); - if (aspectDef == null) - { - throw new InvalidAspectException(aspectTypeQName); - } - // get the node - Node node = getNodeNotNull(nodeRef); - - // remove the aspect, if present - boolean removed = node.getAspects().remove(aspectTypeQName); - // if the aspect was present, remove the associated properties - if (removed) - { - Map nodeProperties = node.getProperties(); - Map propertyDefs = aspectDef.getProperties(); - for (QName propertyName : propertyDefs.keySet()) - { - nodeProperties.remove(propertyName); - } - - // Invoke policy behaviours - invokeOnUpdateNode(nodeRef); - invokeOnRemoveAspect(nodeRef, aspectTypeQName); - - // update the node status - nodeDaoService.recordChangeId(nodeRef); - } - } - - /** - * Performs a check on the set of node aspects - * - * @see Node#getAspects() - */ - public boolean hasAspect(NodeRef nodeRef, QName aspectRef) throws InvalidNodeRefException, InvalidAspectException - { - Node node = getNodeNotNull(nodeRef); - Set aspectQNames = node.getAspects(); - boolean hasAspect = aspectQNames.contains(aspectRef); - // done - return hasAspect; - } - - public Set getAspects(NodeRef nodeRef) throws InvalidNodeRefException - { - Node node = getNodeNotNull(nodeRef); - Set aspectQNames = node.getAspects(); - // copy the set to ensure initialization - Set ret = new HashSet(aspectQNames.size()); - ret.addAll(aspectQNames); - // done - return ret; - } - - public void deleteNode(NodeRef nodeRef) - { - boolean isArchivedNode = false; - boolean requiresDelete = false; - - // Invoke policy behaviours - invokeBeforeDeleteNode(nodeRef); - - // get the node - Node node = getNodeNotNull(nodeRef); - // get the primary parent-child relationship before it is gone - ChildAssociationRef childAssocRef = getPrimaryParent(nodeRef); - // get type and aspect QNames as they will be unavailable after the delete - QName nodeTypeQName = node.getTypeQName(); - Set nodeAspectQNames = node.getAspects(); - - // check if we need to archive the node - StoreRef archiveStoreRef = null; - if (nodeAspectQNames.contains(ContentModel.ASPECT_TEMPORARY)) - { - // the node has the temporary aspect meaning - // it can not be archived - requiresDelete = true; - isArchivedNode = false; - } - else - { - StoreRef storeRef = nodeRef.getStoreRef(); - archiveStoreRef = storeArchiveMap.getArchiveMap().get(storeRef); - // get the type and check if we need archiving - TypeDefinition typeDef = dictionaryService.getType(node.getTypeQName()); - if (typeDef == null || !typeDef.isArchive() || archiveStoreRef == null) - { - requiresDelete = true; - } - } - - if (requiresDelete) - { - // perform a normal deletion - nodeDaoService.deleteNode(node, true); - isArchivedNode = false; - } - else - { - // archive it - archiveNode(nodeRef, archiveStoreRef); - isArchivedNode = true; - } - - // Invoke policy behaviours - invokeOnDeleteNode(childAssocRef, nodeTypeQName, nodeAspectQNames, isArchivedNode); - } - - public ChildAssociationRef addChild(NodeRef parentRef, NodeRef childRef, QName assocTypeQName, QName assocQName) - { - // Invoke policy behaviours - invokeBeforeUpdateNode(parentRef); - invokeBeforeCreateChildAssociation(parentRef, childRef, assocTypeQName, assocQName); - - // get the parent node and ensure that it is a container node - Node parentNode = getNodeNotNull(parentRef); - // get the child node - Node childNode = getNodeNotNull(childRef); - // make the association - ChildAssoc assoc = nodeDaoService.newChildAssoc( - parentNode, - childNode, - false, - assocTypeQName, - assocQName); - // ensure name uniqueness - setChildUniqueName(childNode); - ChildAssociationRef assocRef = assoc.getChildAssocRef(); - NodeRef childNodeRef = assocRef.getChildRef(); - - // check that the child addition of the child has not created a cyclic relationship - // this functionality is provided for free in getPath - getPaths(childNodeRef, false); - - // Invoke policy behaviours - invokeOnCreateChildAssociation(assocRef); - invokeOnUpdateNode(parentRef); - - return assoc.getChildAssocRef(); - } - - public void removeChild(NodeRef parentRef, NodeRef childRef) throws InvalidNodeRefException - { - Node parentNode = getNodeNotNull(parentRef); - - Node childNode = getNodeNotNull(childRef); - Long childNodeId = childNode.getId(); - - // get all the child assocs - ChildAssociationRef primaryAssocRef = null; - Collection assocs = nodeDaoService.getChildAssocs(parentNode); - assocs = new HashSet(assocs); // copy set as we will be modifying it - for (ChildAssoc assoc : assocs) - { - if (!assoc.getChild().getId().equals(childNodeId)) - { - continue; // not a matching association - } - ChildAssociationRef assocRef = assoc.getChildAssocRef(); - // Is this a primary association? - if (assoc.getIsPrimary()) - { - // keep the primary associaton for last - primaryAssocRef = assocRef; - } - else - { - // delete the association instance - it is not primary - invokeBeforeDeleteChildAssociation(assocRef); - nodeDaoService.deleteChildAssoc(assoc, true); // cascade - invokeOnDeleteChildAssociation(assocRef); - } - } - // remove the child if the primary association was a match - if (primaryAssocRef != null) - { - deleteNode(primaryAssocRef.getChildRef()); - } - - // Invoke policy behaviours - invokeOnUpdateNode(parentRef); - - // done - } - - public Map getProperties(NodeRef nodeRef) throws InvalidNodeRefException - { - Node node = getNodeNotNull(nodeRef); - return getPropertiesImpl(node); - } - - private Map getPropertiesImpl(Node node) throws InvalidNodeRefException - { - NodeRef nodeRef = node.getNodeRef(); - - Map nodeProperties = node.getProperties(); - Map ret = new HashMap(nodeProperties.size()); - // copy values - for (Map.Entry entry: nodeProperties.entrySet()) - { - QName propertyQName = entry.getKey(); - PropertyValue propertyValue = entry.getValue(); - // get the property definition - PropertyDefinition propertyDef = dictionaryService.getProperty(propertyQName); - // convert to the correct type - Serializable value = makeSerializableValue(propertyDef, propertyValue); - // copy across - ret.put(propertyQName, value); - } - // spoof referencable properties - addReferencableProperties(nodeRef, node.getId(), ret); - // done - return ret; - } - - public Serializable getProperty(NodeRef nodeRef, QName qname) throws InvalidNodeRefException - { - // spoof referencable properties - if (qname.equals(ContentModel.PROP_STORE_PROTOCOL)) - { - return nodeRef.getStoreRef().getProtocol(); - } - else if (qname.equals(ContentModel.PROP_STORE_IDENTIFIER)) - { - return nodeRef.getStoreRef().getIdentifier(); - } - else if (qname.equals(ContentModel.PROP_NODE_UUID)) - { - return nodeRef.getId(); - } - - // get the property from the node - Node node = getNodeNotNull(nodeRef); - - if (qname.equals(ContentModel.PROP_NODE_DBID)) - { - return node.getId(); - } - - Map properties = node.getProperties(); - PropertyValue propertyValue = properties.get(qname); - - // get the property definition - PropertyDefinition propertyDef = dictionaryService.getProperty(qname); - // convert to the correct type - Serializable value = makeSerializableValue(propertyDef, propertyValue); - // done - return value; - } - - /** - * Ensures that all required properties are present on the node and copies the - * property values to the Node. - *

- * To remove a property, remove it from the map before calling this method. - * Null-valued properties are allowed. - *

- * If any of the values are null, a marker object is put in to mimic nulls. They will be turned back into - * a real nulls when the properties are requested again. - * - * @see Node#getProperties() - */ - public void setProperties(NodeRef nodeRef, Map properties) throws InvalidNodeRefException - { - Node node = getNodeNotNull(nodeRef); - - // Invoke policy behaviours - invokeBeforeUpdateNode(nodeRef); - - // Do the set properties - Map propertiesBefore = getPropertiesImpl(node); - Map propertiesAfter = setPropertiesImpl(node, properties); - - setChildUniqueName(node); // ensure uniqueness - - // Invoke policy behaviours - invokeOnUpdateNode(nodeRef); - invokeOnUpdateProperties(nodeRef, propertiesBefore, propertiesAfter); - } - - /** - * Does the work of setting the property values. Returns a map containing the state of the properties after the set - * operation is complete. - * - * @param node the node - * @param properties the map of property values - * @return the map of property values after the set operation is complete - * @throws InvalidNodeRefException - */ - private Map setPropertiesImpl(Node node, Map properties) throws InvalidNodeRefException - { - ParameterCheck.mandatory("properties", properties); - - // remove referencable properties - removeReferencableProperties(properties); - - // copy properties onto node - Map nodeProperties = node.getProperties(); - nodeProperties.clear(); - - // check the property type and copy the values across - for (QName propertyQName : properties.keySet()) - { - PropertyDefinition propertyDef = dictionaryService.getProperty(propertyQName); - Serializable value = properties.get(propertyQName); - // get a persistable value - PropertyValue propertyValue = makePropertyValue(propertyDef, value); - nodeProperties.put(propertyQName, propertyValue); - } - - // update the node status - NodeRef nodeRef = node.getNodeRef(); - nodeDaoService.recordChangeId(nodeRef); - - // Return the properties after - return Collections.unmodifiableMap(properties); - } - - /** - * Gets the properties map, sets the value (null is allowed) and checks that the new set - * of properties is valid. - * - * @see DbNodeServiceImpl.NullPropertyValue - */ - public void setProperty(NodeRef nodeRef, QName qname, Serializable value) throws InvalidNodeRefException - { - Assert.notNull(qname); - - // Invoke policy behaviours - invokeBeforeUpdateNode(nodeRef); - - // get the node - Node node = getNodeNotNull(nodeRef); - - // Do the set operation - Map propertiesBefore = getPropertiesImpl(node); - Map propertiesAfter = setPropertyImpl(node, qname, value); - - if (qname.equals(ContentModel.PROP_NAME)) - { - setChildUniqueName(node); // ensure uniqueness - } - - // Invoke policy behaviours - invokeOnUpdateNode(nodeRef); - invokeOnUpdateProperties(nodeRef, propertiesBefore, propertiesAfter); - } - - /** - * Does the work of setting a property value. Returns the values of the properties after the set operation is - * complete. - * - * @param node the node - * @param qname the qname of the property - * @param value the value of the property - * @return the values of the properties after the set operation is complete - * @throws InvalidNodeRefException - */ - public Map setPropertyImpl(Node node, QName qname, Serializable value) throws InvalidNodeRefException - { - NodeRef nodeRef = node.getNodeRef(); - - Map properties = node.getProperties(); - PropertyDefinition propertyDef = dictionaryService.getProperty(qname); - // get a persistable value - PropertyValue propertyValue = makePropertyValue(propertyDef, value); - properties.put(qname, propertyValue); - - // update the node status - nodeDaoService.recordChangeId(nodeRef); - - return getPropertiesImpl(node); - } - - /** - * Transforms {@link Node#getParentAssocs()} to a new collection - */ - public Collection getParents(NodeRef nodeRef) throws InvalidNodeRefException - { - Node node = getNodeNotNull(nodeRef); - // get the assocs pointing to it - Collection parentAssocs = node.getParentAssocs(); - // list of results - Collection results = new ArrayList(parentAssocs.size()); - for (ChildAssoc assoc : parentAssocs) - { - // get the parent - Node parentNode = assoc.getParent(); - results.add(parentNode.getNodeRef()); - } - // done - return results; - } - - /** - * Filters out any associations if their qname is not a match to the given pattern. - */ - public List getParentAssocs(NodeRef nodeRef, QNamePattern typeQNamePattern, QNamePattern qnamePattern) - { - Node node = getNodeNotNull(nodeRef); - // get the assocs pointing to it - Collection parentAssocs = node.getParentAssocs(); - // shortcut if there are no assocs - if (parentAssocs.size() == 0) - { - return Collections.emptyList(); - } - // list of results - List results = new ArrayList(parentAssocs.size()); - for (ChildAssoc assoc : parentAssocs) - { - // does the qname match the pattern? - if (!qnamePattern.isMatch(assoc.getQname()) || !typeQNamePattern.isMatch(assoc.getTypeQName())) - { - // no match - ignore - continue; - } - results.add(assoc.getChildAssocRef()); - } - // done - return results; - } - - /** - * Filters out any associations if their qname is not a match to the given pattern. - */ - public List getChildAssocs(NodeRef nodeRef, QNamePattern typeQNamePattern, QNamePattern qnamePattern) - { - Node node = getNodeNotNull(nodeRef); - // get the assocs pointing from it - Collection childAssocRefs = nodeDaoService.getChildAssocRefs(node); - // shortcut if there are no assocs - if (childAssocRefs.size() == 0) - { - return Collections.emptyList(); - } - // sort results - ArrayList orderedList = new ArrayList(childAssocRefs); - Collections.sort(orderedList); - - // list of results - int nthSibling = 0; - Iterator iterator = orderedList.iterator(); - while(iterator.hasNext()) - { - ChildAssociationRef childAssocRef = iterator.next(); - // does the qname match the pattern? - if (!qnamePattern.isMatch(childAssocRef.getQName()) || !typeQNamePattern.isMatch(childAssocRef.getTypeQName())) - { - // no match - remove - iterator.remove(); - } - else - { - childAssocRef.setNthSibling(nthSibling); - nthSibling++; - } - } - // done - return orderedList; - } - - public NodeRef getChildByName(NodeRef nodeRef, QName assocTypeQName, String childName) - { - Node node = getNodeNotNull(nodeRef); - ChildAssoc childAssoc = nodeDaoService.getChildAssoc(node, assocTypeQName, childName); - if (childAssoc != null) - { - return childAssoc.getChild().getNodeRef(); - } - else - { - return null; - } - } - - public ChildAssociationRef getPrimaryParent(NodeRef nodeRef) throws InvalidNodeRefException - { - Node node = getNodeNotNull(nodeRef); - // get the primary parent assoc - ChildAssoc assoc = nodeDaoService.getPrimaryParentAssoc(node); - - // done - the assoc may be null for a root node - ChildAssociationRef assocRef = null; - if (assoc == null) - { - assocRef = new ChildAssociationRef(null, null, null, nodeRef); - } - else - { - assocRef = assoc.getChildAssocRef(); - } - return assocRef; - } - - public AssociationRef createAssociation(NodeRef sourceRef, NodeRef targetRef, QName assocTypeQName) - throws InvalidNodeRefException, AssociationExistsException - { - // Invoke policy behaviours - invokeBeforeUpdateNode(sourceRef); - - Node sourceNode = getNodeNotNull(sourceRef); - Node targetNode = getNodeNotNull(targetRef); - // see if it exists - NodeAssoc assoc = nodeDaoService.getNodeAssoc(sourceNode, targetNode, assocTypeQName); - if (assoc != null) - { - throw new AssociationExistsException(sourceRef, targetRef, assocTypeQName); - } - // we are sure that the association doesn't exist - make it - assoc = nodeDaoService.newNodeAssoc(sourceNode, targetNode, assocTypeQName); - AssociationRef assocRef = assoc.getNodeAssocRef(); - - // Invoke policy behaviours - invokeOnUpdateNode(sourceRef); - invokeOnCreateAssociation(assocRef); - - return assocRef; - } - - public void removeAssociation(NodeRef sourceRef, NodeRef targetRef, QName assocTypeQName) - throws InvalidNodeRefException - { - Node sourceNode = getNodeNotNull(sourceRef); - Node targetNode = getNodeNotNull(targetRef); - // get the association - NodeAssoc assoc = nodeDaoService.getNodeAssoc(sourceNode, targetNode, assocTypeQName); - if (assoc == null) - { - // nothing to remove - return; - } - AssociationRef assocRef = assoc.getNodeAssocRef(); - - // Invoke policy behaviours - invokeBeforeUpdateNode(sourceRef); - - // delete it - nodeDaoService.deleteNodeAssoc(assoc); - - // Invoke policy behaviours - invokeOnUpdateNode(sourceRef); - invokeOnDeleteAssociation(assocRef); - } - - public List getTargetAssocs(NodeRef sourceRef, QNamePattern qnamePattern) - { - Node sourceNode = getNodeNotNull(sourceRef); - // get all assocs to target - Collection assocs = nodeDaoService.getTargetNodeAssocs(sourceNode); - List nodeAssocRefs = new ArrayList(assocs.size()); - for (NodeAssoc assoc : assocs) - { - // check qname pattern - if (!qnamePattern.isMatch(assoc.getTypeQName())) - { - continue; // the assoc name doesn't match the pattern given - } - nodeAssocRefs.add(assoc.getNodeAssocRef()); - } - // done - return nodeAssocRefs; - } - - public List getSourceAssocs(NodeRef targetRef, QNamePattern qnamePattern) - { - Node targetNode = getNodeNotNull(targetRef); - // get all assocs to source - Collection assocs = nodeDaoService.getSourceNodeAssocs(targetNode); - List nodeAssocRefs = new ArrayList(assocs.size()); - for (NodeAssoc assoc : assocs) - { - // check qname pattern - if (!qnamePattern.isMatch(assoc.getTypeQName())) - { - continue; // the assoc name doesn't match the pattern given - } - nodeAssocRefs.add(assoc.getNodeAssocRef()); - } - // done - return nodeAssocRefs; - } - - /** - * Recursive method used to build up paths from a given node to the root. - *

- * Whilst walking up the hierarchy to the root, some nodes may have a root aspect. - * Everytime one of these is encountered, a new path is farmed off, but the method - * continues to walk up the hierarchy. - * - * @param currentNode the node to start from, i.e. the child node to work upwards from - * @param currentPath the path from the current node to the descendent that we started from - * @param completedPaths paths that have reached the root are added to this collection - * @param assocStack the parent-child relationships traversed whilst building the path. - * Used to detected cyclic relationships. - * @param primaryOnly true if only the primary parent association must be traversed. - * If this is true, then the only root is the top level node having no parents. - * @throws CyclicChildRelationshipException - */ - private void prependPaths( - final Node currentNode, - final Path currentPath, - Collection completedPaths, - Stack assocStack, - boolean primaryOnly) - throws CyclicChildRelationshipException - { - NodeRef currentNodeRef = currentNode.getNodeRef(); - // get the parent associations of the given node - Collection parentAssocs = currentNode.getParentAssocs(); - // does the node have parents - boolean hasParents = parentAssocs.size() > 0; - // does the current node have a root aspect? - boolean isRoot = hasAspect(currentNodeRef, ContentModel.ASPECT_ROOT); - boolean isStoreRoot = currentNode.getTypeQName().equals(ContentModel.TYPE_STOREROOT); - - // look for a root. If we only want the primary root, then ignore all but the top-level root. - if (isRoot && !(primaryOnly && hasParents)) // exclude primary search with parents present - { - // create a one-sided assoc ref for the root node and prepend to the stack - // this effectively spoofs the fact that the current node is not below the root - // - we put this assoc in as the first assoc in the path must be a one-sided - // reference pointing to the root node - ChildAssociationRef assocRef = new ChildAssociationRef( - null, - null, - null, - getRootNode(currentNode.getNodeRef().getStoreRef())); - // create a path to save and add the 'root' assoc - Path pathToSave = new Path(); - Path.ChildAssocElement first = null; - for (Path.Element element: currentPath) - { - if (first == null) - { - first = (Path.ChildAssocElement) element; - } - else - { - pathToSave.append(element); - } - } - if (first != null) - { - // mimic an association that would appear if the current node was below - // the root node - // or if first beneath the root node it will make the real thing - ChildAssociationRef updateAssocRef = new ChildAssociationRef( - isStoreRoot ? ContentModel.ASSOC_CHILDREN : first.getRef().getTypeQName(), - getRootNode(currentNode.getNodeRef().getStoreRef()), - first.getRef().getQName(), - first.getRef().getChildRef()); - Path.Element newFirst = new Path.ChildAssocElement(updateAssocRef); - pathToSave.prepend(newFirst); - } - - Path.Element element = new Path.ChildAssocElement(assocRef); - pathToSave.prepend(element); - - // store the path just built - completedPaths.add(pathToSave); - } - - if (parentAssocs.size() == 0 && !isRoot) - { - throw new RuntimeException("Node without parents does not have root aspect: " + - currentNodeRef); - } - // walk up each parent association - for (ChildAssoc assoc : parentAssocs) - { - // does the association already exist in the stack - if (assocStack.contains(assoc)) - { - // the association was present already - throw new CyclicChildRelationshipException( - "Cyclic parent-child relationship detected: \n" + - " current node: " + currentNode + "\n" + - " current path: " + currentPath + "\n" + - " next assoc: " + assoc, - assoc); - } - // do we consider only primary assocs? - if (primaryOnly && !assoc.getIsPrimary()) - { - continue; - } - // build a path element - NodeRef parentRef = assoc.getParent().getNodeRef(); - QName qname = assoc.getQname(); - NodeRef childRef = assoc.getChild().getNodeRef(); - boolean isPrimary = assoc.getIsPrimary(); - // build a real association reference - ChildAssociationRef assocRef = new ChildAssociationRef(assoc.getTypeQName(), parentRef, qname, childRef, isPrimary, -1); - // Ordering is not important here: We are building distinct paths upwards - Path.Element element = new Path.ChildAssocElement(assocRef); - // create a new path that builds on the current path - Path path = new Path(); - path.append(currentPath); - // prepend element - path.prepend(element); - // get parent node - Node parentNode = assoc.getParent(); - - // push the assoc stack, recurse and pop - assocStack.push(assoc); - prependPaths(parentNode, path, completedPaths, assocStack, primaryOnly); - assocStack.pop(); - } - // done - } - - /** - * @see #getPaths(NodeRef, boolean) - * @see #prependPaths(Node, Path, Collection, Stack, boolean) - */ - public Path getPath(NodeRef nodeRef) throws InvalidNodeRefException - { - List paths = getPaths(nodeRef, true); // checks primary path count - if (paths.size() == 1) - { - return paths.get(0); // we know there is only one - } - throw new RuntimeException("Primary path count not checked"); // checked by getPaths() - } - - /** - * When searching for primaryOnly == true, checks that there is exactly - * one path. - * @see #prependPaths(Node, Path, Collection, Stack, boolean) - */ - public List getPaths(NodeRef nodeRef, boolean primaryOnly) throws InvalidNodeRefException - { - // get the starting node - Node node = getNodeNotNull(nodeRef); - // create storage for the paths - only need 1 bucket if we are looking for the primary path - List paths = new ArrayList(primaryOnly ? 1 : 10); - // create an empty current path to start from - Path currentPath = new Path(); - // create storage for touched associations - Stack assocStack = new Stack(); - // call recursive method to sort it out - prependPaths(node, currentPath, paths, assocStack, primaryOnly); - - // check that for the primary only case we have exactly one path - if (primaryOnly && paths.size() != 1) - { - throw new RuntimeException("Node has " + paths.size() + " primary paths: " + nodeRef); - } - - // done - return paths; - } - - private void archiveNode(NodeRef nodeRef, StoreRef archiveStoreRef) - { - Node node = getNodeNotNull(nodeRef); - ChildAssoc primaryParentAssoc = nodeDaoService.getPrimaryParentAssoc(node); - - // add the aspect - Set aspects = node.getAspects(); - aspects.add(ContentModel.ASPECT_ARCHIVED); - Map properties = node.getProperties(); - PropertyValue archivedByProperty = makePropertyValue( - dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_BY), - AuthenticationUtil.getCurrentUserName()); - properties.put(ContentModel.PROP_ARCHIVED_BY, archivedByProperty); - PropertyValue archivedDateProperty = makePropertyValue( - dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_DATE), - new Date()); - properties.put(ContentModel.PROP_ARCHIVED_DATE, archivedDateProperty); - PropertyValue archivedPrimaryParentNodeRefProperty = makePropertyValue( - dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC), - primaryParentAssoc.getChildAssocRef()); - properties.put(ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC, archivedPrimaryParentNodeRefProperty); - PropertyValue originalOwnerProperty = properties.get(ContentModel.PROP_OWNER); - PropertyValue originalCreatorProperty = properties.get(ContentModel.PROP_CREATOR); - if (originalOwnerProperty != null || originalCreatorProperty != null) - { - properties.put( - ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER, - originalOwnerProperty != null ? originalOwnerProperty : originalCreatorProperty); - } - - // change the node ownership - aspects.add(ContentModel.ASPECT_OWNABLE); - PropertyValue newOwnerProperty = makePropertyValue( - dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER), - AuthenticationUtil.getCurrentUserName()); - properties.put(ContentModel.PROP_OWNER, newOwnerProperty); - - // move the node - NodeRef archiveStoreRootNodeRef = getRootNode(archiveStoreRef); - moveNode( - nodeRef, - archiveStoreRootNodeRef, - ContentModel.ASSOC_CHILDREN, - QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedItem")); - - // get the IDs of all the node's primary children, including its own - Map nodesById = getNodeHierarchy(node, null); - - // Archive all the associations between the archived nodes and non-archived nodes - for (Node nodeToArchive : nodesById.values()) - { - archiveAssocs(nodeToArchive, nodesById); - } - - // the node reference has changed due to the store move - nodeRef = node.getNodeRef(); - } - - /** - * Performs all the necessary housekeeping involved in changing a node's store. - * This method cascades down through all the primary children of the node as - * well. - * - * @param node the node whose store is changing - * @param store the new store for the node - */ - private void moveNodeToStore(Node node, Store store) - { - // get the IDs of all the node's primary children, including its own - Map nodesById = getNodeHierarchy(node, null); - - // move each node into the archive store - for (Node nodeToMove : nodesById.values()) - { - NodeRef oldNodeRef = nodeToMove.getNodeRef(); - 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); - } - } - - /** - * Fill the map of all primary children below the given node. - * The given node will be added to the map and the method is recursive - * to all primary children. - * - * @param node the start of the hierarchy - * @param nodesById a map of nodes that will be reused as the return value - * @return Returns a map of nodes in the hierarchy keyed by their IDs - */ - private Map getNodeHierarchy(Node node, Map nodesById) - { - if (nodesById == null) - { - nodesById = new HashMap(23); - } - - Long id = node.getId(); - if (nodesById.containsKey(id)) - { - // this ID was already added - circular reference - logger.warn("Circular hierarchy found including node " + id); - return nodesById; - } - // add the node to the map - nodesById.put(id, node); - // recurse into the primary children - Collection childAssocs = nodeDaoService.getChildAssocs(node); - for (ChildAssoc childAssoc : childAssocs) - { - // cascade into primary associations - if (childAssoc.getIsPrimary()) - { - Node primaryChild = childAssoc.getChild(); - nodesById = getNodeHierarchy(primaryChild, nodesById); - } - } - return nodesById; - } - - /** - * Archive all associations to and from the given node, with the - * exception of associations to or from nodes in the given map. - *

- * Primary parent associations are also ignored. - * - * @param node the node whose associations must be archived - * @param nodesById a map of nodes partaking in the archival process - */ - private void archiveAssocs(Node node, Map nodesById) - { - List childAssocsToDelete = new ArrayList(5); - // child associations - ArrayList archivedChildAssocRefs = new ArrayList(5); - Collection childAssocs = nodeDaoService.getChildAssocs(node); - for (ChildAssoc assoc : childAssocs) - { - Long relatedNodeId = assoc.getChild().getId(); - if (nodesById.containsKey(relatedNodeId)) - { - // a sibling in the archive process - continue; - } - childAssocsToDelete.add(assoc); - archivedChildAssocRefs.add(assoc.getChildAssocRef()); - } - // parent associations - ArrayList archivedParentAssocRefs = new ArrayList(5); - for (ChildAssoc assoc : node.getParentAssocs()) - { - Long relatedNodeId = assoc.getParent().getId(); - if (nodesById.containsKey(relatedNodeId)) - { - // a sibling in the archive process - continue; - } - else if (assoc.getIsPrimary()) - { - // ignore the primary parent as this is handled more specifically - continue; - } - childAssocsToDelete.add(assoc); - archivedParentAssocRefs.add(assoc.getChildAssocRef()); - } - - List nodeAssocsToDelete = new ArrayList(5); - // source associations - ArrayList archivedSourceAssocRefs = new ArrayList(5); - for (NodeAssoc assoc : nodeDaoService.getSourceNodeAssocs(node)) - { - Long relatedNodeId = assoc.getSource().getId(); - if (nodesById.containsKey(relatedNodeId)) - { - // a sibling in the archive process - continue; - } - nodeAssocsToDelete.add(assoc); - archivedSourceAssocRefs.add(assoc.getNodeAssocRef()); - } - // target associations - ArrayList archivedTargetAssocRefs = new ArrayList(5); - for (NodeAssoc assoc : nodeDaoService.getTargetNodeAssocs(node)) - { - Long relatedNodeId = assoc.getTarget().getId(); - if (nodesById.containsKey(relatedNodeId)) - { - // a sibling in the archive process - continue; - } - nodeAssocsToDelete.add(assoc); - archivedTargetAssocRefs.add(assoc.getNodeAssocRef()); - } - // delete child assocs - for (ChildAssoc assoc : childAssocsToDelete) - { - nodeDaoService.deleteChildAssoc(assoc, false); - } - // delete node assocs - for (NodeAssoc assoc : nodeAssocsToDelete) - { - nodeDaoService.deleteNodeAssoc(assoc); - } - - // add archived aspect - node.getAspects().add(ContentModel.ASPECT_ARCHIVED_ASSOCS); - // set properties - Map properties = node.getProperties(); - - if (archivedParentAssocRefs.size() > 0) - { - PropertyDefinition propertyDef = dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_PARENT_ASSOCS); - PropertyValue propertyValue = makePropertyValue(propertyDef, archivedParentAssocRefs); - properties.put(ContentModel.PROP_ARCHIVED_PARENT_ASSOCS, propertyValue); - } - if (archivedChildAssocRefs.size() > 0) - { - PropertyDefinition propertyDef = dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_CHILD_ASSOCS); - PropertyValue propertyValue = makePropertyValue(propertyDef, archivedChildAssocRefs); - properties.put(ContentModel.PROP_ARCHIVED_CHILD_ASSOCS, propertyValue); - } - if (archivedSourceAssocRefs.size() > 0) - { - PropertyDefinition propertyDef = dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_SOURCE_ASSOCS); - PropertyValue propertyValue = makePropertyValue(propertyDef, archivedSourceAssocRefs); - properties.put(ContentModel.PROP_ARCHIVED_SOURCE_ASSOCS, propertyValue); - } - if (archivedTargetAssocRefs.size() > 0) - { - PropertyDefinition propertyDef = dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_TARGET_ASSOCS); - PropertyValue propertyValue = makePropertyValue(propertyDef, archivedTargetAssocRefs); - properties.put(ContentModel.PROP_ARCHIVED_TARGET_ASSOCS, propertyValue); - } - } - - public NodeRef getStoreArchiveNode(StoreRef storeRef) - { - StoreRef archiveStoreRef = storeArchiveMap.getArchiveMap().get(storeRef); - if (archiveStoreRef == null) - { - // no mapping for the given store - return null; - } - else - { - return getRootNode(archiveStoreRef); - } - } - - public NodeRef restoreNode(NodeRef archivedNodeRef, NodeRef destinationParentNodeRef, QName assocTypeQName, QName assocQName) - { - Node archivedNode = getNodeNotNull(archivedNodeRef); - Set aspects = archivedNode.getAspects(); - Map properties = archivedNode.getProperties(); - // the node must be a top-level archive node - if (!aspects.contains(ContentModel.ASPECT_ARCHIVED)) - { - throw new AlfrescoRuntimeException("The node to archive is not an archive node"); - } - ChildAssociationRef originalPrimaryParentAssocRef = (ChildAssociationRef) makeSerializableValue( - dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC), - properties.get(ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC)); - PropertyValue originalOwnerProperty = properties.get(ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER); - // remove the aspect archived aspect - aspects.remove(ContentModel.ASPECT_ARCHIVED); - properties.remove(ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC); - properties.remove(ContentModel.PROP_ARCHIVED_BY); - properties.remove(ContentModel.PROP_ARCHIVED_DATE); - properties.remove(ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER); - - // restore the original ownership - if (originalOwnerProperty != null) - { - aspects.add(ContentModel.ASPECT_OWNABLE); - properties.put(ContentModel.PROP_OWNER, originalOwnerProperty); - } - - if (destinationParentNodeRef == null) - { - // we must restore to the original location - destinationParentNodeRef = originalPrimaryParentAssocRef.getParentRef(); - } - // check the associations - if (assocTypeQName == null) - { - assocTypeQName = originalPrimaryParentAssocRef.getTypeQName(); - } - if (assocQName == null) - { - assocQName = originalPrimaryParentAssocRef.getQName(); - } - - // move the node to the target parent, which may or may not be the original parent - moveNode( - archivedNodeRef, - destinationParentNodeRef, - assocTypeQName, - assocQName); - - // get the IDs of all the node's primary children, including its own - Map restoredNodesById = getNodeHierarchy(archivedNode, null); - // Restore the archived associations, if required - for (Node restoredNode : restoredNodesById.values()) - { - restoreAssocs(restoredNode); - } - - // the node reference has changed due to the store move - NodeRef restoredNodeRef = archivedNode.getNodeRef(); - - // done - if (logger.isDebugEnabled()) - { - logger.debug("Restored node: \n" + - " original noderef: " + archivedNodeRef + "\n" + - " restored noderef: " + restoredNodeRef + "\n" + - " new parent: " + destinationParentNodeRef); - } - return restoredNodeRef; - } - - private void restoreAssocs(Node node) - { - NodeRef nodeRef = node.getNodeRef(); - // set properties - Map properties = node.getProperties(); - - // restore parent associations - Collection parentAssocRefs = (Collection) getProperty( - nodeRef, - ContentModel.PROP_ARCHIVED_PARENT_ASSOCS); - if (parentAssocRefs != null) - { - for (ChildAssociationRef assocRef : parentAssocRefs) - { - NodeRef parentNodeRef = assocRef.getParentRef(); - if (!exists(parentNodeRef)) - { - continue; - } - Node parentNode = getNodeNotNull(parentNodeRef); - // get the name to use for the unique child check - QName assocTypeQName = assocRef.getTypeQName(); - nodeDaoService.newChildAssoc( - parentNode, - node, - assocRef.isPrimary(), - assocTypeQName, - assocRef.getQName()); - } - properties.remove(ContentModel.PROP_ARCHIVED_PARENT_ASSOCS); - } - - // make sure that the node name uniqueness is enforced - setChildUniqueName(node); - - // restore child associations - Collection childAssocRefs = (Collection) getProperty( - nodeRef, - ContentModel.PROP_ARCHIVED_CHILD_ASSOCS); - if (childAssocRefs != null) - { - for (ChildAssociationRef assocRef : childAssocRefs) - { - NodeRef childNodeRef = assocRef.getChildRef(); - if (!exists(childNodeRef)) - { - continue; - } - Node childNode = getNodeNotNull(childNodeRef); - QName assocTypeQName = assocRef.getTypeQName(); - // get the name to use for the unique child check - nodeDaoService.newChildAssoc( - node, - childNode, - assocRef.isPrimary(), - assocTypeQName, - assocRef.getQName()); - // ensure that the name uniqueness is enforced for the child node - setChildUniqueName(childNode); - } - properties.remove(ContentModel.PROP_ARCHIVED_CHILD_ASSOCS); - } - // restore source associations - Collection sourceAssocRefs = (Collection) getProperty( - nodeRef, - ContentModel.PROP_ARCHIVED_SOURCE_ASSOCS); - if (sourceAssocRefs != null) - { - for (AssociationRef assocRef : sourceAssocRefs) - { - NodeRef sourceNodeRef = assocRef.getSourceRef(); - if (!exists(sourceNodeRef)) - { - continue; - } - Node sourceNode = getNodeNotNull(sourceNodeRef); - nodeDaoService.newNodeAssoc(sourceNode, node, assocRef.getTypeQName()); - } - properties.remove(ContentModel.PROP_ARCHIVED_SOURCE_ASSOCS); - } - // restore target associations - Collection targetAssocRefs = (Collection) getProperty( - nodeRef, - ContentModel.PROP_ARCHIVED_TARGET_ASSOCS); - if (targetAssocRefs != null) - { - for (AssociationRef assocRef : targetAssocRefs) - { - NodeRef targetNodeRef = assocRef.getTargetRef(); - if (!exists(targetNodeRef)) - { - continue; - } - Node targetNode = getNodeNotNull(targetNodeRef); - nodeDaoService.newNodeAssoc(node, targetNode, assocRef.getTypeQName()); - } - properties.remove(ContentModel.PROP_ARCHIVED_TARGET_ASSOCS); - } - // remove the aspect - node.getAspects().remove(ContentModel.ASPECT_ARCHIVED_ASSOCS); - } - - /** - * Checks the dictionary's definition of the association to assign a unique name to the child node. - * - * @param assocTypeQName the type of the child association - * @param childNode the child node being added. The name will be extracted from it, if necessary. - */ - private void setChildUniqueName(Node childNode) - { - // get the name property - Map properties = childNode.getProperties(); - PropertyValue nameValue = properties.get(ContentModel.PROP_NAME); - String useName = null; - if (nameValue == null) - { - // no name has been assigned, so assign the ID of the child node - useName = childNode.getUuid(); - } - else - { - useName = (String) nameValue.getValue(DataTypeDefinition.TEXT); - } - // get all the parent assocs - Collection parentAssocs = childNode.getParentAssocs(); - for (ChildAssoc assoc : parentAssocs) - { - QName assocTypeQName = assoc.getTypeQName(); - AssociationDefinition assocDef = dictionaryService.getAssociation(assocTypeQName); - if (!assocDef.isChild()) - { - throw new DataIntegrityViolationException("Child association has non-child type: " + assoc.getId()); - } - ChildAssociationDefinition childAssocDef = (ChildAssociationDefinition) assocDef; - if (childAssocDef.getDuplicateChildNamesAllowed()) - { - // the name is irrelevant, so it doesn't need to be put into the unique key - nodeDaoService.setChildNameUnique(assoc, null); - } - else - { - nodeDaoService.setChildNameUnique(assoc, useName); - } - } - // done - if (logger.isDebugEnabled()) - { - logger.debug( - "Unique name set for all " + parentAssocs.size() + " parent associations: \n" + - " name: " + useName); - } - } -} +/* + * 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.node.db; + +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.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Stack; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.domain.ChildAssoc; +import org.alfresco.repo.domain.Node; +import org.alfresco.repo.domain.NodeAssoc; +import org.alfresco.repo.domain.NodeStatus; +import org.alfresco.repo.domain.PropertyValue; +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; +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.InvalidAspectException; +import org.alfresco.service.cmr.dictionary.InvalidTypeException; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.cmr.repository.AssociationExistsException; +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.CyclicChildRelationshipException; +import org.alfresco.service.cmr.repository.InvalidChildAssociationRefException; +import org.alfresco.service.cmr.repository.InvalidNodeRefException; +import org.alfresco.service.cmr.repository.InvalidStoreRefException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.Path; +import org.alfresco.service.cmr.repository.StoreExistsException; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.repository.NodeRef.Status; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.QNamePattern; +import org.alfresco.util.ParameterCheck; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.util.Assert; + +/** + * Node service using database persistence layer to fulfill functionality + * + * @author Derek Hulley + */ +public class DbNodeServiceImpl extends AbstractNodeServiceImpl +{ + private static Log logger = LogFactory.getLog(DbNodeServiceImpl.class); + private static Log loggerPaths = LogFactory.getLog(DbNodeServiceImpl.class.getName() + ".paths"); + + private DictionaryService dictionaryService; + private NodeDaoService nodeDaoService; + private StoreArchiveMap storeArchiveMap; + private NodeService avmNodeService; + + public DbNodeServiceImpl() + { + storeArchiveMap = new StoreArchiveMap(); // in case it is not set + } + + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + public void setNodeDaoService(NodeDaoService nodeDaoService) + { + this.nodeDaoService = nodeDaoService; + } + + public void setStoreArchiveMap(StoreArchiveMap storeArchiveMap) + { + this.storeArchiveMap = storeArchiveMap; + } + + public void setAvmNodeService(NodeService avmNodeService) + { + this.avmNodeService = avmNodeService; + } + + /** + * Performs a null-safe get of the node + * + * @param nodeRef the node to retrieve + * @return Returns the node entity (never null) + * @throws InvalidNodeRefException if the referenced node could not be found + */ + private Node getNodeNotNull(NodeRef nodeRef) throws InvalidNodeRefException + { + Node unchecked = nodeDaoService.getNode(nodeRef); + if (unchecked == null) + { + throw new InvalidNodeRefException("Node does not exist: " + nodeRef, nodeRef); + } + return unchecked; + } + + public boolean exists(StoreRef storeRef) + { + Store store = nodeDaoService.getStore(storeRef.getProtocol(), storeRef.getIdentifier()); + boolean exists = (store != null); + // done + return exists; + } + + public boolean exists(NodeRef nodeRef) + { + Node node = nodeDaoService.getNode(nodeRef); + boolean exists = (node != null); + // done + return exists; + } + + public Status getNodeStatus(NodeRef nodeRef) + { + NodeStatus nodeStatus = nodeDaoService.getNodeStatus(nodeRef, false); + if (nodeStatus == null) // node never existed + { + return null; + } + else + { + return new NodeRef.Status( + nodeStatus.getTransaction().getChangeTxnId(), + nodeStatus.isDeleted()); + } + } + + /** + * @see NodeDaoService#getStores() + */ + public List getStores() + { + List stores = nodeDaoService.getStores(); + List storeRefs = new ArrayList(stores.size()); + for (Store store : stores) + { + storeRefs.add(store.getStoreRef()); + } + // Now get the AVMStores. + List avmStores = avmNodeService.getStores(); + storeRefs.addAll(avmStores); + // Return them all. + return storeRefs; + } + + /** + * Defers to the typed service + * @see StoreDaoService#createWorkspace(String) + */ + public StoreRef createStore(String protocol, String identifier) + { + StoreRef storeRef = new StoreRef(protocol, identifier); + // check that the store does not already exist + Store store = nodeDaoService.getStore(protocol, identifier); + if (store != null) + { + throw new StoreExistsException("Unable to create a store that already exists: " + storeRef, storeRef); + } + + // invoke policies + invokeBeforeCreateStore(ContentModel.TYPE_STOREROOT, storeRef); + + // create a new one + store = nodeDaoService.createStore(protocol, identifier); + // get the root node + Node rootNode = store.getRootNode(); + // assign the root aspect - this is expected of all roots, even store roots + addAspect(rootNode.getNodeRef(), + ContentModel.ASPECT_ROOT, + Collections.emptyMap()); + + // invoke policies + invokeOnCreateStore(rootNode.getNodeRef()); + + // done + if (!store.getStoreRef().equals(storeRef)) + { + throw new RuntimeException("Incorrect store reference"); + } + return storeRef; + } + + public NodeRef getRootNode(StoreRef storeRef) throws InvalidStoreRefException + { + Store store = nodeDaoService.getStore(storeRef.getProtocol(), storeRef.getIdentifier()); + if (store == null) + { + throw new InvalidStoreRefException("Store does not exist", storeRef); + } + // get the root + Node node = store.getRootNode(); + if (node == null) + { + throw new InvalidStoreRefException("Store does not have a root node", storeRef); + } + NodeRef nodeRef = node.getNodeRef(); + // done + return nodeRef; + } + + /** + * @see #createNode(NodeRef, QName, QName, QName, Map) + */ + public ChildAssociationRef createNode( + NodeRef parentRef, + QName assocTypeQName, + QName assocQName, + QName nodeTypeQName) + { + return this.createNode(parentRef, assocTypeQName, assocQName, nodeTypeQName, null); + } + + /** + * @see org.alfresco.service.cmr.repository.NodeService#createNode(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName, org.alfresco.service.namespace.QName, org.alfresco.service.namespace.QName, java.util.Map) + */ + public ChildAssociationRef createNode( + NodeRef parentRef, + QName assocTypeQName, + QName assocQName, + QName nodeTypeQName, + Map properties) + { + Assert.notNull(parentRef); + Assert.notNull(assocTypeQName); + Assert.notNull(assocQName); + + // null property map is allowed + if (properties == null) + { + properties = new HashMap(); + } + else + { + // Copy the incomming property map since we may need to modify it later + properties = new HashMap(properties); + } + + // Invoke policy behaviour + invokeBeforeUpdateNode(parentRef); + invokeBeforeCreateNode(parentRef, assocTypeQName, assocQName, nodeTypeQName); + + // get the store that the parent belongs to + StoreRef storeRef = parentRef.getStoreRef(); + Store store = nodeDaoService.getStore(storeRef.getProtocol(), storeRef.getIdentifier()); + if (store == null) + { + throw new RuntimeException("No store found for parent node: " + parentRef); + } + + // check the node type + TypeDefinition nodeTypeDef = dictionaryService.getType(nodeTypeQName); + if (nodeTypeDef == null) + { + throw new InvalidTypeException(nodeTypeQName); + } + + // get/generate an ID for the node + String newId = generateGuid(properties); + + // create the node instance + Node childNode = nodeDaoService.newNode(store, newId, nodeTypeQName); + + // get the parent node + Node parentNode = getNodeNotNull(parentRef); + + // Set the default property values + addDefaultPropertyValues(nodeTypeDef, properties); + + // Add the default aspects to the node + addDefaultAspects(nodeTypeDef, childNode, properties); + + // set the properties - it is a new node so only set properties if there are any + Map propertiesBefore = getPropertiesImpl(childNode); + Map propertiesAfter = null; + if (properties.size() > 0) + { + propertiesAfter = setPropertiesImpl(childNode, properties); + } + + // create the association + ChildAssoc childAssoc = nodeDaoService.newChildAssoc( + parentNode, + childNode, + true, + assocTypeQName, + assocQName); + setChildUniqueName(childNode); // ensure uniqueness + ChildAssociationRef childAssocRef = childAssoc.getChildAssocRef(); + + // Invoke policy behaviour + invokeOnCreateNode(childAssocRef); + invokeOnUpdateNode(parentRef); + if (propertiesAfter != null) + { + invokeOnUpdateProperties(childAssocRef.getChildRef(), propertiesBefore, propertiesAfter); + } + + // done + return childAssocRef; + } + + /** + * Add the default aspects to a given node + * + * @param nodeTypeDef + */ + private void addDefaultAspects(ClassDefinition classDefinition, Node node, Map properties) + { + NodeRef nodeRef = node.getNodeRef(); + + // get the mandatory aspects for the node type + List defaultAspectDefs = classDefinition.getDefaultAspects(); + + // add all the aspects to the node + Set nodeAspects = node.getAspects(); + for (AspectDefinition defaultAspectDef : defaultAspectDefs) + { + invokeBeforeAddAspect(nodeRef, defaultAspectDef.getName()); + nodeAspects.add(defaultAspectDef.getName()); + addDefaultPropertyValues(defaultAspectDef, properties); + invokeOnAddAspect(nodeRef, defaultAspectDef.getName()); + + // Now add any default aspects for this aspect + addDefaultAspects(defaultAspectDef, node, properties); + } + } + + /** + * Drops the old primary association and creates a new one + */ + public ChildAssociationRef moveNode( + NodeRef nodeToMoveRef, + NodeRef newParentRef, + QName assocTypeQName, + QName assocQName) + throws InvalidNodeRefException + { + Assert.notNull(nodeToMoveRef); + Assert.notNull(newParentRef); + Assert.notNull(assocTypeQName); + Assert.notNull(assocQName); + + // check the node references + Node nodeToMove = getNodeNotNull(nodeToMoveRef); + Node newParentNode = getNodeNotNull(newParentRef); + // get the primary parent assoc + ChildAssoc oldAssoc = nodeDaoService.getPrimaryParentAssoc(nodeToMove); + ChildAssociationRef oldAssocRef = oldAssoc.getChildAssocRef(); + // get the old parent + Node oldParentNode = oldAssoc.getParent(); + + boolean movingStore = !nodeToMoveRef.getStoreRef().equals(newParentRef.getStoreRef()); + + // data needed for policy invocation + QName nodeToMoveTypeQName = nodeToMove.getTypeQName(); + Set nodeToMoveAspects = nodeToMove.getAspects(); + + // Invoke policy behaviour + if (movingStore) + { + invokeBeforeDeleteNode(nodeToMoveRef); + invokeBeforeCreateNode(newParentRef, assocTypeQName, assocQName, nodeToMoveTypeQName); + } + else + { + invokeBeforeDeleteChildAssociation(oldAssocRef); + invokeBeforeCreateChildAssociation(newParentRef, nodeToMoveRef, assocTypeQName, assocQName); + invokeBeforeUpdateNode(oldParentNode.getNodeRef()); // old parent will be updated + invokeBeforeUpdateNode(newParentRef); // new parent ditto + } + + // remove the child assoc from the old parent + // don't cascade as we will still need the node afterwards + nodeDaoService.deleteChildAssoc(oldAssoc, false); + + // create a new assoc + ChildAssoc newAssoc = nodeDaoService.newChildAssoc( + newParentNode, + nodeToMove, + true, + assocTypeQName, + assocQName); + setChildUniqueName(nodeToMove); // ensure uniqueness + ChildAssociationRef newAssocRef = newAssoc.getChildAssocRef(); + + // If the node is moving stores, then drag the node hierarchy with it + if (movingStore) + { + // do the move + Store newStore = newParentNode.getStore(); + moveNodeToStore(nodeToMove, newStore); + // the node reference will have changed too + nodeToMoveRef = nodeToMove.getNodeRef(); + } + + // check that no cyclic relationships have been created + getPaths(nodeToMoveRef, false); + + // invoke policy behaviour + if (movingStore) + { + // TODO for now indicate that the node has been archived to prevent the version history from being removed + // in the future a onMove policy could be added and remove the need for onDelete and onCreate to be fired here + invokeOnDeleteNode(oldAssocRef, nodeToMoveTypeQName, nodeToMoveAspects, true); + invokeOnCreateNode(newAssoc.getChildAssocRef()); + } + else + { + invokeOnCreateChildAssociation(newAssoc.getChildAssocRef()); + invokeOnDeleteChildAssociation(oldAssoc.getChildAssocRef()); + invokeOnUpdateNode(oldParentNode.getNodeRef()); + invokeOnUpdateNode(newParentRef); + } + invokeOnMoveNode(oldAssocRef, newAssocRef); + + // update the node status + nodeDaoService.recordChangeId(nodeToMoveRef); + + // done + return newAssoc.getChildAssocRef(); + } + + public void setChildAssociationIndex(ChildAssociationRef childAssocRef, int index) + { + // get nodes + Node parentNode = getNodeNotNull(childAssocRef.getParentRef()); + Node childNode = getNodeNotNull(childAssocRef.getChildRef()); + + ChildAssoc assoc = nodeDaoService.getChildAssoc( + parentNode, + childNode, + childAssocRef.getTypeQName(), + childAssocRef.getQName()); + if (assoc == null) + { + throw new InvalidChildAssociationRefException("Unable to set child association index: \n" + + " assoc: " + childAssocRef + "\n" + + " index: " + index, + childAssocRef); + } + // set the index + assoc.setIndex(index); + } + + public QName getType(NodeRef nodeRef) throws InvalidNodeRefException + { + Node node = getNodeNotNull(nodeRef); + return node.getTypeQName(); + } + + /** + * @see org.alfresco.service.cmr.repository.NodeService#setType(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + public void setType(NodeRef nodeRef, QName typeQName) throws InvalidNodeRefException + { + // check the node type + TypeDefinition nodeTypeDef = dictionaryService.getType(typeQName); + if (nodeTypeDef == null) + { + throw new InvalidTypeException(typeQName); + } + + // Invoke policies + invokeBeforeUpdateNode(nodeRef); + + // Get the node and set the new type + Node node = getNodeNotNull(nodeRef); + node.setTypeQName(typeQName); + + // Add the default aspects to the node (update the properties with any new default values) + Map properties = this.getPropertiesImpl(node); + addDefaultAspects(nodeTypeDef, node, properties); + this.setProperties(nodeRef, properties); + + // Invoke policies + invokeOnUpdateNode(nodeRef); + } + + /** + * @see Node#getAspects() + */ + public void addAspect( + NodeRef nodeRef, + QName aspectTypeQName, + Map aspectProperties) + throws InvalidNodeRefException, InvalidAspectException + { + // check that the aspect is legal + AspectDefinition aspectDef = dictionaryService.getAspect(aspectTypeQName); + if (aspectDef == null) + { + throw new InvalidAspectException("The aspect is invalid: " + aspectTypeQName, aspectTypeQName); + } + + // Invoke policy behaviours + invokeBeforeUpdateNode(nodeRef); + invokeBeforeAddAspect(nodeRef, aspectTypeQName); + + Node node = getNodeNotNull(nodeRef); + + // attach the properties to the current node properties + Map nodeProperties = getPropertiesImpl(node); + + if (aspectProperties != null) + { + nodeProperties.putAll(aspectProperties); + } + + // Set any default property values that appear on the aspect + addDefaultPropertyValues(aspectDef, nodeProperties); + + // Add any dependant aspect + addDefaultAspects(aspectDef, node, nodeProperties); + + // Set the property values back on the node + setProperties(nodeRef, nodeProperties); + + // physically attach the aspect to the node + if (node.getAspects().add(aspectTypeQName) == true) + { + // Invoke policy behaviours + invokeOnUpdateNode(nodeRef); + invokeOnAddAspect(nodeRef, aspectTypeQName); + + // update the node status + nodeDaoService.recordChangeId(nodeRef); + } + } + + /** + * @see Node#getAspects() + */ + public void removeAspect(NodeRef nodeRef, QName aspectTypeQName) + throws InvalidNodeRefException, InvalidAspectException + { + // Invoke policy behaviours + invokeBeforeUpdateNode(nodeRef); + invokeBeforeRemoveAspect(nodeRef, aspectTypeQName); + + // get the aspect + AspectDefinition aspectDef = dictionaryService.getAspect(aspectTypeQName); + if (aspectDef == null) + { + throw new InvalidAspectException(aspectTypeQName); + } + // get the node + Node node = getNodeNotNull(nodeRef); + + // remove the aspect, if present + boolean removed = node.getAspects().remove(aspectTypeQName); + // if the aspect was present, remove the associated properties + if (removed) + { + Map nodeProperties = node.getProperties(); + Map propertyDefs = aspectDef.getProperties(); + for (QName propertyName : propertyDefs.keySet()) + { + nodeProperties.remove(propertyName); + } + + // Invoke policy behaviours + invokeOnUpdateNode(nodeRef); + invokeOnRemoveAspect(nodeRef, aspectTypeQName); + + // update the node status + nodeDaoService.recordChangeId(nodeRef); + } + } + + /** + * Performs a check on the set of node aspects + * + * @see Node#getAspects() + */ + public boolean hasAspect(NodeRef nodeRef, QName aspectRef) throws InvalidNodeRefException, InvalidAspectException + { + Node node = getNodeNotNull(nodeRef); + Set aspectQNames = node.getAspects(); + boolean hasAspect = aspectQNames.contains(aspectRef); + // done + return hasAspect; + } + + public Set getAspects(NodeRef nodeRef) throws InvalidNodeRefException + { + Node node = getNodeNotNull(nodeRef); + Set aspectQNames = node.getAspects(); + // copy the set to ensure initialization + Set ret = new HashSet(aspectQNames.size()); + ret.addAll(aspectQNames); + // done + return ret; + } + + public void deleteNode(NodeRef nodeRef) + { + boolean isArchivedNode = false; + boolean requiresDelete = false; + + // Invoke policy behaviours + invokeBeforeDeleteNode(nodeRef); + + // get the node + Node node = getNodeNotNull(nodeRef); + // get the primary parent-child relationship before it is gone + ChildAssociationRef childAssocRef = getPrimaryParent(nodeRef); + // get type and aspect QNames as they will be unavailable after the delete + QName nodeTypeQName = node.getTypeQName(); + Set nodeAspectQNames = node.getAspects(); + + // check if we need to archive the node + StoreRef archiveStoreRef = null; + if (nodeAspectQNames.contains(ContentModel.ASPECT_TEMPORARY)) + { + // the node has the temporary aspect meaning + // it can not be archived + requiresDelete = true; + isArchivedNode = false; + } + else + { + StoreRef storeRef = nodeRef.getStoreRef(); + archiveStoreRef = storeArchiveMap.getArchiveMap().get(storeRef); + // get the type and check if we need archiving + TypeDefinition typeDef = dictionaryService.getType(node.getTypeQName()); + if (typeDef == null || !typeDef.isArchive() || archiveStoreRef == null) + { + requiresDelete = true; + } + } + + if (requiresDelete) + { + // perform a normal deletion + nodeDaoService.deleteNode(node, true); + isArchivedNode = false; + } + else + { + // archive it + archiveNode(nodeRef, archiveStoreRef); + isArchivedNode = true; + } + + // Invoke policy behaviours + invokeOnDeleteNode(childAssocRef, nodeTypeQName, nodeAspectQNames, isArchivedNode); + } + + public ChildAssociationRef addChild(NodeRef parentRef, NodeRef childRef, QName assocTypeQName, QName assocQName) + { + // Invoke policy behaviours + invokeBeforeUpdateNode(parentRef); + invokeBeforeCreateChildAssociation(parentRef, childRef, assocTypeQName, assocQName); + + // get the parent node and ensure that it is a container node + Node parentNode = getNodeNotNull(parentRef); + // get the child node + Node childNode = getNodeNotNull(childRef); + // make the association + ChildAssoc assoc = nodeDaoService.newChildAssoc( + parentNode, + childNode, + false, + assocTypeQName, + assocQName); + // ensure name uniqueness + setChildUniqueName(childNode); + ChildAssociationRef assocRef = assoc.getChildAssocRef(); + NodeRef childNodeRef = assocRef.getChildRef(); + + // check that the child addition of the child has not created a cyclic relationship + // this functionality is provided for free in getPath + getPaths(childNodeRef, false); + + // Invoke policy behaviours + invokeOnCreateChildAssociation(assocRef); + invokeOnUpdateNode(parentRef); + + return assoc.getChildAssocRef(); + } + + public void removeChild(NodeRef parentRef, NodeRef childRef) throws InvalidNodeRefException + { + Node parentNode = getNodeNotNull(parentRef); + Node childNode = getNodeNotNull(childRef); + Long childNodeId = childNode.getId(); + + // get all the child assocs + ChildAssociationRef primaryAssocRef = null; + Collection assocs = nodeDaoService.getChildAssocs(parentNode); + assocs = new HashSet(assocs); // copy set as we will be modifying it + for (ChildAssoc assoc : assocs) + { + if (!assoc.getChild().getId().equals(childNodeId)) + { + continue; // not a matching association + } + ChildAssociationRef assocRef = assoc.getChildAssocRef(); + // Is this a primary association? + if (assoc.getIsPrimary()) + { + // keep the primary associaton for last + primaryAssocRef = assocRef; + } + else + { + // delete the association instance - it is not primary + invokeBeforeDeleteChildAssociation(assocRef); + nodeDaoService.deleteChildAssoc(assoc, true); // cascade + invokeOnDeleteChildAssociation(assocRef); + } + } + // remove the child if the primary association was a match + if (primaryAssocRef != null) + { + deleteNode(primaryAssocRef.getChildRef()); + } + + // Invoke policy behaviours + invokeOnUpdateNode(parentRef); + + // done + } + + public Map getProperties(NodeRef nodeRef) throws InvalidNodeRefException + { + Node node = getNodeNotNull(nodeRef); + return getPropertiesImpl(node); + } + + private Map getPropertiesImpl(Node node) throws InvalidNodeRefException + { + NodeRef nodeRef = node.getNodeRef(); + + Map nodeProperties = node.getProperties(); + Map ret = new HashMap(nodeProperties.size()); + // copy values + for (Map.Entry entry: nodeProperties.entrySet()) + { + QName propertyQName = entry.getKey(); + PropertyValue propertyValue = entry.getValue(); + // get the property definition + PropertyDefinition propertyDef = dictionaryService.getProperty(propertyQName); + // convert to the correct type + Serializable value = makeSerializableValue(propertyDef, propertyValue); + // copy across + ret.put(propertyQName, value); + } + // spoof referencable properties + addReferencableProperties(nodeRef, node.getId(), ret); + // done + return ret; + } + + public Serializable getProperty(NodeRef nodeRef, QName qname) throws InvalidNodeRefException + { + // spoof referencable properties + if (qname.equals(ContentModel.PROP_STORE_PROTOCOL)) + { + return nodeRef.getStoreRef().getProtocol(); + } + else if (qname.equals(ContentModel.PROP_STORE_IDENTIFIER)) + { + return nodeRef.getStoreRef().getIdentifier(); + } + else if (qname.equals(ContentModel.PROP_NODE_UUID)) + { + return nodeRef.getId(); + } + + // get the property from the node + Node node = getNodeNotNull(nodeRef); + + if (qname.equals(ContentModel.PROP_NODE_DBID)) + { + return node.getId(); + } + + Map properties = node.getProperties(); + PropertyValue propertyValue = properties.get(qname); + + // get the property definition + PropertyDefinition propertyDef = dictionaryService.getProperty(qname); + // convert to the correct type + Serializable value = makeSerializableValue(propertyDef, propertyValue); + // done + return value; + } + + /** + * Ensures that all required properties are present on the node and copies the + * property values to the Node. + *

+ * To remove a property, remove it from the map before calling this method. + * Null-valued properties are allowed. + *

+ * If any of the values are null, a marker object is put in to mimic nulls. They will be turned back into + * a real nulls when the properties are requested again. + * + * @see Node#getProperties() + */ + public void setProperties(NodeRef nodeRef, Map properties) throws InvalidNodeRefException + { + Node node = getNodeNotNull(nodeRef); + + // Invoke policy behaviours + invokeBeforeUpdateNode(nodeRef); + + // Do the set properties + Map propertiesBefore = getPropertiesImpl(node); + Map propertiesAfter = setPropertiesImpl(node, properties); + + setChildUniqueName(node); // ensure uniqueness + + // Invoke policy behaviours + invokeOnUpdateNode(nodeRef); + invokeOnUpdateProperties(nodeRef, propertiesBefore, propertiesAfter); + } + + /** + * Does the work of setting the property values. Returns a map containing the state of the properties after the set + * operation is complete. + * + * @param node the node + * @param properties the map of property values + * @return the map of property values after the set operation is complete + * @throws InvalidNodeRefException + */ + private Map setPropertiesImpl(Node node, Map properties) throws InvalidNodeRefException + { + ParameterCheck.mandatory("properties", properties); + + // remove referencable properties + removeReferencableProperties(properties); + + // copy properties onto node + Map nodeProperties = node.getProperties(); + nodeProperties.clear(); + + // check the property type and copy the values across + for (QName propertyQName : properties.keySet()) + { + PropertyDefinition propertyDef = dictionaryService.getProperty(propertyQName); + Serializable value = properties.get(propertyQName); + // get a persistable value + PropertyValue propertyValue = makePropertyValue(propertyDef, value); + nodeProperties.put(propertyQName, propertyValue); + } + + // update the node status + NodeRef nodeRef = node.getNodeRef(); + nodeDaoService.recordChangeId(nodeRef); + + // Return the properties after + return Collections.unmodifiableMap(properties); + } + + /** + * Gets the properties map, sets the value (null is allowed) and checks that the new set + * of properties is valid. + * + * @see DbNodeServiceImpl.NullPropertyValue + */ + public void setProperty(NodeRef nodeRef, QName qname, Serializable value) throws InvalidNodeRefException + { + Assert.notNull(qname); + + // Invoke policy behaviours + invokeBeforeUpdateNode(nodeRef); + + // get the node + Node node = getNodeNotNull(nodeRef); + + // Do the set operation + Map propertiesBefore = getPropertiesImpl(node); + Map propertiesAfter = setPropertyImpl(node, qname, value); + + if (qname.equals(ContentModel.PROP_NAME)) + { + setChildUniqueName(node); // ensure uniqueness + } + + // Invoke policy behaviours + invokeOnUpdateNode(nodeRef); + invokeOnUpdateProperties(nodeRef, propertiesBefore, propertiesAfter); + } + + /** + * Does the work of setting a property value. Returns the values of the properties after the set operation is + * complete. + * + * @param node the node + * @param qname the qname of the property + * @param value the value of the property + * @return the values of the properties after the set operation is complete + * @throws InvalidNodeRefException + */ + public Map setPropertyImpl(Node node, QName qname, Serializable value) throws InvalidNodeRefException + { + NodeRef nodeRef = node.getNodeRef(); + + Map properties = node.getProperties(); + PropertyDefinition propertyDef = dictionaryService.getProperty(qname); + // get a persistable value + PropertyValue propertyValue = makePropertyValue(propertyDef, value); + properties.put(qname, propertyValue); + + // update the node status + nodeDaoService.recordChangeId(nodeRef); + + return getPropertiesImpl(node); + } + + /** + * Transforms {@link Node#getParentAssocs()} to a new collection + */ + public Collection getParents(NodeRef nodeRef) throws InvalidNodeRefException + { + Node node = getNodeNotNull(nodeRef); + // get the assocs pointing to it + Collection parentAssocs = node.getParentAssocs(); + // list of results + Collection results = new ArrayList(parentAssocs.size()); + for (ChildAssoc assoc : parentAssocs) + { + // get the parent + Node parentNode = assoc.getParent(); + results.add(parentNode.getNodeRef()); + } + // done + return results; + } + + /** + * Filters out any associations if their qname is not a match to the given pattern. + */ + public List getParentAssocs(NodeRef nodeRef, QNamePattern typeQNamePattern, QNamePattern qnamePattern) + { + Node node = getNodeNotNull(nodeRef); + // get the assocs pointing to it + Collection parentAssocs = node.getParentAssocs(); + // shortcut if there are no assocs + if (parentAssocs.size() == 0) + { + return Collections.emptyList(); + } + // list of results + List results = new ArrayList(parentAssocs.size()); + for (ChildAssoc assoc : parentAssocs) + { + // does the qname match the pattern? + if (!qnamePattern.isMatch(assoc.getQname()) || !typeQNamePattern.isMatch(assoc.getTypeQName())) + { + // no match - ignore + continue; + } + results.add(assoc.getChildAssocRef()); + } + // done + return results; + } + + /** + * Filters out any associations if their qname is not a match to the given pattern. + */ + public List getChildAssocs(NodeRef nodeRef, QNamePattern typeQNamePattern, QNamePattern qnamePattern) + { + Node node = getNodeNotNull(nodeRef); + // get the assocs pointing from it + Collection childAssocRefs = nodeDaoService.getChildAssocRefs(node); + // shortcut if there are no assocs + if (childAssocRefs.size() == 0) + { + return Collections.emptyList(); + } + // sort results + ArrayList orderedList = new ArrayList(childAssocRefs); + Collections.sort(orderedList); + + // list of results + int nthSibling = 0; + Iterator iterator = orderedList.iterator(); + while(iterator.hasNext()) + { + ChildAssociationRef childAssocRef = iterator.next(); + // does the qname match the pattern? + if (!qnamePattern.isMatch(childAssocRef.getQName()) || !typeQNamePattern.isMatch(childAssocRef.getTypeQName())) + { + // no match - remove + iterator.remove(); + } + else + { + childAssocRef.setNthSibling(nthSibling); + nthSibling++; + } + } + // done + return orderedList; + } + + public NodeRef getChildByName(NodeRef nodeRef, QName assocTypeQName, String childName) + { + Node node = getNodeNotNull(nodeRef); + ChildAssoc childAssoc = nodeDaoService.getChildAssoc(node, assocTypeQName, childName); + if (childAssoc != null) + { + return childAssoc.getChild().getNodeRef(); + } + else + { + return null; + } + } + + public ChildAssociationRef getPrimaryParent(NodeRef nodeRef) throws InvalidNodeRefException + { + Node node = getNodeNotNull(nodeRef); + // get the primary parent assoc + ChildAssoc assoc = nodeDaoService.getPrimaryParentAssoc(node); + + // done - the assoc may be null for a root node + ChildAssociationRef assocRef = null; + if (assoc == null) + { + assocRef = new ChildAssociationRef(null, null, null, nodeRef); + } + else + { + assocRef = assoc.getChildAssocRef(); + } + return assocRef; + } + + public AssociationRef createAssociation(NodeRef sourceRef, NodeRef targetRef, QName assocTypeQName) + throws InvalidNodeRefException, AssociationExistsException + { + // Invoke policy behaviours + invokeBeforeUpdateNode(sourceRef); + + Node sourceNode = getNodeNotNull(sourceRef); + Node targetNode = getNodeNotNull(targetRef); + // see if it exists + NodeAssoc assoc = nodeDaoService.getNodeAssoc(sourceNode, targetNode, assocTypeQName); + if (assoc != null) + { + throw new AssociationExistsException(sourceRef, targetRef, assocTypeQName); + } + // we are sure that the association doesn't exist - make it + assoc = nodeDaoService.newNodeAssoc(sourceNode, targetNode, assocTypeQName); + AssociationRef assocRef = assoc.getNodeAssocRef(); + + // Invoke policy behaviours + invokeOnUpdateNode(sourceRef); + invokeOnCreateAssociation(assocRef); + + return assocRef; + } + + public void removeAssociation(NodeRef sourceRef, NodeRef targetRef, QName assocTypeQName) + throws InvalidNodeRefException + { + Node sourceNode = getNodeNotNull(sourceRef); + Node targetNode = getNodeNotNull(targetRef); + // get the association + NodeAssoc assoc = nodeDaoService.getNodeAssoc(sourceNode, targetNode, assocTypeQName); + if (assoc == null) + { + // nothing to remove + return; + } + AssociationRef assocRef = assoc.getNodeAssocRef(); + + // Invoke policy behaviours + invokeBeforeUpdateNode(sourceRef); + + // delete it + nodeDaoService.deleteNodeAssoc(assoc); + + // Invoke policy behaviours + invokeOnUpdateNode(sourceRef); + invokeOnDeleteAssociation(assocRef); + } + + public List getTargetAssocs(NodeRef sourceRef, QNamePattern qnamePattern) + { + Node sourceNode = getNodeNotNull(sourceRef); + // get all assocs to target + Collection assocs = nodeDaoService.getTargetNodeAssocs(sourceNode); + List nodeAssocRefs = new ArrayList(assocs.size()); + for (NodeAssoc assoc : assocs) + { + // check qname pattern + if (!qnamePattern.isMatch(assoc.getTypeQName())) + { + continue; // the assoc name doesn't match the pattern given + } + nodeAssocRefs.add(assoc.getNodeAssocRef()); + } + // done + return nodeAssocRefs; + } + + public List getSourceAssocs(NodeRef targetRef, QNamePattern qnamePattern) + { + Node targetNode = getNodeNotNull(targetRef); + // get all assocs to source + Collection assocs = nodeDaoService.getSourceNodeAssocs(targetNode); + List nodeAssocRefs = new ArrayList(assocs.size()); + for (NodeAssoc assoc : assocs) + { + // check qname pattern + if (!qnamePattern.isMatch(assoc.getTypeQName())) + { + continue; // the assoc name doesn't match the pattern given + } + nodeAssocRefs.add(assoc.getNodeAssocRef()); + } + // done + return nodeAssocRefs; + } + + /** + * Recursive method used to build up paths from a given node to the root. + *

+ * Whilst walking up the hierarchy to the root, some nodes may have a root aspect. + * Everytime one of these is encountered, a new path is farmed off, but the method + * continues to walk up the hierarchy. + * + * @param currentNode the node to start from, i.e. the child node to work upwards from + * @param currentPath the path from the current node to the descendent that we started from + * @param completedPaths paths that have reached the root are added to this collection + * @param assocStack the parent-child relationships traversed whilst building the path. + * Used to detected cyclic relationships. + * @param primaryOnly true if only the primary parent association must be traversed. + * If this is true, then the only root is the top level node having no parents. + * @throws CyclicChildRelationshipException + */ + private void prependPaths( + final Node currentNode, + final Path currentPath, + Collection completedPaths, + Stack assocStack, + boolean primaryOnly) + throws CyclicChildRelationshipException + { + NodeRef currentNodeRef = currentNode.getNodeRef(); + // get the parent associations of the given node + Collection parentAssocs = currentNode.getParentAssocs(); + // does the node have parents + boolean hasParents = parentAssocs.size() > 0; + // does the current node have a root aspect? + boolean isRoot = hasAspect(currentNodeRef, ContentModel.ASPECT_ROOT); + boolean isStoreRoot = currentNode.getTypeQName().equals(ContentModel.TYPE_STOREROOT); + + // look for a root. If we only want the primary root, then ignore all but the top-level root. + if (isRoot && !(primaryOnly && hasParents)) // exclude primary search with parents present + { + // create a one-sided assoc ref for the root node and prepend to the stack + // this effectively spoofs the fact that the current node is not below the root + // - we put this assoc in as the first assoc in the path must be a one-sided + // reference pointing to the root node + ChildAssociationRef assocRef = new ChildAssociationRef( + null, + null, + null, + getRootNode(currentNode.getNodeRef().getStoreRef())); + // create a path to save and add the 'root' assoc + Path pathToSave = new Path(); + Path.ChildAssocElement first = null; + for (Path.Element element: currentPath) + { + if (first == null) + { + first = (Path.ChildAssocElement) element; + } + else + { + pathToSave.append(element); + } + } + if (first != null) + { + // mimic an association that would appear if the current node was below + // the root node + // or if first beneath the root node it will make the real thing + ChildAssociationRef updateAssocRef = new ChildAssociationRef( + isStoreRoot ? ContentModel.ASSOC_CHILDREN : first.getRef().getTypeQName(), + getRootNode(currentNode.getNodeRef().getStoreRef()), + first.getRef().getQName(), + first.getRef().getChildRef()); + Path.Element newFirst = new Path.ChildAssocElement(updateAssocRef); + pathToSave.prepend(newFirst); + } + + Path.Element element = new Path.ChildAssocElement(assocRef); + pathToSave.prepend(element); + + // store the path just built + completedPaths.add(pathToSave); + } + + if (parentAssocs.size() == 0 && !isRoot) + { + throw new RuntimeException("Node without parents does not have root aspect: " + + currentNodeRef); + } + // walk up each parent association + for (ChildAssoc assoc : parentAssocs) + { + // does the association already exist in the stack + if (assocStack.contains(assoc)) + { + // the association was present already + throw new CyclicChildRelationshipException( + "Cyclic parent-child relationship detected: \n" + + " current node: " + currentNode + "\n" + + " current path: " + currentPath + "\n" + + " next assoc: " + assoc, + assoc); + } + // do we consider only primary assocs? + if (primaryOnly && !assoc.getIsPrimary()) + { + continue; + } + // build a path element + NodeRef parentRef = assoc.getParent().getNodeRef(); + QName qname = assoc.getQname(); + NodeRef childRef = assoc.getChild().getNodeRef(); + boolean isPrimary = assoc.getIsPrimary(); + // build a real association reference + ChildAssociationRef assocRef = new ChildAssociationRef(assoc.getTypeQName(), parentRef, qname, childRef, isPrimary, -1); + // Ordering is not important here: We are building distinct paths upwards + Path.Element element = new Path.ChildAssocElement(assocRef); + // create a new path that builds on the current path + Path path = new Path(); + path.append(currentPath); + // prepend element + path.prepend(element); + // get parent node + Node parentNode = assoc.getParent(); + + // push the assoc stack, recurse and pop + assocStack.push(assoc); + prependPaths(parentNode, path, completedPaths, assocStack, primaryOnly); + assocStack.pop(); + } + // done + } + + /** + * @see #getPaths(NodeRef, boolean) + * @see #prependPaths(Node, Path, Collection, Stack, boolean) + */ + public Path getPath(NodeRef nodeRef) throws InvalidNodeRefException + { + List paths = getPaths(nodeRef, true); // checks primary path count + if (paths.size() == 1) + { + return paths.get(0); // we know there is only one + } + throw new RuntimeException("Primary path count not checked"); // checked by getPaths() + } + + /** + * When searching for primaryOnly == true, checks that there is exactly + * one path. + * @see #prependPaths(Node, Path, Collection, Stack, boolean) + */ + public List getPaths(NodeRef nodeRef, boolean primaryOnly) throws InvalidNodeRefException + { + // get the starting node + Node node = getNodeNotNull(nodeRef); + // create storage for the paths - only need 1 bucket if we are looking for the primary path + List paths = new ArrayList(primaryOnly ? 1 : 10); + // create an empty current path to start from + Path currentPath = new Path(); + // create storage for touched associations + Stack assocStack = new Stack(); + // call recursive method to sort it out + prependPaths(node, currentPath, paths, assocStack, primaryOnly); + + // check that for the primary only case we have exactly one path + if (primaryOnly && paths.size() != 1) + { + throw new RuntimeException("Node has " + paths.size() + " primary paths: " + nodeRef); + } + + // done + if (loggerPaths.isDebugEnabled()) + { + StringBuilder sb = new StringBuilder(256); + if (primaryOnly) + { + sb.append("Primary paths"); + } + else + { + sb.append("Paths"); + } + sb.append(" for node ").append(nodeRef); + for (Path path : paths) + { + sb.append("\n").append(" ").append(path); + } + loggerPaths.debug(sb); + } + return paths; + } + + private void archiveNode(NodeRef nodeRef, StoreRef archiveStoreRef) + { + Node node = getNodeNotNull(nodeRef); + ChildAssoc primaryParentAssoc = nodeDaoService.getPrimaryParentAssoc(node); + + // add the aspect + Set aspects = node.getAspects(); + aspects.add(ContentModel.ASPECT_ARCHIVED); + Map properties = node.getProperties(); + PropertyValue archivedByProperty = makePropertyValue( + dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_BY), + AuthenticationUtil.getCurrentUserName()); + properties.put(ContentModel.PROP_ARCHIVED_BY, archivedByProperty); + PropertyValue archivedDateProperty = makePropertyValue( + dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_DATE), + new Date()); + properties.put(ContentModel.PROP_ARCHIVED_DATE, archivedDateProperty); + PropertyValue archivedPrimaryParentNodeRefProperty = makePropertyValue( + dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC), + primaryParentAssoc.getChildAssocRef()); + properties.put(ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC, archivedPrimaryParentNodeRefProperty); + PropertyValue originalOwnerProperty = properties.get(ContentModel.PROP_OWNER); + PropertyValue originalCreatorProperty = properties.get(ContentModel.PROP_CREATOR); + if (originalOwnerProperty != null || originalCreatorProperty != null) + { + properties.put( + ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER, + originalOwnerProperty != null ? originalOwnerProperty : originalCreatorProperty); + } + + // change the node ownership + aspects.add(ContentModel.ASPECT_OWNABLE); + PropertyValue newOwnerProperty = makePropertyValue( + dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER), + AuthenticationUtil.getCurrentUserName()); + properties.put(ContentModel.PROP_OWNER, newOwnerProperty); + + // move the node + NodeRef archiveStoreRootNodeRef = getRootNode(archiveStoreRef); + moveNode( + nodeRef, + archiveStoreRootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedItem")); + + // get the IDs of all the node's primary children, including its own + Map nodesById = getNodeHierarchy(node, null); + + // Archive all the associations between the archived nodes and non-archived nodes + for (Node nodeToArchive : nodesById.values()) + { + archiveAssocs(nodeToArchive, nodesById); + } + + // the node reference has changed due to the store move + nodeRef = node.getNodeRef(); + } + + /** + * Performs all the necessary housekeeping involved in changing a node's store. + * This method cascades down through all the primary children of the node as + * well. + * + * @param node the node whose store is changing + * @param store the new store for the node + */ + private void moveNodeToStore(Node node, Store store) + { + // get the IDs of all the node's primary children, including its own + Map nodesById = getNodeHierarchy(node, null); + + // move each node into the archive store + for (Node nodeToMove : nodesById.values()) + { + NodeRef oldNodeRef = nodeToMove.getNodeRef(); + 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); + } + } + + /** + * Fill the map of all primary children below the given node. + * The given node will be added to the map and the method is recursive + * to all primary children. + * + * @param node the start of the hierarchy + * @param nodesById a map of nodes that will be reused as the return value + * @return Returns a map of nodes in the hierarchy keyed by their IDs + */ + private Map getNodeHierarchy(Node node, Map nodesById) + { + if (nodesById == null) + { + nodesById = new HashMap(23); + } + + Long id = node.getId(); + if (nodesById.containsKey(id)) + { + // this ID was already added - circular reference + logger.warn("Circular hierarchy found including node " + id); + return nodesById; + } + // add the node to the map + nodesById.put(id, node); + // recurse into the primary children + Collection childAssocs = nodeDaoService.getChildAssocs(node); + for (ChildAssoc childAssoc : childAssocs) + { + // cascade into primary associations + if (childAssoc.getIsPrimary()) + { + Node primaryChild = childAssoc.getChild(); + nodesById = getNodeHierarchy(primaryChild, nodesById); + } + } + return nodesById; + } + + /** + * Archive all associations to and from the given node, with the + * exception of associations to or from nodes in the given map. + *

+ * Primary parent associations are also ignored. + * + * @param node the node whose associations must be archived + * @param nodesById a map of nodes partaking in the archival process + */ + private void archiveAssocs(Node node, Map nodesById) + { + List childAssocsToDelete = new ArrayList(5); + // child associations + ArrayList archivedChildAssocRefs = new ArrayList(5); + Collection childAssocs = nodeDaoService.getChildAssocs(node); + for (ChildAssoc assoc : childAssocs) + { + Long relatedNodeId = assoc.getChild().getId(); + if (nodesById.containsKey(relatedNodeId)) + { + // a sibling in the archive process + continue; + } + childAssocsToDelete.add(assoc); + archivedChildAssocRefs.add(assoc.getChildAssocRef()); + } + // parent associations + ArrayList archivedParentAssocRefs = new ArrayList(5); + for (ChildAssoc assoc : node.getParentAssocs()) + { + Long relatedNodeId = assoc.getParent().getId(); + if (nodesById.containsKey(relatedNodeId)) + { + // a sibling in the archive process + continue; + } + else if (assoc.getIsPrimary()) + { + // ignore the primary parent as this is handled more specifically + continue; + } + childAssocsToDelete.add(assoc); + archivedParentAssocRefs.add(assoc.getChildAssocRef()); + } + + List nodeAssocsToDelete = new ArrayList(5); + // source associations + ArrayList archivedSourceAssocRefs = new ArrayList(5); + for (NodeAssoc assoc : nodeDaoService.getSourceNodeAssocs(node)) + { + Long relatedNodeId = assoc.getSource().getId(); + if (nodesById.containsKey(relatedNodeId)) + { + // a sibling in the archive process + continue; + } + nodeAssocsToDelete.add(assoc); + archivedSourceAssocRefs.add(assoc.getNodeAssocRef()); + } + // target associations + ArrayList archivedTargetAssocRefs = new ArrayList(5); + for (NodeAssoc assoc : nodeDaoService.getTargetNodeAssocs(node)) + { + Long relatedNodeId = assoc.getTarget().getId(); + if (nodesById.containsKey(relatedNodeId)) + { + // a sibling in the archive process + continue; + } + nodeAssocsToDelete.add(assoc); + archivedTargetAssocRefs.add(assoc.getNodeAssocRef()); + } + // delete child assocs + for (ChildAssoc assoc : childAssocsToDelete) + { + nodeDaoService.deleteChildAssoc(assoc, false); + } + // delete node assocs + for (NodeAssoc assoc : nodeAssocsToDelete) + { + nodeDaoService.deleteNodeAssoc(assoc); + } + + // add archived aspect + node.getAspects().add(ContentModel.ASPECT_ARCHIVED_ASSOCS); + // set properties + Map properties = node.getProperties(); + + if (archivedParentAssocRefs.size() > 0) + { + PropertyDefinition propertyDef = dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_PARENT_ASSOCS); + PropertyValue propertyValue = makePropertyValue(propertyDef, archivedParentAssocRefs); + properties.put(ContentModel.PROP_ARCHIVED_PARENT_ASSOCS, propertyValue); + } + if (archivedChildAssocRefs.size() > 0) + { + PropertyDefinition propertyDef = dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_CHILD_ASSOCS); + PropertyValue propertyValue = makePropertyValue(propertyDef, archivedChildAssocRefs); + properties.put(ContentModel.PROP_ARCHIVED_CHILD_ASSOCS, propertyValue); + } + if (archivedSourceAssocRefs.size() > 0) + { + PropertyDefinition propertyDef = dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_SOURCE_ASSOCS); + PropertyValue propertyValue = makePropertyValue(propertyDef, archivedSourceAssocRefs); + properties.put(ContentModel.PROP_ARCHIVED_SOURCE_ASSOCS, propertyValue); + } + if (archivedTargetAssocRefs.size() > 0) + { + PropertyDefinition propertyDef = dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_TARGET_ASSOCS); + PropertyValue propertyValue = makePropertyValue(propertyDef, archivedTargetAssocRefs); + properties.put(ContentModel.PROP_ARCHIVED_TARGET_ASSOCS, propertyValue); + } + } + + public NodeRef getStoreArchiveNode(StoreRef storeRef) + { + StoreRef archiveStoreRef = storeArchiveMap.getArchiveMap().get(storeRef); + if (archiveStoreRef == null) + { + // no mapping for the given store + return null; + } + else + { + return getRootNode(archiveStoreRef); + } + } + + public NodeRef restoreNode(NodeRef archivedNodeRef, NodeRef destinationParentNodeRef, QName assocTypeQName, QName assocQName) + { + Node archivedNode = getNodeNotNull(archivedNodeRef); + Set aspects = archivedNode.getAspects(); + Map properties = archivedNode.getProperties(); + // the node must be a top-level archive node + if (!aspects.contains(ContentModel.ASPECT_ARCHIVED)) + { + throw new AlfrescoRuntimeException("The node to archive is not an archive node"); + } + ChildAssociationRef originalPrimaryParentAssocRef = (ChildAssociationRef) makeSerializableValue( + dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC), + properties.get(ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC)); + PropertyValue originalOwnerProperty = properties.get(ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER); + // remove the aspect archived aspect + aspects.remove(ContentModel.ASPECT_ARCHIVED); + properties.remove(ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC); + properties.remove(ContentModel.PROP_ARCHIVED_BY); + properties.remove(ContentModel.PROP_ARCHIVED_DATE); + properties.remove(ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER); + + // restore the original ownership + if (originalOwnerProperty != null) + { + aspects.add(ContentModel.ASPECT_OWNABLE); + properties.put(ContentModel.PROP_OWNER, originalOwnerProperty); + } + + if (destinationParentNodeRef == null) + { + // we must restore to the original location + destinationParentNodeRef = originalPrimaryParentAssocRef.getParentRef(); + } + // check the associations + if (assocTypeQName == null) + { + assocTypeQName = originalPrimaryParentAssocRef.getTypeQName(); + } + if (assocQName == null) + { + assocQName = originalPrimaryParentAssocRef.getQName(); + } + + // move the node to the target parent, which may or may not be the original parent + moveNode( + archivedNodeRef, + destinationParentNodeRef, + assocTypeQName, + assocQName); + + // get the IDs of all the node's primary children, including its own + Map restoredNodesById = getNodeHierarchy(archivedNode, null); + // Restore the archived associations, if required + for (Node restoredNode : restoredNodesById.values()) + { + restoreAssocs(restoredNode); + } + + // the node reference has changed due to the store move + NodeRef restoredNodeRef = archivedNode.getNodeRef(); + + // done + if (logger.isDebugEnabled()) + { + logger.debug("Restored node: \n" + + " original noderef: " + archivedNodeRef + "\n" + + " restored noderef: " + restoredNodeRef + "\n" + + " new parent: " + destinationParentNodeRef); + } + return restoredNodeRef; + } + + private void restoreAssocs(Node node) + { + NodeRef nodeRef = node.getNodeRef(); + // set properties + Map properties = node.getProperties(); + + // restore parent associations + Collection parentAssocRefs = (Collection) getProperty( + nodeRef, + ContentModel.PROP_ARCHIVED_PARENT_ASSOCS); + if (parentAssocRefs != null) + { + for (ChildAssociationRef assocRef : parentAssocRefs) + { + NodeRef parentNodeRef = assocRef.getParentRef(); + if (!exists(parentNodeRef)) + { + continue; + } + Node parentNode = getNodeNotNull(parentNodeRef); + // get the name to use for the unique child check + QName assocTypeQName = assocRef.getTypeQName(); + nodeDaoService.newChildAssoc( + parentNode, + node, + assocRef.isPrimary(), + assocTypeQName, + assocRef.getQName()); + } + properties.remove(ContentModel.PROP_ARCHIVED_PARENT_ASSOCS); + } + + // make sure that the node name uniqueness is enforced + setChildUniqueName(node); + + // restore child associations + Collection childAssocRefs = (Collection) getProperty( + nodeRef, + ContentModel.PROP_ARCHIVED_CHILD_ASSOCS); + if (childAssocRefs != null) + { + for (ChildAssociationRef assocRef : childAssocRefs) + { + NodeRef childNodeRef = assocRef.getChildRef(); + if (!exists(childNodeRef)) + { + continue; + } + Node childNode = getNodeNotNull(childNodeRef); + QName assocTypeQName = assocRef.getTypeQName(); + // get the name to use for the unique child check + nodeDaoService.newChildAssoc( + node, + childNode, + assocRef.isPrimary(), + assocTypeQName, + assocRef.getQName()); + // ensure that the name uniqueness is enforced for the child node + setChildUniqueName(childNode); + } + properties.remove(ContentModel.PROP_ARCHIVED_CHILD_ASSOCS); + } + // restore source associations + Collection sourceAssocRefs = (Collection) getProperty( + nodeRef, + ContentModel.PROP_ARCHIVED_SOURCE_ASSOCS); + if (sourceAssocRefs != null) + { + for (AssociationRef assocRef : sourceAssocRefs) + { + NodeRef sourceNodeRef = assocRef.getSourceRef(); + if (!exists(sourceNodeRef)) + { + continue; + } + Node sourceNode = getNodeNotNull(sourceNodeRef); + nodeDaoService.newNodeAssoc(sourceNode, node, assocRef.getTypeQName()); + } + properties.remove(ContentModel.PROP_ARCHIVED_SOURCE_ASSOCS); + } + // restore target associations + Collection targetAssocRefs = (Collection) getProperty( + nodeRef, + ContentModel.PROP_ARCHIVED_TARGET_ASSOCS); + if (targetAssocRefs != null) + { + for (AssociationRef assocRef : targetAssocRefs) + { + NodeRef targetNodeRef = assocRef.getTargetRef(); + if (!exists(targetNodeRef)) + { + continue; + } + Node targetNode = getNodeNotNull(targetNodeRef); + nodeDaoService.newNodeAssoc(node, targetNode, assocRef.getTypeQName()); + } + properties.remove(ContentModel.PROP_ARCHIVED_TARGET_ASSOCS); + } + // remove the aspect + node.getAspects().remove(ContentModel.ASPECT_ARCHIVED_ASSOCS); + } + + /** + * Checks the dictionary's definition of the association to assign a unique name to the child node. + * + * @param assocTypeQName the type of the child association + * @param childNode the child node being added. The name will be extracted from it, if necessary. + */ + private void setChildUniqueName(Node childNode) + { + // get the name property + Map properties = childNode.getProperties(); + PropertyValue nameValue = properties.get(ContentModel.PROP_NAME); + String useName = null; + if (nameValue == null) + { + // no name has been assigned, so assign the ID of the child node + useName = childNode.getUuid(); + } + else + { + useName = (String) nameValue.getValue(DataTypeDefinition.TEXT); + } + // get all the parent assocs + Collection parentAssocs = childNode.getParentAssocs(); + for (ChildAssoc assoc : parentAssocs) + { + QName assocTypeQName = assoc.getTypeQName(); + AssociationDefinition assocDef = dictionaryService.getAssociation(assocTypeQName); + if (!assocDef.isChild()) + { + throw new DataIntegrityViolationException("Child association has non-child type: " + assoc.getId()); + } + ChildAssociationDefinition childAssocDef = (ChildAssociationDefinition) assocDef; + if (childAssocDef.getDuplicateChildNamesAllowed()) + { + // the name is irrelevant, so it doesn't need to be put into the unique key + nodeDaoService.setChildNameUnique(assoc, null); + } + else + { + nodeDaoService.setChildNameUnique(assoc, useName); + } + } + // done + if (logger.isDebugEnabled()) + { + logger.debug( + "Unique name set for all " + parentAssocs.size() + " parent associations: \n" + + " name: " + useName); + } + } +} 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 b2872cdeaf..b7f35e16ed 100644 --- a/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java +++ b/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java @@ -1,884 +1,951 @@ -/* - * 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.node.db.hibernate; - -import java.io.Serializable; -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -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; -import org.alfresco.model.ContentModel; -import org.alfresco.repo.domain.ChildAssoc; -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.Server; -import org.alfresco.repo.domain.Store; -import org.alfresco.repo.domain.StoreKey; -import org.alfresco.repo.domain.Transaction; -import org.alfresco.repo.domain.hibernate.ChildAssocImpl; -import org.alfresco.repo.domain.hibernate.NodeAssocImpl; -import org.alfresco.repo.domain.hibernate.NodeImpl; -import org.alfresco.repo.domain.hibernate.NodeStatusImpl; -import org.alfresco.repo.domain.hibernate.ServerImpl; -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.TransactionalDao; -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.namespace.QName; -import org.alfresco.util.GUID; -import org.hibernate.ObjectDeletedException; -import org.hibernate.Query; -import org.hibernate.Session; -import org.springframework.dao.DataAccessException; -import org.springframework.dao.DataIntegrityViolationException; -import org.springframework.orm.hibernate3.HibernateCallback; -import org.springframework.orm.hibernate3.support.HibernateDaoSupport; - -/** - * Hibernate-specific implementation of the persistence-independent node DAO interface - * - * @author Derek Hulley - */ -public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements NodeDaoService, TransactionalDao -{ - private static final String QUERY_GET_ALL_STORES = "store.GetAllStores"; - private static final String UPDATE_SET_CHILD_ASSOC_NAME = "node.updateChildAssocName"; - private static final String QUERY_GET_CHILD_ASSOCS = "node.GetChildAssocs"; - private static final String QUERY_GET_CHILD_ASSOC_BY_TYPE_AND_NAME = "node.GetChildAssocByTypeAndName"; - private static final String QUERY_GET_CHILD_ASSOC_REFS = "node.GetChildAssocRefs"; - private static final String QUERY_GET_NODE_ASSOC = "node.GetNodeAssoc"; - 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_SERVER_BY_IPADDRESS = "server.getServerByIpAddress"; - - /** a uuid identifying this unique instance */ - private final String uuid; - - private final ReadLock serverReadLock; - private final WriteLock serverWriteLock; - private Server server; - - /** - * - */ - public HibernateNodeDaoServiceImpl() - { - this.uuid = GUID.generate(); - - ReentrantReadWriteLock serverReadWriteLock = new ReentrantReadWriteLock(); - serverReadLock = serverReadWriteLock.readLock(); - serverWriteLock = serverReadWriteLock.writeLock(); - } - - /** - * Checks equality by type and uuid - */ - public boolean equals(Object obj) - { - if (obj == null) - { - return false; - } - else if (!(obj instanceof HibernateNodeDaoServiceImpl)) - { - return false; - } - HibernateNodeDaoServiceImpl that = (HibernateNodeDaoServiceImpl) obj; - return this.uuid.equals(that.uuid); - } - - /** - * @see #uuid - */ - public int hashCode() - { - return uuid.hashCode(); - } - - /** - * Gets/creates the server instance to use for the life of this instance - */ - private Server getServer() - { - // get readlock - serverReadLock.lock(); - try - { - if (server != null) - { - return server; - } - } - finally - { - serverReadLock.unlock(); - } - // get the write lock - serverWriteLock.lock(); - try - { - final String ipAddress = InetAddress.getLocalHost().getHostAddress(); - HibernateCallback callback = new HibernateCallback() - { - public Object doInHibernate(Session session) - { - Query query = session - .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_SERVER_BY_IPADDRESS) - .setString("ipAddress", ipAddress); - return query.uniqueResult(); - } - }; - server = (Server) getHibernateTemplate().execute(callback); - // create it if it doesn't exist - if (server == null) - { - server = new ServerImpl(); - server.setIpAddress(ipAddress); - try - { - getSession().save(server); - } - catch (DataIntegrityViolationException e) - { - // get it again - server = (Server) getHibernateTemplate().execute(callback); - if (server == null) - { - throw new AlfrescoRuntimeException("Unable to create server instance: " + ipAddress); - } - } - } - 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"; - private Transaction getCurrentTransaction() - { - Transaction transaction = null; - Serializable txnId = (Serializable) AlfrescoTransactionSupport.getResource(RESOURCE_KEY_TRANSACTION_ID); - if (txnId == null) - { - // no transaction instance has been bound to the transaction - transaction = new TransactionImpl(); - transaction.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId()); - transaction.setServer(getServer()); - txnId = getHibernateTemplate().save(transaction); - // bind the id - AlfrescoTransactionSupport.bindResource(RESOURCE_KEY_TRANSACTION_ID, txnId); - } - else - { - transaction = (Transaction) getHibernateTemplate().get(TransactionImpl.class, txnId); - } - return transaction; - } - - /** - * Does this Session contain any changes which must be - * synchronized with the store? - * - * @return true => changes are pending - */ - public boolean isDirty() - { - // create a callback for the task - HibernateCallback callback = new HibernateCallback() - { - public Object doInHibernate(Session session) - { - return session.isDirty(); - } - }; - // execute the callback - return ((Boolean)getHibernateTemplate().execute(callback)).booleanValue(); - } - - /** - * Just flushes the session - */ - public void flush() - { - getSession().flush(); - } - - /** - * @see #QUERY_GET_ALL_STORES - */ - @SuppressWarnings("unchecked") - public List getStores() - { - HibernateCallback callback = new HibernateCallback() - { - public Object doInHibernate(Session session) - { - Query query = session.getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_ALL_STORES); - return query.list(); - } - }; - List queryResults = (List) getHibernateTemplate().execute(callback); - // done - return queryResults; - } - - /** - * Ensures that the store protocol/identifier combination is unique - */ - public Store createStore(String protocol, String identifier) - { - // ensure that the name isn't in use - Store store = getStore(protocol, identifier); - if (store != null) - { - throw new RuntimeException("A store already exists: \n" + - " protocol: " + protocol + "\n" + - " identifier: " + identifier + "\n" + - " store: " + store); - } - - store = new StoreImpl(); - // set key - store.setKey(new StoreKey(protocol, identifier)); - // persist so that it is present in the hibernate cache - getHibernateTemplate().save(store); - // create and assign a root node - Node rootNode = newNode( - store, - GUID.generate(), - ContentModel.TYPE_STOREROOT); - store.setRootNode(rootNode); - // done - return store; - } - - public Store getStore(String protocol, String identifier) - { - StoreKey storeKey = new StoreKey(protocol, identifier); - Store store = (Store) getHibernateTemplate().get(StoreImpl.class, storeKey); - // done - return store; - } - - /** - * Fetch the node status, if it exists - */ - public NodeStatus getNodeStatus(NodeRef nodeRef, boolean create) - { - NodeKey nodeKey = new NodeKey(nodeRef); - NodeStatus status = null; - try - { - status = (NodeStatus) getHibernateTemplate().get(NodeStatusImpl.class, nodeKey); - } - catch (DataAccessException e) - { - if (e.contains(ObjectDeletedException.class)) - { - // the object no longer exists - return null; - } - throw e; - } - // create if necessary - if (status == null && create) - { - status = new NodeStatusImpl(); - status.setKey(nodeKey); - status.setTransaction(getCurrentTransaction()); - getHibernateTemplate().save(status); - } - // done - return status; - } - - public void recordChangeId(NodeRef nodeRef) - { - NodeKey key = new NodeKey(nodeRef); - - NodeStatus status = (NodeStatus) getHibernateTemplate().get(NodeStatusImpl.class, key); - if (status == null) - { - // the node never existed or the status was deleted - return; - } - else - { - status.getTransaction().setChangeTxnId(AlfrescoTransactionSupport.getTransactionId()); - } - } - - public Node newNode(Store store, String uuid, QName nodeTypeQName) throws InvalidTypeException - { - NodeKey key = new NodeKey(store.getKey(), uuid); - - // create (or reuse) the mandatory node status - NodeStatus status = (NodeStatus) getHibernateTemplate().get(NodeStatusImpl.class, key); - if (status == null) - { - status = new NodeStatusImpl(); - status.setKey(key); - } - else - { - // The node existed at some point. - // Although unlikely, it is possible that the node was deleted in this transaction. - // If that is the case, then the session has to be flushed so that the database - // constraints aren't violated as the node creation will write to the database to - // get an ID - if (status.getTransaction().getChangeTxnId().equals(AlfrescoTransactionSupport.getTransactionId())) - { - // flush - getHibernateTemplate().flush(); - } - } - - // build a concrete node based on a bootstrap type - Node node = new NodeImpl(); - // set other required properties - node.setStore(store); - node.setUuid(uuid); - node.setTypeQName(nodeTypeQName); - // persist the node - getHibernateTemplate().save(node); - - // set required status properties - status.setNode(node); - // assign a transaction - if (status.getTransaction() == null) - { - status.setTransaction(getCurrentTransaction()); - } - // persist the nodestatus - getHibernateTemplate().save(status); - - // done - return node; - } - - public Node getNode(NodeRef nodeRef) - { - // get it via the node status - NodeStatus status = getNodeStatus(nodeRef, false); - if (status == null) - { - // no status implies no node - return null; - } - else - { - // a status may have a node - Node node = status.getNode(); - return node; - } - } - - /** - * Manually ensures that all cascading of associations is taken care of - */ - public void deleteNode(Node node, boolean cascade) - { - // delete all parent assocs - Collection parentAssocs = node.getParentAssocs(); - parentAssocs = new ArrayList(parentAssocs); - for (ChildAssoc assoc : parentAssocs) - { - deleteChildAssoc(assoc, false); // we don't cascade upwards - } - // delete all child assocs - Collection childAssocs = getChildAssocs(node); - childAssocs = new ArrayList(childAssocs); - for (ChildAssoc assoc : childAssocs) - { - deleteChildAssoc(assoc, cascade); // potentially cascade downwards - } - // delete all node associations to and from - List nodeAssocs = getNodeAssocsToAndFrom(node); - for (NodeAssoc assoc : nodeAssocs) - { - getHibernateTemplate().delete(assoc); - } - // update the node status - NodeRef nodeRef = node.getNodeRef(); - NodeStatus nodeStatus = getNodeStatus(nodeRef, true); - nodeStatus.setNode(null); - nodeStatus.getTransaction().setChangeTxnId(AlfrescoTransactionSupport.getTransactionId()); - // finally delete the node - getHibernateTemplate().delete(node); - // flush to ensure constraints can't be violated - getSession().flush(); - // done - } - - private long getCrc(String str) - { - CRC32 crc = new CRC32(); - crc.update(str.getBytes()); - return crc.getValue(); - } - - private static final String TRUNCATED_NAME_INDICATOR = "~~~"; - private String getShortName(String str) - { - int length = str.length(); - if (length <= 50) - { - return str; - } - else - { - StringBuilder ret = new StringBuilder(50); - ret.append(str.substring(0, 47)).append(TRUNCATED_NAME_INDICATOR); - return ret.toString(); - } - } - - public ChildAssoc newChildAssoc( - Node parentNode, - Node childNode, - boolean isPrimary, - QName assocTypeQName, - QName qname) - { - /* - * This initial child association creation will fail IFF there is already - * an association of the given type and name between the two nodes. For new association - * creation, this can only occur if two transactions attempt to create a secondary - * child association between the same two nodes. As this is unlikely, it is - * appropriate to just throw a runtime exception and let the second transaction - * fail. - * - * We don't need to flush the session beforehand as there won't be any deletions - * of the assocation pending. The association deletes, on the other hand, have - * to flush early in order to ensure that the database unique index isn't violated - * if the association is recreated subsequently. - */ - - // assign a random name to the node - String randomName = GUID.generate(); - - ChildAssoc assoc = new ChildAssocImpl(); - assoc.setTypeQName(assocTypeQName); - assoc.setChildNodeName(randomName); - assoc.setChildNodeNameCrc(-1L); // random names compete only with each other - assoc.setQname(qname); - assoc.setIsPrimary(isPrimary); - assoc.buildAssociation(parentNode, childNode); - // persist it, catching the duplicate child name - getHibernateTemplate().save(assoc); - // done - return assoc; - } - - public void setChildNameUnique(final ChildAssoc childAssoc, String childName) - { - /* - * As the Hibernate session is rendered useless when an exception is - * bubbled up, we go direct to the database to update the child association. - * This preserves the session and client code can catch the resulting - * exception and react to it whilst in the same transaction. - * - * We ensure that case-insensitivity is maintained by persisting - * the lowercase version of the child node name. - */ - - String childNameNew = null; - long crc = -1; - if (childName == null) - { - // random names compete only with each other, i.e. not at all - childNameNew = GUID.generate(); - crc = -1; - } - else - { - // assigned names compete exactly - childNameNew = childName.toLowerCase(); - crc = getCrc(childNameNew); - } - - final String childNameNewShort = getShortName(childNameNew); - final long childNameNewCrc = crc; - - // check if the name has changed - if (childAssoc.getChildNodeNameCrc() == childNameNewCrc) - { - if (childAssoc.getChildNodeName().equals(childNameNewShort)) - { - // nothing changed - return; - } - } - - HibernateCallback callback = new HibernateCallback() - { - public Object doInHibernate(Session session) - { - session.flush(); - Query query = session - .getNamedQuery(HibernateNodeDaoServiceImpl.UPDATE_SET_CHILD_ASSOC_NAME) - .setString("newName", childNameNewShort) - .setLong("newNameCrc", childNameNewCrc) - .setLong("childAssocId", childAssoc.getId()); - return (Integer) query.executeUpdate(); - } - }; - try - { - Integer count = (Integer) getHibernateTemplate().execute(callback); - // refresh the entity directly - getHibernateTemplate().refresh(childAssoc); - if (count.intValue() == 0) - { - if (logger.isDebugEnabled()) - { - logger.debug("ChildAssoc not updated: " + childAssoc.getId()); - } - } - } - catch (DataIntegrityViolationException e) - { - NodeRef parentNodeRef = childAssoc.getParent().getNodeRef(); - QName assocTypeQName = childAssoc.getTypeQName(); - throw new DuplicateChildNodeNameException(parentNodeRef, assocTypeQName, childName); - } - } - - @SuppressWarnings("unchecked") - public Collection getChildAssocs(final Node parentNode) - { - HibernateCallback callback = new HibernateCallback() - { - public Object doInHibernate(Session session) - { - Query query = session - .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_CHILD_ASSOCS) - .setLong("parentId", parentNode.getId()); - return query.list(); - } - }; - List queryResults = (List) getHibernateTemplate().execute(callback); - return queryResults; - } - - @SuppressWarnings("unchecked") - public Collection getChildAssocRefs(final Node parentNode) - { - HibernateCallback callback = new HibernateCallback() - { - public Object doInHibernate(Session session) - { - Query query = session - .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_CHILD_ASSOC_REFS) - .setLong("parentId", parentNode.getId()); - return query.list(); - } - }; - List queryResults = (List) getHibernateTemplate().execute(callback); - Collection refs = new ArrayList(queryResults.size()); - NodeRef parentNodeRef = parentNode.getNodeRef(); - for (Object[] row : queryResults) - { - String childProtocol = (String) row[5]; - String childIdentifier = (String) row[6]; - String childUuid = (String) row[7]; - NodeRef childNodeRef = new NodeRef(new StoreRef(childProtocol, childIdentifier), childUuid); - QName assocTypeQName = (QName) row[0]; - QName assocQName = (QName) row[1]; - Boolean assocIsPrimary = (Boolean) row[2]; - Integer assocIndex = (Integer) row[3]; - ChildAssociationRef assocRef = new ChildAssociationRef( - assocTypeQName, - parentNodeRef, - assocQName, - childNodeRef, - assocIsPrimary.booleanValue(), - assocIndex.intValue()); - refs.add(assocRef); - } - return refs; - } - - public ChildAssoc getChildAssoc( - Node parentNode, - Node childNode, - QName assocTypeQName, - QName qname) - { - ChildAssociationRef childAssocRef = new ChildAssociationRef( - assocTypeQName, - parentNode.getNodeRef(), - qname, - childNode.getNodeRef()); - // get all the parent's child associations - Collection assocs = getChildAssocs(parentNode); - // hunt down the desired assoc - for (ChildAssoc assoc : assocs) - { - // is it a match? - if (!assoc.getChildAssocRef().equals(childAssocRef)) // not a match - { - continue; - } - else - { - return assoc; - } - } - // not found - return null; - } - - public ChildAssoc getChildAssoc(final Node parentNode, final QName assocTypeQName, final String childName) - { - HibernateCallback callback = new HibernateCallback() - { - public Object doInHibernate(Session session) - { - String childNameLower = childName.toLowerCase(); - String childNameShort = getShortName(childNameLower); - long childNameLowerCrc = getCrc(childNameLower); - Query query = session - .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_CHILD_ASSOC_BY_TYPE_AND_NAME) - .setLong("parentId", parentNode.getId()) - .setParameter("typeQName", assocTypeQName) - .setParameter("childNodeName", childNameShort) - .setLong("childNodeNameCrc", childNameLowerCrc); - return query.uniqueResult(); - } - }; - ChildAssoc childAssoc = (ChildAssoc) getHibernateTemplate().execute(callback); - return childAssoc; - } - - /** - * Manually enforces cascade deletions down primary associations - */ - public void deleteChildAssoc(ChildAssoc assoc, boolean cascade) - { - Node childNode = assoc.getChild(); - - // maintain inverse association sets - assoc.removeAssociation(); - // remove instance - getHibernateTemplate().delete(assoc); - - if (cascade && assoc.getIsPrimary()) // the assoc is primary - { - // delete the child node - deleteNode(childNode, cascade); - /* - * The child node deletion will cascade delete all assocs to - * and from it, but we have safely removed this one, so no - * duplicate call will be received to do this - */ - } - - // To ensure the validity of the constraint enforcement by the database, - // we have to flush here - getSession().flush(); - } - - public ChildAssoc getPrimaryParentAssoc(Node node) - { - // get the assocs pointing to the node - Collection parentAssocs = node.getParentAssocs(); - ChildAssoc primaryAssoc = null; - for (ChildAssoc assoc : parentAssocs) - { - // ignore non-primary assocs - if (!assoc.getIsPrimary()) - { - continue; - } - else if (primaryAssoc != null) - { - // we have more than one somehow - throw new DataIntegrityViolationException( - "Multiple primary associations: \n" + - " child: " + node + "\n" + - " first primary assoc: " + primaryAssoc + "\n" + - " second primary assoc: " + assoc); - } - primaryAssoc = assoc; - // we keep looping to hunt out data integrity issues - } - // did we find a primary assoc? - if (primaryAssoc == null) - { - // the only condition where this is allowed is if the given node is a root node - Store store = node.getStore(); - Node rootNode = store.getRootNode(); - if (rootNode == null) - { - // a store without a root node - the entire store is hosed - throw new DataIntegrityViolationException("Store has no root node: \n" + - " store: " + store); - } - if (!rootNode.equals(node)) - { - // it wasn't the root node - throw new DataIntegrityViolationException("Non-root node has no primary parent: \n" + - " child: " + node); - } - } - // done - return primaryAssoc; - } - - public NodeAssoc newNodeAssoc(Node sourceNode, Node targetNode, QName assocTypeQName) - { - NodeAssoc assoc = new NodeAssocImpl(); - assoc.setTypeQName(assocTypeQName); - assoc.buildAssociation(sourceNode, targetNode); - // persist - try - { - getHibernateTemplate().save(assoc); - } - catch (DataIntegrityViolationException e) - { - throw new AssociationExistsException( - sourceNode.getNodeRef(), - targetNode.getNodeRef(), - assocTypeQName, - e); - } - // done - return assoc; - } - - @SuppressWarnings("unchecked") - public List getNodeAssocsToAndFrom(final Node node) - { - HibernateCallback callback = new HibernateCallback() - { - public Object doInHibernate(Session session) - { - Query query = session - .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_NODE_ASSOCS_TO_AND_FROM) - .setLong("nodeId", node.getId()); - return query.list(); - } - }; - List results = (List) getHibernateTemplate().execute(callback); - return results; - } - - public NodeAssoc getNodeAssoc( - final Node sourceNode, - final Node targetNode, - final QName assocTypeQName) - { - HibernateCallback callback = new HibernateCallback() - { - public Object doInHibernate(Session session) - { - Query query = session - .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_NODE_ASSOC) - .setLong("sourceId", sourceNode.getId()) - .setLong("targetId", targetNode.getId()) - .setParameter("assocTypeQName", assocTypeQName); - return query.uniqueResult(); - } - }; - NodeAssoc result = (NodeAssoc) getHibernateTemplate().execute(callback); - return result; - } - - @SuppressWarnings("unchecked") - public List getTargetNodeAssocs(final Node sourceNode) - { - HibernateCallback callback = new HibernateCallback() - { - public Object doInHibernate(Session session) - { - Query query = session - .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_TARGET_ASSOCS) - .setLong("sourceId", sourceNode.getId()); - return query.list(); - } - }; - List queryResults = (List) getHibernateTemplate().execute(callback); - return queryResults; - } - - @SuppressWarnings("unchecked") - public List getSourceNodeAssocs(final Node targetNode) - { - HibernateCallback callback = new HibernateCallback() - { - public Object doInHibernate(Session session) - { - Query query = session - .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_SOURCE_ASSOCS) - .setLong("targetId", targetNode.getId()); - return query.list(); - } - }; - List queryResults = (List) getHibernateTemplate().execute(callback); - return queryResults; - } - - public void deleteNodeAssoc(NodeAssoc assoc) - { - // Remove instance - getHibernateTemplate().delete(assoc); - // Flush to ensure that the database constraints aren't violated if the assoc - // is recreated in the transaction - getSession().flush(); - } - - @SuppressWarnings("unchecked") - public List getContentDataStrings() - { - HibernateCallback callback = new HibernateCallback() - { - public Object doInHibernate(Session session) - { - Query query = session.getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_CONTENT_DATA_STRINGS); - return query.list(); - } - }; - List queryResults = (List) getHibernateTemplate().execute(callback); - return queryResults; - } -} \ No newline at end of file +/* + * 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.node.db.hibernate; + +import java.io.Serializable; +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +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; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.domain.ChildAssoc; +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.Server; +import org.alfresco.repo.domain.Store; +import org.alfresco.repo.domain.StoreKey; +import org.alfresco.repo.domain.Transaction; +import org.alfresco.repo.domain.hibernate.ChildAssocImpl; +import org.alfresco.repo.domain.hibernate.NodeAssocImpl; +import org.alfresco.repo.domain.hibernate.NodeImpl; +import org.alfresco.repo.domain.hibernate.NodeStatusImpl; +import org.alfresco.repo.domain.hibernate.ServerImpl; +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.TransactionalDao; +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.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.Session; +import org.springframework.dao.DataAccessException; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.orm.hibernate3.HibernateCallback; +import org.springframework.orm.hibernate3.support.HibernateDaoSupport; + +/** + * Hibernate-specific implementation of the persistence-independent node DAO interface + * + * @author Derek Hulley + */ +public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements NodeDaoService, TransactionalDao +{ + private static final String QUERY_GET_ALL_STORES = "store.GetAllStores"; + private static final String UPDATE_SET_CHILD_ASSOC_NAME = "node.updateChildAssocName"; + private static final String QUERY_GET_CHILD_ASSOCS = "node.GetChildAssocs"; + private static final String QUERY_GET_CHILD_ASSOC_BY_TYPE_AND_NAME = "node.GetChildAssocByTypeAndName"; + private static final String QUERY_GET_CHILD_ASSOC_REFS = "node.GetChildAssocRefs"; + private static final String QUERY_GET_NODE_ASSOC = "node.GetNodeAssoc"; + 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_SERVER_BY_IPADDRESS = "server.getServerByIpAddress"; + + private static Log logger = LogFactory.getLog(HibernateNodeDaoServiceImpl.class); + + /** a uuid identifying this unique instance */ + private final String uuid; + + private final ReadLock serverReadLock; + private final WriteLock serverWriteLock; + private Server server; + + /** + * + */ + public HibernateNodeDaoServiceImpl() + { + this.uuid = GUID.generate(); + + ReentrantReadWriteLock serverReadWriteLock = new ReentrantReadWriteLock(); + serverReadLock = serverReadWriteLock.readLock(); + serverWriteLock = serverReadWriteLock.writeLock(); + } + + /** + * Checks equality by type and uuid + */ + public boolean equals(Object obj) + { + if (obj == null) + { + return false; + } + else if (!(obj instanceof HibernateNodeDaoServiceImpl)) + { + return false; + } + HibernateNodeDaoServiceImpl that = (HibernateNodeDaoServiceImpl) obj; + return this.uuid.equals(that.uuid); + } + + /** + * @see #uuid + */ + public int hashCode() + { + return uuid.hashCode(); + } + + /** + * Gets/creates the server instance to use for the life of this instance + */ + private Server getServer() + { + // get readlock + serverReadLock.lock(); + try + { + if (server != null) + { + return server; + } + } + finally + { + serverReadLock.unlock(); + } + // get the write lock + serverWriteLock.lock(); + try + { + final String ipAddress = InetAddress.getLocalHost().getHostAddress(); + HibernateCallback callback = new HibernateCallback() + { + public Object doInHibernate(Session session) + { + Query query = session + .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_SERVER_BY_IPADDRESS) + .setString("ipAddress", ipAddress); + return query.uniqueResult(); + } + }; + server = (Server) getHibernateTemplate().execute(callback); + // create it if it doesn't exist + if (server == null) + { + server = new ServerImpl(); + server.setIpAddress(ipAddress); + try + { + getSession().save(server); + } + catch (DataIntegrityViolationException e) + { + // get it again + server = (Server) getHibernateTemplate().execute(callback); + if (server == null) + { + throw new AlfrescoRuntimeException("Unable to create server instance: " + ipAddress); + } + } + } + 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"; + private Transaction getCurrentTransaction() + { + Transaction transaction = null; + Serializable txnId = (Serializable) AlfrescoTransactionSupport.getResource(RESOURCE_KEY_TRANSACTION_ID); + if (txnId == null) + { + // no transaction instance has been bound to the transaction + transaction = new TransactionImpl(); + transaction.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId()); + transaction.setServer(getServer()); + txnId = getHibernateTemplate().save(transaction); + // bind the id + AlfrescoTransactionSupport.bindResource(RESOURCE_KEY_TRANSACTION_ID, txnId); + } + else + { + transaction = (Transaction) getHibernateTemplate().get(TransactionImpl.class, txnId); + } + return transaction; + } + + /** + * Does this Session contain any changes which must be + * synchronized with the store? + * + * @return true => changes are pending + */ + public boolean isDirty() + { + // create a callback for the task + HibernateCallback callback = new HibernateCallback() + { + public Object doInHibernate(Session session) + { + return session.isDirty(); + } + }; + // execute the callback + return ((Boolean)getHibernateTemplate().execute(callback)).booleanValue(); + } + + /** + * Just flushes the session + */ + public void flush() + { + getSession().flush(); + } + + /** + * @see #QUERY_GET_ALL_STORES + */ + @SuppressWarnings("unchecked") + public List getStores() + { + HibernateCallback callback = new HibernateCallback() + { + public Object doInHibernate(Session session) + { + Query query = session.getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_ALL_STORES); + return query.list(); + } + }; + List queryResults = (List) getHibernateTemplate().execute(callback); + // done + return queryResults; + } + + /** + * Ensures that the store protocol/identifier combination is unique + */ + public Store createStore(String protocol, String identifier) + { + // ensure that the name isn't in use + Store store = getStore(protocol, identifier); + if (store != null) + { + throw new RuntimeException("A store already exists: \n" + + " protocol: " + protocol + "\n" + + " identifier: " + identifier + "\n" + + " store: " + store); + } + + store = new StoreImpl(); + // set key + store.setKey(new StoreKey(protocol, identifier)); + // persist so that it is present in the hibernate cache + getHibernateTemplate().save(store); + // create and assign a root node + Node rootNode = newNode( + store, + GUID.generate(), + ContentModel.TYPE_STOREROOT); + store.setRootNode(rootNode); + // done + return store; + } + + public Store getStore(String protocol, String identifier) + { + StoreKey storeKey = new StoreKey(protocol, identifier); + Store store = (Store) getHibernateTemplate().get(StoreImpl.class, storeKey); + // done + return store; + } + + /** + * Fetch the node status, if it exists + */ + public NodeStatus getNodeStatus(NodeRef nodeRef, boolean create) + { + NodeKey nodeKey = new NodeKey(nodeRef); + NodeStatus status = null; + try + { + status = (NodeStatus) getHibernateTemplate().get(NodeStatusImpl.class, nodeKey); + } + catch (DataAccessException e) + { + if (e.contains(ObjectDeletedException.class)) + { + // the object no longer exists + return null; + } + throw e; + } + // create if necessary + if (status == null && create) + { + status = new NodeStatusImpl(); + status.setKey(nodeKey); + status.setTransaction(getCurrentTransaction()); + getHibernateTemplate().save(status); + } + // done + return status; + } + + public void recordChangeId(NodeRef nodeRef) + { + NodeKey key = new NodeKey(nodeRef); + + NodeStatus status = (NodeStatus) getHibernateTemplate().get(NodeStatusImpl.class, key); + if (status == null) + { + // the node never existed or the status was deleted + return; + } + else + { + status.getTransaction().setChangeTxnId(AlfrescoTransactionSupport.getTransactionId()); + } + } + + public Node newNode(Store store, String uuid, QName nodeTypeQName) throws InvalidTypeException + { + NodeKey key = new NodeKey(store.getKey(), uuid); + + // create (or reuse) the mandatory node status + NodeStatus status = (NodeStatus) getHibernateTemplate().get(NodeStatusImpl.class, key); + if (status == null) + { + status = new NodeStatusImpl(); + status.setKey(key); + } + else + { + // The node existed at some point. + // Although unlikely, it is possible that the node was deleted in this transaction. + // If that is the case, then the session has to be flushed so that the database + // constraints aren't violated as the node creation will write to the database to + // get an ID + if (status.getTransaction().getChangeTxnId().equals(AlfrescoTransactionSupport.getTransactionId())) + { + // flush + getHibernateTemplate().flush(); + } + } + + // build a concrete node based on a bootstrap type + Node node = new NodeImpl(); + // set other required properties + node.setStore(store); + node.setUuid(uuid); + node.setTypeQName(nodeTypeQName); + // persist the node + getHibernateTemplate().save(node); + + // set required status properties + status.setNode(node); + // assign a transaction + if (status.getTransaction() == null) + { + status.setTransaction(getCurrentTransaction()); + } + // persist the nodestatus + getHibernateTemplate().save(status); + + // done + return node; + } + + public Node getNode(NodeRef nodeRef) + { + // get it via the node status + NodeStatus status = getNodeStatus(nodeRef, false); + if (status == null) + { + // no status implies no node + return null; + } + else + { + // a status may have a node + Node node = status.getNode(); + return node; + } + } + + /** + * Manually ensures that all cascading of associations is taken care of + */ + public void deleteNode(Node node, boolean cascade) + { + Set deletedChildAssocIds = new HashSet(10); + deleteNodeInternal(node, cascade, deletedChildAssocIds); + } + + /** + * + * @param node + * @param cascade true to cascade delete + * @param deletedChildAssocIds previously deleted child associations + */ + private void deleteNodeInternal(Node node, boolean cascade, Set deletedChildAssocIds) + { + // delete all parent assocs + if (logger.isDebugEnabled()) + { + logger.debug("Deleting parent assocs of node " + node.getId()); + } + + Collection parentAssocs = node.getParentAssocs(); + parentAssocs = new ArrayList(parentAssocs); + for (ChildAssoc assoc : parentAssocs) + { + deleteChildAssocInternal(assoc, false, deletedChildAssocIds); // we don't cascade upwards + } + // delete all child assocs + if (logger.isDebugEnabled()) + { + logger.debug("Deleting child assocs of node " + node.getId()); + } + Collection childAssocs = getChildAssocs(node); + childAssocs = new ArrayList(childAssocs); + for (ChildAssoc assoc : childAssocs) + { + deleteChildAssocInternal(assoc, cascade, deletedChildAssocIds); // potentially cascade downwards + } + // delete all node associations to and from + if (logger.isDebugEnabled()) + { + logger.debug("Deleting source and target assocs of node " + node.getId()); + } + List nodeAssocs = getNodeAssocsToAndFrom(node); + for (NodeAssoc assoc : nodeAssocs) + { + getHibernateTemplate().delete(assoc); + } + // update the node status + NodeRef nodeRef = node.getNodeRef(); + NodeStatus nodeStatus = getNodeStatus(nodeRef, true); + nodeStatus.setNode(null); + nodeStatus.getTransaction().setChangeTxnId(AlfrescoTransactionSupport.getTransactionId()); + // finally delete the node + getHibernateTemplate().delete(node); + // flush to ensure constraints can't be violated + getSession().flush(); + // done + } + + private long getCrc(String str) + { + CRC32 crc = new CRC32(); + crc.update(str.getBytes()); + return crc.getValue(); + } + + private static final String TRUNCATED_NAME_INDICATOR = "~~~"; + private String getShortName(String str) + { + int length = str.length(); + if (length <= 50) + { + return str; + } + else + { + StringBuilder ret = new StringBuilder(50); + ret.append(str.substring(0, 47)).append(TRUNCATED_NAME_INDICATOR); + return ret.toString(); + } + } + + public ChildAssoc newChildAssoc( + Node parentNode, + Node childNode, + boolean isPrimary, + QName assocTypeQName, + QName qname) + { + /* + * This initial child association creation will fail IFF there is already + * an association of the given type and name between the two nodes. For new association + * creation, this can only occur if two transactions attempt to create a secondary + * child association between the same two nodes. As this is unlikely, it is + * appropriate to just throw a runtime exception and let the second transaction + * fail. + * + * We don't need to flush the session beforehand as there won't be any deletions + * of the assocation pending. The association deletes, on the other hand, have + * to flush early in order to ensure that the database unique index isn't violated + * if the association is recreated subsequently. + */ + + // assign a random name to the node + String randomName = GUID.generate(); + + ChildAssoc assoc = new ChildAssocImpl(); + assoc.setTypeQName(assocTypeQName); + assoc.setChildNodeName(randomName); + assoc.setChildNodeNameCrc(-1L); // random names compete only with each other + assoc.setQname(qname); + assoc.setIsPrimary(isPrimary); + // persist it, catching the duplicate child name + getHibernateTemplate().save(assoc); + // maintain inverse sets + assoc.buildAssociation(parentNode, childNode); + // done + return assoc; + } + + public void setChildNameUnique(final ChildAssoc childAssoc, String childName) + { + /* + * As the Hibernate session is rendered useless when an exception is + * bubbled up, we go direct to the database to update the child association. + * This preserves the session and client code can catch the resulting + * exception and react to it whilst in the same transaction. + * + * We ensure that case-insensitivity is maintained by persisting + * the lowercase version of the child node name. + */ + + String childNameNew = null; + long crc = -1; + if (childName == null) + { + // random names compete only with each other, i.e. not at all + childNameNew = GUID.generate(); + crc = -1; + } + else + { + // assigned names compete exactly + childNameNew = childName.toLowerCase(); + crc = getCrc(childNameNew); + } + + final String childNameNewShort = getShortName(childNameNew); + final long childNameNewCrc = crc; + + // check if the name has changed + if (childAssoc.getChildNodeNameCrc() == childNameNewCrc) + { + if (childAssoc.getChildNodeName().equals(childNameNewShort)) + { + // nothing changed + return; + } + } + + HibernateCallback callback = new HibernateCallback() + { + public Object doInHibernate(Session session) + { + session.flush(); + Query query = session + .getNamedQuery(HibernateNodeDaoServiceImpl.UPDATE_SET_CHILD_ASSOC_NAME) + .setString("newName", childNameNewShort) + .setLong("newNameCrc", childNameNewCrc) + .setLong("childAssocId", childAssoc.getId()); + return (Integer) query.executeUpdate(); + } + }; + try + { + Integer count = (Integer) getHibernateTemplate().execute(callback); + // refresh the entity directly + if (count.intValue() == 0) + { + if (logger.isDebugEnabled()) + { + logger.debug("ChildAssoc not updated: " + childAssoc.getId()); + } + } + else + { + getHibernateTemplate().refresh(childAssoc); + } + } + catch (DataIntegrityViolationException e) + { + NodeRef parentNodeRef = childAssoc.getParent().getNodeRef(); + QName assocTypeQName = childAssoc.getTypeQName(); + throw new DuplicateChildNodeNameException(parentNodeRef, assocTypeQName, childName); + } + } + + @SuppressWarnings("unchecked") + public Collection getChildAssocs(final Node parentNode) + { + HibernateCallback callback = new HibernateCallback() + { + public Object doInHibernate(Session session) + { + Query query = session + .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_CHILD_ASSOCS) + .setLong("parentId", parentNode.getId()); + return query.list(); + } + }; + List queryResults = (List) getHibernateTemplate().execute(callback); + return queryResults; + } + + @SuppressWarnings("unchecked") + public Collection getChildAssocRefs(final Node parentNode) + { + HibernateCallback callback = new HibernateCallback() + { + public Object doInHibernate(Session session) + { + Query query = session + .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_CHILD_ASSOC_REFS) + .setLong("parentId", parentNode.getId()); + return query.list(); + } + }; + List queryResults = (List) getHibernateTemplate().execute(callback); + Collection refs = new ArrayList(queryResults.size()); + NodeRef parentNodeRef = parentNode.getNodeRef(); + for (Object[] row : queryResults) + { + String childProtocol = (String) row[5]; + String childIdentifier = (String) row[6]; + String childUuid = (String) row[7]; + NodeRef childNodeRef = new NodeRef(new StoreRef(childProtocol, childIdentifier), childUuid); + QName assocTypeQName = (QName) row[0]; + QName assocQName = (QName) row[1]; + Boolean assocIsPrimary = (Boolean) row[2]; + Integer assocIndex = (Integer) row[3]; + ChildAssociationRef assocRef = new ChildAssociationRef( + assocTypeQName, + parentNodeRef, + assocQName, + childNodeRef, + assocIsPrimary.booleanValue(), + assocIndex.intValue()); + refs.add(assocRef); + } + return refs; + } + + public ChildAssoc getChildAssoc( + Node parentNode, + Node childNode, + QName assocTypeQName, + QName qname) + { + ChildAssociationRef childAssocRef = new ChildAssociationRef( + assocTypeQName, + parentNode.getNodeRef(), + qname, + childNode.getNodeRef()); + // get all the parent's child associations + Collection assocs = getChildAssocs(parentNode); + // hunt down the desired assoc + for (ChildAssoc assoc : assocs) + { + // is it a match? + if (!assoc.getChildAssocRef().equals(childAssocRef)) // not a match + { + continue; + } + else + { + return assoc; + } + } + // not found + return null; + } + + public ChildAssoc getChildAssoc(final Node parentNode, final QName assocTypeQName, final String childName) + { + HibernateCallback callback = new HibernateCallback() + { + public Object doInHibernate(Session session) + { + String childNameLower = childName.toLowerCase(); + String childNameShort = getShortName(childNameLower); + long childNameLowerCrc = getCrc(childNameLower); + Query query = session + .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_CHILD_ASSOC_BY_TYPE_AND_NAME) + .setLong("parentId", parentNode.getId()) + .setParameter("typeQName", assocTypeQName) + .setParameter("childNodeName", childNameShort) + .setLong("childNodeNameCrc", childNameLowerCrc); + return query.uniqueResult(); + } + }; + ChildAssoc childAssoc = (ChildAssoc) getHibernateTemplate().execute(callback); + return childAssoc; + } + + /** + * Public level entry-point. + */ + public void deleteChildAssoc(ChildAssoc assoc, boolean cascade) + { + Set deletedChildAssocIds = new HashSet(10); + deleteChildAssocInternal(assoc, cascade, deletedChildAssocIds); + } + + /** + * Cascade deletion of child associations, recording the IDs of deleted assocs. + * + * @param assoc the association to delete + * @param cascade true to cascade to the child node of the association + * @param deletedChildAssocIds already-deleted associations + */ + private void deleteChildAssocInternal(ChildAssoc assoc, boolean cascade, Set deletedChildAssocIds) + { + Long childAssocId = assoc.getId(); + if (deletedChildAssocIds.contains(childAssocId)) + { + if (logger.isDebugEnabled()) + { + logger.debug("Ignoring parent-child association " + assoc.getId()); + } + return; + } + + if (logger.isDebugEnabled()) + { + logger.debug( + "Deleting parent-child association " + assoc.getId() + + (cascade ? " with" : " without") + " cascade:" + + assoc.getParent().getId() + " -> " + assoc.getChild().getId()); + } + + Node childNode = assoc.getChild(); + + // maintain inverse association sets + assoc.removeAssociation(); + // remove instance + getHibernateTemplate().delete(assoc); + deletedChildAssocIds.add(childAssocId); // ensure that we don't attempt to delete it twice + + if (cascade && assoc.getIsPrimary()) // the assoc is primary + { + // delete the child node + deleteNodeInternal(childNode, cascade, deletedChildAssocIds); + /* + * The child node deletion will cascade delete all assocs to + * and from it, but we have safely removed this one, so no + * duplicate call will be received to do this + */ + } + + // To ensure the validity of the constraint enforcement by the database, + // we have to flush here + getSession().flush(); + } + + public ChildAssoc getPrimaryParentAssoc(Node node) + { + // get the assocs pointing to the node + Collection parentAssocs = node.getParentAssocs(); + ChildAssoc primaryAssoc = null; + for (ChildAssoc assoc : parentAssocs) + { + // ignore non-primary assocs + if (!assoc.getIsPrimary()) + { + continue; + } + else if (primaryAssoc != null) + { + // we have more than one somehow + throw new DataIntegrityViolationException( + "Multiple primary associations: \n" + + " child: " + node + "\n" + + " first primary assoc: " + primaryAssoc + "\n" + + " second primary assoc: " + assoc); + } + primaryAssoc = assoc; + // we keep looping to hunt out data integrity issues + } + // did we find a primary assoc? + if (primaryAssoc == null) + { + // the only condition where this is allowed is if the given node is a root node + Store store = node.getStore(); + Node rootNode = store.getRootNode(); + if (rootNode == null) + { + // a store without a root node - the entire store is hosed + throw new DataIntegrityViolationException("Store has no root node: \n" + + " store: " + store); + } + if (!rootNode.equals(node)) + { + // it wasn't the root node + throw new DataIntegrityViolationException("Non-root node has no primary parent: \n" + + " child: " + node); + } + } + // done + return primaryAssoc; + } + + public NodeAssoc newNodeAssoc(Node sourceNode, Node targetNode, QName assocTypeQName) + { + NodeAssoc assoc = new NodeAssocImpl(); + assoc.setTypeQName(assocTypeQName); + assoc.buildAssociation(sourceNode, targetNode); + // persist + try + { + getHibernateTemplate().save(assoc); + } + catch (DataIntegrityViolationException e) + { + throw new AssociationExistsException( + sourceNode.getNodeRef(), + targetNode.getNodeRef(), + assocTypeQName, + e); + } + // done + return assoc; + } + + @SuppressWarnings("unchecked") + public List getNodeAssocsToAndFrom(final Node node) + { + HibernateCallback callback = new HibernateCallback() + { + public Object doInHibernate(Session session) + { + Query query = session + .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_NODE_ASSOCS_TO_AND_FROM) + .setLong("nodeId", node.getId()); + return query.list(); + } + }; + List results = (List) getHibernateTemplate().execute(callback); + return results; + } + + public NodeAssoc getNodeAssoc( + final Node sourceNode, + final Node targetNode, + final QName assocTypeQName) + { + HibernateCallback callback = new HibernateCallback() + { + public Object doInHibernate(Session session) + { + Query query = session + .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_NODE_ASSOC) + .setLong("sourceId", sourceNode.getId()) + .setLong("targetId", targetNode.getId()) + .setParameter("assocTypeQName", assocTypeQName); + return query.uniqueResult(); + } + }; + NodeAssoc result = (NodeAssoc) getHibernateTemplate().execute(callback); + return result; + } + + @SuppressWarnings("unchecked") + public List getTargetNodeAssocs(final Node sourceNode) + { + HibernateCallback callback = new HibernateCallback() + { + public Object doInHibernate(Session session) + { + Query query = session + .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_TARGET_ASSOCS) + .setLong("sourceId", sourceNode.getId()); + return query.list(); + } + }; + List queryResults = (List) getHibernateTemplate().execute(callback); + return queryResults; + } + + @SuppressWarnings("unchecked") + public List getSourceNodeAssocs(final Node targetNode) + { + HibernateCallback callback = new HibernateCallback() + { + public Object doInHibernate(Session session) + { + Query query = session + .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_SOURCE_ASSOCS) + .setLong("targetId", targetNode.getId()); + return query.list(); + } + }; + List queryResults = (List) getHibernateTemplate().execute(callback); + return queryResults; + } + + public void deleteNodeAssoc(NodeAssoc assoc) + { + // Remove instance + getHibernateTemplate().delete(assoc); + // Flush to ensure that the database constraints aren't violated if the assoc + // is recreated in the transaction + getSession().flush(); + } + + @SuppressWarnings("unchecked") + public List getContentDataStrings() + { + HibernateCallback callback = new HibernateCallback() + { + public Object doInHibernate(Session session) + { + Query query = session.getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_CONTENT_DATA_STRINGS); + return query.list(); + } + }; + List queryResults = (List) getHibernateTemplate().execute(callback); + return queryResults; + } +} diff --git a/source/java/org/alfresco/repo/rule/RuleServiceImpl.java b/source/java/org/alfresco/repo/rule/RuleServiceImpl.java index ac31a18ba8..9bf1eea747 100644 --- a/source/java/org/alfresco/repo/rule/RuleServiceImpl.java +++ b/source/java/org/alfresco/repo/rule/RuleServiceImpl.java @@ -924,7 +924,7 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService private boolean checkForCopy(Set executedRules, NodeRef actionedUponNodeRef, Rule rule) { boolean result = true; - if (this.nodeService.hasAspect(actionedUponNodeRef, ContentModel.ASPECT_COPIEDFROM) == true) + if (this.nodeService.exists(actionedUponNodeRef) == true && this.nodeService.hasAspect(actionedUponNodeRef, ContentModel.ASPECT_COPIEDFROM) == true) { if (logger.isDebugEnabled() == true) { diff --git a/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java b/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java index 65325eb4eb..2b79b96556 100644 --- a/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java +++ b/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java @@ -1,335 +1,344 @@ -/* - * 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.service; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -import org.alfresco.service.ServiceDescriptor; -import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.action.ActionService; -import org.alfresco.service.cmr.avm.AVMService; -import org.alfresco.service.cmr.coci.CheckOutCheckInService; -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.cmr.lock.LockService; -import org.alfresco.service.cmr.model.FileFolderService; -import org.alfresco.service.cmr.repository.ContentService; -import org.alfresco.service.cmr.repository.CopyService; -import org.alfresco.service.cmr.repository.MimetypeService; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.ScriptService; -import org.alfresco.service.cmr.repository.TemplateService; -import org.alfresco.service.cmr.rule.RuleService; -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.PermissionService; -import org.alfresco.service.cmr.version.VersionService; -import org.alfresco.service.cmr.view.ExporterService; -import org.alfresco.service.cmr.view.ImporterService; -import org.alfresco.service.cmr.workflow.WorkflowService; -import org.alfresco.service.descriptor.DescriptorService; -import org.alfresco.service.namespace.NamespaceService; -import org.alfresco.service.namespace.QName; -import org.alfresco.service.transaction.TransactionService; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; - - -/** - * Implementation of a Service Registry based on the definition of - * Services contained within a Spring Bean Factory. - * - * @author David Caruana - */ -public class ServiceDescriptorRegistry - implements BeanFactoryAware, BeanFactoryPostProcessor, ServiceRegistry -{ - // Bean Factory within which the registry lives - private BeanFactory beanFactory = null; - - // Service Descriptor map - private Map descriptors = new HashMap(); - - - /* (non-Javadoc) - * @see org.springframework.beans.factory.config.BeanFactoryPostProcessor#postProcessBeanFactory(org.springframework.beans.factory.config.ConfigurableListableBeanFactory) - */ - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException - { - Map beans = beanFactory.getBeansOfType(ServiceDescriptorMetaData.class); - Iterator iter = beans.entrySet().iterator(); - while (iter.hasNext()) - { - Map.Entry entry = (Map.Entry)iter.next(); - ServiceDescriptorMetaData metaData = (ServiceDescriptorMetaData)entry.getValue(); - QName serviceName = QName.createQName(metaData.getNamespace(), (String)entry.getKey()); - StoreRedirector redirector = (entry.getValue() instanceof StoreRedirector) ? (StoreRedirector)entry.getValue() : null; - BeanServiceDescriptor serviceDescriptor = new BeanServiceDescriptor(serviceName, metaData, redirector); - descriptors.put(serviceDescriptor.getQualifiedName(), serviceDescriptor); - } - } - - /* (non-Javadoc) - * @see org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org.springframework.beans.factory.BeanFactory) - */ - public void setBeanFactory(BeanFactory beanFactory) throws BeansException - { - this.beanFactory = beanFactory; - } - - /* (non-Javadoc) - * @see org.alfresco.repo.service.ServiceRegistry#getServices() - */ - public Collection getServices() - { - return Collections.unmodifiableSet(descriptors.keySet()); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.service.ServiceRegistry#isServiceProvided(org.alfresco.repo.ref.QName) - */ - public boolean isServiceProvided(QName service) - { - return descriptors.containsKey(service); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.service.ServiceRegistry#getServiceDescriptor(org.alfresco.repo.ref.QName) - */ - public ServiceDescriptor getServiceDescriptor(QName service) - { - return descriptors.get(service); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.service.ServiceRegistry#getService(org.alfresco.repo.ref.QName) - */ - public Object getService(QName service) - { - return beanFactory.getBean(service.getLocalName()); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getDescriptorService() - */ - public DescriptorService getDescriptorService() - { - return (DescriptorService)getService(DESCRIPTOR_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.service.ServiceRegistry#getNodeService() - */ - public NodeService getNodeService() - { - return (NodeService)getService(NODE_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.service.ServiceRegistry#getNodeService() - */ - public AuthenticationService getAuthenticationService() - { - return (AuthenticationService)getService(AUTHENTICATION_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.service.ServiceRegistry#getContentService() - */ - public ContentService getContentService() - { - return (ContentService)getService(CONTENT_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getMimetypeService() - */ - public MimetypeService getMimetypeService() - { - return (MimetypeService)getService(MIMETYPE_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.service.ServiceRegistry#getVersionService() - */ - public VersionService getVersionService() - { - return (VersionService)getService(VERSION_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.service.ServiceRegistry#getLockService() - */ - public LockService getLockService() - { - return (LockService)getService(LOCK_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.service.ServiceRegistry#getDictionaryService() - */ - public DictionaryService getDictionaryService() - { - return (DictionaryService)getService(DICTIONARY_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getSearchService() - */ - public SearchService getSearchService() - { - return (SearchService)getService(SEARCH_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getTransactionService() - */ - public TransactionService getTransactionService() - { - return (TransactionService)getService(TRANSACTION_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getCopyService() - */ - public CopyService getCopyService() - { - return (CopyService)getService(COPY_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getCheckOutCheckInService() - */ - public CheckOutCheckInService getCheckOutCheckInService() - { - return (CheckOutCheckInService)getService(COCI_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getCategoryService() - */ - public CategoryService getCategoryService() - { - return (CategoryService)getService(CATEGORY_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getNamespaceService() - */ - public NamespaceService getNamespaceService() - { - return (NamespaceService)getService(NAMESPACE_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getImporterService() - */ - public ImporterService getImporterService() - { - return (ImporterService)getService(IMPORTER_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getExporterService() - */ - public ExporterService getExporterService() - { - return (ExporterService)getService(EXPORTER_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getRuleService() - */ - public RuleService getRuleService() - { - return (RuleService)getService(RULE_SERVICE); - } - - /* - * (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getActionService() - */ - public ActionService getActionService() - { - return (ActionService)getService(ACTION_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getPermissionService() - */ - public PermissionService getPermissionService() - { - return (PermissionService)getService(PERMISSIONS_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getAuthorityService() - */ - public AuthorityService getAuthorityService() - { - return (AuthorityService)getService(AUTHORITY_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getTemplateService() - */ - public TemplateService getTemplateService() - { - return (TemplateService)getService(TEMPLATE_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getTemplateService() - */ - public FileFolderService getFileFolderService() - { - return (FileFolderService)getService(FILE_FOLDER_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getScriptService() - */ - public ScriptService getScriptService() - { - return (ScriptService)getService(SCRIPT_SERVICE); - } - - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getWorkflowService() - */ - public WorkflowService getWorkflowService() - { - return (WorkflowService)getService(WORKFLOW_SERVICE); - } - - /** - * Get the AVMService. - * @return The AVMService or null if there is none. - */ - public AVMService getAVMService() - { - return (AVMService)getService(AVM_SERVICE); - } -} +/* + * 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.service; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.alfresco.service.ServiceDescriptor; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.action.ActionService; +import org.alfresco.service.cmr.audit.AuditService; +import org.alfresco.service.cmr.avm.AVMService; +import org.alfresco.service.cmr.coci.CheckOutCheckInService; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.lock.LockService; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.CopyService; +import org.alfresco.service.cmr.repository.MimetypeService; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.ScriptService; +import org.alfresco.service.cmr.repository.TemplateService; +import org.alfresco.service.cmr.rule.RuleService; +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.PermissionService; +import org.alfresco.service.cmr.version.VersionService; +import org.alfresco.service.cmr.view.ExporterService; +import org.alfresco.service.cmr.view.ImporterService; +import org.alfresco.service.cmr.workflow.WorkflowService; +import org.alfresco.service.descriptor.DescriptorService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.transaction.TransactionService; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; + + +/** + * Implementation of a Service Registry based on the definition of + * Services contained within a Spring Bean Factory. + * + * @author David Caruana + */ +public class ServiceDescriptorRegistry + implements BeanFactoryAware, BeanFactoryPostProcessor, ServiceRegistry +{ + // Bean Factory within which the registry lives + private BeanFactory beanFactory = null; + + // Service Descriptor map + private Map descriptors = new HashMap(); + + + /* (non-Javadoc) + * @see org.springframework.beans.factory.config.BeanFactoryPostProcessor#postProcessBeanFactory(org.springframework.beans.factory.config.ConfigurableListableBeanFactory) + */ + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException + { + Map beans = beanFactory.getBeansOfType(ServiceDescriptorMetaData.class); + Iterator iter = beans.entrySet().iterator(); + while (iter.hasNext()) + { + Map.Entry entry = (Map.Entry)iter.next(); + ServiceDescriptorMetaData metaData = (ServiceDescriptorMetaData)entry.getValue(); + QName serviceName = QName.createQName(metaData.getNamespace(), (String)entry.getKey()); + StoreRedirector redirector = (entry.getValue() instanceof StoreRedirector) ? (StoreRedirector)entry.getValue() : null; + BeanServiceDescriptor serviceDescriptor = new BeanServiceDescriptor(serviceName, metaData, redirector); + descriptors.put(serviceDescriptor.getQualifiedName(), serviceDescriptor); + } + } + + /* (non-Javadoc) + * @see org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org.springframework.beans.factory.BeanFactory) + */ + public void setBeanFactory(BeanFactory beanFactory) throws BeansException + { + this.beanFactory = beanFactory; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.service.ServiceRegistry#getServices() + */ + public Collection getServices() + { + return Collections.unmodifiableSet(descriptors.keySet()); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.service.ServiceRegistry#isServiceProvided(org.alfresco.repo.ref.QName) + */ + public boolean isServiceProvided(QName service) + { + return descriptors.containsKey(service); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.service.ServiceRegistry#getServiceDescriptor(org.alfresco.repo.ref.QName) + */ + public ServiceDescriptor getServiceDescriptor(QName service) + { + return descriptors.get(service); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.service.ServiceRegistry#getService(org.alfresco.repo.ref.QName) + */ + public Object getService(QName service) + { + return beanFactory.getBean(service.getLocalName()); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getDescriptorService() + */ + public DescriptorService getDescriptorService() + { + return (DescriptorService)getService(DESCRIPTOR_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.service.ServiceRegistry#getNodeService() + */ + public NodeService getNodeService() + { + return (NodeService)getService(NODE_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.service.ServiceRegistry#getNodeService() + */ + public AuthenticationService getAuthenticationService() + { + return (AuthenticationService)getService(AUTHENTICATION_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.service.ServiceRegistry#getContentService() + */ + public ContentService getContentService() + { + return (ContentService)getService(CONTENT_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getMimetypeService() + */ + public MimetypeService getMimetypeService() + { + return (MimetypeService)getService(MIMETYPE_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.service.ServiceRegistry#getVersionService() + */ + public VersionService getVersionService() + { + return (VersionService)getService(VERSION_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.service.ServiceRegistry#getLockService() + */ + public LockService getLockService() + { + return (LockService)getService(LOCK_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.service.ServiceRegistry#getDictionaryService() + */ + public DictionaryService getDictionaryService() + { + return (DictionaryService)getService(DICTIONARY_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getSearchService() + */ + public SearchService getSearchService() + { + return (SearchService)getService(SEARCH_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getTransactionService() + */ + public TransactionService getTransactionService() + { + return (TransactionService)getService(TRANSACTION_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getCopyService() + */ + public CopyService getCopyService() + { + return (CopyService)getService(COPY_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getCheckOutCheckInService() + */ + public CheckOutCheckInService getCheckOutCheckInService() + { + return (CheckOutCheckInService)getService(COCI_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getCategoryService() + */ + public CategoryService getCategoryService() + { + return (CategoryService)getService(CATEGORY_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getNamespaceService() + */ + public NamespaceService getNamespaceService() + { + return (NamespaceService)getService(NAMESPACE_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getImporterService() + */ + public ImporterService getImporterService() + { + return (ImporterService)getService(IMPORTER_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getExporterService() + */ + public ExporterService getExporterService() + { + return (ExporterService)getService(EXPORTER_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getRuleService() + */ + public RuleService getRuleService() + { + return (RuleService)getService(RULE_SERVICE); + } + + /* + * (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getActionService() + */ + public ActionService getActionService() + { + return (ActionService)getService(ACTION_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getPermissionService() + */ + public PermissionService getPermissionService() + { + return (PermissionService)getService(PERMISSIONS_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getAuthorityService() + */ + public AuthorityService getAuthorityService() + { + return (AuthorityService)getService(AUTHORITY_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getTemplateService() + */ + public TemplateService getTemplateService() + { + return (TemplateService)getService(TEMPLATE_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getTemplateService() + */ + public FileFolderService getFileFolderService() + { + return (FileFolderService)getService(FILE_FOLDER_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getScriptService() + */ + public ScriptService getScriptService() + { + return (ScriptService)getService(SCRIPT_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getWorkflowService() + */ + public WorkflowService getWorkflowService() + { + return (WorkflowService)getService(WORKFLOW_SERVICE); + } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getWorkflowService() + */ + public AuditService getAuditService() + { + return (AuditService)getService(AUDIT_SERVICE); + } + + /** + * Get the AVMService. + * @return The AVMService or null if there is none. + */ + public AVMService getAVMService() + { + return (AVMService)getService(AVM_SERVICE); + } +} diff --git a/source/java/org/alfresco/repo/version/common/counter/VersionCounterServiceTest.java b/source/java/org/alfresco/repo/version/common/counter/VersionCounterServiceTest.java index c4ada34f2f..a9c34e9d63 100644 --- a/source/java/org/alfresco/repo/version/common/counter/VersionCounterServiceTest.java +++ b/source/java/org/alfresco/repo/version/common/counter/VersionCounterServiceTest.java @@ -23,6 +23,7 @@ import javax.transaction.UserTransaction; import junit.framework.TestCase; +import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.repo.transaction.TransactionUtil; import org.alfresco.repo.transaction.TransactionUtil.TransactionWork; import org.alfresco.service.ServiceRegistry; @@ -54,6 +55,10 @@ public class VersionCounterServiceTest extends TestCase nodeService = serviceRegistry.getNodeService(); counter = (VersionCounterService) ctx.getBean("versionCounterService"); + // authenticate + AuthenticationComponent auth = (AuthenticationComponent) ctx.getBean("authenticationComponent"); + auth.setSystemUserAsCurrentUser(); + storeRef1 = nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "test1_" + System.currentTimeMillis()); storeRef2 = nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "test2_" + System.currentTimeMillis()); } diff --git a/source/java/org/alfresco/repo/workflow/StartWorkflowActionExecuterTest.java b/source/java/org/alfresco/repo/workflow/StartWorkflowActionExecuterTest.java index c9e271362a..8a635cbab3 100644 --- a/source/java/org/alfresco/repo/workflow/StartWorkflowActionExecuterTest.java +++ b/source/java/org/alfresco/repo/workflow/StartWorkflowActionExecuterTest.java @@ -77,7 +77,7 @@ 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(StartWorkflowActionExecuter.PARAM_WORKFLOW_NAME, "jbpm$wf:review"); action.setParameterValue(WorkflowModel.PROP_REVIEW_DUE_DATE.toPrefixString(namespaceService), new Date()); NodeRef reviewer = personService.getPerson("admin"); action.setParameterValue(WorkflowModel.ASSOC_REVIEWER.toPrefixString(namespaceService), reviewer); diff --git a/source/java/org/alfresco/repo/workflow/WorkflowServiceImplTest.java b/source/java/org/alfresco/repo/workflow/WorkflowServiceImplTest.java index e61718f534..5747b33103 100644 --- a/source/java/org/alfresco/repo/workflow/WorkflowServiceImplTest.java +++ b/source/java/org/alfresco/repo/workflow/WorkflowServiceImplTest.java @@ -18,6 +18,7 @@ package org.alfresco.repo.workflow; import java.util.List; +import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; @@ -42,6 +43,10 @@ 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(); } public void testGetWorkflowDefinitions() diff --git a/source/java/org/alfresco/service/ServiceRegistry.java b/source/java/org/alfresco/service/ServiceRegistry.java index 9be742d026..c363684352 100644 --- a/source/java/org/alfresco/service/ServiceRegistry.java +++ b/source/java/org/alfresco/service/ServiceRegistry.java @@ -1,275 +1,283 @@ -/* - * 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.service; - -import java.util.Collection; - -import org.alfresco.service.cmr.action.ActionService; -import org.alfresco.service.cmr.avm.AVMService; -import org.alfresco.service.cmr.coci.CheckOutCheckInService; -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.cmr.lock.LockService; -import org.alfresco.service.cmr.model.FileFolderService; -import org.alfresco.service.cmr.repository.ContentService; -import org.alfresco.service.cmr.repository.CopyService; -import org.alfresco.service.cmr.repository.MimetypeService; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.ScriptService; -import org.alfresco.service.cmr.repository.TemplateService; -import org.alfresco.service.cmr.rule.RuleService; -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.PermissionService; -import org.alfresco.service.cmr.version.VersionService; -import org.alfresco.service.cmr.view.ExporterService; -import org.alfresco.service.cmr.view.ImporterService; -import org.alfresco.service.cmr.workflow.WorkflowService; -import org.alfresco.service.descriptor.DescriptorService; -import org.alfresco.service.namespace.NamespaceService; -import org.alfresco.service.namespace.QName; -import org.alfresco.service.transaction.TransactionService; - - -/** - * This interface represents the registry of public Repository Services. - * The registry provides meta-data about each service and provides - * access to the service interface. - * - * @author David Caruana - */ -@PublicService -public interface ServiceRegistry -{ - // Service Bean Names - - static final String SERVICE_REGISTRY = "ServiceRegistry"; - - static final QName REGISTRY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ServiceRegistry"); - static final QName DESCRIPTOR_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "DescriptorService"); - static final QName TRANSACTION_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "TransactionService"); - static final QName AUTHENTICATION_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "AuthenticationService"); - static final QName NAMESPACE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "NamespaceService"); - static final QName DICTIONARY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "DictionaryService"); - static final QName NODE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "NodeService"); - static final QName CONTENT_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ContentService"); - static final QName MIMETYPE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "MimetypeService"); - static final QName SEARCH_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "SearchService"); - static final QName CATEGORY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "CategoryService"); - static final QName COPY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "CopyService"); - static final QName LOCK_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "LockService"); - static final QName VERSION_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "VersionService"); - static final QName COCI_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "CheckoutCheckinService"); - static final QName RULE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "RuleService"); - static final QName IMPORTER_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ImporterService"); - static final QName EXPORTER_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ExporterService"); - static final QName ACTION_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ActionService"); - static final QName PERMISSIONS_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "PermissionService"); - static final QName AUTHORITY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "AuthorityService"); - static final QName TEMPLATE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "TemplateService"); - static final QName FILE_FOLDER_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "FileFolderService"); - 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 AVM_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "AVMService"); - - /** - * Get the list of services provided by the Repository - * - * @return list of provided Services - */ - @NotAuditable - Collection getServices(); - - /** - * Is the specified service provided by the Repository? - * - * @param service name of service to test provision of - * @return true => provided, false => not provided - */ - @NotAuditable - boolean isServiceProvided(QName service); - - /** - * Get meta-data about the specified service - * - * @param service name of service to retrieve meta data for - * @return the service meta data - */ - @NotAuditable - ServiceDescriptor getServiceDescriptor(QName service); - - /** - * Get the specified service. - * - * @param service name of service to retrieve - * @return the service interface (must cast to interface as described in service meta-data) - */ - @NotAuditable - Object getService(QName service); - - /** - * @return the descriptor service - */ - @NotAuditable - DescriptorService getDescriptorService(); - - /** - * @return the transaction service - */ - @NotAuditable - TransactionService getTransactionService(); - - /** - * @return the namespace service (or null, if one is not provided) - */ - @NotAuditable - NamespaceService getNamespaceService(); - - /** - * @return the authentication service (or null, if one is not provided) - */ - @NotAuditable - AuthenticationService getAuthenticationService(); - - /** - * @return the node service (or null, if one is not provided) - */ - @NotAuditable - NodeService getNodeService(); - - /** - * @return the content service (or null, if one is not provided) - */ - @NotAuditable - ContentService getContentService(); - - /** - * @return the mimetype service (or null, if one is not provided) - */ - @NotAuditable - MimetypeService getMimetypeService(); - - /** - * @return the search service (or null, if one is not provided) - */ - @NotAuditable - SearchService getSearchService(); - - /** - * @return the version service (or null, if one is not provided) - */ - @NotAuditable - VersionService getVersionService(); - - /** - * @return the lock service (or null, if one is not provided) - */ - @NotAuditable - LockService getLockService(); - - /** - * @return the dictionary service (or null, if one is not provided) - */ - @NotAuditable - DictionaryService getDictionaryService(); - - /** - * @return the copy service (or null, if one is not provided) - */ - @NotAuditable - CopyService getCopyService(); - - /** - * @return the checkout / checkin service (or null, if one is not provided) - */ - @NotAuditable - CheckOutCheckInService getCheckOutCheckInService(); - - /** - * @return the category service (or null, if one is not provided) - */ - @NotAuditable - CategoryService getCategoryService(); - - /** - * @return the importer service or null if not present - */ - @NotAuditable - ImporterService getImporterService(); - - /** - * @return the exporter service or null if not present - */ - @NotAuditable - ExporterService getExporterService(); - - /** - * @return the rule service (or null, if one is not provided) - */ - @NotAuditable - RuleService getRuleService(); - - /** - * @return the action service (or null if one is not provided) - */ - @NotAuditable - ActionService getActionService(); - - /** - * @return the permission service (or null if one is not provided) - */ - @NotAuditable - PermissionService getPermissionService(); - - /** - * @return the authority service (or null if one is not provided) - */ - @NotAuditable - AuthorityService getAuthorityService(); - - /** - * @return the template service (or null if one is not provided) - */ - @NotAuditable - TemplateService getTemplateService(); - - /** - * @return the file-folder manipulation service (or null if one is not provided) - */ - @NotAuditable - FileFolderService getFileFolderService(); - - /** - * @return the script execution service (or null if one is not provided) - */ - @NotAuditable - ScriptService getScriptService(); - - /** - * @return the workflow service (or null if one is not provided) - */ - @NotAuditable - WorkflowService getWorkflowService(); - - /** - * Get the AVMService. - * @return The AVM service (or null if one is not provided); - */ - @NotAuditable - AVMService getAVMService(); -} +/* + * 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.service; + +import java.util.Collection; + +import org.alfresco.service.cmr.action.ActionService; +import org.alfresco.service.cmr.audit.AuditService; +import org.alfresco.service.cmr.avm.AVMService; +import org.alfresco.service.cmr.coci.CheckOutCheckInService; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.lock.LockService; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.CopyService; +import org.alfresco.service.cmr.repository.MimetypeService; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.ScriptService; +import org.alfresco.service.cmr.repository.TemplateService; +import org.alfresco.service.cmr.rule.RuleService; +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.PermissionService; +import org.alfresco.service.cmr.version.VersionService; +import org.alfresco.service.cmr.view.ExporterService; +import org.alfresco.service.cmr.view.ImporterService; +import org.alfresco.service.cmr.workflow.WorkflowService; +import org.alfresco.service.descriptor.DescriptorService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.transaction.TransactionService; + + +/** + * This interface represents the registry of public Repository Services. + * The registry provides meta-data about each service and provides + * access to the service interface. + * + * @author David Caruana + */ +@PublicService +public interface ServiceRegistry +{ + // Service Bean Names + + static final String SERVICE_REGISTRY = "ServiceRegistry"; + + static final QName REGISTRY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ServiceRegistry"); + static final QName DESCRIPTOR_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "DescriptorService"); + static final QName TRANSACTION_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "TransactionService"); + static final QName AUTHENTICATION_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "AuthenticationService"); + static final QName NAMESPACE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "NamespaceService"); + static final QName DICTIONARY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "DictionaryService"); + static final QName NODE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "NodeService"); + static final QName CONTENT_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ContentService"); + static final QName MIMETYPE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "MimetypeService"); + static final QName SEARCH_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "SearchService"); + static final QName CATEGORY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "CategoryService"); + static final QName COPY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "CopyService"); + static final QName LOCK_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "LockService"); + static final QName VERSION_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "VersionService"); + static final QName COCI_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "CheckoutCheckinService"); + static final QName RULE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "RuleService"); + static final QName IMPORTER_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ImporterService"); + static final QName EXPORTER_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ExporterService"); + static final QName ACTION_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ActionService"); + static final QName PERMISSIONS_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "PermissionService"); + static final QName AUTHORITY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "AuthorityService"); + static final QName TEMPLATE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "TemplateService"); + static final QName FILE_FOLDER_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "FileFolderService"); + 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 AVM_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "AVMService"); + + /** + * Get the list of services provided by the Repository + * + * @return list of provided Services + */ + @NotAuditable + Collection getServices(); + + /** + * Is the specified service provided by the Repository? + * + * @param service name of service to test provision of + * @return true => provided, false => not provided + */ + @NotAuditable + boolean isServiceProvided(QName service); + + /** + * Get meta-data about the specified service + * + * @param service name of service to retrieve meta data for + * @return the service meta data + */ + @NotAuditable + ServiceDescriptor getServiceDescriptor(QName service); + + /** + * Get the specified service. + * + * @param service name of service to retrieve + * @return the service interface (must cast to interface as described in service meta-data) + */ + @NotAuditable + Object getService(QName service); + + /** + * @return the descriptor service + */ + @NotAuditable + DescriptorService getDescriptorService(); + + /** + * @return the transaction service + */ + @NotAuditable + TransactionService getTransactionService(); + + /** + * @return the namespace service (or null, if one is not provided) + */ + @NotAuditable + NamespaceService getNamespaceService(); + + /** + * @return the authentication service (or null, if one is not provided) + */ + @NotAuditable + AuthenticationService getAuthenticationService(); + + /** + * @return the node service (or null, if one is not provided) + */ + @NotAuditable + NodeService getNodeService(); + + /** + * @return the content service (or null, if one is not provided) + */ + @NotAuditable + ContentService getContentService(); + + /** + * @return the mimetype service (or null, if one is not provided) + */ + @NotAuditable + MimetypeService getMimetypeService(); + + /** + * @return the search service (or null, if one is not provided) + */ + @NotAuditable + SearchService getSearchService(); + + /** + * @return the version service (or null, if one is not provided) + */ + @NotAuditable + VersionService getVersionService(); + + /** + * @return the lock service (or null, if one is not provided) + */ + @NotAuditable + LockService getLockService(); + + /** + * @return the dictionary service (or null, if one is not provided) + */ + @NotAuditable + DictionaryService getDictionaryService(); + + /** + * @return the copy service (or null, if one is not provided) + */ + @NotAuditable + CopyService getCopyService(); + + /** + * @return the checkout / checkin service (or null, if one is not provided) + */ + @NotAuditable + CheckOutCheckInService getCheckOutCheckInService(); + + /** + * @return the category service (or null, if one is not provided) + */ + @NotAuditable + CategoryService getCategoryService(); + + /** + * @return the importer service or null if not present + */ + @NotAuditable + ImporterService getImporterService(); + + /** + * @return the exporter service or null if not present + */ + @NotAuditable + ExporterService getExporterService(); + + /** + * @return the rule service (or null, if one is not provided) + */ + @NotAuditable + RuleService getRuleService(); + + /** + * @return the action service (or null if one is not provided) + */ + @NotAuditable + ActionService getActionService(); + + /** + * @return the permission service (or null if one is not provided) + */ + @NotAuditable + PermissionService getPermissionService(); + + /** + * @return the authority service (or null if one is not provided) + */ + @NotAuditable + AuthorityService getAuthorityService(); + + /** + * @return the template service (or null if one is not provided) + */ + @NotAuditable + TemplateService getTemplateService(); + + /** + * @return the file-folder manipulation service (or null if one is not provided) + */ + @NotAuditable + FileFolderService getFileFolderService(); + + /** + * @return the script execution service (or null if one is not provided) + */ + @NotAuditable + ScriptService getScriptService(); + + /** + * @return the workflow service (or null if one is not provided) + */ + @NotAuditable + WorkflowService getWorkflowService(); + + /** + * @return the audit service (or null if one is not provided) + */ + @NotAuditable + AuditService getAuditService(); + + /** + * Get the AVMService. + * @return The AVM service (or null if one is not provided); + */ + @NotAuditable + AVMService getAVMService(); +} diff --git a/source/java/org/alfresco/service/cmr/audit/AuditInfo.java b/source/java/org/alfresco/service/cmr/audit/AuditInfo.java new file mode 100644 index 0000000000..f601a61bc9 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/audit/AuditInfo.java @@ -0,0 +1,202 @@ +/* + * 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.service.cmr.audit; + +import java.io.Serializable; +import java.net.InetAddress; +import java.util.Date; +import java.util.Map; + +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.QName; + +/** + * A single entry in an audit trail + * + * @author Andy Hind + */ +public interface AuditInfo +{ + /** + * The identifier for the application that performed the audit. Method interceptors around public services will use the string 'SystemMethodInterceptor'. + * + * @return - the application (may not be null). + */ + public String getAuditApplication(); + + /** + * The name of the method executed on a public service. + * + * @return - the method name - this may be null for external audit entries. + */ + public String getAuditMethod(); + + /** + * The public service on which a method was invoked. + * + * @return - the service name - this may be null for external audit entries. + */ + public String getAuditService(); + + /** + * The address for the client. (This will be null in version 1.4) + * + * @return - the client address - may be null. + */ + public String getClientAddress(); + + /** + * The timestamp for the audit entry. + * + * @return + */ + public Date getDate(); + + /** + * Is this entry recording an error? + * + * @return + */ + public boolean isFail(); + + /** + * Was this audit entry subject to filtering (which must have been met if an entry is found). Filters are not applied in version 1.4. + * + * @return + */ + public boolean isFiltered(); + + /** + * Get the host address of the server machine. + * + * @return + */ + public String getHostAddress(); + + /** + * Get the ID of the key node. + * + * @return - the id of the key node - this may be null if there is no key or the key is not a node ref. + */ + public String getKeyGUID(); + + /** + * The serialized properties on the key node, if one exists, after the method invocation. Note these values are serialized before the method is called so they are unaffected by + * the method invocation. In V1.4 these are not stored. + * + * @return + */ + public Map getKeyPropertiesAfter(); + + /** + * The serialized properties on the key node, if one exists, before the method invocation. In V1.4 these are not stored. + * + * @return + */ + public Map getKeyPropertiesBefore(); + + /** + * The store ref for the key. + * + * @return - the store ref - this may be null if there is no key. + */ + public StoreRef getKeyStore(); + + /** + * The message entered for application audit entries. + * + * @return - the audit message. This may be null, and will be null for audit entries generated from method invocations. + */ + public String getMessage(); + + /** + * Get the serailized mehod arguments. + * + * These are not stored in V1.4. + * + * @return + */ + public Serializable[] getMethodArguments(); + + /** + * Get the method arguments as strings. + * + * @return + */ + public String[] getMethodArgumentsAsStrings(); + + /** + * Get the path to the key node, if one exists. + * + * @return - the path or null. + */ + public String getPath(); + + /** + * The serialized value of the return object. + * + * This is not available in V1.4. + * + * @return + */ + public Serializable getReturnObject(); + + /** + * Get the return object string value. + * + * @return - the string value of the return object. May be null if the method is of type void or returns null. + */ + public String getReturnObjectAsString(); + + /** + * Get the session id. + * + * This is not stored in V1.4. + * + * @return + */ + public String getSessionId(); + + /** + * Get the deserialized error, if one occurred. + * + * @return the throwable or null. + */ + public Throwable getThrowable(); + + /** + * In 1.4, get the error message (no stack trace). + * + * @return - the error message + */ + public String getThrowableAsString(); + + /** + * Get the transaction id which caused the audit. + * + * @return the Tx id (not null). + */ + public String getTxId(); + + /** + * Get the name of the user who caused the audit entry. + * + * @return - the user name / user authority (not null) + */ + public String getUserIdentifier(); + +} diff --git a/source/java/org/alfresco/service/cmr/audit/AuditService.java b/source/java/org/alfresco/service/cmr/audit/AuditService.java index 8ad7456d87..f6c4c32617 100644 --- a/source/java/org/alfresco/service/cmr/audit/AuditService.java +++ b/source/java/org/alfresco/service/cmr/audit/AuditService.java @@ -16,6 +16,9 @@ */ package org.alfresco.service.cmr.audit; +import java.util.List; + +import org.alfresco.repo.audit.AuditState; import org.alfresco.service.NotAuditable; import org.alfresco.service.PublicService; import org.alfresco.service.cmr.repository.NodeRef; @@ -81,5 +84,14 @@ public interface AuditService */ @NotAuditable public void audit(String source, String description, NodeRef key, Object... args); - + + + /** + * Get the audit trail for a node ref. + * + * @param nodeRef - the node ref for which to get the audit trail. + * @return - tha audit trail + */ + @NotAuditable + public List getAuditTrail(NodeRef nodeRef); } diff --git a/source/java/org/alfresco/service/cmr/repository/CopyService.java b/source/java/org/alfresco/service/cmr/repository/CopyService.java index 801071df37..33a2311e47 100644 --- a/source/java/org/alfresco/service/cmr/repository/CopyService.java +++ b/source/java/org/alfresco/service/cmr/repository/CopyService.java @@ -59,6 +59,11 @@ public interface CopyService * not copied. *

* Source association are not copied. + *

+ * NOTE: The top-level node has it's cm:name property removed for + * associations that do not allow duplicately named children in order + * to prevent any chance of a duplicate name clash. Reassign the + * cm:name property and catch the {@link DuplicateChildNodeNameException}. * * @param sourceNodeRef the node reference used as the source of the copy * @param destinationParent the intended parent of the new node diff --git a/source/java/org/alfresco/service/cmr/repository/NodeService.java b/source/java/org/alfresco/service/cmr/repository/NodeService.java index fc0f8a46d3..51c1f2c6f4 100644 --- a/source/java/org/alfresco/service/cmr/repository/NodeService.java +++ b/source/java/org/alfresco/service/cmr/repository/NodeService.java @@ -1,564 +1,564 @@ -/* - * 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.service.cmr.repository; - -import java.io.Serializable; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.alfresco.repo.domain.DbAccessControlList; -import org.alfresco.service.Auditable; -import org.alfresco.service.PublicService; -import org.alfresco.service.cmr.dictionary.InvalidAspectException; -import org.alfresco.service.cmr.dictionary.InvalidTypeException; -import org.alfresco.service.namespace.QName; -import org.alfresco.service.namespace.QNamePattern; - -/** - * Interface for public and internal node and store operations. - * - * @author Derek Hulley - */ -@PublicService -public interface NodeService -{ - /** - * Gets a list of all available node store references - * - * @return Returns a list of store references - */ - @Auditable - public List getStores(); - - /** - * Create a new store for the given protocol and identifier. The implementation - * may create the store in any number of locations, including a database or - * Subversion. - * - * @param protocol the implementation protocol - * @param identifier the protocol-specific identifier - * @return Returns a reference to the store - * @throws StoreExistsException - */ - @Auditable(parameters = {"protocol", "identifier"}) - public StoreRef createStore(String protocol, String identifier) throws StoreExistsException; - - /** - * @param storeRef a reference to the store to look for - * @return Returns true if the store exists, otherwise false - */ - @Auditable(parameters = {"storeRef"}) - public boolean exists(StoreRef storeRef); - - /** - * @param nodeRef a reference to the node to look for - * @return Returns true if the node exists, otherwise false - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef"}) - public boolean exists(NodeRef nodeRef); - - /** - * Gets the ID of the last transaction that caused the node to change. This includes - * deletions, so it is possible that the node being referenced no longer exists. - * If the node never existed, then null is returned. - * - * @param nodeRef a reference to a current or previously existing node - * @return Returns the status of the node, or null if the node never existed - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef"}) - public NodeRef.Status getNodeStatus(NodeRef nodeRef); - - /** - * @param storeRef a reference to an existing store - * @return Returns a reference to the root node of the store - * @throws InvalidStoreRefException if the store could not be found - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"storeRef"}) - public NodeRef getRootNode(StoreRef storeRef) throws InvalidStoreRefException; - - /** - * @see #createNode(NodeRef, QName, QName, QName, Map) - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"parentRef", "assocTypeQName", "assocQName", "nodeTypeQName"}) - public ChildAssociationRef createNode( - NodeRef parentRef, - QName assocTypeQName, - QName assocQName, - QName nodeTypeQName) - throws InvalidNodeRefException, InvalidTypeException; - - /** - * Creates a new, non-abstract, real node as a primary child of the given parent node. - * - * @param parentRef the parent node - * @param assocTypeQName the type of the association to create. This is used - * for verification against the data dictionary. - * @param assocQName the qualified name of the association - * @param nodeTypeQName a reference to the node type - * @param properties optional map of properties to keyed by their qualified names - * @return Returns a reference to the newly created child association - * @throws InvalidNodeRefException if the parent reference is invalid - * @throws InvalidTypeException if the node type reference is not recognised - * - * @see org.alfresco.service.cmr.dictionary.DictionaryService - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"parentRef", "assocTypeQName", "assocQName", "nodeTypeQName", "properties"}) - public ChildAssociationRef createNode( - NodeRef parentRef, - QName assocTypeQName, - QName assocQName, - QName nodeTypeQName, - Map properties) - throws InvalidNodeRefException, InvalidTypeException; - - /** - * Moves the primary location of the given node. - *

- * This involves changing the node's primary parent and possibly the name of the - * association referencing it. - *

- * If the new parent is in a different store from the original, then the entire - * node hierarchy is moved to the new store. Inter-store associations are not - * affected. - * - * @param nodeToMoveRef the node to move - * @param newParentRef the new parent of the moved node - * @param assocTypeQName the type of the association to create. This is used - * for verification against the data dictionary. - * @param assocQName the qualified name of the new child association - * @return Returns a reference to the newly created child association - * @throws InvalidNodeRefException if either the parent node or move node reference is invalid - * @throws CyclicChildRelationshipException if the child partakes in a cyclic relationship after the add - * - * @see #getPrimaryParent(NodeRef) - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeToMoveRef", "newParentRef", "assocTypeQName", "assocQName"}) - public ChildAssociationRef moveNode( - NodeRef nodeToMoveRef, - NodeRef newParentRef, - QName assocTypeQName, - QName assocQName) - throws InvalidNodeRefException; - - /** - * Set the ordering index of the child association. This affects the ordering of - * of the return values of methods that return a set of children or child - * associations. - * - * @param childAssocRef the child association that must be moved in the order - * @param index an arbitrary index that will affect the return order - * - * @see #getChildAssocs(NodeRef) - * @see #getChildAssocs(NodeRef, QNamePattern, QNamePattern) - * @see ChildAssociationRef#getNthSibling() - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"childAssocRef", "index"}) - public void setChildAssociationIndex( - ChildAssociationRef childAssocRef, - int index) - throws InvalidChildAssociationRefException; - - /** - * @param nodeRef - * @return Returns the type name - * @throws InvalidNodeRefException if the node could not be found - * - * @see org.alfresco.service.cmr.dictionary.DictionaryService - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef"}) - public QName getType(NodeRef nodeRef) throws InvalidNodeRefException; - - /** - * Re-sets the type of the node. Can be called in order specialise a node to a sub-type. - * - * This should be used with caution since calling it changes the type of the node and thus - * implies a different set of aspects, properties and associations. It is the calling codes - * responsibility to ensure that the node is in a approriate state after changing the type. - * - * @param nodeRef the node reference - * @param typeQName the type QName - * - * @since 1.1 - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef", "typeQName"}) - public void setType(NodeRef nodeRef, QName typeQName) throws InvalidNodeRefException; - - /** - * Applies an aspect to the given node. After this method has been called, - * the node with have all the aspect-related properties present - * - * @param nodeRef - * @param aspectTypeQName the aspect to apply to the node - * @param aspectProperties a minimum of the mandatory properties required for - * the aspect - * @throws InvalidNodeRefException - * @throws InvalidAspectException if the class reference is not to a valid aspect - * - * @see org.alfresco.service.cmr.dictionary.DictionaryService#getAspect(QName) - * @see org.alfresco.service.cmr.dictionary.ClassDefinition#getProperties() - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef", "aspectTypeQName", "aspectProperties"}) - public void addAspect( - NodeRef nodeRef, - QName aspectTypeQName, - Map aspectProperties) - throws InvalidNodeRefException, InvalidAspectException; - - /** - * Remove an aspect and all related properties from a node - * - * @param nodeRef - * @param aspectTypeQName the type of aspect to remove - * @throws InvalidNodeRefException if the node could not be found - * @throws InvalidAspectException if the the aspect is unknown or if the - * aspect is mandatory for the class of the node - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef", "aspectTypeQName"}) - public void removeAspect(NodeRef nodeRef, QName aspectTypeQName) - throws InvalidNodeRefException, InvalidAspectException; - - /** - * Determines if a given aspect is present on a node. Aspects may only be - * removed if they are NOT mandatory. - * - * @param nodeRef - * @param aspectTypeQName - * @return Returns true if the aspect has been applied to the given node, - * otherwise false - * @throws InvalidNodeRefException if the node could not be found - * @throws InvalidAspectException if the aspect reference is invalid - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef", "aspectTypeQName"}) - public boolean hasAspect(NodeRef nodeRef, QName aspectTypeQName) - throws InvalidNodeRefException, InvalidAspectException; - - /** - * @param nodeRef - * @return Returns a set of all aspects applied to the node, including mandatory - * aspects - * @throws InvalidNodeRefException if the node could not be found - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef"}) - public Set getAspects(NodeRef nodeRef) throws InvalidNodeRefException; - - /** - * Deletes the given node. - *

- * All associations (both children and regular node associations) - * will be deleted, and where the given node is the primary parent, - * the children will also be cascade deleted. - * - * @param nodeRef reference to a node within a store - * @throws InvalidNodeRefException if the reference given is invalid - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef"}) - public void deleteNode(NodeRef nodeRef) throws InvalidNodeRefException; - - /** - * Makes a parent-child association between the given nodes. Both nodes must belong to the same store. - *

- * - * - * @param parentRef - * @param childRef - * @param assocTypeQName the qualified name of the association type as defined in the datadictionary - * @param qname the qualified name of the association - * @return Returns a reference to the newly created child association - * @throws InvalidNodeRefException if the parent or child nodes could not be found - * @throws CyclicChildRelationshipException if the child partakes in a cyclic relationship after the add - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"parentRef", "childRef", "assocTypeQName", "qname"}) - public ChildAssociationRef addChild( - NodeRef parentRef, - NodeRef childRef, - QName assocTypeQName, - QName qname) throws InvalidNodeRefException; - - /** - * Severs all parent-child relationships between two nodes. - *

- * The child node will be cascade deleted if one of the associations was the - * primary association, i.e. the one with which the child node was created. - * - * @param parentRef the parent end of the association - * @param childRef the child end of the association - * @throws InvalidNodeRefException if the parent or child nodes could not be found - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"parentRef", "childRef"}) - public void removeChild(NodeRef parentRef, NodeRef childRef) throws InvalidNodeRefException; - - /** - * @param nodeRef - * @return Returns all properties keyed by their qualified name - * @throws InvalidNodeRefException if the node could not be found - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef"}) - public Map getProperties(NodeRef nodeRef) throws InvalidNodeRefException; - - /** - * @param nodeRef - * @param qname the qualified name of the property - * @return Returns the value of the property, or null if not yet set - * @throws InvalidNodeRefException if the node could not be found - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef", "qname"}) - public Serializable getProperty(NodeRef nodeRef, QName qname) throws InvalidNodeRefException; - - /** - * Set the values of all properties to be an Serializable instances. - * The properties given must still fulfill the requirements of the class and - * aspects relevant to the node. - *

- * NOTE: Null values are allowed. - * - * @param nodeRef - * @param properties all the properties of the node keyed by their qualified names - * @throws InvalidNodeRefException if the node could not be found - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef", "properties"}) - public void setProperties(NodeRef nodeRef, Map properties) throws InvalidNodeRefException; - - /** - * Sets the value of a property to be any Serializable instance. - * To remove a property value, use {@link #getProperties(NodeRef)}, remove the - * value and call {@link #setProperties(NodeRef, Map)}. - *

- * NOTE: Null values are allowed. - * - * @param nodeRef - * @param qname the fully qualified name of the property - * @param propertyValue the value of the property - never null - * @throws InvalidNodeRefException if the node could not be found - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef", "qname", "value"}) - public void setProperty(NodeRef nodeRef, QName qname, Serializable value) throws InvalidNodeRefException; - - /** - * @param nodeRef the child node - * @return Returns a list of all parent-child associations that exist where the given - * node is the child - * @throws InvalidNodeRefException if the node could not be found - * - * @see #getParentAssocs(NodeRef, QNamePattern, QNamePattern) - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef"}) - public List getParentAssocs(NodeRef nodeRef) throws InvalidNodeRefException; - - /** - * Gets all parent associations where the pattern of the association qualified - * name is a match - *

- * The resultant list is ordered by (a) explicit index and (b) association creation time. - * - * @param nodeRef the child node - * @param typeQNamePattern the pattern that the type qualified name of the association must match - * @param qnamePattern the pattern that the qnames of the assocs must match - * @return Returns a list of all parent-child associations that exist where the given - * node is the child - * @throws InvalidNodeRefException if the node could not be found - * - * @see ChildAssociationRef#getNthSibling() - * @see #setChildAssociationIndex(ChildAssociationRef, int) - * @see QName - * @see org.alfresco.service.namespace.RegexQNamePattern#MATCH_ALL - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef", "typeQNamePattern", "qnamePattern"}) - public List getParentAssocs(NodeRef nodeRef, QNamePattern typeQNamePattern, QNamePattern qnamePattern) - throws InvalidNodeRefException; - - /** - * Get all child associations of the given node. - *

- * The resultant list is ordered by (a) explicit index and (b) association creation time. - * - * @param nodeRef the parent node - usually a container - * @return Returns a collection of ChildAssocRef instances. If the - * node is not a container then the result will be empty. - * @throws InvalidNodeRefException if the node could not be found - * - * @see #getChildAssocs(NodeRef, QNamePattern, QNamePattern) - * @see #setChildAssociationIndex(ChildAssociationRef, int) - * @see ChildAssociationRef#getNthSibling() - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef"}) - public List getChildAssocs(NodeRef nodeRef) throws InvalidNodeRefException; - - /** - * Gets all child associations where the pattern of the association qualified - * name is a match. - * - * @param nodeRef the parent node - usually a container - * @param typeQNamePattern the pattern that the type qualified name of the association must match - * @param qnamePattern the pattern that the qnames of the assocs must match - * @return Returns a list of ChildAssocRef instances. If the - * node is not a container then the result will be empty. - * @throws InvalidNodeRefException if the node could not be found - * - * @see QName - * @see org.alfresco.service.namespace.RegexQNamePattern#MATCH_ALL - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef", "typeQNamePattern", "qnamePattern"}) - public List getChildAssocs( - NodeRef nodeRef, - QNamePattern typeQNamePattern, - QNamePattern qnamePattern) - throws InvalidNodeRefException; - - /** - * Get the node with the given name within the context of the parent node. The name - * is case-insensitive as Alfresco has to support case-insensitive clients as standard. - * - * @param nodeRef the parent node - usuall a container - * @param assocTypeQName the type of the association - * @param childName the name of the node as per the property cm:name - * @return Returns the child node or null if not found - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef", "assocTypeQName", "childName"}) - public NodeRef getChildByName( - NodeRef nodeRef, - QName assocTypeQName, - String childName); - - /** - * Fetches the primary parent-child relationship. - *

- * For a root node, the parent node reference will be null. - * - * @param nodeRef - * @return Returns the primary parent-child association of the node - * @throws InvalidNodeRefException if the node could not be found - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef"}) - public ChildAssociationRef getPrimaryParent(NodeRef nodeRef) throws InvalidNodeRefException; - - /** - * - * @param sourceRef a reference to a real node - * @param targetRef a reference to a node - * @param assocTypeQName the qualified name of the association type - * @return Returns a reference to the new association - * @throws InvalidNodeRefException if either of the nodes could not be found - * @throws AssociationExistsException - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"sourceRef", "targetRef", "assocTypeQName"}) - public AssociationRef createAssociation(NodeRef sourceRef, NodeRef targetRef, QName assocTypeQName) - throws InvalidNodeRefException, AssociationExistsException; - - /** - * - * @param sourceRef the associaton source node - * @param targetRef the association target node - * @param assocTypeQName the qualified name of the association type - * @throws InvalidNodeRefException if either of the nodes could not be found - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"sourceRef", "targetRef", "assocTypeQName"}) - public void removeAssociation(NodeRef sourceRef, NodeRef targetRef, QName assocTypeQName) - throws InvalidNodeRefException; - - /** - * Fetches all associations from the given source where the associations' - * qualified names match the pattern provided. - * - * @param sourceRef the association source - * @param qnamePattern the association qname pattern to match against - * @return Returns a list of NodeAssocRef instances for which the - * given node is a source - * @throws InvalidNodeRefException if the source node could not be found - * - * @see QName - * @see org.alfresco.service.namespace.RegexQNamePattern#MATCH_ALL - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"sourceRef", "qnamePattern"}) - public List getTargetAssocs(NodeRef sourceRef, QNamePattern qnamePattern) - throws InvalidNodeRefException; - - /** - * Fetches all associations to the given target where the associations' - * qualified names match the pattern provided. - * - * @param targetRef the association target - * @param qnamePattern the association qname pattern to match against - * @return Returns a list of NodeAssocRef instances for which the - * given node is a target - * @throws InvalidNodeRefException - * - * @see QName - * @see org.alfresco.service.namespace.RegexQNamePattern#MATCH_ALL - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"targetRef", "qnamePattern"}) - public List getSourceAssocs(NodeRef targetRef, QNamePattern qnamePattern) - throws InvalidNodeRefException; - - /** - * The root node has an entry in the path(s) returned. For this reason, there - * will always be at least one path element in the returned path(s). - * The first element will have a null parent reference and qname. - * - * @param nodeRef - * @return Returns the path to the node along the primary node path - * @throws InvalidNodeRefException if the node could not be found - * - * @see #getPaths(NodeRef, boolean) - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef"}) - public Path getPath(NodeRef nodeRef) throws InvalidNodeRefException; - - /** - * The root node has an entry in the path(s) returned. For this reason, there - * will always be at least one path element in the returned path(s). - * The first element will have a null parent reference and qname. - * - * @param nodeRef - * @param primaryOnly true if only the primary path must be retrieved. If true, the - * result will have exactly one entry. - * @return Returns a List of all possible paths to the given node - * @throws InvalidNodeRefException if the node could not be found - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef", "primaryOnly"}) - public List getPaths(NodeRef nodeRef, boolean primaryOnly) throws InvalidNodeRefException; - - /** - * Get the node where archived items will have gone when deleted from the given store. - * - * @param storeRef the store that items were deleted from - * @return Returns the archive node parent - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"storeRef"}) - public NodeRef getStoreArchiveNode(StoreRef storeRef); - - /** - * Restore an individual node (along with its sub-tree nodes) to the target location. - * The archived node must have the {@link org.alfresco.model.ContentModel#ASPECT_ARCHIVED archived aspect} - * set against it. - * - * @param archivedNodeRef the archived node - * @param destinationParentNodeRef the parent to move the node into - * or null to use the original - * @param assocTypeQName the primary association type name to use in the new location - * or null to use the original - * @param assocQName the primary association name to use in the new location - * or null to use the original - * @return Returns the reference to the newly created node - */ - @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"archivedNodeRef", "destinationParentNodeRef", "assocTypeQName", "assocQName"}) - public NodeRef restoreNode( - NodeRef archivedNodeRef, - NodeRef destinationParentNodeRef, - QName assocTypeQName, - QName assocQName); -} +/* + * 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.service.cmr.repository; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.DbAccessControlList; +import org.alfresco.service.Auditable; +import org.alfresco.service.PublicService; +import org.alfresco.service.cmr.dictionary.InvalidAspectException; +import org.alfresco.service.cmr.dictionary.InvalidTypeException; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.QNamePattern; + +/** + * Interface for public and internal node and store operations. + * + * @author Derek Hulley + */ +@PublicService +public interface NodeService +{ + /** + * Gets a list of all available node store references + * + * @return Returns a list of store references + */ + @Auditable + public List getStores(); + + /** + * Create a new store for the given protocol and identifier. The implementation + * may create the store in any number of locations, including a database or + * Subversion. + * + * @param protocol the implementation protocol + * @param identifier the protocol-specific identifier + * @return Returns a reference to the store + * @throws StoreExistsException + */ + @Auditable(key = Auditable.Key.RETURN, parameters = {"protocol", "identifier"}) + public StoreRef createStore(String protocol, String identifier) throws StoreExistsException; + + /** + * @param storeRef a reference to the store to look for + * @return Returns true if the store exists, otherwise false + */ + @Auditable(parameters = {"storeRef"}) + public boolean exists(StoreRef storeRef); + + /** + * @param nodeRef a reference to the node to look for + * @return Returns true if the node exists, otherwise false + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef"}) + public boolean exists(NodeRef nodeRef); + + /** + * Gets the ID of the last transaction that caused the node to change. This includes + * deletions, so it is possible that the node being referenced no longer exists. + * If the node never existed, then null is returned. + * + * @param nodeRef a reference to a current or previously existing node + * @return Returns the status of the node, or null if the node never existed + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef"}) + public NodeRef.Status getNodeStatus(NodeRef nodeRef); + + /** + * @param storeRef a reference to an existing store + * @return Returns a reference to the root node of the store + * @throws InvalidStoreRefException if the store could not be found + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"storeRef"}) + public NodeRef getRootNode(StoreRef storeRef) throws InvalidStoreRefException; + + /** + * @see #createNode(NodeRef, QName, QName, QName, Map) + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"parentRef", "assocTypeQName", "assocQName", "nodeTypeQName"}) + public ChildAssociationRef createNode( + NodeRef parentRef, + QName assocTypeQName, + QName assocQName, + QName nodeTypeQName) + throws InvalidNodeRefException, InvalidTypeException; + + /** + * Creates a new, non-abstract, real node as a primary child of the given parent node. + * + * @param parentRef the parent node + * @param assocTypeQName the type of the association to create. This is used + * for verification against the data dictionary. + * @param assocQName the qualified name of the association + * @param nodeTypeQName a reference to the node type + * @param properties optional map of properties to keyed by their qualified names + * @return Returns a reference to the newly created child association + * @throws InvalidNodeRefException if the parent reference is invalid + * @throws InvalidTypeException if the node type reference is not recognised + * + * @see org.alfresco.service.cmr.dictionary.DictionaryService + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"parentRef", "assocTypeQName", "assocQName", "nodeTypeQName", "properties"}) + public ChildAssociationRef createNode( + NodeRef parentRef, + QName assocTypeQName, + QName assocQName, + QName nodeTypeQName, + Map properties) + throws InvalidNodeRefException, InvalidTypeException; + + /** + * Moves the primary location of the given node. + *

+ * This involves changing the node's primary parent and possibly the name of the + * association referencing it. + *

+ * If the new parent is in a different store from the original, then the entire + * node hierarchy is moved to the new store. Inter-store associations are not + * affected. + * + * @param nodeToMoveRef the node to move + * @param newParentRef the new parent of the moved node + * @param assocTypeQName the type of the association to create. This is used + * for verification against the data dictionary. + * @param assocQName the qualified name of the new child association + * @return Returns a reference to the newly created child association + * @throws InvalidNodeRefException if either the parent node or move node reference is invalid + * @throws CyclicChildRelationshipException if the child partakes in a cyclic relationship after the add + * + * @see #getPrimaryParent(NodeRef) + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeToMoveRef", "newParentRef", "assocTypeQName", "assocQName"}) + public ChildAssociationRef moveNode( + NodeRef nodeToMoveRef, + NodeRef newParentRef, + QName assocTypeQName, + QName assocQName) + throws InvalidNodeRefException; + + /** + * Set the ordering index of the child association. This affects the ordering of + * of the return values of methods that return a set of children or child + * associations. + * + * @param childAssocRef the child association that must be moved in the order + * @param index an arbitrary index that will affect the return order + * + * @see #getChildAssocs(NodeRef) + * @see #getChildAssocs(NodeRef, QNamePattern, QNamePattern) + * @see ChildAssociationRef#getNthSibling() + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"childAssocRef", "index"}) + public void setChildAssociationIndex( + ChildAssociationRef childAssocRef, + int index) + throws InvalidChildAssociationRefException; + + /** + * @param nodeRef + * @return Returns the type name + * @throws InvalidNodeRefException if the node could not be found + * + * @see org.alfresco.service.cmr.dictionary.DictionaryService + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef"}) + public QName getType(NodeRef nodeRef) throws InvalidNodeRefException; + + /** + * Re-sets the type of the node. Can be called in order specialise a node to a sub-type. + * + * This should be used with caution since calling it changes the type of the node and thus + * implies a different set of aspects, properties and associations. It is the calling codes + * responsibility to ensure that the node is in a approriate state after changing the type. + * + * @param nodeRef the node reference + * @param typeQName the type QName + * + * @since 1.1 + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef", "typeQName"}) + public void setType(NodeRef nodeRef, QName typeQName) throws InvalidNodeRefException; + + /** + * Applies an aspect to the given node. After this method has been called, + * the node with have all the aspect-related properties present + * + * @param nodeRef + * @param aspectTypeQName the aspect to apply to the node + * @param aspectProperties a minimum of the mandatory properties required for + * the aspect + * @throws InvalidNodeRefException + * @throws InvalidAspectException if the class reference is not to a valid aspect + * + * @see org.alfresco.service.cmr.dictionary.DictionaryService#getAspect(QName) + * @see org.alfresco.service.cmr.dictionary.ClassDefinition#getProperties() + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef", "aspectTypeQName", "aspectProperties"}) + public void addAspect( + NodeRef nodeRef, + QName aspectTypeQName, + Map aspectProperties) + throws InvalidNodeRefException, InvalidAspectException; + + /** + * Remove an aspect and all related properties from a node + * + * @param nodeRef + * @param aspectTypeQName the type of aspect to remove + * @throws InvalidNodeRefException if the node could not be found + * @throws InvalidAspectException if the the aspect is unknown or if the + * aspect is mandatory for the class of the node + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef", "aspectTypeQName"}) + public void removeAspect(NodeRef nodeRef, QName aspectTypeQName) + throws InvalidNodeRefException, InvalidAspectException; + + /** + * Determines if a given aspect is present on a node. Aspects may only be + * removed if they are NOT mandatory. + * + * @param nodeRef + * @param aspectTypeQName + * @return Returns true if the aspect has been applied to the given node, + * otherwise false + * @throws InvalidNodeRefException if the node could not be found + * @throws InvalidAspectException if the aspect reference is invalid + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef", "aspectTypeQName"}) + public boolean hasAspect(NodeRef nodeRef, QName aspectTypeQName) + throws InvalidNodeRefException, InvalidAspectException; + + /** + * @param nodeRef + * @return Returns a set of all aspects applied to the node, including mandatory + * aspects + * @throws InvalidNodeRefException if the node could not be found + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef"}) + public Set getAspects(NodeRef nodeRef) throws InvalidNodeRefException; + + /** + * Deletes the given node. + *

+ * All associations (both children and regular node associations) + * will be deleted, and where the given node is the primary parent, + * the children will also be cascade deleted. + * + * @param nodeRef reference to a node within a store + * @throws InvalidNodeRefException if the reference given is invalid + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef"}) + public void deleteNode(NodeRef nodeRef) throws InvalidNodeRefException; + + /** + * Makes a parent-child association between the given nodes. Both nodes must belong to the same store. + *

+ * + * + * @param parentRef + * @param childRef + * @param assocTypeQName the qualified name of the association type as defined in the datadictionary + * @param qname the qualified name of the association + * @return Returns a reference to the newly created child association + * @throws InvalidNodeRefException if the parent or child nodes could not be found + * @throws CyclicChildRelationshipException if the child partakes in a cyclic relationship after the add + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"parentRef", "childRef", "assocTypeQName", "qname"}) + public ChildAssociationRef addChild( + NodeRef parentRef, + NodeRef childRef, + QName assocTypeQName, + QName qname) throws InvalidNodeRefException; + + /** + * Severs all parent-child relationships between two nodes. + *

+ * The child node will be cascade deleted if one of the associations was the + * primary association, i.e. the one with which the child node was created. + * + * @param parentRef the parent end of the association + * @param childRef the child end of the association + * @throws InvalidNodeRefException if the parent or child nodes could not be found + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"parentRef", "childRef"}) + public void removeChild(NodeRef parentRef, NodeRef childRef) throws InvalidNodeRefException; + + /** + * @param nodeRef + * @return Returns all properties keyed by their qualified name + * @throws InvalidNodeRefException if the node could not be found + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef"}) + public Map getProperties(NodeRef nodeRef) throws InvalidNodeRefException; + + /** + * @param nodeRef + * @param qname the qualified name of the property + * @return Returns the value of the property, or null if not yet set + * @throws InvalidNodeRefException if the node could not be found + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef", "qname"}) + public Serializable getProperty(NodeRef nodeRef, QName qname) throws InvalidNodeRefException; + + /** + * Set the values of all properties to be an Serializable instances. + * The properties given must still fulfill the requirements of the class and + * aspects relevant to the node. + *

+ * NOTE: Null values are allowed. + * + * @param nodeRef + * @param properties all the properties of the node keyed by their qualified names + * @throws InvalidNodeRefException if the node could not be found + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef", "properties"}) + public void setProperties(NodeRef nodeRef, Map properties) throws InvalidNodeRefException; + + /** + * Sets the value of a property to be any Serializable instance. + * To remove a property value, use {@link #getProperties(NodeRef)}, remove the + * value and call {@link #setProperties(NodeRef, Map)}. + *

+ * NOTE: Null values are allowed. + * + * @param nodeRef + * @param qname the fully qualified name of the property + * @param propertyValue the value of the property - never null + * @throws InvalidNodeRefException if the node could not be found + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef", "qname", "value"}) + public void setProperty(NodeRef nodeRef, QName qname, Serializable value) throws InvalidNodeRefException; + + /** + * @param nodeRef the child node + * @return Returns a list of all parent-child associations that exist where the given + * node is the child + * @throws InvalidNodeRefException if the node could not be found + * + * @see #getParentAssocs(NodeRef, QNamePattern, QNamePattern) + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef"}) + public List getParentAssocs(NodeRef nodeRef) throws InvalidNodeRefException; + + /** + * Gets all parent associations where the pattern of the association qualified + * name is a match + *

+ * The resultant list is ordered by (a) explicit index and (b) association creation time. + * + * @param nodeRef the child node + * @param typeQNamePattern the pattern that the type qualified name of the association must match + * @param qnamePattern the pattern that the qnames of the assocs must match + * @return Returns a list of all parent-child associations that exist where the given + * node is the child + * @throws InvalidNodeRefException if the node could not be found + * + * @see ChildAssociationRef#getNthSibling() + * @see #setChildAssociationIndex(ChildAssociationRef, int) + * @see QName + * @see org.alfresco.service.namespace.RegexQNamePattern#MATCH_ALL + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef", "typeQNamePattern", "qnamePattern"}) + public List getParentAssocs(NodeRef nodeRef, QNamePattern typeQNamePattern, QNamePattern qnamePattern) + throws InvalidNodeRefException; + + /** + * Get all child associations of the given node. + *

+ * The resultant list is ordered by (a) explicit index and (b) association creation time. + * + * @param nodeRef the parent node - usually a container + * @return Returns a collection of ChildAssocRef instances. If the + * node is not a container then the result will be empty. + * @throws InvalidNodeRefException if the node could not be found + * + * @see #getChildAssocs(NodeRef, QNamePattern, QNamePattern) + * @see #setChildAssociationIndex(ChildAssociationRef, int) + * @see ChildAssociationRef#getNthSibling() + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef"}) + public List getChildAssocs(NodeRef nodeRef) throws InvalidNodeRefException; + + /** + * Gets all child associations where the pattern of the association qualified + * name is a match. + * + * @param nodeRef the parent node - usually a container + * @param typeQNamePattern the pattern that the type qualified name of the association must match + * @param qnamePattern the pattern that the qnames of the assocs must match + * @return Returns a list of ChildAssocRef instances. If the + * node is not a container then the result will be empty. + * @throws InvalidNodeRefException if the node could not be found + * + * @see QName + * @see org.alfresco.service.namespace.RegexQNamePattern#MATCH_ALL + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef", "typeQNamePattern", "qnamePattern"}) + public List getChildAssocs( + NodeRef nodeRef, + QNamePattern typeQNamePattern, + QNamePattern qnamePattern) + throws InvalidNodeRefException; + + /** + * Get the node with the given name within the context of the parent node. The name + * is case-insensitive as Alfresco has to support case-insensitive clients as standard. + * + * @param nodeRef the parent node - usuall a container + * @param assocTypeQName the type of the association + * @param childName the name of the node as per the property cm:name + * @return Returns the child node or null if not found + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef", "assocTypeQName", "childName"}) + public NodeRef getChildByName( + NodeRef nodeRef, + QName assocTypeQName, + String childName); + + /** + * Fetches the primary parent-child relationship. + *

+ * For a root node, the parent node reference will be null. + * + * @param nodeRef + * @return Returns the primary parent-child association of the node + * @throws InvalidNodeRefException if the node could not be found + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef"}) + public ChildAssociationRef getPrimaryParent(NodeRef nodeRef) throws InvalidNodeRefException; + + /** + * + * @param sourceRef a reference to a real node + * @param targetRef a reference to a node + * @param assocTypeQName the qualified name of the association type + * @return Returns a reference to the new association + * @throws InvalidNodeRefException if either of the nodes could not be found + * @throws AssociationExistsException + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"sourceRef", "targetRef", "assocTypeQName"}) + public AssociationRef createAssociation(NodeRef sourceRef, NodeRef targetRef, QName assocTypeQName) + throws InvalidNodeRefException, AssociationExistsException; + + /** + * + * @param sourceRef the associaton source node + * @param targetRef the association target node + * @param assocTypeQName the qualified name of the association type + * @throws InvalidNodeRefException if either of the nodes could not be found + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"sourceRef", "targetRef", "assocTypeQName"}) + public void removeAssociation(NodeRef sourceRef, NodeRef targetRef, QName assocTypeQName) + throws InvalidNodeRefException; + + /** + * Fetches all associations from the given source where the associations' + * qualified names match the pattern provided. + * + * @param sourceRef the association source + * @param qnamePattern the association qname pattern to match against + * @return Returns a list of NodeAssocRef instances for which the + * given node is a source + * @throws InvalidNodeRefException if the source node could not be found + * + * @see QName + * @see org.alfresco.service.namespace.RegexQNamePattern#MATCH_ALL + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"sourceRef", "qnamePattern"}) + public List getTargetAssocs(NodeRef sourceRef, QNamePattern qnamePattern) + throws InvalidNodeRefException; + + /** + * Fetches all associations to the given target where the associations' + * qualified names match the pattern provided. + * + * @param targetRef the association target + * @param qnamePattern the association qname pattern to match against + * @return Returns a list of NodeAssocRef instances for which the + * given node is a target + * @throws InvalidNodeRefException + * + * @see QName + * @see org.alfresco.service.namespace.RegexQNamePattern#MATCH_ALL + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"targetRef", "qnamePattern"}) + public List getSourceAssocs(NodeRef targetRef, QNamePattern qnamePattern) + throws InvalidNodeRefException; + + /** + * The root node has an entry in the path(s) returned. For this reason, there + * will always be at least one path element in the returned path(s). + * The first element will have a null parent reference and qname. + * + * @param nodeRef + * @return Returns the path to the node along the primary node path + * @throws InvalidNodeRefException if the node could not be found + * + * @see #getPaths(NodeRef, boolean) + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef"}) + public Path getPath(NodeRef nodeRef) throws InvalidNodeRefException; + + /** + * The root node has an entry in the path(s) returned. For this reason, there + * will always be at least one path element in the returned path(s). + * The first element will have a null parent reference and qname. + * + * @param nodeRef + * @param primaryOnly true if only the primary path must be retrieved. If true, the + * result will have exactly one entry. + * @return Returns a List of all possible paths to the given node + * @throws InvalidNodeRefException if the node could not be found + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef", "primaryOnly"}) + public List getPaths(NodeRef nodeRef, boolean primaryOnly) throws InvalidNodeRefException; + + /** + * Get the node where archived items will have gone when deleted from the given store. + * + * @param storeRef the store that items were deleted from + * @return Returns the archive node parent + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"storeRef"}) + public NodeRef getStoreArchiveNode(StoreRef storeRef); + + /** + * Restore an individual node (along with its sub-tree nodes) to the target location. + * The archived node must have the {@link org.alfresco.model.ContentModel#ASPECT_ARCHIVED archived aspect} + * set against it. + * + * @param archivedNodeRef the archived node + * @param destinationParentNodeRef the parent to move the node into + * or null to use the original + * @param assocTypeQName the primary association type name to use in the new location + * or null to use the original + * @param assocQName the primary association name to use in the new location + * or null to use the original + * @return Returns the reference to the newly created node + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"archivedNodeRef", "destinationParentNodeRef", "assocTypeQName", "assocQName"}) + public NodeRef restoreNode( + NodeRef archivedNodeRef, + NodeRef destinationParentNodeRef, + QName assocTypeQName, + QName assocQName); +} diff --git a/source/java/org/alfresco/service/cmr/repository/TemplateNode.java b/source/java/org/alfresco/service/cmr/repository/TemplateNode.java index 4372604290..6d2a05b55c 100644 --- a/source/java/org/alfresco/service/cmr/repository/TemplateNode.java +++ b/source/java/org/alfresco/service/cmr/repository/TemplateNode.java @@ -34,6 +34,7 @@ import org.alfresco.repo.template.NodeSearchResultsMap; import org.alfresco.repo.template.SavedSearchResultsMap; import org.alfresco.repo.template.XPathResultsMap; import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.audit.AuditInfo; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.lock.LockStatus; import org.alfresco.service.cmr.security.AccessPermission; @@ -495,7 +496,7 @@ public final class TemplateNode implements Serializable } /** - * @return Display path to this node + * @return Display path to this node - the path built of 'cm:name' attribute values. */ public String getDisplayPath() { @@ -514,6 +515,14 @@ public final class TemplateNode implements Serializable return displayPath; } + /** + * @return QName path to this node. This can be used for Lucene PATH: style queries + */ + public String getQnamePath() + { + return this.services.getNodeService().getPath(this.nodeRef).toPrefixString(this.services.getNamespaceService()); + } + /** * @return the small icon image for this node */ @@ -652,6 +661,14 @@ public final class TemplateNode implements Serializable } + // Audit API + + + public List getAuditTrail() + { + return this.services.getAuditService().getAuditTrail(this.nodeRef); + } + // ------------------------------------------------------------------------------ // Misc helpers diff --git a/source/java/org/alfresco/service/cmr/security/PersonService.java b/source/java/org/alfresco/service/cmr/security/PersonService.java index 2f7665c41d..b9a5b197ff 100644 --- a/source/java/org/alfresco/service/cmr/security/PersonService.java +++ b/source/java/org/alfresco/service/cmr/security/PersonService.java @@ -21,6 +21,7 @@ import java.util.Map; import java.util.Set; import org.alfresco.service.Auditable; +import org.alfresco.service.NotAuditable; import org.alfresco.service.PublicService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; @@ -171,7 +172,7 @@ public interface PersonService * @param caseSensitiveUserName * @return */ - + @NotAuditable public String getUserIdentifier(String caseSensitiveUserName); } diff --git a/source/java/org/alfresco/service/cmr/workflow/WorkflowService.java b/source/java/org/alfresco/service/cmr/workflow/WorkflowService.java index d1c22d461d..80938eefb3 100644 --- a/source/java/org/alfresco/service/cmr/workflow/WorkflowService.java +++ b/source/java/org/alfresco/service/cmr/workflow/WorkflowService.java @@ -111,6 +111,7 @@ public interface WorkflowService * @param workflowName workflow name e.g. jbpm://review * @return the deployed workflow definition */ + @Auditable(parameters = {"workflowName"}) public WorkflowDefinition getDefinitionByName(String workflowName);